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.
Files changed (203) hide show
  1. checksums.yaml +4 -4
  2. data/component.json +1 -1
  3. data/components/indefinido/indemma/master/.gitignore +17 -0
  4. data/components/indefinido/indemma/master/.ruby-gemset +1 -0
  5. data/components/indefinido/indemma/master/.ruby-version +1 -0
  6. data/components/indefinido/indemma/master/Gemfile +12 -0
  7. data/components/indefinido/indemma/master/Guardfile +39 -0
  8. data/components/indefinido/indemma/master/History.md +0 -0
  9. data/components/indefinido/indemma/master/Readme.md +445 -0
  10. data/components/indefinido/indemma/master/build/development.js +26123 -0
  11. data/components/indefinido/indemma/master/build/release.js +16741 -0
  12. data/components/indefinido/indemma/master/build/test.js +26123 -0
  13. data/components/indefinido/indemma/master/component.json +1 -1
  14. data/components/indefinido/indemma/master/components/chaijs/assertion-error/1.0.0/component.json +18 -0
  15. data/components/indefinido/indemma/master/components/chaijs/assertion-error/1.0.0/index.js +110 -0
  16. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/.gitignore +22 -0
  17. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/.mailmap +1 -0
  18. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/.npmignore +14 -0
  19. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/.travis.yml +14 -0
  20. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/CONTRIBUTING.md +173 -0
  21. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/History.md +895 -0
  22. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/Makefile +93 -0
  23. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/README.md +99 -0
  24. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/ReleaseNotes.md +482 -0
  25. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/bower.json +27 -0
  26. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/chai.js +4782 -0
  27. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/component.json +50 -0
  28. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/index.js +1 -0
  29. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/karma.conf.js +28 -0
  30. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/karma.sauce.js +41 -0
  31. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/assertion.js +130 -0
  32. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/config.js +50 -0
  33. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/core/assertions.js +1314 -0
  34. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/interface/assert.js +1056 -0
  35. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/interface/expect.js +12 -0
  36. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/interface/should.js +78 -0
  37. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/addChainableMethod.js +111 -0
  38. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/addMethod.js +43 -0
  39. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/addProperty.js +40 -0
  40. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/flag.js +32 -0
  41. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/getActual.js +18 -0
  42. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/getEnumerableProperties.js +25 -0
  43. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/getMessage.js +49 -0
  44. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/getName.js +20 -0
  45. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/getPathValue.js +102 -0
  46. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/getProperties.js +35 -0
  47. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/index.js +114 -0
  48. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/inspect.js +320 -0
  49. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/objDisplay.js +49 -0
  50. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/overwriteChainableMethod.js +53 -0
  51. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/overwriteMethod.js +51 -0
  52. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/overwriteProperty.js +54 -0
  53. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/test.js +26 -0
  54. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/transferFlags.js +44 -0
  55. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai/utils/type.js +45 -0
  56. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/lib/chai.js +87 -0
  57. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/package.json +42 -0
  58. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/sauce.browsers.js +128 -0
  59. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/support/sauce/sauce_connect_block.sh +7 -0
  60. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/support/sauce/sauce_connect_setup.sh +53 -0
  61. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/test/assert.js +638 -0
  62. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/test/auth/.gitkeep +0 -0
  63. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/test/bootstrap/index.js +22 -0
  64. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/test/bootstrap/karma.js +22 -0
  65. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/test/configuration.js +133 -0
  66. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/test/display/errors.js +14 -0
  67. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/test/display/message.js +47 -0
  68. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/test/expect.js +814 -0
  69. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/test/globalShould.js +15 -0
  70. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/test/plugins.js +24 -0
  71. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/test/should.js +744 -0
  72. data/components/indefinido/indemma/master/components/chaijs/chai/1.9.1/test/utilities.js +309 -0
  73. data/components/indefinido/indemma/master/components/chaijs/deep-eql/0.1.3/component.json +20 -0
  74. data/components/indefinido/indemma/master/components/chaijs/deep-eql/0.1.3/lib/eql.js +257 -0
  75. data/components/indefinido/indemma/master/components/chaijs/type-detect/0.1.1/component.json +18 -0
  76. data/components/indefinido/indemma/master/components/chaijs/type-detect/0.1.1/lib/type.js +142 -0
  77. data/components/indefinido/indemma/master/components/component/bind/1.0.0/component.json +14 -0
  78. data/components/indefinido/indemma/master/components/component/bind/1.0.0/index.js +23 -0
  79. data/components/indefinido/indemma/master/components/component/jquery/1.0.0/component.json +10 -0
  80. data/components/indefinido/indemma/master/components/component/jquery/1.0.0/index.js +10308 -0
  81. data/components/indefinido/indemma/master/components/component/type/1.0.0/component.json +20 -0
  82. data/components/indefinido/indemma/master/components/component/type/1.0.0/index.js +32 -0
  83. data/components/indefinido/indemma/master/components/indefinido/advisable/master/component.json +21 -0
  84. data/components/indefinido/indemma/master/components/indefinido/advisable/master/index.js +1 -0
  85. data/components/indefinido/indemma/master/components/indefinido/advisable/master/lib/advisable.js +60 -0
  86. data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/component.json +39 -0
  87. data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/index.js +1 -0
  88. data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/lib/adapters/rivets.js +40 -0
  89. data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/lib/legacy/notifierable.js +145 -0
  90. data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/lib/legacy/schedulerable.js +147 -0
  91. data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/lib/lookup.js +38 -0
  92. data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/lib/observable/keypath_observer.js +38 -0
  93. data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/lib/observable/observation.js +45 -0
  94. data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/lib/observable/selection.js +57 -0
  95. data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/lib/observable/self_observer.js +38 -0
  96. data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/lib/observable.js +97 -0
  97. data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/lib/platform.js +12 -0
  98. data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/vendor/observe-js/observe.js +1631 -0
  99. data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/vendor/shims/accessors-legacy.js +92 -0
  100. data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/vendor/shims/accessors.js +248 -0
  101. data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/vendor/shims/array.indexOf.js +8 -0
  102. data/components/indefinido/indemma/master/components/indefinido/observable/es6-modules/vendor/shims/object.create.js +77 -0
  103. data/components/indefinido/indemma/master/components/pluma/assimilate/0.4.0/component.json +25 -0
  104. data/components/indefinido/indemma/master/components/pluma/assimilate/0.4.0/dist/assimilate.js +127 -0
  105. data/components/indefinido/indemma/master/karma.conf.js +75 -0
  106. data/components/indefinido/indemma/master/lib/record/associable.js +4 -2
  107. data/components/indefinido/indemma/master/lib/record/dirtyable.js +1 -1
  108. data/components/indefinido/indemma/master/package.json +9 -0
  109. data/components/indefinido/indemma/master/spec/record/associable_spec.js +211 -0
  110. data/components/indefinido/indemma/master/spec/record/dirtyable_spec.js +57 -0
  111. data/components/indefinido/indemma/master/spec/record/maid_spec.js +40 -0
  112. data/components/indefinido/indemma/master/spec/record/persistable_spec.js +46 -0
  113. data/components/indefinido/indemma/master/spec/record/queryable_spec.js +39 -0
  114. data/components/indefinido/indemma/master/spec/record/resource_spec.js +93 -0
  115. data/components/indefinido/indemma/master/spec/record/rest_spec.js +32 -0
  116. data/components/indefinido/indemma/master/spec/record/restfulable_spec.js +346 -0
  117. data/components/indefinido/indemma/master/spec/record/scopable_spec.js +242 -0
  118. data/components/indefinido/indemma/master/spec/record/storable_spec.js +44 -0
  119. data/components/indefinido/indemma/master/spec/record/translationable.js +28 -0
  120. data/components/indefinido/indemma/master/spec/record/validatable_spec.js +142 -0
  121. data/components/indefinido/indemma/master/spec/record/validations/associated_spec.js +43 -0
  122. data/components/indefinido/indemma/master/spec/record/validations/confirmation_spec.js +36 -0
  123. data/components/indefinido/indemma/master/spec/record/validations/cpf_spec.js +34 -0
  124. data/components/indefinido/indemma/master/spec/record/validations/presence_spec.js +28 -0
  125. data/components/indefinido/indemma/master/spec/record/validations/remote_spec.js +87 -0
  126. data/components/indefinido/indemma/master/spec/record/validations/type_spec.js +48 -0
  127. data/components/indefinido/indemma/master/spec/record_spec.js +49 -0
  128. data/components/indefinido/indemma/master/spec/spec_helper.js +15 -0
  129. data/components/indefinido/indemma/master/spec/support/value_objects/phone.js +45 -0
  130. data/components/indefinido/indemma/master/src/lib/extensions/rivets.coffee +17 -0
  131. data/components/indefinido/indemma/master/src/lib/record/associable.coffee +419 -0
  132. data/components/indefinido/indemma/master/src/lib/record/dirtyable.coffee +47 -0
  133. data/components/indefinido/indemma/master/src/lib/record/errors.coffee +20 -0
  134. data/components/indefinido/indemma/master/src/lib/record/maid.coffee +16 -0
  135. data/components/indefinido/indemma/master/src/lib/record/persistable.coffee +34 -0
  136. data/components/indefinido/indemma/master/src/lib/record/queryable.coffee +29 -0
  137. data/components/indefinido/indemma/master/src/lib/record/resource.coffee +107 -0
  138. data/components/indefinido/indemma/master/src/lib/record/rest.coffee +34 -0
  139. data/components/indefinido/indemma/master/src/lib/record/restfulable.coffee +487 -0
  140. data/components/indefinido/indemma/master/src/lib/record/scopable.coffee +279 -0
  141. data/components/indefinido/indemma/master/src/lib/record/storable.coffee +49 -0
  142. data/components/indefinido/indemma/master/src/lib/record/translationable.coffee +18 -0
  143. data/components/indefinido/indemma/master/src/lib/record/validatable.coffee +241 -0
  144. data/components/indefinido/indemma/master/src/lib/record/validations/associated.coffee +34 -0
  145. data/components/indefinido/indemma/master/src/lib/record/validations/confirmation.coffee +17 -0
  146. data/components/indefinido/indemma/master/src/lib/record/validations/cpf.coffee +57 -0
  147. data/components/indefinido/indemma/master/src/lib/record/validations/presence.coffee +16 -0
  148. data/components/indefinido/indemma/master/src/lib/record/validations/remote.coffee +61 -0
  149. data/components/indefinido/indemma/master/src/lib/record/validations/type.coffee +31 -0
  150. data/components/indefinido/indemma/master/src/lib/record/validations/validatorable.coffee +5 -0
  151. data/components/indefinido/indemma/master/src/lib/record.coffee +143 -0
  152. data/components/indefinido/indemma/master/src/spec/record/associable_spec.coffee +205 -0
  153. data/components/indefinido/indemma/master/src/spec/record/dirtyable_spec.coffee +47 -0
  154. data/components/indefinido/indemma/master/src/spec/record/maid_spec.coffee +34 -0
  155. data/components/indefinido/indemma/master/src/spec/record/persistable_spec.coffee +39 -0
  156. data/components/indefinido/indemma/master/src/spec/record/queryable_spec.coffee +33 -0
  157. data/components/indefinido/indemma/master/src/spec/record/resource_spec.coffee +69 -0
  158. data/components/indefinido/indemma/master/src/spec/record/rest_spec.coffee +22 -0
  159. data/components/indefinido/indemma/master/src/spec/record/restfulable_spec.coffee +257 -0
  160. data/components/indefinido/indemma/master/src/spec/record/scopable_spec.coffee +227 -0
  161. data/components/indefinido/indemma/master/src/spec/record/storable_spec.coffee +39 -0
  162. data/components/indefinido/indemma/master/src/spec/record/translationable.coffee +19 -0
  163. data/components/indefinido/indemma/master/src/spec/record/validatable_spec.coffee +127 -0
  164. data/components/indefinido/indemma/master/src/spec/record/validations/associated_spec.coffee +35 -0
  165. data/components/indefinido/indemma/master/src/spec/record/validations/confirmation_spec.coffee +25 -0
  166. data/components/indefinido/indemma/master/src/spec/record/validations/cpf_spec.coffee +27 -0
  167. data/components/indefinido/indemma/master/src/spec/record/validations/presence_spec.coffee +24 -0
  168. data/components/indefinido/indemma/master/src/spec/record/validations/remote_spec.coffee +74 -0
  169. data/components/indefinido/indemma/master/src/spec/record/validations/type_spec.coffee +33 -0
  170. data/components/indefinido/indemma/master/src/spec/record_spec.coffee +29 -0
  171. data/components/indefinido/indemma/master/src/spec/spec_helper.coffee +11 -0
  172. data/components/indefinido/indemma/master/src/spec/support/value_objects/phone.coffee +30 -0
  173. data/components/indefinido/indemma/master/vendor/object/mixin.js +196 -0
  174. data/components/indefinido/indemma/master/vendor/spec/sinon/assert.js +199 -0
  175. data/components/indefinido/indemma/master/vendor/spec/sinon/behavior.js +334 -0
  176. data/components/indefinido/indemma/master/vendor/spec/sinon/call.js +205 -0
  177. data/components/indefinido/indemma/master/vendor/spec/sinon/collection.js +155 -0
  178. data/components/indefinido/indemma/master/vendor/spec/sinon/match.js +245 -0
  179. data/components/indefinido/indemma/master/vendor/spec/sinon/mock.js +451 -0
  180. data/components/indefinido/indemma/master/vendor/spec/sinon/sandbox.js +144 -0
  181. data/components/indefinido/indemma/master/vendor/spec/sinon/spy.js +413 -0
  182. data/components/indefinido/indemma/master/vendor/spec/sinon/stub.js +161 -0
  183. data/components/indefinido/indemma/master/vendor/spec/sinon/test.js +77 -0
  184. data/components/indefinido/indemma/master/vendor/spec/sinon/test_case.js +99 -0
  185. data/components/indefinido/indemma/master/vendor/spec/sinon/util/event.js +94 -0
  186. data/components/indefinido/indemma/master/vendor/spec/sinon/util/fake_server.js +212 -0
  187. data/components/indefinido/indemma/master/vendor/spec/sinon/util/fake_server_with_clock.js +83 -0
  188. data/components/indefinido/indemma/master/vendor/spec/sinon/util/fake_timers.js +405 -0
  189. data/components/indefinido/indemma/master/vendor/spec/sinon/util/fake_xdomain_request.js +206 -0
  190. data/components/indefinido/indemma/master/vendor/spec/sinon/util/fake_xml_http_request.js +575 -0
  191. data/components/indefinido/indemma/master/vendor/spec/sinon/util/timers_ie.js +31 -0
  192. data/components/indefinido/indemma/master/vendor/spec/sinon/util/xdr_ie.js +14 -0
  193. data/components/indefinido/indemma/master/vendor/spec/sinon/util/xhr_ie.js +19 -0
  194. data/components/indefinido/indemma/master/vendor/spec/sinon.js +383 -0
  195. data/lib/assets/javascripts/value_objects/phone.js.coffee +3 -1
  196. data/lib/assets/javascripts/widgets/viewer/plugins/scopable.js.coffee +5 -5
  197. data/lib/ende/version.rb +1 -1
  198. data/vendor/assets/components/ende_build.css +4 -4
  199. data/vendor/assets/components/ende_build.js +7974 -7889
  200. metadata +191 -5
  201. data/vendor/assets/component/build.css +0 -112
  202. data/vendor/assets/components/build.css +0 -112
  203. 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);