<commit_before>'use strict';

var _ = require('lodash');
var fs = require('fs-extra');
var shortid = require('shortid');
var archiver = require('archiver');
var jsonfile = require('jsonfile');
var Promise = require('bluebird');
var path = require('path');
var express = require('express');
var bodyParser = require('body-parser');
var webpack = require('webpack');
var config = require('./webpack.config.dev');
var readline = require('readline');

var packages = require ('./modules/packages');

var readFile = Promise.promisify(fs.readFile);
var writeFile = Promise.promisify(fs.writeFile);
var remove = Promise.promisify(fs.remove);
var mkdirs = Promise.promisify(fs.mkdirs);
var copy = Promise.promisify(fs.copy);
var readJson = Promise.promisify(fs.readJson);
var writeJson = Promise.promisify(fs.writeJson);

var app = express();
var compiler = webpack(config);

app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(express.static(path.join(__dirname, 'assets')));
app.use(express.static(path.join(__dirname, 'modules')));

app.use(require('webpack-dev-middleware')(compiler, {
  noInfo: true,
  publicPath: config.output.publicPath
}));
app.use(require('webpack-hot-middleware')(compiler));

app.post('/download', (req, res) => {
  let params = req.body;

  prepare(params)
    .then(generateFramework)
    .then(generateTemplateEngine)
    .then(generateCssFramework)
    .then(generateCssPreprocessor)
    .then(generateDatabase);

});

app.get('*', function(req, res) {
  res.sendFile(path.join(__dirname, 'index.html'));
});

app.listen(3000, 'localhost', function(err) {
  if (err) {
    console.log(err);
    return;
  }

  console.log('Listening at http://localhost:3000');
});

function generateFramework(params) {
  return new Promise((resolve, reject) => {
    let dest = path.join(__dirname, 'build', params.uid);

    switch (params.framework) {
      case 'express':
        let expressModule = path.join(__dirname, 'modules', 'express');

        return copy(expressModule, dest).then(() => {
          let images = path.join(dest, 'public', 'images');
          let javascripts = path.join(dest, 'public', 'javascripts');
          let stylesheets = path.join(dest, 'public', 'stylesheets');

          let updatePackageJson = function() {
            let packageJson = path.join(dest, 'package.json');
            return readJson(packageJson).then((packageObj) => {
              packageObj.name = params.appName;
              return writeJson(packageJson, packageObj, { spaces: 2 });
            });
          };

          return Promise.all([
            updatePackageJson,
            mkdirs(images),
            mkdirs(javascripts),
            mkdirs(stylesheets)
          ]).then(() => {
            resolve(params);
          });
        });
        break;
      case 'hapi':
        // TODO
        return Promise.reject();
        break;
      case 'sails':
        // TODO
        return Promise.reject();
        break;
      default:
        return Promise.reject('Unsupported Framework');
        break;
    }
  });
}

function generateTemplateEngine(params) {
  return new Promise((resolve, reject) => {
    switch (params.templateEngine) {
      case 'jade':
        return generateJadeTemplateEngine(params).then(() => {
          resolve(params);
        });
        break;
      case 'handlebars':
        // TODO: Not implemented
        return Promise.reject();
        break;
      case 'swig':
        // TODO: Not implemented
        return Promise.reject();
        break;
      case 'none':
        return cleanupTemplateEngineString(params);
        break;
      default:
        return Promise.reject('Unsupported Template Engine');
    }
  });
}

function cleanupTemplateEngineString(params) {
  if (params.framework === 'express') {
    let appFile = path.join(__dirname, 'build', params.uid, 'app.js');

    return readFile(appFile).then((appFileData) => {
      appFileData = removeCode(appFileData, 'EXPRESS_TEMPLATE_ENGINE_CONFIG');
      return writeFile(appFile, appFileData);
    });
  } else if (params.framework === 'hapi') {
    // TODO: not implemented
  } else if (params.framework === 'sails') {
    // TODO: not implemented
  }
}

function cleanupCssFrameworkString(params) {
  // TODO: switch per framework
  if (params.templateEngine === 'jade') {
    let layoutFile = path.join(__dirname, 'build', params.uid, 'views', 'layout.jade');

    return readFile(layoutFile).then((layoutData) => {
      layoutData = removeCode(layoutData, 'CSS_FRAMEWORK_IMPORT');
      return writeFile(layoutFile, layoutData);
    });
  } else if (params.templateEngine === 'handlebars') {
    // TODO: not implemented
  } else if (params.templateEngine === 'swig') {
    // TODO: not implemented
  } else {
    return Promise.resolve();
  }
}

function generateJadeTemplateEngine(params) {
  switch (params.framework) {
    case 'express':
      let jadeExpressFile = path.join(__dirname, 'modules', 'template-engine', 'jade', 'jade-express.js');
      let appFile = path.join(__dirname, 'build', params.uid, 'app.js');

      return replaceCode(appFile, 'EXPRESS_TEMPLATE_ENGINE_CONFIG', jadeExpressFile, { leadingBlankLine: true })
        .then(() => {
          let src = path.join(__dirname, 'modules', 'template-engine', 'jade', 'views');
          let dest = path.join(__dirname, 'build', params.uid, 'views');
          return copy(src, dest);
        });
      break;
    case 'hapi':
      // TODO: not implemented
      break;
    case 'sails':
      // TODO: not implemented
      break;
    default:
      return Promise.reject('Unsupported Framework');
  }
}

function generateBootstrapCss(params) {
  let bootstrapDir = path.join(__dirname, 'modules', 'css-framework', 'bootstrap');
  let jqueryDir = path.join(__dirname, 'modules', 'js-framework', 'jquery');
  let publicDir = path.join(__dirname, 'build', params.uid, 'public');

  return Promise.all([
    addCssImports(params),
    copy(path.join(bootstrapDir, 'main.css'), path.join(publicDir, 'stylesheets', 'main.css')),
    copy(path.join(bootstrapDir, 'fonts'), path.join(publicDir, 'fonts')),
    copy(path.join(bootstrapDir, 'css', 'bootstrap.css'), path.join(publicDir, 'stylesheets', 'vendor', 'bootstrap.css')),
    copy(path.join(bootstrapDir, 'css', 'bootstrap.min.css'), path.join(publicDir, 'stylesheets', 'vendor', 'bootstrap.min.css')),
    copy(path.join(bootstrapDir, 'js', 'bootstrap.js'), path.join(publicDir, 'javascripts', 'vendor', 'bootstrap.js')),
    copy(path.join(bootstrapDir, 'js', 'bootstrap.min.js'), path.join(publicDir, 'javascripts', 'vendor', 'bootstrap.min.js')),
    copy(path.join(jqueryDir, 'jquery.js'), path.join(publicDir, 'javascripts', 'vendor', 'jquery.js')),
    copy(path.join(jqueryDir, 'jquery.min.js'), path.join(publicDir, 'javascripts', 'vendor', 'jquery.min.js')),
    copy(path.join(jqueryDir, 'main.js'), path.join(publicDir, 'javascripts', 'main.js'))
  ]);
}

function generateCssFramework(params) {
  return new Promise((resolve, reject) => {
    // TODO: Sail.js assets dir instead of public
    switch (params.cssFramework) {
      case 'bootstrapCss':
        return generateBootstrapCss(params).then(() => {
          resolve(params);
        });
        break;
      case 'bootstrapLess':
        // TODO: not implemented
        reject();
        break;
      case 'bootstrapSass':
        // TODO: not implemented
        reject();
        break;
      case 'foundationCss':
        // TODO: not implemented
        reject();
        break;
      case 'foundationSass':
        // TODO: not implemented
        reject();
        break;
      case 'bourbonNeat':
        // TODO: not implemented
        reject();
        break;
      case 'none':
        return cleanupCssFrameworkString(params).then(() => {
          resolve(params);
        });
        break;
      default:
        reject('Unsupported CSS Framework');
    }
  });
}

function generatePlainCssPreprocessor(params) {
  let src = path.join(__dirname, 'modules', 'css-preprocessor', 'main.css');
  let dest = path.join(__dirname, 'build', params.uid, 'public', 'stylesheets', 'main.css');
  return copy(src, dest);
}

function generateCssPreprocessor(params) {
  return new Promise((resolve, reject) => {
    switch (params.cssPreprocessor) {
      case 'css':
        return generatePlainCssPreprocessor(params).then(() => {
          resolve(params);
        });
        break;
      case 'sass':
        // TODO: not implemented
        resolve(params);
        break;
      case 'less':
        // TODO: not implemented
        resolve(params);
        break;
      case 'postcss':
        // TODO: not implemented
        resolve(params);
        break;
      default:
        resolve(params);
    }
  });
}

function addCssImports(params) {
  if (params.templateEngine === 'jade') {
    let layoutFile = path.join(__dirname, 'build', params.uid, 'views', 'layout.jade');
    let cssImportFile;

    switch (params.cssFramework) {
      case 'bootstrapCss':
        cssImportFile = path.join(__dirname, 'modules', 'css-framework', 'bootstrap', 'jade-import.jade');
        break;
      case 'bootstrapLess':
        // TODO: not implemented
        break;
      case 'bootstrapSass':
        // TODO: not implemented
        break;
      case 'foundationCss':
        // TODO: not implemented
        break;
      case 'foundationSass':
        // TODO: not implemented
        break;
      case 'bourbonNeat':
        // TODO: not implemented
        break;
      case 'none':
        // TODO: not implemented
        break;
      default:
        break;
    }
    return replaceCode(layoutFile, 'CSS_FRAMEWORK_IMPORT', cssImportFile, { indentLevel: 2 });
  } else if (params.templateEngine === 'handlebars') {
    // TODO
  } else if (params.templateEngine === 'swig') {
    // TODO
  } else {
    Promise.resolve();
  }
}

function generateMongodbDatabase(params) {

  let updatePackageJson = function() {
    let packageJson = path.join(__dirname, 'build', params.uid, 'package.json');
    return readJson(packageJson).then((packageObj) => {
      _.forEach(packages.mongodb, (value, key) => {
        packageObj.dependencies[key] = value;
      });
      return writeJson(packageJson, packageObj, { spaces: 2 });
    });
  };

  return updatePackageJson();

  //switch (params.framework) {
  //  case 'express':
  //    let jadeExpressFile = path.join(__dirname, 'modules', 'template-engine', 'jade', 'jade-express.js');
  //    let appFile = path.join(__dirname, 'build', params.uid, 'app.js');
  //
  //
  //
  //    return replaceCode(appFile, 'EXPRESS_TEMPLATE_ENGINE_CONFIG', jadeExpressFile, { leadingBlankLine: true })
  //      .then(() => {
  //        let src = path.join(__dirname, 'modules', 'template-engine', 'jade', 'views');
  //        let dest = path.join(__dirname, 'build', params.uid, 'views');
  //        return copy(src, dest);
  //      });
  //    break;
  //  case 'hapi':
  //    // TODO: not implemented
  //    break;
  //  case 'sails':
  //    // TODO: not implemented
  //    break;
  //  default:
  //    return Promise.reject('Unsupported Framework');
  //}

}

