<commit_before>/*******************************
           Build Docs
*******************************/

var
  gulp         = require('gulp'),

  // node dependencies
  console      = require('better-console'),
  fs           = require('fs'),

  // gulp dependencies
  autoprefixer = require('gulp-autoprefixer'),
  chmod        = require('gulp-chmod'),
  clone        = require('gulp-clone'),
  flatten      = require('gulp-flatten'),
  gulpif       = require('gulp-if'),
  header       = require('gulp-header'),
  less         = require('gulp-less'),
  minifyCSS    = require('gulp-minify-css'),
  plumber      = require('gulp-plumber'),
  print        = require('gulp-print'),
  rename       = require('gulp-rename'),
  replace      = require('gulp-replace'),
  uglify       = require('gulp-uglify'),

  // user config
  config       = require('../config/docs'),

  // install config
  configSetup  = require('../config/project/config'),
  tasks        = require('../config/project/tasks'),
  install      = require('../config/project/install'),

  // shorthand
  globs,
  assets,
  output,
  source,

  banner       = tasks.banner,
  comments     = tasks.regExp.comments,
  log          = tasks.log,
  settings     = tasks.settings
;

// add internal tasks (concat release)
require('../collections/internal')(gulp);

module.exports = function(callback) {

  var
    stream,
    compressedStream,
    uncompressedStream
  ;

  // use a different config
  config = configSetup.addDerivedValues(config);

  // shorthand
  globs  = config.globs;
  assets = config.paths.assets;
  output = config.paths.output;
  source = config.paths.source;

  /*--------------
     Copy Source
  ---------------*/

  // copy src/ to server
  gulp.src('src/**/*.*')
    .pipe(gulp.dest(output.less))
    .pipe(print(log.created))
  ;

  /*--------------
        Build
  ---------------*/

  console.info('Building Semantic for docs');

  if( !install.isSetup() ) {
    console.error('Cannot build files. Run "gulp install" to set-up Semantic');
    return;
  }

  // unified css stream
  stream = gulp.src(source.definitions + '**/' + globs.components + '.less')
    .pipe(plumber())
    .pipe(less(settings.less))
    .pipe(autoprefixer(settings.prefix))
    .pipe(flatten())
  ;

  // two concurrent streams from same source to concat release
  uncompressedStream = stream.pipe(clone());
  compressedStream   = stream.pipe(clone());

  uncompressedStream
    .pipe(plumber())
    .pipe(replace(comments.variables.in, comments.variables.out))
    .pipe(replace(comments.large.in, comments.large.out))
    .pipe(replace(comments.small.in, comments.small.out))
    .pipe(replace(comments.tiny.in, comments.tiny.out))
    .pipe(replace(assets.source, assets.uncompressed))
    .pipe(header(banner, settings.header))
    .pipe(gulpif(config.hasPermission, chmod(config.permission)))
    .pipe(gulp.dest(output.uncompressed))
    .pipe(print(log.created))
    .on('end', function() {
      gulp.start('package uncompressed docs css');
    })
  ;

  compressedStream = stream
    .pipe(plumber())
    .pipe(clone())
    .pipe(replace(assets.source, assets.compressed))
    .pipe(minifyCSS(settings.minify))
    .pipe(rename(settings.rename.minCSS))
    .pipe(header(banner, settings.header))
    .pipe(gulpif(config.hasPermission, chmod(config.permission)))
    .pipe(gulp.dest(output.compressed))
    .pipe(print(log.created))
    .on('end', function() {
      callback();
      gulp.start('package compressed docs css');
    })
  ;

  // copy assets
  gulp.src(source.themes + '**/assets/**/' + globs.components + '?(s).*')
    .pipe(gulpif(config.hasPermission, chmod(config.permission)))
    .pipe(gulp.dest(output.themes))
  ;

  // copy source javascript
  gulp.src(source.definitions + '**/' + globs.components + '.js')
    .pipe(plumber())
    .pipe(flatten())
    .pipe(gulp.dest(output.uncompressed))
    .pipe(gulpif(config.hasPermission, chmod(config.permission)))
    .pipe(print(log.created))
    .pipe(uglify(settings.uglify))
    .pipe(rename(settings.rename.minJS))
    .pipe(header(banner, settings.header))
    .pipe(gulp.dest(output.compressed))
    .pipe(gulpif(config.hasPermission, chmod(config.permission)))
    .pipe(print(log.created))
    .on('end', function() {
      gulp.start('package compressed docs js');
      gulp.start('package uncompressed docs js');
    })
  ;

};<commit_msg>Fix gulp docs glob<commit_after>/*******************************
           Build Docs
*******************************/

var
  gulp         = require('gulp'),

  // node dependencies
  console      = require('better-console'),
  fs           = require('fs'),

  // gulp dependencies
  autoprefixer = require('gulp-autoprefixer'),
  chmod        = require('gulp-chmod'),
  clone        = require('gulp-clone'),
  flatten      = require('gulp-flatten'),
  gulpif       = require('gulp-if'),
  header       = require('gulp-header'),
  less         = require('gulp-less'),
  minifyCSS    = require('gulp-minify-css'),
  plumber      = require('gulp-plumber'),
  print        = require('gulp-print'),
  rename       = require('gulp-rename'),
  replace      = require('gulp-replace'),
  uglify       = require('gulp-uglify'),

  // user config
  config       = require('../config/docs'),

  // install config
  configSetup  = require('../config/project/config'),
  tasks        = require('../config/project/tasks'),
  install      = require('../config/project/install'),

  // shorthand
  globs,
  assets,
  output,
  source,

  banner       = tasks.banner,
  comments     = tasks.regExp.comments,
  log          = tasks.log,
  settings     = tasks.settings
;

// add internal tasks (concat release)
require('../collections/internal')(gulp);

module.exports = function(callback) {

  var
    stream,
    compressedStream,
    uncompressedStream
  ;

  // use a different config
  config = configSetup.addDerivedValues(config);

  // shorthand
  globs  = config.globs;
  assets = config.paths.assets;
  output = config.paths.output;
  source = config.paths.source;

  /*--------------
     Copy Source
  ---------------*/

  // copy src/ to server
  gulp.src('src/**/*.*')
    .pipe(gulp.dest(output.less))
    .pipe(print(log.created))
  ;

  /*--------------
        Build
  ---------------*/

  console.info('Building Semantic for docs');

  if( !install.isSetup() ) {
    console.error('Cannot build files. Run "gulp install" to set-up Semantic');
    return;
  }

  // unified css stream
  stream = gulp.src(source.definitions + '/**/' + globs.components + '.less')
    .pipe(plumber())
    .pipe(less(settings.less))
    .pipe(autoprefixer(settings.prefix))
    .pipe(flatten())
  ;

  // two concurrent streams from same source to concat release
  uncompressedStream = stream.pipe(clone());
  compressedStream   = stream.pipe(clone());

  uncompressedStream
    .pipe(plumber())
    .pipe(replace(comments.variables.in, comments.variables.out))
    .pipe(replace(comments.large.in, comments.large.out))
    .pipe(replace(comments.small.in, comments.small.out))
    .pipe(replace(comments.tiny.in, comments.tiny.out))
    .pipe(replace(assets.source, assets.uncompressed))
    .pipe(header(banner, settings.header))
    .pipe(gulpif(config.hasPermission, chmod(config.permission)))
    .pipe(gulp.dest(output.uncompressed))
    .pipe(print(log.created))
    .on('end', function() {
      gulp.start('package uncompressed docs css');
    })
  ;

  compressedStream = stream
    .pipe(plumber())
    .pipe(clone())
    .pipe(replace(assets.source, assets.compressed))
    .pipe(minifyCSS(settings.minify))
    .pipe(rename(settings.rename.minCSS))
    .pipe(header(banner, settings.header))
    .pipe(gulpif(config.hasPermission, chmod(config.permission)))
    .pipe(gulp.dest(output.compressed))
    .pipe(print(log.created))
    .on('end', function() {
      callback();
      gulp.start('package compressed docs css');
    })
  ;

  // copy assets
  gulp.src(source.themes + '/**/assets/**/' + globs.components + '?(s).*')
    .pipe(gulpif(config.hasPermission, chmod(config.permission)))
    .pipe(gulp.dest(output.themes))
  ;

  // copy source javascript
  gulp.src(source.definitions + '/**/' + globs.components + '.js')
    .pipe(plumber())
    .pipe(flatten())
    .pipe(gulp.dest(output.uncompressed))
    .pipe(gulpif(config.hasPermission, chmod(config.permission)))
    .pipe(print(log.created))
    .pipe(uglify(settings.uglify))
    .pipe(rename(settings.rename.minJS))
    .pipe(header(banner, settings.header))
    .pipe(gulp.dest(output.compressed))
    .pipe(gulpif(config.hasPermission, chmod(config.permission)))
    .pipe(print(log.created))
    .on('end', function() {
      gulp.start('package compressed docs js');
      gulp.start('package uncompressed docs js');
    })
  ;

};<|endoftext|>


<commit_before>/*
 * grunt-loop-mocha
 * https://github.com/grawk/grunt-loop-mocha
 *
 * Copyright (c) 2013 Matt Edelman
 * Licensed under the MIT license.
 */
"use strict";

