ende 0.5.20 → 0.5.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/component.json +1 -1
- data/components/indefinido/indemma/master/.gitignore +17 -0
- data/components/indefinido/indemma/master/.ruby-gemset +1 -0
- data/components/indefinido/indemma/master/.ruby-version +1 -0
- data/components/indefinido/indemma/master/Gemfile +12 -0
- data/components/indefinido/indemma/master/Guardfile +39 -0
- data/components/indefinido/indemma/master/History.md +0 -0
- data/components/indefinido/indemma/master/Readme.md +445 -0
- data/components/indefinido/indemma/master/build/development.js +26123 -0
- data/components/indefinido/indemma/master/build/release.js +16741 -0
- data/components/indefinido/indemma/master/build/test.js +26123 -0
- data/components/indefinido/indemma/master/component.json +1 -1
- data/components/indefinido/indemma/master/components/chaijs/assertion-error/1.0.0/component.json +18 -0
- data/components/indefinido/indemma/master/components/chaijs/assertion-error/1.0.0/index.js +110 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/.gitignore +22 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/.mailmap +1 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/.npmignore +14 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/.travis.yml +14 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/CONTRIBUTING.md +173 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/History.md +895 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/Makefile +93 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/README.md +99 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/ReleaseNotes.md +482 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/bower.json +27 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/chai.js +4782 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/component.json +50 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/index.js +1 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/karma.conf.js +28 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/karma.sauce.js +41 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/assertion.js +130 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/config.js +50 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/core/assertions.js +1314 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/interface/assert.js +1056 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/interface/expect.js +12 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/interface/should.js +78 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/addChainableMethod.js +111 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/addMethod.js +43 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/addProperty.js +40 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/flag.js +32 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/getActual.js +18 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/getEnumerableProperties.js +25 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/getMessage.js +49 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/getName.js +20 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/getPathValue.js +102 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/getProperties.js +35 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/index.js +114 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/inspect.js +320 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/objDisplay.js +49 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/overwriteChainableMethod.js +53 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/overwriteMethod.js +51 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/overwriteProperty.js +54 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/test.js +26 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/transferFlags.js +44 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/type.js +45 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai.js +87 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/package.json +42 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/sauce.browsers.js +128 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/support/sauce/sauce_connect_block.sh +7 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/support/sauce/sauce_connect_setup.sh +53 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/test/assert.js +638 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/test/auth/.gitkeep +0 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/test/bootstrap/index.js +22 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/test/bootstrap/karma.js +22 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/test/configuration.js +133 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/test/display/errors.js +14 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/test/display/message.js +47 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/test/expect.js +814 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/test/globalShould.js +15 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/test/plugins.js +24 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/test/should.js +744 -0
- data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/test/utilities.js +309 -0
- data/components/indefinido/indemma/master/components/chaijs/deep-eql/0.1.3/component.json +20 -0
- data/components/indefinido/indemma/master/components/chaijs/deep-eql/0.1.3/lib/eql.js +257 -0
- data/components/indefinido/indemma/master/components/chaijs/type-detect/0.1.1/component.json +18 -0
- data/components/indefinido/indemma/master/components/chaijs/type-detect/0.1.1/lib/type.js +142 -0
- data/components/indefinido/indemma/master/components/component/bind/1.0.0/component.json +14 -0
- data/components/indefinido/indemma/master/components/component/bind/1.0.0/index.js +23 -0
- data/components/indefinido/indemma/master/components/component/jquery/1.0.0/component.json +10 -0
- data/components/indefinido/indemma/master/components/component/jquery/1.0.0/index.js +10308 -0
- data/components/indefinido/indemma/master/components/component/type/1.0.0/component.json +20 -0
- data/components/indefinido/indemma/master/components/component/type/1.0.0/index.js +32 -0
- data/components/indefinido/indemma/master/components/indefinido/advisable/master/component.json +21 -0
- data/components/indefinido/indemma/master/components/indefinido/advisable/master/index.js +1 -0
- data/components/indefinido/indemma/master/components/indefinido/advisable/master/lib/advisable.js +60 -0
- data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/component.json +39 -0
- data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/index.js +1 -0
- data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/lib/adapters/rivets.js +40 -0
- data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/lib/legacy/notifierable.js +145 -0
- data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/lib/legacy/schedulerable.js +147 -0
- data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/lib/lookup.js +38 -0
- data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/lib/observable/keypath_observer.js +38 -0
- data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/lib/observable/observation.js +45 -0
- data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/lib/observable/selection.js +57 -0
- data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/lib/observable/self_observer.js +38 -0
- data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/lib/observable.js +97 -0
- data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/lib/platform.js +12 -0
- data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/vendor/observe-js/observe.js +1631 -0
- data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/vendor/shims/accessors-legacy.js +92 -0
- data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/vendor/shims/accessors.js +248 -0
- data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/vendor/shims/array.indexOf.js +8 -0
- data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/vendor/shims/object.create.js +77 -0
- data/components/indefinido/indemma/master/components/pluma/assimilate/0.4.0/component.json +25 -0
- data/components/indefinido/indemma/master/components/pluma/assimilate/0.4.0/dist/assimilate.js +127 -0
- data/components/indefinido/indemma/master/karma.conf.js +75 -0
- data/components/indefinido/indemma/master/lib/record/associable.js +4 -2
- data/components/indefinido/indemma/master/lib/record/dirtyable.js +1 -1
- data/components/indefinido/indemma/master/package.json +9 -0
- data/components/indefinido/indemma/master/spec/record/associable_spec.js +211 -0
- data/components/indefinido/indemma/master/spec/record/dirtyable_spec.js +57 -0
- data/components/indefinido/indemma/master/spec/record/maid_spec.js +40 -0
- data/components/indefinido/indemma/master/spec/record/persistable_spec.js +46 -0
- data/components/indefinido/indemma/master/spec/record/queryable_spec.js +39 -0
- data/components/indefinido/indemma/master/spec/record/resource_spec.js +93 -0
- data/components/indefinido/indemma/master/spec/record/rest_spec.js +32 -0
- data/components/indefinido/indemma/master/spec/record/restfulable_spec.js +346 -0
- data/components/indefinido/indemma/master/spec/record/scopable_spec.js +242 -0
- data/components/indefinido/indemma/master/spec/record/storable_spec.js +44 -0
- data/components/indefinido/indemma/master/spec/record/translationable.js +28 -0
- data/components/indefinido/indemma/master/spec/record/validatable_spec.js +142 -0
- data/components/indefinido/indemma/master/spec/record/validations/associated_spec.js +43 -0
- data/components/indefinido/indemma/master/spec/record/validations/confirmation_spec.js +36 -0
- data/components/indefinido/indemma/master/spec/record/validations/cpf_spec.js +34 -0
- data/components/indefinido/indemma/master/spec/record/validations/presence_spec.js +28 -0
- data/components/indefinido/indemma/master/spec/record/validations/remote_spec.js +87 -0
- data/components/indefinido/indemma/master/spec/record/validations/type_spec.js +48 -0
- data/components/indefinido/indemma/master/spec/record_spec.js +49 -0
- data/components/indefinido/indemma/master/spec/spec_helper.js +15 -0
- data/components/indefinido/indemma/master/spec/support/value_objects/phone.js +45 -0
- data/components/indefinido/indemma/master/src/lib/extensions/rivets.coffee +17 -0
- data/components/indefinido/indemma/master/src/lib/record/associable.coffee +419 -0
- data/components/indefinido/indemma/master/src/lib/record/dirtyable.coffee +47 -0
- data/components/indefinido/indemma/master/src/lib/record/errors.coffee +20 -0
- data/components/indefinido/indemma/master/src/lib/record/maid.coffee +16 -0
- data/components/indefinido/indemma/master/src/lib/record/persistable.coffee +34 -0
- data/components/indefinido/indemma/master/src/lib/record/queryable.coffee +29 -0
- data/components/indefinido/indemma/master/src/lib/record/resource.coffee +107 -0
- data/components/indefinido/indemma/master/src/lib/record/rest.coffee +34 -0
- data/components/indefinido/indemma/master/src/lib/record/restfulable.coffee +487 -0
- data/components/indefinido/indemma/master/src/lib/record/scopable.coffee +279 -0
- data/components/indefinido/indemma/master/src/lib/record/storable.coffee +49 -0
- data/components/indefinido/indemma/master/src/lib/record/translationable.coffee +18 -0
- data/components/indefinido/indemma/master/src/lib/record/validatable.coffee +241 -0
- data/components/indefinido/indemma/master/src/lib/record/validations/associated.coffee +34 -0
- data/components/indefinido/indemma/master/src/lib/record/validations/confirmation.coffee +17 -0
- data/components/indefinido/indemma/master/src/lib/record/validations/cpf.coffee +57 -0
- data/components/indefinido/indemma/master/src/lib/record/validations/presence.coffee +16 -0
- data/components/indefinido/indemma/master/src/lib/record/validations/remote.coffee +61 -0
- data/components/indefinido/indemma/master/src/lib/record/validations/type.coffee +31 -0
- data/components/indefinido/indemma/master/src/lib/record/validations/validatorable.coffee +5 -0
- data/components/indefinido/indemma/master/src/lib/record.coffee +143 -0
- data/components/indefinido/indemma/master/src/spec/record/associable_spec.coffee +205 -0
- data/components/indefinido/indemma/master/src/spec/record/dirtyable_spec.coffee +47 -0
- data/components/indefinido/indemma/master/src/spec/record/maid_spec.coffee +34 -0
- data/components/indefinido/indemma/master/src/spec/record/persistable_spec.coffee +39 -0
- data/components/indefinido/indemma/master/src/spec/record/queryable_spec.coffee +33 -0
- data/components/indefinido/indemma/master/src/spec/record/resource_spec.coffee +69 -0
- data/components/indefinido/indemma/master/src/spec/record/rest_spec.coffee +22 -0
- data/components/indefinido/indemma/master/src/spec/record/restfulable_spec.coffee +257 -0
- data/components/indefinido/indemma/master/src/spec/record/scopable_spec.coffee +227 -0
- data/components/indefinido/indemma/master/src/spec/record/storable_spec.coffee +39 -0
- data/components/indefinido/indemma/master/src/spec/record/translationable.coffee +19 -0
- data/components/indefinido/indemma/master/src/spec/record/validatable_spec.coffee +127 -0
- data/components/indefinido/indemma/master/src/spec/record/validations/associated_spec.coffee +35 -0
- data/components/indefinido/indemma/master/src/spec/record/validations/confirmation_spec.coffee +25 -0
- data/components/indefinido/indemma/master/src/spec/record/validations/cpf_spec.coffee +27 -0
- data/components/indefinido/indemma/master/src/spec/record/validations/presence_spec.coffee +24 -0
- data/components/indefinido/indemma/master/src/spec/record/validations/remote_spec.coffee +74 -0
- data/components/indefinido/indemma/master/src/spec/record/validations/type_spec.coffee +33 -0
- data/components/indefinido/indemma/master/src/spec/record_spec.coffee +29 -0
- data/components/indefinido/indemma/master/src/spec/spec_helper.coffee +11 -0
- data/components/indefinido/indemma/master/src/spec/support/value_objects/phone.coffee +30 -0
- data/components/indefinido/indemma/master/vendor/object/mixin.js +196 -0
- data/components/indefinido/indemma/master/vendor/spec/sinon/assert.js +199 -0
- data/components/indefinido/indemma/master/vendor/spec/sinon/behavior.js +334 -0
- data/components/indefinido/indemma/master/vendor/spec/sinon/call.js +205 -0
- data/components/indefinido/indemma/master/vendor/spec/sinon/collection.js +155 -0
- data/components/indefinido/indemma/master/vendor/spec/sinon/match.js +245 -0
- data/components/indefinido/indemma/master/vendor/spec/sinon/mock.js +451 -0
- data/components/indefinido/indemma/master/vendor/spec/sinon/sandbox.js +144 -0
- data/components/indefinido/indemma/master/vendor/spec/sinon/spy.js +413 -0
- data/components/indefinido/indemma/master/vendor/spec/sinon/stub.js +161 -0
- data/components/indefinido/indemma/master/vendor/spec/sinon/test.js +77 -0
- data/components/indefinido/indemma/master/vendor/spec/sinon/test_case.js +99 -0
- data/components/indefinido/indemma/master/vendor/spec/sinon/util/event.js +94 -0
- data/components/indefinido/indemma/master/vendor/spec/sinon/util/fake_server.js +212 -0
- data/components/indefinido/indemma/master/vendor/spec/sinon/util/fake_server_with_clock.js +83 -0
- data/components/indefinido/indemma/master/vendor/spec/sinon/util/fake_timers.js +405 -0
- data/components/indefinido/indemma/master/vendor/spec/sinon/util/fake_xdomain_request.js +206 -0
- data/components/indefinido/indemma/master/vendor/spec/sinon/util/fake_xml_http_request.js +575 -0
- data/components/indefinido/indemma/master/vendor/spec/sinon/util/timers_ie.js +31 -0
- data/components/indefinido/indemma/master/vendor/spec/sinon/util/xdr_ie.js +14 -0
- data/components/indefinido/indemma/master/vendor/spec/sinon/util/xhr_ie.js +19 -0
- data/components/indefinido/indemma/master/vendor/spec/sinon.js +383 -0
- data/lib/assets/javascripts/value_objects/phone.js.coffee +3 -1
- data/lib/assets/javascripts/widgets/viewer/plugins/scopable.js.coffee +5 -5
- data/lib/ende/version.rb +1 -1
- data/vendor/assets/components/ende_build.css +4 -4
- data/vendor/assets/components/ende_build.js +7974 -7889
- metadata +191 -5
- data/vendor/assets/component/build.css +0 -112
- data/vendor/assets/components/build.css +0 -112
- data/vendor/assets/components/indemma_with_none.js +0 -30553
|
@@ -0,0 +1,1631 @@
|
|
|
1
|
+
// Copyright 2012 Google Inc.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
(function(global) {
|
|
16
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
// Detect and do basic sanity checking on Object/Array.observe.
|
|
19
|
+
function detectObjectObserve() {
|
|
20
|
+
if (typeof Object.observe !== 'function' ||
|
|
21
|
+
typeof Array.observe !== 'function') {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
var records = [];
|
|
26
|
+
|
|
27
|
+
function callback(recs) {
|
|
28
|
+
records = recs;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
var test = {};
|
|
32
|
+
var arr = [];
|
|
33
|
+
Object.observe(test, callback);
|
|
34
|
+
Array.observe(arr, callback);
|
|
35
|
+
test.id = 1;
|
|
36
|
+
test.id = 2;
|
|
37
|
+
delete test.id;
|
|
38
|
+
arr.push(1, 2);
|
|
39
|
+
arr.length = 0;
|
|
40
|
+
|
|
41
|
+
Object.deliverChangeRecords(callback);
|
|
42
|
+
if (records.length !== 5)
|
|
43
|
+
return false;
|
|
44
|
+
|
|
45
|
+
if (records[0].type != 'add' ||
|
|
46
|
+
records[1].type != 'update' ||
|
|
47
|
+
records[2].type != 'delete' ||
|
|
48
|
+
records[3].type != 'splice' ||
|
|
49
|
+
records[4].type != 'splice') {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
Object.unobserve(test, callback);
|
|
54
|
+
Array.unobserve(arr, callback);
|
|
55
|
+
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
var hasObserve = detectObjectObserve();
|
|
60
|
+
|
|
61
|
+
function detectEval() {
|
|
62
|
+
// Don't test for eval if we're running in a Chrome App environment.
|
|
63
|
+
// We check for APIs set that only exist in a Chrome App context.
|
|
64
|
+
if (typeof chrome !== 'undefined' && chrome.app && chrome.app.runtime) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
var f = new Function('', 'return true;');
|
|
70
|
+
return f();
|
|
71
|
+
} catch (ex) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
var hasEval = detectEval();
|
|
77
|
+
|
|
78
|
+
function isIndex(s) {
|
|
79
|
+
return +s === s >>> 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function toNumber(s) {
|
|
83
|
+
return +s;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function isObject(obj) {
|
|
87
|
+
return obj === Object(obj);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
var numberIsNaN = Number.isNaN || function isNaN(value) {
|
|
91
|
+
return typeof value === 'number' && global.isNaN(value);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function areSameValue(left, right) {
|
|
95
|
+
if (left === right)
|
|
96
|
+
return left !== 0 || 1 / left === 1 / right;
|
|
97
|
+
if (numberIsNaN(left) && numberIsNaN(right))
|
|
98
|
+
return true;
|
|
99
|
+
|
|
100
|
+
return left !== left && right !== right;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
var createObject = ('__proto__' in {}) ?
|
|
104
|
+
function(obj) { return obj; } :
|
|
105
|
+
function(obj) {
|
|
106
|
+
var proto = obj.__proto__;
|
|
107
|
+
if (!proto)
|
|
108
|
+
return obj;
|
|
109
|
+
var newObject = Object.create(proto);
|
|
110
|
+
Object.getOwnPropertyNames(obj).forEach(function(name) {
|
|
111
|
+
Object.defineProperty(newObject, name,
|
|
112
|
+
Object.getOwnPropertyDescriptor(obj, name));
|
|
113
|
+
});
|
|
114
|
+
return newObject;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
var identStart = '[\$_a-zA-Z]';
|
|
118
|
+
var identPart = '[\$_a-zA-Z0-9]';
|
|
119
|
+
var ident = identStart + '+' + identPart + '*';
|
|
120
|
+
var elementIndex = '(?:[0-9]|[1-9]+[0-9]+)';
|
|
121
|
+
var identOrElementIndex = '(?:' + ident + '|' + elementIndex + ')';
|
|
122
|
+
var path = '(?:' + identOrElementIndex + ')(?:\\s*\\.\\s*' + identOrElementIndex + ')*';
|
|
123
|
+
var pathRegExp = new RegExp('^' + path + '$');
|
|
124
|
+
|
|
125
|
+
function isPathValid(s) {
|
|
126
|
+
if (typeof s != 'string')
|
|
127
|
+
return false;
|
|
128
|
+
s = s.trim();
|
|
129
|
+
|
|
130
|
+
if (s == '')
|
|
131
|
+
return true;
|
|
132
|
+
|
|
133
|
+
if (s[0] == '.')
|
|
134
|
+
return false;
|
|
135
|
+
|
|
136
|
+
return pathRegExp.test(s);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
var constructorIsPrivate = {};
|
|
140
|
+
|
|
141
|
+
function Path(s, privateToken) {
|
|
142
|
+
if (privateToken !== constructorIsPrivate)
|
|
143
|
+
throw Error('Use Path.get to retrieve path objects');
|
|
144
|
+
|
|
145
|
+
if (s.trim() == '')
|
|
146
|
+
return this;
|
|
147
|
+
|
|
148
|
+
if (isIndex(s)) {
|
|
149
|
+
this.push(s);
|
|
150
|
+
return this;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
s.split(/\s*\.\s*/).filter(function(part) {
|
|
154
|
+
return part;
|
|
155
|
+
}).forEach(function(part) {
|
|
156
|
+
this.push(part);
|
|
157
|
+
}, this);
|
|
158
|
+
|
|
159
|
+
if (hasEval && this.length) {
|
|
160
|
+
this.getValueFrom = this.compiledGetValueFromFn();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// TODO(rafaelw): Make simple LRU cache
|
|
165
|
+
var pathCache = {};
|
|
166
|
+
|
|
167
|
+
function getPath(pathString) {
|
|
168
|
+
if (pathString instanceof Path)
|
|
169
|
+
return pathString;
|
|
170
|
+
|
|
171
|
+
if (pathString == null)
|
|
172
|
+
pathString = '';
|
|
173
|
+
|
|
174
|
+
if (typeof pathString !== 'string')
|
|
175
|
+
pathString = String(pathString);
|
|
176
|
+
|
|
177
|
+
var path = pathCache[pathString];
|
|
178
|
+
if (path)
|
|
179
|
+
return path;
|
|
180
|
+
if (!isPathValid(pathString))
|
|
181
|
+
return invalidPath;
|
|
182
|
+
var path = new Path(pathString, constructorIsPrivate);
|
|
183
|
+
pathCache[pathString] = path;
|
|
184
|
+
return path;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
Path.get = getPath;
|
|
188
|
+
|
|
189
|
+
Path.prototype = createObject({
|
|
190
|
+
__proto__: [],
|
|
191
|
+
valid: true,
|
|
192
|
+
|
|
193
|
+
toString: function() {
|
|
194
|
+
return this.join('.');
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
getValueFrom: function(obj, directObserver) {
|
|
198
|
+
for (var i = 0; i < this.length; i++) {
|
|
199
|
+
if (obj == null)
|
|
200
|
+
return;
|
|
201
|
+
obj = obj[this[i]];
|
|
202
|
+
}
|
|
203
|
+
return obj;
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
iterateObjects: function(obj, observe) {
|
|
207
|
+
for (var i = 0; i < this.length; i++) {
|
|
208
|
+
if (i)
|
|
209
|
+
obj = obj[this[i - 1]];
|
|
210
|
+
if (!isObject(obj))
|
|
211
|
+
return;
|
|
212
|
+
observe(obj, this[0]);
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
compiledGetValueFromFn: function() {
|
|
217
|
+
var accessors = this.map(function(ident) {
|
|
218
|
+
return isIndex(ident) ? '["' + ident + '"]' : '.' + ident;
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
var str = '';
|
|
222
|
+
var pathString = 'obj';
|
|
223
|
+
str += 'if (obj != null';
|
|
224
|
+
var i = 0;
|
|
225
|
+
for (; i < (this.length - 1); i++) {
|
|
226
|
+
var ident = this[i];
|
|
227
|
+
pathString += accessors[i];
|
|
228
|
+
str += ' &&\n ' + pathString + ' != null';
|
|
229
|
+
}
|
|
230
|
+
str += ')\n';
|
|
231
|
+
|
|
232
|
+
pathString += accessors[i];
|
|
233
|
+
|
|
234
|
+
str += ' return ' + pathString + ';\nelse\n return undefined;';
|
|
235
|
+
return new Function('obj', str);
|
|
236
|
+
},
|
|
237
|
+
|
|
238
|
+
setValueFrom: function(obj, value) {
|
|
239
|
+
if (!this.length)
|
|
240
|
+
return false;
|
|
241
|
+
|
|
242
|
+
for (var i = 0; i < this.length - 1; i++) {
|
|
243
|
+
if (!isObject(obj))
|
|
244
|
+
return false;
|
|
245
|
+
obj = obj[this[i]];
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (!isObject(obj))
|
|
249
|
+
return false;
|
|
250
|
+
|
|
251
|
+
obj[this[i]] = value;
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
var invalidPath = new Path('', constructorIsPrivate);
|
|
257
|
+
invalidPath.valid = false;
|
|
258
|
+
invalidPath.getValueFrom = invalidPath.setValueFrom = function() {};
|
|
259
|
+
|
|
260
|
+
var MAX_DIRTY_CHECK_CYCLES = 1000;
|
|
261
|
+
|
|
262
|
+
function dirtyCheck(observer) {
|
|
263
|
+
var cycles = 0;
|
|
264
|
+
while (cycles < MAX_DIRTY_CHECK_CYCLES && observer.check_()) {
|
|
265
|
+
cycles++;
|
|
266
|
+
}
|
|
267
|
+
if (global.testingExposeCycleCount)
|
|
268
|
+
global.dirtyCheckCycleCount = cycles;
|
|
269
|
+
|
|
270
|
+
return cycles > 0;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function objectIsEmpty(object) {
|
|
274
|
+
for (var prop in object)
|
|
275
|
+
return false;
|
|
276
|
+
return true;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function diffIsEmpty(diff) {
|
|
280
|
+
return objectIsEmpty(diff.added) &&
|
|
281
|
+
objectIsEmpty(diff.removed) &&
|
|
282
|
+
objectIsEmpty(diff.changed);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function diffObjectFromOldObject(object, oldObject) {
|
|
286
|
+
var added = {};
|
|
287
|
+
var removed = {};
|
|
288
|
+
var changed = {};
|
|
289
|
+
|
|
290
|
+
for (var prop in oldObject) {
|
|
291
|
+
var newValue = object[prop];
|
|
292
|
+
|
|
293
|
+
if (newValue !== undefined && newValue === oldObject[prop])
|
|
294
|
+
continue;
|
|
295
|
+
|
|
296
|
+
if (!(prop in object)) {
|
|
297
|
+
removed[prop] = undefined;
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (newValue !== oldObject[prop])
|
|
302
|
+
changed[prop] = newValue;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
for (var prop in object) {
|
|
306
|
+
if (prop in oldObject)
|
|
307
|
+
continue;
|
|
308
|
+
|
|
309
|
+
added[prop] = object[prop];
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (Array.isArray(object) && object.length !== oldObject.length)
|
|
313
|
+
changed.length = object.length;
|
|
314
|
+
|
|
315
|
+
return {
|
|
316
|
+
added: added,
|
|
317
|
+
removed: removed,
|
|
318
|
+
changed: changed
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
var eomTasks = [];
|
|
323
|
+
function runEOMTasks() {
|
|
324
|
+
if (!eomTasks.length)
|
|
325
|
+
return false;
|
|
326
|
+
|
|
327
|
+
for (var i = 0; i < eomTasks.length; i++) {
|
|
328
|
+
eomTasks[i]();
|
|
329
|
+
}
|
|
330
|
+
eomTasks.length = 0;
|
|
331
|
+
return true;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
var runEOM = hasObserve ? (function(){
|
|
335
|
+
var eomObj = { pingPong: true };
|
|
336
|
+
var eomRunScheduled = false;
|
|
337
|
+
|
|
338
|
+
Object.observe(eomObj, function() {
|
|
339
|
+
runEOMTasks();
|
|
340
|
+
eomRunScheduled = false;
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
return function(fn) {
|
|
344
|
+
eomTasks.push(fn);
|
|
345
|
+
if (!eomRunScheduled) {
|
|
346
|
+
eomRunScheduled = true;
|
|
347
|
+
eomObj.pingPong = !eomObj.pingPong;
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
})() :
|
|
351
|
+
(function() {
|
|
352
|
+
return function(fn) {
|
|
353
|
+
eomTasks.push(fn);
|
|
354
|
+
};
|
|
355
|
+
})();
|
|
356
|
+
|
|
357
|
+
var observedObjectCache = [];
|
|
358
|
+
|
|
359
|
+
function newObservedObject() {
|
|
360
|
+
var observer;
|
|
361
|
+
var object;
|
|
362
|
+
var discardRecords = false;
|
|
363
|
+
var first = true;
|
|
364
|
+
|
|
365
|
+
function callback(records) {
|
|
366
|
+
if (observer && observer.state_ === OPENED && !discardRecords)
|
|
367
|
+
observer.check_(records);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return {
|
|
371
|
+
open: function(obs) {
|
|
372
|
+
if (observer)
|
|
373
|
+
throw Error('ObservedObject in use');
|
|
374
|
+
|
|
375
|
+
if (!first)
|
|
376
|
+
Object.deliverChangeRecords(callback);
|
|
377
|
+
|
|
378
|
+
observer = obs;
|
|
379
|
+
first = false;
|
|
380
|
+
},
|
|
381
|
+
observe: function(obj, arrayObserve) {
|
|
382
|
+
object = obj;
|
|
383
|
+
if (arrayObserve)
|
|
384
|
+
Array.observe(object, callback);
|
|
385
|
+
else
|
|
386
|
+
Object.observe(object, callback);
|
|
387
|
+
},
|
|
388
|
+
deliver: function(discard) {
|
|
389
|
+
discardRecords = discard;
|
|
390
|
+
Object.deliverChangeRecords(callback);
|
|
391
|
+
discardRecords = false;
|
|
392
|
+
},
|
|
393
|
+
close: function() {
|
|
394
|
+
observer = undefined;
|
|
395
|
+
Object.unobserve(object, callback);
|
|
396
|
+
observedObjectCache.push(this);
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/*
|
|
402
|
+
* The observedSet abstraction is a perf optimization which reduces the total
|
|
403
|
+
* number of Object.observe observations of a set of objects. The idea is that
|
|
404
|
+
* groups of Observers will have some object dependencies in common and this
|
|
405
|
+
* observed set ensures that each object in the transitive closure of
|
|
406
|
+
* dependencies is only observed once. The observedSet acts as a write barrier
|
|
407
|
+
* such that whenever any change comes through, all Observers are checked for
|
|
408
|
+
* changed values.
|
|
409
|
+
*
|
|
410
|
+
* Note that this optimization is explicitly moving work from setup-time to
|
|
411
|
+
* change-time.
|
|
412
|
+
*
|
|
413
|
+
* TODO(rafaelw): Implement "garbage collection". In order to move work off
|
|
414
|
+
* the critical path, when Observers are closed, their observed objects are
|
|
415
|
+
* not Object.unobserve(d). As a result, it's possible that if the observedSet
|
|
416
|
+
* is kept open, but some Observers have been closed, it could cause "leaks"
|
|
417
|
+
* (prevent otherwise collectable objects from being collected). At some
|
|
418
|
+
* point, we should implement incremental "gc" which keeps a list of
|
|
419
|
+
* observedSets which may need clean-up and does small amounts of cleanup on a
|
|
420
|
+
* timeout until all is clean.
|
|
421
|
+
*/
|
|
422
|
+
|
|
423
|
+
function getObservedObject(observer, object, arrayObserve) {
|
|
424
|
+
var dir = observedObjectCache.pop() || newObservedObject();
|
|
425
|
+
dir.open(observer);
|
|
426
|
+
dir.observe(object, arrayObserve);
|
|
427
|
+
return dir;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
var observedSetCache = [];
|
|
431
|
+
|
|
432
|
+
function newObservedSet() {
|
|
433
|
+
var observerCount = 0;
|
|
434
|
+
var observers = [];
|
|
435
|
+
var objects = [];
|
|
436
|
+
var rootObj;
|
|
437
|
+
var rootObjProps;
|
|
438
|
+
|
|
439
|
+
function observe(obj, prop) {
|
|
440
|
+
if (!obj)
|
|
441
|
+
return;
|
|
442
|
+
|
|
443
|
+
if (obj === rootObj)
|
|
444
|
+
rootObjProps[prop] = true;
|
|
445
|
+
|
|
446
|
+
if (objects.indexOf(obj) < 0) {
|
|
447
|
+
objects.push(obj);
|
|
448
|
+
Object.observe(obj, callback);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
observe(Object.getPrototypeOf(obj), prop);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function allRootObjNonObservedProps(recs) {
|
|
455
|
+
for (var i = 0; i < recs.length; i++) {
|
|
456
|
+
var rec = recs[i];
|
|
457
|
+
if (rec.object !== rootObj ||
|
|
458
|
+
rootObjProps[rec.name] ||
|
|
459
|
+
rec.type === 'setPrototype') {
|
|
460
|
+
return false;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
return true;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function callback(recs) {
|
|
467
|
+
if (allRootObjNonObservedProps(recs))
|
|
468
|
+
return;
|
|
469
|
+
|
|
470
|
+
var observer;
|
|
471
|
+
for (var i = 0; i < observers.length; i++) {
|
|
472
|
+
observer = observers[i];
|
|
473
|
+
if (observer.state_ == OPENED) {
|
|
474
|
+
observer.iterateObjects_(observe);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
for (var i = 0; i < observers.length; i++) {
|
|
479
|
+
observer = observers[i];
|
|
480
|
+
if (observer.state_ == OPENED) {
|
|
481
|
+
observer.check_();
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
var record = {
|
|
487
|
+
object: undefined,
|
|
488
|
+
objects: objects,
|
|
489
|
+
open: function(obs, object) {
|
|
490
|
+
if (!rootObj) {
|
|
491
|
+
rootObj = object;
|
|
492
|
+
rootObjProps = {};
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
observers.push(obs);
|
|
496
|
+
observerCount++;
|
|
497
|
+
obs.iterateObjects_(observe);
|
|
498
|
+
},
|
|
499
|
+
close: function(obs) {
|
|
500
|
+
observerCount--;
|
|
501
|
+
if (observerCount > 0) {
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
for (var i = 0; i < objects.length; i++) {
|
|
506
|
+
Object.unobserve(objects[i], callback);
|
|
507
|
+
Observer.unobservedCount++;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
observers.length = 0;
|
|
511
|
+
objects.length = 0;
|
|
512
|
+
rootObj = undefined;
|
|
513
|
+
rootObjProps = undefined;
|
|
514
|
+
observedSetCache.push(this);
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
return record;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
var lastObservedSet;
|
|
522
|
+
|
|
523
|
+
function getObservedSet(observer, obj) {
|
|
524
|
+
if (!lastObservedSet || lastObservedSet.object !== obj) {
|
|
525
|
+
lastObservedSet = observedSetCache.pop() || newObservedSet();
|
|
526
|
+
lastObservedSet.object = obj;
|
|
527
|
+
}
|
|
528
|
+
lastObservedSet.open(observer, obj);
|
|
529
|
+
return lastObservedSet;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
var UNOPENED = 0;
|
|
533
|
+
var OPENED = 1;
|
|
534
|
+
var CLOSED = 2;
|
|
535
|
+
var RESETTING = 3;
|
|
536
|
+
|
|
537
|
+
var nextObserverId = 1;
|
|
538
|
+
|
|
539
|
+
function Observer() {
|
|
540
|
+
this.state_ = UNOPENED;
|
|
541
|
+
this.callback_ = undefined;
|
|
542
|
+
this.target_ = undefined; // TODO(rafaelw): Should be WeakRef
|
|
543
|
+
this.directObserver_ = undefined;
|
|
544
|
+
this.value_ = undefined;
|
|
545
|
+
this.id_ = nextObserverId++;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
Observer.prototype = {
|
|
549
|
+
open: function(callback, target) {
|
|
550
|
+
if (this.state_ != UNOPENED)
|
|
551
|
+
throw Error('Observer has already been opened.');
|
|
552
|
+
|
|
553
|
+
addToAll(this);
|
|
554
|
+
this.callback_ = callback;
|
|
555
|
+
this.target_ = target;
|
|
556
|
+
this.connect_();
|
|
557
|
+
this.state_ = OPENED;
|
|
558
|
+
return this.value_;
|
|
559
|
+
},
|
|
560
|
+
|
|
561
|
+
close: function() {
|
|
562
|
+
if (this.state_ != OPENED)
|
|
563
|
+
return;
|
|
564
|
+
|
|
565
|
+
removeFromAll(this);
|
|
566
|
+
this.disconnect_();
|
|
567
|
+
this.value_ = undefined;
|
|
568
|
+
this.callback_ = undefined;
|
|
569
|
+
this.target_ = undefined;
|
|
570
|
+
this.state_ = CLOSED;
|
|
571
|
+
},
|
|
572
|
+
|
|
573
|
+
deliver: function() {
|
|
574
|
+
if (this.state_ != OPENED)
|
|
575
|
+
return;
|
|
576
|
+
|
|
577
|
+
dirtyCheck(this);
|
|
578
|
+
},
|
|
579
|
+
|
|
580
|
+
report_: function(changes) {
|
|
581
|
+
try {
|
|
582
|
+
this.callback_.apply(this.target_, changes);
|
|
583
|
+
} catch (ex) {
|
|
584
|
+
Observer._errorThrownDuringCallback = true;
|
|
585
|
+
console.error('Exception caught during observer callback: ' +
|
|
586
|
+
(ex.stack || ex));
|
|
587
|
+
}
|
|
588
|
+
},
|
|
589
|
+
|
|
590
|
+
discardChanges: function() {
|
|
591
|
+
this.check_(undefined, true);
|
|
592
|
+
return this.value_;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
var collectObservers = !hasObserve;
|
|
597
|
+
var allObservers;
|
|
598
|
+
Observer._allObserversCount = 0;
|
|
599
|
+
|
|
600
|
+
if (collectObservers) {
|
|
601
|
+
allObservers = [];
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
function addToAll(observer) {
|
|
605
|
+
Observer._allObserversCount++;
|
|
606
|
+
if (!collectObservers)
|
|
607
|
+
return;
|
|
608
|
+
|
|
609
|
+
allObservers.push(observer);
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
function removeFromAll(observer) {
|
|
613
|
+
Observer._allObserversCount--;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
var runningMicrotaskCheckpoint = false;
|
|
617
|
+
|
|
618
|
+
var hasDebugForceFullDelivery = hasObserve && (function() {
|
|
619
|
+
try {
|
|
620
|
+
eval('%RunMicrotasks()');
|
|
621
|
+
return true;
|
|
622
|
+
} catch (ex) {
|
|
623
|
+
return false;
|
|
624
|
+
}
|
|
625
|
+
})();
|
|
626
|
+
|
|
627
|
+
global.Platform = global.Platform || {};
|
|
628
|
+
|
|
629
|
+
global.Platform.performMicrotaskCheckpoint = function() {
|
|
630
|
+
if (runningMicrotaskCheckpoint)
|
|
631
|
+
return;
|
|
632
|
+
|
|
633
|
+
if (hasDebugForceFullDelivery) {
|
|
634
|
+
eval('%RunMicrotasks()');
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
if (!collectObservers)
|
|
639
|
+
return;
|
|
640
|
+
|
|
641
|
+
runningMicrotaskCheckpoint = true;
|
|
642
|
+
|
|
643
|
+
var cycles = 0;
|
|
644
|
+
var anyChanged, toCheck;
|
|
645
|
+
|
|
646
|
+
do {
|
|
647
|
+
cycles++;
|
|
648
|
+
toCheck = allObservers;
|
|
649
|
+
allObservers = [];
|
|
650
|
+
anyChanged = false;
|
|
651
|
+
|
|
652
|
+
for (var i = 0; i < toCheck.length; i++) {
|
|
653
|
+
var observer = toCheck[i];
|
|
654
|
+
if (observer.state_ != OPENED)
|
|
655
|
+
continue;
|
|
656
|
+
|
|
657
|
+
if (observer.check_())
|
|
658
|
+
anyChanged = true;
|
|
659
|
+
|
|
660
|
+
allObservers.push(observer);
|
|
661
|
+
}
|
|
662
|
+
if (runEOMTasks())
|
|
663
|
+
anyChanged = true;
|
|
664
|
+
} while (cycles < MAX_DIRTY_CHECK_CYCLES && anyChanged);
|
|
665
|
+
|
|
666
|
+
if (global.testingExposeCycleCount)
|
|
667
|
+
global.dirtyCheckCycleCount = cycles;
|
|
668
|
+
|
|
669
|
+
runningMicrotaskCheckpoint = false;
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
if (collectObservers) {
|
|
673
|
+
global.Platform.clearObservers = function() {
|
|
674
|
+
allObservers = [];
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
function ObjectObserver(object) {
|
|
679
|
+
Observer.call(this);
|
|
680
|
+
this.value_ = object;
|
|
681
|
+
this.oldObject_ = undefined;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
ObjectObserver.prototype = createObject({
|
|
685
|
+
__proto__: Observer.prototype,
|
|
686
|
+
|
|
687
|
+
arrayObserve: false,
|
|
688
|
+
|
|
689
|
+
connect_: function(callback, target) {
|
|
690
|
+
if (hasObserve) {
|
|
691
|
+
this.directObserver_ = getObservedObject(this, this.value_,
|
|
692
|
+
this.arrayObserve);
|
|
693
|
+
} else {
|
|
694
|
+
this.oldObject_ = this.copyObject(this.value_);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
},
|
|
698
|
+
|
|
699
|
+
copyObject: function(object) {
|
|
700
|
+
var copy = Array.isArray(object) ? [] : {};
|
|
701
|
+
for (var prop in object) {
|
|
702
|
+
copy[prop] = object[prop];
|
|
703
|
+
};
|
|
704
|
+
if (Array.isArray(object))
|
|
705
|
+
copy.length = object.length;
|
|
706
|
+
return copy;
|
|
707
|
+
},
|
|
708
|
+
|
|
709
|
+
check_: function(changeRecords, skipChanges) {
|
|
710
|
+
var diff;
|
|
711
|
+
var oldValues;
|
|
712
|
+
if (hasObserve) {
|
|
713
|
+
if (!changeRecords)
|
|
714
|
+
return false;
|
|
715
|
+
|
|
716
|
+
oldValues = {};
|
|
717
|
+
diff = diffObjectFromChangeRecords(this.value_, changeRecords,
|
|
718
|
+
oldValues);
|
|
719
|
+
} else {
|
|
720
|
+
oldValues = this.oldObject_;
|
|
721
|
+
diff = diffObjectFromOldObject(this.value_, this.oldObject_);
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
if (diffIsEmpty(diff))
|
|
725
|
+
return false;
|
|
726
|
+
|
|
727
|
+
if (!hasObserve)
|
|
728
|
+
this.oldObject_ = this.copyObject(this.value_);
|
|
729
|
+
|
|
730
|
+
this.report_([
|
|
731
|
+
diff.added || {},
|
|
732
|
+
diff.removed || {},
|
|
733
|
+
diff.changed || {},
|
|
734
|
+
function(property) {
|
|
735
|
+
return oldValues[property];
|
|
736
|
+
}
|
|
737
|
+
]);
|
|
738
|
+
|
|
739
|
+
return true;
|
|
740
|
+
},
|
|
741
|
+
|
|
742
|
+
disconnect_: function() {
|
|
743
|
+
if (hasObserve) {
|
|
744
|
+
this.directObserver_.close();
|
|
745
|
+
this.directObserver_ = undefined;
|
|
746
|
+
} else {
|
|
747
|
+
this.oldObject_ = undefined;
|
|
748
|
+
}
|
|
749
|
+
},
|
|
750
|
+
|
|
751
|
+
deliver: function() {
|
|
752
|
+
if (this.state_ != OPENED)
|
|
753
|
+
return;
|
|
754
|
+
|
|
755
|
+
if (hasObserve)
|
|
756
|
+
this.directObserver_.deliver(false);
|
|
757
|
+
else
|
|
758
|
+
dirtyCheck(this);
|
|
759
|
+
},
|
|
760
|
+
|
|
761
|
+
discardChanges: function() {
|
|
762
|
+
if (this.directObserver_)
|
|
763
|
+
this.directObserver_.deliver(true);
|
|
764
|
+
else
|
|
765
|
+
this.oldObject_ = this.copyObject(this.value_);
|
|
766
|
+
|
|
767
|
+
return this.value_;
|
|
768
|
+
}
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
function ArrayObserver(array) {
|
|
772
|
+
if (!Array.isArray(array))
|
|
773
|
+
throw Error('Provided object is not an Array');
|
|
774
|
+
ObjectObserver.call(this, array);
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
ArrayObserver.prototype = createObject({
|
|
778
|
+
|
|
779
|
+
__proto__: ObjectObserver.prototype,
|
|
780
|
+
|
|
781
|
+
arrayObserve: true,
|
|
782
|
+
|
|
783
|
+
copyObject: function(arr) {
|
|
784
|
+
return arr.slice();
|
|
785
|
+
},
|
|
786
|
+
|
|
787
|
+
check_: function(changeRecords) {
|
|
788
|
+
var splices;
|
|
789
|
+
if (hasObserve) {
|
|
790
|
+
if (!changeRecords)
|
|
791
|
+
return false;
|
|
792
|
+
splices = projectArraySplices(this.value_, changeRecords);
|
|
793
|
+
} else {
|
|
794
|
+
splices = calcSplices(this.value_, 0, this.value_.length,
|
|
795
|
+
this.oldObject_, 0, this.oldObject_.length);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
if (!splices || !splices.length)
|
|
799
|
+
return false;
|
|
800
|
+
|
|
801
|
+
if (!hasObserve)
|
|
802
|
+
this.oldObject_ = this.copyObject(this.value_);
|
|
803
|
+
|
|
804
|
+
this.report_([splices]);
|
|
805
|
+
return true;
|
|
806
|
+
}
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
ArrayObserver.applySplices = function(previous, current, splices) {
|
|
810
|
+
splices.forEach(function(splice) {
|
|
811
|
+
var spliceArgs = [splice.index, splice.removed.length];
|
|
812
|
+
var addIndex = splice.index;
|
|
813
|
+
while (addIndex < splice.index + splice.addedCount) {
|
|
814
|
+
spliceArgs.push(current[addIndex]);
|
|
815
|
+
addIndex++;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
Array.prototype.splice.apply(previous, spliceArgs);
|
|
819
|
+
});
|
|
820
|
+
};
|
|
821
|
+
|
|
822
|
+
function PathObserver(object, path) {
|
|
823
|
+
Observer.call(this);
|
|
824
|
+
|
|
825
|
+
this.object_ = object;
|
|
826
|
+
this.path_ = getPath(path);
|
|
827
|
+
this.directObserver_ = undefined;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
PathObserver.prototype = createObject({
|
|
831
|
+
__proto__: Observer.prototype,
|
|
832
|
+
|
|
833
|
+
connect_: function() {
|
|
834
|
+
if (hasObserve)
|
|
835
|
+
this.directObserver_ = getObservedSet(this, this.object_);
|
|
836
|
+
|
|
837
|
+
this.check_(undefined, true);
|
|
838
|
+
},
|
|
839
|
+
|
|
840
|
+
disconnect_: function() {
|
|
841
|
+
this.value_ = undefined;
|
|
842
|
+
|
|
843
|
+
if (this.directObserver_) {
|
|
844
|
+
this.directObserver_.close(this);
|
|
845
|
+
this.directObserver_ = undefined;
|
|
846
|
+
}
|
|
847
|
+
},
|
|
848
|
+
|
|
849
|
+
iterateObjects_: function(observe) {
|
|
850
|
+
this.path_.iterateObjects(this.object_, observe);
|
|
851
|
+
},
|
|
852
|
+
|
|
853
|
+
check_: function(changeRecords, skipChanges) {
|
|
854
|
+
var oldValue = this.value_;
|
|
855
|
+
this.value_ = this.path_.getValueFrom(this.object_);
|
|
856
|
+
if (skipChanges || areSameValue(this.value_, oldValue))
|
|
857
|
+
return false;
|
|
858
|
+
|
|
859
|
+
this.report_([this.value_, oldValue]);
|
|
860
|
+
return true;
|
|
861
|
+
},
|
|
862
|
+
|
|
863
|
+
setValue: function(newValue) {
|
|
864
|
+
if (this.path_)
|
|
865
|
+
this.path_.setValueFrom(this.object_, newValue);
|
|
866
|
+
}
|
|
867
|
+
});
|
|
868
|
+
|
|
869
|
+
function CompoundObserver(reportChangesOnOpen) {
|
|
870
|
+
Observer.call(this);
|
|
871
|
+
|
|
872
|
+
this.reportChangesOnOpen_ = reportChangesOnOpen;
|
|
873
|
+
this.value_ = [];
|
|
874
|
+
this.directObserver_ = undefined;
|
|
875
|
+
this.observed_ = [];
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
var observerSentinel = {};
|
|
879
|
+
|
|
880
|
+
CompoundObserver.prototype = createObject({
|
|
881
|
+
__proto__: Observer.prototype,
|
|
882
|
+
|
|
883
|
+
connect_: function() {
|
|
884
|
+
if (hasObserve) {
|
|
885
|
+
var object;
|
|
886
|
+
var needsDirectObserver = false;
|
|
887
|
+
for (var i = 0; i < this.observed_.length; i += 2) {
|
|
888
|
+
object = this.observed_[i]
|
|
889
|
+
if (object !== observerSentinel) {
|
|
890
|
+
needsDirectObserver = true;
|
|
891
|
+
break;
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
if (needsDirectObserver)
|
|
896
|
+
this.directObserver_ = getObservedSet(this, object);
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
this.check_(undefined, !this.reportChangesOnOpen_);
|
|
900
|
+
},
|
|
901
|
+
|
|
902
|
+
disconnect_: function() {
|
|
903
|
+
for (var i = 0; i < this.observed_.length; i += 2) {
|
|
904
|
+
if (this.observed_[i] === observerSentinel)
|
|
905
|
+
this.observed_[i + 1].close();
|
|
906
|
+
}
|
|
907
|
+
this.observed_.length = 0;
|
|
908
|
+
this.value_.length = 0;
|
|
909
|
+
|
|
910
|
+
if (this.directObserver_) {
|
|
911
|
+
this.directObserver_.close(this);
|
|
912
|
+
this.directObserver_ = undefined;
|
|
913
|
+
}
|
|
914
|
+
},
|
|
915
|
+
|
|
916
|
+
addPath: function(object, path) {
|
|
917
|
+
if (this.state_ != UNOPENED && this.state_ != RESETTING)
|
|
918
|
+
throw Error('Cannot add paths once started.');
|
|
919
|
+
|
|
920
|
+
var path = getPath(path);
|
|
921
|
+
this.observed_.push(object, path);
|
|
922
|
+
if (!this.reportChangesOnOpen_)
|
|
923
|
+
return;
|
|
924
|
+
var index = this.observed_.length / 2 - 1;
|
|
925
|
+
this.value_[index] = path.getValueFrom(object);
|
|
926
|
+
},
|
|
927
|
+
|
|
928
|
+
addObserver: function(observer) {
|
|
929
|
+
if (this.state_ != UNOPENED && this.state_ != RESETTING)
|
|
930
|
+
throw Error('Cannot add observers once started.');
|
|
931
|
+
|
|
932
|
+
this.observed_.push(observerSentinel, observer);
|
|
933
|
+
if (!this.reportChangesOnOpen_)
|
|
934
|
+
return;
|
|
935
|
+
var index = this.observed_.length / 2 - 1;
|
|
936
|
+
this.value_[index] = observer.open(this.deliver, this);
|
|
937
|
+
},
|
|
938
|
+
|
|
939
|
+
startReset: function() {
|
|
940
|
+
if (this.state_ != OPENED)
|
|
941
|
+
throw Error('Can only reset while open');
|
|
942
|
+
|
|
943
|
+
this.state_ = RESETTING;
|
|
944
|
+
this.disconnect_();
|
|
945
|
+
},
|
|
946
|
+
|
|
947
|
+
finishReset: function() {
|
|
948
|
+
if (this.state_ != RESETTING)
|
|
949
|
+
throw Error('Can only finishReset after startReset');
|
|
950
|
+
this.state_ = OPENED;
|
|
951
|
+
this.connect_();
|
|
952
|
+
|
|
953
|
+
return this.value_;
|
|
954
|
+
},
|
|
955
|
+
|
|
956
|
+
iterateObjects_: function(observe) {
|
|
957
|
+
var object;
|
|
958
|
+
for (var i = 0; i < this.observed_.length; i += 2) {
|
|
959
|
+
object = this.observed_[i]
|
|
960
|
+
if (object !== observerSentinel)
|
|
961
|
+
this.observed_[i + 1].iterateObjects(object, observe)
|
|
962
|
+
}
|
|
963
|
+
},
|
|
964
|
+
|
|
965
|
+
check_: function(changeRecords, skipChanges) {
|
|
966
|
+
var oldValues;
|
|
967
|
+
for (var i = 0; i < this.observed_.length; i += 2) {
|
|
968
|
+
var object = this.observed_[i];
|
|
969
|
+
var path = this.observed_[i+1];
|
|
970
|
+
var value;
|
|
971
|
+
if (object === observerSentinel) {
|
|
972
|
+
var observable = path;
|
|
973
|
+
value = this.state_ === UNOPENED ?
|
|
974
|
+
observable.open(this.deliver, this) :
|
|
975
|
+
observable.discardChanges();
|
|
976
|
+
} else {
|
|
977
|
+
value = path.getValueFrom(object);
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
if (skipChanges) {
|
|
981
|
+
this.value_[i / 2] = value;
|
|
982
|
+
continue;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
if (areSameValue(value, this.value_[i / 2]))
|
|
986
|
+
continue;
|
|
987
|
+
|
|
988
|
+
oldValues = oldValues || [];
|
|
989
|
+
oldValues[i / 2] = this.value_[i / 2];
|
|
990
|
+
this.value_[i / 2] = value;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
if (!oldValues)
|
|
994
|
+
return false;
|
|
995
|
+
|
|
996
|
+
// TODO(rafaelw): Having observed_ as the third callback arg here is
|
|
997
|
+
// pretty lame API. Fix.
|
|
998
|
+
this.report_([this.value_, oldValues, this.observed_]);
|
|
999
|
+
return true;
|
|
1000
|
+
}
|
|
1001
|
+
});
|
|
1002
|
+
|
|
1003
|
+
function identFn(value) { return value; }
|
|
1004
|
+
|
|
1005
|
+
function ObserverTransform(observable, getValueFn, setValueFn,
|
|
1006
|
+
dontPassThroughSet) {
|
|
1007
|
+
this.callback_ = undefined;
|
|
1008
|
+
this.target_ = undefined;
|
|
1009
|
+
this.value_ = undefined;
|
|
1010
|
+
this.observable_ = observable;
|
|
1011
|
+
this.getValueFn_ = getValueFn || identFn;
|
|
1012
|
+
this.setValueFn_ = setValueFn || identFn;
|
|
1013
|
+
// TODO(rafaelw): This is a temporary hack. PolymerExpressions needs this
|
|
1014
|
+
// at the moment because of a bug in it's dependency tracking.
|
|
1015
|
+
this.dontPassThroughSet_ = dontPassThroughSet;
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
ObserverTransform.prototype = {
|
|
1019
|
+
open: function(callback, target) {
|
|
1020
|
+
this.callback_ = callback;
|
|
1021
|
+
this.target_ = target;
|
|
1022
|
+
this.value_ =
|
|
1023
|
+
this.getValueFn_(this.observable_.open(this.observedCallback_, this));
|
|
1024
|
+
return this.value_;
|
|
1025
|
+
},
|
|
1026
|
+
|
|
1027
|
+
observedCallback_: function(value) {
|
|
1028
|
+
value = this.getValueFn_(value);
|
|
1029
|
+
if (areSameValue(value, this.value_))
|
|
1030
|
+
return;
|
|
1031
|
+
var oldValue = this.value_;
|
|
1032
|
+
this.value_ = value;
|
|
1033
|
+
this.callback_.call(this.target_, this.value_, oldValue);
|
|
1034
|
+
},
|
|
1035
|
+
|
|
1036
|
+
discardChanges: function() {
|
|
1037
|
+
this.value_ = this.getValueFn_(this.observable_.discardChanges());
|
|
1038
|
+
return this.value_;
|
|
1039
|
+
},
|
|
1040
|
+
|
|
1041
|
+
deliver: function() {
|
|
1042
|
+
return this.observable_.deliver();
|
|
1043
|
+
},
|
|
1044
|
+
|
|
1045
|
+
setValue: function(value) {
|
|
1046
|
+
value = this.setValueFn_(value);
|
|
1047
|
+
if (!this.dontPassThroughSet_ && this.observable_.setValue)
|
|
1048
|
+
return this.observable_.setValue(value);
|
|
1049
|
+
},
|
|
1050
|
+
|
|
1051
|
+
close: function() {
|
|
1052
|
+
if (this.observable_)
|
|
1053
|
+
this.observable_.close();
|
|
1054
|
+
this.callback_ = undefined;
|
|
1055
|
+
this.target_ = undefined;
|
|
1056
|
+
this.observable_ = undefined;
|
|
1057
|
+
this.value_ = undefined;
|
|
1058
|
+
this.getValueFn_ = undefined;
|
|
1059
|
+
this.setValueFn_ = undefined;
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
var expectedRecordTypes = {
|
|
1064
|
+
add: true,
|
|
1065
|
+
update: true,
|
|
1066
|
+
delete: true
|
|
1067
|
+
};
|
|
1068
|
+
|
|
1069
|
+
var updateRecord = {
|
|
1070
|
+
object: undefined,
|
|
1071
|
+
type: 'update',
|
|
1072
|
+
name: undefined,
|
|
1073
|
+
oldValue: undefined
|
|
1074
|
+
};
|
|
1075
|
+
|
|
1076
|
+
function notify(object, name, value, oldValue) {
|
|
1077
|
+
if (areSameValue(value, oldValue))
|
|
1078
|
+
return;
|
|
1079
|
+
|
|
1080
|
+
// TODO(rafaelw): Hack hack hack. This entire code really needs to move
|
|
1081
|
+
// out of observe-js into polymer.
|
|
1082
|
+
if (typeof object.propertyChanged_ == 'function')
|
|
1083
|
+
object.propertyChanged_(name, value, oldValue);
|
|
1084
|
+
|
|
1085
|
+
if (!hasObserve)
|
|
1086
|
+
return;
|
|
1087
|
+
|
|
1088
|
+
var notifier = object.notifier_;
|
|
1089
|
+
if (!notifier)
|
|
1090
|
+
notifier = object.notifier_ = Object.getNotifier(object);
|
|
1091
|
+
|
|
1092
|
+
updateRecord.object = object;
|
|
1093
|
+
updateRecord.name = name;
|
|
1094
|
+
updateRecord.oldValue = oldValue;
|
|
1095
|
+
|
|
1096
|
+
notifier.notify(updateRecord);
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
Observer.createBindablePrototypeAccessor = function(proto, name) {
|
|
1100
|
+
var privateName = name + '_';
|
|
1101
|
+
var privateObservable = name + 'Observable_';
|
|
1102
|
+
|
|
1103
|
+
proto[privateName] = proto[name];
|
|
1104
|
+
|
|
1105
|
+
Object.defineProperty(proto, name, {
|
|
1106
|
+
get: function() {
|
|
1107
|
+
var observable = this[privateObservable];
|
|
1108
|
+
if (observable)
|
|
1109
|
+
observable.deliver();
|
|
1110
|
+
|
|
1111
|
+
return this[privateName];
|
|
1112
|
+
},
|
|
1113
|
+
set: function(value) {
|
|
1114
|
+
var observable = this[privateObservable];
|
|
1115
|
+
if (observable) {
|
|
1116
|
+
observable.setValue(value);
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
var oldValue = this[privateName];
|
|
1121
|
+
this[privateName] = value;
|
|
1122
|
+
notify(this, name, value, oldValue);
|
|
1123
|
+
|
|
1124
|
+
return value;
|
|
1125
|
+
},
|
|
1126
|
+
configurable: true
|
|
1127
|
+
});
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
Observer.bindToInstance = function(instance, name, observable, resolveFn) {
|
|
1131
|
+
var privateName = name + '_';
|
|
1132
|
+
var privateObservable = name + 'Observable_';
|
|
1133
|
+
|
|
1134
|
+
instance[privateObservable] = observable;
|
|
1135
|
+
var oldValue = instance[privateName];
|
|
1136
|
+
var value = observable.open(function(value, oldValue) {
|
|
1137
|
+
instance[privateName] = value;
|
|
1138
|
+
notify(instance, name, value, oldValue);
|
|
1139
|
+
});
|
|
1140
|
+
|
|
1141
|
+
if (resolveFn && !areSameValue(oldValue, value)) {
|
|
1142
|
+
var resolvedValue = resolveFn(oldValue, value);
|
|
1143
|
+
if (!areSameValue(value, resolvedValue)) {
|
|
1144
|
+
value = resolvedValue;
|
|
1145
|
+
if (observable.setValue)
|
|
1146
|
+
observable.setValue(value);
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
instance[privateName] = value;
|
|
1151
|
+
notify(instance, name, value, oldValue);
|
|
1152
|
+
|
|
1153
|
+
return {
|
|
1154
|
+
close: function() {
|
|
1155
|
+
observable.close();
|
|
1156
|
+
instance[privateObservable] = undefined;
|
|
1157
|
+
}
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
function diffObjectFromChangeRecords(object, changeRecords, oldValues) {
|
|
1162
|
+
var added = {};
|
|
1163
|
+
var removed = {};
|
|
1164
|
+
|
|
1165
|
+
for (var i = 0; i < changeRecords.length; i++) {
|
|
1166
|
+
var record = changeRecords[i];
|
|
1167
|
+
if (!expectedRecordTypes[record.type]) {
|
|
1168
|
+
console.error('Unknown changeRecord type: ' + record.type);
|
|
1169
|
+
console.error(record);
|
|
1170
|
+
continue;
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
if (!(record.name in oldValues))
|
|
1174
|
+
oldValues[record.name] = record.oldValue;
|
|
1175
|
+
|
|
1176
|
+
if (record.type == 'update')
|
|
1177
|
+
continue;
|
|
1178
|
+
|
|
1179
|
+
if (record.type == 'add') {
|
|
1180
|
+
if (record.name in removed)
|
|
1181
|
+
delete removed[record.name];
|
|
1182
|
+
else
|
|
1183
|
+
added[record.name] = true;
|
|
1184
|
+
|
|
1185
|
+
continue;
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
// type = 'delete'
|
|
1189
|
+
if (record.name in added) {
|
|
1190
|
+
delete added[record.name];
|
|
1191
|
+
delete oldValues[record.name];
|
|
1192
|
+
} else {
|
|
1193
|
+
removed[record.name] = true;
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
for (var prop in added)
|
|
1198
|
+
added[prop] = object[prop];
|
|
1199
|
+
|
|
1200
|
+
for (var prop in removed)
|
|
1201
|
+
removed[prop] = undefined;
|
|
1202
|
+
|
|
1203
|
+
var changed = {};
|
|
1204
|
+
for (var prop in oldValues) {
|
|
1205
|
+
if (prop in added || prop in removed)
|
|
1206
|
+
continue;
|
|
1207
|
+
|
|
1208
|
+
var newValue = object[prop];
|
|
1209
|
+
if (oldValues[prop] !== newValue)
|
|
1210
|
+
changed[prop] = newValue;
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
return {
|
|
1214
|
+
added: added,
|
|
1215
|
+
removed: removed,
|
|
1216
|
+
changed: changed
|
|
1217
|
+
};
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
function newSplice(index, removed, addedCount) {
|
|
1221
|
+
return {
|
|
1222
|
+
index: index,
|
|
1223
|
+
removed: removed,
|
|
1224
|
+
addedCount: addedCount
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
var EDIT_LEAVE = 0;
|
|
1229
|
+
var EDIT_UPDATE = 1;
|
|
1230
|
+
var EDIT_ADD = 2;
|
|
1231
|
+
var EDIT_DELETE = 3;
|
|
1232
|
+
|
|
1233
|
+
function ArraySplice() {}
|
|
1234
|
+
|
|
1235
|
+
ArraySplice.prototype = {
|
|
1236
|
+
|
|
1237
|
+
// Note: This function is *based* on the computation of the Levenshtein
|
|
1238
|
+
// "edit" distance. The one change is that "updates" are treated as two
|
|
1239
|
+
// edits - not one. With Array splices, an update is really a delete
|
|
1240
|
+
// followed by an add. By retaining this, we optimize for "keeping" the
|
|
1241
|
+
// maximum array items in the original array. For example:
|
|
1242
|
+
//
|
|
1243
|
+
// 'xxxx123' -> '123yyyy'
|
|
1244
|
+
//
|
|
1245
|
+
// With 1-edit updates, the shortest path would be just to update all seven
|
|
1246
|
+
// characters. With 2-edit updates, we delete 4, leave 3, and add 4. This
|
|
1247
|
+
// leaves the substring '123' intact.
|
|
1248
|
+
calcEditDistances: function(current, currentStart, currentEnd,
|
|
1249
|
+
old, oldStart, oldEnd) {
|
|
1250
|
+
// "Deletion" columns
|
|
1251
|
+
var rowCount = oldEnd - oldStart + 1;
|
|
1252
|
+
var columnCount = currentEnd - currentStart + 1;
|
|
1253
|
+
var distances = new Array(rowCount);
|
|
1254
|
+
|
|
1255
|
+
// "Addition" rows. Initialize null column.
|
|
1256
|
+
for (var i = 0; i < rowCount; i++) {
|
|
1257
|
+
distances[i] = new Array(columnCount);
|
|
1258
|
+
distances[i][0] = i;
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
// Initialize null row
|
|
1262
|
+
for (var j = 0; j < columnCount; j++)
|
|
1263
|
+
distances[0][j] = j;
|
|
1264
|
+
|
|
1265
|
+
for (var i = 1; i < rowCount; i++) {
|
|
1266
|
+
for (var j = 1; j < columnCount; j++) {
|
|
1267
|
+
if (this.equals(current[currentStart + j - 1], old[oldStart + i - 1]))
|
|
1268
|
+
distances[i][j] = distances[i - 1][j - 1];
|
|
1269
|
+
else {
|
|
1270
|
+
var north = distances[i - 1][j] + 1;
|
|
1271
|
+
var west = distances[i][j - 1] + 1;
|
|
1272
|
+
distances[i][j] = north < west ? north : west;
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
return distances;
|
|
1278
|
+
},
|
|
1279
|
+
|
|
1280
|
+
// This starts at the final weight, and walks "backward" by finding
|
|
1281
|
+
// the minimum previous weight recursively until the origin of the weight
|
|
1282
|
+
// matrix.
|
|
1283
|
+
spliceOperationsFromEditDistances: function(distances) {
|
|
1284
|
+
var i = distances.length - 1;
|
|
1285
|
+
var j = distances[0].length - 1;
|
|
1286
|
+
var current = distances[i][j];
|
|
1287
|
+
var edits = [];
|
|
1288
|
+
while (i > 0 || j > 0) {
|
|
1289
|
+
if (i == 0) {
|
|
1290
|
+
edits.push(EDIT_ADD);
|
|
1291
|
+
j--;
|
|
1292
|
+
continue;
|
|
1293
|
+
}
|
|
1294
|
+
if (j == 0) {
|
|
1295
|
+
edits.push(EDIT_DELETE);
|
|
1296
|
+
i--;
|
|
1297
|
+
continue;
|
|
1298
|
+
}
|
|
1299
|
+
var northWest = distances[i - 1][j - 1];
|
|
1300
|
+
var west = distances[i - 1][j];
|
|
1301
|
+
var north = distances[i][j - 1];
|
|
1302
|
+
|
|
1303
|
+
var min;
|
|
1304
|
+
if (west < north)
|
|
1305
|
+
min = west < northWest ? west : northWest;
|
|
1306
|
+
else
|
|
1307
|
+
min = north < northWest ? north : northWest;
|
|
1308
|
+
|
|
1309
|
+
if (min == northWest) {
|
|
1310
|
+
if (northWest == current) {
|
|
1311
|
+
edits.push(EDIT_LEAVE);
|
|
1312
|
+
} else {
|
|
1313
|
+
edits.push(EDIT_UPDATE);
|
|
1314
|
+
current = northWest;
|
|
1315
|
+
}
|
|
1316
|
+
i--;
|
|
1317
|
+
j--;
|
|
1318
|
+
} else if (min == west) {
|
|
1319
|
+
edits.push(EDIT_DELETE);
|
|
1320
|
+
i--;
|
|
1321
|
+
current = west;
|
|
1322
|
+
} else {
|
|
1323
|
+
edits.push(EDIT_ADD);
|
|
1324
|
+
j--;
|
|
1325
|
+
current = north;
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
edits.reverse();
|
|
1330
|
+
return edits;
|
|
1331
|
+
},
|
|
1332
|
+
|
|
1333
|
+
/**
|
|
1334
|
+
* Splice Projection functions:
|
|
1335
|
+
*
|
|
1336
|
+
* A splice map is a representation of how a previous array of items
|
|
1337
|
+
* was transformed into a new array of items. Conceptually it is a list of
|
|
1338
|
+
* tuples of
|
|
1339
|
+
*
|
|
1340
|
+
* <index, removed, addedCount>
|
|
1341
|
+
*
|
|
1342
|
+
* which are kept in ascending index order of. The tuple represents that at
|
|
1343
|
+
* the |index|, |removed| sequence of items were removed, and counting forward
|
|
1344
|
+
* from |index|, |addedCount| items were added.
|
|
1345
|
+
*/
|
|
1346
|
+
|
|
1347
|
+
/**
|
|
1348
|
+
* Lacking individual splice mutation information, the minimal set of
|
|
1349
|
+
* splices can be synthesized given the previous state and final state of an
|
|
1350
|
+
* array. The basic approach is to calculate the edit distance matrix and
|
|
1351
|
+
* choose the shortest path through it.
|
|
1352
|
+
*
|
|
1353
|
+
* Complexity: O(l * p)
|
|
1354
|
+
* l: The length of the current array
|
|
1355
|
+
* p: The length of the old array
|
|
1356
|
+
*/
|
|
1357
|
+
calcSplices: function(current, currentStart, currentEnd,
|
|
1358
|
+
old, oldStart, oldEnd) {
|
|
1359
|
+
var prefixCount = 0;
|
|
1360
|
+
var suffixCount = 0;
|
|
1361
|
+
|
|
1362
|
+
var minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
|
|
1363
|
+
if (currentStart == 0 && oldStart == 0)
|
|
1364
|
+
prefixCount = this.sharedPrefix(current, old, minLength);
|
|
1365
|
+
|
|
1366
|
+
if (currentEnd == current.length && oldEnd == old.length)
|
|
1367
|
+
suffixCount = this.sharedSuffix(current, old, minLength - prefixCount);
|
|
1368
|
+
|
|
1369
|
+
currentStart += prefixCount;
|
|
1370
|
+
oldStart += prefixCount;
|
|
1371
|
+
currentEnd -= suffixCount;
|
|
1372
|
+
oldEnd -= suffixCount;
|
|
1373
|
+
|
|
1374
|
+
if (currentEnd - currentStart == 0 && oldEnd - oldStart == 0)
|
|
1375
|
+
return [];
|
|
1376
|
+
|
|
1377
|
+
if (currentStart == currentEnd) {
|
|
1378
|
+
var splice = newSplice(currentStart, [], 0);
|
|
1379
|
+
while (oldStart < oldEnd)
|
|
1380
|
+
splice.removed.push(old[oldStart++]);
|
|
1381
|
+
|
|
1382
|
+
return [ splice ];
|
|
1383
|
+
} else if (oldStart == oldEnd)
|
|
1384
|
+
return [ newSplice(currentStart, [], currentEnd - currentStart) ];
|
|
1385
|
+
|
|
1386
|
+
var ops = this.spliceOperationsFromEditDistances(
|
|
1387
|
+
this.calcEditDistances(current, currentStart, currentEnd,
|
|
1388
|
+
old, oldStart, oldEnd));
|
|
1389
|
+
|
|
1390
|
+
var splice = undefined;
|
|
1391
|
+
var splices = [];
|
|
1392
|
+
var index = currentStart;
|
|
1393
|
+
var oldIndex = oldStart;
|
|
1394
|
+
for (var i = 0; i < ops.length; i++) {
|
|
1395
|
+
switch(ops[i]) {
|
|
1396
|
+
case EDIT_LEAVE:
|
|
1397
|
+
if (splice) {
|
|
1398
|
+
splices.push(splice);
|
|
1399
|
+
splice = undefined;
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
index++;
|
|
1403
|
+
oldIndex++;
|
|
1404
|
+
break;
|
|
1405
|
+
case EDIT_UPDATE:
|
|
1406
|
+
if (!splice)
|
|
1407
|
+
splice = newSplice(index, [], 0);
|
|
1408
|
+
|
|
1409
|
+
splice.addedCount++;
|
|
1410
|
+
index++;
|
|
1411
|
+
|
|
1412
|
+
splice.removed.push(old[oldIndex]);
|
|
1413
|
+
oldIndex++;
|
|
1414
|
+
break;
|
|
1415
|
+
case EDIT_ADD:
|
|
1416
|
+
if (!splice)
|
|
1417
|
+
splice = newSplice(index, [], 0);
|
|
1418
|
+
|
|
1419
|
+
splice.addedCount++;
|
|
1420
|
+
index++;
|
|
1421
|
+
break;
|
|
1422
|
+
case EDIT_DELETE:
|
|
1423
|
+
if (!splice)
|
|
1424
|
+
splice = newSplice(index, [], 0);
|
|
1425
|
+
|
|
1426
|
+
splice.removed.push(old[oldIndex]);
|
|
1427
|
+
oldIndex++;
|
|
1428
|
+
break;
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
if (splice) {
|
|
1433
|
+
splices.push(splice);
|
|
1434
|
+
}
|
|
1435
|
+
return splices;
|
|
1436
|
+
},
|
|
1437
|
+
|
|
1438
|
+
sharedPrefix: function(current, old, searchLength) {
|
|
1439
|
+
for (var i = 0; i < searchLength; i++)
|
|
1440
|
+
if (!this.equals(current[i], old[i]))
|
|
1441
|
+
return i;
|
|
1442
|
+
return searchLength;
|
|
1443
|
+
},
|
|
1444
|
+
|
|
1445
|
+
sharedSuffix: function(current, old, searchLength) {
|
|
1446
|
+
var index1 = current.length;
|
|
1447
|
+
var index2 = old.length;
|
|
1448
|
+
var count = 0;
|
|
1449
|
+
while (count < searchLength && this.equals(current[--index1], old[--index2]))
|
|
1450
|
+
count++;
|
|
1451
|
+
|
|
1452
|
+
return count;
|
|
1453
|
+
},
|
|
1454
|
+
|
|
1455
|
+
calculateSplices: function(current, previous) {
|
|
1456
|
+
return this.calcSplices(current, 0, current.length, previous, 0,
|
|
1457
|
+
previous.length);
|
|
1458
|
+
},
|
|
1459
|
+
|
|
1460
|
+
equals: function(currentValue, previousValue) {
|
|
1461
|
+
return currentValue === previousValue;
|
|
1462
|
+
}
|
|
1463
|
+
};
|
|
1464
|
+
|
|
1465
|
+
var arraySplice = new ArraySplice();
|
|
1466
|
+
|
|
1467
|
+
function calcSplices(current, currentStart, currentEnd,
|
|
1468
|
+
old, oldStart, oldEnd) {
|
|
1469
|
+
return arraySplice.calcSplices(current, currentStart, currentEnd,
|
|
1470
|
+
old, oldStart, oldEnd);
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
function intersect(start1, end1, start2, end2) {
|
|
1474
|
+
// Disjoint
|
|
1475
|
+
if (end1 < start2 || end2 < start1)
|
|
1476
|
+
return -1;
|
|
1477
|
+
|
|
1478
|
+
// Adjacent
|
|
1479
|
+
if (end1 == start2 || end2 == start1)
|
|
1480
|
+
return 0;
|
|
1481
|
+
|
|
1482
|
+
// Non-zero intersect, span1 first
|
|
1483
|
+
if (start1 < start2) {
|
|
1484
|
+
if (end1 < end2)
|
|
1485
|
+
return end1 - start2; // Overlap
|
|
1486
|
+
else
|
|
1487
|
+
return end2 - start2; // Contained
|
|
1488
|
+
} else {
|
|
1489
|
+
// Non-zero intersect, span2 first
|
|
1490
|
+
if (end2 < end1)
|
|
1491
|
+
return end2 - start1; // Overlap
|
|
1492
|
+
else
|
|
1493
|
+
return end1 - start1; // Contained
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
function mergeSplice(splices, index, removed, addedCount) {
|
|
1498
|
+
|
|
1499
|
+
var splice = newSplice(index, removed, addedCount);
|
|
1500
|
+
|
|
1501
|
+
var inserted = false;
|
|
1502
|
+
var insertionOffset = 0;
|
|
1503
|
+
|
|
1504
|
+
for (var i = 0; i < splices.length; i++) {
|
|
1505
|
+
var current = splices[i];
|
|
1506
|
+
current.index += insertionOffset;
|
|
1507
|
+
|
|
1508
|
+
if (inserted)
|
|
1509
|
+
continue;
|
|
1510
|
+
|
|
1511
|
+
var intersectCount = intersect(splice.index,
|
|
1512
|
+
splice.index + splice.removed.length,
|
|
1513
|
+
current.index,
|
|
1514
|
+
current.index + current.addedCount);
|
|
1515
|
+
|
|
1516
|
+
if (intersectCount >= 0) {
|
|
1517
|
+
// Merge the two splices
|
|
1518
|
+
|
|
1519
|
+
splices.splice(i, 1);
|
|
1520
|
+
i--;
|
|
1521
|
+
|
|
1522
|
+
insertionOffset -= current.addedCount - current.removed.length;
|
|
1523
|
+
|
|
1524
|
+
splice.addedCount += current.addedCount - intersectCount;
|
|
1525
|
+
var deleteCount = splice.removed.length +
|
|
1526
|
+
current.removed.length - intersectCount;
|
|
1527
|
+
|
|
1528
|
+
if (!splice.addedCount && !deleteCount) {
|
|
1529
|
+
// merged splice is a noop. discard.
|
|
1530
|
+
inserted = true;
|
|
1531
|
+
} else {
|
|
1532
|
+
var removed = current.removed;
|
|
1533
|
+
|
|
1534
|
+
if (splice.index < current.index) {
|
|
1535
|
+
// some prefix of splice.removed is prepended to current.removed.
|
|
1536
|
+
var prepend = splice.removed.slice(0, current.index - splice.index);
|
|
1537
|
+
Array.prototype.push.apply(prepend, removed);
|
|
1538
|
+
removed = prepend;
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
if (splice.index + splice.removed.length > current.index + current.addedCount) {
|
|
1542
|
+
// some suffix of splice.removed is appended to current.removed.
|
|
1543
|
+
var append = splice.removed.slice(current.index + current.addedCount - splice.index);
|
|
1544
|
+
Array.prototype.push.apply(removed, append);
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
splice.removed = removed;
|
|
1548
|
+
if (current.index < splice.index) {
|
|
1549
|
+
splice.index = current.index;
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
} else if (splice.index < current.index) {
|
|
1553
|
+
// Insert splice here.
|
|
1554
|
+
|
|
1555
|
+
inserted = true;
|
|
1556
|
+
|
|
1557
|
+
splices.splice(i, 0, splice);
|
|
1558
|
+
i++;
|
|
1559
|
+
|
|
1560
|
+
var offset = splice.addedCount - splice.removed.length
|
|
1561
|
+
current.index += offset;
|
|
1562
|
+
insertionOffset += offset;
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
if (!inserted)
|
|
1567
|
+
splices.push(splice);
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
function createInitialSplices(array, changeRecords) {
|
|
1571
|
+
var splices = [];
|
|
1572
|
+
|
|
1573
|
+
for (var i = 0; i < changeRecords.length; i++) {
|
|
1574
|
+
var record = changeRecords[i];
|
|
1575
|
+
switch(record.type) {
|
|
1576
|
+
case 'splice':
|
|
1577
|
+
mergeSplice(splices, record.index, record.removed.slice(), record.addedCount);
|
|
1578
|
+
break;
|
|
1579
|
+
case 'add':
|
|
1580
|
+
case 'update':
|
|
1581
|
+
case 'delete':
|
|
1582
|
+
if (!isIndex(record.name))
|
|
1583
|
+
continue;
|
|
1584
|
+
var index = toNumber(record.name);
|
|
1585
|
+
if (index < 0)
|
|
1586
|
+
continue;
|
|
1587
|
+
mergeSplice(splices, index, [record.oldValue], 1);
|
|
1588
|
+
break;
|
|
1589
|
+
default:
|
|
1590
|
+
console.error('Unexpected record type: ' + JSON.stringify(record));
|
|
1591
|
+
break;
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
return splices;
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
function projectArraySplices(array, changeRecords) {
|
|
1599
|
+
var splices = [];
|
|
1600
|
+
|
|
1601
|
+
createInitialSplices(array, changeRecords).forEach(function(splice) {
|
|
1602
|
+
if (splice.addedCount == 1 && splice.removed.length == 1) {
|
|
1603
|
+
if (splice.removed[0] !== array[splice.index])
|
|
1604
|
+
splices.push(splice);
|
|
1605
|
+
|
|
1606
|
+
return
|
|
1607
|
+
};
|
|
1608
|
+
|
|
1609
|
+
splices = splices.concat(calcSplices(array, splice.index, splice.index + splice.addedCount,
|
|
1610
|
+
splice.removed, 0, splice.removed.length));
|
|
1611
|
+
});
|
|
1612
|
+
|
|
1613
|
+
return splices;
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
global.Observer = Observer;
|
|
1617
|
+
global.Observer.runEOM_ = runEOM;
|
|
1618
|
+
global.Observer.observerSentinel_ = observerSentinel; // for testing.
|
|
1619
|
+
global.Observer.hasObjectObserve = hasObserve;
|
|
1620
|
+
global.ArrayObserver = ArrayObserver;
|
|
1621
|
+
global.ArrayObserver.calculateSplices = function(current, previous) {
|
|
1622
|
+
return arraySplice.calculateSplices(current, previous);
|
|
1623
|
+
};
|
|
1624
|
+
|
|
1625
|
+
global.ArraySplice = ArraySplice;
|
|
1626
|
+
global.ObjectObserver = ObjectObserver;
|
|
1627
|
+
global.PathObserver = PathObserver;
|
|
1628
|
+
global.CompoundObserver = CompoundObserver;
|
|
1629
|
+
global.Path = Path;
|
|
1630
|
+
global.ObserverTransform = ObserverTransform;
|
|
1631
|
+
})(typeof global !== 'undefined' && global && typeof module !== 'undefined' && module ? exports || global : exports || this || window);
|