moel@348: // Knockout Mapping plugin v2.1.2 moel@348: // (c) 2012 Steven Sanderson, Roy Jacobs - http://knockoutjs.com/ moel@348: // License: MIT (http://www.opensource.org/licenses/mit-license.php) moel@348: moel@348: (function (factory) { moel@348: // Module systems magic dance. moel@348: moel@348: if (typeof require === "function" && typeof exports === "object" && typeof module === "object") { moel@348: // CommonJS or Node: hard-coded dependency on "knockout" moel@348: factory(require("knockout"), exports); moel@348: } else if (typeof define === "function" && define["amd"]) { moel@348: // AMD anonymous module with hard-coded dependency on "knockout" moel@348: define(["knockout", "exports"], factory); moel@348: } else { moel@348: // <script> tag: use the global `ko` object, attaching a `mapping` property moel@348: factory(ko, ko.mapping = {}); moel@348: } moel@348: }(function (ko, exports) { moel@348: var DEBUG=true; moel@348: var mappingProperty = "__ko_mapping__"; moel@348: var realKoDependentObservable = ko.dependentObservable; moel@348: var mappingNesting = 0; moel@348: var dependentObservables; moel@348: var visitedObjects; moel@348: moel@348: var _defaultOptions = { moel@348: include: ["_destroy"], moel@348: ignore: [], moel@348: copy: [] moel@348: }; moel@348: var defaultOptions = _defaultOptions; moel@348: moel@348: exports.isMapped = function (viewModel) { moel@348: var unwrapped = ko.utils.unwrapObservable(viewModel); moel@348: return unwrapped && unwrapped[mappingProperty]; moel@348: } moel@348: moel@348: exports.fromJS = function (jsObject /*, inputOptions, target*/ ) { moel@348: if (arguments.length == 0) throw new Error("When calling ko.fromJS, pass the object you want to convert."); moel@348: moel@348: // When mapping is completed, even with an exception, reset the nesting level moel@348: window.setTimeout(function () { moel@348: mappingNesting = 0; moel@348: }, 0); moel@348: moel@348: if (!mappingNesting++) { moel@348: dependentObservables = []; moel@348: visitedObjects = new objectLookup(); moel@348: } moel@348: moel@348: var options; moel@348: var target; moel@348: moel@348: if (arguments.length == 2) { moel@348: if (arguments[1][mappingProperty]) { moel@348: target = arguments[1]; moel@348: } else { moel@348: options = arguments[1]; moel@348: } moel@348: } moel@348: if (arguments.length == 3) { moel@348: options = arguments[1]; moel@348: target = arguments[2]; moel@348: } moel@348: moel@348: if (target) { moel@348: options = mergeOptions(target[mappingProperty], options); moel@348: } else { moel@348: options = mergeOptions(options); moel@348: } moel@348: options.mappedProperties = options.mappedProperties || {}; moel@348: moel@348: var result = updateViewModel(target, jsObject, options); moel@348: if (target) { moel@348: result = target; moel@348: } moel@348: moel@348: // Evaluate any dependent observables that were proxied. moel@348: // Do this in a timeout to defer execution. Basically, any user code that explicitly looks up the DO will perform the first evaluation. Otherwise, moel@348: // it will be done by this code. moel@348: if (!--mappingNesting) { moel@348: window.setTimeout(function () { moel@348: while (dependentObservables.length) { moel@348: var DO = dependentObservables.pop(); moel@348: if (DO) DO(); moel@348: } moel@348: }, 0); moel@348: } moel@348: moel@348: // Save any new mapping options in the view model, so that updateFromJS can use them later. moel@348: result[mappingProperty] = mergeOptions(result[mappingProperty], options); moel@348: moel@348: return result; moel@348: }; moel@348: moel@348: exports.fromJSON = function (jsonString /*, options, target*/ ) { moel@348: var parsed = ko.utils.parseJson(jsonString); moel@348: arguments[0] = parsed; moel@348: return exports.fromJS.apply(this, arguments); moel@348: }; moel@348: moel@348: exports.updateFromJS = function (viewModel) { moel@348: throw new Error("ko.mapping.updateFromJS, use ko.mapping.fromJS instead. Please note that the order of parameters is different!"); moel@348: }; moel@348: moel@348: exports.updateFromJSON = function (viewModel) { moel@348: throw new Error("ko.mapping.updateFromJSON, use ko.mapping.fromJSON instead. Please note that the order of parameters is different!"); moel@348: }; moel@348: moel@348: exports.toJS = function (rootObject, options) { moel@348: if (arguments.length == 0) throw new Error("When calling ko.mapping.toJS, pass the object you want to convert."); moel@348: // Merge in the options used in fromJS moel@348: options = mergeOptions(rootObject[mappingProperty], options); moel@348: moel@348: // We just unwrap everything at every level in the object graph moel@348: return visitModel(rootObject, function (x) { moel@348: return ko.utils.unwrapObservable(x) moel@348: }, options); moel@348: }; moel@348: moel@348: exports.toJSON = function (rootObject, options) { moel@348: var plainJavaScriptObject = exports.toJS(rootObject, options); moel@348: return ko.utils.stringifyJson(plainJavaScriptObject); moel@348: }; moel@348: moel@348: exports.visitModel = function (rootObject, callback, options) { moel@348: if (arguments.length == 0) throw new Error("When calling ko.mapping.visitModel, pass the object you want to visit."); moel@348: // Merge in the options used in fromJS moel@348: options = mergeOptions(rootObject[mappingProperty], options); moel@348: moel@348: return visitModel(rootObject, callback, options); moel@348: }; moel@348: moel@348: exports.defaultOptions = function () { moel@348: if (arguments.length > 0) { moel@348: defaultOptions = arguments[0]; moel@348: } else { moel@348: return defaultOptions; moel@348: } moel@348: }; moel@348: moel@348: exports.resetDefaultOptions = function () { moel@348: defaultOptions = { moel@348: include: _defaultOptions.include.slice(0), moel@348: ignore: _defaultOptions.ignore.slice(0), moel@348: copy: _defaultOptions.copy.slice(0) moel@348: }; moel@348: }; moel@348: moel@348: exports.getType = function(x) { moel@348: if ((x) && (typeof (x) === "object")) { moel@348: if (x.constructor == (new Date).constructor) return "date"; moel@348: if (x.constructor == (new Array).constructor) return "array"; moel@348: } moel@348: return typeof x; moel@348: } moel@348: moel@348: function extendOptionsArray(distArray, sourceArray) { moel@348: return ko.utils.arrayGetDistinctValues( moel@348: ko.utils.arrayPushAll(distArray, sourceArray) moel@348: ); moel@348: } moel@348: moel@348: function extendOptionsObject(target, options) { moel@348: var type = exports.getType, moel@348: name, special = { "include": true, "ignore": true, "copy": true }, moel@348: t, o, i = 1, l = arguments.length; moel@348: if (type(target) !== "object") { moel@348: target = {}; moel@348: } moel@348: for (; i < l; i++) { moel@348: options = arguments[i]; moel@348: if (type(options) !== "object") { moel@348: options = {}; moel@348: } moel@348: for (name in options) { moel@348: t = target[name]; o = options[name]; moel@348: if (name !== "constructor" && special[name] && type(o) !== "array") { moel@348: if (type(o) !== "string") { moel@348: throw new Error("ko.mapping.defaultOptions()." + name + " should be an array or string."); moel@348: } moel@348: o = [o]; moel@348: } moel@348: switch (type(o)) { moel@348: case "object": // Recurse moel@348: t = type(t) === "object" ? t : {}; moel@348: target[name] = extendOptionsObject(t, o); moel@348: break; moel@348: case "array": moel@348: t = type(t) === "array" ? t : []; moel@348: target[name] = extendOptionsArray(t, o); moel@348: break; moel@348: default: moel@348: target[name] = o; moel@348: } moel@348: } moel@348: } moel@348: return target; moel@348: } moel@348: moel@348: function mergeOptions() { moel@348: var options = ko.utils.arrayPushAll([{}, defaultOptions], arguments); // Always use empty object as target to avoid changing default options moel@348: options = extendOptionsObject.apply(this, options); moel@348: return options; moel@348: } moel@348: moel@348: // When using a 'create' callback, we proxy the dependent observable so that it doesn't immediately evaluate on creation. moel@348: // The reason is that the dependent observables in the user-specified callback may contain references to properties that have not been mapped yet. moel@348: function withProxyDependentObservable(dependentObservables, callback) { moel@348: var localDO = ko.dependentObservable; moel@348: ko.dependentObservable = function (read, owner, options) { moel@348: options = options || {}; moel@348: moel@348: if (read && typeof read == "object") { // mirrors condition in knockout implementation of DO's moel@348: options = read; moel@348: } moel@348: moel@348: var realDeferEvaluation = options.deferEvaluation; moel@348: moel@348: var isRemoved = false; moel@348: moel@348: // We wrap the original dependent observable so that we can remove it from the 'dependentObservables' list we need to evaluate after mapping has moel@348: // completed if the user already evaluated the DO themselves in the meantime. moel@348: var wrap = function (DO) { moel@348: var wrapped = realKoDependentObservable({ moel@348: read: function () { moel@348: if (!isRemoved) { moel@348: ko.utils.arrayRemoveItem(dependentObservables, DO); moel@348: isRemoved = true; moel@348: } moel@348: return DO.apply(DO, arguments); moel@348: }, moel@348: write: function (val) { moel@348: return DO(val); moel@348: }, moel@348: deferEvaluation: true moel@348: }); moel@348: if(DEBUG) wrapped._wrapper = true; moel@348: return wrapped; moel@348: }; moel@348: moel@348: options.deferEvaluation = true; // will either set for just options, or both read/options. moel@348: var realDependentObservable = new realKoDependentObservable(read, owner, options); moel@348: moel@348: if (!realDeferEvaluation) { moel@348: realDependentObservable = wrap(realDependentObservable); moel@348: dependentObservables.push(realDependentObservable); moel@348: } moel@348: moel@348: return realDependentObservable; moel@348: } moel@348: ko.dependentObservable.fn = realKoDependentObservable.fn; moel@348: ko.computed = ko.dependentObservable; moel@348: var result = callback(); moel@348: ko.dependentObservable = localDO; moel@348: ko.computed = ko.dependentObservable; moel@348: return result; moel@348: } moel@348: moel@348: function updateViewModel(mappedRootObject, rootObject, options, parentName, parent, parentPropertyName) { moel@348: var isArray = ko.utils.unwrapObservable(rootObject) instanceof Array; moel@348: moel@348: // If nested object was already mapped previously, take the options from it moel@348: if (parentName !== undefined && exports.isMapped(mappedRootObject)) { moel@348: options = ko.utils.unwrapObservable(mappedRootObject)[mappingProperty]; moel@348: parentName = ""; moel@348: parentPropertyName = ""; moel@348: } moel@348: moel@348: parentName = parentName || ""; moel@348: parentPropertyName = parentPropertyName || ""; moel@348: moel@348: var callbackParams = { moel@348: data: rootObject, moel@348: parent: parent moel@348: }; moel@348: moel@348: var getCallback = function (name) { moel@348: var callback; moel@348: if (parentName === "") { moel@348: callback = options[name]; moel@348: } else if (callback = options[parentName]) { moel@348: callback = callback[name] moel@348: } moel@348: return callback; moel@348: }; moel@348: moel@348: var hasCreateCallback = function () { moel@348: return getCallback("create") instanceof Function; moel@348: }; moel@348: moel@348: var createCallback = function (data) { moel@348: return withProxyDependentObservable(dependentObservables, function () { moel@348: return getCallback("create")({ moel@348: data: data || callbackParams.data, moel@348: parent: callbackParams.parent moel@348: }); moel@348: }); moel@348: }; moel@348: moel@348: var hasUpdateCallback = function () { moel@348: return getCallback("update") instanceof Function; moel@348: }; moel@348: moel@348: var updateCallback = function (obj, data) { moel@348: var params = { moel@348: data: data || callbackParams.data, moel@348: parent: callbackParams.parent, moel@348: target: ko.utils.unwrapObservable(obj) moel@348: }; moel@348: moel@348: if (ko.isWriteableObservable(obj)) { moel@348: params.observable = obj; moel@348: } moel@348: moel@348: return getCallback("update")(params); moel@348: } moel@348: moel@348: var alreadyMapped = visitedObjects.get(rootObject); moel@348: if (alreadyMapped) { moel@348: return alreadyMapped; moel@348: } moel@348: moel@348: if (!isArray) { moel@348: // For atomic types, do a direct update on the observable moel@348: if (!canHaveProperties(rootObject)) { moel@348: switch (exports.getType(rootObject)) { moel@348: case "function": moel@348: if (hasUpdateCallback()) { moel@348: if (ko.isWriteableObservable(rootObject)) { moel@348: rootObject(updateCallback(rootObject)); moel@348: mappedRootObject = rootObject; moel@348: } else { moel@348: mappedRootObject = updateCallback(rootObject); moel@348: } moel@348: } else { moel@348: mappedRootObject = rootObject; moel@348: } moel@348: break; moel@348: default: moel@348: if (ko.isWriteableObservable(mappedRootObject)) { moel@348: if (hasUpdateCallback()) { moel@348: mappedRootObject(updateCallback(mappedRootObject)); moel@348: } else { moel@348: mappedRootObject(ko.utils.unwrapObservable(rootObject)); moel@348: } moel@348: } else { moel@348: if (hasCreateCallback()) { moel@348: mappedRootObject = createCallback(); moel@348: } else { moel@348: mappedRootObject = ko.observable(ko.utils.unwrapObservable(rootObject)); moel@348: } moel@348: moel@348: if (hasUpdateCallback()) { moel@348: mappedRootObject(updateCallback(mappedRootObject)); moel@348: } moel@348: } moel@348: break; moel@348: } moel@348: moel@348: } else { moel@348: mappedRootObject = ko.utils.unwrapObservable(mappedRootObject); moel@348: if (!mappedRootObject) { moel@348: if (hasCreateCallback()) { moel@348: var result = createCallback(); moel@348: moel@348: if (hasUpdateCallback()) { moel@348: result = updateCallback(result); moel@348: } moel@348: moel@348: return result; moel@348: } else { moel@348: if (hasUpdateCallback()) { moel@348: return updateCallback(result); moel@348: } moel@348: moel@348: mappedRootObject = {}; moel@348: } moel@348: } moel@348: moel@348: if (hasUpdateCallback()) { moel@348: mappedRootObject = updateCallback(mappedRootObject); moel@348: } moel@348: moel@348: visitedObjects.save(rootObject, mappedRootObject); moel@348: moel@348: // For non-atomic types, visit all properties and update recursively moel@348: visitPropertiesOrArrayEntries(rootObject, function (indexer) { moel@348: var fullPropertyName = getPropertyName(parentPropertyName, rootObject, indexer); moel@348: moel@348: if (ko.utils.arrayIndexOf(options.ignore, fullPropertyName) != -1) { moel@348: return; moel@348: } moel@348: moel@348: if (ko.utils.arrayIndexOf(options.copy, fullPropertyName) != -1) { moel@348: mappedRootObject[indexer] = rootObject[indexer]; moel@348: return; moel@348: } moel@348: moel@348: // In case we are adding an already mapped property, fill it with the previously mapped property value to prevent recursion. moel@348: // If this is a property that was generated by fromJS, we should use the options specified there moel@348: var prevMappedProperty = visitedObjects.get(rootObject[indexer]); moel@348: var value = prevMappedProperty || updateViewModel(mappedRootObject[indexer], rootObject[indexer], options, indexer, mappedRootObject, fullPropertyName); moel@348: moel@348: if (ko.isWriteableObservable(mappedRootObject[indexer])) { moel@348: mappedRootObject[indexer](ko.utils.unwrapObservable(value)); moel@348: } else { moel@348: mappedRootObject[indexer] = value; moel@348: } moel@348: moel@348: options.mappedProperties[fullPropertyName] = true; moel@348: }); moel@348: } moel@348: } else { moel@348: var changes = []; moel@348: moel@348: var hasKeyCallback = getCallback("key") instanceof Function; moel@348: var keyCallback = hasKeyCallback ? getCallback("key") : function (x) { moel@348: return x; moel@348: }; moel@348: if (!ko.isObservable(mappedRootObject)) { moel@348: // When creating the new observable array, also add a bunch of utility functions that take the 'key' of the array items into account. moel@348: mappedRootObject = ko.observableArray([]); moel@348: moel@348: mappedRootObject.mappedRemove = function (valueOrPredicate) { moel@348: var predicate = typeof valueOrPredicate == "function" ? valueOrPredicate : function (value) { moel@348: return value === keyCallback(valueOrPredicate); moel@348: }; moel@348: return mappedRootObject.remove(function (item) { moel@348: return predicate(keyCallback(item)); moel@348: }); moel@348: } moel@348: moel@348: mappedRootObject.mappedRemoveAll = function (arrayOfValues) { moel@348: var arrayOfKeys = filterArrayByKey(arrayOfValues, keyCallback); moel@348: return mappedRootObject.remove(function (item) { moel@348: return ko.utils.arrayIndexOf(arrayOfKeys, keyCallback(item)) != -1; moel@348: }); moel@348: } moel@348: moel@348: mappedRootObject.mappedDestroy = function (valueOrPredicate) { moel@348: var predicate = typeof valueOrPredicate == "function" ? valueOrPredicate : function (value) { moel@348: return value === keyCallback(valueOrPredicate); moel@348: }; moel@348: return mappedRootObject.destroy(function (item) { moel@348: return predicate(keyCallback(item)); moel@348: }); moel@348: } moel@348: moel@348: mappedRootObject.mappedDestroyAll = function (arrayOfValues) { moel@348: var arrayOfKeys = filterArrayByKey(arrayOfValues, keyCallback); moel@348: return mappedRootObject.destroy(function (item) { moel@348: return ko.utils.arrayIndexOf(arrayOfKeys, keyCallback(item)) != -1; moel@348: }); moel@348: } moel@348: moel@348: mappedRootObject.mappedIndexOf = function (item) { moel@348: var keys = filterArrayByKey(mappedRootObject(), keyCallback); moel@348: var key = keyCallback(item); moel@348: return ko.utils.arrayIndexOf(keys, key); moel@348: } moel@348: moel@348: mappedRootObject.mappedCreate = function (value) { moel@348: if (mappedRootObject.mappedIndexOf(value) !== -1) { moel@348: throw new Error("There already is an object with the key that you specified."); moel@348: } moel@348: moel@348: var item = hasCreateCallback() ? createCallback(value) : value; moel@348: if (hasUpdateCallback()) { moel@348: var newValue = updateCallback(item, value); moel@348: if (ko.isWriteableObservable(item)) { moel@348: item(newValue); moel@348: } else { moel@348: item = newValue; moel@348: } moel@348: } moel@348: mappedRootObject.push(item); moel@348: return item; moel@348: } moel@348: } moel@348: moel@348: var currentArrayKeys = filterArrayByKey(ko.utils.unwrapObservable(mappedRootObject), keyCallback).sort(); moel@348: var newArrayKeys = filterArrayByKey(rootObject, keyCallback); moel@348: if (hasKeyCallback) newArrayKeys.sort(); moel@348: var editScript = ko.utils.compareArrays(currentArrayKeys, newArrayKeys); moel@348: moel@348: var ignoreIndexOf = {}; moel@348: moel@348: var newContents = []; moel@348: for (var i = 0, j = editScript.length; i < j; i++) { moel@348: var key = editScript[i]; moel@348: var mappedItem; moel@348: var fullPropertyName = getPropertyName(parentPropertyName, rootObject, i); moel@348: switch (key.status) { moel@348: case "added": moel@348: var item = getItemByKey(ko.utils.unwrapObservable(rootObject), key.value, keyCallback); moel@348: mappedItem = updateViewModel(undefined, item, options, parentName, mappedRootObject, fullPropertyName); moel@348: if(!hasCreateCallback()) { moel@348: mappedItem = ko.utils.unwrapObservable(mappedItem); moel@348: } moel@348: moel@348: var index = ignorableIndexOf(ko.utils.unwrapObservable(rootObject), item, ignoreIndexOf); moel@348: newContents[index] = mappedItem; moel@348: ignoreIndexOf[index] = true; moel@348: break; moel@348: case "retained": moel@348: var item = getItemByKey(ko.utils.unwrapObservable(rootObject), key.value, keyCallback); moel@348: mappedItem = getItemByKey(mappedRootObject, key.value, keyCallback); moel@348: updateViewModel(mappedItem, item, options, parentName, mappedRootObject, fullPropertyName); moel@348: moel@348: var index = ignorableIndexOf(ko.utils.unwrapObservable(rootObject), item, ignoreIndexOf); moel@348: newContents[index] = mappedItem; moel@348: ignoreIndexOf[index] = true; moel@348: break; moel@348: case "deleted": moel@348: mappedItem = getItemByKey(mappedRootObject, key.value, keyCallback); moel@348: break; moel@348: } moel@348: moel@348: changes.push({ moel@348: event: key.status, moel@348: item: mappedItem moel@348: }); moel@348: } moel@348: moel@348: mappedRootObject(newContents); moel@348: moel@348: var arrayChangedCallback = getCallback("arrayChanged"); moel@348: if (arrayChangedCallback instanceof Function) { moel@348: ko.utils.arrayForEach(changes, function (change) { moel@348: arrayChangedCallback(change.event, change.item); moel@348: }); moel@348: } moel@348: } moel@348: moel@348: return mappedRootObject; moel@348: } moel@348: moel@348: function ignorableIndexOf(array, item, ignoreIndices) { moel@348: for (var i = 0, j = array.length; i < j; i++) { moel@348: if (ignoreIndices[i] === true) continue; moel@348: if (array[i] === item) return i; moel@348: } moel@348: return null; moel@348: } moel@348: moel@348: function mapKey(item, callback) { moel@348: var mappedItem; moel@348: if (callback) mappedItem = callback(item); moel@348: if (exports.getType(mappedItem) === "undefined") mappedItem = item; moel@348: moel@348: return ko.utils.unwrapObservable(mappedItem); moel@348: } moel@348: moel@348: function getItemByKey(array, key, callback) { moel@348: var filtered = ko.utils.arrayFilter(ko.utils.unwrapObservable(array), function (item) { moel@348: return mapKey(item, callback) === key; moel@348: }); moel@348: moel@348: if (filtered.length == 0) throw new Error("When calling ko.update*, the key '" + key + "' was not found!"); moel@348: if ((filtered.length > 1) && (canHaveProperties(filtered[0]))) throw new Error("When calling ko.update*, the key '" + key + "' was not unique!"); moel@348: moel@348: return filtered[0]; moel@348: } moel@348: moel@348: function filterArrayByKey(array, callback) { moel@348: return ko.utils.arrayMap(ko.utils.unwrapObservable(array), function (item) { moel@348: if (callback) { moel@348: return mapKey(item, callback); moel@348: } else { moel@348: return item; moel@348: } moel@348: }); moel@348: } moel@348: moel@348: function visitPropertiesOrArrayEntries(rootObject, visitorCallback) { moel@348: if (rootObject instanceof Array) { moel@348: for (var i = 0; i < rootObject.length; i++) moel@348: visitorCallback(i); moel@348: } else { moel@348: for (var propertyName in rootObject) moel@348: visitorCallback(propertyName); moel@348: } moel@348: }; moel@348: moel@348: function canHaveProperties(object) { moel@348: var type = exports.getType(object); moel@348: return (type === "object" || type === "array") && (object !== null) && (type !== "undefined"); moel@348: } moel@348: moel@348: // Based on the parentName, this creates a fully classified name of a property moel@348: moel@348: function getPropertyName(parentName, parent, indexer) { moel@348: var propertyName = parentName || ""; moel@348: if (parent instanceof Array) { moel@348: if (parentName) { moel@348: propertyName += "[" + indexer + "]"; moel@348: } moel@348: } else { moel@348: if (parentName) { moel@348: propertyName += "."; moel@348: } moel@348: propertyName += indexer; moel@348: } moel@348: return propertyName; moel@348: } moel@348: moel@348: function visitModel(rootObject, callback, options, parentName, fullParentName) { moel@348: // If nested object was already mapped previously, take the options from it moel@348: if (parentName !== undefined && exports.isMapped(rootObject)) { moel@348: //options = ko.utils.unwrapObservable(rootObject)[mappingProperty]; moel@348: options = mergeOptions(ko.utils.unwrapObservable(rootObject)[mappingProperty], options); moel@348: parentName = ""; moel@348: } moel@348: moel@348: if (parentName === undefined) { // the first call moel@348: visitedObjects = new objectLookup(); moel@348: } moel@348: moel@348: parentName = parentName || ""; moel@348: moel@348: var mappedRootObject; moel@348: var unwrappedRootObject = ko.utils.unwrapObservable(rootObject); moel@348: if (!canHaveProperties(unwrappedRootObject)) { moel@348: return callback(rootObject, fullParentName); moel@348: } else { moel@348: // Only do a callback, but ignore the results moel@348: callback(rootObject, fullParentName); moel@348: mappedRootObject = unwrappedRootObject instanceof Array ? [] : {}; moel@348: } moel@348: moel@348: visitedObjects.save(rootObject, mappedRootObject); moel@348: moel@348: var origFullParentName = fullParentName; moel@348: visitPropertiesOrArrayEntries(unwrappedRootObject, function (indexer) { moel@348: if (options.ignore && ko.utils.arrayIndexOf(options.ignore, indexer) != -1) return; moel@348: moel@348: var propertyValue = unwrappedRootObject[indexer]; moel@348: var fullPropertyName = getPropertyName(parentName, unwrappedRootObject, indexer); moel@348: moel@348: // If we don't want to explicitly copy the unmapped property... moel@348: if (ko.utils.arrayIndexOf(options.copy, indexer) === -1) { moel@348: // ...find out if it's a property we want to explicitly include moel@348: if (ko.utils.arrayIndexOf(options.include, indexer) === -1) { moel@348: // Options contains all the properties that were part of the original object. moel@348: // 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. moel@348: if (options.mappedProperties && !options.mappedProperties[fullPropertyName] && !(unwrappedRootObject instanceof Array)) { moel@348: return; moel@348: } moel@348: } moel@348: } moel@348: moel@348: fullParentName = getPropertyName(origFullParentName, unwrappedRootObject, indexer); moel@348: moel@348: var propertyType = exports.getType(ko.utils.unwrapObservable(propertyValue)); moel@348: switch (propertyType) { moel@348: case "object": moel@348: case "array": moel@348: case "undefined": moel@348: var previouslyMappedValue = visitedObjects.get(propertyValue); moel@348: mappedRootObject[indexer] = (exports.getType(previouslyMappedValue) !== "undefined") ? previouslyMappedValue : visitModel(propertyValue, callback, options, fullPropertyName, fullParentName); moel@348: break; moel@348: default: moel@348: mappedRootObject[indexer] = callback(propertyValue, fullParentName); moel@348: } moel@348: }); moel@348: moel@348: return mappedRootObject; moel@348: } moel@348: moel@348: function objectLookup() { moel@348: var keys = []; moel@348: var values = []; moel@348: this.save = function (key, value) { moel@348: var existingIndex = ko.utils.arrayIndexOf(keys, key); moel@348: if (existingIndex >= 0) values[existingIndex] = value; moel@348: else { moel@348: keys.push(key); moel@348: values.push(value); moel@348: } moel@348: }; moel@348: this.get = function (key) { moel@348: var existingIndex = ko.utils.arrayIndexOf(keys, key); moel@348: return (existingIndex >= 0) ? values[existingIndex] : undefined; moel@348: }; moel@348: }; moel@348: }));