module.exports = function (grunt) {

  var path = require('path'),
    fs = require('fs'),
    util = grunt.util,
    nutil = require('util'),
    child_process = require("child_process"),
    _ = require('lodash'),
    async = require('async'),
    exists = grunt.file.exists,
    iterationError = false,
    iterationRemaining,
    noFail = false,
    iterationResults = {};

  grunt.registerMultiTask('loopmocha', 'Run mocha multiple times', function () {
    console.log("loopmocha.options.loop", grunt.config.get("loopmocha.options.loop"));
    var options = this.options(),
      mochaDefaultOptions = grunt.config.get("loopmocha.options.mocha"),
      mochaOptions = _.merge(mochaDefaultOptions, options.mocha),
      loopOptions = grunt.config.get("loopmocha.options.loop"),
      allDefaultOptions = grunt.config.get("loopmocha.options"),
      otherDefaultOptions = {},
      otherOptions = {},
      reportLocation = loopOptions.reportLocation || '',
      asyncMethod = (loopOptions.parallel) ? "map" : "mapSeries",
      binPath = '.bin/mocha' + (process.platform === 'win32' ? '.cmd' : ''),
      mocha_path = path.join(__dirname, '..', '/node_modules/', binPath),
      iterations = options.iterations || undefined,
      iterationRemaining = Object.keys(iterations).length,
      iterationIndex = 0,
      done = this.async(),
      filesSrc = this.filesSrc,
      runStamp = (new Date()).getTime();

    //catalog any extra default options
    _.each(_.omit(allDefaultOptions, 'src', 'basedir', 'mocha', 'iterations'), function (value, key) {
      otherDefaultOptions[key] = value;
    });
    //catalog any extra task specific options
    _.each(_.omit(options, 'src', 'basedir', 'mocha', 'iterations'), function (value, key) {
      otherOptions[key] = value;
    });
    //merge together the default and task specific extra options
    otherOptions = _.merge(otherDefaultOptions, otherOptions);
    if (!exists(mocha_path)) {
      var i = module.paths.length,
        bin;
      while (i--) {
        bin = path.join(module.paths[i], binPath);
        if (exists(bin)) {
          mocha_path = bin;
          break;
        }
      }
    }

    if (!exists(mocha_path)) {
      grunt.fail.warn('Unable to find mocha.');
    }

    // map or mapLimit.  The cb will take an err and result...
    async[asyncMethod](iterations, function (el, cb) {
      var i,
        opts = {},
        localopts = [],
        localOtherOptions = {},
        localOtherOptionsStringified = {},
        localMochaOptions = (el.mocha) ? _.merge(mochaOptions, el.mocha) : mochaOptions,
        itLabel = runStamp + "-" + ((el.description) ? (el.description) : (++iterationIndex)); // = opts_array.slice(0);

      //merge the iteration extra options with the default/task specific extra options
      _.each(_.omit(el, 'description', 'mocha'), function (value, key) {
        localOtherOptions[key] = value;
      });
      localOtherOptions = _.merge(otherOptions, localOtherOptions);

      //stringify the extra options for passage via env
      _.each(localOtherOptions, function (value, key) {
        console.log("[grunt-loop-mocha] setting ENV var ", key, "with value", value);
        localOtherOptionsStringified[key] = JSON.stringify(value);
      });

      // put the localMochaOptions into opts so we can pass
      // to mocha with --thing.  Remove options grunt-loop-mocha
      // is supporting
      _.each(_.omit(localMochaOptions
        , 'reportLocation'
        , 'iterations'
        , 'noFail'
        , 'limit' // the limit var for mapLimit
      )
        , function (value, key) {
          if (value !== 0) {
            //console.log("added from A", key);
            opts[key] = value || "";
          }
        });
      if (localMochaOptions.reporter === "xunit-file") {
        var reportFolderExists = (fs.existsSync(reportLocation) && fs.statSync(reportLocation).isDirectory());
        if (!reportFolderExists) {
          done(new Error("[grunt-loop-mocha] You need to make sure your report directory exists before using the xunit-file reporter"));
        }
      }
      if (localMochaOptions.noFail && localMochaOptions.noFail.toString().toLowerCase() === "true") {
        noFail = true;
      }

      Object.keys(opts).forEach(function (key) {
        if (key === "description") {
          return;
        }
        localopts.push("--" + key);
        if (opts[key] !== "" && opts[key] !== undefined) {
          localopts.push(opts[key]);
        }
      });

      // spill off the processes.  This can be quite a few.
      // the results will be in the form
      // [[returnCode, itterationName], ...]
      require('./process-loop.js')(grunt)({
          filesSrc: filesSrc, mocha_path: mocha_path, reportLocation: reportLocation, localopts: localopts, localOtherOptionsStringified: localOtherOptionsStringified, itLabel: itLabel, localMochaOptions: localMochaOptions, loopOptions: loopOptions
        }
        , cb);

    }, function (err, results) {
      var iterationResults = results.reduce(function (results, runs) {
        runs.forEach(function (run) {
          results[run[1]] = run[0];
          iterationError = iterationError || !!run[0];
        });
        return results;
      }, {});
      if (iterationError) {
        var msg = "[grunt-loop-mocha] error, please check erroneous iteration(s): " + JSON.stringify(iterationResults);
        if (noFail === true) {
          console.log(msg);
          done();
        }
        else {
          done(new Error(msg));
        }

      } else {
        done();
      }
    });
  });
};
<commit_msg>fixing issue that task level loop option didnt overwrite default loop option<commit_after>/*
 * grunt-loop-mocha
 * https://github.com/grawk/grunt-loop-mocha
 *
 * Copyright (c) 2013 Matt Edelman
 * Licensed under the MIT license.
 */
"use strict";

module.exports = function (grunt) {

  var path = require('path'),
    fs = require('fs'),
    util = grunt.util,
    nutil = require('util'),
    child_process = require("child_process"),
    _ = require('lodash'),
    async = require('async'),
    exists = grunt.file.exists,
    iterationError = false,
    iterationRemaining,
    noFail = false,
    iterationResults = {};

  grunt.registerMultiTask('loopmocha', 'Run mocha multiple times', function () {
    console.log("loopmocha.options.loop", grunt.config.get("loopmocha.options.loop"));
    var options = this.options(),
      mochaDefaultOptions = grunt.config.get("loopmocha.options.mocha"),
      mochaOptions = _.merge(mochaDefaultOptions, options.mocha),
      loopDefaultOptions = grunt.config.get("loopmocha.options.loop"),
      loopOptions = _.merge(loopDefaultOptions, options.loop),
      allDefaultOptions = grunt.config.get("loopmocha.options"),
      otherDefaultOptions = {},
      otherOptions = {},
      reportLocation = loopOptions.reportLocation || '',
      asyncMethod = (loopOptions.parallel) ? "map" : "mapSeries",
      binPath = '.bin/mocha' + (process.platform === 'win32' ? '.cmd' : ''),
      mocha_path = path.join(__dirname, '..', '/node_modules/', binPath),
      iterations = options.iterations || undefined,
      iterationRemaining = Object.keys(iterations).length,
      iterationIndex = 0,
      done = this.async(),
      filesSrc = this.filesSrc,
      runStamp = (new Date()).getTime();

    //catalog any extra default options
    _.each(_.omit(allDefaultOptions, 'src', 'basedir', 'mocha', 'iterations'), function (value, key) {
      otherDefaultOptions[key] = value;
    });
    //catalog any extra task specific options
    _.each(_.omit(options, 'src', 'basedir', 'mocha', 'iterations'), function (value, key) {
      otherOptions[key] = value;
    });
    //merge together the default and task specific extra options
    otherOptions = _.merge(otherDefaultOptions, otherOptions);
    if (!exists(mocha_path)) {
      var i = module.paths.length,
        bin;
      while (i--) {
        bin = path.join(module.paths[i], binPath);
        if (exists(bin)) {
          mocha_path = bin;
          break;
        }
      }
    }

    if (!exists(mocha_path)) {
      grunt.fail.warn('Unable to find mocha.');
    }

    // map or mapLimit.  The cb will take an err and result...
    async[asyncMethod](iterations, function (el, cb) {
      var i,
        opts = {},
        localopts = [],
        localOtherOptions = {},
        localOtherOptionsStringified = {},
        localMochaOptions = (el.mocha) ? _.merge(mochaOptions, el.mocha) : mochaOptions,
        itLabel = runStamp + "-" + ((el.description) ? (el.description) : (++iterationIndex)); // = opts_array.slice(0);

      //merge the iteration extra options with the default/task specific extra options
      _.each(_.omit(el, 'description', 'mocha'), function (value, key) {
        localOtherOptions[key] = value;
      });
      localOtherOptions = _.merge(otherOptions, localOtherOptions);

      //stringify the extra options for passage via env
      _.each(localOtherOptions, function (value, key) {
        console.log("[grunt-loop-mocha] setting ENV var ", key, "with value", value);
        localOtherOptionsStringified[key] = JSON.stringify(value);
      });

      // put the localMochaOptions into opts so we can pass
      // to mocha with --thing.  Remove options grunt-loop-mocha
      // is supporting
      _.each(_.omit(localMochaOptions
        , 'reportLocation'
        , 'iterations'
        , 'noFail'
        , 'limit' // the limit var for mapLimit
      )
        , function (value, key) {
          if (value !== 0) {
            //console.log("added from A", key);
            opts[key] = value || "";
          }
        });
      if (localMochaOptions.reporter === "xunit-file") {
        var reportFolderExists = (fs.existsSync(reportLocation) && fs.statSync(reportLocation).isDirectory());
        if (!reportFolderExists) {
          done(new Error("[grunt-loop-mocha] You need to make sure your report directory exists before using the xunit-file reporter"));
        }
      }
      if (localMochaOptions.noFail && localMochaOptions.noFail.toString().toLowerCase() === "true") {
        noFail = true;
      }

      Object.keys(opts).forEach(function (key) {
        if (key === "description") {
          return;
        }
        localopts.push("--" + key);
        if (opts[key] !== "" && opts[key] !== undefined) {
          localopts.push(opts[key]);
        }
      });

      // spill off the processes.  This can be quite a few.
      // the results will be in the form
      // [[returnCode, itterationName], ...]
      require('./process-loop.js')(grunt)({
          filesSrc: filesSrc, mocha_path: mocha_path, reportLocation: reportLocation, localopts: localopts, localOtherOptionsStringified: localOtherOptionsStringified, itLabel: itLabel, localMochaOptions: localMochaOptions, loopOptions: loopOptions
        }
        , cb);

    }, function (err, results) {
      var iterationResults = results.reduce(function (results, runs) {
        runs.forEach(function (run) {
          results[run[1]] = run[0];
          iterationError = iterationError || !!run[0];
        });
        return results;
      }, {});
      if (iterationError) {
        var msg = "[grunt-loop-mocha] error, please check erroneous iteration(s): " + JSON.stringify(iterationResults);
        if (noFail === true) {
          console.log(msg);
          done();
        }
        else {
          done(new Error(msg));
        }

      } else {
        done();
      }
    });
  });
};
<|endoftext|>


<commit_before>/*
 * Main Storage controller. Encapsulates all ORM/Waterline stuff.
 */

/*
 * Module dependencies.
 */
var Waterline = require('waterline');
var pranaAdapter = require('waterline-prana');
var mongoAdapter = require('sails-mongo');
var utils = require('prana').utils;
var modelPatch = require('./model-patch');
var adapterMethods = require('./adapter-patch');

/**
 * Main Storage controller class.
 *
 * @class Storage
 */
var Storage = module.exports = function(application) {
  this.application = application;

  // Create Waterline instance.
  this.waterline = new Waterline();
};

/**
 * Initialize the Storage at a given port.
 *
 * @param {Number} port Port number.
 * @param {Function} [callback] Optional callback to run when the Storage is initialized.
 */
Storage.prototype.init = function(callback) {
  // Add methods not implemented in waterline-prana that we need for
  // overrides to be persisted.
  utils.extend(pranaAdapter, adapterMethods(this.application));

  // Pass Choko instance to the Prana Waterline adapter.
  pranaAdapter.setApplication(this.application);

  var config = {
    adapters: {
      'prana': pranaAdapter,
      'mongo': mongoAdapter
    },
    connections: {
      'prana': {
        adapter: 'prana'
      },
      'mongo': {
        adapter: 'mongo',
        url: this.application.settings.database
      }
    }
  };

  // Initialize Waterline ORM.
  var application = this.application;
  this.waterline.initialize(config, function(error, models) {
    if (error) {
      return callback(error);
    }

    // Waterline lowercase all collection names so we need to fix mappings
    // to make them consistent.
    var collections = {};
    for (var collectionName in models.collections) {
      var collection = models.collections[collectionName];
      var schema = collection.waterline.schema[collectionName];

      // Add type and application object to collections.
      collection.application = application;
      collection.type = application.types[schema.tableName];

      collections[schema.tableName] = collection;
    }

    callback(null, collections);
  });
};