function generateDatabase(params) {
  return new Promise((resolve, reject) => {
    switch (params.database) {
      case 'mongodb':
        return generateMongodbDatabase(params).then(() => {
          resolve(params);
        });
        break;
      case 'mysql':
        // TODO: not implemented
        reject();
        break;
      case 'postgresql':
        // TODO: not implemented
        reject();
        break;
      case 'rethinkdb':
        // TODO: not implemented
        reject();
        break;
      case 'none':
        resolve(params);
        break;
      default:
        reject('Unsupported Database');
    }
  });
}

/**
 *
 * @param srcFile {buffer} - where to replace
 * @param subStr {string} - what to replace
 * @param newSrcFile {string} - replace it with this
 * @param opts {object} - options
 * @returns {string}
 */
function replaceCode(srcFile, subStr, newSrcFile, opts) {
  return readFile(srcFile).then((srcData) => {
    return readFile(newSrcFile).then((newSrcData) => {
      opts = opts || {};
      let array = srcData.toString().split('\n');

      array.forEach((line, index) => {
        if (line.includes(subStr)) {
          if (opts.indentLevel) {
            newSrcData = indentCode(newSrcData, opts.indentLevel);
          }

          newSrcData = newSrcData.toString().split('\n').filter(Boolean).join('\n');

          if (opts.leadingBlankLine) {
            newSrcData = '\n' + newSrcData;
          }

          array[index] = newSrcData;
        }
      });

      srcData = array.join('\n');

      return writeFile(srcFile, srcData);
    });
  });
}

/**
 *
 * @param subStr {string} - what to indent
 * @param indentLevel {number} - how many levels to indent
 * @returns {string}
 */
function indentCode(subStr, indentLevel) {
  let defaultIndentation = 2;
  let indent = ' '.repeat(indentLevel * defaultIndentation);
  let array = subStr.toString().split('\n').filter(Boolean);
  array.forEach((line, index) => {
    array[index] = indent + line;
  });
  return array.join('\n');
}

/**
 *
 * @param src {buffer} - where to remove
 * @param subStr {string} - what to remove
 * @returns {string}
 */
function removeCode(src, subStr) {
  let array = src.toString().split('\n');
  array.forEach((line, index) => {
    if (line.includes(subStr)) {
      array.splice(index, 1);
    }
  });
  return array.join('\n');
}

function generateZip(req, res) {
  return new Promise(function(resolve, reject) {
    var archive = archiver('zip');

    archive.on('error', function(err) {
      res.status(500).send(err.message);
    });

    res.on('close', function() {
      console.log('closing...')
      console.log('Archive wrote %d bytes', archive.pointer());
      return res.status(200).send('OK').end();
    });

    res.attachment('megaboilerplate-express.zip');

    archive.pipe(res);

    var files = [
      __dirname + '/modules/express/app.js',
      __dirname + '/modules/express/package.json'
    ];

    for (var i in files) {
      archive.append(fs.createReadStream(files[i]), { name: path.basename(files[i]) });
    }

    archive.finalize();
  });
}

function cleanup(params) {
  return new Promise((resolve, reject) => {
    let location = path.join(__dirname, 'build', params.uid);
    return remove(location);
  });
}

function prepare(params) {
  return new Promise((resolve, reject) => {
    params.uid = shortid.generate();
    let location = path.join(__dirname, 'build', params.uid);
    return mkdirs(location).then(() => {
      resolve(params);
    });
  });
}
<commit_msg>Created a new function addPackageDependencies<commit_after>'use strict';

var _ = require('lodash');
var fs = require('fs-extra');
var shortid = require('shortid');
var archiver = require('archiver');
var jsonfile = require('jsonfile');
var Promise = require('bluebird');
var path = require('path');
var express = require('express');
var bodyParser = require('body-parser');
var webpack = require('webpack');
var config = require('./webpack.config.dev');
var readline = require('readline');

var packages = require ('./modules/packages');

var readFile = Promise.promisify(fs.readFile);
var writeFile = Promise.promisify(fs.writeFile);
var remove = Promise.promisify(fs.remove);
var mkdirs = Promise.promisify(fs.mkdirs);
var copy = Promise.promisify(fs.copy);
var readJson = Promise.promisify(fs.readJson);
var writeJson = Promise.promisify(fs.writeJson);

var app = express();
var compiler = webpack(config);

app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(express.static(path.join(__dirname, 'assets')));
app.use(express.static(path.join(__dirname, 'modules')));

app.use(require('webpack-dev-middleware')(compiler, {
  noInfo: true,
  publicPath: config.output.publicPath
}));
app.use(require('webpack-hot-middleware')(compiler));

app.post('/download', (req, res) => {
  let params = req.body;

  prepare(params)
    .then(generateFramework)
    .then(generateTemplateEngine)
    .then(generateCssFramework)
    .then(generateCssPreprocessor)
    .then(generateDatabase);

});

app.get('*', function(req, res) {
  res.sendFile(path.join(__dirname, 'index.html'));
});

app.listen(3000, 'localhost', function(err) {
  if (err) {
    console.log(err);
    return;
  }

  console.log('Listening at http://localhost:3000');
});

function generateFramework(params) {
  return new Promise((resolve, reject) => {
    let dest = path.join(__dirname, 'build', params.uid);

    switch (params.framework) {
      case 'express':
        let expressModule = path.join(__dirname, 'modules', 'express');

        return copy(expressModule, dest).then(() => {
          let images = path.join(dest, 'public', 'images');
          let javascripts = path.join(dest, 'public', 'javascripts');
          let stylesheets = path.join(dest, 'public', 'stylesheets');

          let updatePackageJson = function() {
            let packageJson = path.join(dest, 'package.json');
            return readJson(packageJson).then((packageObj) => {
              packageObj.name = params.appName;
              return writeJson(packageJson, packageObj, { spaces: 2 });
            });
          };

          return Promise.all([
            updatePackageJson,
            mkdirs(images),
            mkdirs(javascripts),
            mkdirs(stylesheets)
          ]).then(() => {
            resolve(params);
          });
        });
        break;
      case 'hapi':
        // TODO
        return Promise.reject();
        break;
      case 'sails':
        // TODO
        return Promise.reject();
        break;
      default:
        return Promise.reject('Unsupported Framework');
        break;
    }
  });
}

function generateTemplateEngine(params) {
  return new Promise((resolve, reject) => {
    switch (params.templateEngine) {
      case 'jade':
        return generateJadeTemplateEngine(params).then(() => {
          resolve(params);
        });
        break;
      case 'handlebars':
        // TODO: Not implemented
        return Promise.reject();
        break;
      case 'swig':
        // TODO: Not implemented
        return Promise.reject();
        break;
      case 'none':
        return cleanupTemplateEngineString(params);
        break;
      default:
        return Promise.reject('Unsupported Template Engine');
    }
  });
}

function cleanupTemplateEngineString(params) {
  if (params.framework === 'express') {
    let appFile = path.join(__dirname, 'build', params.uid, 'app.js');

    return readFile(appFile).then((appFileData) => {
      appFileData = removeCode(appFileData, 'EXPRESS_TEMPLATE_ENGINE_CONFIG');
      return writeFile(appFile, appFileData);
    });
  } else if (params.framework === 'hapi') {
    // TODO: not implemented
  } else if (params.framework === 'sails') {
    // TODO: not implemented
  }
}

function cleanupCssFrameworkString(params) {
  // TODO: switch per framework
  if (params.templateEngine === 'jade') {
    let layoutFile = path.join(__dirname, 'build', params.uid, 'views', 'layout.jade');

    return readFile(layoutFile).then((layoutData) => {
      layoutData = removeCode(layoutData, 'CSS_FRAMEWORK_IMPORT');
      return writeFile(layoutFile, layoutData);
    });
  } else if (params.templateEngine === 'handlebars') {
    // TODO: not implemented
  } else if (params.templateEngine === 'swig') {
    // TODO: not implemented
  } else {
    return Promise.resolve();
  }
}

function generateJadeTemplateEngine(params) {
  switch (params.framework) {
    case 'express':
      let jadeExpressFile = path.join(__dirname, 'modules', 'template-engine', 'jade', 'jade-express.js');
      let appFile = path.join(__dirname, 'build', params.uid, 'app.js');

      return replaceCode(appFile, 'EXPRESS_TEMPLATE_ENGINE_CONFIG', jadeExpressFile, { leadingBlankLine: true })
        .then(() => {
          let src = path.join(__dirname, 'modules', 'template-engine', 'jade', 'views');
          let dest = path.join(__dirname, 'build', params.uid, 'views');
          return copy(src, dest);
        });
      break;
    case 'hapi':
      // TODO: not implemented
      break;
    case 'sails':
      // TODO: not implemented
      break;
    default:
      return Promise.reject('Unsupported Framework');
  }
}

function generateBootstrapCss(params) {
  let bootstrapDir = path.join(__dirname, 'modules', 'css-framework', 'bootstrap');
  let jqueryDir = path.join(__dirname, 'modules', 'js-framework', 'jquery');
  let publicDir = path.join(__dirname, 'build', params.uid, 'public');

  return Promise.all([
    addCssImports(params),
    copy(path.join(bootstrapDir, 'main.css'), path.join(publicDir, 'stylesheets', 'main.css')),
    copy(path.join(bootstrapDir, 'fonts'), path.join(publicDir, 'fonts')),
    copy(path.join(bootstrapDir, 'css', 'bootstrap.css'), path.join(publicDir, 'stylesheets', 'vendor', 'bootstrap.css')),
    copy(path.join(bootstrapDir, 'css', 'bootstrap.min.css'), path.join(publicDir, 'stylesheets', 'vendor', 'bootstrap.min.css')),
    copy(path.join(bootstrapDir, 'js', 'bootstrap.js'), path.join(publicDir, 'javascripts', 'vendor', 'bootstrap.js')),
    copy(path.join(bootstrapDir, 'js', 'bootstrap.min.js'), path.join(publicDir, 'javascripts', 'vendor', 'bootstrap.min.js')),
    copy(path.join(jqueryDir, 'jquery.js'), path.join(publicDir, 'javascripts', 'vendor', 'jquery.js')),
    copy(path.join(jqueryDir, 'jquery.min.js'), path.join(publicDir, 'javascripts', 'vendor', 'jquery.min.js')),
    copy(path.join(jqueryDir, 'main.js'), path.join(publicDir, 'javascripts', 'main.js'))
  ]);
}

