exports.createWidget = function(id, name, args) { if ("undefined" != typeof name && null !== name && _.isObject(name) && !_.isString(name)) { args = name; name = DEFAULT_WIDGET; } return new (require("alloy/widgets/" + id + "/controllers/" + (name || DEFAULT_WIDGET)))(args); };
apiCall(params, function(_response) { if (_response.success) { if (_response.code != 304) { if (deleteAllOnFetch || params.deleteAllOnFetch) { deleteAllSQL(); } var data = parseJSON(_response, parentNode); if (!params.localOnly) { //we dont want to manipulate the data on localOnly requests saveData(data); } } resp = readSQL(data); _.isFunction(params.success) && params.success(resp, _response.code, _response.xhr); model.trigger("fetch"); } else { //error or offline - read local data if (!params.localOnly && (params.initFetchWithLocalData || initFetchWithLocalData)) { }else{ resp = readSQL(); } if (_.isUndefined(_response.offline)) { //error _.isFunction(params.error) && params.error(returnErrorResponse ? _response : resp); } else { //offline - still a data success _.isFunction(params.success) && params.success(resp); model.trigger("fetch"); } } });
apiCall(params, function(_response) { if (_response.success) { var data = parseJSON(_response, parentNode); // Rest API should return a new model id. resp = saveData(data); _.isFunction(params.success) && params.success(resp, _response.code, _response.xhr); } else { // offline or error // save data locally when server returned an error if (!_response.localOnly && (params.disableSaveDataLocallyOnServerError || disableSaveDataLocallyOnServerError)) { logger(DEBUG, "NOTICE: The data is not being saved locally"); } else { resp = saveData(); } if (_.isUndefined(_response.offline)) { // error _.isFunction(params.error) && params.error(returnErrorResponse ? _response : resp); } else { //offline - still a data success _.isFunction(params.success) && params.success(resp); } } });
exports.C = function(name, modelDesc, model) { var extendObj = { model: model }; var config = (model ? model.prototype.config : {}) || {}; var mod; if (config.adapter && config.adapter.type) { mod = require('alloy/sync/' + config.adapter.type); extendObj.sync = function(method, model, opts) { mod.sync(method,model,opts); }; } else { extendObj.sync = function(method, model, opts) { Ti.API.warn('Execution of ' + method + '#sync() function on a collection that does not support persistence'); Ti.API.warn('model: ' + JSON.stringify(model.toJSON())); }; } var Collection = Backbone.Collection.extend(extendObj); Collection.prototype.config = config; // extend the collection object if (_.isFunction(modelDesc.extendCollection)) { Collection = modelDesc.extendCollection(Collection) || Collection; } // do any post collection creation code form the sync adapter if (mod && _.isFunction(mod.afterCollectionCreate)) { mod.afterCollectionCreate(Collection); } return Collection; };
///////////////////////////////////////////// //SQL INTERFACE ///////////////////////////////////////////// function saveData(data) { if (!data && !isCollection) { data = model.toJSON(); } if (!data) { // its empty return; } if (!_.isArray(data)) { // its a model if (!_.isUndefined(data["is_deleted"])) { //delete item deleteSQL(data[model.idAttribute]); } else if (sqlFindItem(data[model.idAttribute]).length == 1) { //item exists - update it return updateSQL(data); } else { //write data to local sql return createSQL(data); } } else { //its an array of models var currentModels = sqlCurrentModels(); for (var i in data) { if (!_.isUndefined(data[i]["is_deleted"])) { //delete item deleteSQL(data[i][model.idAttribute]); } else if (_.indexOf(currentModels, data[i][model.idAttribute]) != -1) { //item exists - update it updateSQL(data[i]); } else { //write data to local sql createSQL(data[i]); } } } }
exports.addClass = function(controller, proxy, classes, opts) { // make sure we actually have classes to add if (!classes) { if (opts) { if (MW320_CHECK) { delete opts.apiName; } proxy.applyProperties(opts); } return; } else { // create a union of the existing classes with the new one(s) var pClasses = proxy[CONST.CLASS_PROPERTY] || []; var beforeLen = pClasses.length; classes = _.isString(classes) ? classes.split(/\s+/) : classes; var newClasses = _.union(pClasses, classes || []); // make sure we actually added classes before processing styles if (beforeLen === newClasses.length) { if (opts) { if (MW320_CHECK) { delete opts.apiName; } proxy.applyProperties(opts); } return; } else { processStyle(controller, proxy, newClasses, opts); } } };
exports.removeClass = function(controller, proxy, classes, opts) { classes = classes || []; var pClasses = proxy[CONST.CLASS_PROPERTY] || []; var beforeLen = pClasses.length; // make sure there's classes to remove before processing if (!beforeLen || !classes.length) { if (opts) { if (MW320_CHECK) { delete opts.apiName; } proxy.applyProperties(opts); } return; } else { // remove the given class(es) classes = _.isString(classes) ? classes.split(/\s+/) : classes; var newClasses = _.difference(pClasses, classes); // make sure there was actually a difference before processing if (beforeLen === newClasses.length) { if (opts) { if (MW320_CHECK) { delete opts.apiName; } proxy.applyProperties(opts); } return; } else { processStyle(controller, proxy, newClasses, opts, RESET); } } };
exports.C = function(name, modelDesc, model) { var extendObj = { model: model, sync: function(method, model, opts) { var config = (model.config || {}); var type = (config.adapter ? config.adapter.type : null) || 'localDefault'; if (type === 'localDefault') { type = OS_MOBILEWEB ? 'localStorage' : 'sql'; } require('alloy/sync/'+type).sync(method,model,opts); } }; var Collection = Backbone.Collection.extend(extendObj); var config = Collection.prototype.config = model.prototype.config; var type = (config.adapter ? config.adapter.type : null) || 'localDefault'; var adapter = require('alloy/sync/'+type); if (_.isFunction(adapter.afterCollectionCreate)) { adapter.afterCollectionCreate(Collection); } if (_.isFunction(modelDesc.extendCollection)) { Collection = modelDesc.extendCollection(Collection) || Collection; } return Collection; };
/* * Adapted version of node.extend https://www.npmjs.org/package/node.extend * * Original copyright: * * node.extend * Copyright 2011, John Resig * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * @fileoverview * Port of jQuery.extend that actually works on node.js */ function deepExtend() { var target = arguments[0] || {}; var i = 1; var length = arguments.length; var deep = false; var options, name, src, copy, copy_is_array, clone; // Handle a deep copy situation if (typeof target === 'boolean') { deep = target; target = arguments[1] || {}; // skip the boolean and the target i = 2; } // Handle case when target is a string or something (possible in deep copy) if (typeof target !== 'object' && !_.isFunction(target)) { target = {}; } for (; i < length; i++) { // Only deal with non-null/undefined values options = arguments[i] if (options != null) { if (typeof options === 'string') { options = options.split(''); } // Extend the base object for (name in options) { src = target[name]; copy = options[name]; // Prevent never-ending loop if (target === copy) { continue; } // Recurse if we're merging plain objects or arrays if (deep && copy && (_.isObject(copy) || (copy_is_array = _.isArray(copy)))) { if (copy_is_array) { copy_is_array = false; clone = src && _.isArray(src) ? src : []; } else { clone = src && _.isObject(src) ? src : {}; } // Never move original objects, clone them target[name] = deepExtend(deep, clone, copy); // Don't bring in undefined values } else if (typeof copy !== 'undefined') { target[name] = copy; } } } } // Return the modified object return target; };
function readSQL() { if(DEBUG){ Ti.API.debug("[SQL REST API] readSQL"); } var sql = opts.query || 'SELECT * FROM ' + table; // execute the select query db = Ti.Database.open(dbName); if (opts.query) { var rs = db.execute(opts.query.sql, opts.query.params); } else { if(opts.data){ //extend sql where with data opts.sql = opts.sql || {}; opts.sql.where = opts.sql.where || {}; _.extend(opts.sql.where, opts.data ); } var sql = _buildQuery(table, opts.sql || opts); if(DEBUG){ Ti.API.debug("[SQL REST API] SQL QUERY: " + sql); } var rs = db.execute(sql); } var len = 0; var values = []; // iterate through all queried rows while (rs.isValidRow()) { var o = {}; var fc = 0; fc = _.isFunction(rs.fieldCount) ? rs.fieldCount() : rs.fieldCount; // create list of rows returned from query _.times(fc, function(c) { var fn = rs.fieldName(c); o[fn] = rs.fieldByName(fn); }); values.push(o); if(isCollection){ //push the models var m = new model.config.Model(o); model.models.push(m); } len++; rs.next(); } // close off db after read query rs.close(); db.close(); // shape response based on whether it's a model or collection model.length = len; if(DEBUG){Ti.API.debug("readSQL length: " + len);} return len === 1 ? resp = values[0] : resp = values; }
exports.createWidget = function(id, name, args) { if (typeof name !== 'undefined' && name !== null && _.isObject(name) && !_.isString(name)) { args = name; name = DEFAULT_WIDGET; } return new (require('alloy/widgets/' + id + '/controllers/' + (name || DEFAULT_WIDGET)))(args); };
function parseJSON(DEBUG, _response, parentNode) { var data = _response.responseJSON; if (!_.isUndefined(parentNode)) { data = _.isFunction(parentNode) ? parentNode(data) : traverseProperties(data, parentNode); } logger(DEBUG, "server response", _response); return data; }
function installDatabase(config) { var dbFile = config.adapter.db_file; var table = config.adapter.collection_name; var rx = /(^|.*\/)([^\/]+)\.[^\/]+$/; var match = dbFile.match(rx); if (null === match) throw 'Invalid sql database filename "' + dbFile + '"'; config.adapter.db_name = config.adapter.db_name || match[2]; var dbName = config.adapter.db_name; Ti.API.debug('Installing sql database "' + dbFile + '" with name "' + dbName + '"'); var db = Ti.Database.install(dbFile, dbName); if (false === config.adapter.remoteBackup && true) { Ti.API.debug('iCloud "do not backup" flag set for database "' + dbFile + '"'); db.file.setRemoteBackup(false); } var rs = db.execute('pragma table_info("' + table + '");'); var cName, cType, columns = {}; if (rs) { while (rs.isValidRow()) { cName = rs.fieldByName("name"); cType = rs.fieldByName("type"); columns[cName] = cType; cName !== ALLOY_ID_DEFAULT || config.adapter.idAttribute || (config.adapter.idAttribute = ALLOY_ID_DEFAULT); rs.next(); } rs.close(); } else { config.adapter.idAttribute ? config.adapter.idAttribute : ALLOY_ID_DEFAULT; for (var k in config.columns) { cName = k; cType = config.columns[k]; cName !== ALLOY_ID_DEFAULT || config.adapter.idAttribute ? k === config.adapter.idAttribute && (cType += " UNIQUE") : config.adapter.idAttribute = ALLOY_ID_DEFAULT; columns[cName] = cType; } } config.columns = columns; if (config.adapter.idAttribute) { if (!_.contains(_.keys(config.columns), config.adapter.idAttribute)) throw 'config.adapter.idAttribute "' + config.adapter.idAttribute + '" not found in list of columns for table "' + table + '"\n' + "columns: [" + _.keys(config.columns).join(",") + "]"; } else { Ti.API.info('No config.adapter.idAttribute specified for table "' + table + '"'); Ti.API.info('Adding "' + ALLOY_ID_DEFAULT + '" to uniquely identify rows'); var fullStrings = [], colStrings = []; _.each(config.columns, function(type, name) { colStrings.push(name); fullStrings.push(name + " " + type); }); var colsString = colStrings.join(","); db.execute("ALTER TABLE " + table + " RENAME TO " + table + "_temp;"); db.execute("CREATE TABLE " + table + "(" + fullStrings.join(",") + "," + ALLOY_ID_DEFAULT + " TEXT UNIQUE);"); db.execute("INSERT INTO " + table + "(" + colsString + "," + ALLOY_ID_DEFAULT + ") SELECT " + colsString + ",CAST(_ROWID_ AS TEXT) FROM " + table + "_temp;"); db.execute("DROP TABLE " + table + "_temp;"); config.columns[ALLOY_ID_DEFAULT] = "TEXT UNIQUE"; config.adapter.idAttribute = ALLOY_ID_DEFAULT; } db.close(); }
function parseJSON(_response, parentNode) { var data = JSON.parse(_response.responseText); if (!_.isUndefined(parentNode)) { data = _.isFunction(parentNode) ? parentNode(data) : traverseProperties(data, parentNode); } if (DEBUG) { Ti.API.info("[SQL REST API] server response: "); Ti.API.debug(data) } return data; }
_.each(data, function(v, f) { if (_.isArray(v)) { //select multiple items var innerWhere = []; _.each(v, function(value) { innerWhere.push(f + whereOperator + _valueType(value)); }); where.push(innerWhere.join(' OR ')); } else if (_.isObject(v)) { where = whereBuilder(where, v, whereOperator); } else { where.push(f + whereOperator + _valueType(v)); } });
function installDatabase(config) { // get the database name from the db file path var dbFile = config.adapter.db_file; var table = config.adapter.collection_name; var rx = /^([\/]{0,1})([^\/]+)\.[^\/]+$/; var match = dbFile.match(rx); if (match === null) { throw 'Invalid sql database filename "' + dbFile + '"'; } //var isAbsolute = match[1] ? true : false; var dbName = config.adapter.db_name = match[2]; // install and open the preloaded db Ti.API.debug('Installing sql database "' + dbFile + '" with name "' + dbName + '"'); var db = Ti.Database.install(dbFile, dbName); // compose config.columns from table definition in database var rs = db.execute('pragma table_info("' + table + '");'); var columns = {}; while (rs.isValidRow()) { var cName = rs.fieldByName('name'); var cType = rs.fieldByName('type'); columns[cName] = cType; // see if it already has the ALLOY_ID_DEFAULT if (cName === ALLOY_ID_DEFAULT && !config.adapter.idAttribute) { config.adapter.idAttribute = ALLOY_ID_DEFAULT; } rs.next(); } config.columns = columns; rs.close(); // make sure we have a unique id field if (config.adapter.idAttribute) { if (!_.contains(_.keys(config.columns), config.adapter.idAttribute)) { throw 'config.adapter.idAttribute "' + config.adapter.idAttribute + '" not found in list of columns for table "' + table + '"\n' + 'columns: [' + _.keys(config.columns).join(',') + ']'; } } else { Ti.API.info('No config.adapter.idAttribute specified for table "' + table + '"'); Ti.API.info('Adding "' + ALLOY_ID_DEFAULT + '" to uniquely identify rows'); db.execute('ALTER TABLE ' + table + ' ADD ' + ALLOY_ID_DEFAULT + ' TEXT;'); config.columns[ALLOY_ID_DEFAULT] = 'TEXT'; config.adapter.idAttribute = ALLOY_ID_DEFAULT; } // close the db handle db.close(); }
function updateSQL(data) { var attrObj = {}; if (DEBUG) { Ti.API.debug("updateSQL data:"); Ti.API.debug(data); } if (data) { attrObj = data; } else { if (!isCollection) { attrObj = model.toJSON(); } else { Ti.API.error("Its a collection - error!"); } } // Create arrays for insert query var names = [], values = [], q = []; for (var k in columns) { if (!_.isUndefined(attrObj[k])) {//only update those who are in the data names.push(k + '=?'); if (_.isObject(attrObj[k])) { values.push(JSON.stringify(attrObj[k])); } else { values.push(attrObj[k]); } q.push('?'); } } // compose the update query var sql = 'UPDATE ' + table + ' SET ' + names.join(',') + ' WHERE ' + model.idAttribute + '=?'; values.push(attrObj[model.idAttribute]); if (DEBUG) { Ti.API.debug("updateSQL sql: " + sql); Ti.API.debug(values); } // execute the update db = Ti.Database.open(dbName); db.execute(sql, values); if (lastModifiedColumn && _.isUndefined(params.disableLastModified)) { var updateSQL = "UPDATE " + table + " SET " + lastModifiedColumn + " = DATETIME('NOW') WHERE " + model.idAttribute + "=?"; db.execute(updateSQL, attrObj[model.idAttribute]); } db.close(); return attrObj; }
exports.deepExtend = function() { var target = arguments[0] || {}; var i = 1; var length = arguments.length; var deep = false; var options, name, src, copy, copy_is_array, clone; if ("boolean" == typeof target) { deep = target; target = arguments[1] || {}; i = 2; } "object" == typeof target || _.isFunction(target) || (target = {}); for (;length > i; i++) { options = arguments[i]; if (null != options) { "string" == typeof options && (options = options.split("")); for (name in options) { src = target[name]; copy = options[name]; if (target === copy) continue; if (deep && copy && !_.isFunction(copy) && _.isObject(copy) && ((copy_is_array = _.isArray(copy)) || !_.has(copy, "apiName"))) { if (copy_is_array) { copy_is_array = false; clone = src && _.isArray(src) ? src : []; } else clone = _.isDate(copy) ? new Date(copy.valueOf()) : src && _.isObject(src) ? src : {}; target[name] = exports.deepExtend(deep, clone, copy); } else target[name] = copy; } } } return target; };
exports.createStyle = function(controller, opts, defaults) { var classes, apiName; if (!opts) return {}; classes = _.isArray(opts.classes) ? opts.classes.slice(0) : _.isString(opts.classes) ? opts.classes.split(/\s+/) : []; apiName = opts.apiName; apiName && -1 === apiName.indexOf(".") && (apiName = addNamespace(apiName)); var styleArray; styleArray = controller && _.isObject(controller) ? require("alloy/widgets/" + controller.widgetId + "/styles/" + controller.name) : require("alloy/styles/" + controller); var styleFinal = {}; var i, len; for (i = 0, len = styleArray.length; len > i; i++) { var style = styleArray[i]; var styleApi = style.key; style.isApi && -1 === styleApi.indexOf(".") && (styleApi = (CONST.IMPLICIT_NAMESPACES[styleApi] || CONST.NAMESPACE_DEFAULT) + "." + styleApi); if (style.isId && opts.id && style.key === opts.id || style.isClass && _.contains(classes, style.key)) ; else { if (!style.isApi) continue; -1 === style.key.indexOf(".") && (style.key = addNamespace(style.key)); if (style.key !== apiName) continue; } if (style.queries && style.queries.formFactor && !Alloy[style.queries.formFactor]) continue; _.extend(styleFinal, style.style); } var extraStyle = _.omit(opts, [ CONST.CLASS_PROPERTY, CONST.APINAME_PROPERTY ]); _.extend(styleFinal, extraStyle); styleFinal[CONST.CLASS_PROPERTY] = classes; styleFinal[CONST.APINAME_PROPERTY] = apiName; return defaults ? _.defaults(styleFinal, defaults) : styleFinal; };
function updateSQL(data) { var attrObj = {}; logger(DEBUG, "updateSQL data: ", data); if (data) { attrObj = data; } else { if (!isCollection) { attrObj = model.toJSON(); } else { Ti.API.error("Its a collection - error!"); } } // Create arrays for insert query var names = [], values = [], q = []; for (var k in columns) { if (!_.isUndefined(attrObj[k])) { //only update those who are in the data names.push(k + '=?'); if (_.isObject(attrObj[k])) { values.push(JSON.stringify(attrObj[k])); } else { values.push(attrObj[k]); } q.push('?'); } } if (lastModifiedColumn && _.isUndefined(params.disableLastModified)) { values[_.indexOf(names, lastModifiedColumn + "=?")] = lastModifiedDateFormat ? moment().format(lastModifiedDateFormat) : moment().lang('en').zone('GMT').format('YYYY-MM-DD HH:mm:ss ZZ'); } // compose the update query var sql = 'UPDATE ' + table + ' SET ' + names.join(',') + ' WHERE ' + model.idAttribute + '=?'; values.push(attrObj[model.idAttribute]); logger(DEBUG, "updateSQL sql query: " + sql); logger(DEBUG, "updateSQL values: ", values); // execute the update db = Ti.Database.open(dbName); db.execute(sql, values); db.close(); return attrObj; }
exports.addClass = function(controller, proxy, classes, opts) { if (!classes) { opts && proxy.applyProperties(opts); return; } var pClasses = proxy[CONST.CLASS_PROPERTY] || []; var beforeLen = pClasses.length; classes = _.isString(classes) ? classes.split(/\s+/) : classes; var newClasses = _.union(pClasses, classes || []); if (beforeLen === newClasses.length) { opts && proxy.applyProperties(opts); return; } processStyle(controller, proxy, newClasses, opts); };
exports.M = function(name, modelDesc, migrations) { var config = modelDesc.config; var type = (config.adapter ? config.adapter.type : null) || 'localDefault'; if (type === 'localDefault') { type = OS_MOBILEWEB ? 'localStorage' : 'sql'; } var adapter = require('alloy/sync/'+type); var extendObj = { defaults: config.defaults, sync: function(method, model, opts) { var config = model.config || {}; var adapterObj = config.adapter || {}; var type = (config.adapter ? config.adapter.type : null) || 'localDefault'; if (type === 'localDefault') { type = OS_MOBILEWEB ? 'localStorage' : 'sql'; } require('alloy/sync/'+type).sync(method,model,opts); } }; var extendClass = {}; // construct the model based on the current adapter type if (migrations) { extendClass.migrations = migrations; } // Run the pre model creation code, if any if (_.isFunction(adapter.beforeModelCreate)) { config = adapter.beforeModelCreate(config, name) || config; } // Create the Model object var Model = Backbone.Model.extend(extendObj, extendClass); Model.prototype.config = config; // Extend the Model with extendModel(), if defined if (_.isFunction(modelDesc.extendModel)) { Model = modelDesc.extendModel(Model) || Model; } // Run the post model creation code, if any if (_.isFunction(adapter.afterModelCreate)) { adapter.afterModelCreate(Model, name); } return Model; };
exports.removeClass = function(controller, proxy, classes, opts) { classes = classes || []; var pClasses = proxy[CONST.CLASS_PROPERTY] || []; var beforeLen = pClasses.length; if (!beforeLen || !classes.length) { opts && proxy.applyProperties(opts); return; } classes = _.isString(classes) ? classes.split(/\s+/) : classes; var newClasses = _.difference(pClasses, classes); if (beforeLen === newClasses.length) { opts && proxy.applyProperties(opts); return; } processStyle(controller, proxy, newClasses, opts, RESET); };
extendCollection: function(Collection) { _.extend(Collection.prototype, { // extended functions and properties go here }); return Collection; }
extendModel: function(Model) { _.extend(Model.prototype, { // extended functions and properties go here }); return Model; },
apiCall(params, function(_response) { if (_response.success) { var data = parseJSON(_response, parentNode); resp = deleteSQL(); _.isFunction(params.success) && params.success(resp); } else { resp = deleteSQL(); if (_.isUndefined(_response.offline)) { //error _.isFunction(params.error) && params.error(resp); } else { //offline - still a data success _.isFunction(params.success) && params.success(resp); } } });
extendCollection: function(Collection) { _.extend(Collection.prototype, { comparator: function(tc) { return tc.get("TID"); } }); return Collection; }
function Sync(method, model, opts) { function storeModel(data) { localStorage.setItem(name, JSON.stringify(data)); } var name = model.config.adapter.collection_name, data = model.config.data, resp = null; switch (method) { case "create": if (!model.id) { model.id = guid(); model.set(model.idAttribute, model.id); } data[model.id] = model; storeModel(data); resp = model.toJSON(); break; case "read": var store = localStorage.getItem(name); var store_data = store && JSON.parse(store) || {}; var len = 0; for (var key in store_data) { var m = new model.config.Model(store_data[key]); model.models.push(m); len++; } model.length = len; resp = 1 === len ? model.models[0] : model.models; break; case "update": data[model.id] = model; storeModel(data); resp = model.toJSON(); break; case "delete": delete data[model.id]; storeModel(data); resp = model.toJSON(); } if (resp) { _.isFunction(opts.success) && opts.success(resp); "read" === method && model.trigger("fetch"); } else _.isFunction(opts.error) && opts.error(resp); }
apiCall(params, function(_response) { if (_response.success) { var data = parseJSON(_response, parentNode); //Rest API should return a new model id. resp = saveData(data); _.isFunction(params.success) && params.success(resp); } else { //offline or error resp = saveData(); if (_.isUndefined(_response.offline)) { // error _.isFunction(params.error) && params.error(resp); } else { //offline - still a data success _.isFunction(params.success) && params.success(resp); } } });
exports.M = function(name, modelDesc, migrations) { var config = (modelDesc || {}).config || {}; var adapter = config.adapter || {}; var extendObj = {}; var extendClass = {}; var mod; if (adapter.type) { mod = require('alloy/sync/' + adapter.type); extendObj.sync = function(method, model, opts) { mod.sync(method, model, opts); }; } else { extendObj.sync = function(method, model, opts) { Ti.API.warn('Execution of ' + method + '#sync() function on a model that does not support persistence'); Ti.API.warn('model: ' + JSON.stringify(model.toJSON())); }; } extendObj.defaults = config.defaults; // construct the model based on the current adapter type if (migrations) { extendClass.migrations = migrations; } // Run the pre model creation code, if any if (mod && _.isFunction(mod.beforeModelCreate)) { config = mod.beforeModelCreate(config, name) || config; } // Create the Model object var Model = Backbone.Model.extend(extendObj, extendClass); Model.prototype.config = config; // Extend the Model with extendModel(), if defined if (_.isFunction(modelDesc.extendModel)) { Model = modelDesc.extendModel(Model) || Model; } // Run the post model creation code, if any if (mod && _.isFunction(mod.afterModelCreate)) { mod.afterModelCreate(Model, name); } return Model; };