Storage.prototype.type = function(name, settings) {
  // Create schema and register it in Waterline.
  var schema = {
    identity: name,
    tableName: name,
    schema: false,

    // @todo: allow extensions to add storages/connections/adapters.
    connection: (settings.storage == 'database' ? 'mongo' : 'prana'),

    // Disable automatic migration.
    migrate: 'safe',

    // Disable auto primary key and created/update.
    autoPK: (!settings.keyProperty),
    autoCreatedAt: false,
    autoUpdatedAt: false,

    // Initialize attibutes.
    attributes: {}
  };

  if (settings.fields) {
    // Create attributes for all type fields.
    for (fieldName in settings.fields) {
      var fieldSettings = settings.fields[fieldName];

      // Process reference fields.
      if (fieldSettings.type == 'reference' && fieldSettings.reference) {
        if (fieldSettings.reference.inline) {
          // Use 'array' or 'json' for inline references.
          schema.attributes[fieldName] = {
            type: fieldSettings.reference.multiple ? 'array' : 'json',
            required: fieldSettings.required
          };
        }
        else {
          if (fieldSettings.reference.multiple) {
            var attribute = schema.attributes[fieldName] = {
              // Many-to-many relationships will just use array for now, since
              // waterline does not support many to many relatioships with
              // embbeded documents.
              // @todo: eventually we may be able to use something like this.
              // collection: fieldSettings.reference.type
              type: 'array'
            };
            if ('required' in fieldSettings) {
              attribute.required = fieldSettings.required;
            }
          }
          else {
            schema.attributes[fieldName] = {
              required: fieldSettings.required,
              model: fieldSettings.reference.type
            };
          }
        }
      }
      else {
        // Process scalar fields.
        var field = this.application.fields[fieldSettings.type];
        var primaryKey = (settings.keyProperty == fieldName);
        var attribute = schema.attributes[fieldName] = {
          // Default type to string.
          type: 'string'
        };

        if (field.schema) {
          switch (typeof field.schema) {

            // If schema is a string it's the attribute type.
            case 'string':
              utils.extend(attribute, {
                type: field.schema
              });
              break;

            // If schema is a object it's the attribute settings object.
            case 'object':
              utils.extend(attribute, field.schema);
              break;

            // If schema is a function run it to get the attribute settings
            // object.
            case 'function':
              utils.extend(attribute, field.schema(fieldSettings));
              break;
          }
        }

        // Add required/unique properties if set.
        ['required', 'unique'].map(function(what) {
          if (what in fieldSettings) {
            attribute[what] = fieldSettings[what];
          }
        });

        if (primaryKey) {
          attribute.primaryKey = true;
        }
      }
    }
  }

  // Add validation related methods.
  // @todo: rename this something else than patch, since it's not this anymore.
  utils.extend(schema, modelPatch);

  // Add before/after callbacks.
  ['Validate', 'Update', 'Create', 'Destroy'].map(function(operation) {
    ['before', 'after'].map(function(when) {
      var callbackName = when + operation;
      if (callbackName in settings) {
        schema[callbackName] = settings[callbackName];
      }
    });
  });

  // Add type specific collection methods.
  if (settings.statics && schema.attributes) {
    utils.extend(schema, settings.statics);
  }

  // Add type methods to model.
  if (settings.methods && schema.attributes) {
    utils.extend(schema.attributes, settings.methods);
  }

  var collection = Waterline.Collection.extend(schema);

  // Load the collection into Waterline.
  this.waterline.loadCollection(collection);

  return collection;
};
<commit_msg>Breaking Storage.prototype.type() into small functions part 1/2. #147<commit_after>/*
 * Main Storage controller. Encapsulates all ORM/Waterline stuff.
 */

/*
 * Module dependencies.
 */
var Waterline = require('waterline');
var pranaAdapter = require('waterline-prana');
var mongoAdapter = require('sails-mongo');
var utils = require('prana').utils;
var modelPatch = require('./model-patch');
var adapterMethods = require('./adapter-patch');

/**
 * Main Storage controller class.
 *
 * @class Storage
 */
var Storage = module.exports = function(application) {
  this.application = application;

  // Create Waterline instance.
  this.waterline = new Waterline();
};

/**
 * Initialize the Storage at a given port.
 *
 * @param {Number} port Port number.
 * @param {Function} [callback] Optional callback to run when the Storage is initialized.
 */
Storage.prototype.init = function(callback) {
  // Add methods not implemented in waterline-prana that we need for
  // overrides to be persisted.
  utils.extend(pranaAdapter, adapterMethods(this.application));

  // Pass Choko instance to the Prana Waterline adapter.
  pranaAdapter.setApplication(this.application);

  var config = {
    adapters: {
      'prana': pranaAdapter,
      'mongo': mongoAdapter
    },
    connections: {
      'prana': {
        adapter: 'prana'
      },
      'mongo': {
        adapter: 'mongo',
        url: this.application.settings.database
      }
    }
  };

  // Initialize Waterline ORM.
  var application = this.application;
  this.waterline.initialize(config, function(error, models) {
    if (error) {
      return callback(error);
    }

    // Waterline lowercase all collection names so we need to fix mappings
    // to make them consistent.
    var collections = {};
    for (var collectionName in models.collections) {
      var collection = models.collections[collectionName];
      var schema = collection.waterline.schema[collectionName];

      // Add type and application object to collections.
      collection.application = application;
      collection.type = application.types[schema.tableName];

      collections[schema.tableName] = collection;
    }

    callback(null, collections);
  });
};

Storage.prototype.type = function(name, settings) {
  // Create schema and register it in Waterline.
  var schema = {
    identity: name,
    tableName: name,
    schema: false,

    // @todo: allow extensions to add storages/connections/adapters.
    connection: (settings.storage == 'database' ? 'mongo' : 'prana'),

    // Disable automatic migration.
    migrate: 'safe',

    // Disable auto primary key and created/update.
    autoPK: (!settings.keyProperty),
    autoCreatedAt: false,
    autoUpdatedAt: false,

    // Initialize attibutes.
    attributes: {}
  };

  if (settings.fields) {
    this.processFields(schema, settings);
  }

  // Add validation related methods.
  // @todo: rename this something else than patch, since it's not this anymore.
  utils.extend(schema, modelPatch);

  // Add before/after callbacks.
  ['Validate', 'Update', 'Create', 'Destroy'].map(function(operation) {
    ['before', 'after'].map(function(when) {
      var callbackName = when + operation;
      if (callbackName in settings) {
        schema[callbackName] = settings[callbackName];
      }
    });
  });

  // Add type specific collection methods.
  if (settings.statics && schema.attributes) {
    utils.extend(schema, settings.statics);
  }

  // Add type methods to model.
  if (settings.methods && schema.attributes) {
    utils.extend(schema.attributes, settings.methods);
  }

  var collection = Waterline.Collection.extend(schema);

  // Load the collection into Waterline.
  this.waterline.loadCollection(collection);

  return collection;
};

Storage.prototype.processFields = function(schema, settings) {
  // Create attributes for all type fields.
  for (fieldName in settings.fields) {
    var fieldSettings = settings.fields[fieldName];

    // Process reference fields.
    if (fieldSettings.type == 'reference' && fieldSettings.reference) {
      if (fieldSettings.reference.inline) {
        // Use 'array' or 'json' for inline references.
        schema.attributes[fieldName] = {
          type: fieldSettings.reference.multiple ? 'array' : 'json',
          required: fieldSettings.required
        };
      }
      else {
        if (fieldSettings.reference.multiple) {
          var attribute = schema.attributes[fieldName] = {
            // Many-to-many relationships will just use array for now, since
            // waterline does not support many to many relatioships with
            // embbeded documents.
            // @todo: eventually we may be able to use something like this.
            // collection: fieldSettings.reference.type
            type: 'array'
          };
          if ('required' in fieldSettings) {
            attribute.required = fieldSettings.required;
          }
        }
        else {
          schema.attributes[fieldName] = {
            required: fieldSettings.required,
            model: fieldSettings.reference.type
          };
        }
      }
    }
    else {
      // Process scalar fields.
      var field = this.application.fields[fieldSettings.type];
      var primaryKey = (settings.keyProperty == fieldName);
      var attribute = schema.attributes[fieldName] = {
        // Default type to string.
        type: 'string'
      };

      if (field.schema) {
        switch (typeof field.schema) {

          // If schema is a string it's the attribute type.
          case 'string':
            utils.extend(attribute, {
              type: field.schema
            });
            break;

          // If schema is a object it's the attribute settings object.
          case 'object':
            utils.extend(attribute, field.schema);
            break;

          // If schema is a function run it to get the attribute settings
          // object.
          case 'function':
            utils.extend(attribute, field.schema(fieldSettings));
            break;
        }
      }

      // Add required/unique properties if set.
      ['required', 'unique'].map(function(what) {
        if (what in fieldSettings) {
          attribute[what] = fieldSettings[what];
        }
      });

      if (primaryKey) {
        attribute.primaryKey = true;
      }
    }
  }
};
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import {Modal, Form, FormControl, FormGroup, Button} from 'react-bootstrap';

class SignupForm extends Component {
    constructor(props) {
        super(props);
        this.state = {
            email: '',
            isModalOpen: false
        };
    }

    hideModal = () => {
        this.setState({isModalOpen: false});
    }

    handleEmailChange = (event) => {
        this.setState({email: event.target.value});
    }

    signupSubmit = (event) => {
        event.preventDefault();
        console.log('email: ' + this.state.email); //TODO: remove
        //TODO: Signup request to backend
        setTimeout(() => {
            this.setState({
                isModalOpen: true,
                email: ''
            });
        }, 2000);
    }

    render() {
        return (

                <Form className="navbar-form signup-form" onSubmit={this.signupSubmit}>
                    <FormGroup>
                        <FormControl
                            type="text"
                            name="email"
                            autoComplete="off"
                            placeholder="example@uoa.gr"
                            value={this.state.email}
                            onChange={this.handleEmailChange}
                        />
                    </FormGroup>
                    <Button
                        type="submit"
                        bsStyle="primary"
                        id="btn-signup"
                    >
                        Sign up
                    </Button>
                </Form>
                <Modal show={this.state.isModalOpen} onHide={this.hideModal}>
                    <Modal.Header closeButton>
                        <Modal.Title>Signup complete</Modal.Title>
                    </Modal.Header>
                    <Modal.Body>

Please check your email and follow the link we have sent you to start chatting.

                    </Modal.Body>
                </Modal>

        );
    }
}

export default SignupForm;
<commit_msg>Disable signup button while loading<commit_after>import React, { Component } from 'react';
import {Modal, Form, FormControl, FormGroup, Button} from 'react-bootstrap';

class SignupForm extends Component {
    constructor(props) {
        super(props);
        this.state = {
            email: '',
            isModalOpen: false,
            isSignupButtonLoading: false
        };
    }

    hideModal = () => {
        this.setState({isModalOpen: false});
    }

    handleEmailChange = (event) => {
        this.setState({email: event.target.value});
    }

    signupSubmit = (event) => {
        event.preventDefault();
        console.log('email: ' + this.state.email); //TODO: remove
        this.setState({isSignupButtonLoading: true});
        //TODO: Signup request to backend
        setTimeout(() => {
            this.setState({
                isSignupButtonLoading: false,
                isModalOpen: true,
                email: ''
            });
        }, 2000);
    }

