PageSourceSearch

https://lighthouseapp.com/javascripts/lh3/bower/knockout-mapping/knockout.mapping.js

js lighthouseapp.com collected 2026-10-01 10:15:23 UTC 26,873 bytes, 806 lines download raw bytes

1(function (factory) {
2	// Module systems magic dance.
3
4	if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
5		// CommonJS or Node: hard-coded dependency on "knockout"
6		factory(require("knockout"), exports);
7	} else if (typeof define === "function" && define["amd"]) {
8		// AMD anonymous module with hard-coded dependency on "knockout"
9		define(["knockout", "exports"], factory);
10	} else {
11		// <script> tag: use the global `ko` object, attaching a `mapping` property
12		factory(ko, ko.mapping = {});
13	}
14}(function (ko, exports) {
15	var DEBUG=true;
16	var mappingProperty = "__ko_mapping__";
17	var realKoDependentObservable = ko.dependentObservable;
18	var mappingNesting = 0;
19	var dependentObservables;
20	var visitedObjects;
21	var recognizedRootProperties = ["create", "update", "key", "arrayChanged"];
22	var emptyReturn = {};
23
24	var _defaultOptions = {
25		include: ["_destroy"],
26		ignore: [],
27		copy: [],
28		observe: []
29	};
30	var defaultOptions = _defaultOptions;
31
32	// Author: KennyTM @ StackOverflow
33	function unionArrays (x, y) {
34		var obj = {};
35		for (var i = x.length - 1; i >= 0; -- i) obj[x[i]] = x[i];
36		for (var i = y.length - 1; i >= 0; -- i) obj[y[i]] = y[i];
37		var res = [];
38
39		for (var k in obj) {
40			res.push(obj[k]);
41		};
42
43		return res;
44	}
45
46	function extendObject(destination, source) {
47		var destType;
48
49		for (var key in source) {
50			if (source.hasOwnProperty(key) && source[key]) {
51				destType = exports.getType(destination[key]);
52				if (key && destination[key] && destType !== "array" && destType !== "string") {
53					extendObject(destination[key], source[key]);
54				} else {
55					var bothArrays = exports.getType(destination[key]) === "array" && exports.getType(source[key]) === "array";
56					if (bothArrays) {
57						destination[key] = unionArrays(destination[key], source[key]);
58					} else {
59						destination[key] = source[key];
60					}
61				}
62			}
63		}
64	}
65
66	function merge(obj1, obj2) {
67		var merged = {};
68		extendObject(merged, obj1);
69		extendObject(merged, obj2);
70
71		return merged;
72	}
73
74	exports.isMapped = function (viewModel) {
75		var unwrapped = ko.utils.unwrapObservable(viewModel);
76		return unwrapped && unwrapped[mappingProperty];
77	}
78
79	exports.fromJS = function (jsObject /*, inputOptions, target*/ ) {
80		if (arguments.length == 0) throw new Error("When calling ko.fromJS, pass the object you want to convert.");
81
82		try {
83			if (!mappingNesting++) {
84				dependentObservables = [];
85				visitedObjects = new objectLookup();
86			}
87
88			var options;
89			var target;
90
91			if (arguments.length == 2) {
92				if (arguments[1][mappingProperty]) {
93					target = arguments[1];
94				} else {
95					options = arguments[1];
96				}
97			}
98			if (arguments.length == 3) {
99				options = arguments[1];
100				target = arguments[2];
101			}
102
103			if (target) {
104				options = merge(options, target[mappingProperty]);
105			}
106			options = fillOptions(options);
107
108			var result = updateViewModel(target, jsObject, options);
109			if (target) {
110				result = target;
111			}
112
113			// Evaluate any dependent observables that were proxied.
114			// Do this after the model's observables have been created
115			if (!--mappingNesting) {
116				while (dependentObservables.length) {
117					var DO = dependentObservables.pop();
118					if (DO) {
119						DO();
120						
121						// Move this magic property to the underlying dependent observable
122						DO.__DO["throttleEvaluation"] = DO["throttleEvaluation"];
123					}
124				}
125			}
126
127			// Save any new mapping options in the view model, so that updateFromJS can use them later.
128			result[mappingProperty] = merge(result[mappingProperty], options);
129
130			return result;
131		} catch(e) {
132			mappingNesting = 0;
133			throw e;
134		}
135	};
136
137	exports.fromJSON = function (jsonString /*, options, target*/ ) {
138		var parsed = ko.utils.parseJson(jsonString);
139		arguments[0] = parsed;
140		return exports.fromJS.apply(this, arguments);
141	};
142
143	exports.updateFromJS = function (viewModel) {
144		throw new Error("ko.mapping.updateFromJS, use ko.mapping.fromJS instead. Please note that the order of parameters is different!");
145	};
146
147	exports.updateFromJSON = function (viewModel) {
148		throw new Error("ko.mapping.updateFromJSON, use ko.mapping.fromJSON instead. Please note that the order of parameters is different!");
149	};
150
151	exports.toJS = function (rootObject, options) {
152		if (!defaultOptions) exports.resetDefaultOptions();
153
154		if (arguments.length == 0) throw new Error("When calling ko.mapping.toJS, pass the object you want to convert.");
155		if (exports.getType(defaultOptions.ignore) !== "array") throw new Error("ko.mapping.defaultOptions().ignore should be an array.");
156		if (exports.getType(defaultOptions.include) !== "array") throw new Error("ko.mapping.defaultOptions().include should be an array.");
157		if (exports.getType(defaultOptions.copy) !== "array") throw new Error("ko.mapping.defaultOptions().copy should be an array.");
158
159		// Merge in the options used in fromJS
160		options = fillOptions(options, rootObject[mappingProperty]);
161
162		// We just unwrap everything at every level in the object graph
163		return exports.visitModel(rootObject, function (x) {
164			return ko.utils.unwrapObservable(x)
165		}, options);
166	};
167
168	exports.toJSON = function (rootObject, options) {
169		var plainJavaScriptObject = exports.toJS(rootObject, options);
170		return ko.utils.stringifyJson(plainJavaScriptObject);
171	};
172
173	exports.defaultOptions = function () {
174		if (arguments.length > 0) {
175			defaultOptions = arguments[0];
176		} else {
177			return defaultOptions;
178		}
179	};
180
181	exports.resetDefaultOptions = function () {
182		defaultOptions = {
183			include: _defaultOptions.include.slice(0),
184			ignore: _defaultOptions.ignore.slice(0),
185			copy: _defaultOptions.copy.slice(0)
186		};
187	};
188
189	exports.getType = function(x) {
190		if ((x) && (typeof (x) === "object")) {
191			if (x.constructor === Date) return "date";
192			if (x.constructor === Array) return "array";
193		}
194		return typeof x;
195	}
196
197	function fillOptions(rawOptions, otherOptions) {
198		var options = merge({}, rawOptions);
199
200		// Move recognized root-level properties into a root namespace
201		for (var i = recognizedRootProperties.length - 1; i >= 0; i--) {
202			var property = recognizedRootProperties[i];
203			
204			// Carry on, unless this property is present
205			if (!options[property]) continue;
206			
207			// Move the property into the root namespace
208			if (!(options[""] instanceof Object)) options[""] = {};
209			options[""][property] = options[property];
210			delete options[property];
211		}
212
213		if (otherOptions) {
214			options.ignore = mergeArrays(otherOptions.ignore, options.ignore);
215			options.include = mergeArrays(otherOptions.include, options.include);
216			options.copy = mergeArrays(otherOptions.copy, options.copy);
217			options.observe = mergeArrays(otherOptions.observe, options.observe);
218		}
219		options.ignore = mergeArrays(options.ignore, defaultOptions.ignore);
220		options.include = mergeArrays(options.include, defaultOptions.include);
221		options.copy = mergeArrays(options.copy, defaultOptions.copy);
222		options.observe = mergeArrays(options.observe, defaultOptions.observe);
223
224		options.mappedProperties = options.mappedProperties || {};
225		options.copiedProperties = options.copiedProperties || {};
226		return options;
227	}
228
229	function mergeArrays(a, b) {
230		if (exports.getType(a) !== "array") {
231			if (exports.getType(a) === "undefined") a = [];
232			else a = [a];
233		}
234		if (exports.getType(b) !== "array") {
235			if (exports.getType(b) === "undefined") b = [];
236			else b = [b];
237		}
238
239		return ko.utils.arrayGetDistinctValues(a.concat(b));
240	}
241
242	// When using a 'create' callback, we proxy the dependent observable so that it doesn't immediately evaluate on creation.
243	// The reason is that the dependent observables in the user-specified callback may contain references to properties that have not been mapped yet.
244	function withProxyDependentObservable(dependentObservables, callback) {
245		var localDO = ko.dependentObservable;
246		ko.dependentObservable = function (read, owner, options) {
247			options = options || {};
248
249			if (read && typeof read == "object") { // mirrors condition in knockout implementation of DO's
250				options = read;
251			}
252
253			var realDeferEvaluation = options.deferEvaluation;
254
255			var isRemoved = false;
256
257			// We wrap the original dependent observable so that we can remove it from the 'dependentObservables' list we need to evaluate after mapping has
258			// completed if the user already evaluated the DO themselves in the meantime.
259			var wrap = function (DO) {
260				// Temporarily revert ko.dependentObservable, since it is used in ko.isWriteableObservable
261				var tmp = ko.dependentObservable;
262				ko.dependentObservable = realKoDependentObservable;
263				var isWriteable = ko.isWriteableObservable(DO);
264				ko.dependentObservable = tmp;
265
266				var wrapped = realKoDependentObservable({
267					read: function () {
268						if (!isRemoved) {
269							ko.utils.arrayRemoveItem(dependentObservables, DO);
270							isRemoved = true;
271						}
272						return DO.apply(DO, arguments);
273					},
274					write: isWriteable && function (val) {
275						return DO(val);
276					},
277					deferEvaluation: true
278				});
279				if (DEBUG) wrapped._wrapper = true;
280				wrapped.__DO = DO;
281				return wrapped;
282			};
283			
284			options.deferEvaluation = true; // will either set for just options, or both read/options.
285			var realDependentObservable = new realKoDependentObservable(read, owner, options);
286
287			if (!realDeferEvaluation) {
288				realDependentObservable = wrap(realDependentObservable);
289				dependentObservables.push(realDependentObservable);
290			}
291
292			return realDependentObservable;
293		}
294		ko.dependentObservable.fn = realKoDependentObservable.fn;
295		ko.computed = ko.dependentObservable;
296		var result = callback();
297		ko.dependentObservable = localDO;
298		ko.computed = ko.dependentObservable;
299		return result;
300	}
301
302	function updateViewModel(mappedRootObject, rootObject, options, parentName, parent, parentPropertyName, mappedParent) {
303		var isArray = exports.getType(ko.utils.unwrapObservable(rootObject)) === "array";
304
305		parentPropertyName = parentPropertyName || "";
306
307		// If this object was already mapped previously, take the options from there and merge them with our existing ones.
308		if (exports.isMapped(mappedRootObject)) {
309			var previousMapping = ko.utils.unwrapObservable(mappedRootObject)[mappingProperty];
310			options = merge(previousMapping, options);
311		}
312
313		var callbackParams = {
314			data: rootObject,
315			parent: mappedParent || parent
316		};
317
318		var hasCreateCallback = function () {
319			return options[parentName] && options[parentName].create instanceof Function;
320		};
321
322		var createCallback = function (data) {
323			return withProxyDependentObservable(dependentObservables, function () {
324				
325				if (ko.utils.unwrapObservable(parent) instanceof Array) {
326					return options[parentName].create({
327						data: data || callbackParams.data,
328						parent: callbackParams.parent,
329						skip: emptyReturn
330					});
vendor: 5,749 bytes, lines 331-504
331				} else {
332					return options[parentName].create({
333						data: data || callbackParams.data,
334						parent: callbackParams.parent
335					});
336				}				
337			});
338		};
339
340		var hasUpdateCallback = function () {
341			return options[parentName] && options[parentName].update instanceof Function;
342		};
343
344		var updateCallback = function (obj, data) {
345			var params = {
346				data: data || callbackParams.data,
347				parent: callbackParams.parent,
348				target: ko.utils.unwrapObservable(obj)
349			};
350
351			if (ko.isWriteableObservable(obj)) {
352				params.observable = obj;
353			}
354
355			return options[parentName].update(params);
356		}
357
358		var alreadyMapped = visitedObjects.get(rootObject);
359		if (alreadyMapped) {
360			return alreadyMapped;
361		}
362
363		parentName = parentName || "";
364
365		if (!isArray) {
366			// For atomic types, do a direct update on the observable
367			if (!canHaveProperties(rootObject)) {
368				switch (exports.getType(rootObject)) {
369				case "function":
370					if (hasUpdateCallback()) {
371						if (ko.isWriteableObservable(rootObject)) {
372							rootObject(updateCallback(rootObject));
373							mappedRootObject = rootObject;
374						} else {
375							mappedRootObject = updateCallback(rootObject);
376						}
377					} else {
378						mappedRootObject = rootObject;
379					}
380					break;
381				default:
382					if (ko.isWriteableObservable(mappedRootObject)) {
383						if (hasUpdateCallback()) {
384							var valueToWrite = updateCallback(mappedRootObject);
385							mappedRootObject(valueToWrite);
386							return valueToWrite;
387						} else {
388							var valueToWrite = ko.utils.unwrapObservable(rootObject);
389							mappedRootObject(valueToWrite);
390							return valueToWrite;
391						}
392					} else {
393						var hasCreateOrUpdateCallback = hasCreateCallback() || hasUpdateCallback();
394						
395						if (hasCreateCallback()) {
396							mappedRootObject = createCallback();
397						} else {
398							mappedRootObject = ko.observable(ko.utils.unwrapObservable(rootObject));
399						}
400
401						if (hasUpdateCallback()) {
402							mappedRootObject(updateCallback(mappedRootObject));
403						}
404						
405						if (hasCreateOrUpdateCallback) return mappedRootObject;
406					}
407				}
408
409			} else {
410				mappedRootObject = ko.utils.unwrapObservable(mappedRootObject);
411				if (!mappedRootObject) {
412					if (hasCreateCallback()) {
413						var result = createCallback();
414
415						if (hasUpdateCallback()) {
416							result = updateCallback(result);
417						}
418
419						return result;
420					} else {
421						if (hasUpdateCallback()) {
422							return updateCallback(result);
423						}
424
425						mappedRootObject = {};
426					}
427				}
428
429				if (hasUpdateCallback()) {
430					mappedRootObject = updateCallback(mappedRootObject);
431				}
432
433				visitedObjects.save(rootObject, mappedRootObject);
434				if (hasUpdateCallback()) return mappedRootObject;
435
436				// For non-atomic types, visit all properties and update recursively
437				visitPropertiesOrArrayEntries(rootObject, function (indexer) {
438					var fullPropertyName = parentPropertyName.length ? parentPropertyName + "." + indexer : indexer;
439
440					if (ko.utils.arrayIndexOf(options.ignore, fullPropertyName) != -1) {
441						return;
442					}
443
444					if (ko.utils.arrayIndexOf(options.copy, fullPropertyName) != -1) {
445						mappedRootObject[indexer] = rootObject[indexer];
446						return;
447					}
448
449					if(typeof rootObject[indexer] != "object" && typeof rootObject[indexer] != "array" && options.observe.length > 0 && ko.utils.arrayIndexOf(options.observe, fullPropertyName) == -1)
450					{
451						mappedRootObject[indexer] = rootObject[indexer];
452						options.copiedProperties[fullPropertyName] = true;
453						return;
454					}
455					
456					// In case we are adding an already mapped property, fill it with the previously mapped property value to prevent recursion.
457					// If this is a property that was generated by fromJS, we should use the options specified there
458					var prevMappedProperty = visitedObjects.get(rootObject[indexer]);
459					var retval = updateViewModel(mappedRootObject[indexer], rootObject[indexer], options, indexer, mappedRootObject, fullPropertyName, mappedRootObject);
460					var value = prevMappedProperty || retval;
461					
462					if(options.observe.length > 0 && ko.utils.arrayIndexOf(options.observe, fullPropertyName) == -1)
463					{
464						mappedRootObject[indexer] = value();
465						options.copiedProperties[fullPropertyName] = true;
466						return;
467					}
468					
469					if (ko.isWriteableObservable(mappedRootObject[indexer])) {
470						value = ko.utils.unwrapObservable(value);
471						if (mappedRootObject[indexer]() !== value) {
472							mappedRootObject[indexer](value);
473						}
474					} else {
475						value = mappedRootObject[indexer] === undefined ? value : ko.utils.unwrapObservable(value);
476						mappedRootObject[indexer] = value;
477					}
478
479					options.mappedProperties[fullPropertyName] = true;
480				});
481			}
482		} else { //mappedRootObject is an array
483			var changes = [];
484
485			var hasKeyCallback = false;
486			var keyCallback = function (x) {
487				return x;
488			}
489			if (options[parentName] && options[parentName].key) {
490				keyCallback = options[parentName].key;
491				hasKeyCallback = true;
492			}
493
494			if (!ko.isObservable(mappedRootObject)) {
495				// When creating the new observable array, also add a bunch of utility functions that take the 'key' of the array items into account.
496				mappedRootObject = ko.observableArray([]);
497
498				mappedRootObject.mappedRemove = function (valueOrPredicate) {
499					var predicate = typeof valueOrPredicate == "function" ? valueOrPredicate : function (value) {
500							return value === keyCallback(valueOrPredicate);
501						};
502					return mappedRootObject.remove(function (item) {
503						return predicate(keyCallback(item));
504					});
505				}
506
507				mappedRootObject.mappedRemoveAll = function (arrayOfValues) {
508					var arrayOfKeys = filterArrayByKey(arrayOfValues, keyCallback);
509					return mappedRootObject.remove(function (item) {
510						return ko.utils.arrayIndexOf(arrayOfKeys, keyCallback(item)) != -1;
511					});
512				}
513
514				mappedRootObject.mappedDestroy = function (valueOrPredicate) {
515					var predicate = typeof valueOrPredicate == "function" ? valueOrPredicate : function (value) {
516							return value === keyCallback(valueOrPredicate);
517						};
518					return mappedRootObject.destroy(function (item) {
519						return predicate(keyCallback(item));
520					});
521				}
522
523				mappedRootObject.mappedDestroyAll = function (arrayOfValues) {
524					var arrayOfKeys = filterArrayByKey(arrayOfValues, keyCallback);
525					return mappedRootObject.destroy(function (item) {
526						return ko.utils.arrayIndexOf(arrayOfKeys, keyCallback(item)) != -1;
527					});
528				}
529
530				mappedRootObject.mappedIndexOf = function (item) {
531					var keys = filterArrayByKey(mappedRootObject(), keyCallback);
532					var key = keyCallback(item);
533					return ko.utils.arrayIndexOf(keys, key);
534				}
535
536				mappedRootObject.mappedGet = function (item) {
537					return mappedRootObject()[mappedRootObject.mappedIndexOf(item)];
538				}
539
540				mappedRootObject.mappedCreate = function (value) {
541					if (mappedRootObject.mappedIndexOf(value) !== -1) {
542						throw new Error("There already is an object with the key that you specified.");
543					}
544
545					var item = hasCreateCallback() ? createCallback(value) : value;
546					if (hasUpdateCallback()) {
547						var newValue = updateCallback(item, value);
548						if (ko.isWriteableObservable(item)) {
549							item(newValue);
550						} else {
551							item = newValue;
552						}
553					}
554					mappedRootObject.push(item);
555					return item;
556				}
557			}
558
559			var currentArrayKeys = filterArrayByKey(ko.utils.unwrapObservable(mappedRootObject), keyCallback).sort();
560			var newArrayKeys = filterArrayByKey(rootObject, keyCallback);
561			if (hasKeyCallback) newArrayKeys.sort();
562			var editScript = ko.utils.compareArrays(currentArrayKeys, newArrayKeys);
563
564			var ignoreIndexOf = {};
565			
566			var i, j;
567
568			var unwrappedRootObject = ko.utils.unwrapObservable(rootObject);
569			var itemsByKey = {};
570			var optimizedKeys = true;
571			for (i = 0, j = unwrappedRootObject.length; i < j; i++) {
572				var key = keyCallback(unwrappedRootObject[i]);
573				if (key === undefined || key instanceof Object) {
574					optimizedKeys = false;
575					break;
576				}
577				itemsByKey[key] = unwrappedRootObject[i];
578			}
579
580			var newContents = [];
581			var passedOver = 0;
582			for (i = 0, j = editScript.length; i < j; i++) {
583				var key = editScript[i];
584				var mappedItem;
585				var fullPropertyName = parentPropertyName + "[" + i + "]";
586				switch (key.status) {
587				case "added":
588					var item = optimizedKeys ? itemsByKey[key.value] : getItemByKey(ko.utils.unwrapObservable(rootObject), key.value, keyCallback);
589					mappedItem = updateViewModel(undefined, item, options, parentName, mappedRootObject, fullPropertyName, parent);
590					if(!hasCreateCallback()) {
591						mappedItem = ko.utils.unwrapObservable(mappedItem);
592					}
593
594					var index = ignorableIndexOf(ko.utils.unwrapObservable(rootObject), item, ignoreIndexOf);
595					
596					if (mappedItem === emptyReturn) {
597						passedOver++;
598					} else {
599						newContents[index - passedOver] = mappedItem;
600					}
601						
602					ignoreIndexOf[index] = true;
603					break;
604				case "retained":
605					var item = optimizedKeys ? itemsByKey[key.value] : getItemByKey(ko.utils.unwrapObservable(rootObject), key.value, keyCallback);
606					mappedItem = getItemByKey(mappedRootObject, key.value, keyCallback);
607					updateViewModel(mappedItem, item, options, parentName, mappedRootObject, fullPropertyName, parent);
608
609					var index = ignorableIndexOf(ko.utils.unwrapObservable(rootObject), item, ignoreIndexOf);
610					newContents[index] = mappedItem;
611					ignoreIndexOf[index] = true;
612					break;
613				case "deleted":
614					mappedItem = getItemByKey(mappedRootObject, key.value, keyCallback);
615					break;
616				}
617
618				changes.push({
619					event: key.status,
620					item: mappedItem
621				});
622			}
623
624			mappedRootObject(newContents);
625
626			if (options[parentName] && options[parentName].arrayChanged) {
627				ko.utils.arrayForEach(changes, function (change) {
628					options[parentName].arrayChanged(change.event, change.item);
629				});
630			}
631		}
632
633		return mappedRootObject;
634	}
635
636	function ignorableIndexOf(array, item, ignoreIndices) {
637		for (var i = 0, j = array.length; i < j; i++) {
638			if (ignoreIndices[i] === true) continue;
639			if (array[i] === item) return i;
640		}
641		return null;
642	}
643
644	function mapKey(item, callback) {
645		var mappedItem;
646		if (callback) mappedItem = callback(item);
647		if (exports.getType(mappedItem) === "undefined") mappedItem = item;
648
649		return ko.utils.unwrapObservable(mappedItem);
650	}
651
652	function getItemByKey(array, key, callback) {
653		array = ko.utils.unwrapObservable(array);
654		for (var i = 0, j = array.length; i < j; i++) {
655			var item = array[i];
656			if (mapKey(item, callback) === key) return item;
657		}
658
659		throw new Error("When calling ko.update*, the key '" + key + "' was not found!");
660	}
661
662	function filterArrayByKey(array, callback) {
663		return ko.utils.arrayMap(ko.utils.unwrapObservable(array), function (item) {
664			if (callback) {
665				return mapKey(item, callback);
666			} else {
667				return item;
668			}
669		});
670	}
671
672	function visitPropertiesOrArrayEntries(rootObject, visitorCallback) {
673		if (exports.getType(rootObject) === "array") {
674			for (var i = 0; i < rootObject.length; i++)
675			visitorCallback(i);
676		} else {
677			for (var propertyName in rootObject)
678			visitorCallback(propertyName);
679		}
680	};
681
682	function canHaveProperties(object) {
683		var type = exports.getType(object);
684		return ((type === "object") || (type === "array")) && (object !== null);
685	}
686
687	// Based on the parentName, this creates a fully classified name of a property
688
689	function getPropertyName(parentName, parent, indexer) {
690		var propertyName = parentName || "";
691		if (exports.getType(parent) === "array") {
692			if (parentName) {
693				propertyName += "[" + indexer + "]";
694			}
695		} else {
696			if (parentName) {
697				propertyName += ".";
698			}
699			propertyName += indexer;
700		}
701		return propertyName;
702	}
703
704	exports.visitModel = function (rootObject, callback, options) {
705		options = options || {};
706		options.visitedObjects = options.visitedObjects || new objectLookup();
707
708		var mappedRootObject;
709		var unwrappedRootObject = ko.utils.unwrapObservable(rootObject);
710
711		if (!canHaveProperties(unwrappedRootObject)) {
712			return callback(rootObject, options.parentName);
713		} else {
714			options = fillOptions(options, unwrappedRootObject[mappingProperty]);
715
716			// Only do a callback, but ignore the results
717			callback(rootObject, options.parentName);
718			mappedRootObject = exports.getType(unwrappedRootObject) === "array" ? [] : {};
719		}
720
721		options.visitedObjects.save(rootObject, mappedRootObject);
722
723		var parentName = options.parentName;
724		visitPropertiesOrArrayEntries(unwrappedRootObject, function (indexer) {
725			if (options.ignore && ko.utils.arrayIndexOf(options.ignore, indexer) != -1) return;
726
727			var propertyValue = unwrappedRootObject[indexer];
728			options.parentName = getPropertyName(parentName, unwrappedRootObject, indexer);
729
730			// If we don't want to explicitly copy the unmapped property...
731			if (ko.utils.arrayIndexOf(options.copy, indexer) === -1) {
732				// ...find out if it's a property we want to explicitly include
733				if (ko.utils.arrayIndexOf(options.include, indexer) === -1) {
734					// The mapped properties object contains all the properties that were part of the original object.
735					// If a property does not exist, and it is not because it is part of an array (e.g. "myProp[3]"), then it should not be unmapped.
736				    if (unwrappedRootObject[mappingProperty]
737				        && unwrappedRootObject[mappingProperty].mappedProperties && !unwrappedRootObject[mappingProperty].mappedProperties[indexer]
738				        && unwrappedRootObject[mappingProperty].copiedProperties && !unwrappedRootObject[mappingProperty].copiedProperties[indexer]
739				        && !(exports.getType(unwrappedRootObject) === "array")) {
740						return;
741					}
742				}
743			}
744
745			var outputProperty;
746			switch (exports.getType(ko.utils.unwrapObservable(propertyValue))) {
747			case "object":
748			case "array":
749			case "undefined":
750				var previouslyMappedValue = options.visitedObjects.get(propertyValue);
751				mappedRootObject[indexer] = (exports.getType(previouslyMappedValue) !== "undefined") ? previouslyMappedValue : exports.visitModel(propertyValue, callback, options);
752				break;
753			default:
754				mappedRootObject[indexer] = callback(propertyValue, options.parentName);
755			}
756		});
757
758		return mappedRootObject;
759	}
760
761	function simpleObjectLookup() {
762		var keys = [];
763		var values = [];
764		this.save = function (key, value) {
765			var existingIndex = ko.utils.arrayIndexOf(keys, key);
766			if (existingIndex >= 0) values[existingIndex] = value;
767			else {
768				keys.push(key);
769				values.push(value);
770			}
771		};
772		this.get = function (key) {
773			var existingIndex = ko.utils.arrayIndexOf(keys, key);
774			var value = (existingIndex >= 0) ? values[existingIndex] : undefined;
775			return value;
776		};
777	};
778	
779	function objectLookup() {
780		var buckets = {};
781		
782		var findBucket = function(key) {
783			var bucketKey;
784			try {
785				bucketKey = key;//JSON.stringify(key);
786			}
787			catch (e) {
788				bucketKey = "$$$";
789			}
790
791			var bucket = buckets[bucketKey];
792			if (bucket === undefined) {
793				bucket = new simpleObjectLookup();
794				buckets[bucketKey] = bucket;
795			}
796			return bucket;
797		};
798		
799		this.save = function (key, value) {
800			findBucket(key).save(key, value);
801		};
802		this.get = function (key) {
803			return findBucket(key).get(key);
804		};
805	};
806}));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.