Source: src/modules/mvc/mvc.js

/**
 * The page callback for mvc/collection/list/%/%.
 * @param {String} module
 * @param {String} type
 * @return {Object}
 */
function collection_list_page(module, type) {
  try {
    var content = {
      'collection_list': {
        'theme': 'jqm_item_list',
        'title': t('Collection')
      }
    };
    var items = [];
    var collection = collection_load(module, type);
    if (collection) {
      for (var id in collection) {
          if (!collection.hasOwnProperty(id)) { continue; }
          var item = collection[id];
          var path = 'mvc/item/' + module + '/' + type + '/' + id;
          items.push(l(item.name, path));
      }
      content.collection_list.items = items;
    }
    return content;
  }
  catch (error) { console.log('collection_list_page - ' + error); }
}

/**
 * Given a module name and model type, this will return the collection
 * JSON object.
 * @param {String} module
 * @param {String} type
 * @return {*}
 */
function collection_load(module, type) {
  try {
    return JSON.parse(
      window.localStorage.getItem(
        mvc_get_collection_key('collection', module, type)
      )
    );
  }
  catch (error) { console.log('collection_load - ' + error); }
}

/**
 * Given a module name, model type and item collection, this will save the
 * collection JSON object to local storage.
 * @param {String} module
 * @param {String} type
 * @param {Object} collection
 */
function collection_save(module, type, collection) {
  try {
    window.localStorage.setItem(
      mvc_get_collection_key('collection', module, type),
      JSON.stringify(collection)
    );
  }
  catch (error) { console.log('collection_save - ' + error); }
}

/**
 * Given a bucket (e.g. collection, settings), module name and mvc model type,
 * this will return the local storage key used for the model type's item
 * collection.
 * @param {String} bucket
 * @param {String} module
 * @param {String} model_type
 * @return {String}
 */
function mvc_get_collection_key(bucket, module, model_type) {
    return 'mvc_' + bucket + '_' + module + '_' + model_type;
}

/**
 * Implements hook_install().
 */
function mvc_install() {
  try {
    // Load models...
    // For each module that implements hook_mvc_model(), iterate over each model
    // placing it into drupalgap.mvc.models, each model will be placed into
    // a namespace according to the module that implements it, to avoid
    // namespace collisions.
    var modules = module_implements('mvc_model');
    for (var i = 0; i < modules.length; i++) {
      var module = modules[i];
      var models = module_invoke(module, 'mvc_model');
      if (models) {
        // Create namespace for model, keyed by module name.
        if (!drupalgap.mvc.models[module]) {
          drupalgap.mvc.models[module] = {};
        }
        // For each model type...
        for (var model_type in models) {
            if (!models.hasOwnProperty(model_type)) { continue; }
            var model = models[model_type];
            // Set the primary key 'id', the module name, and model type
            // on the model fields. These are the mvc_model_system_fields().
            model.fields.id = {
              'type': 'hidden',
              'title': t('ID'),
              'required': false
            };
            model.fields.module = {
              'type': 'hidden',
              'title': t('Module'),
              'required': true,
              'default_value': module
            };
            model.fields.type = {
              'type': 'hidden',
              'title': t('Model Type'),
              'required': true,
              'default_value': model_type
            };
            // Add each model type to its namespace within drupalgap.mvc.models
            drupalgap.mvc.models[module][model_type] = model;
            // Save an empty collection to local storage for this model type, if
            // one doesn't already exist.
            var collection_key = mvc_get_collection_key(
              'collection',
              module,
              model_type
            );
            if (!window.localStorage.getItem(collection_key)) {
              window.localStorage.setItem(collection_key, '[]');
              // Save settings for the collection to local storage. The auto
              // increment value represents an item id, we start counting at
              // zero since the collection is an array.
              window.localStorage.setItem(
                mvc_get_collection_key('settings', module, model_type),
                '{"auto_increment":0}'
              );
            }
        }
      }
    }
    //console.log(JSON.stringify(drupalgap.mvc.models));
    //drupalgap_alert('drupalgap_mvc_init');
    // These may not be needed. Perhaps we should just call assumed hooks that
    // should be implemented for any custom views and controllers at the time
    // they are needed, probably no need to bundle them inside drupalgap.mvc.
    //var views = module_invoke_all('mvc_view');
    //var controllers = module_invoke_all('mvc_controller');
  }
  catch (error) { console.log('mvc_install - ' + error); }
}

/**
 * Implements hook_menu().
 * @return {Object}
 */