function generateCssFramework(params) {
  return new Promise((resolve, reject) => {
    // TODO: Sail.js assets dir instead of public
    switch (params.cssFramework) {
      case 'bootstrapCss':
        return generateBootstrapCss(params).then(() => {
          resolve(params);
        });
        break;
      case 'bootstrapLess':
        // TODO: not implemented
        reject();
        break;
      case 'bootstrapSass':
        // TODO: not implemented
        reject();
        break;
      case 'foundationCss':
        // TODO: not implemented
        reject();
        break;
      case 'foundationSass':
        // TODO: not implemented
        reject();
        break;
      case 'bourbonNeat':
        // TODO: not implemented
        reject();
        break;
      case 'none':
        return cleanupCssFrameworkString(params).then(() => {
          resolve(params);
        });
        break;
      default:
        reject('Unsupported CSS Framework');
    }
  });
}

function generatePlainCssPreprocessor(params) {
  let src = path.join(__dirname, 'modules', 'css-preprocessor', 'main.css');
  let dest = path.join(__dirname, 'build', params.uid, 'public', 'stylesheets', 'main.css');
  return copy(src, dest);
}

function generateCssPreprocessor(params) {
  return new Promise((resolve, reject) => {
    switch (params.cssPreprocessor) {
      case 'css':
        return generatePlainCssPreprocessor(params).then(() => {
          resolve(params);
        });
        break;
      case 'sass':
        // TODO: not implemented
        resolve(params);
        break;
      case 'less':
        // TODO: not implemented
        resolve(params);
        break;
      case 'postcss':
        // TODO: not implemented
        resolve(params);
        break;
      default:
        resolve(params);
    }
  });
}

function addCssImports(params) {
  if (params.templateEngine === 'jade') {
    let layoutFile = path.join(__dirname, 'build', params.uid, 'views', 'layout.jade');
    let cssImportFile;

    switch (params.cssFramework) {
      case 'bootstrapCss':
        cssImportFile = path.join(__dirname, 'modules', 'css-framework', 'bootstrap', 'jade-import.jade');
        break;
      case 'bootstrapLess':
        // TODO: not implemented
        break;
      case 'bootstrapSass':
        // TODO: not implemented
        break;
      case 'foundationCss':
        // TODO: not implemented
        break;
      case 'foundationSass':
        // TODO: not implemented
        break;
      case 'bourbonNeat':
        // TODO: not implemented
        break;
      case 'none':
        // TODO: not implemented
        break;
      default:
        break;
    }
    return replaceCode(layoutFile, 'CSS_FRAMEWORK_IMPORT', cssImportFile, { indentLevel: 2 });
  } else if (params.templateEngine === 'handlebars') {
    // TODO
  } else if (params.templateEngine === 'swig') {
    // TODO
  } else {
    Promise.resolve();
  }
}

function addPackageDependencies(dependencies, params, isDev) {
  let packageJson = path.join(__dirname, 'build', params.uid, 'package.json');

  return readJson(packageJson).then((packageObj) => {

    for (var key in dependencies) {
      if (dependencies.hasOwnProperty(key)) {
        if (isDev) {
          packageObj.devDependencies = packageObj.devDependencies || {};
          packageObj.devDependencies[key] = dependencies[key];
        } else {
          packageObj.dependencies[key] = dependencies[key];
        }
      }
    }

    return writeJson(packageJson, packageObj, { spaces: 2 });
  });
}

function generateMongodbDatabase(params) {

  addPackageDependencies(packages.mongodb, params);

  //switch (params.framework) {
  //  case 'express':
  //    let jadeExpressFile = path.join(__dirname, 'modules', 'template-engine', 'jade', 'jade-express.js');
  //    let appFile = path.join(__dirname, 'build', params.uid, 'app.js');
  //
  //
  //
  //    return replaceCode(appFile, 'EXPRESS_TEMPLATE_ENGINE_CONFIG', jadeExpressFile, { leadingBlankLine: true })
  //      .then(() => {
  //        let src = path.join(__dirname, 'modules', 'template-engine', 'jade', 'views');
  //        let dest = path.join(__dirname, 'build', params.uid, 'views');
  //        return copy(src, dest);
  //      });
  //    break;
  //  case 'hapi':
  //    // TODO: not implemented
  //    break;
  //  case 'sails':
  //    // TODO: not implemented
  //    break;
  //  default:
  //    return Promise.reject('Unsupported Framework');
  //}

}

function generateDatabase(params) {
  return new Promise((resolve, reject) => {
    switch (params.database) {
      case 'mongodb':
        return generateMongodbDatabase(params).then(() => {
          resolve(params);
        });
        break;
      case 'mysql':
        // TODO: not implemented
        reject();
        break;
      case 'postgresql':
        // TODO: not implemented
        reject();
        break;
      case 'rethinkdb':
        // TODO: not implemented
        reject();
        break;
      case 'none':
        resolve(params);
        break;
      default:
        reject('Unsupported Database');
    }
  });
}

/**
 *
 * @param srcFile {buffer} - where to replace
 * @param subStr {string} - what to replace
 * @param newSrcFile {string} - replace it with this
 * @param opts {object} - options
 * @returns {string}
 */
function replaceCode(srcFile, subStr, newSrcFile, opts) {
  return readFile(srcFile).then((srcData) => {
    return readFile(newSrcFile).then((newSrcData) => {
      opts = opts || {};
      let array = srcData.toString().split('\n');

      array.forEach((line, index) => {
        if (line.includes(subStr)) {
          if (opts.indentLevel) {
            newSrcData = indentCode(newSrcData, opts.indentLevel);
          }

          newSrcData = newSrcData.toString().split('\n').filter(Boolean).join('\n');

          if (opts.leadingBlankLine) {
            newSrcData = '\n' + newSrcData;
          }

          array[index] = newSrcData;
        }
      });

      srcData = array.join('\n');

      return writeFile(srcFile, srcData);
    });
  });
}

/**
 *
 * @param subStr {string} - what to indent
 * @param indentLevel {number} - how many levels to indent
 * @returns {string}
 */
function indentCode(subStr, indentLevel) {
  let defaultIndentation = 2;
  let indent = ' '.repeat(indentLevel * defaultIndentation);
  let array = subStr.toString().split('\n').filter(Boolean);
  array.forEach((line, index) => {
    array[index] = indent + line;
  });
  return array.join('\n');
}

/**
 *
 * @param src {buffer} - where to remove
 * @param subStr {string} - what to remove
 * @returns {string}
 */
function removeCode(src, subStr) {
  let array = src.toString().split('\n');
  array.forEach((line, index) => {
    if (line.includes(subStr)) {
      array.splice(index, 1);
    }
  });
  return array.join('\n');
}

function generateZip(req, res) {
  return new Promise(function(resolve, reject) {
    var archive = archiver('zip');

    archive.on('error', function(err) {
      res.status(500).send(err.message);
    });

    res.on('close', function() {
      console.log('closing...')
      console.log('Archive wrote %d bytes', archive.pointer());
      return res.status(200).send('OK').end();
    });

    res.attachment('megaboilerplate-express.zip');

    archive.pipe(res);

    var files = [
      __dirname + '/modules/express/app.js',
      __dirname + '/modules/express/package.json'
    ];

    for (var i in files) {
      archive.append(fs.createReadStream(files[i]), { name: path.basename(files[i]) });
    }

    archive.finalize();
  });
}

function cleanup(params) {
  return new Promise((resolve, reject) => {
    let location = path.join(__dirname, 'build', params.uid);
    return remove(location);
  });
}

function prepare(params) {
  return new Promise((resolve, reject) => {
    params.uid = shortid.generate();
    let location = path.join(__dirname, 'build', params.uid);
    return mkdirs(location).then(() => {
      resolve(params);
    });
  });
}
<|endoftext|>


<commit_before>import React, { PropTypes } from 'react';

import './ComponentName.scss';

const ComponentName = ({ children }) => (
  {`<${children}>`}
);

ComponentName.propTypes = {
  children: PropTypes.string.isRequired,
};

export default ComponentName;
<commit_msg>Make ComponentName components link to the API section about it<commit_after>import React, { PropTypes } from 'react';

import './ComponentName.scss';

const ComponentName = ({ children }) => (
  <a
    href={`https://github.com/joshwcomeau/react-collection-helpers#${children.toLowerCase()}`}
    className="ComponentName"
  >
    {`<${children}>`}

);

ComponentName.propTypes = {
  children: PropTypes.string.isRequired,
};

export default ComponentName;
<|endoftext|>


<commit_before>'use strict';

import React, {
  Component,
  TouchableOpacity,
  StyleSheet,
  Text,
  View,
  Alert
} from 'react-native';

// import css variables
import design from '../design';

import * as ideaActions from '../actions/ideaActions';
import {bindActionCreators} from 'redux';
import { connect } from 'react-redux';

import ActivityView from 'react-native-activity-view';

class Settings extends Component {
  constructor(props) {
    super(props);
  }

  _handleShare() {
    const { state } = this.props;

    var ideasString = state.ideas.map((idea, index) => {
      return state.ideas.length - parseInt(index) + '-' + idea.title;
    }).reverse().join('\n');

    ideasString = 'My idea list:\n\n' + ideasString;

    console.log('ideasString', ideasString);
    ActivityView.show({
      text: ideasString
    });
  }
  _handleResetIdeas() {
    const { actions } = this.props;

    Alert.alert(
      'Confirm suppression',
      'Are you sure you want to delete all of your ideas?',
      [{
        text: 'OK', onPress: () => {
          actions.reset();
          this.props.navigator.pop();
        }
      },
      {
        text: 'Cancel'
      }]
    );
  }

  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.inputLabel}>
          Send or share your idea list:
        </Text>
        <TouchableOpacity onPress={this._handleShare.bind(this)} style={design.designComp.button}>
          <Text style={design.designComp.buttonText}>Email/Share</Text>
        </TouchableOpacity>
        <Text style={styles.inputLabel}>
          Delete your idea list:
        </Text>
        <TouchableOpacity onPress={this._handleResetIdeas.bind(this)} style={design.designComp.button}>
          <Text style={design.designComp.buttonText}>Reset ideas</Text>
        </TouchableOpacity>
      </View>
    );
  }
}

