cleaning up (1)

This commit is contained in:
DadaMonad
2014-12-14 17:00:02 +00:00
parent 9b582fc795
commit 7696864841
50 changed files with 9302 additions and 6 deletions

32
bower_components/observe-js/.bower.json vendored Normal file
View File

@@ -0,0 +1,32 @@
{
"name": "observe-js",
"homepage": "https://github.com/Polymer/observe-js",
"authors": [
"The Polymer Authors"
],
"description": "A library for observing Arrays, Objects and PathValues",
"main": "src/observe.js",
"keywords": [
"Object.observe"
],
"license": "BSD",
"private": true,
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
],
"version": "0.5.1",
"_release": "0.5.1",
"_resolution": {
"type": "version",
"tag": "0.5.1",
"commit": "d530515b4afe7d0a6b61c06f1a6c64f07bcbbf57"
},
"_source": "git://github.com/Polymer/observe-js.git",
"_target": "~0.5.1",
"_originalSource": "observe-js",
"_direct": true
}

9
bower_components/observe-js/AUTHORS vendored Normal file
View File

@@ -0,0 +1,9 @@
# Names should be added to this file with this pattern:
#
# For individuals:
# Name <email address>
#
# For organizations:
# Organization <fnmatch pattern>
#
Google Inc. <*@google.com>

203
bower_components/observe-js/README.md vendored Normal file
View File