    render() {
        return (

                <Form className="navbar-form signup-form" onSubmit={this.signupSubmit}>
                    <FormGroup>
                        <FormControl
                            type="text"
                            name="email"
                            autoComplete="off"
                            placeholder="example@uoa.gr"
                            value={this.state.email}
                            disabled={this.state.isSignupButtonLoading}
                            onChange={this.handleEmailChange}
                        />
                    </FormGroup>
                    <Button
                        type="submit"
                        bsStyle="primary"
                        id="btn-signup"
                        disabled={this.state.isSignupButtonLoading}
                        onClick={!this.state.isSignupButtonLoading? this.signupSubmit : null}
                    >
                        {this.state.isSignupButtonLoading? 'Signing up...' : 'Sign up'}
                    </Button>
                </Form>
                <Modal show={this.state.isModalOpen} onHide={this.hideModal}>
                    <Modal.Header closeButton>
                        <Modal.Title>Signup complete</Modal.Title>
                    </Modal.Header>
                    <Modal.Body>

Please check your email and follow the link we have sent you to start chatting.

                    </Modal.Body>
                </Modal>

        );
    }
}

export default SignupForm;
<|endoftext|>


<commit_before>import React from 'react';
import test from 'ava';
import { Link } from 'react-router-dom';
import { shallow } from 'enzyme';
import sinon from 'sinon';
require('ignore-styles')
import App from '../src/App.js';
import Router from "react-router-dom";
const app = <App/>
const wrapper = shallow(
  app
);

test('has correct title', t => {
	t.truthy(doesAppContainElement(
Enter your name below
));
});

test('text is ennen kutsua at beginning', t => {
	t.truthy(doesAppContainElement(
ennen kutsua
));
});

test('has correct input field', t => {
  t.truthy(doesAppContainElement(<input ref="input" placeholder="enter your name here" type="text" name="username"/>));
});

test('has correct button', t => {
  t.truthy(doesAppContainElement(<input type="submit" value="send" />));
});

test('has link to contract page', t => {
  t.truthy(doesAppContainElement(<Link to="/contract"> Go to contract page </Link>));
});

function doesAppContainElement(element) {
  return wrapper.containsMatchingElement(element);
};
<commit_msg>cleaned imports<commit_after>import React from 'react';
import test from 'ava';
import { Link, Router } from 'react-router-dom';
import { shallow } from 'enzyme';
//import sinon from 'sinon';
require('ignore-styles')
import App from '../src/App.js';

const app = <App/>
const wrapper = shallow(
  app
);

test('has correct title', t => {
	t.truthy(doesAppContainElement(
Enter your name below
));
});

test('text is ennen kutsua at beginning', t => {
	t.truthy(doesAppContainElement(
ennen kutsua
));
});

test('has correct input field', t => {
  t.truthy(doesAppContainElement(<input ref="input" placeholder="enter your name here" type="text" name="username"/>));
});

test('has correct button', t => {
  t.truthy(doesAppContainElement(<input type="submit" value="send" />));
});

test('has link to contract page', t => {
  t.truthy(doesAppContainElement(<Link to="/contract"> Go to contract page </Link>));
});

function doesAppContainElement(element) {
  return wrapper.containsMatchingElement(element);
};
<|endoftext|>


<commit_before>// @flow
import * as React from 'react'
import {StyleSheet, View, Text, Platform} from 'react-native'
import Icon from 'react-native-vector-icons/Ionicons'
import * as c from '../../components/colors'

const buttonStyles = StyleSheet.create({
	button: {
		flexDirection: 'row',
		alignItems: 'center',
		marginRight: 8,
		paddingHorizontal: 8,
		paddingVertical: 0,
		marginVertical: 8,
		borderWidth: 1,
		borderRadius: 2,
	},
	activeButton: {
		backgroundColor: c.mandarin,
		borderColor: c.mandarin,
	},
	inactiveButton: {
		borderColor: c.iosDisabledText,
	},
	activeText: {
		color: c.white,
	},
	inactiveText: {
		color: c.iosDisabledText,
	},
	textWithIcon: {
		paddingRight: 8,
	},
})

type ButtonPropsType = {
	iconName?: string,
	title: string,
	isActive: boolean,
}

export function ToolbarButton({title, iconName, isActive}: ButtonPropsType) {
	let icon
	if (!iconName) {
		icon = null
	} else if (Platform.OS === 'ios') {
		icon = isActive ? iconName : iconName + '-outline'
	} else if (Platform.OS === 'android') {
		icon = iconName
	}

	let activeButtonStyle = isActive
		? buttonStyles.activeButton
		: buttonStyles.inactiveButton
	let activeContentStyle = isActive
		? buttonStyles.activeText
		: buttonStyles.inactiveText

	let textWithIconStyle = icon ? buttonStyles.textWithIcon : null
	let activeTextStyle = {
		fontWeight: isActive && Platform.OS === 'android' ? 'bold' : 'normal',
	}

	return (
		<View style={[buttonStyles.button, activeButtonStyle]}>
			<Text style={[activeContentStyle, textWithIconStyle, activeTextStyle]}>
				{title}
			</Text>
			{icon ? <Icon name={icon} size={18} style={activeContentStyle} /> : null}
		</View>
	)
}
<commit_msg>change toolbar button to blue<commit_after>// @flow
import * as React from 'react'
import {StyleSheet, View, Text, Platform} from 'react-native'
import Icon from 'react-native-vector-icons/Ionicons'
import * as c from '../../components/colors'

const buttonStyles = StyleSheet.create({
	button: {
		flexDirection: 'row',
		alignItems: 'center',
		marginRight: 8,
		paddingHorizontal: 8,
		paddingVertical: 0,
		marginVertical: 8,
		borderWidth: 1,
		borderRadius: 2,
	},
	activeButton: {
		backgroundColor: c.carletonBlue,
		borderColor: c.carletonBlue,
	},
	inactiveButton: {
		borderColor: c.iosDisabledText,
	},
	activeText: {
		color: c.white,
	},
	inactiveText: {
		color: c.iosDisabledText,
	},
	textWithIcon: {
		paddingRight: 8,
	},
})

type ButtonPropsType = {
	iconName?: string,
	title: string,
	isActive: boolean,
}

export function ToolbarButton({title, iconName, isActive}: ButtonPropsType) {
	let icon
	if (!iconName) {
		icon = null
	} else if (Platform.OS === 'ios') {
		icon = isActive ? iconName : iconName + '-outline'
	} else if (Platform.OS === 'android') {
		icon = iconName
	}

	let activeButtonStyle = isActive
		? buttonStyles.activeButton
		: buttonStyles.inactiveButton
	let activeContentStyle = isActive
		? buttonStyles.activeText
		: buttonStyles.inactiveText

	let textWithIconStyle = icon ? buttonStyles.textWithIcon : null
	let activeTextStyle = {
		fontWeight: isActive && Platform.OS === 'android' ? 'bold' : 'normal',
	}

	return (
		<View style={[buttonStyles.button, activeButtonStyle]}>
			<Text style={[activeContentStyle, textWithIconStyle, activeTextStyle]}>
				{title}
			</Text>
			{icon ? <Icon name={icon} size={18} style={activeContentStyle} /> : null}
		</View>
	)
}
<|endoftext|>


<commit_before>/**
@module ember
@submodule ember-old-router
*/

var get = Ember.get, set = Ember.set;

/**
  Ember.HashLocation implements the location API using the browser's
  hash. At present, it relies on a hashchange event existing in the
  browser.

  @class HashLocation
  @namespace Ember
  @extends Ember.Object
*/
Ember.HashExclamationLocation = Ember.Object.extend({

  init: function() {
    set(this, 'location', get(this, 'location') || window.location);
  },

  /**
    @private

    Returns the current `location.hash`, minus the '#!' at the front.

    @method getURL
  */
  getURL: function() {
    return get(this, 'location').hash.substr(2);
  },

  /**
    @private

    Set the `location.hash` and remembers what was set. This prevents
    `onUpdateURL` callbacks from triggering when the hash was set by
    `HashLocation`.

    Also, added an exclamation mark in front of the path.

    @method setURL
    @param path {String}
  */
  setURL: function(path) {
    exclam_path = '!'+path;
    get(this, 'location').hash = exclam_path;
    set(this, 'lastSetURL', exclam_path);
  },

  /**
    @private

    Register a callback to be invoked when the hash changes. These
    callbacks will execute when the user presses the back or forward
    button, but not after `setURL` is invoked.

    @method onUpdateURL
    @param callback {Function}
  */
  onUpdateURL: function(callback) {
    var self = this;
    var guid = Ember.guidFor(this);

    Ember.$(window).bind('hashchange.ember-location-'+guid, function() {
      var path = location.hash.substr(2);
      if (get(self, 'lastSetURL') === path) { return; }

      set(self, 'lastSetURL', null);

      callback(path);
    });
  },

  /**
    @private

    Given a URL, formats it to be placed into the page as part
    of an element's `href` attribute.

    This is used, for example, when using the {{action}} helper
    to generate a URL based on an event.

    @method formatURL
    @param url {String}
  */
  formatURL: function(url) {
    return '#!'+url;
  },

  willDestroy: function() {
    var guid = Ember.guidFor(this);

    Ember.$(window).unbind('hashchange.ember-location-'+guid);
  }
});

Ember.Location.registerImplementation('hash_exclamation', Ember.HashLocation);
<commit_msg>Fixed oversight in ember_hash_exclamation.<commit_after>/**
@module ember
@submodule ember-old-router
*/

var get = Ember.get, set = Ember.set;

/**
  Ember.HashLocation implements the location API using the browser's
  hash. At present, it relies on a hashchange event existing in the
  browser.

  @class HashLocation
  @namespace Ember
  @extends Ember.Object
*/
Ember.HashExclamationLocation = Ember.Object.extend({

  init: function() {
    set(this, 'location', get(this, 'location') || window.location);
  },

  /**
    @private

    Returns the current `location.hash`, minus the '#!' at the front.

    @method getURL
  */
  getURL: function() {
    return get(this, 'location').hash.substr(2);
  },

  /**
    @private

    Set the `location.hash` and remembers what was set. This prevents
    `onUpdateURL` callbacks from triggering when the hash was set by
    `HashLocation`.

    Also, added an exclamation mark in front of the path.

    @method setURL
    @param path {String}
  */
  setURL: function(path) {
    exclam_path = '!'+path;
    get(this, 'location').hash = exclam_path;
    set(this, 'lastSetURL', exclam_path);
  },

  /**
    @private

    Register a callback to be invoked when the hash changes. These
    callbacks will execute when the user presses the back or forward
    button, but not after `setURL` is invoked.

    @method onUpdateURL
    @param callback {Function}
  */
  onUpdateURL: function(callback) {
    var self = this;
    var guid = Ember.guidFor(this);

    Ember.$(window).bind('hashchange.ember-location-'+guid, function() {
      var path = location.hash.substr(2);
      if (get(self, 'lastSetURL') === path) { return; }

      set(self, 'lastSetURL', null);

      callback(path);
    });
  },

  /**
    @private

    Given a URL, formats it to be placed into the page as part
    of an element's `href` attribute.

    This is used, for example, when using the {{action}} helper
    to generate a URL based on an event.

    @method formatURL
    @param url {String}
  */
  formatURL: function(url) {
    return '#!'+url;
  },

  willDestroy: function() {
    var guid = Ember.guidFor(this);

    Ember.$(window).unbind('hashchange.ember-location-'+guid);
  }
});