function mvc_menu() {
    var items = {
      'mvc/collection/list/%/%': {
        'page_callback': 'collection_list_page',
        'page_arguments': [3, 4]
      },
      'mvc/item/%/%/%': {
        'page_callback': 'item_view_page',
        'page_arguments': [2, 3, 4]
      },
      'mvc/item-add/%/%': {
        title: t('Add'),
        page_callback: 'drupalgap_get_form',
        page_arguments: ['item_create_form', 2, 3]
      }
    };
    return items;
}

/**
 * Returns an array of system fields (properties) to be used on item JSON
 * object.
 * @return {Array}
 */
function mvc_model_system_fields() {
    return ['id', 'module', 'type'];
}

// We'll need developer friendly front end functions, e.g.
// model_load();
// model_save();
// model_delete();
// item_load();
// item_save();
// item_delete();
// etc...

/**
 * Given a module name and a corresponding model name, this will load the model
 * from drupalgap.mvc.models.
 * @param {String} module
 * @param {String} name
 * @return {Object}
 */
function model_load(module, name) {
  try {
    if (
      drupalgap.mvc.models[module] === 'undefined' ||
      drupalgap.mvc.models[module][name] === 'undefined'
    ) { return false; }
    return drupalgap.mvc.models[module][name];
  }
  catch (error) { console.log('model_load - ' + error); }
}

/**
 * Given a module type, and model type, this generates and returns the form JSON
 * object to create a model item.
 * @param {Object} form
 * @param {Object} form_state
 * @param {String} module
 * @param {String} type
 * @return {Object}
 */
function item_create_form(form, form_state, module, type) {
   try {
    //var form = drupalgap_form_defaults('item_create_form');
    var model = model_load(module, type);
    if (model) {
      // @todo - this could be dangerous just overriding the elements variable,
      // we should iterate over the model fields and add them one by one
      // instead.
      form.elements = model.fields;
      form.buttons.cancel = drupalgap_form_cancel_button();
      form.elements.submit = {
        type: 'submit',
        value: t('Create')
      };
    }
    return form;
  }
  catch (error) { console.log('item_create_form - ' + error); }
}

/**
 * Handles the submission of an mvc model item creation form.
 * @param {Object} form
 * @param {Object} form_state
 */
function item_create_form_submit(form, form_state) {
  try {
    // Save the item and then view it.
    if (item_save(form_state.values)) {
      /*var path = 'item/' +
                 form_state.values.module + '/' +
                 form_state.values.type + '/' +
                 form_state.values.id;*/
      var path = 'mvc/collection/list/' +
                 form_state.values.module + '/' +
                 form_state.values.type;
      // If there is a form action path set, use that instead.
      if (form.action) { path = form.action; }
      // Go to our destination path, and force a reload on the page.
      drupalgap_goto(path, {reloadPage: true});
    }
    else {
      var msg = 'item_create_form_submit - failed to save item!';
      drupalgap_alert(msg);
    }
  }
  catch (error) {
    console.log('item_create_form_submit - ' + error);
  }
}

/**
 * Given a module name, mvc model type, and item id, this will return the item,
 * or false if the item fails to load.
 * @param {String} module
 * @param {String} type
 * @param {String} id
 * @return {Object}
 */
function item_load(module, type, id) {
  try {
    var item = false;
    var collection = collection_load(module, type);
    if (collection && typeof collection[id] !== 'undefined') {
      item = collection[id];
    }
    return item;
  }
  catch (error) { console.log('item_load - ' + error); }
}

/**
 * Given an mvc item, this saves it to local storage.
 * @param {Object} item
 * @return {Boolean}
 */
function item_save(item) {
  try {
    if (typeof item === 'undefined') { return false; }

    // Grab the settings for this collection.
    var settings_key = mvc_get_collection_key(
      'settings',
      item.module,
      item.type
    );
    var settings = JSON.parse(window.localStorage.getItem(settings_key));

    // If there is no id, then this is a new item, grab the next id to use.
    if (!item.id) {
      item.id = settings.auto_increment;
    }
    // Load the collection from local storage.
    var collection = collection_load(item.module, item.type);

    // Add the item onto the collection.
    collection[item.id] = item;

    // Save the collection to local storage.
    collection_save(item.module, item.type, collection);

    // Increment to the next item id and save the settings.
    settings.auto_increment = item.id + 1;
    window.localStorage.setItem(settings_key, JSON.stringify(settings));

    return true;
  }
  catch (error) { console.log('item_save - ' + error); }
}

/**
 * The page callback for mvc/item/%/%/%.
 * @param {String} module
 * @param {String} type
 * @param {Object} item
 * @return {String}
 */
function item_view_page(module, type, item) {
  try {
    var html = '';
    var model = model_load(module, type);
    if (model) { html = theme('item', {'model': model, 'item': item}); }
    return html;
  }
  catch (error) { console.log('item_view_page - ' + error); }
}