1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/Resources/Web/js/knockout.mapping-latest.js Sun May 27 20:15:32 2012 +0000
1.3 @@ -0,0 +1,687 @@
1.4 +// Knockout Mapping plugin v2.1.2
1.5 +// (c) 2012 Steven Sanderson, Roy Jacobs - http://knockoutjs.com/
1.6 +// License: MIT (http://www.opensource.org/licenses/mit-license.php)
1.7 +
1.8 +(function (factory) {
1.9 + // Module systems magic dance.
1.10 +
1.11 + if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
1.12 + // CommonJS or Node: hard-coded dependency on "knockout"
1.13 + factory(require("knockout"), exports);
1.14 + } else if (typeof define === "function" && define["amd"]) {
1.15 + // AMD anonymous module with hard-coded dependency on "knockout"
1.16 + define(["knockout", "exports"], factory);
1.17 + } else {
1.18 + // <script> tag: use the global `ko` object, attaching a `mapping` property
1.19 + factory(ko, ko.mapping = {});
1.20 + }
1.21 +}(function (ko, exports) {
1.22 + var DEBUG=true;
1.23 + var mappingProperty = "__ko_mapping__";
1.24 + var realKoDependentObservable = ko.dependentObservable;
1.25 + var mappingNesting = 0;
1.26 + var dependentObservables;
1.27 + var visitedObjects;
1.28 +
1.29 + var _defaultOptions = {
1.30 + include: ["_destroy"],
1.31 + ignore: [],
1.32 + copy: []
1.33 + };
1.34 + var defaultOptions = _defaultOptions;
1.35 +
1.36 + exports.isMapped = function (viewModel) {
1.37 + var unwrapped = ko.utils.unwrapObservable(viewModel);
1.38 + return unwrapped && unwrapped[mappingProperty];
1.39 + }
1.40 +
1.41 + exports.fromJS = function (jsObject /*, inputOptions, target*/ ) {
1.42 + if (arguments.length == 0) throw new Error("When calling ko.fromJS, pass the object you want to convert.");
1.43 +
1.44 + // When mapping is completed, even with an exception, reset the nesting level
1.45 + window.setTimeout(function () {
1.46 + mappingNesting = 0;
1.47 + }, 0);
1.48 +
1.49 + if (!mappingNesting++) {
1.50 + dependentObservables = [];
1.51 + visitedObjects = new objectLookup();
1.52 + }
1.53 +
1.54 + var options;
1.55 + var target;
1.56 +
1.57 + if (arguments.length == 2) {
1.58 + if (arguments[1][mappingProperty]) {
1.59 + target = arguments[1];
1.60 + } else {
1.61 + options = arguments[1];
1.62 + }
1.63 + }
1.64 + if (arguments.length == 3) {
1.65 + options = arguments[1];
1.66 + target = arguments[2];
1.67 + }
1.68 +
1.69 + if (target) {
1.70 + options = mergeOptions(target[mappingProperty], options);
1.71 + } else {
1.72 + options = mergeOptions(options);
1.73 + }
1.74 + options.mappedProperties = options.mappedProperties || {};
1.75 +
1.76 + var result = updateViewModel(target, jsObject, options);
1.77 + if (target) {
1.78 + result = target;
1.79 + }
1.80 +
1.81 + // Evaluate any dependent observables that were proxied.
1.82 + // Do this in a timeout to defer execution. Basically, any user code that explicitly looks up the DO will perform the first evaluation. Otherwise,
1.83 + // it will be done by this code.
1.84 + if (!--mappingNesting) {
1.85 + window.setTimeout(function () {
1.86 + while (dependentObservables.length) {
1.87 + var DO = dependentObservables.pop();
1.88 + if (DO) DO();
1.89 + }
1.90 + }, 0);
1.91 + }
1.92 +
1.93 + // Save any new mapping options in the view model, so that updateFromJS can use them later.
1.94 + result[mappingProperty] = mergeOptions(result[mappingProperty], options);
1.95 +
1.96 + return result;
1.97 + };
1.98 +
1.99 + exports.fromJSON = function (jsonString /*, options, target*/ ) {
1.100 + var parsed = ko.utils.parseJson(jsonString);
1.101 + arguments[0] = parsed;
1.102 + return exports.fromJS.apply(this, arguments);
1.103 + };
1.104 +
1.105 + exports.updateFromJS = function (viewModel) {
1.106 + throw new Error("ko.mapping.updateFromJS, use ko.mapping.fromJS instead. Please note that the order of parameters is different!");
1.107 + };
1.108 +
1.109 + exports.updateFromJSON = function (viewModel) {
1.110 + throw new Error("ko.mapping.updateFromJSON, use ko.mapping.fromJSON instead. Please note that the order of parameters is different!");
1.111 + };
1.112 +
1.113 + exports.toJS = function (rootObject, options) {
1.114 + if (arguments.length == 0) throw new Error("When calling ko.mapping.toJS, pass the object you want to convert.");
1.115 + // Merge in the options used in fromJS
1.116 + options = mergeOptions(rootObject[mappingProperty], options);
1.117 +
1.118 + // We just unwrap everything at every level in the object graph
1.119 + return visitModel(rootObject, function (x) {
1.120 + return ko.utils.unwrapObservable(x)
1.121 + }, options);
1.122 + };
1.123 +
1.124 + exports.toJSON = function (rootObject, options) {
1.125 + var plainJavaScriptObject = exports.toJS(rootObject, options);
1.126 + return ko.utils.stringifyJson(plainJavaScriptObject);
1.127 + };
1.128 +
1.129 + exports.visitModel = function (rootObject, callback, options) {
1.130 + if (arguments.length == 0) throw new Error("When calling ko.mapping.visitModel, pass the object you want to visit.");
1.131 + // Merge in the options used in fromJS
1.132 + options = mergeOptions(rootObject[mappingProperty], options);
1.133 +
1.134 + return visitModel(rootObject, callback, options);
1.135 + };
1.136 +
1.137 + exports.defaultOptions = function () {
1.138 + if (arguments.length > 0) {
1.139 + defaultOptions = arguments[0];
1.140 + } else {
1.141 + return defaultOptions;
1.142 + }
1.143 + };
1.144 +
1.145 + exports.resetDefaultOptions = function () {
1.146 + defaultOptions = {
1.147 + include: _defaultOptions.include.slice(0),
1.148 + ignore: _defaultOptions.ignore.slice(0),
1.149 + copy: _defaultOptions.copy.slice(0)
1.150 + };
1.151 + };
1.152 +
1.153 + exports.getType = function(x) {
1.154 + if ((x) && (typeof (x) === "object")) {
1.155 + if (x.constructor == (new Date).constructor) return "date";
1.156 + if (x.constructor == (new Array).constructor) return "array";
1.157 + }
1.158 + return typeof x;
1.159 + }
1.160 +
1.161 + function extendOptionsArray(distArray, sourceArray) {
1.162 + return ko.utils.arrayGetDistinctValues(
1.163 + ko.utils.arrayPushAll(distArray, sourceArray)
1.164 + );
1.165 + }
1.166 +
1.167 + function extendOptionsObject(target, options) {
1.168 + var type = exports.getType,
1.169 + name, special = { "include": true, "ignore": true, "copy": true },
1.170 + t, o, i = 1, l = arguments.length;
1.171 + if (type(target) !== "object") {
1.172 + target = {};
1.173 + }
1.174 + for (; i < l; i++) {
1.175 + options = arguments[i];
1.176 + if (type(options) !== "object") {
1.177 + options = {};
1.178 + }
1.179 + for (name in options) {
1.180 + t = target[name]; o = options[name];
1.181 + if (name !== "constructor" && special[name] && type(o) !== "array") {
1.182 + if (type(o) !== "string") {
1.183 + throw new Error("ko.mapping.defaultOptions()." + name + " should be an array or string.");
1.184 + }
1.185 + o = [o];
1.186 + }
1.187 + switch (type(o)) {
1.188 + case "object": // Recurse
1.189 + t = type(t) === "object" ? t : {};
1.190 + target[name] = extendOptionsObject(t, o);
1.191 + break;
1.192 + case "array":
1.193 + t = type(t) === "array" ? t : [];
1.194 + target[name] = extendOptionsArray(t, o);
1.195 + break;
1.196 + default:
1.197 + target[name] = o;
1.198 + }
1.199 + }
1.200 + }
1.201 + return target;
1.202 + }
1.203 +
1.204 + function mergeOptions() {
1.205 + var options = ko.utils.arrayPushAll([{}, defaultOptions], arguments); // Always use empty object as target to avoid changing default options
1.206 + options = extendOptionsObject.apply(this, options);
1.207 + return options;
1.208 + }
1.209 +
1.210 + // When using a 'create' callback, we proxy the dependent observable so that it doesn't immediately evaluate on creation.
1.211 + // The reason is that the dependent observables in the user-specified callback may contain references to properties that have not been mapped yet.
1.212 + function withProxyDependentObservable(dependentObservables, callback) {
1.213 + var localDO = ko.dependentObservable;
1.214 + ko.dependentObservable = function (read, owner, options) {
1.215 + options = options || {};
1.216 +
1.217 + if (read && typeof read == "object") { // mirrors condition in knockout implementation of DO's
1.218 + options = read;
1.219 + }
1.220 +
1.221 + var realDeferEvaluation = options.deferEvaluation;
1.222 +
1.223 + var isRemoved = false;
1.224 +
1.225 + // We wrap the original dependent observable so that we can remove it from the 'dependentObservables' list we need to evaluate after mapping has
1.226 + // completed if the user already evaluated the DO themselves in the meantime.
1.227 + var wrap = function (DO) {
1.228 + var wrapped = realKoDependentObservable({
1.229 + read: function () {
1.230 + if (!isRemoved) {
1.231 + ko.utils.arrayRemoveItem(dependentObservables, DO);
1.232 + isRemoved = true;
1.233 + }
1.234 + return DO.apply(DO, arguments);
1.235 + },
1.236 + write: function (val) {
1.237 + return DO(val);
1.238 + },
1.239 + deferEvaluation: true
1.240 + });
1.241 + if(DEBUG) wrapped._wrapper = true;
1.242 + return wrapped;
1.243 + };
1.244 +
1.245 + options.deferEvaluation = true; // will either set for just options, or both read/options.
1.246 + var realDependentObservable = new realKoDependentObservable(read, owner, options);
1.247 +
1.248 + if (!realDeferEvaluation) {
1.249 + realDependentObservable = wrap(realDependentObservable);
1.250 + dependentObservables.push(realDependentObservable);
1.251 + }
1.252 +
1.253 + return realDependentObservable;
1.254 + }
1.255 + ko.dependentObservable.fn = realKoDependentObservable.fn;
1.256 + ko.computed = ko.dependentObservable;
1.257 + var result = callback();
1.258 + ko.dependentObservable = localDO;
1.259 + ko.computed = ko.dependentObservable;
1.260 + return result;
1.261 + }
1.262 +
1.263 + function updateViewModel(mappedRootObject, rootObject, options, parentName, parent, parentPropertyName) {
1.264 + var isArray = ko.utils.unwrapObservable(rootObject) instanceof Array;
1.265 +
1.266 + // If nested object was already mapped previously, take the options from it
1.267 + if (parentName !== undefined && exports.isMapped(mappedRootObject)) {
1.268 + options = ko.utils.unwrapObservable(mappedRootObject)[mappingProperty];
1.269 + parentName = "";
1.270 + parentPropertyName = "";
1.271 + }
1.272 +
1.273 + parentName = parentName || "";
1.274 + parentPropertyName = parentPropertyName || "";
1.275 +
1.276 + var callbackParams = {
1.277 + data: rootObject,
1.278 + parent: parent
1.279 + };
1.280 +
1.281 + var getCallback = function (name) {
1.282 + var callback;
1.283 + if (parentName === "") {
1.284 + callback = options[name];
1.285 + } else if (callback = options[parentName]) {
1.286 + callback = callback[name]
1.287 + }
1.288 + return callback;
1.289 + };
1.290 +
1.291 + var hasCreateCallback = function () {
1.292 + return getCallback("create") instanceof Function;
1.293 + };
1.294 +
1.295 + var createCallback = function (data) {
1.296 + return withProxyDependentObservable(dependentObservables, function () {
1.297 + return getCallback("create")({
1.298 + data: data || callbackParams.data,
1.299 + parent: callbackParams.parent
1.300 + });
1.301 + });
1.302 + };
1.303 +
1.304 + var hasUpdateCallback = function () {
1.305 + return getCallback("update") instanceof Function;
1.306 + };
1.307 +
1.308 + var updateCallback = function (obj, data) {
1.309 + var params = {
1.310 + data: data || callbackParams.data,
1.311 + parent: callbackParams.parent,
1.312 + target: ko.utils.unwrapObservable(obj)
1.313 + };
1.314 +
1.315 + if (ko.isWriteableObservable(obj)) {
1.316 + params.observable = obj;
1.317 + }
1.318 +
1.319 + return getCallback("update")(params);
1.320 + }
1.321 +
1.322 + var alreadyMapped = visitedObjects.get(rootObject);
1.323 + if (alreadyMapped) {
1.324 + return alreadyMapped;
1.325 + }
1.326 +
1.327 + if (!isArray) {
1.328 + // For atomic types, do a direct update on the observable
1.329 + if (!canHaveProperties(rootObject)) {
1.330 + switch (exports.getType(rootObject)) {
1.331 + case "function":
1.332 + if (hasUpdateCallback()) {
1.333 + if (ko.isWriteableObservable(rootObject)) {
1.334 + rootObject(updateCallback(rootObject));
1.335 + mappedRootObject = rootObject;
1.336 + } else {
1.337 + mappedRootObject = updateCallback(rootObject);
1.338 + }
1.339 + } else {
1.340 + mappedRootObject = rootObject;
1.341 + }
1.342 + break;
1.343 + default:
1.344 + if (ko.isWriteableObservable(mappedRootObject)) {
1.345 + if (hasUpdateCallback()) {
1.346 + mappedRootObject(updateCallback(mappedRootObject));
1.347 + } else {
1.348 + mappedRootObject(ko.utils.unwrapObservable(rootObject));
1.349 + }
1.350 + } else {
1.351 + if (hasCreateCallback()) {
1.352 + mappedRootObject = createCallback();
1.353 + } else {
1.354 + mappedRootObject = ko.observable(ko.utils.unwrapObservable(rootObject));
1.355 + }
1.356 +
1.357 + if (hasUpdateCallback()) {
1.358 + mappedRootObject(updateCallback(mappedRootObject));
1.359 + }
1.360 + }
1.361 + break;
1.362 + }
1.363 +
1.364 + } else {
1.365 + mappedRootObject = ko.utils.unwrapObservable(mappedRootObject);
1.366 + if (!mappedRootObject) {
1.367 + if (hasCreateCallback()) {
1.368 + var result = createCallback();
1.369 +
1.370 + if (hasUpdateCallback()) {
1.371 + result = updateCallback(result);
1.372 + }
1.373 +
1.374 + return result;
1.375 + } else {
1.376 + if (hasUpdateCallback()) {
1.377 + return updateCallback(result);
1.378 + }
1.379 +
1.380 + mappedRootObject = {};
1.381 + }
1.382 + }
1.383 +
1.384 + if (hasUpdateCallback()) {
1.385 + mappedRootObject = updateCallback(mappedRootObject);
1.386 + }
1.387 +
1.388 + visitedObjects.save(rootObject, mappedRootObject);
1.389 +
1.390 + // For non-atomic types, visit all properties and update recursively
1.391 + visitPropertiesOrArrayEntries(rootObject, function (indexer) {
1.392 + var fullPropertyName = getPropertyName(parentPropertyName, rootObject, indexer);
1.393 +
1.394 + if (ko.utils.arrayIndexOf(options.ignore, fullPropertyName) != -1) {
1.395 + return;
1.396 + }
1.397 +
1.398 + if (ko.utils.arrayIndexOf(options.copy, fullPropertyName) != -1) {
1.399 + mappedRootObject[indexer] = rootObject[indexer];
1.400 + return;
1.401 + }
1.402 +
1.403 + // In case we are adding an already mapped property, fill it with the previously mapped property value to prevent recursion.
1.404 + // If this is a property that was generated by fromJS, we should use the options specified there
1.405 + var prevMappedProperty = visitedObjects.get(rootObject[indexer]);
1.406 + var value = prevMappedProperty || updateViewModel(mappedRootObject[indexer], rootObject[indexer], options, indexer, mappedRootObject, fullPropertyName);
1.407 +
1.408 + if (ko.isWriteableObservable(mappedRootObject[indexer])) {
1.409 + mappedRootObject[indexer](ko.utils.unwrapObservable(value));
1.410 + } else {
1.411 + mappedRootObject[indexer] = value;
1.412 + }
1.413 +
1.414 + options.mappedProperties[fullPropertyName] = true;
1.415 + });
1.416 + }
1.417 + } else {
1.418 + var changes = [];
1.419 +
1.420 + var hasKeyCallback = getCallback("key") instanceof Function;
1.421 + var keyCallback = hasKeyCallback ? getCallback("key") : function (x) {
1.422 + return x;
1.423 + };
1.424 + if (!ko.isObservable(mappedRootObject)) {
1.425 + // When creating the new observable array, also add a bunch of utility functions that take the 'key' of the array items into account.
1.426 + mappedRootObject = ko.observableArray([]);
1.427 +
1.428 + mappedRootObject.mappedRemove = function (valueOrPredicate) {
1.429 + var predicate = typeof valueOrPredicate == "function" ? valueOrPredicate : function (value) {
1.430 + return value === keyCallback(valueOrPredicate);
1.431 + };
1.432 + return mappedRootObject.remove(function (item) {
1.433 + return predicate(keyCallback(item));
1.434 + });
1.435 + }
1.436 +
1.437 + mappedRootObject.mappedRemoveAll = function (arrayOfValues) {
1.438 + var arrayOfKeys = filterArrayByKey(arrayOfValues, keyCallback);
1.439 + return mappedRootObject.remove(function (item) {
1.440 + return ko.utils.arrayIndexOf(arrayOfKeys, keyCallback(item)) != -1;
1.441 + });
1.442 + }
1.443 +
1.444 + mappedRootObject.mappedDestroy = function (valueOrPredicate) {
1.445 + var predicate = typeof valueOrPredicate == "function" ? valueOrPredicate : function (value) {
1.446 + return value === keyCallback(valueOrPredicate);
1.447 + };
1.448 + return mappedRootObject.destroy(function (item) {
1.449 + return predicate(keyCallback(item));
1.450 + });
1.451 + }
1.452 +
1.453 + mappedRootObject.mappedDestroyAll = function (arrayOfValues) {
1.454 + var arrayOfKeys = filterArrayByKey(arrayOfValues, keyCallback);
1.455 + return mappedRootObject.destroy(function (item) {
1.456 + return ko.utils.arrayIndexOf(arrayOfKeys, keyCallback(item)) != -1;
1.457 + });
1.458 + }
1.459 +
1.460 + mappedRootObject.mappedIndexOf = function (item) {
1.461 + var keys = filterArrayByKey(mappedRootObject(), keyCallback);
1.462 + var key = keyCallback(item);
1.463 + return ko.utils.arrayIndexOf(keys, key);
1.464 + }
1.465 +
1.466 + mappedRootObject.mappedCreate = function (value) {
1.467 + if (mappedRootObject.mappedIndexOf(value) !== -1) {
1.468 + throw new Error("There already is an object with the key that you specified.");
1.469 + }
1.470 +
1.471 + var item = hasCreateCallback() ? createCallback(value) : value;
1.472 + if (hasUpdateCallback()) {
1.473 + var newValue = updateCallback(item, value);
1.474 + if (ko.isWriteableObservable(item)) {
1.475 + item(newValue);
1.476 + } else {
1.477 + item = newValue;
1.478 + }
1.479 + }
1.480 + mappedRootObject.push(item);
1.481 + return item;
1.482 + }
1.483 + }
1.484 +
1.485 + var currentArrayKeys = filterArrayByKey(ko.utils.unwrapObservable(mappedRootObject), keyCallback).sort();
1.486 + var newArrayKeys = filterArrayByKey(rootObject, keyCallback);
1.487 + if (hasKeyCallback) newArrayKeys.sort();
1.488 + var editScript = ko.utils.compareArrays(currentArrayKeys, newArrayKeys);
1.489 +
1.490 + var ignoreIndexOf = {};
1.491 +
1.492 + var newContents = [];
1.493 + for (var i = 0, j = editScript.length; i < j; i++) {
1.494 + var key = editScript[i];
1.495 + var mappedItem;
1.496 + var fullPropertyName = getPropertyName(parentPropertyName, rootObject, i);
1.497 + switch (key.status) {
1.498 + case "added":
1.499 + var item = getItemByKey(ko.utils.unwrapObservable(rootObject), key.value, keyCallback);
1.500 + mappedItem = updateViewModel(undefined, item, options, parentName, mappedRootObject, fullPropertyName);
1.501 + if(!hasCreateCallback()) {
1.502 + mappedItem = ko.utils.unwrapObservable(mappedItem);
1.503 + }
1.504 +
1.505 + var index = ignorableIndexOf(ko.utils.unwrapObservable(rootObject), item, ignoreIndexOf);
1.506 + newContents[index] = mappedItem;
1.507 + ignoreIndexOf[index] = true;
1.508 + break;
1.509 + case "retained":
1.510 + var item = getItemByKey(ko.utils.unwrapObservable(rootObject), key.value, keyCallback);
1.511 + mappedItem = getItemByKey(mappedRootObject, key.value, keyCallback);
1.512 + updateViewModel(mappedItem, item, options, parentName, mappedRootObject, fullPropertyName);
1.513 +
1.514 + var index = ignorableIndexOf(ko.utils.unwrapObservable(rootObject), item, ignoreIndexOf);
1.515 + newContents[index] = mappedItem;
1.516 + ignoreIndexOf[index] = true;
1.517 + break;
1.518 + case "deleted":
1.519 + mappedItem = getItemByKey(mappedRootObject, key.value, keyCallback);
1.520 + break;
1.521 + }
1.522 +
1.523 + changes.push({
1.524 + event: key.status,
1.525 + item: mappedItem
1.526 + });
1.527 + }
1.528 +
1.529 + mappedRootObject(newContents);
1.530 +
1.531 + var arrayChangedCallback = getCallback("arrayChanged");
1.532 + if (arrayChangedCallback instanceof Function) {
1.533 + ko.utils.arrayForEach(changes, function (change) {
1.534 + arrayChangedCallback(change.event, change.item);
1.535 + });
1.536 + }
1.537 + }
1.538 +
1.539 + return mappedRootObject;
1.540 + }
1.541 +
1.542 + function ignorableIndexOf(array, item, ignoreIndices) {
1.543 + for (var i = 0, j = array.length; i < j; i++) {
1.544 + if (ignoreIndices[i] === true) continue;
1.545 + if (array[i] === item) return i;
1.546 + }
1.547 + return null;
1.548 + }
1.549 +
1.550 + function mapKey(item, callback) {
1.551 + var mappedItem;
1.552 + if (callback) mappedItem = callback(item);
1.553 + if (exports.getType(mappedItem) === "undefined") mappedItem = item;
1.554 +
1.555 + return ko.utils.unwrapObservable(mappedItem);
1.556 + }
1.557 +
1.558 + function getItemByKey(array, key, callback) {
1.559 + var filtered = ko.utils.arrayFilter(ko.utils.unwrapObservable(array), function (item) {
1.560 + return mapKey(item, callback) === key;
1.561 + });
1.562 +
1.563 + if (filtered.length == 0) throw new Error("When calling ko.update*, the key '" + key + "' was not found!");
1.564 + if ((filtered.length > 1) && (canHaveProperties(filtered[0]))) throw new Error("When calling ko.update*, the key '" + key + "' was not unique!");
1.565 +
1.566 + return filtered[0];
1.567 + }
1.568 +
1.569 + function filterArrayByKey(array, callback) {
1.570 + return ko.utils.arrayMap(ko.utils.unwrapObservable(array), function (item) {
1.571 + if (callback) {
1.572 + return mapKey(item, callback);
1.573 + } else {
1.574 + return item;
1.575 + }
1.576 + });
1.577 + }
1.578 +
1.579 + function visitPropertiesOrArrayEntries(rootObject, visitorCallback) {
1.580 + if (rootObject instanceof Array) {
1.581 + for (var i = 0; i < rootObject.length; i++)
1.582 + visitorCallback(i);
1.583 + } else {
1.584 + for (var propertyName in rootObject)
1.585 + visitorCallback(propertyName);
1.586 + }
1.587 + };
1.588 +
1.589 + function canHaveProperties(object) {
1.590 + var type = exports.getType(object);
1.591 + return (type === "object" || type === "array") && (object !== null) && (type !== "undefined");
1.592 + }
1.593 +
1.594 + // Based on the parentName, this creates a fully classified name of a property
1.595 +
1.596 + function getPropertyName(parentName, parent, indexer) {
1.597 + var propertyName = parentName || "";
1.598 + if (parent instanceof Array) {
1.599 + if (parentName) {
1.600 + propertyName += "[" + indexer + "]";
1.601 + }
1.602 + } else {
1.603 + if (parentName) {
1.604 + propertyName += ".";
1.605 + }
1.606 + propertyName += indexer;
1.607 + }
1.608 + return propertyName;
1.609 + }
1.610 +
1.611 + function visitModel(rootObject, callback, options, parentName, fullParentName) {
1.612 + // If nested object was already mapped previously, take the options from it
1.613 + if (parentName !== undefined && exports.isMapped(rootObject)) {
1.614 + //options = ko.utils.unwrapObservable(rootObject)[mappingProperty];
1.615 + options = mergeOptions(ko.utils.unwrapObservable(rootObject)[mappingProperty], options);
1.616 + parentName = "";
1.617 + }
1.618 +
1.619 + if (parentName === undefined) { // the first call
1.620 + visitedObjects = new objectLookup();
1.621 + }
1.622 +
1.623 + parentName = parentName || "";
1.624 +
1.625 + var mappedRootObject;
1.626 + var unwrappedRootObject = ko.utils.unwrapObservable(rootObject);
1.627 + if (!canHaveProperties(unwrappedRootObject)) {
1.628 + return callback(rootObject, fullParentName);
1.629 + } else {
1.630 + // Only do a callback, but ignore the results
1.631 + callback(rootObject, fullParentName);
1.632 + mappedRootObject = unwrappedRootObject instanceof Array ? [] : {};
1.633 + }
1.634 +
1.635 + visitedObjects.save(rootObject, mappedRootObject);
1.636 +
1.637 + var origFullParentName = fullParentName;
1.638 + visitPropertiesOrArrayEntries(unwrappedRootObject, function (indexer) {
1.639 + if (options.ignore && ko.utils.arrayIndexOf(options.ignore, indexer) != -1) return;
1.640 +
1.641 + var propertyValue = unwrappedRootObject[indexer];
1.642 + var fullPropertyName = getPropertyName(parentName, unwrappedRootObject, indexer);
1.643 +
1.644 + // If we don't want to explicitly copy the unmapped property...
1.645 + if (ko.utils.arrayIndexOf(options.copy, indexer) === -1) {
1.646 + // ...find out if it's a property we want to explicitly include
1.647 + if (ko.utils.arrayIndexOf(options.include, indexer) === -1) {
1.648 + // Options contains all the properties that were part of the original object.
1.649 + // 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.
1.650 + if (options.mappedProperties && !options.mappedProperties[fullPropertyName] && !(unwrappedRootObject instanceof Array)) {
1.651 + return;
1.652 + }
1.653 + }
1.654 + }
1.655 +
1.656 + fullParentName = getPropertyName(origFullParentName, unwrappedRootObject, indexer);
1.657 +
1.658 + var propertyType = exports.getType(ko.utils.unwrapObservable(propertyValue));
1.659 + switch (propertyType) {
1.660 + case "object":
1.661 + case "array":
1.662 + case "undefined":
1.663 + var previouslyMappedValue = visitedObjects.get(propertyValue);
1.664 + mappedRootObject[indexer] = (exports.getType(previouslyMappedValue) !== "undefined") ? previouslyMappedValue : visitModel(propertyValue, callback, options, fullPropertyName, fullParentName);
1.665 + break;
1.666 + default:
1.667 + mappedRootObject[indexer] = callback(propertyValue, fullParentName);
1.668 + }
1.669 + });
1.670 +
1.671 + return mappedRootObject;
1.672 + }
1.673 +
1.674 + function objectLookup() {
1.675 + var keys = [];
1.676 + var values = [];
1.677 + this.save = function (key, value) {
1.678 + var existingIndex = ko.utils.arrayIndexOf(keys, key);
1.679 + if (existingIndex >= 0) values[existingIndex] = value;
1.680 + else {
1.681 + keys.push(key);
1.682 + values.push(value);
1.683 + }
1.684 + };
1.685 + this.get = function (key) {
1.686 + var existingIndex = ko.utils.arrayIndexOf(keys, key);
1.687 + return (existingIndex >= 0) ? values[existingIndex] : undefined;
1.688 + };
1.689 + };
1.690 +}));
1.691 \ No newline at end of file