<commit_before>const omit = require('lodash.omit');
const Proto = require('uberproto');
const errors = require('@feathersjs/errors');
const { select, filterQuery } = require('@feathersjs/commons');

const utils = require('./utils');

class Service {
  constructor (options) {
    if (!options) {
      throw new Error('Sequelize options have to be provided');
    }

    if (!options.Model) {
      throw new Error('You must provide a Sequelize Model');
    }
    this.options = options;
    this.paginate = options.paginate || {};
    this.Model = options.Model;
    this.id = options.id || 'id';
    this.events = options.events;
    this.raw = options.raw !== false;
  }

  getModel (params) {
    return this.Model;
  }

  applyScope (params) {
    if ((params.sequelize || {}).scope) {
      return this.getModel(params).scope(params.sequelize.scope);
    }
    return this.getModel(params);
  }

  extend (obj) {
    return Proto.extend(obj, this);
  }

  _find (params, getFilter = filterQuery, paginate) {
    const { filters, query } = getFilter(params.query || {});
    const where = utils.getWhere(query);
    const order = utils.getOrder(filters.$sort);

    const q = Object.assign({
      where,
      order,
      limit: filters.$limit,
      offset: filters.$skip,
      raw: this.raw,
      distinct: true
    }, params.sequelize);

    if (filters.$select) {
      q.attributes = filters.$select;
    }

    let Model = this.applyScope(params);

    // Until Sequelize fix all the findAndCount issues, a few 'hacks' are needed to get the total count correct

    // Adding an empty include changes the way the count is done
    // See: https://github.com/sequelize/sequelize/blob/7e441a6a5ca44749acd3567b59b1d6ceb06ae64b/lib/model.js#L1780-L1782
    q.include = q.include || [];

    // Non-raw is the default but setting it manually breaks paging
    // See: https://github.com/sequelize/sequelize/issues/7931
    if (q.raw === false) {
      delete q.raw;
    }

    if (paginate) {
      return Model.findAndCountAll(q).then(result => {
        return {
          total: result.count,
          limit: filters.$limit,
          skip: filters.$skip || 0,
          data: result.rows
        };
      }).catch(utils.errorHandler);
    } else {
      return Model.findAll(q).then(result => {
        return {
          data: result
        };
      }).catch(utils.errorHandler);
    }
  }

  find (params) {
    const paginate = (params && typeof params.paginate !== 'undefined') ? params.paginate : this.paginate;
    const result = this._find(params, where => filterQuery(where, paginate), paginate);

    if (!paginate.default) {
      return result.then(page => page.data);
    }

    return result;
  }

  _get (id, params) {
    const where = utils.getWhere(params.query);

    // Attach 'where' constraints, if any were used.
    const q = Object.assign({
      raw: this.raw,
      where: Object.assign({[this.id]: id}, where)
    }, params.sequelize);

    let Model = this.applyScope(params);

    // findById calls findAll under the hood. We use findAll so that
    // eager loading can be used without a separate code path.
    return Model.findAll(q).then(result => {
      if (result.length === 0) {
        throw new errors.NotFound(`No record found for id '${id}'`);
      }

      return result[0];
    })
      .then(select(params, this.id))
      .catch(error => {
        throw new errors.NotFound(`No record found for id '${id}'`, error);
      });
  }

  // returns either the model intance for an id or all unpaginated
  // items for `params` if id is null
  _getOrFind (id, params) {
    if (id === null) {
      return this._find(params).then(page => page.data);
    }

    return this._get(id, params);
  }

  get (id, params) {
    return this._get(id, params).then(select(params, this.id));
  }

  create (data, params) {
    const options = Object.assign({raw: this.raw}, params.sequelize);
    // Model.create's `raw` option is different from other methods.
    // In order to use `raw` consistently to serialize the result,
    // we need to shadow the Model.create use of raw, which we provide
    // access to by specifying `ignoreSetters`.
    const ignoreSetters = Boolean(options.ignoreSetters);
    const createOptions = Object.assign({}, options, {raw: ignoreSetters});
    const isArray = Array.isArray(data);
    let promise;

    let Model = this.applyScope(params);

    if (isArray) {
      promise = Model.bulkCreate(data, createOptions);
    } else {
      promise = Model.create(data, createOptions);
    }

    return promise.then(result => {
      const sel = select(params, this.id);
      if (options.raw === false) {
        return result;
      }
      if (isArray) {
        return result.map(item => sel(item.toJSON()));
      }
      return sel(result.toJSON());
    }).catch(utils.errorHandler);
  }

  patch (id, data, params) {
    const where = Object.assign({}, filterQuery(params.query || {}).query);
    const mapIds = page => page.data.map(current => current[this.id]);

    if (id !== null) {
      where[this.id] = id;
    }

    const options = Object.assign({raw: this.raw}, params.sequelize, { where });

    let Model = this.applyScope(params);

    // This is the best way to implement patch in sql, the other dialects 'should' use a transaction.
    if (Model.sequelize.options.dialect === 'postgres' && params.$returning !== false) {
      options.returning = true;

      return this._getOrFind(id, params)
        .then(results => this.getModel(params).update(omit(data, this.id), options))
        .then(results => {
          if (id === null) {
            return results[1];
          }

          if (!results[1].length) {
            throw new errors.NotFound(`No record found for id '${id}'`);
          }

          return results[1][0];
        })
        .then(select(params, this.id))
        .catch(utils.errorHandler);
    }

    // By default we will just query for the one id. For multi patch
    // we create a list of the ids of all items that will be changed
    // to re-query them after the update
    const ids = id === null ? this._find(params)
      .then(mapIds) : Promise.resolve([ id ]);

    return ids
      .then(idList => {
        // Create a new query that re-queries all ids that
        // were originally changed
        const findParams = Object.assign({}, params, {
          query: { [this.id]: { $in: idList } }
        });

        return Model.update(omit(data, this.id), options)
          .then(() => {
            if (params.$returning !== false) {
              return this._getOrFind(id, findParams);
            } else {
              return Promise.resolve([]);
            }
          });
      })
      .then(select(params, this.id))
      .catch(utils.errorHandler);
  }

  update (id, data, params) {
    const where = Object.assign({}, filterQuery(params.query || {}).query);
    const options = Object.assign({ raw: this.raw }, params.sequelize);

    if (Array.isArray(data)) {
      return Promise.reject(new errors.BadRequest('Not replacing multiple records. Did you mean `patch`?'));
    }

    // Force the {raw: false} option as the instance is needed to properly
    // update

    return this._get(id, { sequelize: { raw: false }, query: where }).then(instance => {
      if (!instance) {
        throw new errors.NotFound(`No record found for id '${id}'`);
      }

      let copy = {};
      Object.keys(instance.toJSON()).forEach(key => {
        if (typeof data[key] === 'undefined') {
          copy[key] = null;
        } else {
          copy[key] = data[key];
        }
      });

      return instance.update(copy, {raw: false}).then(() => this._get(id, {sequelize: options}));
    })
      .then(select(params, this.id))
      .catch(utils.errorHandler);
  }

  remove (id, params) {
    const opts = Object.assign({ raw: this.raw }, params);
    const where = Object.assign({}, filterQuery(params.query || {}).query);

    if (id !== null) {
      where[this.id] = id;
    }

    const options = Object.assign({}, params.sequelize, { where });

    let Model = this.applyScope(params);

    if (params.$returning !== false) {
      return this._getOrFind(id, opts).then(data => {
        return Model.destroy(options).then(() => data);
      })
        .then(select(params, this.id))
        .catch(utils.errorHandler);
    } else {
      return Model.destroy(options).then(() => Promise.resolve([]))
        .then(select(params, this.id))
        .catch(utils.errorHandler);
    }
  }
}

function init (options) {
  return new Service(options);
};

module.exports = init;