@@ -0,0 +1,203 @@
[![Build status](http://www.polymer-project.org/build/observe-js/status.png "Build status")](http://build.chromium.org/p/client.polymer/waterfall) [![Analytics](https://ga-beacon.appspot.com/UA-39334307-2/Polymer/observe-js/README)](https://github.com/igrigorik/ga-beacon)
## Learn the tech
### Why observe-js?
observe-js is a library for observing changes in JavaScript data. It exposes a high-level API and uses Object.observe if available, and otherwise performs dirty-checking. observe-js requires ECMAScript 5.
### Observable
observe-js implements a set of observers (PathObserver, ArrayObserver, ObjectObserver, CompoundObserver, ObserverTransform) which all implement the Observable interface:
```JavaScript
{
// Begins observation. Value changes will be reported by invoking |changeFn| with |opt_receiver| as
// the target, if provided. Returns the initial value of the observation.
open: function(changeFn, opt_receiver) {},
// Report any changes now (does nothing if there are no changes to report).
deliver: function() {},
// If there are changes to report, ignore them. Returns the current value of the observation.
discardChanges: function() {},
// Ends observation. Frees resources and drops references to observed objects.
close: function() {}
}
```
### PathObserver
PathObserver observes a "value-at-a-path" from a given object:
```JavaScript
var obj = { foo: { bar: 'baz' } };
var observer = new PathObserver(obj, 'foo.bar');
observer.open(function(newValue, oldValue) {
// respond to obj.foo.bar having changed value.
});
```
PathObserver will report a change whenever the value obtained by the corresponding path expression (e.g. `obj.foo.bar`) would return a different value.
PathObserver also exposes a `setValue` method which attempts to update the underlying value. Setting the value does not affect notification state (in other words, a caller sets the value but does not `discardChanges`, the `changeFn` will be notified of the change).
```JavaScript
observer.setValue('boo');
assert(obj.foo.bar == 'boo');
```
Notes:
* If the path is ever unreachable, the value is considered to be `undefined`.
* If the path is empty (e.g. `''`), it is said to be the empty path and its value is its root object.
* PathObservation respects values on the prototype chain
### ArrayObserver
ArrayObserver observes the index-positions of an Array and reports changes as the minimal set of "splices" which would have had the same effect.
```JavaScript
var arr = [0, 1, 2, 4];
var observer = new ArrayObserver(arr);
observer.open(function(splices) {
// respond to changes to the elements of arr.
splices.forEach(function(splice) {
splice.index; // index position that the change occurred.
splice.removed; // an array of values representing the sequence of elements which were removed
splice.addedCount; // the number of elements which were inserted.
});
});
```
ArrayObserver also exposes a utility function: `applySplices`. The purpose of `applySplices` is to transform a copy of an old state of an array into a copy of its current state, given the current state and the splices reported from the ArrayObserver.
```JavaScript
AraryObserver.applySplices = function(previous, current, splices) { }
```
### ObjectObserver
ObjectObserver observes the set of own-properties of an object and their values.
```JavaScript
var myObj = { id: 1, foo: 'bar' };
var observer = new ObjectObserver(myObj);
observer.open(function(added, removed, changed, getOldValueFn) {
// respond to changes to the obj.
Object.keys(added).forEach(function(property) {
property; // a property which has been been added to obj
added[property]; // its value
});
Object.keys(removed).forEach(function(property) {
property; // a property which has been been removed from obj
getOldValueFn(property); // its old value
});
Object.keys(changed).forEach(function(property) {
property; // a property on obj which has changed value.
changed[property]; // its value
getOldValueFn(property); // its old value
});
});
```
### CompoundObserver
CompoundObserver allows simultaneous observation of multiple paths and/or Observables. It reports any and all changes in to the provided `changeFn` callback.
```JavaScript
var obj = {
a: 1,
b: 2,
};
var otherObj = { c: 3 };
var observer = new CompoundObserver();
observer.addPath(obj, 'a');
observer.addObserver(new PathObserver(obj, 'b'));
observer.addPath(otherObj, 'c');
observer.open(function(newValues, oldValues) {
// Use for-in to iterte which values have changed.
for (var i in oldValues) {
console.log('The ' + i + 'th value changed from: ' + newValues[i] + ' to: ' + oldValues[i]);
}
});
```
### ObserverTransform
ObserverTransform is used to dynamically transform observed value(s).
```JavaScript
var obj = { value: 10 };
var observer = new PathObserver(obj, 'value');
function getValue(value) { return value * 2 };
function setValue(value) { return value / 2 };
var transform = new ObserverTransform(observer, getValue, setValue);
// returns 20.
transform.open(function(newValue, oldValue) {
console.log('new: ' + newValue + ', old: ' + oldValue);
});
obj.value = 20;
transform.deliver(); // 'new: 40, old: 20'
transform.setValue(4); // obj.value === 2;
```
ObserverTransform can also be used to reduce a set of observed values to a single value:
```JavaScript
var obj = { a: 1, b: 2, c: 3 };
var observer = new CompoundObserver();
observer.addPath(obj, 'a');
observer.addPath(obj, 'b');
observer.addPath(obj, 'c');
var transform = new ObserverTransform(observer, fuction(values) {
var value = 0;
for (var i = 0; i < values.length; i++)
value += values[i]
return value;
});
// returns 6.
transform.open(function(newValue, oldValue) {
console.log('new: ' + newValue + ', old: ' + oldValue);
});
obj.a = 2;
obj.c = 10;
transform.deliver(); // 'new: 14, old: 6'
```
### Path objects
A path is an ECMAScript expression consisting only of identifiers (`myVal`), member accesses (`foo.bar`) and key lookup with literal values (`arr[0]` `obj['str-value'].bar.baz`).
`Path.get('foo.bar.baz')` returns a Path object which represents the path. Path objects have the following API:
```JavaScript
{
// Returns the current of the path from the provided object. If eval() is available, a compiled getter will be
// used for better performance.
getValueFrom: function(obj) { }
// Attempts to set the value of the path from the provided object. Returns true IFF the path was reachable and
// set.
setValueFrom: function(obj, newValue) { }
}
```
Path objects are interned (e.g. `assert(Path.get('foo.bar.baz') === Path.get('foo.bar.baz'));`) and are used internally to avoid excessive parsing of path strings. Observers which take path strings as arguments will also accept Path objects.
## About delivery of changes
observe-js is intended for use in environments which implement Object.observe, but it supports use in environments which do not.
If `Object.observe` is present, and observers have changes to report, their callbacks will be invoked at the end of the current turn (microtask). In a browser environment, this is generally at the end of an event.
If `Object.observe` is absent, `Platform.performMicrotaskCheckpoint()` must be called to trigger delivery of changes. If `Object.observe` is implemented, `Platform.performMicrotaskCheckpoint()` has no effect.

View File

@@ -0,0 +1,183 @@
/*
* Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
(function(global) {
'use strict';
function now() {
return global.performance && typeof performance.now == 'function' ?
performance.now() : Date.now();
}
function checkpoint() {
if (global.Platform &&
typeof Platform.performMicrotaskCheckpoint == 'function') {
Platform.performMicrotaskCheckpoint();
}
}
// TODO(rafaelw): Add simple Promise polyfill for IE.
var TESTING_TICKS = 400;
var TICKS_PER_FRAME = 16;
var MAX_RUNS = 50;
function Benchmark(testingTicks, ticksPerFrame, maxRuns) {
this.testingTicks = testingTicks || TESTING_TICKS;
this.ticksPerFrame = ticksPerFrame || TICKS_PER_FRAME;
this.maxRuns = maxRuns || 50;
this.average = 0;
}
Benchmark.prototype = {
// Abstract API
setup: function(variation) {},
test: function() {
throw Error('Not test function found');
},
cleanup: function() {},
runOne: function(variation) {
this.setup(variation);
var before = now();
this.test(variation);
var self = this;
return Promise.resolve().then(function() {
checkpoint();
var after = now();
self.cleanup(variation);
return after - before;
});
},
runMany: function(count, variation) {
var self = this;
return new Promise(function(fulfill) {
var total = 0;
function next(time) {
if (!count) {
fulfill(total);
return;
}
self.runOne(variation).then(function(time) {
count--;
total += time;
next();
});
}
requestAnimationFrame(next);
});
},
runVariation: function(variation, reportFn) {
var self = this;
reportFn = reportFn || function() {}
return new Promise(function(fulfill) {
self.runMany(3, variation).then(function(time) {
return time/3;
}).then(function(estimate) {
var runsPerFrame = Math.ceil(self.ticksPerFrame / estimate);
var frames = Math.ceil(self.testingTicks / self.ticksPerFrame);
var maxFrames = Math.ceil(self.maxRuns / runsPerFrame);
frames = Math.min(frames, maxFrames);
var count = 0;
var total = 0;
function next() {
if (!frames) {
self.average = total / count;
self.dispose();
fulfill(self.average);
return;
}
self.runMany(runsPerFrame, variation).then(function(time) {
frames--;
total += time;
count += runsPerFrame;
reportFn(variation, count);
next();
});
}
next();
});
});
},
run: function(variations, reportFn) {
if (!Array.isArray(variations)) {
return this.runVariation(variations, reportFn);
}
var self = this;
variations = variations.slice();
return new Promise(function(fulfill) {
var results = [];
function next() {
if (!variations.length) {
fulfill(results);
return;
}
var variation = variations.shift();
self.runVariation(variation, reportFn).then(function(time) {
results.push(time);
next();
});
}
next();
});
}
};
function all(benchmarks, variations, statusFn) {
return new Promise(function(fulfill) {
var results = [];
var current;
function next() {
current = benchmarks.shift();
if (!current) {
fulfill(results);
return;
}
function update(variation, runs) {
statusFn(current, variation, runs);
}
current.run(variations, update).then(function(time) {
results.push(time);
next();
});
}
next();
});
}
global.Benchmark = Benchmark;
global.Benchmark.all = all;
})(this);

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
var console = {
log: print
};
var requestAnimationFrame = function(callback) {
callback();
}
recordCount = 0;
var alert = print;
function reportResults(times) {
console.log(JSON.stringify(times));
}
function reportStatus(b, variation, count) {
console.log(b.objectCount + ' objects, ' + count + ' runs.');
}
var objectCounts = [ 4000, 8000, 16000 ];
var benchmarks = [];
objectCounts.forEach(function(objectCount, i) {
benchmarks.push(
new SetupPathBenchmark('', objectCount));
});
Benchmark.all(benchmarks, 0, reportStatus).then(reportResults);

View File

@@ -0,0 +1,181 @@
<html>
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<head>
<title>Observation Benchmarks</title>
<meta charset="utf-8">
<script src="../src/observe.js"></script>
<script src="chartjs/Chart.js"></script>
<script src="benchmark.js"></script>
<script src="observation_benchmark.js"></script>
<style>
* {
font-family: arial, helvetica, sans-serif;
font-weight: 400;
}
body {
margin: 0;
padding: 0;
background-color: rgba(0, 0, 0, .1);
}
</style>
</head>
<body>
<h1>Observation Benchmarks</h1>
<select id="benchmarkSelect">
<option>ObjectBenchmark</option>
<option>SetupObjectBenchmark</option>
<option>ArrayBenchmark</option>
<option>SetupArrayBenchmark</option>
<option>PathBenchmark</option>
<option>SetupPathBenchmark</option>
</select>
<select id="configSelect">
</select>
<button id="go">Run Benchmarks</button>
<span>Object Count: </span>
<input id="objectCountInput" style="width: 200px" value="4000, 8000, 16000, 32000"><br>
<span>Mutation Count: </span>
<input id="mutationCountInput" style="width: 200px" value="0, 100, 200, 400, 800, 1600"><br>
<br>
<span id="status"></span>
<section style="width: 100%">
<article>
<div style="display:inline-block; padding-bottom: 20px">
Times in ms
</div>
<div style="display:inline-block">
<canvas id="times" width="800" height="400"></canvas>
</div>
<div style="display:inline-block">
<ul id="legendList">
</ul>
</div>
</article>
</section>
<h3 style="margin-left: 440px">Object Set Size</h3>
<script>
var benchmark;
var goButton = document.getElementById('go');
var objectCountInput = document.getElementById('objectCountInput');
var mutationCountInput = document.getElementById('mutationCountInput');
var statusSpan = document.getElementById('status');
var timesCanvas = document.getElementById('times');
var benchmarkSelect = document.getElementById('benchmarkSelect');
var configSelect = document.getElementById('configSelect');
function changeBenchmark() {
benchmark = window[benchmarkSelect.value];
configSelect.textContent = '';
benchmark.configs.forEach(function(config) {
var option = document.createElement('option');
option.textContent = config;
configSelect.appendChild(option);
});
document.title = benchmarkSelect.value;
}
benchmarkSelect.addEventListener('change', changeBenchmark);
changeBenchmark();
var ul = document.getElementById('legendList');
var colors = [
[0, 0, 255],
[138,43,226],
[165,42,42],
[100,149,237],
[220,20,60],
[184,134,11]
].map(function(rgb) {
return 'rgba(' + rgb.join(',') + ',.7)';
});
goButton.addEventListener('click', function() {
goButton.disabled = true;
goButton.textContent = 'Running...';
ul.textContent = '';
var objectCounts = objectCountInput.value.split(',').map(function(val) {
return Number(val);
});
var mutationCounts = mutationCountInput.value.split(',').map(function(val) {
return Number(val);
});
mutationCounts.forEach(function(count, i) {
var li = document.createElement('li');
li.textContent = count + ' mutations.'
li.style.color = colors[i];
ul.appendChild(li);
});
var results = [];
function benchmarkComplete(times) {
timesArray = [];
var index = 0;
mutationCounts.forEach(function(mutationCount, i) {
timesArray.push([]);
objectCounts.forEach(function(objectCount, j) {
timesArray[i][j] = times[j][i];
});
});
timesCanvas.height = 400;
timesCanvas.width = 800;
timesCanvas.setAttribute('style', '');
var ctx = timesCanvas.getContext("2d");
new Chart(ctx).Line({
labels: objectCounts,
datasets: timesArray.map(function(times, i) {
return {
fillColor: 'rgba(255, 255, 255, 0)',
strokeColor: colors[i],
pointColor: colors[i],
pointStrokeColor: "#fff",
data: times
};
})
}, {
bezierCurve: false
});
goButton.disabled = false;
goButton.textContent = 'Run Benchmarks';
statusSpan.textContent = '';
}
function updateStatus(benchmark, mutationCount, count) {
statusSpan.textContent = 'Testing: ' +
benchmark.objectCount + ' objects, ' +
mutationCount + ' mutations ... ' +
count + ' runs';
}
var benchmarks = objectCounts.map(function(objectCount) {
return new benchmark(configSelect.value, objectCount);
});
Benchmark.all(benchmarks, mutationCounts, updateStatus)
.then(benchmarkComplete);
});
</script>
</body>
</html>

View File

@@ -0,0 +1,357 @@
/*
* Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
(function(global) {
'use strict';
var createObject = ('__proto__' in {}) ?
function(obj) { return obj; } :
function(obj) {
var proto = obj.__proto__;
if (!proto)
return obj;
var newObject = Object.create(proto);
Object.getOwnPropertyNames(obj).forEach(function(name) {
Object.defineProperty(newObject, name,
Object.getOwnPropertyDescriptor(obj, name));
});
return newObject;
};
function ObservationBenchmark(objectCount) {
Benchmark.call(this);
this.objectCount = objectCount;
}
ObservationBenchmark.prototype = createObject({
__proto__: Benchmark.prototype,
setup: function() {
this.mutations = 0;
if (this.objects)
return;
this.objects = [];
this.observers = [];
this.objectIndex = 0;
while (this.objects.length < this.objectCount) {
var obj = this.newObject();
this.objects.push(obj);
var observer = this.newObserver(obj);
observer.open(this.observerCallback, this);
this.observers.push(observer);
}
},
test: function(mutationCount) {
while (mutationCount > 0) {
var obj = this.objects[this.objectIndex];
mutationCount -= this.mutateObject(obj);
this.mutations++;
this.objectIndex++;
if (this.objectIndex == this.objects.length) {
this.objectIndex = 0;
}
}
},
cleanup: function() {
if (this.mutations !== 0)
alert('Error: mutationCount == ' + this.mutationCount);
this.mutations = 0;
},
dispose: function() {
this.objects = null;
while (this.observers.length) {
this.observers.pop().close();
}
this.observers = null;
if (Observer._allObserversCount != 0) {
alert('Observers leaked');
}
},
observerCallback: function() {
this.mutations--;
}
});
function SetupObservationBenchmark(objectCount) {
Benchmark.call(this);
this.objectCount = objectCount;
}
SetupObservationBenchmark.prototype = createObject({
__proto__: Benchmark.prototype,
setup: function() {
this.mutations = 0;
this.objects = [];
this.observers = [];
while (this.objects.length < this.objectCount) {
var obj = this.newObject();
this.objects.push(obj);
}
},
test: function() {
for (var i = 0; i < this.objects.length; i++) {
var obj = this.objects[i];
var observer = this.newObserver(obj);
observer.open(this.observerCallback, this);
this.observers.push(observer);
}
},
cleanup: function() {
while (this.observers.length) {
this.observers.pop().close();
}
if (Observer._allObserversCount != 0) {
alert('Observers leaked');
}
this.objects = null;
this.observers = null;
},
dispose: function() {
}
});
function ObjectBenchmark(config, objectCount) {
ObservationBenchmark.call(this, objectCount);
this.properties = [];
for (var i = 0; i < ObjectBenchmark.propertyCount; i++) {
this.properties.push(String.fromCharCode(97 + i));
}
}
ObjectBenchmark.configs = [];
ObjectBenchmark.propertyCount = 15;
ObjectBenchmark.prototype = createObject({
__proto__: ObservationBenchmark.prototype,
newObject: function() {
var obj = {};
for (var j = 0; j < ObjectBenchmark.propertyCount; j++)
obj[this.properties[j]] = j;
return obj;
},
newObserver: function(obj) {
return new ObjectObserver(obj);
},
mutateObject: function(obj) {
var size = Math.floor(ObjectBenchmark.propertyCount / 3);
for (var i = 0; i < size; i++) {
obj[this.properties[i]]++;
}
return size;
}
});
function SetupObjectBenchmark(config, objectCount) {
SetupObservationBenchmark.call(this, objectCount);
this.properties = [];
for (var i = 0; i < ObjectBenchmark.propertyCount; i++) {
this.properties.push(String.fromCharCode(97 + i));
}
}
SetupObjectBenchmark.configs = [];
SetupObjectBenchmark.propertyCount = 15;
SetupObjectBenchmark.prototype = createObject({
__proto__: SetupObservationBenchmark.prototype,
newObject: function() {
var obj = {};
for (var j = 0; j < SetupObjectBenchmark.propertyCount; j++)
obj[this.properties[j]] = j;
return obj;
},
newObserver: function(obj) {
return new ObjectObserver(obj);
}
});
function ArrayBenchmark(config, objectCount) {
ObservationBenchmark.call(this, objectCount);
var tokens = config.split('/');
this.operation = tokens[0];
this.undo = tokens[1];
};
ArrayBenchmark.configs = ['splice', 'update', 'push/pop', 'shift/unshift'];
ArrayBenchmark.elementCount = 100;
ArrayBenchmark.prototype = createObject({
__proto__: ObservationBenchmark.prototype,
newObject: function() {
var array = [];
for (var i = 0; i < ArrayBenchmark.elementCount; i++)
array.push(i);
return array;
},
newObserver: function(array) {
return new ArrayObserver(array);
},
mutateObject: function(array) {
switch (this.operation) {
case 'update':
var mutationsMade = 0;
var size = Math.floor(ArrayBenchmark.elementCount / 10);
for (var j = 0; j < size; j++) {
array[j*size] += 1;
mutationsMade++;
}
return mutationsMade;
case 'splice':
var size = Math.floor(ArrayBenchmark.elementCount / 5);
var removed = array.splice(size, size);
Array.prototype.splice.apply(array, [size*2, 0].concat(removed));
return size * 2;
default:
var val = array[this.undo]();
array[this.operation](val + 1);
return 2;
}
}
});
function SetupArrayBenchmark(config, objectCount) {
ObservationBenchmark.call(this, objectCount);
};
SetupArrayBenchmark.configs = [];
SetupArrayBenchmark.propertyCount = 15;
SetupArrayBenchmark.prototype = createObject({
__proto__: SetupObservationBenchmark.prototype,
newObject: function() {
var array = [];
for (var i = 0; i < ArrayBenchmark.elementCount; i++)
array.push(i);
return array;
},
newObserver: function(array) {
return new ArrayObserver(array);
}
});
function PathBenchmark(config, objectCount) {
ObservationBenchmark.call(this, objectCount);
this.leaf = config === 'leaf';
this.path = Path.get('foo.bar.baz');
this.firstPathProp = Path.get(this.path[0]);
}
PathBenchmark.configs = ['leaf', 'root'];
PathBenchmark.prototype = createObject({
__proto__: ObservationBenchmark.prototype,
newPath: function(parts, value) {
var obj = {};
var ref = obj;
var prop;
for (var i = 0; i < parts.length - 1; i++) {
prop = parts[i];
ref[prop] = {};
ref = ref[prop];
}
prop = parts[parts.length - 1];
ref[prop] = value;
return obj;
},
newObject: function() {
return this.newPath(this.path, 1);
},
newObserver: function(obj) {
return new PathObserver(obj, this.path);
},
mutateObject: function(obj) {
var val = this.path.getValueFrom(obj);
if (this.leaf) {
this.path.setValueFrom(obj, val + 1);
} else {
this.firstPathProp.setValueFrom(obj, this.newPath(this.path.slice(1), val + 1));
}
return 1;
}
});
function SetupPathBenchmark(config, objectCount) {
ObservationBenchmark.call(this, objectCount);
this.path = Path.get('foo.bar.baz');
}
SetupPathBenchmark.configs = [];
SetupPathBenchmark.prototype = createObject({
__proto__: SetupObservationBenchmark.prototype,
newPath: function(parts, value) {
var obj = {};
var ref = obj;
var prop;
for (var i = 0; i < parts.length - 1; i++) {
prop = parts[i];
ref[prop] = {};
ref = ref[prop];
}
prop = parts[parts.length - 1];
ref[prop] = value;
return obj;
},
newObject: function() {
return this.newPath(this.path, 1);
},
newObserver: function(obj) {
return new PathObserver(obj, this.path);
}
});
global.ObjectBenchmark = ObjectBenchmark;
global.SetupObjectBenchmark = SetupObjectBenchmark;
global.ArrayBenchmark = ArrayBenchmark;
global.SetupArrayBenchmark = SetupArrayBenchmark;
global.PathBenchmark = PathBenchmark;
global.SetupPathBenchmark = SetupPathBenchmark;
})(this);

22
bower_components/observe-js/bower.json vendored Normal file
View File

@@ -0,0 +1,22 @@
{
"name": "observe-js",
"homepage": "https://github.com/Polymer/observe-js",
"authors": [
"The Polymer Authors"
],
"description": "A library for observing Arrays, Objects and PathValues",
"main": "src/observe.js",
"keywords": [
"Object.observe"
],
"license": "BSD",
"private": true,
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
],
"version": "0.5.1"
}

View File

@@ -0,0 +1,4 @@
# This file is used by gcl to get repository specific information.
CODE_REVIEW_SERVER: https://codereview.appspot.com
VIEW_VC: https://github.com/Polymer/observe-js/commit/

View File

@@ -0,0 +1,32 @@
/*
* @license
* Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
module.exports = function(karma) {
var common = require('../../tools/test/karma-common.conf.js');
karma.set(common.mixin_common_opts(karma, {
// base path, that will be used to resolve files and exclude
basePath: '../',
// list of files / patterns to load in the browser
files: [
'node_modules/chai/chai.js',
'conf/mocha.conf.js',
'src/observe.js',
'util/array_reduction.js',
'tests/*.js'
],
// list of files to exclude
exclude: [
'tests/d8_array_fuzzer.js',
'tests/d8_planner_test.js'
],
}));
};

View File

@@ -0,0 +1,15 @@
/*
* @license
* Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
mocha.setup({
ui:'tdd',
ignoreLeaks: true
});
var assert = chai.assert;

View File

@@ -0,0 +1,63 @@
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<script src="../change_summary.js"></script>
<script src="constrain.js"></script>
<script src="persist.js"></script>
<script src="mdv-object-observe/include.js"></script>
<h1>Circles</h1>
<div data-controller="CircleController">
<template iterate>
<div style="border: 1px solid black; margin: 8px">
<table>
<tr><td>radius:</td><td><input type="number" value="{{ radius }}"></td></tr>
<tr><td>area:</td><td><input type="number" value="{{ area }}"></td></tr>
<tr><td>circumference:</td><td><input type="number" value="{{ circumference }}"></td></tr>
</table>
<button data-action="click:delete">Delete</button>
</div>
</template>
<button data-action="click:add">New</button>
</div>
<script>
function Circle(radius) {
// circumference = 2*PI*radius
constrain(this, {
radius: function() { return this.circumference / (2*Math.PI); },
circumference: function() { return 2 * Math.PI * this.radius; }
});
// area = PI*r^2'
constrain(this, {
area: function() { return Math.PI * Math.pow(this.radius, 2); },
radius: function() { return Math.sqrt(this.area / Math.PI); }
});
if (radius)
this.radius = radius;
}
function CircleController(elm) {
this.circles = elm.model = persistDB.retrieve(Circle);
}
CircleController.prototype = {
delete: function(circle) {
var index = this.circles.indexOf(circle);
this.circles.splice(index, 1);
},
add: function() {
this.circles.push(new Circle());
}
}
</script>

View File

@@ -0,0 +1,29 @@
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<h1>The world's simplest constraint-solver</h1>
<script src="constrain.js"></script>
<script>
function Circle(radius) {
// circumference = 2*PI*radius
constrain(this, {
radius: function() { return this.circumference / (2*Math.PI); },
circumference: function() { return 2 * Math.PI * this.radius; }
});
// area = PI*r^2'
constrain(this, {
area: function() { return Math.PI * Math.pow(this.radius, 2); },
radius: function() { return Math.sqrt(this.area / Math.PI); }
});
if (radius)
this.radius = radius;
}
</script>

View File

@@ -0,0 +1,403 @@
/*
* Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
(function(global) {
/* This is a very simple version of the QuickPlan algorithm for solving
* mutli-variable contraints. (http://www.cs.utk.edu/~bvz/quickplan.html)
* The implementation varies from the standard described approach in a few ways:
*
* -There is no notion of constraint heirarchy. Here, all constraints are
* considered REQUIRED.
*
* -There is no "improvement" phase where rejected constraints are added back
* in an attempt to find a "better solution"
*
* -In place of the above two, a heuristic is used to pick the "weakest"
* free constraint to remove. A function, "stayFunc" is passed to the
* Variable class and is expected to return a priority value for the variable
* 0 being highest and 1, 2, 3, etc... being lower.
*
* I suspect these variations result in there being no guarentee of choosing the
* optimal solution, but it does seem to work well for the examples I've tested.
* Note also that the DeltaBlue planner can be used in a similar pattern,
* but it only supports single variable assignment.
*
* Note also that this is hacky and thrown together. Don't expect it to work
* much at all =-).
*/
function Map() {
this.map_ = new global.Map;
this.keys_ = [];
}
Map.prototype = {
get: function(key) {
return this.map_.get(key);
},
set: function(key, value) {
if (!this.map_.has(key))
this.keys_.push(key);
return this.map_.set(key, value);
},
has: function(key) {
return this.map_.has(key);
},
delete: function(key) {
this.keys_.splice(this.keys_.indexOf(key), 1);
this.map_.delete(key);
},
keys: function() {
return this.keys_.slice();
}
}
function Variable(property, stayFunc) {
this.property = property;
this.stayFunc = stayFunc || function() {
//console.log("Warning: using default stay func");
return 0;
};
this.methods = [];
};
Variable.prototype = {
addMethod: function(method) {
this.methods.push(method);
},
removeMethod: function(method) {
this.methods.splice(this.methods.indexOf(method), 1);
},
isFree: function() {
return this.methods.length <= 1;
},
get stayPriority() {
return this.stayFunc(this.property);
}
}
function Method(opts) {
opts = opts || {};
this.name = opts.name || 'function() { ... }';
this.outputs = opts.outputs || [];
this.f = opts.f || function() {
console.log('Warning: using default execution function');
};
};
Method.prototype = {
planned_: false,
variables_: [],
set planned(planned) {
this.planned_ = planned;
if (this.planned_) {
if (this.variables_) {
// Remove this method from all variables.
this.variables_.forEach(function(variable) {
variable.removeMethod(this);
}, this);
}
this.variables_ = null;
} else {
this.variables_ = null;
// Get & add this method to all variables.
if (this.constraint && this.constraint.planner) {
this.variables_ = this.outputs.map(function(output) {
var variable = this.constraint.planner.getVariable(output);
variable.addMethod(this);
return variable;
}, this);
}
}
},
get planned() {
return this.planned_;
},
isFree: function() {
// Return true only if all variables are free.
var variables = this.variables_;
for (var i = variables.length - 1; i >= 0; i--) {
if (!variables[i].isFree())
return false;
}
return true;
},
weakerOf: function(other) {
if (!other) {
return this;
}
// Prefer a method that assigns to fewer variables.
if (this.variables_.length != other.variables_.length) {
return this.variables_.length < other.variables_.length ? this : other;
}
// Note: A weaker stay priority is a higher number.
return this.getStayPriority() >= other.getStayPriority() ? this : other;
},
getStayPriority: function() {
// This returns the strongest (lowest) stay priority of this method's
// output variables.
return retval = this.variables_.reduce(function(min, variable) {
return Math.min(min, variable.stayPriority);
}, Infinity);
},
execute: function() {
console.log(JSON.stringify(this.outputs) + ' <= ' + this.name);
this.f();
}
};
function Constraint(methods, when) {
this.methods = methods;
this.when = when;
};
Constraint.prototype = {
executionMethod_: null,
set executionMethod(executionMethod) {
this.executionMethod_ = executionMethod;
var planned = !!this.executionMethod_;
this.methods.forEach(function(method) {
method.constraint = this;
method.planned = planned;
}, this);
},
get executionMethod() {
return this.executionMethod_;
},
getWeakestFreeMethod: function() {
var methods = this.methods;
var weakest = null;
for (var i = 0; i < methods.length; i++) {
var method = methods[i];
if (method.isFree())
weakest = method.weakerOf(weakest);
}
return weakest;
},
execute: function() {
this.executionMethod.execute();
}
};
function Planner(object) {
this.object = object;
this.properties = {};
this.priority = []
var self = this;
this.stayFunc = function(property) {
if (self.object[property] === undefined)
return Infinity;
var index = self.priority.indexOf(property);
return index >= 0 ? index : Infinity;
}
Object.observe(this.object, internalCallback);
};
Planner.prototype = {
plan_: null,
deliverChanged: function(changeRecords) {
var needsResolve = false;
changeRecords.forEach(function(change) {
var property = change.name;
if (!(property in this.properties))
return;
var index = this.priority.indexOf(property);
if (index >= 0)
this.priority.splice(this.priority.indexOf(property), 1);
this.priority.unshift(property);
needsResolve = true;
}, this);
if (!needsResolve)
return;
console.log('Resolving: ' + Object.getPrototypeOf(changeRecords[0].object).constructor.name);
Object.unobserve(this.object, internalCallback);
this.execute();
console.log('...Done: ' + JSON.stringify(this.object));
Object.observe(this.object, internalCallback);
},
addConstraint: function(methods) {
methods.forEach(function(method) {
method.outputs.forEach(function(output) {
this.properties[output] = true;
}, this);
}, this);
var constraint = new Constraint(methods);
this.constraints = this.constraints || [];
if (this.constraints.indexOf(constraint) < 0) {
this.plan_ = null;
this.constraints.push(constraint);
}
return constraint;
},
removeConstraint: function(constraint) {
var index = this.constraints.indexOf(constraint);
if (index >= 0) {
this.plan_ = null;
var removed = this.constraints.splice(index, 1)[0];
}
return constraint;
},
getVariable: function(property) {
var index = this.properties_.indexOf(property);
if (index >= 0) {
return this.variables_[index];
}
this.properties_.push(property);
var variable = new Variable(property, this.stayFunc);
this.variables_.push(variable);
return variable;
},
get plan() {
if (this.plan_) {
return this.plan_;
}
this.plan_ = [];
this.properties_ = [];
this.variables_ = [];
var unplanned = this.constraints.filter(function(constraint) {
// Note: setting executionMethod must take place after setting planner.
if (constraint.when && !constraint.when()) {
// Conditional and currenty disabled => not in use.
constraint.planner = null;
constraint.executionMethod = null;
return false;
} else {
// In use.
constraint.planner = this;
constraint.executionMethod = null;
return true;
}
}, this);
while (unplanned.length > 0) {
var method = this.chooseNextMethod(unplanned);
if (!method) {
throw "Cycle detected";
}
var nextConstraint = method.constraint;
unplanned.splice(unplanned.indexOf(nextConstraint), 1);
this.plan_.unshift(nextConstraint);
nextConstraint.executionMethod = method;
}
return this.plan_;
},
chooseNextMethod: function(constraints) {
var weakest = null;
for (var i = 0; i < constraints.length; i++) {
var current = constraints[i].getWeakestFreeMethod();
weakest = current ? current.weakerOf(weakest) : weakest;
}
return weakest;
},
run: function() {
this.execute();
},
execute: function() {
this.plan_ = null;
this.executing = true;
this.plan.forEach(function(constraint) {
constraint.execute();
});
this.executing = false;
}
}
var planners = new WeakMap;
function internalCallback(changeRecords) {
var changeMap = new Map;
changeRecords.forEach(function(change) {
if (!planners.has(change.object))
return;
var changes = changeMap.get(change.object);
if (!changes) {
changeMap.set(change.object, [change]);
return;
}
changes.push(change);
});
changeMap.keys().forEach(function(object) {
planners.get(object).deliverChanged(changeMap.get(object));
});
}
// Register callback to assign delivery order.
var register = {};
Object.observe(register, internalCallback);
Object.unobserve(register, internalCallback);
global.constrain = function(obj, methodFunctions) {
var planner = planners.get(obj);
if (!planner) {
planner = new Planner(obj);
planners.set(obj, planner);
}
planner.addConstraint(Object.keys(methodFunctions).map(function(property) {
var func = methodFunctions[property];
return new Method({
name: func.toString(),
outputs: [ property ],
f: function() { obj[property] = func.apply(obj); }
});
}));
}
})(this);