Ember.Location.registerImplementation('hash_exclamation', Ember.HashExclamationLocation);
<|endoftext|>


<commit_before>import assert from 'assert';
import App from '../src/App';

describe('App', () => {
  it('when beginning of message is #', () => {
    const params = { user_name: 'app2641', text: '# beginning of message is #' };
    const app = new App(params);
    assert.equal(app.run(), null);
  });

  it('when not exists sheet', () => {
    const params = { user_name: 'anonymous', text: 'bye' };
    const app = new App(params);
    assert.equal(app.run(), null);
  });

  it('when not find appropriate command', () => {
    const params = { user_name: 'app2641', text: 'hoge' };
    const app = new App(params);
    assert.equal(app.run(), null);
  });
});
<commit_msg>Add green test<commit_after>import assert from 'assert';
import App from '../src/App';

describe('App', () => {
  it('when beginning of message is #', () => {
    const params = { user_name: 'app2641', text: '# beginning of message is #' };
    const app = new App(params);
    assert.equal(app.run(), null);
  });

  it('when not exists sheet', () => {
    const params = { user_name: 'anonymous', text: 'bye' };
    const app = new App(params);
    assert.equal(app.run(), null);
  });

  it('when not find appropriate command', () => {
    const params = { user_name: 'app2641', text: 'hoge' };
    const app = new App(params);
    assert.equal(app.run(), null);
  });

  it('when process is successful', () => {
    const params = { user_name: 'app2641', text: '1/1 おは' };
    const app = new App(params);
    assert.equal(app.run(), true);
  });
});
<|endoftext|>


<commit_before>/* SBHS-Timetable-Node: Countdown and timetable all at once (NodeJS app).
 * Copyright (C) 2014 James Ye, Simon Shields
 *
 * This file is part of SBHS-Timetable-Node.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
/* global belltimes, currentBellIndex, calculateUpcomingLesson, reloadBelltimes, todayNames, updatePeriodLabel, updateSidebarStatus */ /* jshint -W098 */

function handleLeftPane() {
	/* Fill out the left pane */
	'use strict';
	var pane = document.getElementById('left-pane'),
		html = '

Subject	Teacher	Room
',
		timetable = todayNames.timetable,
		prefix, subj, suffix, room, teacher, fullTeacher, subjName, finalised,
		roomChanged, teacherChanged, cancelled = false;
	if (window.timetableCached) {
		html = '
This data may be outdated
' + html;
	}
	for (var i = 1; i < 6; i++) {
		if (!(i in timetable) || !timetable[i].room) {
			html += '
Free
';
		}
		else {
			todayNames.timetable[i].expanded = false;
			prefix = '';
			subj = '';
			suffix = '';
			subj = timetable[i].title;
			room = timetable[i].room;
			teacher = timetable[i].teacher;
			fullTeacher = timetable[i].fullTeacher;
			subjName = timetable[i].fullName;
			finalised = timetable.variationsFinalised;
			if (/\d$/.test(timetable[i].title) || /[a-z][A-Z]$/.test(timetable[i].title)) {
				suffix = timetable[i].title.substr(-1);
				subj = subj.slice(0,-1);
			}
			if (subj !== 'SDs' /* Software design special case */ && subj.length == 3 /*|| (subj.length == 2 && suffix === '')*/ || /^[WXYZ]/.test(subj)) { // very tentative guess that this is an elective - char 1 should be prefix
				prefix = subj[0];
				subj = subj.substr(1);
			}
			if (timetable[i].changed && todayNames.variationsFinalised) {
				if (timetable[i].varies) {
					if (timetable[i].cancelled) {
						cancelled = true;
					}
					else if (timetable[i].hasCasual) {
						teacherChanged = true;
						teacher = timetable[i].casual.toUpperCase();
						fullTeacher = timetable[i].casualDisplay.trim();
					}
				}
				if (timetable[i].roomFrom) {
					roomChanged = true;
					room = timetable[i].roomTo;
				}
			}
			var nSubj = '';
			var idx = 0;

			for (var j = 0; j < subj.length; j++) {
				var z = subjName.indexOf(subj[j+1]);
				z = z < 0 ? undefined : z;
				var mStr = subjName.substring(idx+1, z);
				nSubj += subj[j] + ''+mStr+'';
				idx = z;
			}
			var nTeach = '';
			idx = 0;
			var temp = teacher.toLowerCase();
			var fullTemp = fullTeacher;
			if (/^M(r|s) /.test(fullTemp)) {
				fullTemp = fullTemp.split(' ').slice(1).join(' ');
			}
			if (fullTemp[0].toLowerCase() !== teacher[0].toLowerCase()) {
				// probably a split class, no expandability because otherwise it's broken
				fullTemp = teacher;
			}
			fullTemp = fullTemp.replace(' ', ' ');
			for (var k = 0; k < teacher.length; k++) {
				var y = fullTemp.toLowerCase().indexOf(temp[k+1]);
				y = y < 0 ? undefined : y;
				var extra = fullTemp.substring(idx+1, y);
				if (k !== 0) {
					nTeach += '';
				}
				nTeach += (k === 0 ? teacher[k].toUpperCase() : teacher[k].toLowerCase()) + ''+extra.toLowerCase()+'';
				if (k !== 0) {
					nTeach += '';
				}
				idx = y;
			}
			if (cancelled) {
				room = 'N/A';
			}
			html += '
'+timetable[i].year+prefix+''+nSubj+''+suffix+'	'+nTeach+'	'+room+'
';
			cancelled = false;
			roomChanged = false;
			teacherChanged = false;
		}
	}
	html += '

';
	pane.innerHTML = html;
}

function getLoggedIn() {
	/* Are you logged in? */
	'use strict';
	return window.loggedIn;
}

function expandSubject(event, id) {
	'use strict';
	if (todayNames.timetable[id].expanded) {
		$('.subj-expand', event.currentTarget.parentNode).velocity('stop').velocity('transition.slideLeftBigOut');
		todayNames.timetable[id].expanded = false;
	}
	else {
		$('.subj-expand', event.currentTarget.parentNode).velocity('stop').velocity('transition.slideLeftBigIn');
		todayNames.timetable[id].expanded = true;
	}
}

function teacherExpand(event, id) {
	'use strict';
	var el = event.currentTarget.parentNode;
	if (el.tagName.toLowerCase() === 'span') {
		console.log('#nope');
		el = el.parentNode;
	}
	if (todayNames.timetable[id].teachExpanded) {
		$('.teach-expand', el).velocity('stop').velocity('transition.slideLeftBigOut');
		todayNames.timetable[id].teachExpanded = false;
	}
	else {
		$('.teach-expand', el).velocity('stop').velocity('transition.slideLeftBigIn');
		todayNames.timetable[id].teachExpanded = true;
	}
}

function handleTimetable() {
	/* Fill out the timetable */
	'use strict';
	/* jshint validthis: true, -W041 */
	window.timetableCached = false;
	if (window.belltimes == null || belltimes.status === 'Error') {
		reloadBelltimes(); // use today.json's date.
	}
	var res;
	try {
		res = JSON.parse(this.responseText);
	}
	catch (e) {
		res = {'status': 'damn'};
		$('#left-pane .umad').text('SBHS is broken :(');
		return;
	}
	if (window.belltimes != null) {
		var lsKey = belltimes.day + belltimes.weekType;
		if (res.timetable && !res.hasVariations) {
			window.localStorage[lsKey] = this.responseText;
		}
	}
	if (!res.timetable) {
		$('#rtd-status').html('Real-time data inaccessible');
		return; // we don't want to do that... TODO refresh access token and stuff
	}
	window.todayNames = res;
	$('#rtd-status').html('Loaded real-time data');
	setTimeout(function() {
		$('#rtd-status').velocity('fadeOut');
		setTimeout(function() { $('#rtd-status').velocity('fadeOut').html('').css({'display': 'block'}); }, 1000);
	}, 3000);
	if (currentBellIndex == -1) {
		calculateUpcomingLesson();
	}
	handleLeftPane();
	if (belltimes.status !== 'Error') {
		updatePeriodLabel();
	}
	updateSidebarStatus();
}

function loadTimetable() {
	/* Get the timetable */
	'use strict';
	window.timetableCached = false;
	if (belltimes && (belltimes.day+belltimes.weekType) in window.localStorage) {
		window.timetableCached = true;
		console.log('loading from localStorage');
		window.todayNames = JSON.parse(window.localStorage[belltimes.day+belltimes.weekType]);
		setTimeout(handleLeftPane, 0);
		updateSidebarStatus();
		$('#rtd-status').html('Loading real-time data…');
	}
	else if (!getLoggedIn() && !window.todayNames) {
		console.log('umm');
		window.todayNames = {timetable: {failure: true}};
	}
	if (!getLoggedIn()) {
		$('#rtd-status').html('Log in for real-time data');
		return;
	}
	var xhr = new XMLHttpRequest();
	xhr.onload = handleTimetable;
	xhr.open('GET', '/api/today.json?stupid_proxy_caching_is_stupid', true);
	xhr.send();
}
<commit_msg>timetable.js: Dr is a prefix too<commit_after>/* SBHS-Timetable-Node: Countdown and timetable all at once (NodeJS app).
 * Copyright (C) 2014 James Ye, Simon Shields
 *
 * This file is part of SBHS-Timetable-Node.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
/* global belltimes, currentBellIndex, calculateUpcomingLesson, reloadBelltimes, todayNames, updatePeriodLabel, updateSidebarStatus */ /* jshint -W098 */