// Exposed Modules
Object.assign(module.exports, {
  default: init,
  Service
});
<commit_msg>Default paginate option value set to false instead of empty object (#217)<commit_after>const omit = require('lodash.omit');
const Proto = require('uberproto');
const errors = require('@feathersjs/errors');
const { select, filterQuery } = require('@feathersjs/commons');

const utils = require('./utils');

class Service {
  constructor (options) {
    if (!options) {
      throw new Error('Sequelize options have to be provided');
    }

    if (!options.Model) {
      throw new Error('You must provide a Sequelize Model');
    }

    this.paginate = options.paginate || false;
    this.options = options;
    this.Model = options.Model;
    this.id = options.id || 'id';
    this.events = options.events;
    this.raw = options.raw !== false;
  }

  getModel (params) {
    return this.Model;
  }

  applyScope (params) {
    if ((params.sequelize || {}).scope) {
      return this.getModel(params).scope(params.sequelize.scope);
    }
    return this.getModel(params);
  }

  extend (obj) {
    return Proto.extend(obj, this);
  }

  _find (params, getFilter = filterQuery, paginate) {
    const { filters, query } = getFilter(params.query || {});
    const where = utils.getWhere(query);
    const order = utils.getOrder(filters.$sort);

    const q = Object.assign({
      where,
      order,
      limit: filters.$limit,
      offset: filters.$skip,
      raw: this.raw,
      distinct: true
    }, params.sequelize);

    if (filters.$select) {
      q.attributes = filters.$select;
    }

    let Model = this.applyScope(params);

    // Until Sequelize fix all the findAndCount issues, a few 'hacks' are needed to get the total count correct

    // Adding an empty include changes the way the count is done
    // See: https://github.com/sequelize/sequelize/blob/7e441a6a5ca44749acd3567b59b1d6ceb06ae64b/lib/model.js#L1780-L1782
    q.include = q.include || [];

    // Non-raw is the default but setting it manually breaks paging
    // See: https://github.com/sequelize/sequelize/issues/7931
    if (q.raw === false) {
      delete q.raw;
    }

    if (paginate) {
      return Model.findAndCountAll(q).then(result => {
        return {
          total: result.count,
          limit: filters.$limit,
          skip: filters.$skip || 0,
          data: result.rows
        };
      }).catch(utils.errorHandler);
    } else {
      return Model.findAll(q).then(result => {
        return {
          data: result
        };
      }).catch(utils.errorHandler);
    }
  }

  find (params) {
    const paginate = (params && typeof params.paginate !== 'undefined') ? params.paginate : this.paginate;
    const result = this._find(params, where => filterQuery(where, paginate), paginate);

    if (!paginate.default) {
      return result.then(page => page.data);
    }

    return result;
  }

  _get (id, params) {
    const where = utils.getWhere(params.query);

    // Attach 'where' constraints, if any were used.
    const q = Object.assign({
      raw: this.raw,
      where: Object.assign({[this.id]: id}, where)
    }, params.sequelize);

    let Model = this.applyScope(params);

    // findById calls findAll under the hood. We use findAll so that
    // eager loading can be used without a separate code path.
    return Model.findAll(q).then(result => {
      if (result.length === 0) {
        throw new errors.NotFound(`No record found for id '${id}'`);
      }

      return result[0];
    })
      .then(select(params, this.id))
      .catch(error => {
        throw new errors.NotFound(`No record found for id '${id}'`, error);
      });
  }

  // returns either the model intance for an id or all unpaginated
  // items for `params` if id is null
  _getOrFind (id, params) {
    if (id === null) {
      return this._find(params).then(page => page.data);
    }

    return this._get(id, params);
  }

  get (id, params) {
    return this._get(id, params).then(select(params, this.id));
  }

  create (data, params) {
    const options = Object.assign({raw: this.raw}, params.sequelize);
    // Model.create's `raw` option is different from other methods.
    // In order to use `raw` consistently to serialize the result,
    // we need to shadow the Model.create use of raw, which we provide
    // access to by specifying `ignoreSetters`.
    const ignoreSetters = Boolean(options.ignoreSetters);
    const createOptions = Object.assign({}, options, {raw: ignoreSetters});
    const isArray = Array.isArray(data);
    let promise;

    let Model = this.applyScope(params);

    if (isArray) {
      promise = Model.bulkCreate(data, createOptions);
    } else {
      promise = Model.create(data, createOptions);
    }

    return promise.then(result => {
      const sel = select(params, this.id);
      if (options.raw === false) {
        return result;
      }
      if (isArray) {
        return result.map(item => sel(item.toJSON()));
      }
      return sel(result.toJSON());
    }).catch(utils.errorHandler);
  }

  patch (id, data, params) {
    const where = Object.assign({}, filterQuery(params.query || {}).query);
    const mapIds = page => page.data.map(current => current[this.id]);

    if (id !== null) {
      where[this.id] = id;
    }

    const options = Object.assign({raw: this.raw}, params.sequelize, { where });

    let Model = this.applyScope(params);

    // This is the best way to implement patch in sql, the other dialects 'should' use a transaction.
    if (Model.sequelize.options.dialect === 'postgres' && params.$returning !== false) {
      options.returning = true;

      return this._getOrFind(id, params)
        .then(results => this.getModel(params).update(omit(data, this.id), options))
        .then(results => {
          if (id === null) {
            return results[1];
          }

          if (!results[1].length) {
            throw new errors.NotFound(`No record found for id '${id}'`);
          }

          return results[1][0];
        })
        .then(select(params, this.id))
        .catch(utils.errorHandler);
    }

    // By default we will just query for the one id. For multi patch
    // we create a list of the ids of all items that will be changed
    // to re-query them after the update
    const ids = id === null ? this._find(params)
      .then(mapIds) : Promise.resolve([ id ]);

    return ids
      .then(idList => {
        // Create a new query that re-queries all ids that
        // were originally changed
        const findParams = Object.assign({}, params, {
          query: { [this.id]: { $in: idList } }
        });

        return Model.update(omit(data, this.id), options)
          .then(() => {
            if (params.$returning !== false) {
              return this._getOrFind(id, findParams);
            } else {
              return Promise.resolve([]);
            }
          });
      })
      .then(select(params, this.id))
      .catch(utils.errorHandler);
  }

  update (id, data, params) {
    const where = Object.assign({}, filterQuery(params.query || {}).query);
    const options = Object.assign({ raw: this.raw }, params.sequelize);

    if (Array.isArray(data)) {
      return Promise.reject(new errors.BadRequest('Not replacing multiple records. Did you mean `patch`?'));
    }

    // Force the {raw: false} option as the instance is needed to properly
    // update

    return this._get(id, { sequelize: { raw: false }, query: where }).then(instance => {
      if (!instance) {
        throw new errors.NotFound(`No record found for id '${id}'`);
      }

      let copy = {};
      Object.keys(instance.toJSON()).forEach(key => {
        if (typeof data[key] === 'undefined') {
          copy[key] = null;
        } else {
          copy[key] = data[key];
        }
      });

      return instance.update(copy, {raw: false}).then(() => this._get(id, {sequelize: options}));
    })
      .then(select(params, this.id))
      .catch(utils.errorHandler);
  }

  remove (id, params) {
    const opts = Object.assign({ raw: this.raw }, params);
    const where = Object.assign({}, filterQuery(params.query || {}).query);

    if (id !== null) {
      where[this.id] = id;
    }

    const options = Object.assign({}, params.sequelize, { where });

    let Model = this.applyScope(params);

    if (params.$returning !== false) {
      return this._getOrFind(id, opts).then(data => {
        return Model.destroy(options).then(() => data);
      })
        .then(select(params, this.id))
        .catch(utils.errorHandler);
    } else {
      return Model.destroy(options).then(() => Promise.resolve([]))
        .then(select(params, this.id))
        .catch(utils.errorHandler);
    }
  }
}

function init (options) {
  return new Service(options);
}

module.exports = init;

// Exposed Modules
Object.assign(module.exports, {
  default: init,
  Service
});
<|endoftext|>


<commit_before>'use strict';

var path = require('path');
var gulp = require('gulp');
var concat = require('gulp-concat');
var rename = require('gulp-rename');
var minifyCss = require('gulp-clean-css');
var prefixer = require('gulp-autoprefixer');
var sourcemaps = require('gulp-sourcemaps');
var less = require('gulp-less');
var watch = require('gulp-watch');

var frontSrcDir = path.join(__dirname, '/app/front');
var frontScriptsDir = path.join(frontSrcDir, '/scripts');
var frontStylesDir = path.join(frontSrcDir, '/styles');
var frontAssetsDir = path.join(frontSrcDir, '/assets');

var publicDir = path.join(__dirname, '/app/public');
var publicStylesDir = path.join(publicDir, '/styles');
var publicFontsDir = path.join(publicDir, '/fonts');
var publicAssetsDir = path.join(publicDir, '/assets');

var viewsDir = path.join(__dirname, '/app/views');
var servicesDir = path.join(__dirname, '/app/services');

var nodeModulesDir = path.join(__dirname, '/node_modules');

gulp.task('default', [
  'styles',
  'assets'
]);

gulp.task('watch', ['default'], function() {
  var files = [
    path.join(frontScriptsDir, '/**/*.js'),
    path.join(servicesDir, '/**/*.js'),
    path.join(frontStylesDir, '/**/*.less'),
    path.join(viewsDir, '/**/*.html')
  ];
  watch(files, {usePolling: true}, function() {
    gulp.start('default');
  });
});

// Styles

gulp.task('styles', [
  'styles.vendor',
  'styles.application'
]);

gulp.task('styles.application', function() {
  var files = [
    path.join(frontStylesDir, '/styles.less')
  ];
  return gulp.src(files)
    .pipe(sourcemaps.init())
    .pipe(less())
    .pipe(prefixer({browsers: ['last 4 versions']}))
    .pipe(minifyCss({compatibility: 'ie8'}))
    .pipe(concat('app.css'))
    .pipe(sourcemaps.write())
    .pipe(gulp.dest(publicStylesDir));
});

gulp.task('styles.vendor', function() {
  var files = [
    path.join(nodeModulesDir, '/font-awesome/css/font-awesome.min.css'),
    path.join(nodeModulesDir, '/os-bootstrap/dist/css/os-bootstrap.min.css'),
    path.join(nodeModulesDir, '/angular/angular-csp.css'),
    path.join(nodeModulesDir, '/c3/c3.min.css'),
    path.join(nodeModulesDir, '/typeahead.js-bootstrap-css/typeaheadjs.css'),
    path.join(nodeModulesDir, '/goodtables-ui/dist/goodtables-ui.min.css')
  ];
  return gulp.src(files)
    .pipe(concat('vendor.css'))
    .pipe(gulp.dest(publicStylesDir));
});

// Assets

gulp.task('assets', [
  'assets.fonts',
  'assets.images',
  'assets.favicon'
]);

gulp.task('assets.fonts', function() {
  var files = [
    path.join(nodeModulesDir, '/font-awesome/fonts/*'),
    path.join(nodeModulesDir, '/os-bootstrap/dist/fonts/*')
  ];
  return gulp.src(files)
    .pipe(gulp.dest(publicFontsDir));
});

gulp.task('assets.images', function() {
  var files = [
    path.join(frontAssetsDir, '/**/*'),
    path.join(nodeModulesDir,
      '/os-bootstrap/dist/assets/os-branding/vector/light/os.svg'),
    path.join(nodeModulesDir,
      '/os-bootstrap/dist/assets/os-branding/vector/light/packager.svg'),
    path.join(nodeModulesDir,
      '/os-bootstrap/dist/assets/os-branding/vector/light/ospackager.svg')
  ];
  return gulp.src(files)
    .pipe(gulp.dest(publicAssetsDir));
});

gulp.task('assets.favicon', function() {
  var files = [
    path.join(nodeModulesDir,
      '/os-bootstrap/dist/assets/os-branding/packager-favicon.ico')
  ];
  return gulp.src(files)
    .pipe(rename('favicon.ico'))
    .pipe(gulp.dest(publicDir));
});
<commit_msg>Remove /c3 from gulp task. Not used in os-packager<commit_after>'use strict';

var path = require('path');
var gulp = require('gulp');
var concat = require('gulp-concat');
var rename = require('gulp-rename');
var minifyCss = require('gulp-clean-css');
var prefixer = require('gulp-autoprefixer');
var sourcemaps = require('gulp-sourcemaps');
var less = require('gulp-less');
var watch = require('gulp-watch');

var frontSrcDir = path.join(__dirname, '/app/front');
var frontScriptsDir = path.join(frontSrcDir, '/scripts');
var frontStylesDir = path.join(frontSrcDir, '/styles');
var frontAssetsDir = path.join(frontSrcDir, '/assets');

var publicDir = path.join(__dirname, '/app/public');
var publicStylesDir = path.join(publicDir, '/styles');
var publicFontsDir = path.join(publicDir, '/fonts');
var publicAssetsDir = path.join(publicDir, '/assets');

var viewsDir = path.join(__dirname, '/app/views');
var servicesDir = path.join(__dirname, '/app/services');

var nodeModulesDir = path.join(__dirname, '/node_modules');

gulp.task('default', [
  'styles',
  'assets'
]);

gulp.task('watch', ['default'], function() {
  var files = [
    path.join(frontScriptsDir, '/**/*.js'),
    path.join(servicesDir, '/**/*.js'),
    path.join(frontStylesDir, '/**/*.less'),
    path.join(viewsDir, '/**/*.html')
  ];
  watch(files, {usePolling: true}, function() {
    gulp.start('default');
  });
});

// Styles

gulp.task('styles', [
  'styles.vendor',
  'styles.application'
]);

gulp.task('styles.application', function() {
  var files = [
    path.join(frontStylesDir, '/styles.less')
  ];
  return gulp.src(files)
    .pipe(sourcemaps.init())
    .pipe(less())
    .pipe(prefixer({browsers: ['last 4 versions']}))
    .pipe(minifyCss({compatibility: 'ie8'}))
    .pipe(concat('app.css'))
    .pipe(sourcemaps.write())
    .pipe(gulp.dest(publicStylesDir));
});

gulp.task('styles.vendor', function() {
  var files = [
    path.join(nodeModulesDir, '/font-awesome/css/font-awesome.min.css'),
    path.join(nodeModulesDir, '/os-bootstrap/dist/css/os-bootstrap.min.css'),
    path.join(nodeModulesDir, '/angular/angular-csp.css'),
    path.join(nodeModulesDir, '/typeahead.js-bootstrap-css/typeaheadjs.css'),
    path.join(nodeModulesDir, '/goodtables-ui/dist/goodtables-ui.min.css')
  ];
  return gulp.src(files)
    .pipe(concat('vendor.css'))
    .pipe(gulp.dest(publicStylesDir));
});

// Assets

gulp.task('assets', [
  'assets.fonts',
  'assets.images',
  'assets.favicon'
]);

gulp.task('assets.fonts', function() {
  var files = [
    path.join(nodeModulesDir, '/font-awesome/fonts/*'),
    path.join(nodeModulesDir, '/os-bootstrap/dist/fonts/*')
  ];
  return gulp.src(files)
    .pipe(gulp.dest(publicFontsDir));
});

gulp.task('assets.images', function() {
  var files = [
    path.join(frontAssetsDir, '/**/*'),
    path.join(nodeModulesDir,
      '/os-bootstrap/dist/assets/os-branding/vector/light/os.svg'),
    path.join(nodeModulesDir,
      '/os-bootstrap/dist/assets/os-branding/vector/light/packager.svg'),
    path.join(nodeModulesDir,
      '/os-bootstrap/dist/assets/os-branding/vector/light/ospackager.svg')
  ];
  return gulp.src(files)
    .pipe(gulp.dest(publicAssetsDir));
});

gulp.task('assets.favicon', function() {
  var files = [
    path.join(nodeModulesDir,
      '/os-bootstrap/dist/assets/os-branding/packager-favicon.ico')
  ];
  return gulp.src(files)
    .pipe(rename('favicon.ico'))
    .pipe(gulp.dest(publicDir));
});
<|endoftext|>


<commit_before>
/**
 * Module dependencies.
 */

var fs = require('fs');
var request = require('./util/request');
var debug = require('debug')('wpcom:media');

/**
 * Default
 */

var def = {
  "apiVersion": "1.1"
};

/**
 * Media methods
 *
 * @param {String} id
 * @param {String} sid site id
 * @param {WPCOM} wpcom
 * @api public
 */

function Media(id, sid, wpcom) {
  if (!(this instanceof Media)) {
    return new Media(id, sid, wpcom);
  }

  this.wpcom = wpcom;
  this._sid = sid;
  this._id = id;

  if (!this._id) {
    debug('WARN: media `id` is not defined');
  }
}

/**
 * Get media
 *
 * @param {Object} [query]
 * @param {Function} fn
 * @api public
 */

Media.prototype.get = function (query, fn) {
  var path = '/sites/' + this._sid + '/media/' + this._id;
  return request.get(this.wpcom, def, path, query, fn);
};

/**
 * Edit media
 *
 * @param {Object} [query]
 * @param {Object} body
 * @param {Function} fn
 * @api public
 */

Media.prototype.update = function (query, body, fn) {
  var path = '/sites/' + this._sid + '/media/' + this._id;
  return request.update(this.wpcom, def, path, query, body, fn);
};

/**
 * Add media file
 *
 * @param {Object} [query]
 * @param {String|Object|Array} files
 * @param {Function} fn
 */

Media.prototype.addFiles = function (query, files, fn) {
  var i = 0;

  if ('function' === typeof files) {
    fn = files;
    files = query;
    query = {};
  }

  var params = {
    path: '/sites/' + this._sid + '/media/new',
    formData: []
  };

  // process formData
  files = Array.isArray(files) ? files : [files];

  var f;
  var isStream;
  var isFile;

  for (i = 0; i < files.length; i++) {
    f = files[i];

    f = 'string' === typeof f ? fs.createReadStream(f) : f;

    isStream = !!f._readableState;

    debug('is stream: %s', isStream);
    debug('is file: %s', isFile);

    if (!isFile && !isStream) {
      // process file attributes like as `title`, `description`, ...
      for (var k in f) {
        debug('add %o => %o', k, f[k]);
        if ('file' != k) {
          var param = 'attrs[' + i + '][' + k + ']';
          params.formData.push([param, f[k]]);
        }
      }
      // set file path
      f = f.file;
      f = 'string' === typeof f ? fs.createReadStream(f) : f;
    }

    params.formData.push(['media[]', f]);
  }

  return request.update(this.wpcom, def, params, query, fn);
};

/**
 * Add media files from URL
 *
 * @param {Object} [query]
 * @param {String|Array|Object} files
 * @param {Function} fn
 */

Media.prototype.addUrls = function (query, media, fn) {
  if ('function' === typeof media) {
    fn = media;
    media = query;
    query = {};
  }

  var path = '/sites/' + this._sid + '/media/new';
  var body = { media_urls: [] };

  // process formData
  media = Array.isArray(media) ? media : [ media ];
  for (var i = 0; i < media.length; i++) {
    var m = media[i];
    var url;

    if ('string' == typeof m) {
      url = m;
    } else {
      if (!body.attrs) body.attrs = [];

      // add attributes
      body.attrs[i] = {};
      for (var k in m) {
        if ('url' != k) {
          body.attrs[i][k] = m[k];
        }
      }
      url = m[k];
    }

    // push url into [media_url]
    body.media_urls.push(url);
  }

  return request.update(this.wpcom, def, path, query, body, fn);
};

/**
 * Delete media
 *
 * @param {Object} [query]
 * @param {Function} fn
 * @api public
 */

Media.prototype['delete'] =
Media.prototype.del = function (query, fn) {
  if ('function' == typeof query) {
    fn = query;
    query = {};
  }

  var params = {
    path: '/sites/' + this._sid + '/media/' + this._id + '/delete',
    method: 'post'
  };

  return this.wpcom.sendRequest(params, query, null, fn);
};

/**
 * Expose `Media` module
 */

module.exports = Media;<commit_msg>media: improve defining vars<commit_after>
/**
 * Module dependencies.
 */

var fs = require('fs');
var request = require('./util/request');
var debug = require('debug')('wpcom:media');

/**
 * Default
 */

var def = {
  "apiVersion": "1.1"
};

/**
 * Media methods
 *
 * @param {String} id
 * @param {String} sid site id
 * @param {WPCOM} wpcom
 * @api public
 */

function Media(id, sid, wpcom) {
  if (!(this instanceof Media)) {
    return new Media(id, sid, wpcom);
  }

  this.wpcom = wpcom;
  this._sid = sid;
  this._id = id;

  if (!this._id) {
    debug('WARN: media `id` is not defined');
  }
}

/**
 * Get media
 *
 * @param {Object} [query]
 * @param {Function} fn
 * @api public
 */

Media.prototype.get = function (query, fn) {
  var path = '/sites/' + this._sid + '/media/' + this._id;
  return request.get(this.wpcom, def, path, query, fn);
};

/**
 * Edit media
 *
 * @param {Object} [query]
 * @param {Object} body
 * @param {Function} fn
 * @api public
 */

Media.prototype.update = function (query, body, fn) {
  var path = '/sites/' + this._sid + '/media/' + this._id;
  return request.update(this.wpcom, def, path, query, body, fn);
};

/**
 * Add media file
 *
 * @param {Object} [query]
 * @param {String|Object|Array} files
 * @param {Function} fn
 */

Media.prototype.addFiles = function (query, files, fn) {
  var i = 0;

  if ('function' === typeof files) {
    fn = files;
    files = query;
    query = {};
  }

  var params = {
    path: '/sites/' + this._sid + '/media/new',
    formData: []
  };

  // process formData
  files = Array.isArray(files) ? files : [files];

  var f, k, param;
  var isStream;
  var isFile;

  for (i = 0; i < files.length; i++) {
    f = files[i];
    f = 'string' === typeof f ? fs.createReadStream(f) : f;

    isStream = !!f._readableState;
    isFile = 'undefined' !== typeof FILE && f instanceof File;

    debug('is stream: %s', isStream);
    debug('is file: %s', isFile);

    if (!isFile && !isStream) {
      // process file attributes like as `title`, `description`, ...
      for (k in f) {
        debug('add %o => %o', k, f[k]);
        if ('file' !== k) {
          param = 'attrs[' + i + '][' + k + ']';
          params.formData.push([param, f[k]]);
        }
      }
      // set file path
      f = f.file;
      f = 'string' === typeof f ? fs.createReadStream(f) : f;
    }

    params.formData.push(['media[]', f]);
  }

  return request.update(this.wpcom, def, params, query, fn);
};

/**
 * Add media files from URL
 *
 * @param {Object} [query]
 * @param {String|Array|Object} files
 * @param {Function} fn
 */

Media.prototype.addUrls = function (query, media, fn) {
  if ('function' === typeof media) {
    fn = media;
    media = query;
    query = {};
  }

  var path = '/sites/' + this._sid + '/media/new';
  var body = { media_urls: [] };

  // process formData
  media = Array.isArray(media) ? media : [ media ];
  for (var i = 0; i < media.length; i++) {
    var m = media[i];
    var url;

    if ('string' == typeof m) {
      url = m;
    } else {
      if (!body.attrs) body.attrs = [];

      // add attributes
      body.attrs[i] = {};
      for (var k in m) {
        if ('url' != k) {
          body.attrs[i][k] = m[k];
        }
      }
      url = m[k];
    }

    // push url into [media_url]
    body.media_urls.push(url);
  }

  return request.update(this.wpcom, def, path, query, body, fn);
};

/**
 * Delete media
 *
 * @param {Object} [query]
 * @param {Function} fn
 * @api public
 */

Media.prototype['delete'] =
Media.prototype.del = function (query, fn) {
  if ('function' == typeof query) {
    fn = query;
    query = {};
  }

  var params = {
    path: '/sites/' + this._sid + '/media/' + this._id + '/delete',
    method: 'post'
  };

  return this.wpcom.sendRequest(params, query, null, fn);
};

/**
 * Expose `Media` module
 */

module.exports = Media;<|endoftext|>


<commit_before>//@steal-clean
/*******************************************************************************
 * OpenAjax.js
 *
 * Reference implementation of the OpenAjax Hub, as specified by OpenAjax Alliance.
 * Specification is under development at:
 *
 *   http://www.openajax.org/member/wiki/OpenAjax_Hub_Specification
 *
 * Copyright 2006-2009 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy
 * of the License at http://www.apache.org/licenses/LICENSE-2.0 . Unless
 * required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 *
 ******************************************************************************/
steal.then(function(){
// prevent re-definition of the OpenAjax object
if(!window["OpenAjax"]){
	/**
	* @class OpenAjax
	* Use OpenAjax.hub to publish and subscribe to messages.
 	*/
	OpenAjax = new function(){
		var t = true;
		var f = false;
		var g = window;
		var ooh = "org.openajax.hub.";

		var h = {};
		this.hub = h;
		h.implementer = "http://openajax.org";
		h.implVersion = "2.0";
		h.specVersion = "2.0";
		h.implExtraData = {};
		var libs = {};
		h.libraries = libs;

		h.registerLibrary = function(prefix, nsURL, version, extra){
			libs[prefix] = {
				prefix: prefix,
				namespaceURI: nsURL,
				version: version,
				extraData: extra
			};
			this.publish(ooh+"registerLibrary", libs[prefix]);
		}
		h.unregisterLibrary = function(prefix){
			this.publish(ooh+"unregisterLibrary", libs[prefix]);
			delete libs[prefix];
		}

		h._subscriptions = { c:{}, s:[] };
		h._cleanup = [];
		h._subIndex = 0;
		h._pubDepth = 0;

		h.subscribe = function(name, callback, scope, subscriberData, filter)
		{
			if(!scope){
				scope = window;
			}
			var handle = name + "." + this._subIndex;
			var sub = { scope: scope, cb: callback, fcb: filter, data: subscriberData, sid: this._subIndex++, hdl: handle };
			var path = name.split(".");
	 		this._subscribe(this._subscriptions, path, 0, sub);
			return handle;
		}

		h.publish = function(name, message)
		{
			var path = name.split(".");
			this._pubDepth++;
			this._publish(this._subscriptions, path, 0, name, message);
			this._pubDepth--;
			if((this._cleanup.length > 0) && (this._pubDepth == 0)) {
				for(var i = 0; i < this._cleanup.length; i++)
					this.unsubscribe(this._cleanup[i].hdl);
				delete(this._cleanup);
				this._cleanup = [];
			}
		}

		h.unsubscribe = function(sub)
		{
			var path = sub.split(".");
			var sid = path.pop();
			this._unsubscribe(this._subscriptions, path, 0, sid);
		}

		h._subscribe = function(tree, path, index, sub)
		{
			var token = path[index];
			if(index == path.length)
				tree.s.push(sub);
			else {
				if(typeof tree.c == "undefined")
					 tree.c = {};
				if(typeof tree.c[token] == "undefined") {
					tree.c[token] = { c: {}, s: [] };
					this._subscribe(tree.c[token], path, index + 1, sub);
				}
				else
					this._subscribe( tree.c[token], path, index + 1, sub);
			}
		}

		h._publish = function(tree, path, index, name, msg, pid) {
			if(typeof tree != "undefined") {
				var node;
				if(index == path.length) {
					node = tree;
				} else {
					this._publish(tree.c[path[index]], path, index + 1, name, msg, pid);
					this._publish(tree.c["*"], path, index + 1, name, msg, pid);
					node = tree.c["**"];
				}
				if(typeof node != "undefined") {
					var callbacks = node.s;
					var max = callbacks.length;
					for(var i = 0; i < max; i++) {
						if(callbacks[i].cb) {
							var sc = callbacks[i].scope;
							var cb = callbacks[i].cb;
							var fcb = callbacks[i].fcb;
							var d = callbacks[i].data;
							if(typeof cb == "string"){
								// get a function object
								cb = sc[cb];
							}
							if(typeof fcb == "string"){
								// get a function object
								fcb = sc[fcb];
							}
							if((!fcb) || (fcb.call(sc, name, msg, d))) {
								cb.call(sc, name, msg, d, pid);
							}
						}
					}
				}
			}
		}

		h._unsubscribe = function(tree, path, index, sid) {
			if(typeof tree != "undefined") {
				if(index < path.length) {
					var childNode = tree.c[path[index]];
					this._unsubscribe(childNode, path, index + 1, sid);
					if(childNode.s.length == 0) {
						for(var x in childNode.c)
					 		return;
						delete tree.c[path[index]];
					}
					return;
				}
				else {
					var callbacks = tree.s;
					var max = callbacks.length;
					for(var i = 0; i < max; i++)
						if(sid == callbacks[i].sid) {
							if(this._pubDepth > 0) {
								callbacks[i].cb = null;
								this._cleanup.push(callbacks[i]);
							}
							else
								callbacks.splice(i, 1);
							return;
						}
				}
			}
		}
		// The following function is provided for automatic testing purposes.
		// It is not expected to be deployed in run-time OpenAjax Hub implementations.
		h.reinit = function()
		{
			for (var lib in OpenAjax.hub.libraries) {
				delete OpenAjax.hub.libraries[lib];
			}
			OpenAjax.hub.registerLibrary("OpenAjax", "http://openajax.org/hub", "1.0", {});

			delete OpenAjax._subscriptions;
			OpenAjax._subscriptions = {c:{},s:[]};
			delete OpenAjax._cleanup;
			OpenAjax._cleanup = [];
			OpenAjax._subIndex = 0;
			OpenAjax._pubDepth = 0;
		}
	};
	// Register the OpenAjax Hub itself as a library.
	OpenAjax.hub.registerLibrary("OpenAjax", "http://openajax.org/hub", "1.0", {});

}
OpenAjax.hub.registerLibrary("JavaScriptMVC", "http://JavaScriptMVC.com", "3.1", {});
});
<commit_msg>Removed space and added tab (minor formatting issue)<commit_after>//@steal-clean
/*******************************************************************************
 * OpenAjax.js
 *
 * Reference implementation of the OpenAjax Hub, as specified by OpenAjax Alliance.
 * Specification is under development at:
 *
 *   http://www.openajax.org/member/wiki/OpenAjax_Hub_Specification
 *
 * Copyright 2006-2009 OpenAjax Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy
 * of the License at http://www.apache.org/licenses/LICENSE-2.0 . Unless
 * required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 *
 ******************************************************************************/
steal.then(function(){
// prevent re-definition of the OpenAjax object
if(!window["OpenAjax"]){
	/**
	* @class OpenAjax
	* Use OpenAjax.hub to publish and subscribe to messages.
	*/
	OpenAjax = new function(){
		var t = true;
		var f = false;
		var g = window;
		var ooh = "org.openajax.hub.";

		var h = {};
		this.hub = h;
		h.implementer = "http://openajax.org";
		h.implVersion = "2.0";
		h.specVersion = "2.0";
		h.implExtraData = {};
		var libs = {};
		h.libraries = libs;

		h.registerLibrary = function(prefix, nsURL, version, extra){
			libs[prefix] = {
				prefix: prefix,
				namespaceURI: nsURL,
				version: version,
				extraData: extra
			};
			this.publish(ooh+"registerLibrary", libs[prefix]);
		}
		h.unregisterLibrary = function(prefix){
			this.publish(ooh+"unregisterLibrary", libs[prefix]);
			delete libs[prefix];
		}

		h._subscriptions = { c:{}, s:[] };
		h._cleanup = [];
		h._subIndex = 0;
		h._pubDepth = 0;

		h.subscribe = function(name, callback, scope, subscriberData, filter)
		{
			if(!scope){
				scope = window;
			}
			var handle = name + "." + this._subIndex;
			var sub = { scope: scope, cb: callback, fcb: filter, data: subscriberData, sid: this._subIndex++, hdl: handle };
			var path = name.split(".");
	 		this._subscribe(this._subscriptions, path, 0, sub);
			return handle;
		}

		h.publish = function(name, message)
		{
			var path = name.split(".");
			this._pubDepth++;
			this._publish(this._subscriptions, path, 0, name, message);
			this._pubDepth--;
			if((this._cleanup.length > 0) && (this._pubDepth == 0)) {
				for(var i = 0; i < this._cleanup.length; i++)
					this.unsubscribe(this._cleanup[i].hdl);
				delete(this._cleanup);
				this._cleanup = [];
			}
		}

		h.unsubscribe = function(sub)
		{
			var path = sub.split(".");
			var sid = path.pop();
			this._unsubscribe(this._subscriptions, path, 0, sid);
		}

		h._subscribe = function(tree, path, index, sub)
		{
			var token = path[index];
			if(index == path.length)
				tree.s.push(sub);
			else {
				if(typeof tree.c == "undefined")
					 tree.c = {};
				if(typeof tree.c[token] == "undefined") {
					tree.c[token] = { c: {}, s: [] };
					this._subscribe(tree.c[token], path, index + 1, sub);
				}
				else
					this._subscribe( tree.c[token], path, index + 1, sub);
			}
		}

		h._publish = function(tree, path, index, name, msg, pid) {
			if(typeof tree != "undefined") {
				var node;
				if(index == path.length) {
					node = tree;
				} else {
					this._publish(tree.c[path[index]], path, index + 1, name, msg, pid);
					this._publish(tree.c["*"], path, index + 1, name, msg, pid);
					node = tree.c["**"];
				}
				if(typeof node != "undefined") {
					var callbacks = node.s;
					var max = callbacks.length;
					for(var i = 0; i < max; i++) {
						if(callbacks[i].cb) {
							var sc = callbacks[i].scope;
							var cb = callbacks[i].cb;
							var fcb = callbacks[i].fcb;
							var d = callbacks[i].data;
							if(typeof cb == "string"){
								// get a function object
								cb = sc[cb];
							}
							if(typeof fcb == "string"){
								// get a function object
								fcb = sc[fcb];
							}
							if((!fcb) || (fcb.call(sc, name, msg, d))) {
								cb.call(sc, name, msg, d, pid);
							}
						}
					}
				}
			}
		}

		h._unsubscribe = function(tree, path, index, sid) {
			if(typeof tree != "undefined") {
				if(index < path.length) {
					var childNode = tree.c[path[index]];
					this._unsubscribe(childNode, path, index + 1, sid);
					if(childNode.s.length == 0) {
						for(var x in childNode.c)
					 		return;
						delete tree.c[path[index]];
					}
					return;
				}
				else {
					var callbacks = tree.s;
					var max = callbacks.length;
					for(var i = 0; i < max; i++)
						if(sid == callbacks[i].sid) {
							if(this._pubDepth > 0) {
								callbacks[i].cb = null;
								this._cleanup.push(callbacks[i]);
							}
							else
								callbacks.splice(i, 1);
							return;
						}
				}
			}
		}
		// The following function is provided for automatic testing purposes.
		// It is not expected to be deployed in run-time OpenAjax Hub implementations.
		h.reinit = function()
		{
			for (var lib in OpenAjax.hub.libraries) {
				delete OpenAjax.hub.libraries[lib];
			}
			OpenAjax.hub.registerLibrary("OpenAjax", "http://openajax.org/hub", "1.0", {});

			delete OpenAjax._subscriptions;
			OpenAjax._subscriptions = {c:{},s:[]};
			delete OpenAjax._cleanup;
			OpenAjax._cleanup = [];
			OpenAjax._subIndex = 0;
			OpenAjax._pubDepth = 0;
		}
	};
	// Register the OpenAjax Hub itself as a library.
	OpenAjax.hub.registerLibrary("OpenAjax", "http://openajax.org/hub", "1.0", {});

}
OpenAjax.hub.registerLibrary("JavaScriptMVC", "http://JavaScriptMVC.com", "3.1", {});
});
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { observer } from 'mobx-react';
import ElectronWebView from 'react-electron-web-view';

import ServiceModel from '../../../models/Service';

@observer
class ServiceWebview extends Component {
  static propTypes = {
    service: PropTypes.instanceOf(ServiceModel).isRequired,
    setWebviewReference: PropTypes.func.isRequired,
    detachService: PropTypes.func.isRequired,
  };

  webview = null;

  componentWillUnmount() {
    const { service, detachService } = this.props;
    detachService({ service });
  }

  render() {
    const {
      service,
      setWebviewReference,
    } = this.props;

    return (
      <ElectronWebView
        ref={(webview) => { this.webview = webview; }}
        autosize
        src={service.url}
        preload="./webview/recipe.js"
        partition={`persist:service-${service.id}`}
        onDidAttach={() => {
          setWebviewReference({
            serviceId: service.id,
            webview: this.webview.view,
          });
        }}
        onUpdateTargetUrl={this.updateTargetUrl}
        useragent={service.userAgent}
        disablewebsecurity={service.recipe.disablewebsecurity}
        allowpopups
      />
    );
  }
}

export default ServiceWebview;
<commit_msg>Add workaround for electron webview issue with disappearing cursors<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { observer } from 'mobx-react';
import ElectronWebView from 'react-electron-web-view';

import ServiceModel from '../../../models/Service';

@observer
class ServiceWebview extends Component {
  static propTypes = {
    service: PropTypes.instanceOf(ServiceModel).isRequired,
    setWebviewReference: PropTypes.func.isRequired,
    detachService: PropTypes.func.isRequired,
  };

  webview = null;

  componentWillUnmount() {
    const { service, detachService } = this.props;
    detachService({ service });
  }

  refocusWebview = () => {
    const { webview } = this;
    if (!webview) return;
    webview.view.blur();
    webview.view.focus();
  };

  render() {
    const {
      service,
      setWebviewReference,
    } = this.props;

    return (
      <ElectronWebView
        ref={(webview) => {
          this.webview = webview;
          webview.view.addEventListener('did-stop-loading', this.refocusWebview);
        }}
        autosize
        src={service.url}
        preload="./webview/recipe.js"
        partition={`persist:service-${service.id}`}
        onDidAttach={() => {
          setWebviewReference({
            serviceId: service.id,
            webview: this.webview.view,
          });
        }}
        onUpdateTargetUrl={this.updateTargetUrl}
        useragent={service.userAgent}
        disablewebsecurity={service.recipe.disablewebsecurity}
        allowpopups
      />
    );
  }
}

export default ServiceWebview;
<|endoftext|>


<commit_before>/* eslint no-console:0,react/no-multi-comp:0 */
/*
 * Based on react-redux Provider tests
 * https://github.com/rackt/react-redux/blob/master/test/components/Provider.spec.js
 */
import test from 'ava';
import sinon from 'sinon';
import React, { PropTypes, Component, Children } from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-addons-test-utils';
import 'babel-core/register';
import initJsDom from '../helpers/_document';
import createPubSubConnector from '../../src/components/createPubSubConnector';
import createPubSub from '../../src/adapter/createPubSub';

initJsDom();

class Passthrough extends Component {
  render() {
    return (<div {...this.props} />);
  }
}

class ProviderMock extends Component {
  getChildContext() {
    return { pubSubCore: this.props.pubSubCore };
  }

  render() {
    return Children.only(this.props.children);
  }
}
ProviderMock.childContextTypes = {
  pubSubCore: PropTypes.object.isRequired,
};
ProviderMock.propTypes = {
  pubSubCore: PropTypes.object.isRequired,
  children: PropTypes.element.isRequired,
};

test('should add the PubSub core to the child context', t => {
  const pubSubCore = createPubSub();

  class Container extends Component {
    render() {
      return (<div {...this.props} />);
    }
  }
  const WrapperContainer = createPubSubConnector()(Container);

  const tree = TestUtils.renderIntoDocument(
    <ProviderMock pubSubCore={pubSubCore}>
    <WrapperContainer pass="through" />
    </ProviderMock>
  );

  const container = TestUtils.findRenderedComponentWithType(tree, WrapperContainer);
  t.is(container.context.pubSubCore, pubSubCore);

  t.end();
});

test('should pass props to the given component', t => {
  const pubSubCore = createPubSub();

  class Container extends Component {
    render() {
      return (<Passthrough {...this.props} />);
    }
  }
  const WrapperContainer = createPubSubConnector()(Container);

  const container = TestUtils.renderIntoDocument(
    <ProviderMock pubSubCore={pubSubCore}>
    <WrapperContainer pass="through" count={33} />
    </ProviderMock>
  );

  const stub = TestUtils.findRenderedComponentWithType(container, Passthrough);
  t.is(stub.props.pass, 'through');
  t.is(stub.props.count, 33);
  t.is(stub.props.other, undefined);
  t.doesNotThrow(
    () => TestUtils.findRenderedComponentWithType(container, WrapperContainer)
  );

  t.end();
});

test('should pass pubSub subscriptions as props to the given component', t => {
  const subscription = {
    add: sinon.spy(),
    removeAll: sinon.spy(),
    publish: sinon.spy(),
  };
  const pubSubCore = {
    register: () => subscription,
    unregister() {},
  };

  class Container extends Component {
    render() {
      return (<Passthrough {...this.props} />);
    }
  }
  const WrapperContainer = createPubSubConnector()(Container);

  const container = TestUtils.renderIntoDocument(
    <ProviderMock pubSubCore={pubSubCore}>
    <WrapperContainer />
    </ProviderMock>
  );

  const stub = TestUtils.findRenderedComponentWithType(container, Passthrough);
  const cb = () => {};
  const action = { msg: 'message' };
  subscription.add('test_action', cb);
  subscription.publish('test_action', action);
  subscription.removeAll();
  t.is(stub.props.pubSub, subscription);
  t.true(subscription.add.calledOnce);
  t.true(subscription.add.calledWith('test_action', cb));
  t.true(subscription.publish.calledOnce);
  t.true(subscription.publish.calledWith('test_action', action));
  t.true(subscription.removeAll.calledOnce);

  t.end();
});

test('should unsubscribe before unmounting', t => {
  const unregisterSpy = sinon.spy();
  const pubSubCore = createPubSub();
  const unregister = pubSubCore.unregister;
  pubSubCore.unregister = (...rest) => {
    unregisterSpy();
    unregister(...rest);
  };

  class Container extends Component {
    render() {
      return (<Passthrough {...this.props} />);
    }
  }
  const WrapperContainer = createPubSubConnector()(Container);

  const div = document.createElement('div');
  ReactDOM.render(
    <ProviderMock pubSubCore={pubSubCore}>
    <WrapperContainer />
    </ProviderMock>,
    div
  );

  t.is(unregisterSpy.callCount, 0);
  ReactDOM.unmountComponentAtNode(div);
  t.is(unregisterSpy.callCount, 1);

  t.end();
});

test('should hoist non-react statics from wrapped component', t => {
  class Container extends Component {
    render() {
      return (<Passthrough />);
    }
  }
  Container.extraMethodSay = () => 'Hey there!';
  Container.foo = 'bar';

  const WrappedContainer = createPubSubConnector()(Container);

  t.is(typeof WrappedContainer.extraMethodSay, 'function');
  t.is(WrappedContainer.extraMethodSay(), 'Hey there!');
  t.is(WrappedContainer.foo, 'bar');

  t.end();
});

test('should use the pubSub core from the props instead of from the context if present', t => {
  const pubSubCore = createPubSub();
  class Container extends Component {
    render() {
      return (<Passthrough />);
    }
  }
  const WrappedContainer = createPubSubConnector()(Container);

  const container = TestUtils.renderIntoDocument(<WrappedContainer pubSubCore={pubSubCore} />);
  t.is(container.pubSubCore, pubSubCore);

  t.end();
});

test('should throw an error if the pubSub core is not in the props or context', t => {
  class Container extends Component {
    render() {
      return (<Passthrough />);
    }
  }
  const WrappedContainer = createPubSubConnector()(Container);

  t.throws(
    () => TestUtils.renderIntoDocument(<WrappedContainer />),
    /Could not find "pubSubCore"/
  );

  t.end();
});

test('should return the instance of the wrapped component for use in calling child methods', t => {
  const pubSubCore = createPubSub();
  const someData = { some: 'data' };

  class Container extends Component {
    someInstanceMethod() {
      return someData;
    }

    render() {
      return (<Passthrough />);
    }
  }
  const WrappedComponent = createPubSubConnector(null, null, { withRef: true })(Container);

  const tree = TestUtils.renderIntoDocument(
    <ProviderMock pubSubCore={pubSubCore}>
    <WrappedComponent />
    </ProviderMock>
  );

  const decorated = TestUtils.findRenderedComponentWithType(tree, WrappedComponent);

  t.throws(() => decorated.someInstanceMethod());
  t.is(decorated.getWrappedInstance().someInstanceMethod(), someData);
  t.is(decorated.refs.wrappedInstance.someInstanceMethod(), someData);

  t.end();
});

test('should subscribe pure function components to the pubSub core', t => {
  const subscription = {
    add: sinon.spy(),
    removeAll: sinon.spy(),
    publish: sinon.spy(),
  };
  const pubSubCore = {
    register: () => subscription,
    unregister() {},
  };

  const Container = createPubSubConnector()(function Container(props) {
    return (<Passthrough {...props}/>);
  });

  const spy = sinon.spy(console, 'error');
  const tree = TestUtils.renderIntoDocument(
    <ProviderMock pubSubCore={pubSubCore}>
    <Container />
    </ProviderMock>
  );
  console.error.restore();
  t.is(spy.callCount, 0);

  const stub = TestUtils.findRenderedComponentWithType(tree, Passthrough);
  const cb = () => {};
  const action = { msg: 'message' };
  subscription.add('test_action', cb);
  subscription.publish('test_action', action);
  subscription.removeAll();
  t.is(stub.props.pubSub, subscription);
  t.true(subscription.add.calledOnce);
  t.true(subscription.add.calledWith('test_action', cb));
  t.true(subscription.publish.calledOnce);
  t.true(subscription.publish.calledWith('test_action', action));
  t.true(subscription.removeAll.calledOnce);

  t.end();
});

test(
  'should map subscriptions defined in `mapSubscriptionsToProps` and '
  + 'pass their payload as props to wrapped component',
  t => {
    const SPEAK = 'speak';
    const UPDATE = 'update';
    const pubSubCore = createPubSub();
    const register = pubSubCore.register;
    let pubSub;
    pubSubCore.register = (...args) => {
      pubSub = register(...args);
      return pubSub;
    };

    class Container extends Component {
      render() {
        return (<Passthrough {...this.props} />);
      }
    }

    class ControlledState extends Component {
      constructor() {
        super();
        this.state = { updateField: 'label' };
      }

      render() {
        const { updateField } = this.state;
        return (<WrapperContainer updateField={updateField} />);
      }
    }

    const mapSubscriptionsToProps = {
      [SPEAK]: 'lastMessage',
      [UPDATE]: (args, props) => {
        const [payload = {}] = args;
        const { updateField: key } = props;
        return { updatedField: payload[key] };
      },
    };
    const WrapperContainer = createPubSubConnector(mapSubscriptionsToProps)(Container);

    const tree = TestUtils.renderIntoDocument(
      <ProviderMock pubSubCore={pubSubCore}>
      <ControlledState />
      </ProviderMock>
    );
    const stub = TestUtils.findRenderedComponentWithType(tree, Passthrough);
    const wrapper = TestUtils.findRenderedComponentWithType(tree, ControlledState);

    t.is(stub.props.lastMessage, undefined);
    pubSub.publish(SPEAK, 'Hey there!');
    t.is(stub.props.lastMessage, 'Hey there!');

    t.is(stub.props.updatedField, undefined);
    pubSub.publish(UPDATE, { label: 'myLabel', name: 'myName' });
    t.is(stub.props.updatedField, 'myLabel');
    wrapper.setState({ updateField: 'name' });
    t.is(stub.props.updatedField, 'myName');
    pubSub.publish(UPDATE, { label: 'myLabel', name: 'myNewName' });
    t.is(stub.props.updatedField, 'myNewName');

    pubSub.publish(UPDATE, { label: 'myLabel' });
    t.is(stub.props.updatedField, undefined);

    pubSub.publish(SPEAK, 'New Message');
    t.is(stub.props.lastMessage, 'New Message');

    t.end();
  }
);

test(
  'should update mapped subscriptions defined in `mapSubscriptionsToProps` when'
  + ' props changes only if the callback defines a second arguments in its signature',
  t => {
    const SIMPLE_UPDATE = 'simpleUpdate';
    const pubSubCore = createPubSub();
    const register = pubSubCore.register;
    let pubSub;
    pubSubCore.register = (...args) => {
      pubSub = register(...args);
      return pubSub;
    };

    class Container extends Component {
      render() {
        return (<Passthrough {...this.props} />);
      }
    }

    class ControlledState extends Component {
      constructor() {
        super();
        this.state = { updateField: 'label' };
      }

      render() {
        const { updateField } = this.state;
        return (<WrapperContainer updateField={updateField} />);
      }
    }

    const mapSubscriptionsToProps = {
      [SIMPLE_UPDATE]: (args) => {
        const [payload = {}] = args;
        const now = new Date().getTime();
        return { simpleField: payload.name, now };
      },
    };
    const WrapperContainer = createPubSubConnector(mapSubscriptionsToProps)(Container);

    const tree = TestUtils.renderIntoDocument(
      <ProviderMock pubSubCore={pubSubCore}>
      <ControlledState />
      </ProviderMock>
    );
    const stub = TestUtils.findRenderedComponentWithType(tree, Passthrough);
    const wrapper = TestUtils.findRenderedComponentWithType(tree, ControlledState);

    t.is(stub.props.simpleField, undefined);
    pubSub.publish(SIMPLE_UPDATE, { name: 'john' });
    t.is(stub.props.simpleField, 'john');
    const nowValue = stub.props.now;
    wrapper.setState({ updateField: 'label' });
    t.is(stub.props.now, nowValue);

    t.end();
  }
);

// test('should not invoke mapPublish when props change if it only has one argument', t => {
//   const pubSubCore = createPubSub();
//
//   let invocationCount = 0;
//
//   const mapPublish = () => {
//     invocationCount++;
//     return {};
//   };
//
//   class WithoutProps extends Component {
//     render() {
//       return (<Passthrough {...this.props}/>);
//     }
//   }
//   const WrappedContainer = createPubSubConnector(null, mapPublish)(WithoutProps);
//
//   class OuterComponent extends Component {
//     constructor() {
//       super();
//       this.state = { foo: 'FOO' };
//     }
//
//     setFoo(foo) {
//       this.setState({ foo });
//     }
//
//     render() {
//       return (
//         <div>
//         <WrappedContainer {...this.state} />
//         </div>
//       );
//     }
//   }
//
//   let outerComponent;
//   TestUtils.renderIntoDocument(
//     <ProviderMock pubSubCore={pubSubCore}>
//     <OuterComponent ref={c => outerComponent = c} />
//     </ProviderMock>
//   );
//
//   outerComponent.setFoo('BAR');
//   outerComponent.setFoo('DID');
//
//   t.is(invocationCount, 1);
//
//   t.end();
// });
<commit_msg>Added initial test for 'mapPublishProps' of createPubSubConnector<commit_after>/* eslint no-console:0,react/no-multi-comp:0 */
/*
 * Based on react-redux Provider tests
 * https://github.com/rackt/react-redux/blob/master/test/components/Provider.spec.js
 */
import test from 'ava';
import sinon from 'sinon';
import React, { PropTypes, Component, Children } from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-addons-test-utils';
import 'babel-core/register';
import initJsDom from '../helpers/_document';
import createPubSubConnector from '../../src/components/createPubSubConnector';
import createPubSub from '../../src/adapter/createPubSub';

initJsDom();

class Passthrough extends Component {
  render() {
    return (<div {...this.props} />);
  }
}

class ProviderMock extends Component {
  getChildContext() {
    return { pubSubCore: this.props.pubSubCore };
  }

  render() {
    return Children.only(this.props.children);
  }
}
ProviderMock.childContextTypes = {
  pubSubCore: PropTypes.object.isRequired,
};
ProviderMock.propTypes = {
  pubSubCore: PropTypes.object.isRequired,
  children: PropTypes.element.isRequired,
};

test('should add the PubSub core to the child context', t => {
  const pubSubCore = createPubSub();

  class Container extends Component {
    render() {
      return (<div {...this.props} />);
    }
  }
  const WrapperContainer = createPubSubConnector()(Container);

  const tree = TestUtils.renderIntoDocument(
    <ProviderMock pubSubCore={pubSubCore}>
    <WrapperContainer pass="through" />
    </ProviderMock>
  );

  const container = TestUtils.findRenderedComponentWithType(tree, WrapperContainer);
  t.is(container.context.pubSubCore, pubSubCore);

  t.end();
});

test('should pass props to the given component', t => {
  const pubSubCore = createPubSub();

  class Container extends Component {
    render() {
      return (<Passthrough {...this.props} />);
    }
  }
  const WrapperContainer = createPubSubConnector()(Container);

  const container = TestUtils.renderIntoDocument(
    <ProviderMock pubSubCore={pubSubCore}>
    <WrapperContainer pass="through" count={33} />
    </ProviderMock>
  );

  const stub = TestUtils.findRenderedComponentWithType(container, Passthrough);
  t.is(stub.props.pass, 'through');
  t.is(stub.props.count, 33);
  t.is(stub.props.other, undefined);
  t.doesNotThrow(
    () => TestUtils.findRenderedComponentWithType(container, WrapperContainer)
  );

  t.end();
});

test('should pass pubSub subscriptions as props to the given component', t => {
  const subscription = {
    add: sinon.spy(),
    removeAll: sinon.spy(),
    publish: sinon.spy(),
  };
  const pubSubCore = {
    register: () => subscription,
    unregister() {},
  };

  class Container extends Component {
    render() {
      return (<Passthrough {...this.props} />);
    }
  }
  const WrapperContainer = createPubSubConnector()(Container);

  const container = TestUtils.renderIntoDocument(
    <ProviderMock pubSubCore={pubSubCore}>
    <WrapperContainer />
    </ProviderMock>
  );

  const stub = TestUtils.findRenderedComponentWithType(container, Passthrough);
  const cb = () => {};
  const action = { msg: 'message' };
  subscription.add('test_action', cb);
  subscription.publish('test_action', action);
  subscription.removeAll();
  t.is(stub.props.pubSub, subscription);
  t.true(subscription.add.calledOnce);
  t.true(subscription.add.calledWith('test_action', cb));
  t.true(subscription.publish.calledOnce);
  t.true(subscription.publish.calledWith('test_action', action));
  t.true(subscription.removeAll.calledOnce);

  t.end();
});

test('should unsubscribe before unmounting', t => {
  const unregisterSpy = sinon.spy();
  const pubSubCore = createPubSub();
  const unregister = pubSubCore.unregister;
  pubSubCore.unregister = (...rest) => {
    unregisterSpy();
    unregister(...rest);
  };

  class Container extends Component {
    render() {
      return (<Passthrough {...this.props} />);
    }
  }
  const WrapperContainer = createPubSubConnector()(Container);

  const div = document.createElement('div');
  ReactDOM.render(
    <ProviderMock pubSubCore={pubSubCore}>
    <WrapperContainer />
    </ProviderMock>,
    div
  );

  t.is(unregisterSpy.callCount, 0);
  ReactDOM.unmountComponentAtNode(div);
  t.is(unregisterSpy.callCount, 1);

  t.end();
});

test('should hoist non-react statics from wrapped component', t => {
  class Container extends Component {
    render() {
      return (<Passthrough />);
    }
  }
  Container.extraMethodSay = () => 'Hey there!';
  Container.foo = 'bar';

  const WrappedContainer = createPubSubConnector()(Container);

  t.is(typeof WrappedContainer.extraMethodSay, 'function');
  t.is(WrappedContainer.extraMethodSay(), 'Hey there!');
  t.is(WrappedContainer.foo, 'bar');

  t.end();
});

test('should use the pubSub core from the props instead of from the context if present', t => {
  const pubSubCore = createPubSub();
  class Container extends Component {
    render() {
      return (<Passthrough />);
    }
  }
  const WrappedContainer = createPubSubConnector()(Container);

  const container = TestUtils.renderIntoDocument(<WrappedContainer pubSubCore={pubSubCore} />);
  t.is(container.pubSubCore, pubSubCore);

  t.end();
});

test('should throw an error if the pubSub core is not in the props or context', t => {
  class Container extends Component {
    render() {
      return (<Passthrough />);
    }
  }
  const WrappedContainer = createPubSubConnector()(Container);

  t.throws(
    () => TestUtils.renderIntoDocument(<WrappedContainer />),
    /Could not find "pubSubCore"/
  );

  t.end();
});

test('should return the instance of the wrapped component for use in calling child methods', t => {
  const pubSubCore = createPubSub();
  const someData = { some: 'data' };

  class Container extends Component {
    someInstanceMethod() {
      return someData;
    }

    render() {
      return (<Passthrough />);
    }
  }
  const WrappedComponent = createPubSubConnector(null, null, { withRef: true })(Container);

  const tree = TestUtils.renderIntoDocument(
    <ProviderMock pubSubCore={pubSubCore}>
    <WrappedComponent />
    </ProviderMock>
  );

  const decorated = TestUtils.findRenderedComponentWithType(tree, WrappedComponent);

  t.throws(() => decorated.someInstanceMethod());
  t.is(decorated.getWrappedInstance().someInstanceMethod(), someData);
  t.is(decorated.refs.wrappedInstance.someInstanceMethod(), someData);

  t.end();
});

test('should subscribe pure function components to the pubSub core', t => {
  const subscription = {
    add: sinon.spy(),
    removeAll: sinon.spy(),
    publish: sinon.spy(),
  };
  const pubSubCore = {
    register: () => subscription,
    unregister() {},
  };

  const Container = createPubSubConnector()(function Container(props) {
    return (<Passthrough {...props}/>);
  });

  const spy = sinon.spy(console, 'error');
  const tree = TestUtils.renderIntoDocument(
    <ProviderMock pubSubCore={pubSubCore}>
    <Container />
    </ProviderMock>
  );
  console.error.restore();
  t.is(spy.callCount, 0);

  const stub = TestUtils.findRenderedComponentWithType(tree, Passthrough);
  const cb = () => {};
  const action = { msg: 'message' };
  subscription.add('test_action', cb);
  subscription.publish('test_action', action);
  subscription.removeAll();
  t.is(stub.props.pubSub, subscription);
  t.true(subscription.add.calledOnce);
  t.true(subscription.add.calledWith('test_action', cb));
  t.true(subscription.publish.calledOnce);
  t.true(subscription.publish.calledWith('test_action', action));
  t.true(subscription.removeAll.calledOnce);

  t.end();
});

// TODO: improve this test, splitting it in more atomic scenarios
test(
  'should map subscriptions defined in `mapSubscriptionsToProps` and '
  + 'pass their payload as props to wrapped component',
  t => {
    const SPEAK = 'speak';
    const UPDATE = 'update';
    const pubSubCore = createPubSub();
    const register = pubSubCore.register;
    let pubSub;
    pubSubCore.register = (...args) => {
      pubSub = register(...args);
      return pubSub;
    };

    class Container extends Component {
      render() {
        return (<Passthrough {...this.props} />);
      }
    }

    class ControlledState extends Component {
      constructor() {
        super();
        this.state = { updateField: 'label' };
      }

      render() {
        const { updateField } = this.state;
        return (<WrapperContainer updateField={updateField} />);
      }
    }

    const mapSubscriptionsToProps = {
      [SPEAK]: 'lastMessage',
      [UPDATE]: (args, props) => {
        const [payload = {}] = args;
        const { updateField: key } = props;
        return { updatedField: payload[key] };
      },
    };
    const WrapperContainer = createPubSubConnector(mapSubscriptionsToProps)(Container);

    const tree = TestUtils.renderIntoDocument(
      <ProviderMock pubSubCore={pubSubCore}>
      <ControlledState />
      </ProviderMock>
    );
    const stub = TestUtils.findRenderedComponentWithType(tree, Passthrough);
    const wrapper = TestUtils.findRenderedComponentWithType(tree, ControlledState);

    t.is(stub.props.lastMessage, undefined);
    pubSub.publish(SPEAK, 'Hey there!');
    t.is(stub.props.lastMessage, 'Hey there!');

    t.is(stub.props.updatedField, undefined);
    pubSub.publish(UPDATE, { label: 'myLabel', name: 'myName' });
    t.is(stub.props.updatedField, 'myLabel');
    wrapper.setState({ updateField: 'name' });
    t.is(stub.props.updatedField, 'myName');
    pubSub.publish(UPDATE, { label: 'myLabel', name: 'myNewName' });
    t.is(stub.props.updatedField, 'myNewName');

    pubSub.publish(UPDATE, { label: 'myLabel' });
    t.is(stub.props.updatedField, undefined);

    pubSub.publish(SPEAK, 'New Message');
    t.is(stub.props.lastMessage, 'New Message');

    t.end();
  }
);

test(
  'should update mapped subscriptions defined in `mapSubscriptionsToProps` when'
  + ' props changes only if the callback defines a second arguments in its signature',
  t => {
    const SIMPLE_UPDATE = 'simpleUpdate';
    const pubSubCore = createPubSub();
    const register = pubSubCore.register;
    let pubSub;
    pubSubCore.register = (...args) => {
      pubSub = register(...args);
      return pubSub;
    };

    class Container extends Component {
      render() {
        return (<Passthrough {...this.props} />);
      }
    }

    class ControlledState extends Component {
      constructor() {
        super();
        this.state = { updateField: 'label' };
      }

      render() {
        const { updateField } = this.state;
        return (<WrapperContainer updateField={updateField} />);
      }
    }

    const mapSubscriptionsToProps = {
      [SIMPLE_UPDATE]: (args) => {
        const [payload = {}] = args;
        const now = new Date().getTime();
        return { simpleField: payload.name, now };
      },
    };
    const WrapperContainer = createPubSubConnector(mapSubscriptionsToProps)(Container);

    const tree = TestUtils.renderIntoDocument(
      <ProviderMock pubSubCore={pubSubCore}>
      <ControlledState />
      </ProviderMock>
    );
    const stub = TestUtils.findRenderedComponentWithType(tree, Passthrough);
    const wrapper = TestUtils.findRenderedComponentWithType(tree, ControlledState);

    t.is(stub.props.simpleField, undefined);
    pubSub.publish(SIMPLE_UPDATE, { name: 'john' });
    t.is(stub.props.simpleField, 'john');
    const nowValue = stub.props.now;
    wrapper.setState({ updateField: 'label' });
    t.is(stub.props.now, nowValue);

    t.end();
  }
);

// TODO: create a similar test for 'mapSubscriptionsToProps'
test('should not invoke mapPublish when props change if it only has one argument', t => {
  const pubSubCore = createPubSub();

  let invocationCount = 0;

  const mapPublish = () => {
    invocationCount++;
    return {};
  };

  class WithoutProps extends Component {
    render() {
      return (<Passthrough {...this.props}/>);
    }
  }
  const WrappedContainer = createPubSubConnector(null, mapPublish)(WithoutProps);

  class OuterComponent extends Component {
    constructor() {
      super();
      this.state = { foo: 'FOO' };
    }

    setFoo(foo) {
      this.setState({ foo });
    }

    render() {
      return (
        <div>
        <WrappedContainer {...this.state} />
        </div>
      );
    }
  }

  let outerComponent;
  TestUtils.renderIntoDocument(
    <ProviderMock pubSubCore={pubSubCore}>
    <OuterComponent ref={c => outerComponent = c} />
    </ProviderMock>
  );

  outerComponent.setFoo('BAR');
  outerComponent.setFoo('DID');

  t.is(invocationCount, 1);

  t.end();
});

// TODO: create a similar test for 'mapSubscriptionsToProps'
test('should invoke mapPublish every time props are changed if it has a second argument', t => {
  const pubSubCore = createPubSub();

  let invocationCount = 0;
  let propsPassedIn;

  const mapPublish = (args, props) => { // eslint-disable-line no-unused-vars
    invocationCount++;
    propsPassedIn = props;
    return {};
  };

  class WithoutProps extends Component {
    render() {
      return (<Passthrough {...this.props}/>);
    }
  }
  const WrappedContainer = createPubSubConnector(null, mapPublish)(WithoutProps);

  class OuterComponent extends Component {
    constructor() {
      super();
      this.state = { foo: 'FOO' };
    }

    setFoo(foo) {
      this.setState({ foo });
    }

    render() {
      return (
        <div>
        <WrappedContainer {...this.state} />
        </div>
      );
    }
  }

  let outerComponent;
  TestUtils.renderIntoDocument(
    <ProviderMock pubSubCore={pubSubCore}>
    <OuterComponent ref={c => outerComponent = c} />
    </ProviderMock>
  );

  outerComponent.setFoo('BAR');
  outerComponent.setFoo('DID');

  t.is(invocationCount, 3);
  t.same(propsPassedIn, { foo: 'DID' });

  t.end();
});

test('should pass publish and avoid subscription if arguments are falsy', t => {
  const pubSubCore = createPubSub();
  const register = pubSubCore.register;
  let pubSub;
  pubSubCore.register = (...args) => {
    pubSub = register(...args);
    return pubSub;
  };

  function runCheck(...connectArgs) {
    class Container extends Component {
      render() {
        return (<Passthrough {...this.props} />);
      }
    }
    const WrappedContainer = createPubSubConnector(...connectArgs)(Container);

    const container = TestUtils.renderIntoDocument(
      <ProviderMock pubSubCore={pubSubCore}>
      <WrappedContainer pass="through" />
      </ProviderMock>
    );
    const stub = TestUtils.findRenderedComponentWithType(container, Passthrough);

    t.is(stub.props.publish, pubSub.publish);
    t.is(stub.props.foo, undefined);
    t.is(stub.props.pass, 'through');
    t.doesNotThrow(() => TestUtils.findRenderedComponentWithType(container, WrappedContainer));
    const decorated = TestUtils.findRenderedComponentWithType(container, WrappedContainer);
    t.false(decorated.hasSubscriptions());
  }

  runCheck();
  runCheck(null, null, null);
  runCheck(false, false, false);

  t.end();
});
<|endoftext|>


<commit_before>/**
 * sails-generate-generator
 *
 * Usage:
 * `sails generate generator :type`
 *
 * @scope {String} type    [required, type of generator to create]
 *
 * @type {Object}
 */
module.exports = {

	templatesDirectory: require('path').resolve(__dirname,'../templates'),

	before: require('./before'),

	after: function (scope, cb) {
		// TODO: install this generator's dependencies
		cb();
	},

	targets: {
		'./Generator.js':       { template: 'Generator.js' },
		'./CONTRIBUTING.md':    { template: 'CONTRIBUTING.md' },
		'./FAQ.md':             { template: 'FAQ.md' },
		'./LICENSE':            { template: 'LICENSE' },
		'./README.md':          { template: 'README.md' },
		'./.gitignore':         { template: '.gitignore' },
		'./.jshintrc':          { template: '.jshintrc' },
		'./.editorconfig':      { template: '.editorconfig' },
		'./package.json':       { template: 'package.json' },
		'./templates/.gitkeep': { template: 'templates/.gitkeep' },
		'./templates/example.template.js': { copy: 'templates/example.template.js' },

		// Skip tests for now to keep it simple:
		// './:generatorName/test/runner.js':     { template: _pathTo('test/runner.js') },
		// './:generatorName/test/mocha.opts':    { template: _pathTo('test/mocha.opts') },
		// './:generatorName/test/helpers/lifecycle.js':  { template: _pathTo('test/helpers/lifecycle.js') },
	}
};
<commit_msg>Update index.js<commit_after>/**
 * sails-generate-generator
 *
 * Usage:
 * `sails generate generator :type`
 *
 * @scope {String} type    [required, type of generator to create]
 *
 * @type {Object}
 */
module.exports = {

	templatesDirectory: require('path').resolve(__dirname,'../templates'),

	before: require('./before'),

	after: function (scope, cb) {
		// TODO: install this generator's dependencies
		cb();
	},

	targets: {
		'./Generator.js':       { template: 'Generator.js' },
		'./CONTRIBUTING.md':    { template: 'CONTRIBUTING.md' },
		'./FAQ.md':             { template: 'FAQ.md' },
		'./LICENSE':            { template: 'LICENSE' },
		'./README.md':          { template: 'README.md' },
		'./.gitignore':         { template: 'gitignore' },
		'./.jshintrc':          { template: '.jshintrc' },
		'./.editorconfig':      { template: '.editorconfig' },
		'./package.json':       { template: 'package.json' },
		'./templates/.gitkeep': { template: 'templates/.gitkeep' },
		'./templates/example.template.js': { copy: 'templates/example.template.js' },

		// Skip tests for now to keep it simple:
		// './:generatorName/test/runner.js':     { template: _pathTo('test/runner.js') },
		// './:generatorName/test/mocha.opts':    { template: _pathTo('test/mocha.opts') },
		// './:generatorName/test/helpers/lifecycle.js':  { template: _pathTo('test/helpers/lifecycle.js') },
	}
};
<|endoftext|>


<commit_before>/****************************
 * Misskey Core gulp settings
 ****************************/

'use strict';

const gulp = require('gulp');
const babel = require('gulp-babel');
const ts = require('gulp-typescript');
const tslint = require('gulp-tslint');
const stylus = require('gulp-stylus');
const es = require('event-stream');

const project = ts.createProject('tsconfig.json');

gulp.task('build', [
	'build:js',
	'build:ts',
	'build:styles',
	'build:copy'
]);

gulp.task('build:js', () => {
	return gulp.src('./src/**/*.js')
		.pipe(babel({
			presets: ['es2015', 'stage-3']
		}))
		.pipe(gulp.dest('./built/'));
});

gulp.task('build:ts', () => {
	project
		.src()
		.pipe(ts(project))
		.pipe(babel({
			presets: ['es2015', 'stage-3']
		}))
		.pipe(gulp.dest('./built/'));
});

gulp.task('build:styles', () => {
	return gulp.src('./src/web/**/*.styl')
		.pipe(stylus())
		.pipe(gulp.dest('./built/web/'));
});

gulp.task('build:copy', () => {
	return es.merge(
		gulp.src([
			'./src/resources/**/*'
		]).pipe(gulp.dest('./built/resources/')),
		gulp.src([
			'./src/web/**/*',
			'!**/*.ts',
			'!**/*.ls',
			'!**/*.styl'
		]).pipe(gulp.dest('./built/web/'))
	);
});

gulp.task('test', [
	'lint'
]);

gulp.task('lint', () =>
	gulp.src('./src/**/*.ts')
	.pipe(tslint())
	.pipe(tslint.report('verbose'))
);
<commit_msg>gulpfile: follow tslint update<commit_after>/****************************
 * Misskey Core gulp settings
 ****************************/

'use strict';

const gulp = require('gulp');
const babel = require('gulp-babel');
const ts = require('gulp-typescript');
const tslint = require('gulp-tslint');
const stylus = require('gulp-stylus');
const es = require('event-stream');

const project = ts.createProject('tsconfig.json');

gulp.task('build', [
	'build:js',
	'build:ts',
	'build:styles',
	'build:copy'
]);

gulp.task('build:js', () => {
	return gulp.src('./src/**/*.js')
		.pipe(babel({
			presets: ['es2015', 'stage-3']
		}))
		.pipe(gulp.dest('./built/'));
});

gulp.task('build:ts', () => {
	project
		.src()
		.pipe(ts(project))
		.pipe(babel({
			presets: ['es2015', 'stage-3']
		}))
		.pipe(gulp.dest('./built/'));
});

gulp.task('build:styles', () => {
	return gulp.src('./src/web/**/*.styl')
		.pipe(stylus())
		.pipe(gulp.dest('./built/web/'));
});

gulp.task('build:copy', () => {
	return es.merge(
		gulp.src([
			'./src/resources/**/*'
		]).pipe(gulp.dest('./built/resources/')),
		gulp.src([
			'./src/web/**/*',
			'!**/*.ts',
			'!**/*.ls',
			'!**/*.styl'
		]).pipe(gulp.dest('./built/web/'))
	);
});

gulp.task('test', [
	'lint'
]);

gulp.task('lint', () =>
	gulp.src('./src/**/*.ts')
		.pipe(tslint({
			formatter: "verbose"
		}))
		.pipe(tslint.report())
);
<|endoftext|>


<commit_before>function cleanErrors() {
    $("#errorText").html("");
    $("#errorModal").modal('hide');
}

function cleanForm() {
    $("#longUrl").val("");
}

function cleanResults() {
    $("#resultLink").html("").attr("href", "");
    $("#result").addClass('invisible');

    $("#qrCode img").attr("src", "");
    $("#qrCode").addClass('invisible');
}

function showError(errorMessage) {
    $("#errorText").html(errorMessage);
    $("#errorModal").modal('show');
}

function sendLink(long_url) {
    var storeLinkRoute = "/api/store";

    var longUrl = long_url.trim();
    var json = {
        link: longUrl
    };
    doPost(storeLinkRoute, json, onSuccessStoreLink, onFailStoreLink);
}

function onSuccessStoreLink(data, textStatus, jqXHR) {
    cleanErrors();
    cleanForm();
    if (jqXHR.status === 201) {
        var ident = data.ident;
        if (ident === undefined || ident.trim().length === 0) {
            showError("Internal error. Got malformed reply from server");
            return;
        }

        $("#resultLink").html(window.location.origin + "/" + ident).attr("href", ident);
        $("#result").removeClass('invisible');
        updateCounter();
        generateQRCode(ident);
    }
}

function onFailStoreLink(jqXHR, textStatus, errorThrown) {
    if (jqXHR !== null || jqXHR !== undefined) {

        var replyRaw = jqXHR.responseText;
        console.log("Reply JSON: " + replyRaw);
        var reply = JSON.parse(replyRaw);

        var errors = [];

        if (reply.errors !== undefined) {
            if (reply.errors.length > 0) {
                $.each(reply.errors, function (index, value) {
                    if (value.field === "link") {
                        value.field = "Long URL";
                    }
                    errors.push(value.field + " " + value.errorMessage);
                });
            }
        } else if (reply.error !== undefined) {
            errors.push(reply.error.field + " " + reply.error.errorMessage);
        }

        var errorText = "Validation failed
";
        if (errors.length >= 0) {
            errorText += "Errors:
";
            $.each(errors, function (index, value) {
                errorText += value + "
";
            });
        }
        showError(errorText);
    }
}

function copyLinkToClipboard() {
    var $temp = $("<input>");
    $("body").append($temp);
    $temp.val($("#resultLink").text()).select();
    document.execCommand("copy");
    $temp.remove();

    $("#linkCopiedModal").modal('show');
    setTimeout(function () {
        $("#linkCopiedModal").modal('hide');
    }, 1000);
}

function updateCounter() {
    var counter = $("#overallLinksNum");
    var currentNum = counter.text();

    if ($.isNumeric(currentNum)) {
        counter.text(parseInt(currentNum) + 1);
    } else {
        console.error("Failed to update counter. Current counter value is not a number")
    }
}

function calculateQRCodeSize() {
    var browserWidth = $(window).width();
    var defaultQRBlockSize = 371;
    var defaultQRCodeSize = 350;
    var qrBlockRatio = 0.943; //350/371

    var size;
    if (browserWidth > defaultQRBlockSize) {
        size = defaultQRCodeSize;
    } else {
        size = browserWidth * qrBlockRatio;
    }

    return size;
}

function generateQRCode(ident) {
    var size = calculateQRCodeSize();
    var qrCodeGeneratorRoute = "/api/qrCode/" + ident + "/" + parseInt(size);
    doGet(qrCodeGeneratorRoute, onSuccessGenerateQRCode, onFailGenerateQRCode);
}

function onSuccessGenerateQRCode(data, textStatus, jqXHR) {
    if (jqXHR.status === 200) {
        var qrCode = data.qrCode;
        if (qrCode === undefined || qrCode.trim().length === 0) {
            showError("Internal error. Got malformed reply from QR generator");
            return;
        }

        $("#qrCode img").attr('src', qrCode);
        $("#qrCode").removeClass('invisible');
    }
}

function onFailGenerateQRCode(jqXHR, textStatus, errorThrown) {
    showError("Internal error. Got malformed reply from QR generator");
    $("#qrCode img").attr("src", "");
    $("#qrCode").addClass('invisible');

    if (jqXHR !== null) {
        var replyRaw = jqXHR.responseText;
        console.debug("QR Code Reply JSON: " + replyRaw);
        console.debug("QR Code Reply TextStatus: " + textStatus);
        console.debug("QR Code Reply ErrorThrown: " + errorThrown);
    }
}

function handleForm(e) {
    e.preventDefault();
    cleanErrors();
    cleanResults();

    var isFormValid = true;

    var longUrl = $("#longUrl").val();
    console.log("Got long URL: " + longUrl);
    cleanForm();
    if (longUrl === undefined || longUrl.trim().length === 0) {
        var errorMessage = "Long URL cannot be empty";
        showError(errorMessage);
        isFormValid = false;
    }
    if (isFormValid) {
        sendLink(longUrl);
    }
}

$(document).ready(function () {
    $("#shortenIt").on('click', handleForm);
    $('[data-toggle="tooltip"]').tooltip();
    $("#copyLink").on('click', copyLinkToClipboard);
});
<commit_msg>Changes in front.js<commit_after>function cleanErrors() {
    $("#errorText").html("");
    $("#errorModal").modal("hide");
}

function cleanForm() {
    $("#longUrl").val("");
}

function cleanResults() {
    $("#resultLink").html("").attr("href", "");
    $("#result").addClass("invisible");

    $("#qrCode img").attr("src", "");
    $("#qrCode").addClass("invisible");
}

function showError(errorMessage) {
    $("#errorText").html(errorMessage);
    $("#errorModal").modal("show");
}

function updateCounter() {
    var counter = $("#overallLinksNum");
    var currentNum = counter.text();

    if ($.isNumeric(currentNum)) {
        counter.text(parseInt(currentNum) + 1);
    } else {
        console.error("Failed to update counter. Current counter value is not a number")
    }
}

function calculateQRCodeSize() {
    var browserWidth = $(window).width();
    var defaultQRBlockSize = 371;
    var defaultQRCodeSize = 350;
    var qrBlockRatio = 0.943; //350/371

    var size;
    if (browserWidth > defaultQRBlockSize) {
        size = defaultQRCodeSize;
    } else {
        size = browserWidth * qrBlockRatio;
    }

    return size;
}

function generateQRCode(ident) {
    var size = calculateQRCodeSize();
    var qrCodeGeneratorRoute = "/api/qrCode/" + ident + "/" + parseInt(size, 10);
    doGet(qrCodeGeneratorRoute, onSuccessGenerateQRCode, onFailGenerateQRCode);
}

function onFailGenerateQRCode(jqXHR, textStatus, errorThrown) {
    showError("Internal error. Got malformed reply from QR generator");
    $("#qrCode img").attr("src", "");
    $("#qrCode").addClass('invisible');

    if (jqXHR !== null) {
        var replyRaw = jqXHR.responseText;
        console.debug("QR Code Reply JSON: " + replyRaw);
        console.debug("QR Code Reply TextStatus: " + textStatus);
        console.debug("QR Code Reply ErrorThrown: " + errorThrown);
    }
}

function onSuccessStoreLink(data, textStatus, jqXHR) {
    cleanErrors();
    cleanForm();
    if (jqXHR.status === 201) {
        var ident = data.ident;
        if (ident === null || ident.trim().length === 0) {
            showError("Internal error. Got malformed reply from server");
            return;
        }

        $("#resultLink").html(window.location.origin + "/" + ident).attr("href", ident);
        $("#result").removeClass("invisible");
        updateCounter();
        generateQRCode(ident);
    }
}

function onSuccessGenerateQRCode(data, textStatus, jqXHR) {
    if (jqXHR.status === 200) {
        var qrCode = data.qrCode;
        if (qrCode.trim().length === 0) {
            showError("Internal error. Got malformed reply from QR generator");
            return;
        }

        $("#qrCode img").attr("src", qrCode);
        $("#qrCode").removeClass("invisible");
    }
}

function onFailStoreLink(jqXHR, textStatus, errorThrown) {
    if (jqXHR !== null) {

        var replyRaw = jqXHR.responseText;
        console.debug("Reply JSON: " + replyRaw);
        var reply = JSON.parse(replyRaw);

        var errors = [];

        if (reply.errors !== undefined) {
            if (reply.errors.length > 0) {
                $.each(reply.errors, function (index, value) {
                    if (value.field === "link") {
                        value.field = "Long URL";
                    }
                    errors.push(value.field + " " + value.errorMessage);
                });
            }
        } else if (reply.error !== undefined) {
            errors.push(reply.error.field + " " + reply.error.errorMessage);
        }

        var errorText = "Validation failed
";
        if (errors.length >= 0) {
            errorText += "Errors:
";
            $.each(errors, function (index, value) {
                errorText += value + "
";
            });
        }
        showError(errorText);
    }
}

function sendLink(longUrl) {
    var storeLinkRoute = "/api/store";

    longUrl = longUrl.trim();
    var json = {
        link: longUrl
    };
    doPost(storeLinkRoute, json, onSuccessStoreLink, onFailStoreLink);
}

function copyLinkToClipboard() {
    var $temp = $("<input>");
    $("body").append($temp);
    $temp.val($("#resultLink").text()).select();
    document.execCommand("copy");
    $temp.remove();

    $("#linkCopiedModal").modal("show");
    setTimeout(function () {
        $("#linkCopiedModal").modal("hide");
    }, 1000);
}

function handleForm(e) {
    e.preventDefault();
    cleanErrors();
    cleanResults();

    var isFormValid = true;

    var longUrl = $("#longUrl").val();
    console.debug("Got long URL: " + longUrl);
    cleanForm();
    if (longUrl.trim().length === 0) {
        var errorMessage = "Long URL cannot be empty";
        showError(errorMessage);
        isFormValid = false;
    }
    if (isFormValid) {
        sendLink(longUrl);
    }
}

$(document).ready(function () {
    $("#shortenIt").on("click", handleForm);
    $('[data-toggle="tooltip"]').tooltip();
    $("#copyLink").on("click", copyLinkToClipboard);
});
<|endoftext|>


<commit_before>// Runner for unit tests which are written in bash. For each file in the
// oref0/tests directory whose name ends in .sh, generates a separate test
// which runs it and asserts that it exits with status 0 (success).
"use strict"

var should = require('should');
var fs = require("fs");
var path = require("path");
var child_process = require("child_process");

describe("shell-script tests", function() {
    var bashUnitTestFiles = [];
    fs.readdirSync("tests").forEach(function(filename) {
        if(filename.endsWith(".sh"))
            bashUnitTestFiles.push(path.join("tests", filename));
    });

    bashUnitTestFiles.forEach(function(testFile) {
        it(testFile, function() {
            var utilProcess = child_process.spawnSync(testFile, [], {
                timeout: 1000, //milliseconds
                encoding: "UTF-8",
            });

            should.equal(utilProcess.status, 0, "Bash unit test returned failure.");
        });
    });
});<commit_msg>print hint for getting more info on Bash unit test failures<commit_after>// Runner for unit tests which are written in bash. For each file in the
// oref0/tests directory whose name ends in .sh, generates a separate test
// which runs it and asserts that it exits with status 0 (success).
"use strict"

var should = require('should');
var fs = require("fs");
var path = require("path");
var child_process = require("child_process");

describe("shell-script tests", function() {
    var bashUnitTestFiles = [];
    fs.readdirSync("tests").forEach(function(filename) {
        if(filename.endsWith(".sh"))
            bashUnitTestFiles.push(path.join("tests", filename));
    });

    bashUnitTestFiles.forEach(function(testFile) {
        it(testFile, function() {
            var utilProcess = child_process.spawnSync(testFile, [], {
                timeout: 1000, //milliseconds
                encoding: "UTF-8",
            });

            //console.error(utilProcess.error);
            should.equal(utilProcess.status, 0, "Bash unit test returned failure: run " + testFile + " manualy for details.");
        });
    });
});
<|endoftext|>