knockoutjs-rails 1.04 → 1.05

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -20,7 +20,7 @@ Add the following directive to your Javascript manifest file (application.js):
20
20
 
21
21
  ## Versioning
22
22
 
23
- knockoutjs-rails 1.04 == Knockout.js 1.04
23
+ knockoutjs-rails 1.05 == Knockout.js 1.05
24
24
 
25
25
  Every attempt is made to mirror the currently shipping Knockout.js version number wherever possible.
26
26
  The major and minor version numbers will always represent the Knockout.js version, but the patch level
@@ -1,5 +1,5 @@
1
1
  module Knockoutjs
2
2
  module Rails
3
- VERSION = "1.04"
3
+ VERSION = "1.05"
4
4
  end
5
5
  end
@@ -1,4 +1,4 @@
1
- // Knockout JavaScript library v1.04
1
+ // Knockout JavaScript library v1.05
2
2
  // (c) 2010 Steven Sanderson - http://knockoutjs.com/
3
3
  // License: Ms-Pl (http://www.opensource.org/licenses/ms-pl.html)
4
4
 
@@ -7,10 +7,10 @@ var ko = window.ko = {};
7
7
 
8
8
  ko.utils = new (function () {
9
9
  var stringTrimRegex = /^(\s|\u00A0)+|(\s|\u00A0)+$/g;
10
-
10
+
11
11
  return {
12
- fieldsIncludedWithJsonPost: ['authenticity_token', /^__RequestVerificationToken(_.*)?$/],
13
-
12
+ fieldsIncludedWithJsonPost: ['authenticity_token', /^__RequestVerificationToken(_.*)?$/],
13
+
14
14
  arrayForEach: function (array, action) {
15
15
  for (var i = 0, j = array.length; i < j; i++)
16
16
  action(array[i]);
@@ -64,6 +64,11 @@ ko.utils = new (function () {
64
64
  result.push(array[i]);
65
65
  return result;
66
66
  },
67
+
68
+ arrayPushAll: function (array, valuesToPush) {
69
+ for (var i = 0, j = valuesToPush.length; i < j; i++)
70
+ array.push(valuesToPush[i]);
71
+ },
67
72
 
68
73
  emptyDomNode: function (domNode) {
69
74
  while (domNode.firstChild) {
@@ -223,24 +228,24 @@ ko.utils = new (function () {
223
228
  },
224
229
 
225
230
  makeArray: function(arrayLikeObject) {
226
- var result = [];
227
- for (var i = arrayLikeObject.length - 1; i >= 0; i--){
228
- result.push(arrayLikeObject[i]);
229
- };
230
- return result;
231
+ var result = [];
232
+ for (var i = arrayLikeObject.length - 1; i >= 0; i--){
233
+ result.push(arrayLikeObject[i]);
234
+ };
235
+ return result;
231
236
  },
232
237
 
233
238
  getFormFields: function(form, fieldName) {
234
- var fields = ko.utils.makeArray(form.getElementsByTagName("INPUT")).concat(ko.utils.makeArray(form.getElementsByTagName("TEXTAREA")));
235
- var isMatchingField = (typeof fieldName == 'string')
236
- ? function(field) { return field.name === fieldName }
237
- : function(field) { return fieldName.test(field.name) }; // Treat fieldName as regex or object containing predicate
238
- var matches = [];
239
- for (var i = fields.length - 1; i >= 0; i--) {
240
- if (isMatchingField(fields[i]))
241
- matches.push(fields[i]);
242
- };
243
- return matches;
239
+ var fields = ko.utils.makeArray(form.getElementsByTagName("INPUT")).concat(ko.utils.makeArray(form.getElementsByTagName("TEXTAREA")));
240
+ var isMatchingField = (typeof fieldName == 'string')
241
+ ? function(field) { return field.name === fieldName }
242
+ : function(field) { return fieldName.test(field.name) }; // Treat fieldName as regex or object containing predicate
243
+ var matches = [];
244
+ for (var i = fields.length - 1; i >= 0; i--) {
245
+ if (isMatchingField(fields[i]))
246
+ matches.push(fields[i]);
247
+ };
248
+ return matches;
244
249
  },
245
250
 
246
251
  stringifyJson: function (data) {
@@ -250,22 +255,22 @@ ko.utils = new (function () {
250
255
  },
251
256
 
252
257
  postJson: function (urlOrForm, data, options) {
253
- options = options || {};
254
- var params = options.params || {};
255
- var includeFields = options.includeFields || this.fieldsIncludedWithJsonPost;
256
- var url = urlOrForm;
257
-
258
- // If we were given a form, use its 'action' URL and pick out any requested field values
259
- if((typeof urlOrForm == 'object') && (urlOrForm.tagName == "FORM")) {
260
- var originalForm = urlOrForm;
261
- url = originalForm.action;
262
- for (var i = includeFields.length - 1; i >= 0; i--) {
263
- var fields = ko.utils.getFormFields(originalForm, includeFields[i]);
264
- for (var j = fields.length - 1; j >= 0; j--)
265
- params[fields[j].name] = fields[j].value;
266
- }
267
- }
268
-
258
+ options = options || {};
259
+ var params = options.params || {};
260
+ var includeFields = options.includeFields || this.fieldsIncludedWithJsonPost;
261
+ var url = urlOrForm;
262
+
263
+ // If we were given a form, use its 'action' URL and pick out any requested field values
264
+ if((typeof urlOrForm == 'object') && (urlOrForm.tagName == "FORM")) {
265
+ var originalForm = urlOrForm;
266
+ url = originalForm.action;
267
+ for (var i = includeFields.length - 1; i >= 0; i--) {
268
+ var fields = ko.utils.getFormFields(originalForm, includeFields[i]);
269
+ for (var j = fields.length - 1; j >= 0; j--)
270
+ params[fields[j].name] = fields[j].value;
271
+ }
272
+ }
273
+
269
274
  data = ko.utils.unwrapObservable(data);
270
275
  var form = document.createElement("FORM");
271
276
  form.style.display = "none";
@@ -614,7 +619,39 @@ ko.dependentObservable = function (evaluatorFunction, evaluatorFunctionTarget, o
614
619
  evaluate();
615
620
  return dependentObservable;
616
621
  };
617
- ko.dependentObservable.__ko_proto__ = ko.observable;/// <reference path="../utils.js" />
622
+ ko.dependentObservable.__ko_proto__ = ko.observable;(function () {
623
+ // Normally, SELECT elements and their OPTIONs can only take value of type 'string' (because the values
624
+ // are stored on DOM attributes). ko.selectExtensions provides a way for SELECTs/OPTIONs to have values
625
+ // that are arbitrary objects. This is very convenient when implementing things like cascading dropdowns.
626
+ ko.selectExtensions = {
627
+ readValue : function(element) {
628
+ if (element.tagName == 'OPTION') {
629
+ var valueAttributeValue = element.getAttribute("value");
630
+ if (valueAttributeValue !== ko.bindingHandlers.options.optionValueDomDataKey)
631
+ return valueAttributeValue;
632
+ return ko.utils.domData.get(element, ko.bindingHandlers.options.optionValueDomDataKey);
633
+ } else if (element.tagName == 'SELECT')
634
+ return element.selectedIndex >= 0 ? ko.selectExtensions.readValue(element.options[element.selectedIndex]) : undefined;
635
+ else
636
+ return element.value;
637
+ },
638
+
639
+ writeValue: function(element, value) {
640
+ if (element.tagName == 'OPTION') {
641
+ ko.utils.domData.set(element, ko.bindingHandlers.options.optionValueDomDataKey, value);
642
+ element.value = ko.bindingHandlers.options.optionValueDomDataKey;
643
+ } else if (element.tagName == 'SELECT') {
644
+ for (var i = element.options.length - 1; i >= 0; i--) {
645
+ if (ko.selectExtensions.readValue(element.options[i]) == value) {
646
+ element.selectedIndex = i;
647
+ break;
648
+ }
649
+ }
650
+ } else
651
+ element.value = value;
652
+ }
653
+ };
654
+ })();/// <reference path="../utils.js" />
618
655
 
619
656
  ko.jsonExpressionRewriting = (function () {
620
657
  var restoreCapturedTokensRegex = /\[ko_token_(\d+)\]/g;
@@ -737,13 +774,19 @@ ko.jsonExpressionRewriting = (function () {
737
774
  function () {
738
775
  var evaluatedBindings = (typeof bindings == "function") ? bindings() : bindings;
739
776
  var parsedBindings = evaluatedBindings || parseBindingAttribute(node.getAttribute(bindingAttributeName), viewModel);
777
+
778
+ // First run all the inits, so bindings can register for notification on changes
779
+ if (isFirstEvaluation) {
780
+ for (var bindingKey in parsedBindings) {
781
+ if (ko.bindingHandlers[bindingKey] && typeof ko.bindingHandlers[bindingKey].init == "function")
782
+ invokeBindingHandler(ko.bindingHandlers[bindingKey].init, node, parsedBindings[bindingKey], parsedBindings, viewModel);
783
+ }
784
+ }
785
+
786
+ // ... then run all the updates, which might trigger changes even on the first evaluation
740
787
  for (var bindingKey in parsedBindings) {
741
- if (ko.bindingHandlers[bindingKey]) {
742
- if (isFirstEvaluation && typeof ko.bindingHandlers[bindingKey].init == "function")
743
- invokeBindingHandler(ko.bindingHandlers[bindingKey].init, node, parsedBindings[bindingKey], parsedBindings, viewModel);
744
- if (typeof ko.bindingHandlers[bindingKey].update == "function")
788
+ if (ko.bindingHandlers[bindingKey] && typeof ko.bindingHandlers[bindingKey].update == "function")
745
789
  invokeBindingHandler(ko.bindingHandlers[bindingKey].update, node, parsedBindings[bindingKey], parsedBindings, viewModel);
746
- }
747
790
  }
748
791
  },
749
792
  null,
@@ -817,15 +860,18 @@ ko.bindingHandlers.value = {
817
860
  init: function (element, value, allBindings) {
818
861
  var eventName = allBindings.valueUpdate || "change";
819
862
  if (ko.isWriteableObservable(value))
820
- ko.utils.registerEventHandler(element, eventName, function () { value(this.value); });
863
+ ko.utils.registerEventHandler(element, eventName, function () {
864
+ value(ko.selectExtensions.readValue(this));
865
+ });
821
866
  else if (allBindings._ko_property_writers && allBindings._ko_property_writers.value)
822
- ko.utils.registerEventHandler(element, eventName, function () { allBindings._ko_property_writers.value(this.value); });
867
+ ko.utils.registerEventHandler(element, eventName, function () {
868
+ allBindings._ko_property_writers.value(ko.selectExtensions.readValue(this));
869
+ });
823
870
  },
824
871
  update: function (element, value) {
825
872
  var newValue = ko.utils.unwrapObservable(value);
826
-
827
- if (newValue != element.value) {
828
- var applyValueAction = function () { element.value = newValue; };
873
+ if (newValue != ko.selectExtensions.readValue(element)) {
874
+ var applyValueAction = function () { ko.selectExtensions.writeValue(element, newValue); };
829
875
  applyValueAction();
830
876
 
831
877
  // Workaround for IE6 bug: It won't reliably apply values to SELECT nodes during the same execution thread
@@ -840,14 +886,13 @@ ko.bindingHandlers.value = {
840
886
 
841
887
  ko.bindingHandlers.options = {
842
888
  update: function (element, value, allBindings) {
843
-
844
889
  if (element.tagName != "SELECT")
845
- throw new Error("values binding applies only to SELECT elements");
890
+ throw new Error("options binding applies only to SELECT elements");
846
891
 
847
892
  var previousSelectedValues = ko.utils.arrayMap(ko.utils.arrayFilter(element.childNodes, function (node) {
848
893
  return node.tagName && node.tagName == "OPTION" && node.selected;
849
894
  }), function (node) {
850
- return node.value || node.innerText || node.textContent;
895
+ return ko.selectExtensions.readValue(node) || node.innerText || node.textContent;
851
896
  });
852
897
 
853
898
  value = ko.utils.unwrapObservable(value);
@@ -857,24 +902,37 @@ ko.bindingHandlers.options = {
857
902
  if (value) {
858
903
  if (typeof value.length != "number")
859
904
  value = [value];
905
+ if (allBindings.optionsCaption) {
906
+ var option = document.createElement("OPTION");
907
+ option.innerHTML = allBindings.optionsCaption;
908
+ ko.selectExtensions.writeValue(option, undefined);
909
+ element.appendChild(option);
910
+ }
860
911
  for (var i = 0, j = value.length; i < j; i++) {
861
912
  var option = document.createElement("OPTION");
862
- var optionValue = typeof allBindings.options_value == "string" ? value[i][allBindings.options_value] : value[i];
863
- option.value = optionValue.toString();
864
- option.innerHTML = (typeof allBindings.options_text == "string" ? value[i][allBindings.options_text] : optionValue).toString();
913
+ var optionValue = typeof allBindings.optionsValue == "string" ? value[i][allBindings.optionsValue] : value[i];
914
+ if (typeof optionValue == 'object')
915
+ ko.selectExtensions.writeValue(option, optionValue);
916
+ else
917
+ option.value = optionValue.toString();
918
+ option.innerHTML = (typeof allBindings.optionsText == "string" ? value[i][allBindings.optionsText] : optionValue).toString();
865
919
  element.appendChild(option);
866
920
  }
867
921
 
868
922
  // IE6 doesn't like us to assign selection to OPTION nodes before they're added to the document.
869
923
  // That's why we first added them without selection. Now it's time to set the selection.
870
924
  var newOptions = element.getElementsByTagName("OPTION");
925
+ var countSelectionsRetained = 0;
871
926
  for (var i = 0, j = newOptions.length; i < j; i++) {
872
- if (ko.utils.arrayIndexOf(previousSelectedValues, newOptions[i].value) >= 0)
927
+ if (ko.utils.arrayIndexOf(previousSelectedValues, ko.selectExtensions.readValue(newOptions[i])) >= 0) {
873
928
  ko.utils.setOptionNodeSelectionState(newOptions[i], true);
929
+ countSelectionsRetained++;
930
+ }
874
931
  }
875
932
  }
876
933
  }
877
934
  };
935
+ ko.bindingHandlers.options.optionValueDomDataKey = '__ko.bindingHandlers.options.optionValueDomData__';
878
936
 
879
937
  ko.bindingHandlers.selectedOptions = {
880
938
  getSelectedValuesFromSelectNode: function (selectNode) {
@@ -883,12 +941,11 @@ ko.bindingHandlers.selectedOptions = {
883
941
  for (var i = 0, j = nodes.length; i < j; i++) {
884
942
  var node = nodes[i];
885
943
  if ((node.tagName == "OPTION") && node.selected)
886
- result.push(node.value);
944
+ result.push(ko.selectExtensions.readValue(node));
887
945
  }
888
946
  return result;
889
947
  },
890
948
  init: function (element, value, allBindings) {
891
-
892
949
  if (ko.isWriteableObservable(value))
893
950
  ko.utils.registerEventHandler(element, "change", function () { value(ko.bindingHandlers.selectedOptions.getSelectedValuesFromSelectNode(this)); });
894
951
  else if (allBindings._ko_property_writers && allBindings._ko_property_writers.value)
@@ -904,7 +961,7 @@ ko.bindingHandlers.selectedOptions = {
904
961
  for (var i = 0, j = nodes.length; i < j; i++) {
905
962
  var node = nodes[i];
906
963
  if (node.tagName == "OPTION")
907
- ko.utils.setOptionNodeSelectionState(node, ko.utils.arrayIndexOf(newValue, node.value) >= 0);
964
+ ko.utils.setOptionNodeSelectionState(node, ko.utils.arrayIndexOf(newValue, ko.selectExtensions.readValue(node)) >= 0);
908
965
  }
909
966
  }
910
967
  }
@@ -1122,10 +1179,10 @@ ko.templateRewriting = (function () {
1122
1179
  if (typeof unwrappedArray.length == "undefined") // Coerce single value into array
1123
1180
  unwrappedArray = [unwrappedArray];
1124
1181
 
1125
- // Filter out any entries marked as destroyed
1126
- var filteredArray = ko.utils.arrayFilter(unwrappedArray, function(item) {
1127
- return options.includeDestroyed || !item._destroy;
1128
- });
1182
+ // Filter out any entries marked as destroyed
1183
+ var filteredArray = ko.utils.arrayFilter(unwrappedArray, function(item) {
1184
+ return options.includeDestroyed || !item._destroy;
1185
+ });
1129
1186
 
1130
1187
  ko.utils.setDomNodeChildrenFromArrayMapping(targetNode, filteredArray, function (arrayValue) {
1131
1188
  return executeTemplate(null, "ignoreTargetNode", template, arrayValue, options);
@@ -1240,6 +1297,24 @@ ko.templateRewriting = (function () {
1240
1297
  // so that its children is again the concatenation of the mappings of the array elements, but don't re-map any array elements that we
1241
1298
  // previously mapped - retain those nodes, and just insert/delete other ones
1242
1299
 
1300
+ function mapNodeAndRefreshWhenChanged(mapping, valueToMap) {
1301
+ // Map this array value inside a dependentObservable so we re-map when any dependency changes
1302
+ var mappedNodes = [];
1303
+ ko.dependentObservable(function() {
1304
+ var newMappedNodes = mapping(valueToMap) || [];
1305
+
1306
+ // On subsequent evaluations, just replace the previously-inserted DOM nodes
1307
+ if (mappedNodes.length > 0)
1308
+ ko.utils.replaceDomNodes(mappedNodes, newMappedNodes);
1309
+
1310
+ // Replace the contents of the mappedNodes array, thereby updating the record
1311
+ // of which nodes would be deleted if valueToMap was itself later removed
1312
+ mappedNodes.splice(0, mappedNodes.length);
1313
+ ko.utils.arrayPushAll(mappedNodes, newMappedNodes);
1314
+ }, null, { disposeWhen: function() { return (mappedNodes.length == 0) || !ko.utils.domNodeIsAttachedToDocument(mappedNodes[0]) } });
1315
+ return mappedNodes;
1316
+ }
1317
+
1243
1318
  ko.utils.setDomNodeChildrenFromArrayMapping = function (domNode, array, mapping, options) {
1244
1319
  // Compare the provided array against the previous one
1245
1320
  array = array || [];
@@ -1275,28 +1350,28 @@ ko.templateRewriting = (function () {
1275
1350
  lastMappingResultIndex++;
1276
1351
  break;
1277
1352
 
1278
- case "added":
1279
- // Map this array value and insert the resulting nodes at the current insertion point
1280
- var mappedNodes = mapping(editScript[i].value) || [];
1281
- newMappingResult.push({ arrayEntry: editScript[i].value, domNodes: mappedNodes });
1282
- for (var nodeIndex = 0, nodeIndexMax = mappedNodes.length; nodeIndex < nodeIndexMax; nodeIndex++) {
1283
- var node = mappedNodes[nodeIndex];
1284
- nodesAdded.push(node);
1285
- if (insertAfterNode == null) {
1286
- // Insert at beginning
1287
- if (domNode.firstChild)
1288
- domNode.insertBefore(node, domNode.firstChild);
1289
- else
1290
- domNode.appendChild(node);
1291
- } else {
1292
- // Insert after insertion point
1293
- if (insertAfterNode.nextSibling)
1294
- domNode.insertBefore(node, insertAfterNode.nextSibling);
1353
+ case "added":
1354
+ var mappedNodes = mapNodeAndRefreshWhenChanged(mapping, editScript[i].value);
1355
+ // On the first evaluation, insert the nodes at the current insertion point
1356
+ newMappingResult.push({ arrayEntry: editScript[i].value, domNodes: mappedNodes });
1357
+ for (var nodeIndex = 0, nodeIndexMax = mappedNodes.length; nodeIndex < nodeIndexMax; nodeIndex++) {
1358
+ var node = mappedNodes[nodeIndex];
1359
+ nodesAdded.push(node);
1360
+ if (insertAfterNode == null) {
1361
+ // Insert at beginning
1362
+ if (domNode.firstChild)
1363
+ domNode.insertBefore(node, domNode.firstChild);
1364
+ else
1365
+ domNode.appendChild(node);
1366
+ } else {
1367
+ // Insert after insertion point
1368
+ if (insertAfterNode.nextSibling)
1369
+ domNode.insertBefore(node, insertAfterNode.nextSibling);
1295
1370
  else
1296
- domNode.appendChild(node);
1297
- }
1298
- insertAfterNode = node;
1299
- }
1371
+ domNode.appendChild(node);
1372
+ }
1373
+ insertAfterNode = node;
1374
+ }
1300
1375
  break;
1301
1376
  }
1302
1377
  }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: knockoutjs-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: '1.04'
4
+ version: '1.05'
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -13,7 +13,7 @@ date: 2012-01-28 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: railties
16
- requirement: &70158057167200 !ruby/object:Gem::Requirement
16
+ requirement: &70366174337380 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,7 +21,7 @@ dependencies:
21
21
  version: '3.1'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70158057167200
24
+ version_requirements: *70366174337380
25
25
  description: Knockout is a JavaScript library that helps you to create rich, responsive
26
26
  display and editor user interfaces with a clean underlying data model
27
27
  email: