Source: js/lib/core/backend.js

/**
 * @class Backend
 * @classdesc This class is responsible to manage all app. 
 */
function Backend() {
  /**
   * Root all project info
   * @namespace Project
   */
  CBUtil.createNameSpace('Project');
  /**
   * Root all cloudbook info
   * @namespace Cloudbook
   */
  CBUtil.createNameSpace('Cloudbook');
  /**
   * Components are loaded
   * @namespace Actions
   * @memberOf Cloudbook
   */
  CBUtil.createNameSpace('Cloudbook.Actions');
  /**
   * Sections are available
   * @namespace Sections
   * @memberOf Cloudbook
   */
  CBUtil.createNameSpace('Cloudbook.Sections');
  /**
   * Ui information
   * @namespace UI
   * @memberOf Cloudbook
   */
  CBUtil.createNameSpace('Cloudbook.UI');
  /**
   * Description of project. This contain authods, name project , etc
   * @namespace Info
   * @memberOf Project
   */
  CBUtil.createNameSpace('Project.Info');
  /**
   * All sections, subsections and components of project
   * @namespace Data
   * @memberOf Project
   */
  CBUtil.createNameSpace('Project.Data');

  /**
   * Define jquery selector where render CBObjects
   * @type {String}
   */
  Cloudbook.UI.targetcontent = '#targetcontent';
  /**
   * Define jquery selector where render components buttons
   * @type {String}
   */
  Cloudbook.UI.navactions = '#navactions';
  /**
   * Define jquery selector where render components buttons
   * @type {String}
   */
  Cloudbook.UI.navsections = '#navsections';

}

/**
 * Check exists workspace and userconfig. If any not exists, create it.
 */
Backend.prototype.prepareWorkspace = function prepareWorkspace() {
  var fs = require('fs'),
      userConfig = application.config.user.getInstance();
  if ( ! fs.existsSync(Cloudbook.workspace)){
    fs.mkdirSync(Cloudbook.workspace);
  }
  userConfig.initialize();
};

/**
 * Go over components path to find components and append this components on Project.Actions namespace. 
 */
Backend.prototype.loadComponents = function loadComponents() {
  this.loadComponentsRecursive('./components');

};

/**
 * Go over components path to find components and append this components on Cloudbook.Actions with id
 * Also load extra scripts needed to components work fine
 * @param  {String} componentpath Path to load component
 */
Backend.prototype.loadComponentsRecursive = function loadComponentsRecursive(componentpath) {
  var fs = require('fs');
  var path = require('path');
  var that = this;
  var metadatapath = path.join(componentpath,'metadata.json');
  if(fs.existsSync(metadatapath)){
    var description = require("./"+metadatapath);
    Cloudbook.Actions[description.idtype] = {};
    Cloudbook.Actions[description.idtype]['path'] = componentpath;
    Cloudbook.Actions[description.idtype]['component'] = CBUtil.req(path.join(componentpath,'core.js'));
    that.loadComponentExtraScripts(componentpath , description);
  }
  else{
    var listdirectories = CBUtil.readOnlyDirectories(componentpath);
    listdirectories.forEach(function(directory){
      that.loadComponentsRecursive(path.join(componentpath,directory));

    });
  }

};

/**
 * Load all sections available 
 */
Backend.prototype.loadSectionsObjects = function() {
  Cloudbook.Sections = {};
  Cloudbook.Sections['basic'] = CBUtil.req('js/lib/core/cbsection/cbsection.js');
};


/**
 * On component metadata file may be field "external_scripts" . This field include libraries must be included on head file to work component fine.
 * This method is reponsible read metadata info to be include all files indicate on "external_scripts". This files are loaded 
 * @param  {String} pluginpath relative path to root component
 * @param  {Object} infobutton JSON created from metadata file.
 * @param  {String[]} infobutton.external_scripts List paths to be included. This not support folders, only files.
 */
Backend.prototype.loadComponentExtraScripts = function loadComponentExtraScripts(pluginpath,infobutton) {
  if (infobutton.hasOwnProperty('external_scripts')) {
      var fs = require('fs');
      var path = require('path');
      infobutton['external_scripts'].forEach(function(scriptpath){
        var script = fs.readFileSync("./"+ path.join(pluginpath,scriptpath),'utf8');
        eval(script);
      });
  } 
};


/**
 * On component metadata file may be field "external_css" . This field include style files that may be included to decorate component 
 * function or elements created by component . This method is reponsible read metadata info to be include all files indicate on "external_scripts". This files are loaded 
 * @param  {String} pluginpath relative path to root component
 * @param  {Object} infobutton JSON created from metadata file.
 * @param  {String[]} infobutton.external_css List paths to be included. This not support folders, only files.
 */
Backend.prototype.loadComponentExtraCss = function loadComponentExtraCss(pluginpath, infobutton){
  if (infobutton.hasOwnProperty('external_css')) {
      var head = document.getElementsByTagName('head')[0];
      infobutton['external_css'].forEach(function(csspath){
        var css = document.createElement('link');
        css.rel = 'stylesheet';
        css.href = pluginpath + "/" + csspath;
        head.appendChild(css);
      }); 
  }
}


/**
 * Initalize sections. This void Sections namespace and render initial section
 * @return {String} Identifier initial section created by default  
 */
Backend.prototype.initSections = function initSections() {
  var that = this,
      CBStorage = application.storagemanager.getInstance(),
      auxcbsection = new Cloudbook.Sections['basic']();
  return this.appendNewSectionObjectByUID(CBStorage.setRoot(auxcbsection),'basic');
};


/**
 * Create new section and append into section indicate. This section is identifier string instead of CBSection object. 
 * @param  {String} cbparentuid       Section identifier where new section will be added
 * @param  {String} typesection Type of section to create.
 * @return {String}             Identifier that section created
 */
Backend.prototype.appendNewSectionObjectByUID = function appendNewSectionObjectByUID(cbparentuid,typesection) {
  var cbsonuid = CBUtil.uniqueId();
  var auxcbsection = new Cloudbook.Sections[typesection]();
  var CBStorage = application.storagemanager.getInstance();
  CBStorage.setSectionById(auxcbsection,cbsonuid);
  var parentsection = CBStorage.getSectionById(cbparentuid);
  parentsection.sections.push(cbsonuid);
  CBStorage.setSectionById(parentsection,cbparentuid);
  return cbsonuid;
};

/**
 * Load project from path. This method void project and discard not saved changes. 
 * @param  {String} projectPath 
 */
Backend.prototype.loadProject = function(projectPath) {
  var fs = require('fs');
  var path = require('path');
  if (fs.existsSync(projectPath)){
    var contentproject = fs.readFileSync(projectPath);
    var CBStorage = application.storagemanager.getInstance();
    var projectdata = JSON.parse(contentproject);

    Project.Info.projectpath = path.dirname(projectPath);
    Project.Info.projectname = projectdata.name;

    this.voidProject();
    Project.Info.projectname = projectPath;

    projectdata.data.sections.forEach(function(section){
      var cbsectionid = section[0];
      var rawsection = section[1];
      var tempsection = new Cloudbook.Sections[rawsection.idtype](rawsection);

      rawsection.content.forEach(function(element){
        var x = new Cloudbook.Actions[element['idtype']]['component'](element);
        tempsection.content.push(x);
      });
      CBStorage.setSectionById(tempsection,cbsectionid);
    });
  }
  
};

/**
 * Save project into path indicate. This function don't save binary files. This files are stored 
 * into folder created for this purpose on workspace folder.  
 * @param  {String} projectPath Path where project will be stored
 */
Backend.prototype.saveProject = function(projectfolder) {
  var fs = require('fs');
  var objectProject = {};
  var CBStorage = application.storagemanager.getInstance();
  var projectpath = projectfolder + "/project.cloudbook";
  objectProject['name'] = Project.Info.projectname;
  objectProject['author'] = "Usuario 1 <micorreo@midominio.com>";
  objectProject['data'] = {};
  objectProject['data']['sections'] = [];
  function walk(uid){
    var section = CBStorage.getSectionById(uid);
    return {obj: section, list: section.sections};
  }
  var root = CBStorage.getRoot();
  objectProject['data']['sections'].push(['root',root]);
  var pool = [];
  pool = pool.concat(root.sections);
  var identifier = null;
  var result = null;
  while(identifier = pool.pop()){
    result = walk(identifier);
    objectProject['data']['sections'].push([identifier,result.obj]);
    pool = pool.concat(result.list);
  }
  var result_string = JSON.stringify(objectProject,null," ");
  fs.writeFile(projectpath,result_string);
};




/**
 * Empty project sections
 */
Backend.prototype.voidProject = function() {
  this.initSections();
};

/**
 * Empty targetcontent and render objects from section id indicate into targetcontent.
 * @param  {String} id Section id.
 */
Backend.prototype.loadContent = function loadContent(id){
  var CBStorage = application.storagemanager.getInstance();
  $(Cloudbook.UI.targetcontent).html("");
  var section = CBStorage.getSectionById(id);
  if (section !== undefined ){
    section.content.forEach(function (element){
      var x = element.editorView();
      $(Cloudbook.UI.targetcontent).append(x);
      element.add_callback(x,element);
    });
  }
}

/**
 * Replace subsections of a section by subsections indicate into argument. 
 * @param  {String} sectionid      Id of section
 * @param  {String[]} subsectionsids Id's subsections
 */
Backend.prototype.regenerateSubsection = function regenerateSubsection(sectionid,subsectionsids) {
  var CBStorage = application.storagemanager.getInstance();
  var section = CBStorage.getSectionById(sectionid);
  section.sections = subsectionsids;
  CBStorage.setSectionById(section,sectionid);
};

/**
 * Create new project with its folder. Also add information into userconfig.
 * @param  {String} projectname Project name
 */
Backend.prototype.createProject = function createProject(projectname) {
  var fs = require('fs'),
      userconfig = application.config.user.getInstance();
  /**
   * Path project save all files
   * @type {String}
   */
  Project.Info.projectpath = Cloudbook.workspace + projectname;
  /**
   * Project name
   * @type {String}
   */
  Project.Info.projectname = projectname;
  fs.mkdirSync(Project.Info.projectpath,0775);
  fs.mkdirSync(Project.Info.projectpath+"/rsrc",0775);
  userconfig.newProject(projectname);
};

/**
 * Check if project exists
 * @param  {String} projectname Project name
 * @return {Boolean}             True exists project, False not exists
 */
Backend.prototype.checkProjectExists = function checkProjectExists(projectname) {
  var fs = require('fs');
  if(fs.existsSync(Cloudbook.workspace + projectname)){
    return true;
  }
  return false;
};

/**
 * Update section name. 
 * @param  {String} name        New section name
 * @param  {String} cbsectionid Section identifier to update name.
 */
Backend.prototype.updateSectionName = function(name,cbsectionid) {
  var CBStorage = application.storagemanager.getInstance();
  x = CBStorage.getSectionById(cbsectionid);
  x.name = name;
  CBStorage.setSectionById(x,cbsectionid);
};

Backend.prototype.deleteSection = function(cbsectionid) {
  /**
   * @todo This method must delete binary files related with section and subsections
   */
  throw "Method not implemented";
};

/**
 * This namespace has singleton instance of Backend class
 * @namespace backend
 * @memberOf application
 */
CBUtil.createNameSpace('application.backend');
application.backend = CBUtil.singleton(Backend);