function handleLeftPane() {
	/* Fill out the left pane */
	'use strict';
	var pane = document.getElementById('left-pane'),
		html = '

Subject	Teacher	Room
',
		timetable = todayNames.timetable,
		prefix, subj, suffix, room, teacher, fullTeacher, subjName, finalised,
		roomChanged, teacherChanged, cancelled = false;
	if (window.timetableCached) {
		html = '
This data may be outdated
' + html;
	}
	for (var i = 1; i < 6; i++) {
		if (!(i in timetable) || !timetable[i].room) {
			html += '
Free
';
		}
		else {
			todayNames.timetable[i].expanded = false;
			prefix = '';
			subj = '';
			suffix = '';
			subj = timetable[i].title;
			room = timetable[i].room;
			teacher = timetable[i].teacher;
			fullTeacher = timetable[i].fullTeacher;
			subjName = timetable[i].fullName;
			finalised = timetable.variationsFinalised;
			if (/\d$/.test(timetable[i].title) || /[a-z][A-Z]$/.test(timetable[i].title)) {
				suffix = timetable[i].title.substr(-1);
				subj = subj.slice(0,-1);
			}
			if (subj !== 'SDs' /* Software design special case */ && subj.length == 3 /*|| (subj.length == 2 && suffix === '')*/ || /^[WXYZ]/.test(subj)) { // very tentative guess that this is an elective - char 1 should be prefix
				prefix = subj[0];
				subj = subj.substr(1);
			}
			if (timetable[i].changed && todayNames.variationsFinalised) {
				if (timetable[i].varies) {
					if (timetable[i].cancelled) {
						cancelled = true;
					}
					else if (timetable[i].hasCasual) {
						teacherChanged = true;
						teacher = timetable[i].casual.toUpperCase();
						fullTeacher = timetable[i].casualDisplay.trim();
					}
				}
				if (timetable[i].roomFrom) {
					roomChanged = true;
					room = timetable[i].roomTo;
				}
			}
			var nSubj = '';
			var idx = 0;

			for (var j = 0; j < subj.length; j++) {
				var z = subjName.indexOf(subj[j+1]);
				z = z < 0 ? undefined : z;
				var mStr = subjName.substring(idx+1, z);
				nSubj += subj[j] + ''+mStr+'';
				idx = z;
			}
			var nTeach = '';
			idx = 0;
			var temp = teacher.toLowerCase();
			var fullTemp = fullTeacher;
			if (/^(Mr|Ms|Dr) /.test(fullTemp)) {
				fullTemp = fullTemp.split(' ').slice(1).join(' ');
			}
			if (fullTemp[0].toLowerCase() !== teacher[0].toLowerCase()) {
				// probably a split class, no expandability because otherwise it's broken
				fullTemp = teacher;
			}
			fullTemp = fullTemp.replace(' ', ' ');
			for (var k = 0; k < teacher.length; k++) {
				var y = fullTemp.toLowerCase().indexOf(temp[k+1]);
				y = y < 0 ? undefined : y;
				var extra = fullTemp.substring(idx+1, y);
				if (k !== 0) {
					nTeach += '';
				}
				nTeach += (k === 0 ? teacher[k].toUpperCase() : teacher[k].toLowerCase()) + ''+extra.toLowerCase()+'';
				if (k !== 0) {
					nTeach += '';
				}
				idx = y;
			}
			if (cancelled) {
				room = 'N/A';
			}
			html += '
'+timetable[i].year+prefix+''+nSubj+''+suffix+'	'+nTeach+'	'+room+'
';
			cancelled = false;
			roomChanged = false;
			teacherChanged = false;
		}
	}
	html += '

';
	pane.innerHTML = html;
}

function getLoggedIn() {
	/* Are you logged in? */
	'use strict';
	return window.loggedIn;
}

function expandSubject(event, id) {
	'use strict';
	if (todayNames.timetable[id].expanded) {
		$('.subj-expand', event.currentTarget.parentNode).velocity('stop').velocity('transition.slideLeftBigOut');
		todayNames.timetable[id].expanded = false;
	}
	else {
		$('.subj-expand', event.currentTarget.parentNode).velocity('stop').velocity('transition.slideLeftBigIn');
		todayNames.timetable[id].expanded = true;
	}
}

function teacherExpand(event, id) {
	'use strict';
	var el = event.currentTarget.parentNode;
	if (el.tagName.toLowerCase() === 'span') {
		console.log('#nope');
		el = el.parentNode;
	}
	if (todayNames.timetable[id].teachExpanded) {
		$('.teach-expand', el).velocity('stop').velocity('transition.slideLeftBigOut');
		todayNames.timetable[id].teachExpanded = false;
	}
	else {
		$('.teach-expand', el).velocity('stop').velocity('transition.slideLeftBigIn');
		todayNames.timetable[id].teachExpanded = true;
	}
}

function handleTimetable() {
	/* Fill out the timetable */
	'use strict';
	/* jshint validthis: true, -W041 */
	window.timetableCached = false;
	if (window.belltimes == null || belltimes.status === 'Error') {
		reloadBelltimes(); // use today.json's date.
	}
	var res;
	try {
		res = JSON.parse(this.responseText);
	}
	catch (e) {
		res = {'status': 'damn'};
		$('#left-pane .umad').text('SBHS is broken :(');
		return;
	}
	if (window.belltimes != null) {
		var lsKey = belltimes.day + belltimes.weekType;
		if (res.timetable && !res.hasVariations) {
			window.localStorage[lsKey] = this.responseText;
		}
	}
	if (!res.timetable) {
		$('#rtd-status').html('Real-time data inaccessible');
		return; // we don't want to do that... TODO refresh access token and stuff
	}
	window.todayNames = res;
	$('#rtd-status').html('Loaded real-time data');
	setTimeout(function() {
		$('#rtd-status').velocity('fadeOut');
		setTimeout(function() { $('#rtd-status').velocity('fadeOut').html('').css({'display': 'block'}); }, 1000);
	}, 3000);
	if (currentBellIndex == -1) {
		calculateUpcomingLesson();
	}
	handleLeftPane();
	if (belltimes.status !== 'Error') {
		updatePeriodLabel();
	}
	updateSidebarStatus();
}

function loadTimetable() {
	/* Get the timetable */
	'use strict';
	window.timetableCached = false;
	if (belltimes && (belltimes.day+belltimes.weekType) in window.localStorage) {
		window.timetableCached = true;
		console.log('loading from localStorage');
		window.todayNames = JSON.parse(window.localStorage[belltimes.day+belltimes.weekType]);
		setTimeout(handleLeftPane, 0);
		updateSidebarStatus();
		$('#rtd-status').html('Loading real-time data…');
	}
	else if (!getLoggedIn() && !window.todayNames) {
		console.log('umm');
		window.todayNames = {timetable: {failure: true}};
	}
	if (!getLoggedIn()) {
		$('#rtd-status').html('Log in for real-time data');
		return;
	}
	var xhr = new XMLHttpRequest();
	xhr.onload = handleTimetable;
	xhr.open('GET', '/api/today.json?stupid_proxy_caching_is_stupid', true);
	xhr.send();
}
<|endoftext|>


<commit_before>"use strict";

/**
 * Load required modules
 */
var	express = require('express'),
	bodyParser = require('body-parser'),
	cookieParser = require('cookie-parser'),
	expressSession = require('express-session'),
	path = require('path'),
	moment = require('moment'),
	events = require('events'),
	passport = require('passport'),
	LocalStrategy = require('passport-local').Strategy,
	bunyan = require('bunyan'),
    async = require('async');

/**
 * Linz constructor
 */
function Linz () {

	// internal properties
	this.settings = {};
	this.loggers = {};

}

/**
 * Expose Linz
 */
var linz = module.exports = exports = new Linz;

/**
 * Define local variables used by the Linz class
 */
var	routesManager = require('./lib/router'),
	passportHelpers = require('./lib/helpers-passport'),
	helpersModels = require('./lib/helpers-models'),
    helpersConfigs = require('./lib/helpers-configs'),
	debugModels = require('debug')('linz:models'),
    debugConfigs = require('debug')('linz:configs'),
	debugGeneral = require('debug')('linz:general'),
	debugSet = require('debug')('linz:set'),
	error = require('./lib/errors');

/**
 * Expose linz modules
 */
linz.formtools = require('./lib/formtools');
linz.middleware = require('./middleware-public');
linz.api = require('./lib/api');
linz.utils = require('./lib/utils');

/**
 * Linz inherits from EventEmitter for Linz.prototype.on('event') handling
 *
 */
 Linz.prototype.__proto__ = events.EventEmitter.prototype;

/**
 * Initialize linz with the express app
 * signature: app, mongoose, options
 *
 */
Linz.prototype.init = function () {

	var _app,
		_mongoose,
		_options;

	// loop through the arguments, and determine what is what
	for (var i = 0; i < arguments.length; i++) {

		var arg = arguments[i];

		if (arg.constructor.name === 'Mongoose') {
            debugGeneral('Using passed in mongoose object');
			_mongoose = arg;
		} else if (arg.constructor.name === 'Function' && typeof arg === 'function') {
            debugGeneral('Using passed in express app');
			_app = arg;
		} else  if (arg.constructor.name === 'Object' && typeof arg === 'object') {
            debugGeneral('Found options configuration object');
			_options = arg;
		}

	}

	// use anything that is passed in
	_app = _app || express();
	_mongoose = _mongoose || require('mongoose');
	_options = _options || {};

	// reference required properties
	this.app = _app;
	this.mongoose = _mongoose;

	// apply default linz options
	this.options(require('./lib/defaults'));

	// overlay runtime options, these will override linz defaults
	this.options(_options);

	this.on('admin path', function () {

		this.app.locals['linzNavigation'] = [
			{
				name: 'Models',
				href: this.get('admin path') + '/models/list'
			},
			{
				name: 'Logs',
				href: this.get('admin path') + '/logs/request/list'
			},
			{
				name: 'Logout',
				href: this.get('admin path') + '/logout'
			}
		];

	});

	this.configure();

	return this;

};

/**
* Initialize the application
*
* @api private
*/

Linz.prototype.configure = function() {

    var _this = this;

    async.series([

        function (cb) {
            return _this.defaultConfiguration(cb);
        },

        function (cb) {
            return _this.bootstrapExpress(cb);
        },

        function (cb) {
            return _this.mountAdmin(cb);
        },

        function (cb) {
            return _this.setupORM(cb);
        },

        function (cb) {
            return _this.loadConfigs(cb);
        },

        function (cb) {

            _this.set('app', _this.app);

            // check for either a connect(ed|ing) mongoose object, or mongoose URI
            if ([1,2].indexOf(_this.mongoose.connection.readyState) < 0) {

                if (!_this.get('mongo')) {
                    throw new Error('You must either supply a connected mongoose object, or a mongo URI');
                }

                _this.mongoose.connect(_this.get('mongo'));
                _this.mongoose.connection.on('connected', function () {
                    _this.initConfigs(cb);
                });

            } else {
                return cb(null);
            }

        },

        function (cb) {
            return _this.loadModels(cb);
        },

        function (cb) {
            return _this.bootstrapExpressLocals(cb);
        },

        function (cb) {
            return _this.checkSetup(cb);
        }

    ], function (err) {

        if (err) {
            throw err;
        }

        return _this.emit('initialised');

    });

};

/**
* Load ORM
*
* @return void
*/
Linz.prototype.setupORM = function (cb) {

    // extend the mongoose types
    helpersModels.extendTypes();

    return cb(null);

};

/**
* Search and look for the models folder, then go ahead and load them
*
* @return void
*/
Linz.prototype.loadModels = function (cb) {

	if (!this.get('load models')) {

		this.set('models', []);
        return cb(null);

	}

	// set the default models path
	this.set('models path', path.resolve(this.get('cwd'), 'models'), false);

	var modelsPath = this.get('models path');

	this.set('models', helpersModels.loadModels(modelsPath));

    return cb(null);

};

/**
* Search and look for the configs folder, then go ahead and load them
*
* @return void
*/
Linz.prototype.loadConfigs = function (cb) {

    if (!this.get('load configs')) {

        this.set('configs', []);
        return cb(null);

    }

    // set the default configs path
    this.set('configs path', path.resolve(this.get('cwd'), 'configs'), false);

    var configsPath = this.get('configs path');

    this.set('configs', helpersConfigs.loadConfigs(configsPath));

    return cb(null);

};

/**
* Load document from configs collection and create new ones if none are found.
*
* @return void
*/
Linz.prototype.initConfigs = function (cb) {

    var db = this.mongoose.connection.db,
        configs = this.get('configs');

    db.collection(this.get('configs collection name'), function (err, collection) {

        async.each(Object.keys(configs), function (configName, initDone) {

            collection.findOne({ _id: configName }, function (err, doc) {

                if (err) {
                    return initDone(err);
                }

                if (doc) {

                    var updatedDoc = {};

                    // doc exists, check if there are any new properties
                    configs[configName].schema.eachPath(function (fieldName, field) {

                        if (!doc[fieldName]) {
                            updatedDoc[fieldName] = linz.formtools.utils.getDefaultValue(field);
                            doc[fieldName] = updatedDoc[fieldName];
                        }

                    });

                    if (!Object.keys(updatedDoc).length) {

                        configs[configName].config = doc;
                        debugConfigs('Initialised config %s', configName);

                        // since there are no change, return early
                        return initDone(null);
                    }

                    // update doc with changes
                    return collection.update({ _id: configName }, { $set: updatedDoc }, { w:1 }, function (err, result) {

                        if (err) {
                            throw new Error('Unable to write config file %s to database. ' + err.message, configName);
                        }

                        // add config to linz
                        configs[configName].config = doc;

                        debugConfigs('Initialised config %s', configName);

                        return initDone(err);

                    });

                }

                var newConfig = {};

                // contruct doc from config schema
                configs[configName].schema.eachPath(function (fieldName, field) {
                    newConfig[fieldName] = linz.formtools.utils.getDefaultValue(field);
                });

                // overwrite _id field with custom id name
                newConfig['_id'] = configName;

                collection.insert(newConfig, {w:1}, function(err, result) {

                    if (err) {
                        throw new Error('Unable to write config file %s to database. ' + err.message, configName);
                    }

                    debugConfigs('Initialised config %s', configName);

                    // add new config to linz
                    configs[configName].config = newConfig;

                    return initDone(null);

                });

            });

        }, function (err) {
            return cb(err);
        });

    });

};

/**
* Setup the default configuration for Linz
*
* @return void
*/
Linz.prototype.defaultConfiguration = function (cb) {

	var _this = this;

	debugGeneral('Setting up the default configuration');

	// setup the router
	this.router = routesManager.getRouter();

	// assign the admin routes
	routesManager.setupAdminRoutes();

	// assign the model CRUD routes
	routesManager.setupModelRoutes(this.models);

    // assign the config CRUD routes
    routesManager.setupConfigsRoutes();

	// assign the logging routes
	if (this.get('request logging') === true) routesManager.setupLoggingRoutes();

	// store the cwd
	this.set('cwd', process.cwd());

	// place holder for the models, which get loaded in next
	this.set('models', []);

    return cb(null);

};

Linz.prototype.mountAdmin = function (cb) {

	debugGeneral('Mounting Linz on ' + this.get('admin path'));
	this.app.use(this.get('admin path'), this.router);

    return cb(null);
};

/**
* We need to setup the host express app with a few bits and pieces
*
* @api private
*/
Linz.prototype.bootstrapExpress = function (cb) {

	// only run once
	if (this.bBootstrapExpress) {
        return cb(null);
    }

	if (!this.bBootstrapExpress) {
        this.bBootstrapExpress = true;
    }

	debugGeneral('Bootstrapping express');

	this.app.engine('jade', require('jade').__express);
	this.app.set('view engine', 'jade');

	// setup body-parser, and cookie-parser
	this.app.use(bodyParser());
	this.app.use(cookieParser());

	// need to hook session in here as it must be before passport.initialize
	if (typeof this.get('session middleware') === 'function') {
		this.app.use(this.get('session middleware'));
	} else {
		this.app.use(expressSession({ secret: 'linzcookiesecret' }));
	}

	// initialize passport
	this.app.use(this.get('admin path'), passport.initialize());
	this.app.use(this.get('admin path'), passport.session());

	// setup stylus for admin css
	this.app.use(this.get('admin path') + '/public/', require('stylus').middleware(path.resolve(__dirname, 'public')));

	// setup admin static routes
	this.app.use(this.get('admin path') + '/public/', express.static(path.resolve(__dirname, 'public')));

	// setup passport for authentication
	passport.use('linz-local', passportHelpers.login);

	// add serialize and deserialize functionality
	passportHelpers.setupPassport(passport);

    return cb(null);

};

/**
* Setup express locals for admin template generation
*
* @api private
*/
Linz.prototype.bootstrapExpressLocals = function (cb) {

	// expose our settings to the rendering engine
	this.app.locals['linz'] = this;

	// expose the linz admin navigation
	this.app.locals['linzNavigation'] = this.buildNavigation();

	this.app.locals['adminPath'] = this.get('admin path');

	this.app.locals['adminTitle'] = this.get('admin title');

    this.app.locals['env'] = process.env.NODE_ENV || 'development';

    return cb(null);

};

Linz.prototype.options = function (opts) {

	var _this = this,
		opts = opts || {};

	for (var opt in opts) {
		_this.set(opt, opts[opt]);
	}

};

/**
* Set a value or setting on Linz
*
* @return Linz for chaining
*/

Linz.prototype.set = function (setting, val, override) {

	if (arguments.length === 1) {
		return this.settings[setting];
	}

	// can only set the following values once
	var onceOnly = ['models path'];

	if (onceOnly.indexOf(setting) >= 0 && this.settings[setting] !== undefined) {
		return debugSet('You can only set \'' + setting + '\' once. Ignoring set this time.');
	}

	// defaults to true
	override = !(override === false);

	// override unless previously set
	if (override || override === false && this.settings[setting] === undefined) {
		debugSet('Setting ' + setting + ' to ' + val);
		this.settings[setting] = val;
		this.emit(setting, val);
	}

	return this;

};

/**
* Get a value or setting on Linz
*
* @return setting value
*/

Linz.prototype.get = function (setting) {
	return this.settings[setting];
};

/*
* Set a settings value to true
* synonym for admin.set('setting', true);
* @return Linz for chaining
*/

Linz.prototype.enable = function (setting) {
	this.set(setting, true);
	return this;
};

/*
* Set a settings value to false
* synonym for admin.set('setting', false);
* @return Linz for chaining
*/
Linz.prototype.disable = function (setting) {
	this.set(setting, false);
	return this;
};

/*
* Return the linz navigation structure
*
* @api private
*/
Linz.prototype.buildNavigation = function () {

	var nav = [],
		_this = this,
		linzModels = this.get('models');

	// add a reference for all of the models
	var models = {
		name: 'Models',
		href: this.get('admin path') + '/models/list',
		children: [
			{
				'name': 'All',
				href: this.get('admin path') + '/models/list'
			}
		]
	};

	// add each model to the navigation tree
	Object.keys(linzModels).forEach(function (model) {

		models.children.push({
			name: linzModels[model].label,
			href: _this.get('admin path') + '/model/' + model + '/list'
		});

	});
	nav.push(models);

    // add a reference for configs
    var configs = {
        name: 'Configs',
        href: this.get('admin path') + '/configs/list'
    };
    nav.push(configs);

	// add a reference for the logs
	var logs = {
		name: 'Logs',
		href: this.get('admin path') + '/logs/request/list'
	};
	nav.push(logs);

	return nav;

}

/**
 * Check the setup of linz and make sure we've got everything we require
 * @return {[type]} [description]
 */
Linz.prototype.checkSetup = function(cb) {

	if (this.get('user model') === undefined) {
		error.log('You must define a user model');
	}

    return cb(null);

};

/**
* Create a logger which you can use in your applications.
* Linz uses bunyan internally and exposes this method to easily create logs within your application.
*
* @param {String|Object} options
* @return an instance of the logger
* @api public
*/
Linz.prototype.logger = function (options) {

	// user can pass either a String or an Object
	if (typeof options === 'string') options = {name: options};

	// The name parameter must exist
	if (!options.name) throw new Error('You must pass the name parameter when creating a logger.');

	// If we haven't already created a logger, create one now, transparently passing the options
	if (!this.loggers[options.name]) {
		this.loggers[options.name] = bunyan.createLogger(options);
	}

	return this.loggers[options.name];

};
<commit_msg>Fixed an issue in which falsy config fields, would be incorrectly overwritten upon application launch.<commit_after>"use strict";

/**
 * Load required modules
 */
var	express = require('express'),
	bodyParser = require('body-parser'),
	cookieParser = require('cookie-parser'),
	expressSession = require('express-session'),
	path = require('path'),
	moment = require('moment'),
	events = require('events'),
	passport = require('passport'),
	LocalStrategy = require('passport-local').Strategy,
	bunyan = require('bunyan'),
    async = require('async');

/**
 * Linz constructor
 */
function Linz () {

	// internal properties
	this.settings = {};
	this.loggers = {};

}

/**
 * Expose Linz
 */
var linz = module.exports = exports = new Linz;

/**
 * Define local variables used by the Linz class
 */
var	routesManager = require('./lib/router'),
	passportHelpers = require('./lib/helpers-passport'),
	helpersModels = require('./lib/helpers-models'),
    helpersConfigs = require('./lib/helpers-configs'),
	debugModels = require('debug')('linz:models'),
    debugConfigs = require('debug')('linz:configs'),
	debugGeneral = require('debug')('linz:general'),
	debugSet = require('debug')('linz:set'),
	error = require('./lib/errors');

/**
 * Expose linz modules
 */
linz.formtools = require('./lib/formtools');
linz.middleware = require('./middleware-public');
linz.api = require('./lib/api');
linz.utils = require('./lib/utils');

/**
 * Linz inherits from EventEmitter for Linz.prototype.on('event') handling
 *
 */
 Linz.prototype.__proto__ = events.EventEmitter.prototype;

/**
 * Initialize linz with the express app
 * signature: app, mongoose, options
 *
 */
Linz.prototype.init = function () {

	var _app,
		_mongoose,
		_options;

	// loop through the arguments, and determine what is what
	for (var i = 0; i < arguments.length; i++) {

		var arg = arguments[i];

		if (arg.constructor.name === 'Mongoose') {
            debugGeneral('Using passed in mongoose object');
			_mongoose = arg;
		} else if (arg.constructor.name === 'Function' && typeof arg === 'function') {
            debugGeneral('Using passed in express app');
			_app = arg;
		} else  if (arg.constructor.name === 'Object' && typeof arg === 'object') {
            debugGeneral('Found options configuration object');
			_options = arg;
		}

	}

	// use anything that is passed in
	_app = _app || express();
	_mongoose = _mongoose || require('mongoose');
	_options = _options || {};

	// reference required properties
	this.app = _app;
	this.mongoose = _mongoose;

	// apply default linz options
	this.options(require('./lib/defaults'));

	// overlay runtime options, these will override linz defaults
	this.options(_options);

	this.on('admin path', function () {

		this.app.locals['linzNavigation'] = [
			{
				name: 'Models',
				href: this.get('admin path') + '/models/list'
			},
			{
				name: 'Logs',
				href: this.get('admin path') + '/logs/request/list'
			},
			{
				name: 'Logout',
				href: this.get('admin path') + '/logout'
			}
		];

	});

	this.configure();

	return this;

};

