kea-rails 1.0.4 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 41bd1eac8219569939c938b547353ca7c9d1278c
4
- data.tar.gz: 09eb89eab320e446e915de000b41b428af9ce557
3
+ metadata.gz: 7114b9b4ade204e3dea3a496fc050fcc98ee6835
4
+ data.tar.gz: 27deae513503b4b475116bb20b8de56440d4f362
5
5
  SHA512:
6
- metadata.gz: dcdc1ce36da5e0e52630b689e6be1737bc05067ac1b8a1e60ae7c30e51d3c39b851a23057b00468063eb303bdc43fe9f416058b5041c11d59ae5485143836c00
7
- data.tar.gz: 4feb3931baa0e9349e28b31f246bde3f4b5f32afe519681fb9b0195fed4c26ebb6f527547fe0d44ad338bbfeb3a7ed5c18201c9c990cee76903a1f4f369eeca5
6
+ metadata.gz: a0f71e1f6bdc2abf06dcb32a88b30087c10b9e6182410ce7da262deec6afd977dbbec2ddab5cefd839581c6de7c76fdef48aba2bba3bb529c244ae16722aa098
7
+ data.tar.gz: a76dcd363f6f31f0545ce798f78dfd78b0200b85f6b93a7ade1a59e6e839eb3ed68196dcf0a6fdf3fd5af5146fff2bbfb7b7d1a4218525249fef14b492fe90b1
@@ -17,7 +17,7 @@
17
17
  });
18
18
  }
19
19
 