View File

@@ -0,0 +1,13 @@
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<h1>The worlds simplest persistence system</h1>
<script src="../change_summary.js"></script>
<script src="persist.js"></script>

View File

@@ -0,0 +1,246 @@
/*
* Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
(function(global) {
function Set() {
this.set_ = new global.Set;
this.keys_ = [];
}
Set.prototype = {
add: function(key) {
if (!this.set_.has(key))
this.keys_.push(key);
return this.set_.add(key);
},
has: function(key) {
return this.set_.has(key);
},
delete: function(key) {
this.keys_.splice(this.keys_.indexOf(key), 1);
this.set_.delete(key);
},
keys: function() {
return this.keys_.slice();
}
}
var dbName = 'PersistObserved';
var version;
var db;
var storeNames = {};
function constructorName(objOrFunction) {
if (typeof objOrFunction == 'function')
return objOrFunction.name;
else
return Object.getPrototypeOf(objOrFunction).constructor.name;
}
function getKeyPath(constructor) {
return constructor.keyPath || 'id';
}
function onerror(e) {
console.log('Error: ' + e);
};
var postOpen = [];
function openDB() {
var request = webkitIndexedDB.open(dbName);
request.onerror = onerror;
request.onsuccess = function(e) {
db = e.target.result;
version = db.version || 0;
for (var i = 0; i < db.objectStoreNames.length; i++)
storeNames[db.objectStoreNames.item(i)] = true;
postOpen.forEach(function(action) {
action();
});
};
}
function handleChanged(changeRecords) {
changeRecords.forEach(function(change) {
persist(change.object);
});
}
var observer = new ChangeSummary(function(summaries) {
storeChanges = {};
function getChange(obj) {
var change = storeChanges[constructorName(obj)];
if (change)
return change;
change = {
keyPath: getKeyPath(obj),
needsAdd: new Set,
needsSave: new Set,
needsDelete: new Set
};
storeChanges[storeName] = change;
return change;
}
summaries.forEach(function(summary) {
if (!Array.isArray(summary.object)) {
getChange(summary.object).needsSave.add(summary.object);
return;
}
summary.arraySplices.forEach(function(splice) {
for (var i = 0; i < splice.removed.length; i++) {
var obj = splice.removed[i];
var change = getChange(obj);
if (change.needsAdd.has(obj))
change.needsAdd.delete(obj);
else
change.needsDelete.add(obj);
}
for (var i = splice.index; i < splice.index + splice.addedCount; i++) {
var obj = summary.object[i];
var change = getChange(obj);
if (change.needsDelete.has(obj))
change.needsDelete.delete(obj);
else
change.needsAdd.add(obj);
}
});
});
var storeNames = Object.keys(storeChanges);
console.log('Persisting: ' + JSON.stringify(storeNames));
var trans = db.transaction(storeNames, "readwrite");
trans.onerror = onerror;
trans.oncomplete = function() {
console.log('...complete');
}
storeNames.forEach(function(storeName) {
var change = storeChanges[storeName];
var store = trans.objectStore(storeName);
change.needsDelete.keys().forEach(function(obj) {
var request = store.delete(obj[change.keyPath]);
request.onerror = onerror;
request.onsuccess = function(e) {
console.log(' deleted: ' + JSON.stringify(obj));
delete obj[keyPath];
observer.unobserve(obj);
if (change.needsSave.has(obj))
change.needsSave.delete(obj);
};
});
change.needsSave.keys().forEach(function(obj) {
var request = store.put(obj);
request.onerror = onerror;
request.onsuccess = function(e) {
console.log(' saved: ' + JSON.stringify(obj));
};
});
change.needsAdd.keys().forEach(function(obj) {
obj[keyPath] = ++maxIds[storeName];
var request = store.put(obj);
request.onerror = onerror;
request.onsuccess = function(e) {
console.log(' created: ' + JSON.stringify(obj));
observer.observe(obj);
};
});
});
});
var maxIds = {};
global.persistDB = {};
global.persistDB.retrieve = function(constructor) {
var results = [];
var instance = new constructor();
keyPath = constructor.keyPath || 'id';
storeName = constructor.name;
maxIds[storeName] = maxIds[storeName] || 0;
function doRetrieve() {
console.log("Retrieving: " + storeName);
var trans = db.transaction([storeName]);
var store = trans.objectStore(storeName);
var keyRange = webkitIDBKeyRange.lowerBound(0);
var request = store.openCursor(keyRange);
request.onerror = onerror;
request.onsuccess = function(e) {
var result = e.target.result;
if (!!result == false) {
observer.observePropertySet(results);
console.log('...complete');
return;
}
var object = result.value;
maxIds[storeName] = Math.max(maxIds[storeName], object[keyPath]);
object.__proto__ = instance;
constructor.apply(object);
results.push(object);
observer.observe(object);
console.log(' => ' + JSON.stringify(object));
result.continue();
};
}
function createStore() {
console.log('Creating store: ' + storeName);
version++;
var request = db.setVersion(version);
request.onerror = onerror;
request.onsuccess = function(e) {
var store = db.createObjectStore(storeName, { keyPath: keyPath });
storeNames[storeName] = true;
e.target.transaction.oncomplete = doRetrieve;
};
}
var action = function() {
if (storeName in storeNames)
doRetrieve()
else
createStore();
}
if (db)
action();
else
postOpen.push(action);
return results;
};
openDB();
})(this);

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
module.exports = function(grunt) {
grunt.initConfig({
karma: {
options: {
configFile: 'conf/karma.conf.js',
keepalive: true
},
buildbot: {
reporters: ['crbot'],
logLevel: 'OFF'
},
'observe-js': {
}
}
});
grunt.loadTasks('../tools/tasks');
grunt.loadNpmTasks('grunt-karma');
grunt.registerTask('default', 'test');
grunt.registerTask('test', ['override-chrome-launcher', 'karma:observe-js']);
grunt.registerTask('test-buildbot', ['override-chrome-launcher', 'karma:buildbot']);
};

73
bower_components/observe-js/index.html vendored Normal file
View File

@@ -0,0 +1,73 @@
<!--
@license
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!doctype html>
<html>
<head>
<title>polymer api</title>
<style>
html, body {
font-family: Arial, sans-serif;
white-space: nowrap;
overflow: hidden;
}
[noviewer] [ifnoviewer] {
display: block;
}
[detector], [ifnoviewer], [noviewer] [ifviewer] {
display: none;
}
[ifviewer], [ifnoviewer] {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
iframe {
border: none;
margin: 0;
width: 100%;
height: 100%;
}
#remote {
position: absolute;
top: 0;
right: 0;
}
</style>
<script src="../webcomponentsjs/webcomponents.js"></script>
<link rel="import" href="../polymer-home-page/polymer-home-page.html">
</head>
<body>
<img detector src="../polymer-home-page/bowager-logo.png" onerror="noviewer()">
<polymer-home-page ifviewer></polymer-home-page>
<div ifnoviewer>
<span id="remote">[remote]</span>
<iframe></iframe>
</div>
<!-- -->
<script>
var remoteDocs = 'http://turbogadgetry.com/bowertopia/components/';
// if no local info viewer, load it remotely
function noviewer() {
document.body.setAttribute('noviewer', '');
var path = location.pathname.split('/');
var module = path.pop() || path.pop();
document.querySelector('iframe').src = remoteDocs + module;
document.querySelector('title').textContent = module;
}
// for testing only
var opts = window.location.search;
if (opts.indexOf('noviewer') >= 0) {
noviewer();
}
</script>
</body>
</html>

View File

@@ -0,0 +1,33 @@
{
"name": "observe-js",
"version": "0.4.2",
"description": "observe-js is a library for observing changes on JavaScript objects/arrays",
"main": "src/observe.js",
"directories": {
"example": "examples",
"test": "tests"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://github.com/Polymer/observe-js.git"
},
"author": "The Polymer Authors",
"license": "BSD",
"readmeFilename": "README.md",
"devDependencies": {
"chai": "*",
"mocha": "*",
"grunt": "*",
"grunt-karma": "*",
"karma": "~0.12.0",
"karma-mocha": "*",
"karma-firefox-launcher": "*",
"karma-ie-launcher": "*",
"karma-safari-launcher": "*",
"karma-script-launcher": "*",
"karma-crbot-reporter": "*"
}
}

1711
bower_components/observe-js/src/observe.js vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,309 @@
// Copyright 2012 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
(function(global) {
"use strict";
function ArraySet() {
this.entries = [];
}
ArraySet.prototype = {
add: function(key) {
if (this.entries.indexOf(key) >= 0)
return;
this.entries.push(key);
},
delete: function(key) {
var i = this.entries.indexOf(key);
if (i < 0)
return;
this.entries.splice(i, 1);
},
first: function() {
return this.entries[0];
},
get size() {
return this.entries.length;
}
};
function UIDSet() {
this.entries = {};
this.size = 0;
}
UIDSet.prototype = {
add: function(key) {
if (this.entries[key.__UID__] !== undefined)
return;
this.entries[key.__UID__] = key;
this.size++;
},
delete: function(key) {
if (this.entries[key.__UID__] === undefined)
return;
this.entries[key.__UID__] = undefined;
this.size--;
}
};
function Heap(scoreFunction, populate) {
this.scoreFunction = scoreFunction;
this.content = populate || [];
if (this.content.length)
this.build();
}
Heap.prototype = {
get size() {
return this.content.length;
},
build: function() {
var lastNonLeaf = Math.floor(this.content.length / 2) - 1;
for (var i = lastNonLeaf; i >= 0; i--)
this.sinkDown(i);
},
push: function(element) {
this.content.push(element);
this.bubbleUp(this.content.length - 1);
},
pop: function() {
var result = this.content[0];
var end = this.content.pop();
if (this.content.length) {
this.content[0] = end;
this.sinkDown(0);
}
return result;
},
delete: function(element) {
var len = this.content.length;
for (var i = 0; i < len; i++) {
if (this.content[i] == element) {
var end = this.content.pop();
if (i != len - 1) {
this.content[i] = end;
if (this.scoreFunction(end) < this.scoreFunction(node)) this.bubbleUp(i);
else this.sinkDown(i);
}
return;
}
}
},
bubbleUp: function(n) {
var element = this.content[n];
while (n > 0) {
var parentN = Math.floor((n + 1) / 2) - 1,
parent = this.content[parentN];
if (this.scoreFunction(element) <= this.scoreFunction(parent))
break;
this.content[parentN] = element;
this.content[n] = parent;
n = parentN;
}
},
sinkDown: function(n) {
var length = this.content.length,
element = this.content[n],
elemScore = this.scoreFunction(element);
do {
var child2N = (n + 1) * 2
var child1N = child2N - 1;
var swap = null;
var swapScore = elemScore;
if (child1N < length) {
var child1 = this.content[child1N],
child1Score = this.scoreFunction(child1);
if (child1Score > elemScore) {
swap = child1N;
swapScore = child1Score;
}
}
if (child2N < length) {
var child2 = this.content[child2N],
child2Score = this.scoreFunction(child2);
if (child2Score > swapScore)
swap = child2N;
}
if (swap != null) {
this.content[n] = this.content[swap];
this.content[swap] = element;
n = swap;
}
} while (swap != null);
}
};
function Variable(stayFunc) {
this.stayFunc = stayFunc;
this.methods = new ArraySet;
};
Variable.prototype = {
freeMethod: function() {
return this.methods.first();
}
}
function Method(constraint, variable) {
this.constraint = constraint;
this.variable = variable;
};
function Constraint(planner) {
this.planner = planner;
this.methods = [];
};
Constraint.prototype = {
addMethod: function(variable) {
var method = new Method(this, variable);
this.methods.push(method);
method.__UID__ = this.planner.methodUIDCounter++;
return method;
},
reset: function() {
this.methods.forEach(function(method) {
method.variable.methods.add(method);
});
},
remove: function() {
this.methods.forEach(function(method) {
method.variable.methods.delete(method);
});
}
};
function Planner() {
this.variables = [];
this.constraints = [];
this.variableUIDCounter = 1;
this.methodUIDCounter = 1;
};
Planner.prototype = {
addVariable: function(stayFunc) {
var variable = new Variable(stayFunc);
variable.__UID__ = this.variableUIDCounter++;
this.variables.push(variable);
return variable;
},
addConstraint: function() {
var constraint = new Constraint(this);
this.constraints.push(constraint);
return constraint;
},
removeConstraint: function(constraint) {
var index = this.constraints.indexOf(constraint);
if (index < 0)
return;
constraint.remove();
this.constraints.splice(index, 1);
this.constraints.forEach(function(constraint) {
constraint.reset();
});
this.variables = this.variables.filter(function(variable) {
return variable.methods.size;
});
},
getPlan: function() {
this.variables.forEach(function(variable) {
variable.priority = variable.stayFunc();
});
this.constraints.forEach(function(constraint) {
constraint.reset();
});
var methods = [];
var free = [];
var overconstrained = new UIDSet;
this.variables.forEach(function(variable) {
var methodCount = variable.methods.size;
if (methodCount > 1)
overconstrained.add(variable);
else if (methodCount == 1)
free.push(variable);
});
free = new Heap(function(variable) {
return variable.priority;
}, free);
while (free.size) {
var lowest;
do {
lowest = free.pop();
} while (free.size && !lowest.methods.size);
if (!lowest.methods.size)
break;
var method = lowest.freeMethod();
var constraint = method.constraint;
constraint.remove();
constraint.methods.forEach(function(method) {
var variable = method.variable;
if (variable.methods.size == 1) {
overconstrained.delete(variable);
free.push(variable);
}
});
methods.push(method);
}
if (overconstrained.size)
return undefined;
return methods.reverse();
}
}
global.Planner = Planner;
})(this);