export default connect(
  state => ({
    state: state.ideas
  }),
  (dispatch) => ({
    actions: bindActionCreators(ideaActions, dispatch)
  })
)(Settings);

const styles = StyleSheet.create({
  container: {
    flex: 1
  },
  inputLabel: {
    paddingLeft: 17,
    paddingTop: 24,
    fontSize: 20
  }
});
<commit_msg>Fix typo<commit_after>'use strict';

import React, {
  Component,
  TouchableOpacity,
  StyleSheet,
  Text,
  View,
  Alert
} from 'react-native';

// import css variables
import design from '../design';

import * as ideaActions from '../actions/ideaActions';
import {bindActionCreators} from 'redux';
import { connect } from 'react-redux';

import ActivityView from 'react-native-activity-view';

class Settings extends Component {
  constructor(props) {
    super(props);
  }

  _handleShare() {
    const { state } = this.props;

    let ideasString = state.ideas.map((idea, index) => {
      return state.ideas.length - parseInt(index) + '-' + idea.title;
    }).reverse().join('\n');

    ideasString = 'My idea list:\n\n' + ideasString;
    ActivityView.show({
      text: ideasString
    });
  }
  _handleResetIdeas() {
    const { actions } = this.props;

    Alert.alert(
      'Confirm suppression',
      'Are you sure you want to delete all of your ideas?',
      [{
        text: 'OK', onPress: () => {
          actions.reset();
          this.props.navigator.pop();
        }
      },
      {
        text: 'Cancel'
      }]
    );
  }

  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.inputLabel}>
          Send or share your idea list:
        </Text>
        <TouchableOpacity onPress={this._handleShare.bind(this)} style={design.designComp.button}>
          <Text style={design.designComp.buttonText}>Email/Share</Text>
        </TouchableOpacity>
        <Text style={styles.inputLabel}>
          Delete your idea list:
        </Text>
        <TouchableOpacity onPress={this._handleResetIdeas.bind(this)} style={design.designComp.button}>
          <Text style={design.designComp.buttonText}>Reset ideas</Text>
        </TouchableOpacity>
      </View>
    );
  }
}

export default connect(
  state => ({
    state: state.ideas
  }),
  (dispatch) => ({
    actions: bindActionCreators(ideaActions, dispatch)
  })
)(Settings);

const styles = StyleSheet.create({
  container: {
    flex: 1
  },
  inputLabel: {
    paddingLeft: 17,
    paddingTop: 24,
    fontSize: 20
  }
});
<|endoftext|>


<commit_before>/*************************************************
 * Copyright (c) 2016 Ansible, Inc.
 *
 * All Rights Reserved
 *************************************************/
import ReconnectingWebSocket from 'reconnectingwebsocket';
export default
['$rootScope', '$location', '$log','$state', '$q',
    function ($rootScope, $location, $log, $state, $q) {
        var needsResubscribing = false,
        socketPromise = $q.defer();
        return {
            init: function() {
                var self = this,
                    host = window.location.host,
                    protocol,
                    url;

                if($location.protocol() === 'http'){
                    protocol = 'ws';
                }
                if($location.protocol() === 'https'){
                    protocol = 'wss';
                }
                url = `${protocol}://${host}/websocket/`;

                if (!$rootScope.sessionTimer || ($rootScope.sessionTimer && !$rootScope.sessionTimer.isExpired())) {
                    // We have a valid session token, so attempt socket connection
                    $log.debug('Socket connecting to: ' + url);

                    self.socket = new ReconnectingWebSocket(url, null, {
                        timeoutInterval: 3000,
                        maxReconnectAttempts: 10
                    });

                    self.socket.onopen = function () {
                        $log.debug("Websocket connection opened.");
                        socketPromise.resolve();
                        self.checkStatus();
                        if(needsResubscribing){
                            self.subscribe(self.getLast());
                            needsResubscribing = false;
                        }

                    };

                    self.socket.onerror = function (error) {
                        self.checkStatus();
                        $log.debug('Websocket Error Logged: ' + error); //log errors
                    };

                    self.socket.onconnecting = function (event) {
                        self.checkStatus();
                        $log.debug('Websocket reconnecting');
                        needsResubscribing = true;
                    };

                    self.socket.onclose = function (event) {
                        self.checkStatus();
                        $log.debug(`Websocket disconnected`);
                    };

                    self.socket.onmessage = this.onMessage;

                    return self.socket;
                }
                else {
                    // encountered expired token, redirect to login page
                    $rootScope.sessionTimer.expireSession('idle');
                    $location.url('/login');
                }
            },
            onMessage: function(e){
                // Function called when messages are received on by the UI from
                // the API over the websocket. This will route each message to
                // the appropriate controller for the current $state.
                e.data = e.data.replace(/\\/g, '');
                e.data = e.data.substr(0, e.data.length-1);
                e.data = e.data.substr(1);
                $log.debug('Received From Server: ' + e.data);

                var data = JSON.parse(e.data), str = "";
                if(data.group_name==="jobs" && !('status' in data)){
                    // we know that this must have been a
                    // summary complete message b/c status is missing.
                    // A an object w/ group_name === "jobs" AND a 'status' key
                    // means it was for the event: status_changed.
                    $log.debug('Job summary_complete ' + data.unified_job_id);
                    $rootScope.$broadcast('ws-jobs-summary', data);
                    return;
                }
                else if(data.group_name==="job_events"){
                    // The naming scheme is "ws" then a
                    // dash (-) and the group_name, then the job ID
                    // ex: 'ws-jobs-<jobId>'
                    str = `ws-${data.group_name}-${data.job}`;
                }
                else if(data.group_name==="ad_hoc_command_events"){
                    // The naming scheme is "ws" then a
                    // dash (-) and the group_name, then the job ID
                    // ex: 'ws-jobs-<jobId>'
                    str = `ws-${data.group_name}-${data.ad_hoc_command}`;
                }
                else if(data.group_name==="control"){
                    // As of Tower v. 3.1.0, there is only 1 "control"
                    // message, which is for expiring the session if the
                    // session limit is breached.
                    $log.debug(data.reason);
                    $rootScope.sessionTimer.expireSession('session_limit');
                    $state.go('signOut');
                }
                else {
                    // The naming scheme is "ws" then a
                    // dash (-) and the group_name.
                    // ex: 'ws-jobs'
                    str = `ws-${data.group_name}`;
                }
                $rootScope.$broadcast(str, data);
            },
            disconnect: function(){
                if(this.socket){
                    this.socket.close();
                }
            },
            subscribe: function(state){
                // Subscribe is used to tell the API that the UI wants to
                // listen for specific messages. A subscription object could
                // look like {"groups":{"jobs": ["status_changed", "summary"]}.
                // This is used by all socket-enabled $states
                this.emit(JSON.stringify(state.socket));
                this.setLast(state);
            },
            unsubscribe: function(state){
                // Unsubscribing tells the API that the user is no longer on
                // on a socket-enabled page, and sends an empty groups object
                // to the API: {"groups": {}}.
                // This is used for all pages that are socket-disabled
                if(this.requiresNewSubscribe(state)){
                    this.emit(JSON.stringify(state.socket));
                }
                this.setLast(state);
            },
            setLast: function(state){
                this.last = state;
            },
            getLast: function(){
                return this.last;
            },
            requiresNewSubscribe(state){
                // This function is used for unsubscribing. If the last $state
                // required an "unsubscribe", then we don't need to unsubscribe
                // again, b/c the UI is already unsubscribed from all groups
                if (this.getLast() !== undefined){
                    if( _.isEmpty(state.socket.groups) && _.isEmpty(this.getLast().socket.groups)){
                        return false;
                    }
                    else {
                        return true;
                    }
                }
                else {
                    return true;
                }
            },
            checkStatus: function() {
                // Function for changing the socket indicator icon in the nav bar
                var self = this;
                if(self){
                    if(self.socket){
                        if (self.socket.readyState === 0 ) {
                            $rootScope.socketStatus = 'connecting';
                            $rootScope.socketTip = "Live events: attempting to connect to the Tower server.";
                        }
                        else if (self.socket.readyState === 1){
                            $rootScope.socketStatus = 'ok';
                            $rootScope.socketTip = "Live events: connected. Pages containing job status information will automatically update in real-time.";
                        }
                        else if (self.socket.readyState === 2 || self.socket.readyState === 3 ){
                            $rootScope.socketStatus = 'error';
                            $rootScope.socketTip = "Live events: error connecting to the Tower server.";
                        }
                        return;
                    }
                }

            },
            emit: function(data, callback) {
                // Function used for sending objects to the API over the
                // websocket.
                var self = this;
                $log.debug('Sent to Websocket Server: ' + data);
                socketPromise.promise.then(function(){
                    self.socket.send(data, function () {
                        var args = arguments;
                        self.scope.$apply(function () {
                            if (callback) {
                                callback.apply(self.socket, args);
                            }
                        });
                    });
                });
            },
            addStateResolve: function(state, id){
                // This function is used for add a state resolve to all states,
                // socket-enabled AND socket-disabled, and whether the $state
                // requires a subscribe or an unsubscribe
                var self = this;
                socketPromise.promise.then(function(){
                    if(!state.socket){
                        state.socket = {groups: {}};
                        self.unsubscribe(state);
                    }
                    else{
                        if(state.socket.groups.hasOwnProperty( "job_events")){
                            state.socket.groups.job_events = [id];
                        }
                        if(state.socket.groups.hasOwnProperty( "ad_hoc_command_events")){
                            state.socket.groups.ad_hoc_command_events = [id];
                        }
                        self.subscribe(state);
                    }
                    return true;
                });
            }
        };
    }];
<commit_msg>Removing front end double parsing of socket messages<commit_after>/*************************************************
 * Copyright (c) 2016 Ansible, Inc.
 *
 * All Rights Reserved
 *************************************************/
import ReconnectingWebSocket from 'reconnectingwebsocket';
export default
['$rootScope', '$location', '$log','$state', '$q',
    function ($rootScope, $location, $log, $state, $q) {
        var needsResubscribing = false,
        socketPromise = $q.defer();
        return {
            init: function() {
                var self = this,
                    host = window.location.host,
                    protocol,
                    url;

                if($location.protocol() === 'http'){
                    protocol = 'ws';
                }
                if($location.protocol() === 'https'){
                    protocol = 'wss';
                }
                url = `${protocol}://${host}/websocket/`;

                if (!$rootScope.sessionTimer || ($rootScope.sessionTimer && !$rootScope.sessionTimer.isExpired())) {
                    // We have a valid session token, so attempt socket connection
                    $log.debug('Socket connecting to: ' + url);

                    self.socket = new ReconnectingWebSocket(url, null, {
                        timeoutInterval: 3000,
                        maxReconnectAttempts: 10
                    });

                    self.socket.onopen = function () {
                        $log.debug("Websocket connection opened.");
                        socketPromise.resolve();
                        self.checkStatus();
                        if(needsResubscribing){
                            self.subscribe(self.getLast());
                            needsResubscribing = false;
                        }

                    };

                    self.socket.onerror = function (error) {
                        self.checkStatus();
                        $log.debug('Websocket Error Logged: ' + error); //log errors
                    };

                    self.socket.onconnecting = function (event) {
                        self.checkStatus();
                        $log.debug('Websocket reconnecting');
                        needsResubscribing = true;
                    };

                    self.socket.onclose = function (event) {
                        self.checkStatus();
                        $log.debug(`Websocket disconnected`);
                    };

                    self.socket.onmessage = this.onMessage;

                    return self.socket;
                }
                else {
                    // encountered expired token, redirect to login page
                    $rootScope.sessionTimer.expireSession('idle');
                    $location.url('/login');
                }
            },
            onMessage: function(e){
                // Function called when messages are received on by the UI from
                // the API over the websocket. This will route each message to
                // the appropriate controller for the current $state.
                $log.debug('Received From Server: ' + e.data);

                var data = JSON.parse(e.data), str = "";
                if(data.group_name==="jobs" && !('status' in data)){
                    // we know that this must have been a
                    // summary complete message b/c status is missing.
                    // A an object w/ group_name === "jobs" AND a 'status' key
                    // means it was for the event: status_changed.
                    $log.debug('Job summary_complete ' + data.unified_job_id);
                    $rootScope.$broadcast('ws-jobs-summary', data);
                    return;
                }
                else if(data.group_name==="job_events"){
                    // The naming scheme is "ws" then a
                    // dash (-) and the group_name, then the job ID
                    // ex: 'ws-jobs-<jobId>'
                    str = `ws-${data.group_name}-${data.job}`;
                }
                else if(data.group_name==="ad_hoc_command_events"){
                    // The naming scheme is "ws" then a
                    // dash (-) and the group_name, then the job ID
                    // ex: 'ws-jobs-<jobId>'
                    str = `ws-${data.group_name}-${data.ad_hoc_command}`;
                }
                else if(data.group_name==="control"){
                    // As of Tower v. 3.1.0, there is only 1 "control"
                    // message, which is for expiring the session if the
                    // session limit is breached.
                    $log.debug(data.reason);
                    $rootScope.sessionTimer.expireSession('session_limit');
                    $state.go('signOut');
                }
                else {
                    // The naming scheme is "ws" then a
                    // dash (-) and the group_name.
                    // ex: 'ws-jobs'
                    str = `ws-${data.group_name}`;
                }
                $rootScope.$broadcast(str, data);
            },
            disconnect: function(){
                if(this.socket){
                    this.socket.close();
                }
            },
            subscribe: function(state){
                // Subscribe is used to tell the API that the UI wants to
                // listen for specific messages. A subscription object could
                // look like {"groups":{"jobs": ["status_changed", "summary"]}.
                // This is used by all socket-enabled $states
                this.emit(JSON.stringify(state.socket));
                this.setLast(state);
            },
            unsubscribe: function(state){
                // Unsubscribing tells the API that the user is no longer on
                // on a socket-enabled page, and sends an empty groups object
                // to the API: {"groups": {}}.
                // This is used for all pages that are socket-disabled
                if(this.requiresNewSubscribe(state)){
                    this.emit(JSON.stringify(state.socket));
                }
                this.setLast(state);
            },
            setLast: function(state){
                this.last = state;
            },
            getLast: function(){
                return this.last;
            },
            requiresNewSubscribe(state){
                // This function is used for unsubscribing. If the last $state
                // required an "unsubscribe", then we don't need to unsubscribe
                // again, b/c the UI is already unsubscribed from all groups
                if (this.getLast() !== undefined){
                    if( _.isEmpty(state.socket.groups) && _.isEmpty(this.getLast().socket.groups)){
                        return false;
                    }
                    else {
                        return true;
                    }
                }
                else {
                    return true;
                }
            },
            checkStatus: function() {
                // Function for changing the socket indicator icon in the nav bar
                var self = this;
                if(self){
                    if(self.socket){
                        if (self.socket.readyState === 0 ) {
                            $rootScope.socketStatus = 'connecting';
                            $rootScope.socketTip = "Live events: attempting to connect to the Tower server.";
                        }
                        else if (self.socket.readyState === 1){
                            $rootScope.socketStatus = 'ok';
                            $rootScope.socketTip = "Live events: connected. Pages containing job status information will automatically update in real-time.";
                        }
                        else if (self.socket.readyState === 2 || self.socket.readyState === 3 ){
                            $rootScope.socketStatus = 'error';
                            $rootScope.socketTip = "Live events: error connecting to the Tower server.";
                        }
                        return;
                    }
                }

            },
            emit: function(data, callback) {
                // Function used for sending objects to the API over the
                // websocket.
                var self = this;
                $log.debug('Sent to Websocket Server: ' + data);
                socketPromise.promise.then(function(){
                    self.socket.send(data, function () {
                        var args = arguments;
                        self.scope.$apply(function () {
                            if (callback) {
                                callback.apply(self.socket, args);
                            }
                        });
                    });
                });
            },
            addStateResolve: function(state, id){
                // This function is used for add a state resolve to all states,
                // socket-enabled AND socket-disabled, and whether the $state
                // requires a subscribe or an unsubscribe
                var self = this;
                socketPromise.promise.then(function(){
                    if(!state.socket){
                        state.socket = {groups: {}};
                        self.unsubscribe(state);
                    }
                    else{
                        if(state.socket.groups.hasOwnProperty( "job_events")){
                            state.socket.groups.job_events = [id];
                        }
                        if(state.socket.groups.hasOwnProperty( "ad_hoc_command_events")){
                            state.socket.groups.ad_hoc_command_events = [id];
                        }
                        self.subscribe(state);
                    }
                    return true;
                });
            }
        };
    }];
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
	errorHandler = require('./errors.server.controller'),
	List = mongoose.model('List'),
	Users = mongoose.model('Users'),
	_ = require('lodash');

/**
 * Reads all the sublists for the given list
 */
exports.getLists = function(req, res) {
	List.find().populate('user', 'username').exec(function(err, lists) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.json(lists);
		}
	});
};

/**
 * Creates a list for the current username
 */
exports.create = function(req, res) {
	var list = new List(req.body);
	list.user = req.user;

	// For some reason the server likes to insert escape characters here, so we
	// must remove them manually. Step 1, convert to string.
	var workingJson = JSON.stringify(list.access);

	// Removing all backslashes, they can't be in emails.
	workingJson = workingJson.replace("\\", "");

	// Removing the first and second-to-last character; these are unnecessary
	// quotes.
	workingJson = workingJson.replace("\"\"", "\"");

	list.access = JSON.parse(workingJson);

	list.save(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.json(list);
		}
	});
};

/**
 * Update a list by ID
 */
exports.update = function(req, res) {
	var list = req.list;

 	list = _.extend(list, req.body);

	list.save(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.json(list);
		}
	});
};

/**
 * Remove a list by ID
 */
exports.remove = function(req, res) {
	var list = req.list;

	list.remove(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.json(list);
		}
	});
};

exports.rootID = function(req, res, username) {
	Users.find().exec(function(err, users) {
		if (err) {
			return res.status(404).send({
				message: 'Could not find any users'
			});
		}
		// Array of references to all root lists
		var allUsers = users.map(function(user) { return user._id; });

		List.findOne({$and: [{user: {$in: allUsers}}, {root: true}]}, function(err, doc) {
			if (err) {
				return res.status(404).send({
					message: 'Root list not found'
				});
			}
			res.send(doc._id);
		});
	}
}

/**
 * Gets a single List based on its ID
 */
exports.getListByID = function(req, res, next, id) {
	if (!mongoose.Types.ObjectId.isValid(id)) {
		return res.status(400).send({
			message: 'Requested list is invalid'
		});
	}

	List.findById(id).exec(function(err, list) {
		if (err) return next(err);
		if (!list) {
			return res.status(404).send({
				message: 'List not found'
			});
		}
		req.list = list;
		next();
	});
};

/**
 * Returns the current level of authorization of the validated user
 */
exports.hasAuthorization = function(req, res, next) {

	next();
};
<commit_msg>Forgot a ;<commit_after>'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
	errorHandler = require('./errors.server.controller'),
	List = mongoose.model('List'),
	Users = mongoose.model('Users'),
	_ = require('lodash');

/**
 * Reads all the sublists for the given list
 */
exports.getLists = function(req, res) {
	List.find().populate('user', 'username').exec(function(err, lists) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.json(lists);
		}
	});
};

/**
 * Creates a list for the current username
 */
exports.create = function(req, res) {
	var list = new List(req.body);
	list.user = req.user;

	// For some reason the server likes to insert escape characters here, so we
	// must remove them manually. Step 1, convert to string.
	var workingJson = JSON.stringify(list.access);

	// Removing all backslashes, they can't be in emails.
	workingJson = workingJson.replace("\\", "");

	// Removing the first and second-to-last character; these are unnecessary
	// quotes.
	workingJson = workingJson.replace("\"\"", "\"");

	list.access = JSON.parse(workingJson);

	list.save(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.json(list);
		}
	});
};

/**
 * Update a list by ID
 */
exports.update = function(req, res) {
	var list = req.list;

 	list = _.extend(list, req.body);

	list.save(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.json(list);
		}
	});
};

/**
 * Remove a list by ID
 */
exports.remove = function(req, res) {
	var list = req.list;

	list.remove(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.json(list);
		}
	});
};