/**
* Initialize the application
*
* @api private
*/

Linz.prototype.configure = function() {

    var _this = this;

    async.series([

        function (cb) {
            return _this.defaultConfiguration(cb);
        },

        function (cb) {
            return _this.bootstrapExpress(cb);
        },

        function (cb) {
            return _this.mountAdmin(cb);
        },

        function (cb) {
            return _this.setupORM(cb);
        },

        function (cb) {
            return _this.loadConfigs(cb);
        },

        function (cb) {

            _this.set('app', _this.app);

            // check for either a connect(ed|ing) mongoose object, or mongoose URI
            if ([1,2].indexOf(_this.mongoose.connection.readyState) < 0) {

                if (!_this.get('mongo')) {
                    throw new Error('You must either supply a connected mongoose object, or a mongo URI');
                }

                _this.mongoose.connect(_this.get('mongo'));
                _this.mongoose.connection.on('connected', function () {
                    _this.initConfigs(cb);
                });

            } else {
                return cb(null);
            }

        },

        function (cb) {
            return _this.loadModels(cb);
        },

        function (cb) {
            return _this.bootstrapExpressLocals(cb);
        },

        function (cb) {
            return _this.checkSetup(cb);
        }

    ], function (err) {

        if (err) {
            throw err;
        }

        return _this.emit('initialised');

    });

};

/**
* Load ORM
*
* @return void
*/
Linz.prototype.setupORM = function (cb) {

    // extend the mongoose types
    helpersModels.extendTypes();

    return cb(null);

};

/**
* Search and look for the models folder, then go ahead and load them
*
* @return void
*/
Linz.prototype.loadModels = function (cb) {

	if (!this.get('load models')) {

		this.set('models', []);
        return cb(null);

	}

	// set the default models path
	this.set('models path', path.resolve(this.get('cwd'), 'models'), false);

	var modelsPath = this.get('models path');

	this.set('models', helpersModels.loadModels(modelsPath));

    return cb(null);

};

/**
* Search and look for the configs folder, then go ahead and load them
*
* @return void
*/
Linz.prototype.loadConfigs = function (cb) {

    if (!this.get('load configs')) {

        this.set('configs', []);
        return cb(null);

    }

    // set the default configs path
    this.set('configs path', path.resolve(this.get('cwd'), 'configs'), false);

    var configsPath = this.get('configs path');

    this.set('configs', helpersConfigs.loadConfigs(configsPath));

    return cb(null);

};

/**
* Load document from configs collection and create new ones if none are found.
*
* @return void
*/
Linz.prototype.initConfigs = function (cb) {

    var db = this.mongoose.connection.db,
        configs = this.get('configs');

    db.collection(this.get('configs collection name'), function (err, collection) {

        async.each(Object.keys(configs), function (configName, initDone) {

            collection.findOne({ _id: configName }, function (err, doc) {

                if (err) {
                    return initDone(err);
                }

                if (doc) {

                    var updatedDoc = {};

                    // doc exists, check if there are any new properties
                    configs[configName].schema.eachPath(function (fieldName, field) {

                        if (doc[fieldName] === undefined) {
                            updatedDoc[fieldName] = linz.formtools.utils.getDefaultValue(field);
                            doc[fieldName] = updatedDoc[fieldName];
                        }

                    });

                    if (!Object.keys(updatedDoc).length) {

                        configs[configName].config = doc;
                        debugConfigs('Initialised config %s', configName);

                        // since there are no change, return early
                        return initDone(null);
                    }

                    // update doc with changes
                    return collection.update({ _id: configName }, { $set: updatedDoc }, { w:1 }, function (err, result) {

                        if (err) {
                            throw new Error('Unable to write config file %s to database. ' + err.message, configName);
                        }

                        // add config to linz
                        configs[configName].config = doc;

                        debugConfigs('Initialised config %s', configName);

                        return initDone(err);

                    });

                }

                var newConfig = {};

                // contruct doc from config schema
                configs[configName].schema.eachPath(function (fieldName, field) {
                    newConfig[fieldName] = linz.formtools.utils.getDefaultValue(field);
                });

                // overwrite _id field with custom id name
                newConfig['_id'] = configName;

                collection.insert(newConfig, {w:1}, function(err, result) {

                    if (err) {
                        throw new Error('Unable to write config file %s to database. ' + err.message, configName);
                    }

                    debugConfigs('Initialised config %s', configName);

                    // add new config to linz
                    configs[configName].config = newConfig;

                    return initDone(null);

                });

            });

        }, function (err) {
            return cb(err);
        });

    });

};

/**
* Setup the default configuration for Linz
*
* @return void
*/
Linz.prototype.defaultConfiguration = function (cb) {

	var _this = this;

	debugGeneral('Setting up the default configuration');

	// setup the router
	this.router = routesManager.getRouter();

	// assign the admin routes
	routesManager.setupAdminRoutes();

	// assign the model CRUD routes
	routesManager.setupModelRoutes(this.models);

    // assign the config CRUD routes
    routesManager.setupConfigsRoutes();

	// assign the logging routes
	if (this.get('request logging') === true) routesManager.setupLoggingRoutes();

	// store the cwd
	this.set('cwd', process.cwd());

	// place holder for the models, which get loaded in next
	this.set('models', []);

    return cb(null);

};

Linz.prototype.mountAdmin = function (cb) {

	debugGeneral('Mounting Linz on ' + this.get('admin path'));
	this.app.use(this.get('admin path'), this.router);

    return cb(null);
};

/**
* We need to setup the host express app with a few bits and pieces
*
* @api private
*/
Linz.prototype.bootstrapExpress = function (cb) {

	// only run once
	if (this.bBootstrapExpress) {
        return cb(null);
    }

	if (!this.bBootstrapExpress) {
        this.bBootstrapExpress = true;
    }

	debugGeneral('Bootstrapping express');

	this.app.engine('jade', require('jade').__express);
	this.app.set('view engine', 'jade');

	// setup body-parser, and cookie-parser
	this.app.use(bodyParser());
	this.app.use(cookieParser());

	// need to hook session in here as it must be before passport.initialize
	if (typeof this.get('session middleware') === 'function') {
		this.app.use(this.get('session middleware'));
	} else {
		this.app.use(expressSession({ secret: 'linzcookiesecret' }));
	}

	// initialize passport
	this.app.use(this.get('admin path'), passport.initialize());
	this.app.use(this.get('admin path'), passport.session());

	// setup stylus for admin css
	this.app.use(this.get('admin path') + '/public/', require('stylus').middleware(path.resolve(__dirname, 'public')));

	// setup admin static routes
	this.app.use(this.get('admin path') + '/public/', express.static(path.resolve(__dirname, 'public')));

	// setup passport for authentication
	passport.use('linz-local', passportHelpers.login);

	// add serialize and deserialize functionality
	passportHelpers.setupPassport(passport);

    return cb(null);

};

/**
* Setup express locals for admin template generation
*
* @api private
*/
Linz.prototype.bootstrapExpressLocals = function (cb) {

	// expose our settings to the rendering engine
	this.app.locals['linz'] = this;

	// expose the linz admin navigation
	this.app.locals['linzNavigation'] = this.buildNavigation();

	this.app.locals['adminPath'] = this.get('admin path');

	this.app.locals['adminTitle'] = this.get('admin title');

    this.app.locals['env'] = process.env.NODE_ENV || 'development';

    return cb(null);

};

Linz.prototype.options = function (opts) {

	var _this = this,
		opts = opts || {};

	for (var opt in opts) {
		_this.set(opt, opts[opt]);
	}

};

/**
* Set a value or setting on Linz
*
* @return Linz for chaining
*/

Linz.prototype.set = function (setting, val, override) {

	if (arguments.length === 1) {
		return this.settings[setting];
	}

	// can only set the following values once
	var onceOnly = ['models path'];

	if (onceOnly.indexOf(setting) >= 0 && this.settings[setting] !== undefined) {
		return debugSet('You can only set \'' + setting + '\' once. Ignoring set this time.');
	}

	// defaults to true
	override = !(override === false);

	// override unless previously set
	if (override || override === false && this.settings[setting] === undefined) {
		debugSet('Setting ' + setting + ' to ' + val);
		this.settings[setting] = val;
		this.emit(setting, val);
	}

	return this;

};

/**
* Get a value or setting on Linz
*
* @return setting value
*/

Linz.prototype.get = function (setting) {
	return this.settings[setting];
};

/*
* Set a settings value to true
* synonym for admin.set('setting', true);
* @return Linz for chaining
*/

Linz.prototype.enable = function (setting) {
	this.set(setting, true);
	return this;
};

/*
* Set a settings value to false
* synonym for admin.set('setting', false);
* @return Linz for chaining
*/
Linz.prototype.disable = function (setting) {
	this.set(setting, false);
	return this;
};

/*
* Return the linz navigation structure
*
* @api private
*/
Linz.prototype.buildNavigation = function () {

	var nav = [],
		_this = this,
		linzModels = this.get('models');

	// add a reference for all of the models
	var models = {
		name: 'Models',
		href: this.get('admin path') + '/models/list',
		children: [
			{
				'name': 'All',
				href: this.get('admin path') + '/models/list'
			}
		]
	};

	// add each model to the navigation tree
	Object.keys(linzModels).forEach(function (model) {

		models.children.push({
			name: linzModels[model].label,
			href: _this.get('admin path') + '/model/' + model + '/list'
		});

	});
	nav.push(models);

    // add a reference for configs
    var configs = {
        name: 'Configs',
        href: this.get('admin path') + '/configs/list'
    };
    nav.push(configs);

	// add a reference for the logs
	var logs = {
		name: 'Logs',
		href: this.get('admin path') + '/logs/request/list'
	};
	nav.push(logs);

	return nav;

}

/**
 * Check the setup of linz and make sure we've got everything we require
 * @return {[type]} [description]
 */
Linz.prototype.checkSetup = function(cb) {

	if (this.get('user model') === undefined) {
		error.log('You must define a user model');
	}

    return cb(null);

};

/**
* Create a logger which you can use in your applications.
* Linz uses bunyan internally and exposes this method to easily create logs within your application.
*
* @param {String|Object} options
* @return an instance of the logger
* @api public
*/
Linz.prototype.logger = function (options) {

	// user can pass either a String or an Object
	if (typeof options === 'string') options = {name: options};

	// The name parameter must exist
	if (!options.name) throw new Error('You must pass the name parameter when creating a logger.');

	// If we haven't already created a logger, create one now, transparently passing the options
	if (!this.loggers[options.name]) {
		this.loggers[options.name] = bunyan.createLogger(options);
	}

	return this.loggers[options.name];

};
<|endoftext|>


<commit_before>/*global tinymce, module, require, define, global, self */

;(function (f) {
  'use strict';

  // CommonJS
  if (typeof exports === 'object' && typeof module !== 'undefined') {
    module.exports = f(require('jquery'));

  // RequireJS
  } else if (typeof define === 'function'  && define.amd) {
    define(['jquery'], f);

  //