UNPKG

knockout.mapper.js

Version:

Knockout.js plugin for object mapping including validation.

299 lines (275 loc) 9.6 kB
/// Knockout Mapper plugin /// (c) 2014 Imre Fazekas /// License: MIT (http://www.opensource.org/licenses/mit-license.php) (function (factory) { // Module systems magic dance. if (typeof require === "function" && typeof exports === "object" && typeof module === "object") { // CommonJS or Node: hard-coded dependency on "knockout" factory(require("knockout"), exports); } else if (typeof define === "function" && define.amd) { // AMD anonymous module with hard-coded dependency on "knockout" define(["knockout", "exports"], factory); } else { // <script> tag: use the global `ko` object, attaching a `mapping` property factory(ko, ko.mapping = {}); } }( function (ko, exports) { var breaker = {}; var nativeForEach = Array.prototype.forEach; var toString = Object.prototype.toString; var isArray = Array.isArray || function(obj) { return toString.call(obj) === '[object Array]'; }; var isString = function (obj) { return "[object String]" === toString.call(obj); }; var isObject = function (obj) { return obj === Object(obj); }; var isBoolean = function(obj) { return obj === true || obj === false || toString.call(obj) === '[object Boolean]'; }; var isNumber = function (obj) { return (toString.call(obj) === "[object " + Number + "]") || !isNaN(obj); }; var isFunction = function (obj) { return toString.call(obj) === "[object " + Function + "]"; }; var isDate = function (obj) { return toString.call(obj) === "[object " + Date + "]"; }; if (typeof (/./) !== 'function') { isFunction = function(obj) { return typeof obj === 'function'; }; } var each = function(obj, iterator, context) { if (obj === null) return; if (nativeForEach && obj.forEach === nativeForEach) { obj.forEach(iterator, context); } else if ( isArray(obj) ) { for (var i = 0, l = obj.length; i < l; i+=1) { if (iterator.call(context, obj[i], i, obj) === breaker) return; } } else { for (var key in obj) { if (iterator.call(context, obj[key], key, obj) === breaker) return; } } }; exports.toJSONByPrototype = ko.toJSONByPrototype = function(VM, M){ var self = VM; var toInnerJSON = function(obj, dataModel, viewModel){ Object.keys(dataModel).forEach( function(key, index, array){ if( viewModel && viewModel[key] ){ var value = dataModel[key]; if( isArray( value ) ){ obj[ key ] = []; if( isArray( viewModel[key] ) ) obj[ key ] = viewModel[key]; else{ var marray = viewModel[key](); var isAnObject = value.length > 0 && value[0] && isObject( value[0] ); if( isAnObject ){ each( marray, function(element, ind, list){ obj[ key ].push( toInnerJSON( {}, value[0], element) ); } ); } else { each( marray, function(element, ind, list){ obj[ key ].push( isFunction( element ) ? element() : element ); } ); } } } else if( isString( value ) || isNumber( value ) || isBoolean( value ) ){ obj[ key ] = isFunction( viewModel[ key ] ) ? viewModel[ key ]() : viewModel[ key ]; } else if( isFunction( value ) ){ return; } else if( isObject( value ) && isFunction( value.read ) && isFunction( value.write ) ){ return; } else if( isObject( value ) ){ obj[ key ] = {}; toInnerJSON( obj[ key ], value, value._observable ? viewModel[ key ]() : viewModel[ key ] ); } } }); return obj; }; return toInnerJSON( {}, M, self ); }; exports.resetViewModel = ko.resetViewModel = function(VM){ var self = VM; var innerResetViewModel = function(viewModel){ if( !viewModel ) return; if( isFunction(viewModel) ){ if( viewModel.originalValue ) viewModel( viewModel.originalValue() ); } else if( isObject(viewModel) ){ each( viewModel, function(value, key, list){ innerResetViewModel( viewModel[ key ] ); }); } return viewModel; }; innerResetViewModel( self ); }; // pass the viewmodel to build up, the reference data model, validation definions, functions and static data exports.updateViewModel = ko.updateViewModel = function(VM, M){ var self = VM; var innerUpdateViewModel = function(data, viewModel, path){ if(data && viewModel) each( data, function(value, key, list){ if( viewModel[ key ] ){ var name = path + '.' + key; if( isArray( value ) && viewModel[ key ] ){ viewModel[ key ]([]); var isAnObject = value.length > 0 && value[0] && isObject( value[0] ); if( isAnObject ){ each( value, function(element, index, list){ viewModel[ key ].push( exports.mapObject({}, element ) ); } ); } else { viewModel[ key ]( value ); } } else if( isString( value ) || isNumber( value ) || isBoolean( value ) ){ if( viewModel[ key ] && isFunction(viewModel[ key ]) ) viewModel[ key ]( value ); } else if( isFunction( value ) ){ return; } else if( isObject( value ) && isFunction( value.read ) && isFunction( value.write ) ){ return; } else if( isObject( value ) ){ if( isFunction( viewModel[ key ] ) ) viewModel[ key ]( value ); else innerUpdateViewModel( value, viewModel[ key ], name ); } } } ); return viewModel; }; innerUpdateViewModel( M, self, ''); }; ko.extenders.originalValue = function (observable, value) { if(!observable.originalValue) { observable.originalValue = ko.observable( value ); } }; function extend( model, validation ){ model.extend( isFunction(validation) ? { fn: validation } : validation ); } exports.mapObject = ko.mapObject = function(VM, M, V, F, S, options){ var self = VM; var _m = M, _v = V || {}, _f = F || {}, _s = S || {}; var mappingOptions = options || {}; var _MakeViewModel = function(data, viewModel, validationRules, context){ var validation = validationRules || {}; each( data, function(value, key, list){ if(key === '_observable') return; if( isArray( value ) ){ viewModel[ key ] = ko.observableArray(); if(validation[key]) { extend( viewModel[key], (isArray(validation[key]) && validation[key].length>0) ? validation[key][0] : validation[key] ); } var isAnObject = value.length > 0 && value[0] && isObject( value[0] ); if( isAnObject ){ each( value, function(element, index, array){ viewModel[ key ].push( _MakeViewModel(element, {}, {}, context) ); } ); } else{ each( value, function(element, index, array){ viewModel[ key ].push( element ); } ); } } else if( isString( value ) || isNumber( value ) || isBoolean( value ) || isDate( value ) ){ viewModel[ key ] = ko.observable( value ).extend( { originalValue: value } ); if(validation[key]) { extend( viewModel[key], validation[key] ); } } else if( isFunction( value ) ){ //viewModel[ key ] = ko.computed( value, context ); return; } else if( isObject( value ) && isFunction( value.read ) && isFunction( value.write ) ){ //viewModel[ key ] = ko.computed( value, context ); return; } else if( isObject( value ) ){ viewModel[ key ] = value._observable ? ko.observable() : {}; _MakeViewModel( value, viewModel[ key ], validation[key], context ); } } ); return viewModel; }; var _MakeComputerModel = function(data, viewModel, validationRules, context){ var validation = validationRules || {}; each( data, function(value, key, list){ if( isArray( value ) ){ var isAnObject = value.length > 0 && value[0] && isObject( value[0] ); if( isAnObject ){ each( value, function(element, index, array){ _MakeViewModel(element, {}, context); } ); } } else if( isString( value ) || isNumber( value ) || isBoolean( value ) || isDate( value ) ){ return; } else if( isFunction( value ) ){ viewModel[ key ] = ko.pureComputed( value, context ); } else if( isObject( value ) && isFunction( value.read ) && isFunction( value.write ) ){ viewModel[ key ] = ko.pureComputed( value, context ); if( validation[key] && mappingOptions.validateReadWrite ) { extend( viewModel[key], validation[key] ); } } else if( isObject( value ) ){ _MakeComputerModel( value, viewModel[ key ], validation[key], context ); } } ); return viewModel; }; var _MakeFunctions = function( _methods, viewModel, context){ each( _methods, function(value, key, list){ if( isArray( value ) ){ viewModel[ key ] = []; each( value, function(element, index, array){ viewModel[ key ].push( isFunction(element) ? element.bind( context ) : element ); } ); } else if( isFunction(value) ){ viewModel[ key ] = value.bind( context ); } else if( isObject(value) ){ if( !viewModel[ key ] ) viewModel[ key ] = {}; _MakeFunctions( value, viewModel[ key ], context ); } }); }; //Statics each( _s, function(value, key, list){ self[key] = value; }); //ViewModel _MakeViewModel( _m, self, _v, self ); _MakeComputerModel( _m, self, _v, self ); //Behaviours _MakeFunctions( _f, self, self ); if( self.init ) self.init(); return self; }; } ));