20
- if (setupContext && typeof childVm.setup === 'function') {
20
+ if (typeof childVm.setup === 'function') {
21
21
  childVm.setup(setupContext);
22
22
  }
23
23
 
@@ -0,0 +1,32 @@
1
+ (function(ko, $) {
2
+ "use strict";
3
+
4
+ ko.components.register('confirmation-button', {
5
+ viewModel: function(params) {
6
+ this.text = ko.observable(params.initialText || '');
7
+ this.css = params.css || '';
8
+ },
9
+ template: '<!-- ko template: { nodes: $componentTemplateNodes } --><!-- /ko -->'
10
+ });
11
+
12
+ })(ko, $);
13
+
14
+ (function(ko, $) {
15
+ "use strict";
16
+
17
+ ko.components.register('confirmation-button', {
18
+ viewModel: {
19
+ createViewModel: function createViewModel(params, componentInfo) {
20
+ var ConfirmationButtonVm;
21
+
22
+ ConfirmationButtonVm = function ConfirmationButtonVm(params) {
23
+
24
+ };
25
+
26
+ return new ConfirmationButtonVm(params);
27
+ }
28
+ },
29
+ template: 'x'
30
+ });
31
+
32
+ })(ko, $);
@@ -1,5 +1,6 @@
1
1
  //= require_directory ./bindings
2
2
  //= require_directory ./extenders
3
+ //= require_directory ./components
3
4
  //= require ./models/base
4
5
  //= require ./services/base
5
6
  //= require ./viewmodels/base
@@ -1,4 +1,4 @@
1
- //= require knockout-3.2.0-debug
1
+ //= require knockout-3.3.0-debug
2
2
 
3
3
  //= require_directory ../../../../vendor/assets/javascripts/.
4
4
 
@@ -12,7 +12,9 @@ module Kea
12
12
  options[:scope] ||= self
13
13
  options[:url_options] ||= url_options
14
14
 
15
- target.active_model_serializer.new(target, options).to_json
15
+ serializer = options[:serializer] || target.active_model_serializer
16
+
17
+ serializer.new(target, options).to_json
16
18
  end
17
19
 
18
20
  def cache_json(object, path = nil, options = {})
@@ -25,6 +27,20 @@ module Kea
25
27
  content_for :json_cache, "window.app.cache['#{path}'] = #{content};\n".html_safe
26
28
  end
27
29
 
30
+ def knockout_template(name, partial: nil, &block)
31
+ content_for :knockout_templates do
32
+ if partial
33
+ content_tag :script, type: "text/html", id: name do
34
+ render partial: partial
35
+ end
36
+ else
37
+ content_tag :script, type: "text/html", id: name do
38
+ capture(&block)
39
+ end
40
+ end
41
+ end
42
+ end
43
+
28
44
  def overlay_template(name, partial: nil, &block)
29
45
  content_for :knockout_templates do
30
46
  if partial
@@ -1,3 +1,3 @@
1
1
  module Kea
2
- VERSION = "1.0.4"
2
+ VERSION = "1.0.5"
3
3
  end
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Knockout JavaScript library v3.2.0
2
+ * Knockout JavaScript library v3.3.0
3
3
  * (c) Steven Sanderson - http://knockoutjs.com/
4
4
  * License: MIT (http://www.opensource.org/licenses/mit-license.php)
5
5
  */
@@ -16,18 +16,17 @@ var DEBUG=true;
16
16
  JSON = window["JSON"];
17
17
  (function(factory) {
18
18
  // Support three module loading scenarios
19
- if (typeof require === 'function' && typeof exports === 'object' && typeof module === 'object') {
20
- // [1] CommonJS/Node.js
21
- var target = module['exports'] || exports; // module.exports is for Node.js
22
- factory(target, require);
23
- } else if (typeof define === 'function' && define['amd']) {
24
- // [2] AMD anonymous module
19
+ if (typeof define === 'function' && define['amd']) {
20
+ // [1] AMD anonymous module
25
21
  define(['exports', 'require'], factory);
22
+ } else if (typeof require === 'function' && typeof exports === 'object' && typeof module === 'object') {
23
+ // [2] CommonJS/Node.js
24
+ factory(module['exports'] || exports); // module.exports is for Node.js
26
25
  } else {
27
26
  // [3] No module loader (plain <script> tag) - put directly in global namespace
28
27
  factory(window['ko'] = {});
29
28
  }
30
- }(function(koExports, require){
29
+ }(function(koExports, amdRequire){
31
30
  // Internally, all KO objects are attached to koExports (even the non-exported ones whose names will be minified by the closure compiler).
32
31
  // In the future, the following "ko" variable may be made distinct from "koExports" so that private objects are not externally reachable.
33
32
  var ko = typeof koExports !== 'undefined' ? koExports : {};
@@ -46,7 +45,7 @@ ko.exportSymbol = function(koPath, object) {
46
45
  ko.exportProperty = function(owner, publicName, object) {
47
46
  owner[publicName] = object;
48
47
  };
49
- ko.version = "3.2.0";
48
+ ko.version = "3.3.0";
50
49
 
51
50
  ko.exportSymbol('version', ko.version);
52
51
  ko.utils = (function () {
@@ -113,6 +112,37 @@ ko.utils = (function () {
113
112
  return (inputType == "checkbox") || (inputType == "radio");
114
113
  }
115
114
 
115
+ // For details on the pattern for changing node classes
116
+ // see: https://github.com/knockout/knockout/issues/1597
117
+ var cssClassNameRegex = /\S+/g;
118
+
119
+ function toggleDomNodeCssClass(node, classNames, shouldHaveClass) {
120
+ var addOrRemoveFn;
121
+ if (classNames) {
122
+ if (typeof node.classList === 'object') {
123
+ addOrRemoveFn = node.classList[shouldHaveClass ? 'add' : 'remove'];
124
+ ko.utils.arrayForEach(classNames.match(cssClassNameRegex), function(className) {
125
+ addOrRemoveFn.call(node.classList, className);
126
+ });
127
+ } else if (typeof node.className['baseVal'] === 'string') {
128
+ // SVG tag .classNames is an SVGAnimatedString instance
129
+ toggleObjectClassPropertyString(node.className, 'baseVal', classNames, shouldHaveClass);
130
+ } else {
131
+ // node.className ought to be a string.
132
+ toggleObjectClassPropertyString(node, 'className', classNames, shouldHaveClass);
133
+ }
134
+ }
135
+ }
136
+
137
+ function toggleObjectClassPropertyString(obj, prop, classNames, shouldHaveClass) {
138
+ // obj/prop is either a node/'className' or a SVGAnimatedString/'baseVal'.
139
+ var currentClassNames = obj[prop].match(cssClassNameRegex) || [];
140
+ ko.utils.arrayForEach(classNames.match(cssClassNameRegex), function(className) {
141
+ ko.utils.addOrRemoveItem(currentClassNames, className, shouldHaveClass);
142
+ });
143
+ obj[prop] = currentClassNames.join(" ");
144
+ }
145
+
116
146
  return {
117
147
  fieldsIncludedWithJsonPost: ['authenticity_token', /^__RequestVerificationToken(_.*)?$/],
118
148
 
@@ -226,8 +256,9 @@ ko.utils = (function () {
226
256
  // Ensure it's a real array, as we're about to reparent the nodes and
227
257
  // we don't want the underlying collection to change while we're doing that.
228
258
  var nodesArray = ko.utils.makeArray(nodes);
259
+ var templateDocument = (nodesArray[0] && nodesArray[0].ownerDocument) || document;
229
260
 
230
- var container = document.createElement('div');
261
+ var container = templateDocument.createElement('div');
231
262
  for (var i = 0, j = nodesArray.length; i < j; i++) {
232
263
  container.appendChild(ko.cleanNode(nodesArray[i]));
233
264
  }
@@ -283,7 +314,7 @@ ko.utils = (function () {
283
314
 
284
315
  // Rule [A]
285
316
  while (continuousNodeArray.length && continuousNodeArray[0].parentNode !== parentNode)
286
- continuousNodeArray.shift();
317
+ continuousNodeArray.splice(0, 1);
287
318
 
288
319
  // Rule [B]
289
320
  if (continuousNodeArray.length > 1) {
@@ -412,16 +443,7 @@ ko.utils = (function () {
412
443
  return ko.isObservable(value) ? value.peek() : value;
413
444
  },
414
445
 
415
- toggleDomNodeCssClass: function (node, classNames, shouldHaveClass) {
416
- if (classNames) {
417
- var cssClassNameRegex = /\S+/g,
418
- currentClassNames = node.className.match(cssClassNameRegex) || [];
419
- ko.utils.arrayForEach(classNames.match(cssClassNameRegex), function(className) {
420
- ko.utils.addOrRemoveItem(currentClassNames, className, shouldHaveClass);
421
- });
422
- node.className = currentClassNames.join(" ");
423
- }
424
- },
446
+ toggleDomNodeCssClass: toggleDomNodeCssClass,
425
447
 
426
448
  setTextContent: function(element, textContent) {
427
449
  var value = ko.utils.unwrapObservable(textContent);
@@ -595,16 +617,26 @@ ko.exportSymbol('utils.triggerEvent', ko.utils.triggerEvent);
595
617
  ko.exportSymbol('utils.unwrapObservable', ko.utils.unwrapObservable);
596
618
  ko.exportSymbol('utils.objectForEach', ko.utils.objectForEach);
597
619
  ko.exportSymbol('utils.addOrRemoveItem', ko.utils.addOrRemoveItem);
620
+ ko.exportSymbol('utils.setTextContent', ko.utils.setTextContent);
598
621
  ko.exportSymbol('unwrap', ko.utils.unwrapObservable); // Convenient shorthand, because this is used so commonly
599
622
 
600
623
  if (!Function.prototype['bind']) {
601
624
  // Function.prototype.bind is a standard part of ECMAScript 5th Edition (December 2009, http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf)
602
625
  // In case the browser doesn't implement it natively, provide a JavaScript implementation. This implementation is based on the one in prototype.js
603
626
  Function.prototype['bind'] = function (object) {
604
- var originalFunction = this, args = Array.prototype.slice.call(arguments), object = args.shift();
605
- return function () {
606
- return originalFunction.apply(object, args.concat(Array.prototype.slice.call(arguments)));
607
- };
627
+ var originalFunction = this;
628
+ if (arguments.length === 1) {
629
+ return function () {
630
+ return originalFunction.apply(object, arguments);
631
+ };
632
+ } else {
633
+ var partialArgs = Array.prototype.slice.call(arguments, 1);
634
+ return function () {
635
+ var args = partialArgs.slice(0);
636
+ args.push.apply(args, arguments);
637
+ return originalFunction.apply(object, args);
638
+ };
639
+ }
608
640
  };
609
641
  }
610
642
 
@@ -751,7 +783,7 @@ ko.utils.domNodeDisposal = new (function () {
751
783
  if (jQueryInstance && (typeof jQueryInstance['cleanData'] == "function"))
752
784
  jQueryInstance['cleanData']([node]);
753
785
  }
754
- }
786
+ };
755
787
  })();
756
788
  ko.cleanNode = ko.utils.domNodeDisposal.cleanNode; // Shorthand name for convenience
757
789
  ko.removeNode = ko.utils.domNodeDisposal.removeNode; // Shorthand name for convenience
@@ -763,7 +795,10 @@ ko.exportSymbol('utils.domNodeDisposal.removeDisposeCallback', ko.utils.domNodeD
763
795
  (function () {
764
796
  var leadingCommentRegex = /^(\s*)<!--(.*?)-->/;
765
797
 
766
- function simpleHtmlParse(html) {
798
+ function simpleHtmlParse(html, documentContext) {
799
+ documentContext || (documentContext = document);
800
+ var windowContext = documentContext['parentWindow'] || documentContext['defaultView'] || window;
801
+
767
802
  // Based on jQuery's "clean" function, but only accounting for table-related elements.
768
803
  // If you have referenced jQuery, this won't be used anyway - KO will use jQuery's "clean" function directly
769
804
 
@@ -773,7 +808,7 @@ ko.exportSymbol('utils.domNodeDisposal.removeDisposeCallback', ko.utils.domNodeD
773
808
  // (possibly a text node) in front of the comment. So, KO does not attempt to workaround this IE issue automatically at present.
774
809
 
775
810
  // Trim whitespace, otherwise indexOf won't work as expected
776
- var tags = ko.utils.stringTrim(html).toLowerCase(), div = document.createElement("div");
811
+ var tags = ko.utils.stringTrim(html).toLowerCase(), div = documentContext.createElement("div");
777
812
 
778
813
  // Finds the first match from the left column, and returns the corresponding "wrap" data from the right column
779
814
  var wrap = tags.match(/^<(thead|tbody|tfoot)/) && [1, "<table>", "</table>"] ||
@@ -784,8 +819,8 @@ ko.exportSymbol('utils.domNodeDisposal.removeDisposeCallback', ko.utils.domNodeD
784
819
  // Go to html and back, then peel off extra wrappers
785
820
  // Note that we always prefix with some dummy text, because otherwise, IE<9 will strip out leading comment nodes in descendants. Total madness.
786
821
  var markup = "ignored<div>" + wrap[1] + html + wrap[2] + "</div>";
787
- if (typeof window['innerShiv'] == "function") {
788
- div.appendChild(window['innerShiv'](markup));
822
+ if (typeof windowContext['innerShiv'] == "function") {
823
+ div.appendChild(windowContext['innerShiv'](markup));
789
824
  } else {
790
825
  div.innerHTML = markup;
791
826
  }
@@ -797,13 +832,13 @@ ko.exportSymbol('utils.domNodeDisposal.removeDisposeCallback', ko.utils.domNodeD
797
832
  return ko.utils.makeArray(div.lastChild.childNodes);
798
833
  }
799
834
 
800
- function jQueryHtmlParse(html) {
835
+ function jQueryHtmlParse(html, documentContext) {
801
836
  // jQuery's "parseHTML" function was introduced in jQuery 1.8.0 and is a documented public API.
802
837
  if (jQueryInstance['parseHTML']) {
803
- return jQueryInstance['parseHTML'](html) || []; // Ensure we always return an array and never null
838
+ return jQueryInstance['parseHTML'](html, documentContext) || []; // Ensure we always return an array and never null
804
839
  } else {
805
840
  // For jQuery < 1.8.0, we fall back on the undocumented internal "clean" function.
806
- var elems = jQueryInstance['clean']([html]);
841
+ var elems = jQueryInstance['clean']([html], documentContext);
807
842
 
808
843
  // As of jQuery 1.7.1, jQuery parses the HTML by appending it to some dummy parent nodes held in an in-memory document fragment.
809
844
  // Unfortunately, it never clears the dummy parent nodes from the document fragment, so it leaks memory over time.
@@ -822,9 +857,9 @@ ko.exportSymbol('utils.domNodeDisposal.removeDisposeCallback', ko.utils.domNodeD
822
857
  }
823
858
  }
824
859
 
825
- ko.utils.parseHtmlFragment = function(html) {
826
- return jQueryInstance ? jQueryHtmlParse(html) // As below, benefit from jQuery's optimisations where possible
827
- : simpleHtmlParse(html); // ... otherwise, this simple logic will do in most common cases.
860
+ ko.utils.parseHtmlFragment = function(html, documentContext) {
861
+ return jQueryInstance ? jQueryHtmlParse(html, documentContext) // As below, benefit from jQuery's optimisations where possible
862
+ : simpleHtmlParse(html, documentContext); // ... otherwise, this simple logic will do in most common cases.
828
863
  };
829
864
 
830
865
  ko.utils.setHtml = function(node, html) {
@@ -844,7 +879,7 @@ ko.exportSymbol('utils.domNodeDisposal.removeDisposeCallback', ko.utils.domNodeD
844
879
  jQueryInstance(node)['html'](html);
845
880
  } else {
846
881
  // ... otherwise, use KO's own parsing logic.
847
- var parsedNodes = ko.utils.parseHtmlFragment(html);
882
+ var parsedNodes = ko.utils.parseHtmlFragment(html, node.ownerDocument);
848
883
  for (var i = 0; i < parsedNodes.length; i++)
849
884
  node.appendChild(parsedNodes[i]);
850
885
  }
@@ -1011,7 +1046,7 @@ function applyExtenders(requestedExtenders) {
1011
1046
  ko.exportSymbol('extenders', ko.extenders);
1012
1047
 
1013
1048
  ko.subscription = function (target, callback, disposeCallback) {
1014
- this.target = target;
1049
+ this._target = target;
1015
1050
  this.callback = callback;
1016
1051
  this.disposeCallback = disposeCallback;
1017
1052
  this.isDisposed = false;
@@ -1025,6 +1060,7 @@ ko.subscription.prototype.dispose = function () {
1025
1060
  ko.subscribable = function () {
1026
1061
  ko.utils.setPrototypeOfOrExtend(this, ko.subscribable['fn']);
1027
1062
  this._subscriptions = {};
1063
+ this._versionNumber = 1;
1028
1064
  }
1029
1065
 
1030
1066
  var defaultEvent = "change";
@@ -1054,6 +1090,9 @@ var ko_subscribable_fn = {
1054
1090
 
1055
1091
  "notifySubscribers": function (valueToNotify, event) {
1056
1092
  event = event || defaultEvent;
1093
+ if (event === defaultEvent) {
1094
+ this.updateVersion();
1095
+ }
1057
1096
  if (this.hasSubscriptionsForEvent(event)) {
1058
1097
  try {
1059
1098
  ko.dependencyDetection.begin(); // Begin suppressing dependency detection (by setting the top frame to undefined)
@@ -1069,6 +1108,18 @@ var ko_subscribable_fn = {
1069
1108
  }
1070
1109
  },
1071
1110
 
1111
+ getVersion: function () {
1112
+ return this._versionNumber;
1113
+ },
1114
+
1115
+ hasChanged: function (versionToCheck) {
1116
+ return this.getVersion() !== versionToCheck;
1117
+ },
1118
+
1119
+ updateVersion: function () {
1120
+ ++this._versionNumber;
1121
+ },
1122
+
1072
1123
  limit: function(limitFunction) {
1073
1124
  var self = this, selfIsObservable = ko.isObservable(self),
1074
1125
  isPending, previousValue, pendingValue, beforeChange = 'beforeChange';
@@ -1115,12 +1166,16 @@ var ko_subscribable_fn = {
1115
1166
  return this._subscriptions[event] && this._subscriptions[event].length;
1116
1167
  },
1117
1168
 
1118
- getSubscriptionsCount: function () {
1119
- var total = 0;
1120
- ko.utils.objectForEach(this._subscriptions, function(eventName, subscriptions) {
1121
- total += subscriptions.length;
1122
- });
1123
- return total;
1169
+ getSubscriptionsCount: function (event) {
1170
+ if (event) {
1171
+ return this._subscriptions[event] && this._subscriptions[event].length || 0;
1172
+ } else {
1173
+ var total = 0;
1174
+ ko.utils.objectForEach(this._subscriptions, function(eventName, subscriptions) {
1175
+ total += subscriptions.length;
1176
+ });
1177
+ return total;
1178
+ }
1124
1179
  },
1125
1180
 
1126
1181
  isDifferent: function(oldValue, newValue) {
@@ -1213,6 +1268,8 @@ ko.exportSymbol('computedContext', ko.computedContext);
1213
1268
  ko.exportSymbol('computedContext.getDependenciesCount', ko.computedContext.getDependenciesCount);
1214
1269
  ko.exportSymbol('computedContext.isInitial', ko.computedContext.isInitial);
1215
1270
  ko.exportSymbol('computedContext.isSleeping', ko.computedContext.isSleeping);
1271
+
1272
+ ko.exportSymbol('ignoreDependencies', ko.ignoreDependencies = ko.dependencyDetection.ignore);
1216
1273
  ko.observable = function (initialValue) {
1217
1274
  var _latestValue = initialValue;
1218
1275
 
@@ -1418,15 +1475,27 @@ ko.extenders['trackArrayChanges'] = function(target) {
1418
1475
  }
1419
1476
  var trackingChanges = false,
1420
1477
  cachedDiff = null,
1478
+ arrayChangeSubscription,
1421
1479
  pendingNotifications = 0,
1422
- underlyingSubscribeFunction = target.subscribe;
1480
+ underlyingBeforeSubscriptionAddFunction = target.beforeSubscriptionAdd,
1481
+ underlyingAfterSubscriptionRemoveFunction = target.afterSubscriptionRemove;
1423
1482
 
1424
- // Intercept "subscribe" calls, and for array change events, ensure change tracking is enabled
1425
- target.subscribe = target['subscribe'] = function(callback, callbackTarget, event) {
1483
+ // Watch "subscribe" calls, and for array change events, ensure change tracking is enabled
1484
+ target.beforeSubscriptionAdd = function (event) {
1485
+ if (underlyingBeforeSubscriptionAddFunction)
1486
+ underlyingBeforeSubscriptionAddFunction.call(target, event);
1426
1487
  if (event === arrayChangeEventName) {
1427
1488
  trackChanges();
1428
1489
  }
1429
- return underlyingSubscribeFunction.apply(this, arguments);
1490
+ };
1491
+ // Watch "dispose" calls, and for array change events, ensure change tracking is disabled when all are disposed
1492
+ target.afterSubscriptionRemove = function (event) {
1493
+ if (underlyingAfterSubscriptionRemoveFunction)
1494
+ underlyingAfterSubscriptionRemoveFunction.call(target, event);
1495
+ if (event === arrayChangeEventName && !target.hasSubscriptionsForEvent(arrayChangeEventName)) {
1496
+ arrayChangeSubscription.dispose();
1497
+ trackingChanges = false;
1498
+ }
1430
1499
  };
1431
1500
 
1432
1501
  function trackChanges() {
@@ -1450,22 +1519,23 @@ ko.extenders['trackArrayChanges'] = function(target) {
1450
1519
  // change it's possible to produce a diff
1451
1520
  var previousContents = [].concat(target.peek() || []);
1452
1521
  cachedDiff = null;
1453
- target.subscribe(function(currentContents) {
1522
+ arrayChangeSubscription = target.subscribe(function(currentContents) {
1454
1523
  // Make a copy of the current contents and ensure it's an array
1455
1524
  currentContents = [].concat(currentContents || []);
1456
1525
 
1457
1526
  // Compute the diff and issue notifications, but only if someone is listening
1458
1527
  if (target.hasSubscriptionsForEvent(arrayChangeEventName)) {
1459
1528
  var changes = getChanges(previousContents, currentContents);
1460
- if (changes.length) {
1461
- target['notifySubscribers'](changes, arrayChangeEventName);
1462
- }
1463
1529
  }
1464
1530
 
1465
1531
  // Eliminate references to the old, removed items, so they can be GCed
1466
1532
  previousContents = currentContents;
1467
1533
  cachedDiff = null;
1468
1534
  pendingNotifications = 0;
1535
+
1536
+ if (changes && changes.length) {
1537
+ target['notifySubscribers'](changes, arrayChangeEventName);
1538
+ }
1469
1539
  });
1470
1540
  }
1471
1541
 
@@ -1558,44 +1628,58 @@ ko.computed = ko.dependentObservable = function (evaluatorFunctionOrOptions, eva
1558
1628
  if (typeof readFunction != "function")
1559
1629
  throw new Error("Pass a function that returns the value of the ko.computed");
1560
1630
 
1561
- function addSubscriptionToDependency(subscribable, id) {
1562
- if (!_subscriptionsToDependencies[id]) {
1563
- _subscriptionsToDependencies[id] = subscribable.subscribe(evaluatePossiblyAsync);
1564
- ++_dependenciesCount;
1631
+ function addDependencyTracking(id, target, trackingObj) {
1632
+ if (pure && target === dependentObservable) {
1633
+ throw Error("A 'pure' computed must not be called recursively");
1565
1634
  }
1635
+
1636
+ dependencyTracking[id] = trackingObj;
1637
+ trackingObj._order = _dependenciesCount++;
1638
+ trackingObj._version = target.getVersion();
1566
1639
  }
1567
1640
 
1568
- function disposeAllSubscriptionsToDependencies() {
1569
- ko.utils.objectForEach(_subscriptionsToDependencies, function (id, subscription) {
1570
- subscription.dispose();
1571
- });
1572
- _subscriptionsToDependencies = {};
1641
+ function haveDependenciesChanged() {
1642
+ var id, dependency;
1643
+ for (id in dependencyTracking) {
1644
+ if (dependencyTracking.hasOwnProperty(id)) {
1645
+ dependency = dependencyTracking[id];
1646
+ if (dependency._target.hasChanged(dependency._version)) {
1647
+ return true;
1648
+ }
1649
+ }
1650
+ }
1573
1651
  }
1574
1652
 
1575
1653
  function disposeComputed() {
1576
- disposeAllSubscriptionsToDependencies();
1654
+ if (!isSleeping && dependencyTracking) {
1655
+ ko.utils.objectForEach(dependencyTracking, function (id, dependency) {
1656
+ if (dependency.dispose)
1657
+ dependency.dispose();
1658
+ });
1659
+ }
1660
+ dependencyTracking = null;
1577
1661
  _dependenciesCount = 0;
1578
1662
  _isDisposed = true;
1579
1663
  _needsEvaluation = false;
1664
+ isSleeping = false;
1580
1665
  }
1581
1666
 
1582
1667
  function evaluatePossiblyAsync() {
1583
1668
  var throttleEvaluationTimeout = dependentObservable['throttleEvaluation'];
1584
1669
  if (throttleEvaluationTimeout && throttleEvaluationTimeout >= 0) {
1585
1670
  clearTimeout(evaluationTimeoutInstance);
1586
- evaluationTimeoutInstance = setTimeout(evaluateImmediate, throttleEvaluationTimeout);
1671
+ evaluationTimeoutInstance = setTimeout(function () {
1672
+ evaluateImmediate(true /*notifyChange*/);
1673
+ }, throttleEvaluationTimeout);
1587
1674
  } else if (dependentObservable._evalRateLimited) {
1588
1675
  dependentObservable._evalRateLimited();
1589
1676
  } else {
1590
- evaluateImmediate();
1677
+ evaluateImmediate(true /*notifyChange*/);
1591
1678
  }
1592
1679
  }
1593
1680
 
1594
- function evaluateImmediate(suppressChangeNotification) {
1681
+ function evaluateImmediate(notifyChange) {
1595
1682
  if (_isBeingEvaluated) {
1596
- if (pure) {
1597
- throw Error("A 'pure' computed must not be called recursively");
1598
- }
1599
1683
  // If the evaluation of a ko.computed causes side effects, it's possible that it will trigger its own re-evaluation.
1600
1684
  // This is not desirable (it's hard for a developer to realise a chain of dependencies might cause this, and they almost
1601
1685
  // certainly didn't intend infinite re-evaluations). So, for predictability, we simply prevent ko.computeds from causing
@@ -1621,82 +1705,71 @@ ko.computed = ko.dependentObservable = function (evaluatorFunctionOrOptions, eva
1621
1705
 
1622
1706
  _isBeingEvaluated = true;
1623
1707
 
1624
- // When sleeping, recalculate the value and return.
1625
- if (isSleeping) {
1626
- try {
1627
- var dependencyTracking = {};
1628
- ko.dependencyDetection.begin({
1629
- callback: function (subscribable, id) {
1630
- if (!dependencyTracking[id]) {
1631
- dependencyTracking[id] = 1;
1632
- ++_dependenciesCount;
1708
+ try {
1709
+ // Initially, we assume that none of the subscriptions are still being used (i.e., all are candidates for disposal).
1710
+ // Then, during evaluation, we cross off any that are in fact still being used.
1711
+ var disposalCandidates = dependencyTracking,
1712
+ disposalCount = _dependenciesCount,
1713
+ isInitial = pure ? undefined : !_dependenciesCount; // If we're evaluating when there are no previous dependencies, it must be the first time
1714
+
1715
+ ko.dependencyDetection.begin({
1716
+ callback: function(subscribable, id) {
1717
+ if (!_isDisposed) {
1718
+ if (disposalCount && disposalCandidates[id]) {
1719
+ // Don't want to dispose this subscription, as it's still being used
1720
+ addDependencyTracking(id, subscribable, disposalCandidates[id]);
1721
+ delete disposalCandidates[id];
1722
+ --disposalCount;
1723
+ } else if (!dependencyTracking[id]) {
1724
+ // Brand new subscription - add it
1725
+ addDependencyTracking(id, subscribable, isSleeping ? { _target: subscribable } : subscribable.subscribe(evaluatePossiblyAsync));
1633
1726
  }
1634
- },
1635
- computed: dependentObservable,
1636
- isInitial: undefined
1637
- });
1638
- _dependenciesCount = 0;
1639
- _latestValue = readFunction.call(evaluatorFunctionTarget);
1640
- } finally {
1641
- ko.dependencyDetection.end();
1642
- _isBeingEvaluated = false;
1643
- }
1644
- } else {
1645
- try {
1646
- // Initially, we assume that none of the subscriptions are still being used (i.e., all are candidates for disposal).
1647
- // Then, during evaluation, we cross off any that are in fact still being used.
1648
- var disposalCandidates = _subscriptionsToDependencies, disposalCount = _dependenciesCount;
1649
- ko.dependencyDetection.begin({
1650
- callback: function(subscribable, id) {
1651
- if (!_isDisposed) {
1652
- if (disposalCount && disposalCandidates[id]) {
1653
- // Don't want to dispose this subscription, as it's still being used
1654
- _subscriptionsToDependencies[id] = disposalCandidates[id];
1655
- ++_dependenciesCount;
1656
- delete disposalCandidates[id];
1657
- --disposalCount;
1658
- } else {
1659
- // Brand new subscription - add it
1660
- addSubscriptionToDependency(subscribable, id);
1661
- }
1662
- }
1663
- },
1664
- computed: dependentObservable,
1665
- isInitial: pure ? undefined : !_dependenciesCount // If we're evaluating when there are no previous dependencies, it must be the first time
1666
- });
1727
+ }
1728
+ },
1729
+ computed: dependentObservable,
1730
+ isInitial: isInitial
1731
+ });
1667
1732
 
1668
- _subscriptionsToDependencies = {};
1669
- _dependenciesCount = 0;
1733
+ dependencyTracking = {};
1734
+ _dependenciesCount = 0;
1670
1735
 
1671
- try {
1672
- var newValue = evaluatorFunctionTarget ? readFunction.call(evaluatorFunctionTarget) : readFunction();
1736
+ try {
1737
+ var newValue = evaluatorFunctionTarget ? readFunction.call(evaluatorFunctionTarget) : readFunction();
1673
1738
 
1674
- } finally {
1675
- ko.dependencyDetection.end();
1739
+ } finally {
1740
+ ko.dependencyDetection.end();
1676
1741
 
1677
- // For each subscription no longer being used, remove it from the active subscriptions list and dispose it
1678
- if (disposalCount) {
1679
- ko.utils.objectForEach(disposalCandidates, function(id, toDispose) {
1742
+ // For each subscription no longer being used, remove it from the active subscriptions list and dispose it
1743
+ if (disposalCount && !isSleeping) {
1744
+ ko.utils.objectForEach(disposalCandidates, function(id, toDispose) {
1745
+ if (toDispose.dispose)
1680
1746
  toDispose.dispose();
1681
- });
1682
- }
1683
-
1684
- _needsEvaluation = false;
1747
+ });
1685
1748
  }
1686
1749
 
1687
- if (dependentObservable.isDifferent(_latestValue, newValue)) {
1688
- dependentObservable["notifySubscribers"](_latestValue, "beforeChange");
1750
+ _needsEvaluation = false;
1751
+ }
1689
1752
 
1690
- _latestValue = newValue;
1691
- if (DEBUG) dependentObservable._latestValue = _latestValue;
1753
+ if (dependentObservable.isDifferent(_latestValue, newValue)) {
1754
+ if (!isSleeping) {
1755
+ notify(_latestValue, "beforeChange");
1756
+ }
1692
1757
 
1693
- if (suppressChangeNotification !== true) { // Check for strict true value since setTimeout in Firefox passes a numeric value to the function
1694
- dependentObservable["notifySubscribers"](_latestValue);
1695
- }
1758
+ _latestValue = newValue;
1759
+ if (DEBUG) dependentObservable._latestValue = _latestValue;
1760
+
1761
+ if (isSleeping) {
1762
+ dependentObservable.updateVersion();
1763
+ } else if (notifyChange) {
1764
+ notify(_latestValue);
1696
1765
  }
1697
- } finally {
1698
- _isBeingEvaluated = false;
1699
1766
  }
1767
+
1768
+ if (isInitial) {
1769
+ notify(_latestValue, "awake");
1770
+ }
1771
+ } finally {
1772
+ _isBeingEvaluated = false;
1700
1773
  }
1701
1774
 
1702
1775
  if (!_dependenciesCount)
@@ -1715,17 +1788,18 @@ ko.computed = ko.dependentObservable = function (evaluatorFunctionOrOptions, eva
1715
1788
  } else {
1716
1789
  // Reading the value
1717
1790
  ko.dependencyDetection.registerDependency(dependentObservable);
1718
- if (_needsEvaluation)
1719
- evaluateImmediate(true /* suppressChangeNotification */);
1791
+ if (_needsEvaluation || (isSleeping && haveDependenciesChanged())) {
1792
+ evaluateImmediate();
1793
+ }
1720
1794
  return _latestValue;
1721
1795
  }
1722
1796
  }
1723
1797
 
1724
1798
  function peek() {
1725
- // Peek won't re-evaluate, except to get the initial value when "deferEvaluation" is set, or while the computed is sleeping.
1726
- // Those are the only times that both of these conditions will be satisfied.
1727
- if (_needsEvaluation && !_dependenciesCount)
1728
- evaluateImmediate(true /* suppressChangeNotification */);
1799
+ // Peek won't re-evaluate, except while the computed is sleeping or to get the initial value when "deferEvaluation" is set.
1800
+ if ((_needsEvaluation && !_dependenciesCount) || (isSleeping && haveDependenciesChanged())) {
1801
+ evaluateImmediate();
1802
+ }
1729
1803
  return _latestValue;
1730
1804
  }
1731
1805
 
@@ -1733,13 +1807,17 @@ ko.computed = ko.dependentObservable = function (evaluatorFunctionOrOptions, eva
1733
1807
  return _needsEvaluation || _dependenciesCount > 0;
1734
1808
  }
1735
1809
 
1810
+ function notify(value, event) {
1811
+ dependentObservable["notifySubscribers"](value, event);
1812
+ }
1813
+
1736
1814
  // By here, "options" is always non-null
1737
1815
  var writeFunction = options["write"],
1738
1816
  disposeWhenNodeIsRemoved = options["disposeWhenNodeIsRemoved"] || options.disposeWhenNodeIsRemoved || null,
1739
1817
  disposeWhenOption = options["disposeWhen"] || options.disposeWhen,
1740
1818
  disposeWhen = disposeWhenOption,
1741
1819
  dispose = disposeComputed,
1742
- _subscriptionsToDependencies = {},
1820
+ dependencyTracking = {},
1743
1821
  _dependenciesCount = 0,
1744
1822
  evaluationTimeoutInstance = null;
1745
1823
 
@@ -1751,7 +1829,7 @@ ko.computed = ko.dependentObservable = function (evaluatorFunctionOrOptions, eva
1751
1829
 
1752
1830
  dependentObservable.peek = peek;
1753
1831
  dependentObservable.getDependenciesCount = function () { return _dependenciesCount; };
1754
- dependentObservable.hasWriteFunction = typeof options["write"] === "function";
1832
+ dependentObservable.hasWriteFunction = typeof writeFunction === "function";
1755
1833
  dependentObservable.dispose = function () { dispose(); };
1756
1834
  dependentObservable.isActive = isActive;
1757
1835
 
@@ -1773,24 +1851,69 @@ ko.computed = ko.dependentObservable = function (evaluatorFunctionOrOptions, eva
1773
1851
  if (options['pure']) {
1774
1852
  pure = true;
1775
1853
  isSleeping = true; // Starts off sleeping; will awake on the first subscription
1776
- dependentObservable.beforeSubscriptionAdd = function () {
1777
- // If asleep, wake up the computed and evaluate to register any dependencies.
1778
- if (isSleeping) {
1854
+ dependentObservable.beforeSubscriptionAdd = function (event) {
1855
+ // If asleep, wake up the computed by subscribing to any dependencies.
1856
+ if (!_isDisposed && isSleeping && event == 'change') {
1779
1857
  isSleeping = false;
1780
- evaluateImmediate(true /* suppressChangeNotification */);
1858
+ if (_needsEvaluation || haveDependenciesChanged()) {
1859
+ dependencyTracking = null;
1860
+ _dependenciesCount = 0;
1861
+ _needsEvaluation = true;
1862
+ evaluateImmediate();
1863
+ } else {
1864
+ // First put the dependencies in order
1865
+ var dependeciesOrder = [];
1866
+ ko.utils.objectForEach(dependencyTracking, function (id, dependency) {
1867
+ dependeciesOrder[dependency._order] = id;
1868
+ });
1869
+ // Next, subscribe to each one
1870
+ ko.utils.arrayForEach(dependeciesOrder, function(id, order) {
1871
+ var dependency = dependencyTracking[id],
1872
+ subscription = dependency._target.subscribe(evaluatePossiblyAsync);
1873
+ subscription._order = order;
1874
+ subscription._version = dependency._version;
1875
+ dependencyTracking[id] = subscription;
1876
+ });
1877
+ }
1878
+ if (!_isDisposed) { // test since evaluating could trigger disposal
1879
+ notify(_latestValue, "awake");
1880
+ }
1781
1881
  }
1782
- }
1783
- dependentObservable.afterSubscriptionRemove = function () {
1784
- if (!dependentObservable.getSubscriptionsCount()) {
1785
- disposeAllSubscriptionsToDependencies();
1786
- isSleeping = _needsEvaluation = true;
1882
+ };
1883
+
1884
+ dependentObservable.afterSubscriptionRemove = function (event) {
1885
+ if (!_isDisposed && event == 'change' && !dependentObservable.hasSubscriptionsForEvent('change')) {
1886
+ ko.utils.objectForEach(dependencyTracking, function (id, dependency) {
1887
+ if (dependency.dispose) {
1888
+ dependencyTracking[id] = {
1889
+ _target: dependency._target,
1890
+ _order: dependency._order,
1891
+ _version: dependency._version
1892
+ };
1893
+ dependency.dispose();
1894
+ }
1895
+ });
1896
+ isSleeping = true;
1897
+ notify(undefined, "asleep");
1787
1898
  }
1788
- }
1899
+ };
1900
+
1901
+ // Because a pure computed is not automatically updated while it is sleeping, we can't
1902
+ // simply return the version number. Instead, we check if any of the dependencies have
1903
+ // changed and conditionally re-evaluate the computed observable.
1904
+ dependentObservable._originalGetVersion = dependentObservable.getVersion;
1905
+ dependentObservable.getVersion = function () {
1906
+ if (isSleeping && (_needsEvaluation || haveDependenciesChanged())) {
1907
+ evaluateImmediate();
1908
+ }
1909
+ return dependentObservable._originalGetVersion();
1910
+ };
1789
1911
  } else if (options['deferEvaluation']) {
1790
1912
  // This will force a computed with deferEvaluation to evaluate when the first subscriptions is registered.
1791
- dependentObservable.beforeSubscriptionAdd = function () {
1792
- peek();
1793
- delete dependentObservable.beforeSubscriptionAdd;
1913
+ dependentObservable.beforeSubscriptionAdd = function (event) {
1914
+ if (event == 'change' || event == 'beforeChange') {
1915
+ peek();
1916
+ }
1794
1917
  }
1795
1918
  }
1796
1919
 
@@ -2085,7 +2208,7 @@ ko.expressionRewriting = (function () {
2085
2208
  if (str.charCodeAt(0) === 123) str = str.slice(1, -1);
2086
2209
 
2087
2210
  // Split into tokens
2088
- var result = [], toks = str.match(bindingToken), key, values, depth = 0;
2211
+ var result = [], toks = str.match(bindingToken), key, values = [], depth = 0;
2089
2212
 
2090
2213
  if (toks) {
2091
2214
  // Append a comma so that we don't need a separate code block to deal with the last item
@@ -2096,15 +2219,17 @@ ko.expressionRewriting = (function () {
2096
2219
  // A comma signals the end of a key/value pair if depth is zero
2097
2220
  if (c === 44) { // ","
2098
2221
  if (depth <= 0) {
2099
- if (key)
2100
- result.push(values ? {key: key, value: values.join('')} : {'unknown': key});
2101
- key = values = depth = 0;
2222
+ result.push((key && values.length) ? {key: key, value: values.join('')} : {'unknown': key || values.join('')});
2223
+ key = depth = 0;
2224
+ values = [];
2102
2225
  continue;
2103
2226
  }
2104
2227
  // Simply skip the colon that separates the name and value
2105
2228
  } else if (c === 58) { // ":"
2106
- if (!values)
2229
+ if (!depth && !key && values.length === 1) {
2230
+ key = values.pop();
2107
2231
  continue;
2232
+ }
2108
2233
  // A set of slashes is initially matched as a regular expression, but could be division
2109
2234
  } else if (c === 47 && i && tok.length > 1) { // "/"
2110
2235
  // Look at the end of the previous token to determine if the slash is actually division
@@ -2123,15 +2248,11 @@ ko.expressionRewriting = (function () {
2123
2248
  ++depth;
2124
2249
  } else if (c === 41 || c === 125 || c === 93) { // ')', '}', ']'
2125
2250
  --depth;
2126
- // The key must be a single token; if it's a string, trim the quotes
2127
- } else if (!key && !values) {
2128
- key = (c === 34 || c === 39) /* '"', "'" */ ? tok.slice(1, -1) : tok;
2129
- continue;
2251
+ // The key will be the first token; if it's a string, trim the quotes
2252
+ } else if (!key && !values.length && (c === 34 || c === 39)) { // '"', "'"
2253
+ tok = tok.slice(1, -1);
2130
2254
  }
2131
- if (values)
2132
- values.push(tok);
2133
- else
2134
- values = [tok];
2255
+ values.push(tok);
2135
2256
  }
2136
2257
  }
2137
2258
  return result;
@@ -2512,9 +2633,10 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
2512
2633
  // may consider adding <template> to this list, because such elements' contents are always
2513
2634
  // intended to be bound in a different context from where they appear in the document.
2514
2635
  var bindingDoesNotRecurseIntoElementTypes = {
2515
- // Don't want bindings that operate on text nodes to mutate <script> contents,
2636
+ // Don't want bindings that operate on text nodes to mutate <script> and <textarea> contents,
2516
2637
  // because it's unexpected and a potential XSS issue
2517
- 'script': true
2638
+ 'script': true,
2639
+ 'textarea': true
2518
2640
  };
2519
2641
 
2520
2642
  // Use an overridable method for retrieving binding handlers so that a plugins may support dynamically created handlers
@@ -2978,8 +3100,15 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
2978
3100
  var cachedDefinition = getObjectOwnProperty(loadedDefinitionsCache, componentName);
2979
3101
  if (cachedDefinition) {
2980
3102
  // It's already loaded and cached. Reuse the same definition object.
2981
- // Note that for API consistency, even cache hits complete asynchronously.
2982
- setTimeout(function() { callback(cachedDefinition) }, 0);
3103
+ // Note that for API consistency, even cache hits complete asynchronously by default.
3104
+ // You can bypass this by putting synchronous:true on your component config.
3105
+ if (cachedDefinition.isSynchronousComponent) {
3106
+ ko.dependencyDetection.ignore(function() { // See comment in loaderRegistryBehaviors.js for reasoning
3107
+ callback(cachedDefinition.definition);
3108
+ });
3109
+ } else {
3110
+ setTimeout(function() { callback(cachedDefinition.definition); }, 0);
3111
+ }
2983
3112
  } else {
2984
3113
  // Join the loading process that is already underway, or start a new one.
2985
3114
  loadComponentAndNotify(componentName, callback);
@@ -3003,14 +3132,22 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
3003
3132
  if (!subscribable) {
3004
3133
  // It's not started loading yet. Start loading, and when it's done, move it to loadedDefinitionsCache.
3005
3134
  subscribable = loadingSubscribablesCache[componentName] = new ko.subscribable();
3006
- beginLoadingComponent(componentName, function(definition) {
3007
- loadedDefinitionsCache[componentName] = definition;
3135
+ subscribable.subscribe(callback);
3136
+
3137
+ beginLoadingComponent(componentName, function(definition, config) {
3138
+ var isSynchronousComponent = !!(config && config['synchronous']);
3139
+ loadedDefinitionsCache[componentName] = { definition: definition, isSynchronousComponent: isSynchronousComponent };
3008
3140
  delete loadingSubscribablesCache[componentName];
3009
3141
 
3010
3142
  // For API consistency, all loads complete asynchronously. However we want to avoid
3011
3143
  // adding an extra setTimeout if it's unnecessary (i.e., the completion is already
3012
3144
  // async) since setTimeout(..., 0) still takes about 16ms or more on most browsers.
3013
- if (completedAsync) {
3145
+ //
3146
+ // You can bypass the 'always synchronous' feature by putting the synchronous:true
3147
+ // flag on your component configuration when you register it.
3148
+ if (completedAsync || isSynchronousComponent) {
3149
+ // Note that notifySubscribers ignores any dependencies read within the callback.
3150
+ // See comment in loaderRegistryBehaviors.js for reasoning
3014
3151
  subscribable['notifySubscribers'](definition);
3015
3152
  } else {
3016
3153
  setTimeout(function() {
@@ -3019,8 +3156,9 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
3019
3156
  }
3020
3157
  });
3021
3158
  completedAsync = true;
3159
+ } else {
3160
+ subscribable.subscribe(callback);
3022
3161
  }
3023
- subscribable.subscribe(callback);
3024
3162
  }
3025
3163
 
3026
3164
  function beginLoadingComponent(componentName, callback) {
@@ -3028,14 +3166,14 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
3028
3166
  if (config) {
3029
3167
  // We have a config, so now load its definition
3030
3168
  getFirstResultFromLoaders('loadComponent', [componentName, config], function(definition) {
3031
- callback(definition);
3169
+ callback(definition, config);
3032
3170
  });
3033
3171
  } else {
3034
3172
  // The component has no config - it's unknown to all the loaders.
3035
3173
  // Note that this is not an error (e.g., a module loading error) - that would abort the
3036
3174
  // process and this callback would not run. For this callback to run, all loaders must
3037
3175
  // have confirmed they don't know about this component.
3038
- callback(null);
3176
+ callback(null, null);
3039
3177
  }
3040
3178
  });
3041
3179
  }
@@ -3291,8 +3429,8 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
3291
3429
  function possiblyGetConfigFromAmd(errorCallback, config, callback) {
3292
3430
  if (typeof config['require'] === 'string') {
3293
3431
  // The config is the value of an AMD module
3294
- if (require || window['require']) {
3295
- (require || window['require'])([config['require']], callback);
3432
+ if (amdRequire || window['require']) {
3433
+ (amdRequire || window['require'])([config['require']], callback);
3296
3434
  } else {
3297
3435
  errorCallback('Uses require, but no AMD loader is present');
3298
3436
  }
@@ -3364,18 +3502,26 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
3364
3502
  return ko.computed(paramValue, null, { disposeWhenNodeIsRemoved: elem });
3365
3503
  }),
3366
3504
  result = ko.utils.objectMap(rawParamComputedValues, function(paramValueComputed, paramName) {
3505
+ var paramValue = paramValueComputed.peek();
3367
3506
  // Does the evaluation of the parameter value unwrap any observables?
3368
3507
  if (!paramValueComputed.isActive()) {
3369
3508
  // No it doesn't, so there's no need for any computed wrapper. Just pass through the supplied value directly.
3370
3509
  // Example: "someVal: firstName, age: 123" (whether or not firstName is an observable/computed)
3371
- return paramValueComputed.peek();
3510
+ return paramValue;
3372
3511
  } else {
3373
3512
  // Yes it does. Supply a computed property that unwraps both the outer (binding expression)
3374
3513
  // level of observability, and any inner (resulting model value) level of observability.
3375
- // This means the component doesn't have to worry about multiple unwrapping.
3376
- return ko.computed(function() {
3377
- return ko.utils.unwrapObservable(paramValueComputed());
3378
- }, null, { disposeWhenNodeIsRemoved: elem });
3514
+ // This means the component doesn't have to worry about multiple unwrapping. If the value is a
3515
+ // writable observable, the computed will also be writable and pass the value on to the observable.
3516
+ return ko.computed({
3517
+ 'read': function() {
3518
+ return ko.utils.unwrapObservable(paramValueComputed());
3519
+ },
3520
+ 'write': ko.isWriteableObservable(paramValue) && function(value) {
3521
+ paramValueComputed()(value);
3522
+ },
3523
+ disposeWhenNodeIsRemoved: elem
3524
+ });
3379
3525
  }
3380
3526
  });
3381
3527
 
@@ -3438,7 +3584,8 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
3438
3584
 
3439
3585
  // Any in-flight loading operation is no longer relevant, so make sure we ignore its completion
3440
3586
  currentLoadingOperationId = null;
3441
- };
3587
+ },
3588
+ originalChildNodes = ko.utils.makeArray(ko.virtualElements.childNodes(element));
3442
3589
 
3443
3590
  ko.utils.domNodeDisposal.addDisposeCallback(element, disposeAssociatedComponentViewModel);
3444
3591
 
@@ -3472,8 +3619,11 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
3472
3619
  throw new Error('Unknown component \'' + componentName + '\'');
3473
3620
  }
3474
3621
  cloneTemplateIntoElement(componentName, componentDefinition, element);
3475
- var componentViewModel = createViewModel(componentDefinition, element, componentParams),
3476
- childBindingContext = bindingContext['createChildContext'](componentViewModel);
3622
+ var componentViewModel = createViewModel(componentDefinition, element, originalChildNodes, componentParams),
3623
+ childBindingContext = bindingContext['createChildContext'](componentViewModel, /* dataItemAlias */ undefined, function(ctx) {
3624
+ ctx['$component'] = componentViewModel;
3625
+ ctx['$componentTemplateNodes'] = originalChildNodes;
3626
+ });
3477
3627
  currentViewModel = componentViewModel;
3478
3628
  ko.applyBindingsToDescendants(childBindingContext, element);
3479
3629
  });
@@ -3495,10 +3645,10 @@ ko.exportSymbol('bindingProvider', ko.bindingProvider);
3495
3645
  ko.virtualElements.setDomNodeChildren(element, clonedNodesArray);
3496
3646
  }
3497
3647
 
3498
- function createViewModel(componentDefinition, element, componentParams) {
3648
+ function createViewModel(componentDefinition, element, originalChildNodes, componentParams) {
3499
3649
  var componentViewModelFactory = componentDefinition['createViewModel'];
3500
3650
  return componentViewModelFactory
3501
- ? componentViewModelFactory.call(componentDefinition, componentParams, { element: element })
3651
+ ? componentViewModelFactory.call(componentDefinition, componentParams, { 'element': element, 'templateNodes': originalChildNodes })
3502
3652
  : componentParams; // Template-only component
3503
3653
  }
3504
3654
 
@@ -3651,7 +3801,7 @@ ko.bindingHandlers['checkedValue'] = {
3651
3801
  ko.bindingHandlers['css'] = {
3652
3802
  'update': function (element, valueAccessor) {
3653
3803
  var value = ko.utils.unwrapObservable(valueAccessor());
3654
- if (typeof value == "object") {
3804
+ if (value !== null && typeof value == "object") {
3655
3805
  ko.utils.objectForEach(value, function(className, shouldHaveClass) {
3656
3806
  shouldHaveClass = ko.utils.unwrapObservable(shouldHaveClass);
3657
3807
  ko.utils.toggleDomNodeCssClass(element, className, shouldHaveClass);
@@ -3893,19 +4043,23 @@ ko.bindingHandlers['options'] = {
3893
4043
  return ko.utils.arrayFilter(element.options, function (node) { return node.selected; });
3894
4044
  }
3895
4045
 
3896
- var selectWasPreviouslyEmpty = element.length == 0;
3897
- var previousScrollTop = (!selectWasPreviouslyEmpty && element.multiple) ? element.scrollTop : null;
3898
- var unwrappedArray = ko.utils.unwrapObservable(valueAccessor());
3899
- var includeDestroyed = allBindings.get('optionsIncludeDestroyed');
3900
- var arrayToDomNodeChildrenOptions = {};
3901
- var captionValue;
3902
- var filteredArray;
3903
- var previousSelectedValues;
3904
-
3905
- if (element.multiple) {
3906
- previousSelectedValues = ko.utils.arrayMap(selectedOptions(), ko.selectExtensions.readValue);
3907
- } else {
3908
- previousSelectedValues = element.selectedIndex >= 0 ? [ ko.selectExtensions.readValue(element.options[element.selectedIndex]) ] : [];
4046
+ var selectWasPreviouslyEmpty = element.length == 0,
4047
+ multiple = element.multiple,
4048
+ previousScrollTop = (!selectWasPreviouslyEmpty && multiple) ? element.scrollTop : null,
4049
+ unwrappedArray = ko.utils.unwrapObservable(valueAccessor()),
4050
+ valueAllowUnset = allBindings.get('valueAllowUnset') && allBindings['has']('value'),
4051
+ includeDestroyed = allBindings.get('optionsIncludeDestroyed'),
4052
+ arrayToDomNodeChildrenOptions = {},
4053
+ captionValue,
4054
+ filteredArray,
4055
+ previousSelectedValues = [];
4056
+
4057
+ if (!valueAllowUnset) {
4058
+ if (multiple) {
4059
+ previousSelectedValues = ko.utils.arrayMap(selectedOptions(), ko.selectExtensions.readValue);
4060
+ } else if (element.selectedIndex >= 0) {
4061
+ previousSelectedValues.push(ko.selectExtensions.readValue(element.options[element.selectedIndex]));
4062
+ }
3909
4063
  }
3910
4064
 
3911
4065
  if (unwrappedArray) {
@@ -3946,7 +4100,7 @@ ko.bindingHandlers['options'] = {
3946
4100
  var itemUpdate = false;
3947
4101
  function optionForArrayItem(arrayEntry, index, oldOptions) {
3948
4102
  if (oldOptions.length) {
3949
- previousSelectedValues = oldOptions[0].selected ? [ ko.selectExtensions.readValue(oldOptions[0]) ] : [];
4103
+ previousSelectedValues = !valueAllowUnset && oldOptions[0].selected ? [ ko.selectExtensions.readValue(oldOptions[0]) ] : [];
3950
4104
  itemUpdate = true;
3951
4105
  }
3952
4106
  var option = element.ownerDocument.createElement("option");
@@ -3973,20 +4127,25 @@ ko.bindingHandlers['options'] = {
3973
4127
  };
3974
4128
 
3975
4129
  function setSelectionCallback(arrayEntry, newOptions) {
3976
- // IE6 doesn't like us to assign selection to OPTION nodes before they're added to the document.
3977
- // That's why we first added them without selection. Now it's time to set the selection.
3978
- if (previousSelectedValues.length) {
4130
+ if (itemUpdate && valueAllowUnset) {
4131
+ // The model value is authoritative, so make sure its value is the one selected
4132
+ // There is no need to use dependencyDetection.ignore since setDomNodeChildrenFromArrayMapping does so already.
4133
+ ko.selectExtensions.writeValue(element, ko.utils.unwrapObservable(allBindings.get('value')), true /* allowUnset */);
4134
+ } else if (previousSelectedValues.length) {
4135
+ // IE6 doesn't like us to assign selection to OPTION nodes before they're added to the document.
4136
+ // That's why we first added them without selection. Now it's time to set the selection.
3979
4137
  var isSelected = ko.utils.arrayIndexOf(previousSelectedValues, ko.selectExtensions.readValue(newOptions[0])) >= 0;
3980
4138
  ko.utils.setOptionNodeSelectionState(newOptions[0], isSelected);
3981
4139
 
3982
4140
  // If this option was changed from being selected during a single-item update, notify the change
3983
- if (itemUpdate && !isSelected)
4141
+ if (itemUpdate && !isSelected) {
3984
4142
  ko.dependencyDetection.ignore(ko.utils.triggerEvent, null, [element, "change"]);
4143
+ }
3985
4144
  }
3986
4145
  }
3987
4146
 
3988
4147
  var callback = setSelectionCallback;
3989
- if (allBindings['has']('optionsAfterRender')) {
4148
+ if (allBindings['has']('optionsAfterRender') && typeof allBindings.get('optionsAfterRender') == "function") {
3990
4149
  callback = function(arrayEntry, newOptions) {
3991
4150
  setSelectionCallback(arrayEntry, newOptions);
3992
4151
  ko.dependencyDetection.ignore(allBindings.get('optionsAfterRender'), null, [newOptions[0], arrayEntry !== captionPlaceholder ? arrayEntry : undefined]);
@@ -3996,13 +4155,13 @@ ko.bindingHandlers['options'] = {
3996
4155
  ko.utils.setDomNodeChildrenFromArrayMapping(element, filteredArray, optionForArrayItem, arrayToDomNodeChildrenOptions, callback);
3997
4156
 
3998
4157
  ko.dependencyDetection.ignore(function () {
3999
- if (allBindings.get('valueAllowUnset') && allBindings['has']('value')) {
4158
+ if (valueAllowUnset) {
4000
4159
  // The model value is authoritative, so make sure its value is the one selected
4001
4160
  ko.selectExtensions.writeValue(element, ko.utils.unwrapObservable(allBindings.get('value')), true /* allowUnset */);
4002
4161
  } else {
4003
4162
  // Determine if the selection has changed as a result of updating the options list
4004
4163
  var selectionChanged;
4005
- if (element.multiple) {
4164
+ if (multiple) {
4006
4165
  // For a multiple-select box, compare the new selection count to the previous one
4007
4166
  // But if nothing was selected before, the selection can't have changed
4008
4167
  selectionChanged = previousSelectedValues.length && selectedOptions().length < previousSelectedValues.length;
@@ -4417,6 +4576,7 @@ makeEventHandlerShortcut('click');
4417
4576
  // // - you might also want to make bindingContext.$parent, bindingContext.$parents,
4418
4577
  // // and bindingContext.$root available in the template too
4419
4578
  // // - options gives you access to any other properties set on "data-bind: { template: options }"
4579
+ // // - templateDocument is the document object of the template
4420
4580
  // //
4421
4581
  // // Return value: an array of DOM nodes
4422
4582
  // }
@@ -4434,7 +4594,7 @@ makeEventHandlerShortcut('click');
4434
4594
 
4435
4595
  ko.templateEngine = function () { };
4436
4596
 
4437
- ko.templateEngine.prototype['renderTemplateSource'] = function (templateSource, bindingContext, options) {
4597
+ ko.templateEngine.prototype['renderTemplateSource'] = function (templateSource, bindingContext, options, templateDocument) {
4438
4598
  throw new Error("Override renderTemplateSource");
4439
4599
  };
4440
4600
 
@@ -4459,7 +4619,7 @@ ko.templateEngine.prototype['makeTemplateSource'] = function(template, templateD
4459
4619
 
4460
4620
  ko.templateEngine.prototype['renderTemplate'] = function (template, bindingContext, options, templateDocument) {
4461
4621
  var templateSource = this['makeTemplateSource'](template, templateDocument);
4462
- return this['renderTemplateSource'](templateSource, bindingContext, options);
4622
+ return this['renderTemplateSource'](templateSource, bindingContext, options, templateDocument);
4463
4623
  };
4464
4624
 
4465
4625
  ko.templateEngine.prototype['isTemplateRewritten'] = function (template, templateDocument) {
@@ -4479,7 +4639,7 @@ ko.templateEngine.prototype['rewriteTemplate'] = function (template, rewriterCal
4479
4639
  ko.exportSymbol('templateEngine', ko.templateEngine);
4480
4640
 
4481
4641
  ko.templateRewriting = (function () {
4482
- var memoizeDataBindingAttributeSyntaxRegex = /(<([a-z]+\d*)(?:\s+(?!data-bind\s*=\s*)[a-z0-9\-]+(?:=(?:\"[^\"]*\"|\'[^\']*\'))?)*\s+)data-bind\s*=\s*(["'])([\s\S]*?)\3/gi;
4642
+ var memoizeDataBindingAttributeSyntaxRegex = /(<([a-z]+\d*)(?:\s+(?!data-bind\s*=\s*)[a-z0-9\-]+(?:=(?:\"[^\"]*\"|\'[^\']*\'|[^>]*))?)*\s+)data-bind\s*=\s*(["'])([\s\S]*?)\3/gi;
4483
4643
  var memoizeVirtualContainerBindingSyntaxRegex = /<!--\s*ko\b\s*([\s\S]*?)\s*-->/g;
4484
4644
 
4485
4645
  function validateDataBindValuesForRewriting(keyValueArray) {
@@ -4554,10 +4714,10 @@ ko.exportSymbol('__tr_ambtns', ko.templateRewriting.applyMemoizedBindingsToNextS
4554
4714
  // with the rendered template output.
4555
4715
  // You can implement your own template source if you want to fetch/store templates somewhere other than in DOM elements.
4556
4716
  // Template sources need to have the following functions:
4557
- // text() - returns the template text from your storage location
4558
- // text(value) - writes the supplied template text to your storage location
4559
- // data(key) - reads values stored using data(key, value) - see below
4560
- // data(key, value) - associates "value" with this template and the key "key". Is used to store information like "isRewritten".
4717
+ // text() - returns the template text from your storage location
4718
+ // text(value) - writes the supplied template text to your storage location
4719
+ // data(key) - reads values stored using data(key, value) - see below
4720
+ // data(key, value) - associates "value" with this template and the key "key". Is used to store information like "isRewritten".
4561
4721
  //
4562
4722
  // Optionally, template sources can also have the following functions:
4563
4723
  // nodes() - returns a DOM element containing the nodes of this template, where available
@@ -4720,7 +4880,7 @@ ko.exportSymbol('__tr_ambtns', ko.templateRewriting.applyMemoizedBindingsToNextS
4720
4880
  function executeTemplate(targetNodeOrNodeArray, renderMode, template, bindingContext, options) {
4721
4881
  options = options || {};
4722
4882
  var firstTargetNode = targetNodeOrNodeArray && getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
4723
- var templateDocument = firstTargetNode && firstTargetNode.ownerDocument;
4883
+ var templateDocument = (firstTargetNode || template || {}).ownerDocument;
4724
4884
  var templateEngineToUse = (options['templateEngine'] || _templateEngine);
4725
4885
  ko.templateRewriting.ensureTemplateIsRewritten(template, templateEngineToUse, templateDocument);
4726
4886
  var renderedNodesArray = templateEngineToUse['renderTemplate'](template, bindingContext, options, templateDocument);
@@ -4826,6 +4986,10 @@ ko.exportSymbol('__tr_ambtns', ko.templateRewriting.applyMemoizedBindingsToNextS
4826
4986
  activateBindingsOnContinuousNodeArray(addedNodesArray, arrayItemContext);
4827
4987
  if (options['afterRender'])
4828
4988
  options['afterRender'](addedNodesArray, arrayValue);
4989
+
4990
+ // release the "cache" variable, so that it can be collected by
4991
+ // the GC when its value isn't used from within the bindings anymore.
4992
+ arrayItemContext = null;
4829
4993
  };
4830
4994
 
4831
4995
  return ko.dependentObservable(function () {
@@ -4860,6 +5024,17 @@ ko.exportSymbol('__tr_ambtns', ko.templateRewriting.applyMemoizedBindingsToNextS
4860
5024
  if (typeof bindingValue == "string" || bindingValue['name']) {
4861
5025
  // It's a named template - clear the element
4862
5026
  ko.virtualElements.emptyNode(element);
5027
+ } else if ('nodes' in bindingValue) {
5028
+ // We've been given an array of DOM nodes. Save them as the template source.
5029
+ // There is no known use case for the node array being an observable array (if the output
5030
+ // varies, put that behavior *into* your template - that's what templates are for), and
5031
+ // the implementation would be a mess, so assert that it's not observable.
5032
+ var nodes = bindingValue['nodes'] || [];
5033
+ if (ko.isObservable(nodes)) {
5034
+ throw new Error('The "nodes" option must be a plain, non-observable array.');
5035
+ }
5036
+ var container = ko.utils.moveCleanedNodesToContainerElement(nodes); // This also removes the nodes from their current parent
5037
+ new ko.templateSources.anonymousTemplate(element)['nodes'](container);
4863
5038
  } else {
4864
5039
  // It's an anonymous template - store the element contents, then clear the element
4865
5040
  var templateNodes = ko.virtualElements.childNodes(element),
@@ -5194,7 +5369,7 @@ ko.nativeTemplateEngine = function () {
5194
5369
 
5195
5370
  ko.nativeTemplateEngine.prototype = new ko.templateEngine();
5196
5371
  ko.nativeTemplateEngine.prototype.constructor = ko.nativeTemplateEngine;
5197
- ko.nativeTemplateEngine.prototype['renderTemplateSource'] = function (templateSource, bindingContext, options) {
5372
+ ko.nativeTemplateEngine.prototype['renderTemplateSource'] = function (templateSource, bindingContext, options, templateDocument) {
5198
5373
  var useNodesIfAvailable = !(ko.utils.ieVersion < 9), // IE<9 cloneNode doesn't work properly
5199
5374
  templateNodesFunc = useNodesIfAvailable ? templateSource['nodes'] : null,
5200
5375
  templateNodes = templateNodesFunc ? templateSource['nodes']() : null;
@@ -5203,7 +5378,7 @@ ko.nativeTemplateEngine.prototype['renderTemplateSource'] = function (templateSo
5203
5378
  return ko.utils.makeArray(templateNodes.cloneNode(true).childNodes);
5204
5379
  } else {
5205
5380
  var templateText = templateSource['text']();
5206
- return ko.utils.parseHtmlFragment(templateText);
5381
+ return ko.utils.parseHtmlFragment(templateText, templateDocument);
5207
5382
  }
5208
5383
  };
5209
5384
 
@@ -5240,7 +5415,8 @@ ko.exportSymbol('nativeTemplateEngine', ko.nativeTemplateEngine);
5240
5415
  return jQueryInstance['tmpl'](compiledTemplate, data, jQueryTemplateOptions);
5241
5416
  }
5242
5417
 
5243
- this['renderTemplateSource'] = function(templateSource, bindingContext, options) {
5418
+ this['renderTemplateSource'] = function(templateSource, bindingContext, options, templateDocument) {
5419
+ templateDocument = templateDocument || document;
5244
5420
  options = options || {};
5245
5421
  ensureHasReferencedJQueryTemplates();
5246
5422
 
@@ -5259,7 +5435,7 @@ ko.exportSymbol('nativeTemplateEngine', ko.nativeTemplateEngine);
5259
5435
  var jQueryTemplateOptions = jQueryInstance['extend']({ 'koBindingContext': bindingContext }, options['templateOptions']);
5260
5436
 
5261
5437
  var resultNodes = executeTemplate(precompiled, data, jQueryTemplateOptions);
5262
- resultNodes['appendTo'](document.createElement("div")); // Using "appendTo" forces jQuery/jQuery.tmpl to perform necessary cleanup work
5438
+ resultNodes['appendTo'](templateDocument.createElement("div")); // Using "appendTo" forces jQuery/jQuery.tmpl to perform necessary cleanup work
5263
5439
 
5264
5440
  jQueryInstance['fragments'] = {}; // Clear jQuery's fragment cache to avoid a memory leak after a large number of template renders
5265
5441
  return resultNodes;
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kea-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 1.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jan-Christian Foeh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-09 00:00:00.000000000 Z
11
+ date: 2015-02-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -42,6 +42,7 @@ files:
42
42
  - app/assets/javascripts/kea/bindings/submit_button.js
43
43
  - app/assets/javascripts/kea/bindings/validation_state.js
44
44
  - app/assets/javascripts/kea/bindings/wait_for_vm.js
45
+ - app/assets/javascripts/kea/components/confirmation_button.js
45
46
  - app/assets/javascripts/kea/extenders/equals.js
46
47
  - app/assets/javascripts/kea/extenders/external_validator.js
47
48
  - app/assets/javascripts/kea/extenders/min_length.js
@@ -203,7 +204,7 @@ files:
203
204
  - vendor/assets/components/veiljs/veil.js
204
205
  - vendor/assets/javascripts/fuse.js
205
206
  - vendor/assets/javascripts/jquery-ui.js
206
- - vendor/assets/javascripts/knockout-3.2.0-debug.js
207
+ - vendor/assets/javascripts/knockout-3.3.0-debug.js
207
208
  - vendor/assets/javascripts/moment.js
208
209
  - vendor/assets/javascripts/pikaday.js
209
210
  - vendor/assets/stylesheets/pikaday.css