exports.rootID = function(req, res, username) {
	Users.find().exec(function(err, users) {
		if (err) {
			return res.status(404).send({
				message: 'Could not find any users'
			});
		}
		// Array of references to all root lists
		var allUsers = users.map(function(user) { return user._id; });

		List.findOne({$and: [{user: {$in: allUsers}}, {root: true}]}, function(err, doc) {
			if (err) {
				return res.status(404).send({
					message: 'Root list not found'
				});
			}
			res.send(doc._id);
		});
	}
};

/**
 * Gets a single List based on its ID
 */
exports.getListByID = function(req, res, next, id) {
	if (!mongoose.Types.ObjectId.isValid(id)) {
		return res.status(400).send({
			message: 'Requested list is invalid'
		});
	}

	List.findById(id).exec(function(err, list) {
		if (err) return next(err);
		if (!list) {
			return res.status(404).send({
				message: 'List not found'
			});
		}
		req.list = list;
		next();
	});
};

/**
 * Returns the current level of authorization of the validated user
 */
exports.hasAuthorization = function(req, res, next) {

	next();
};
<|endoftext|>


<commit_before>require.config({
	baseUrl : "./",
	paths : {
		"knockout" : "scripts/lib/knockout-3.0.0",
		"knockout-amd-helpers" : "scripts/lib/knockout-amd-helpers",
		"knockout-postbox" : "scripts/lib/knockout-postbox",
		"text" : "scripts/lib/text",
		"jquery" : "scripts/lib/jquery-1.9.1.min",
		"jquery-ui" : "scripts/lib/jquery-ui-1.10.3.custom.min"
	}
});

require([ "knockout","jquery", "scripts/modules/topicexplorer-view-model",
		"knockout-amd-helpers", "knockout-postbox",
		"text", "jquery-ui", "knockout-jqueryui" ], function(ko, $, App) {
	var timeoutId = null;
	var self=this;
	ko.bindingHandlers.module.baseDir = "scripts/modules";

	// global delegates
	$(document).delegate(".menuActivator", 'click', function(e){
		if($(this).next().height() + 26 > $(this).parent().height()) {
	    	$(this).next().css('max-height', $(this).parent().height() - 26);
	    }

		$(this).toggleClass("rotate1 rotate2");
	    $(this).next().toggle('blind');

	}).delegate("#invisbleTabs > span", 'click', function() {
		$('#tabMenu').toggle('blind');
	}).delegate("a.ui-corner-all", 'mouseover', function() {
		$(this).addClass('ui-state-focus');
	}).delegate("a.ui-corner-all", 'mouseout', function() {
		$(this).removeClass('ui-state-focus');
	}).delegate(".tab", 'mouseover', function() {
		$(this).children('img').show();
	}).delegate(".tab", 'mouseout', function() {
		$(this).children('img').hide();
	}).delegate(".documentList li", 'mouseenter', function(){
		$(this).addClass('myHover').find(".docButtons").show();
	}).delegate(".documentList li", 'mouseleave', function(){
		$(this).removeClass('myHover').find(".docButtons").hide();
	}).delegate(".documentList circle", "mouseover", function(){
		$(this).attr("r", "7");
	}).delegate(".documentList circle", "mouseout", function(){
		$(this).attr("r", "5");
	}).delegate(".ui-menu-item circle", "mouseover", function(){
		var self = this;
		if (!timeoutId) {
	        timeoutId = window.setTimeout(function() {
	            timeoutId = null;
	            moveToTopic($(self).attr('id').split('_')[1]);
		    }, 1500);
		}
		$(self).attr("r", "7");
	}).delegate(".ui-menu-item circle", "mouseout", function(){
		if (timeoutId) {
		    window.clearTimeout(timeoutId);
		    timeoutId = null;
		}
		$(this).attr("r", "5");
	}).delegate("#groupG rect", "mouseover", function(){
		$(this).attr("height", "17");
		$(this).attr("y", "0");
	}).delegate("#groupG rect", "mouseout", function(){
		$(this).attr("height", "13");
		$(this).attr("y", "2");
	}).delegate(".topicList", "mouseenter", function(){
		$(this).children(":first").show();
	}).delegate("#desktop", "mouseenter", function(){
		$(this).children(":first").show();
		if($("#desktop").scrollTop() > 1000) {
			$("#jumpToStart").show();
		}
	}).delegate("#desktop, .topicList", "mouseleave", function(){
		$(this).children(":first").hide();
		$(this).children(":first").next().hide();
		$(this).children(":first").removeClass("rotate2");
		$(this).children(":first").addClass("rotate1");
		$("#jumpToStart").hide();
	}).delegate("#jumpToStart", "click", function() {
		$("#desktop").animate({
			scrollTop: 0
		});
	}).delegate(".documentList circle, #groupG rect", "click", moveToTopic);

	self.minHeight = 400;
	self.minWidth = 800;

	$(window).resize(function() {
		ko.postbox.publish("windowWidth",Math.max(self.minWidth, $(window).width(), /* For opera: */ document.documentElement.clientWidth));
		ko.postbox.publish("windowHeight",Math.max(self.minHeight, $(window).height(), /* For opera: */ document.documentElement.clientHeight));
		setTopicSlider();
	});

	setTimeout(function() {
		ko.applyBindings(new App());
	}, 0);

	setTimeout(function() {
		ko.postbox.publish("windowWidth",Math.max(self.minWidth, $(window).width(), /* For opera: */ document.documentElement.clientWidth));
		ko.postbox.publish("windowHeight",Math.max(self.minHeight, $(window).height(), /* For opera: */ document.documentElement.clientHeight));
		setTopicSlider();
	}, 2500);

});

function setTopicSlider() {
	var slider = $('.topicPrevSlider');
	var maxListWidth = $('.topicList > ul').width();
	var topicDivWidth = $('.topicList').width();
	var bottomDivWidth = $('.topicBottomSliderDiv').width();

	slider.width((topicDivWidth*bottomDivWidth)/ maxListWidth);

	$('.topicPrevSlider > span').css('margin-left', (topicDivWidth * bottomDivWidth) / (maxListWidth * 2) - ($('.topicPrevSlider > span').width() / 2));
	var maxScrollPos = $('.topicBottomSliderDiv').width() - $('.topicPrevSlider').width();
	$( ".topicPrevSlider" ).draggable({
		axis: "x",
		containment: [ 0, 0, maxScrollPos, 0 ],
		drag: function( event, ui ) {
			var maxScroll = (maxListWidth - topicDivWidth);
			var scroll = ((ui.position.left + 4) / maxScrollPos) * maxScroll ;
			$('.topicList').scrollLeft(scroll);
			$('#topicMenu, .topicList > img').css('left', scroll);
		}
	});
};

function makeMenu(el) {
	$(el).menu({
		select : function(event, ui) {
			$(this).parent().parent().prev()
				.toggleClass("rotate1 rotate2");
			$(this).parent().parent().toggle('blind');
		}
	});
};

function getScrollPositionByValue(val) {
	var offset = 3;
	var scroll = val;
	var maxScrollPos = $('.topicBottomSliderDiv').width() - $('.topicPrevSlider').width();
	var maxScroll = $('.topicList > ul').width() - $('.topicList').width();
	var position = Math.round(scroll*maxScrollPos/maxScroll)+offset;
	if(position < 0)
		position = 0;
	if(position > maxScrollPos)
		position = maxScrollPos;
	return position;
};

function moveToTopic(self) {

	var topic_id = 0;
	if(!self.currentTarget) {
		topic_id = self;
	} else {
		topic_id = $(self.currentTarget).attr('id').split("_")[1];
	}
	var offset = $(".topicList").width() / 2 - $("#topic" + topic_id).width() / 2;

	$(".topicList").animate({
		scrollLeft : ($("#topic" + topic_id).position().left - offset)
	}, {
		duration : 2000,
		easing : "swing"
	});
	$(".topicPrevSlider").animate({
		left : getScrollPositionByValue(($("#topic" + topic_id).position().left - offset))
	}, {
		duration : 2000,
		easing : "swing"
	});
	setTimeout(function() {
		$(".topicList > img, #topicMenu").css('left', $(".topicList").scrollLeft());
	}, 2000);
};

<commit_msg>knockoutjqueryui deleted<commit_after>require.config({
	baseUrl : "./",
	paths : {
		"knockout" : "scripts/lib/knockout-3.0.0",
		"knockout-amd-helpers" : "scripts/lib/knockout-amd-helpers",
		"knockout-postbox" : "scripts/lib/knockout-postbox",
		"text" : "scripts/lib/text",
		"jquery" : "scripts/lib/jquery-1.9.1.min",
		"jquery-ui" : "scripts/lib/jquery-ui-1.10.3.custom.min"
	}
});

require([ "knockout","jquery", "scripts/modules/topicexplorer-view-model",
		"knockout-amd-helpers", "knockout-postbox",
		"text", "jquery-ui" ], function(ko, $, App) {
	var timeoutId = null;
	var self=this;
	ko.bindingHandlers.module.baseDir = "scripts/modules";

	// global delegates
	$(document).delegate(".menuActivator", 'click', function(e){
		if($(this).next().height() + 26 > $(this).parent().height()) {
	    	$(this).next().css('max-height', $(this).parent().height() - 26);
	    }

		$(this).toggleClass("rotate1 rotate2");
	    $(this).next().toggle('blind');

	}).delegate("#invisbleTabs > span", 'click', function() {
		$('#tabMenu').toggle('blind');
	}).delegate("a.ui-corner-all", 'mouseover', function() {
		$(this).addClass('ui-state-focus');
	}).delegate("a.ui-corner-all", 'mouseout', function() {
		$(this).removeClass('ui-state-focus');
	}).delegate(".tab", 'mouseover', function() {
		$(this).children('img').show();
	}).delegate(".tab", 'mouseout', function() {
		$(this).children('img').hide();
	}).delegate(".documentList li", 'mouseenter', function(){
		$(this).addClass('myHover').find(".docButtons").show();
	}).delegate(".documentList li", 'mouseleave', function(){
		$(this).removeClass('myHover').find(".docButtons").hide();
	}).delegate(".documentList circle", "mouseover", function(){
		$(this).attr("r", "7");
	}).delegate(".documentList circle", "mouseout", function(){
		$(this).attr("r", "5");
	}).delegate(".ui-menu-item circle", "mouseover", function(){
		var self = this;
		if (!timeoutId) {
	        timeoutId = window.setTimeout(function() {
	            timeoutId = null;
	            moveToTopic($(self).attr('id').split('_')[1]);
		    }, 1500);
		}
		$(self).attr("r", "7");
	}).delegate(".ui-menu-item circle", "mouseout", function(){
		if (timeoutId) {
		    window.clearTimeout(timeoutId);
		    timeoutId = null;
		}
		$(this).attr("r", "5");
	}).delegate("#groupG rect", "mouseover", function(){
		$(this).attr("height", "17");
		$(this).attr("y", "0");
	}).delegate("#groupG rect", "mouseout", function(){
		$(this).attr("height", "13");
		$(this).attr("y", "2");
	}).delegate(".topicList", "mouseenter", function(){
		$(this).children(":first").show();
	}).delegate("#desktop", "mouseenter", function(){
		$(this).children(":first").show();
		if($("#desktop").scrollTop() > 1000) {
			$("#jumpToStart").show();
		}
	}).delegate("#desktop, .topicList", "mouseleave", function(){
		$(this).children(":first").hide();
		$(this).children(":first").next().hide();
		$(this).children(":first").removeClass("rotate2");
		$(this).children(":first").addClass("rotate1");
		$("#jumpToStart").hide();
	}).delegate("#jumpToStart", "click", function() {
		$("#desktop").animate({
			scrollTop: 0
		});
	}).delegate(".documentList circle, #groupG rect", "click", moveToTopic);

	self.minHeight = 400;
	self.minWidth = 800;

	$(window).resize(function() {
		ko.postbox.publish("windowWidth",Math.max(self.minWidth, $(window).width(), /* For opera: */ document.documentElement.clientWidth));
		ko.postbox.publish("windowHeight",Math.max(self.minHeight, $(window).height(), /* For opera: */ document.documentElement.clientHeight));
		setTopicSlider();
	});

	setTimeout(function() {
		ko.applyBindings(new App());
	}, 0);

	setTimeout(function() {
		ko.postbox.publish("windowWidth",Math.max(self.minWidth, $(window).width(), /* For opera: */ document.documentElement.clientWidth));
		ko.postbox.publish("windowHeight",Math.max(self.minHeight, $(window).height(), /* For opera: */ document.documentElement.clientHeight));
		setTopicSlider();
	}, 2500);

});

function setTopicSlider() {
	var slider = $('.topicPrevSlider');
	var maxListWidth = $('.topicList > ul').width();
	var topicDivWidth = $('.topicList').width();
	var bottomDivWidth = $('.topicBottomSliderDiv').width();

	slider.width((topicDivWidth*bottomDivWidth)/ maxListWidth);

	$('.topicPrevSlider > span').css('margin-left', (topicDivWidth * bottomDivWidth) / (maxListWidth * 2) - ($('.topicPrevSlider > span').width() / 2));
	var maxScrollPos = $('.topicBottomSliderDiv').width() - $('.topicPrevSlider').width();
	$( ".topicPrevSlider" ).draggable({
		axis: "x",
		containment: [ 0, 0, maxScrollPos, 0 ],
		drag: function( event, ui ) {
			var maxScroll = (maxListWidth - topicDivWidth);
			var scroll = ((ui.position.left + 4) / maxScrollPos) * maxScroll ;
			$('.topicList').scrollLeft(scroll);
			$('#topicMenu, .topicList > img').css('left', scroll);
		}
	});
};

function makeMenu(el) {
	$(el).menu({
		select : function(event, ui) {
			$(this).parent().parent().prev()
				.toggleClass("rotate1 rotate2");
			$(this).parent().parent().toggle('blind');
		}
	});
};

function getScrollPositionByValue(val) {
	var offset = 3;
	var scroll = val;
	var maxScrollPos = $('.topicBottomSliderDiv').width() - $('.topicPrevSlider').width();
	var maxScroll = $('.topicList > ul').width() - $('.topicList').width();
	var position = Math.round(scroll*maxScrollPos/maxScroll)+offset;
	if(position < 0)
		position = 0;
	if(position > maxScrollPos)
		position = maxScrollPos;
	return position;
};

function moveToTopic(self) {

	var topic_id = 0;
	if(!self.currentTarget) {
		topic_id = self;
	} else {
		topic_id = $(self.currentTarget).attr('id').split("_")[1];
	}
	var offset = $(".topicList").width() / 2 - $("#topic" + topic_id).width() / 2;

	$(".topicList").animate({
		scrollLeft : ($("#topic" + topic_id).position().left - offset)
	}, {
		duration : 2000,
		easing : "swing"
	});
	$(".topicPrevSlider").animate({
		left : getScrollPositionByValue(($("#topic" + topic_id).position().left - offset))
	}, {
		duration : 2000,
		easing : "swing"
	});
	setTimeout(function() {
		$(".topicList > img, #topicMenu").css('left', $(".topicList").scrollLeft());
	}, 2000);
};

<|endoftext|>


<commit_before>/*global window: false */
module.exports = function(ngModule) {
  'use strict';

  ngModule.service('Pouch', function ($q, rooConfig, LocalStorageService) {

    function getDB(name) {
      return new PouchDB(name, rooConfig.getDbOptions());
    }

    /**
     * Function to update the records that are read-only
     * from the server with whatever changes have been
     * made on the client but have not yet been synced
     * to the server, for ui display purposes. This
     * is called currently from the MasterCtrl, so
     * basically on app load. change_id is dbname::id;
     * so, for example, looks something like this:
     *"gemini_cushion_fist::WO-20150226-2949689"
     */
    function shimRecords(downDbName, downDocs) {
      var deferred = $q.defer();
      var promises = [];
      var jorgeNames = rooConfig.getUpDbs();
      // Loop through all of the write databases
      _.each(jorgeNames, function (jorgeName) {
        // Get all the documents in the current write db
        getDB(jorgeName).allDocs({
          include_docs: true
        })
          .then(function (jorgeDocs) {
            if (jorgeDocs.total_rows > 0) {
              _.each(downDocs.rows, function(doc) {
                // The the current write db has any documents, loop through
                // them and see if any of the documents have a change_id that
                // matches the current read-only db
                _.each(jorgeDocs.rows, function (row) {
                  var d = $q.defer();
                  var change_id = row.doc.change_id;
                  var split = change_id.split('::');
                  var dbName = split[0];
                  var id = split[1];
                  if (dbName === downDbName) {
                    // Find if any document matches
                    if(doc.id === id) {
                      _.extend(doc.doc, JSON.parse(row.doc.change));
                    }
                  }
                  d.resolve(doc);
                  promises.push(d.promise);
                });
              });
            }
            $q.all(promises).then(function () {
              deferred.resolve(downDocs);
            });
          });
      });

      return deferred.promise;
    }

    /**
     * Function to update a given read-only record
     * from the server with whatever changes have been
     * made on the client but have not yet been synced
     * to the server, for ui display purposes. This
     * is called currently from the MasterCtrl, so
     * basically on app load. change_id is dbname::id;
     * so, for example, looks something like this:
     *"gemini_cushion_fist::WO-20150226-2949689"
     */
    function shimRecord(downDbName, downDoc) {
      var deferred = $q.defer();
      var jorgeNames = rooConfig.getUpDbs();
      // Loop through all of the write databases
      _.each(jorgeNames, function (jorgeName) {
        // Get all the documents in the current write db
        getDB(jorgeName).allDocs({
          include_docs: true
        }).then(function (jorgeDocs) {
            if (jorgeDocs.total_rows > 0) {
              // The the current write db has any documents, loop through
              // them and see if any of the documents have a change_id that
              // matches the current read-only db
              _.each(jorgeDocs.rows, function (row) {
                var change_id = row.doc.change_id;
                var split = change_id.split('::');
                var dbName = split[0];
                var id = split[1];
                if (dbName === downDbName) {
                  // Find if any document matches
                  if(downDoc._id === id) {
                    _.extend(downDoc, JSON.parse(row.doc.change));
                  }
                }
              });
            }
            deferred.resolve(downDoc);
          });
        });
      return deferred.promise;
    }

  return function (db) {

    this.db = db;

    this.getAll = function (options) {
      var self = this;
      var deferred = $q.defer();

      var o = _.extend({include_docs: true}, options);

      getDB(self.db)
      .allDocs(o)
      .then(function (docs) {
        return shimRecords(self.db, docs);
      }).then(function (docs) {
        deferred.resolve(_.pluck(docs.rows, 'doc'));
      });

      return deferred.promise;
    };

    this.query = function(query, options){
      var self = this;
      var deferred = $q.defer();

      var o = options || {};

      getDB(self.db).query(query, o)
      .then(function (doc) {
        return shimRecord(self.db, doc);
      }).then(function (doc) {
        deferred.resolve(doc);
      });
      return deferred.promise;
    };

    this.getAll = function () {
      var self = this;
      var deferred = $q.defer();
      getDB(self.db).allDocs({
        include_docs: true
      }).then(function (docs) {
        return shimRecords(self.db, docs);
      }).then(function (docs) {
        deferred.resolve(_.pluck(docs.rows, 'doc'));
      });
      return deferred.promise;
    };

    this.get = function (docId) {
      var self = this;
      var deferred = $q.defer();
      getDB(self.db).get(docId, {
        include_docs: true
      }).then(function (doc) {
        return shimRecord(self.db, doc);
      }).then(function (doc) {
        deferred.resolve(doc);
      });
      return deferred.promise;
    };

      /**
       * Retrieve attachment's local url
       * @param {string} docId - Document's id were the attachment exists.
       * @param {string} attrId - The id for the attachment itself.
       */
      this.getAttachment = function (docId, attrId) {
        var self = this;
        var deferred = $q.defer();
        var URL = window.URL;
        getDB(self.db).getAttachment(docId, attrId)
        .then(function (blob) {
          var url = URL.createObjectURL(blob);
          deferred.resolve(url);
        });
        return deferred.promise;
      };

      this.putEntry = function putEntry(table, id, obj, method, endpoint, data, headers, user, successCb, errorCb) {
        var entry = {
            _id: new moment().unix().toString(),
            change_id: '' + table + '::' + id,
            change: JSON.stringify(obj),
            method: method,
            endpoint: endpoint,
            data: data,
            headers: headers,
            user: user
          },
          self = this;

        getDB(self.db).put(entry).then(successCb, errorCb);
      };

      /**
       * Sync db with predefined CouchDB
       * @param {object} user - Any User object to be passed into getParams.
       * @param {object} options - functions to determine params and filters.
       */
      this.syncDB = function syncDB(user, opts) {
        var self = this;

        if(rooConfig.getOptions().destoryOnSync){
            console.log('removing table', self.db);
	          getDB(self.db)
              .destroy()
              .then(function(){
                self.performSync(user, opts);
              });
	        }else{
            self.performSync(user, opts);
          }
      };

      this.performSync = function(user, opts){
          var self = this;
          var deferred = $q.defer();

          // Initialize the remote and local databases
          var remote = new PouchDB(rooConfig.getCouchConfig().couchUrl + '/' + self.db);
          var local = getDB(self.db);
          console.log('syncing', self.db);
          var replicationOptions = {batch_size: 2};

          // Set replication options only if they were passed in
          if(opts.filter){
            replicationOptions.filter = opts.filter;
          }
          if(opts.getParams){
            replicationOptions.query_params = opts.getParams(user);
          }

          // Perform replication
          local.replicate.from(remote, replicationOptions)
            .on('complete', function (result) {
              try {
                // Make an entry in the logs
                LocalStorageService.addEntryToLog(user.employeeID, self.db, result);
                deferred.resolve();
              } catch(error) {
                console.log(error);
                deferred.reject(error);
              }
              return deferred.promise;
            });
        };

      this.update = function update(db, obj, id) {
        var deferred = $q.defer();
        var cushiondb = getDB(db);
        cushiondb.get(id).then(function success(doc) {
          var newDoc = _.extend(doc, obj);
          cushiondb.put(newDoc).then(function success(result) {
            deferred.resolve(result);
          }, function error(result) {
            deferred.reject(result);
          });
        }, function error(result) {
          deferred.reject(result);
        });
        return deferred.promise;
      };

      this.sendJorgeDB = function sendJorgeDB(db, user) {
        console.log('syncing ' + db + ' for user ' + user.displayName);
        var deferred = $q.defer();
        var promises = [];
        // Do some stuff here.
        getDB(db).allDocs({
          include_docs: true
        }).then(function (docs) {
          // Make an entry in the logs
          _.forEach(docs.rows, function(row) {
            promises.push(LocalStorageService.addEntryToLog(user.employeeID, db, row));
          });

          $q.all(promises).then(function(result) {
            deferred.resolve(result);
          });
        });
        return deferred.promise;
      };
    };
  });
};
<commit_msg>better sync vs replicate<commit_after>/*global window: false */
module.exports = function(ngModule) {
  'use strict';

  ngModule.service('Pouch', function ($q, rooConfig, LocalStorageService) {

    function getDB(name) {
      return new PouchDB(name, rooConfig.getDbOptions());
    }

    /**
     * Function to update the records that are read-only
     * from the server with whatever changes have been
     * made on the client but have not yet been synced
     * to the server, for ui display purposes. This
     * is called currently from the MasterCtrl, so
     * basically on app load. change_id is dbname::id;
     * so, for example, looks something like this:
     *"gemini_cushion_fist::WO-20150226-2949689"
     */
    function shimRecords(downDbName, downDocs) {
      var deferred = $q.defer();
      var promises = [];
      var jorgeNames = rooConfig.getUpDbs();
      // Loop through all of the write databases
      _.each(jorgeNames, function (jorgeName) {
        // Get all the documents in the current write db
        getDB(jorgeName).allDocs({
          include_docs: true
        })
          .then(function (jorgeDocs) {
            if (jorgeDocs.total_rows > 0) {
              _.each(downDocs.rows, function(doc) {
                // The the current write db has any documents, loop through
                // them and see if any of the documents have a change_id that
                // matches the current read-only db
                _.each(jorgeDocs.rows, function (row) {
                  var d = $q.defer();
                  var change_id = row.doc.change_id;
                  var split = change_id.split('::');
                  var dbName = split[0];
                  var id = split[1];
                  if (dbName === downDbName) {
                    // Find if any document matches
                    if(doc.id === id) {
                      _.extend(doc.doc, JSON.parse(row.doc.change));
                    }
                  }
                  d.resolve(doc);
                  promises.push(d.promise);
                });
              });
            }
            $q.all(promises).then(function () {
              deferred.resolve(downDocs);
            });
          });
      });

      return deferred.promise;
    }

    /**
     * Function to update a given read-only record
     * from the server with whatever changes have been
     * made on the client but have not yet been synced
     * to the server, for ui display purposes. This
     * is called currently from the MasterCtrl, so
     * basically on app load. change_id is dbname::id;
     * so, for example, looks something like this:
     *"gemini_cushion_fist::WO-20150226-2949689"
     */
    function shimRecord(downDbName, downDoc) {
      var deferred = $q.defer();
      var jorgeNames = rooConfig.getUpDbs();
      // Loop through all of the write databases
      _.each(jorgeNames, function (jorgeName) {
        // Get all the documents in the current write db
        getDB(jorgeName).allDocs({
          include_docs: true
        }).then(function (jorgeDocs) {
            if (jorgeDocs.total_rows > 0) {
              // The the current write db has any documents, loop through
              // them and see if any of the documents have a change_id that
              // matches the current read-only db
              _.each(jorgeDocs.rows, function (row) {
                var change_id = row.doc.change_id;
                var split = change_id.split('::');
                var dbName = split[0];
                var id = split[1];
                if (dbName === downDbName) {
                  // Find if any document matches
                  if(downDoc._id === id) {
                    _.extend(downDoc, JSON.parse(row.doc.change));
                  }
                }
              });
            }
            deferred.resolve(downDoc);
          });
        });
      return deferred.promise;
    }

  return function (db) {

    this.db = db;

    this.getAll = function (options) {
      var self = this;
      var deferred = $q.defer();

      var o = _.extend({include_docs: true}, options);

      getDB(self.db)
      .allDocs(o)
      .then(function (docs) {
        return shimRecords(self.db, docs);
      }).then(function (docs) {
        deferred.resolve(_.pluck(docs.rows, 'doc'));
      });

      return deferred.promise;
    };

    this.query = function(query, options){
      var self = this;
      var deferred = $q.defer();

      var o = options || {};

      getDB(self.db).query(query, o)
      .then(function (doc) {
        return shimRecord(self.db, doc);
      }).then(function (doc) {
        deferred.resolve(doc);
      });
      return deferred.promise;
    };

    this.getAll = function () {
      var self = this;
      var deferred = $q.defer();
      getDB(self.db).allDocs({
        include_docs: true
      }).then(function (docs) {
        return shimRecords(self.db, docs);
      }).then(function (docs) {
        deferred.resolve(_.pluck(docs.rows, 'doc'));
      });
      return deferred.promise;
    };

    this.get = function (docId) {
      var self = this;
      var deferred = $q.defer();
      getDB(self.db).get(docId, {
        include_docs: true
      }).then(function (doc) {
        return shimRecord(self.db, doc);
      }).then(function (doc) {
        deferred.resolve(doc);
      });
      return deferred.promise;
    };

      /**
       * Retrieve attachment's local url
       * @param {string} docId - Document's id were the attachment exists.
       * @param {string} attrId - The id for the attachment itself.
       */
      this.getAttachment = function (docId, attrId) {
        var self = this;
        var deferred = $q.defer();
        var URL = window.URL;
        getDB(self.db).getAttachment(docId, attrId)
        .then(function (blob) {
          var url = URL.createObjectURL(blob);
          deferred.resolve(url);
        });
        return deferred.promise;
      };

      this.putEntry = function putEntry(table, id, obj, method, endpoint, data, headers, user, successCb, errorCb) {
        var entry = {
            _id: new moment().unix().toString(),
            change_id: '' + table + '::' + id,
            change: JSON.stringify(obj),
            method: method,
            endpoint: endpoint,
            data: data,
            headers: headers,
            user: user
          },
          self = this;

        getDB(self.db).put(entry).then(successCb, errorCb);
      };

      /**
       * Replicate db with predefined CouchDB
       * @param {object} user - Any User object to be passed into getParams.
       * @param {object} options - functions to determine params and filters.
       */
      this.replicateDB = function(user, opts) {
        var self = this;

        if(rooConfig.getOptions().destoryOnSync){
            console.log('removing table', self.db);
	          getDB(self.db)
              .destroy()
              .then(function(){
                self.performReplication(user, opts);
              });
	        }else{
            self.performReplication(user, opts);
          }
      };

      this.performReplication = function(user, opts){
          var self = this;
          var deferred = $q.defer();

          // Initialize the remote and local databases
          var remote = new PouchDB(rooConfig.getCouchConfig().couchUrl + '/' + self.db);
          var local = getDB(self.db);
          console.log('Replicating', self.db);
          var replicationOptions = _.extend({batch_size: 2}, opts);

          if(opts.getParams){
            replicationOptions.query_params = opts.getParams(user);
          }

          // Perform replication
          local.replicate.from(remote, replicationOptions)
            .on('complete', function (result) {
              try {
                // Make an entry in the logs
                LocalStorageService.addEntryToLog(user.employeeID, self.db, result);
                deferred.resolve();
              } catch(error) {
                console.log(error);
                deferred.reject(error);
              }
              return deferred.promise;
            });
        };

      this.performSync = function(user, opts){
        var self = this;
        var deferred = $q.defer();

        console.log('Syncing', self.db);
        var syncOptions = _.extend({batch_size: 2}, opts);

        if(opts.getParams){
          syncOptions.query_params = opts.getParams(user);
        }

        // Perform replication
        PouchDB.sync(self.db, rooConfig.getCouchConfig().couchUrl + '/' + self.db, syncOptions)
          .on('complete', function (result) {
            try {
              // Make an entry in the logs
              LocalStorageService.addEntryToLog(user.employeeID, self.db, result);
              deferred.resolve();
            } catch(error) {
              console.log(error);
              deferred.reject(error);
            }
            return deferred.promise;
          });
      };

      this.update = function(db, obj, id) {
        var deferred = $q.defer();
        var cushiondb = getDB(db);
        cushiondb.get(id).then(function success(doc) {
          var newDoc = _.extend(doc, obj);
          cushiondb.put(newDoc).then(function success(result) {
            deferred.resolve(result);
          }, function error(result) {
            deferred.reject(result);
          });
        }, function error(result) {
          deferred.reject(result);
        });
        return deferred.promise;
      };

      this.sendJorgeDB = function(db, user) {
        console.log('syncing ' + db + ' for user ' + user.displayName);
        var deferred = $q.defer();
        var promises = [];
        // Do some stuff here.
        getDB(db).allDocs({
          include_docs: true
        }).then(function (docs) {
          // Make an entry in the logs
          _.forEach(docs.rows, function(row) {
            promises.push(LocalStorageService.addEntryToLog(user.employeeID, db, row));
          });

          $q.all(promises).then(function(result) {
            deferred.resolve(result);
          });
        });
        return deferred.promise;
      };
    };

  });
};
<|endoftext|>