<commit_before>// ----------------------------------------------------------------------------- [Imports]

let Command = require('../Command')
let Utils = require('../utils')

// ----------------------------------------------------------------------------- [Class]

class MakeModel extends Command {

  /**
   * Create a new MakeModel instance.
   *
   * @param args
   */
  constructor (args) {
    // execute the super class constructor method
    super()

    // define usage
    this.usage = 'stellar makeModel <model_name> --module=<module_name> [--options]'

    // save the parsed console arguments
    this.args = args
  }

  /**
   * Execute the command.
   */
  execute () {
    // we need at least two arguments. One: the command name, two: the model name
    if (this.args._.length < 2) {
      this.printUsage()
      return false
    }

    // we need the module name here the model must be created
    if (this.args.module === undefined || typeof this.args.module !== 'string' || this.args.module.length === 0) {
      this.printError('You need to specify the module here the model must be created')
      return false
    }

    // check if the entered module name exists
    if (!Utils.moduleExists(this.args.module)) {
      this.printError(`The module "${this.args.module}" does not exists`)
      return false
    }

    // build module path
    const modulePath = `${Utils.getCurrentUniverse()}/modules/${this.args.module}`

    // ensure the models folder exists
    Utils.createFolder(`${modulePath}/models`)

    // build the new model file path
    let modelNameNormalized = this.args._[ 1 ].toLowerCase()
    let modelNameCapitalize = modelNameNormalized.charAt(0).toUpperCase() + modelNameNormalized.slice(1)
    let newFilePath = `${modulePath}/models/${modelNameNormalized}.js`

    // create the new model file
    Utils.createFile(newFilePath, Utils.getTemplate('model'))

    // print success message
    this.printSuccess(`The "${this.args._[ 1 ]}" model was created!`)

    // check if is to create an action file with the crud operations
    if (this.args.crud !== undefined) {
      // hash with the data to use on the template
      let data = {
        modelName: modelNameNormalized,
        modelNameCapitalize: modelNameCapitalize,
        rest: (this.args.rest !== undefined)
      }

      // build the output path
      let actionFilePath = `${modulePath}/actions/${modelNameNormalized}.js`

      // process the template
      Utils.generateFileFromTemplate('actionCrud', data, actionFilePath)

      // print success message
      this.printSuccess(`The CRUD operations for the "${this.args._[ 1 ]}" model was created!`)
    }

    // if the crud and rest options are present generate actions
    if (this.args.crud !== undefined && this.args.rest !== undefined) {
      let routes = {
        all: [],
        get: [],
        post: [],
        put: [],
        delete: []
      }

      // if the routes.json file exists load it
      if (Utils.exists(`${modulePath}/routes.json`)) { routes = require(`${modulePath}/routes.json`) }

      // add the new routes
      routes.get.push({ path: `/${modelNameNormalized}`, action: `get${modelNameCapitalize}s` })
      routes.get.push({ path: `/${modelNameNormalized}/:id`, action: `get${modelNameCapitalize}` })
      routes.post.push({ path: `/${modelNameNormalized}`, action: `create${modelNameCapitalize}` })
      routes.put.push({ path: `/${modelNameNormalized}/:id`, action: `edit${modelNameCapitalize}` })
      routes.delete.push({ path: `/${modelNameNormalized}/:id`, action: `remove${modelNameCapitalize}` })

      // save the new file
      Utils.createFile(`${modulePath}/routes.json`, JSON.stringify(routes, null, 2))

      // success
      this.printSuccess(`The routes for the "${this.args._[ 1 ]}" model was created!`)
    }

    return true
  }

}

// exports the function to execute the command
module.exports = args => (new MakeModel(args)).execute()
<commit_msg>add missing strict mode<commit_after>'use strict'

// ----------------------------------------------------------------------------- [Imports]

let Command = require('../Command')
let Utils = require('../utils')

// ----------------------------------------------------------------------------- [Class]

class MakeModel extends Command {

  /**
   * Create a new MakeModel instance.
   *
   * @param args
   */
  constructor (args) {
    // execute the super class constructor method
    super()

    // define usage
    this.usage = 'stellar makeModel <model_name> --module=<module_name> [--options]'

    // save the parsed console arguments
    this.args = args
  }

  /**
   * Execute the command.
   */
  execute () {
    // we need at least two arguments. One: the command name, two: the model name
    if (this.args._.length < 2) {
      this.printUsage()
      return false
    }

    // we need the module name here the model must be created
    if (this.args.module === undefined || typeof this.args.module !== 'string' || this.args.module.length === 0) {
      this.printError('You need to specify the module here the model must be created')
      return false
    }

    // check if the entered module name exists
    if (!Utils.moduleExists(this.args.module)) {
      this.printError(`The module "${this.args.module}" does not exists`)
      return false
    }

    // build module path
    const modulePath = `${Utils.getCurrentUniverse()}/modules/${this.args.module}`

    // ensure the models folder exists
    Utils.createFolder(`${modulePath}/models`)

    // build the new model file path
    let modelNameNormalized = this.args._[ 1 ].toLowerCase()
    let modelNameCapitalize = modelNameNormalized.charAt(0).toUpperCase() + modelNameNormalized.slice(1)
    let newFilePath = `${modulePath}/models/${modelNameNormalized}.js`

    // create the new model file
    Utils.createFile(newFilePath, Utils.getTemplate('model'))

    // print success message
    this.printSuccess(`The "${this.args._[ 1 ]}" model was created!`)

    // check if is to create an action file with the crud operations
    if (this.args.crud !== undefined) {
      // hash with the data to use on the template
      let data = {
        modelName: modelNameNormalized,
        modelNameCapitalize: modelNameCapitalize,
        rest: (this.args.rest !== undefined)
      }

      // build the output path
      let actionFilePath = `${modulePath}/actions/${modelNameNormalized}.js`

      // process the template
      Utils.generateFileFromTemplate('actionCrud', data, actionFilePath)

      // print success message
      this.printSuccess(`The CRUD operations for the "${this.args._[ 1 ]}" model was created!`)
    }

    // if the crud and rest options are present generate actions
    if (this.args.crud !== undefined && this.args.rest !== undefined) {
      let routes = {
        all: [],
        get: [],
        post: [],
        put: [],
        delete: []
      }

      // if the routes.json file exists load it
      if (Utils.exists(`${modulePath}/routes.json`)) { routes = require(`${modulePath}/routes.json`) }

      // add the new routes
      routes.get.push({ path: `/${modelNameNormalized}`, action: `get${modelNameCapitalize}s` })
      routes.get.push({ path: `/${modelNameNormalized}/:id`, action: `get${modelNameCapitalize}` })
      routes.post.push({ path: `/${modelNameNormalized}`, action: `create${modelNameCapitalize}` })
      routes.put.push({ path: `/${modelNameNormalized}/:id`, action: `edit${modelNameCapitalize}` })
      routes.delete.push({ path: `/${modelNameNormalized}/:id`, action: `remove${modelNameCapitalize}` })

      // save the new file
      Utils.createFile(`${modulePath}/routes.json`, JSON.stringify(routes, null, 2))

      // success
      this.printSuccess(`The routes for the "${this.args._[ 1 ]}" model was created!`)
    }

    return true
  }

}

// exports the function to execute the command
module.exports = args => (new MakeModel(args)).execute()
<|endoftext|>


<commit_before>'use strict';

var L = require('leaflet');

var MaterialGeocoderControl = L.mapbox.GeocoderControl.extend({
  onAdd: function(map) {
    var container = L.DomUtil.create('div', 'leaflet-control-geocoder-mdl leaflet-bar-mdl leaflet-control'),
        form = L.DomUtil.create('form', 'geocoder-form', container),
        textField = L.DomUtil.create('div', 'mdl-textfield mdl-js-textfield mdl-shadow--2dp', form),
        input = L.DomUtil.create('input', 'mdl-textfield__input', textField),
        label = L.DomUtil.create('label', 'mdl-textfield__label', textField),
        icon = L.DomUtil.create('i', 'material-icons', label),
        clearSearchButton = L.DomUtil.create('button', 'mdl-button mdl-js-button mdl-button--icon clear-search', textField),
        clearSearchIcon = L.DomUtil.create('i', 'material-icons', clearSearchButton),
        progressBar = L.DomUtil.create('div', 'mdl-progress mdl-js-progress mdl-progress__indeterminate', textField),
        results = L.DomUtil.create('div', 'leaflet-control-geocoder-results-mdl mdl-shadow--2dp', container);

    icon.innerHTML = 'search';
    clearSearchIcon.innerHTML = 'close';
    label.setAttribute('for', 'mapbox-geocoder-mdl')
    input.setAttribute('id', 'mapbox-geocoder-mdl')
    input.type = 'text';
    input.setAttribute('placeholder', 'Search...');

    // L.DomUtil.addClass(results, 'show-results');
    // this._map.on('click', this._closeResults, this);

    clearSearchButton.type = "button";
    L.DomEvent.addListener(clearSearchButton, 'click', this._clearSearch, this);

    L.DomEvent.addListener(form, 'submit', this._geocode, this);
    L.DomEvent.addListener(input, 'keyup', this._autocomplete, this);
    L.DomEvent.disableClickPropagation(container);

    this._textField = textField;
    this._map = map;
    this._results = results;
    this._input = input;
    this._form = form;

    return container;
  },
  _clearSearch: function() {
    this._input.value = null;
    L.DomUtil.removeClass(this._textField, 'is-dirty');
    this._closeResults();
  },
  _closeResults: function() {
    L.DomUtil.removeClass(this._results, 'show-results');
    // have results fade out before removing result elements
    if (this._results.childNodes.length > 0) {
      setTimeout(function(results) {
        while (results.firstChild) {
            results.removeChild(results.firstChild);
        };
      }, 500, this._results);
    }
  },
  _displayResults: function(features) {
    L.DomUtil.addClass(this._results, 'show-results');
    if (features.length === 0) {
      var message = L.DomUtil.create('span', 'no-results-message', this._results);
      message.innerHTML = 'No results found.';
    } else {
      L.mapbox.GeocoderControl.prototype._displayResults.apply(this, [features]);
    }
  },
});

module.exports.MaterialGeocoderControl = MaterialGeocoderControl;

module.exports.materialGeocoderControl = function(_, options) {
  return new MaterialGeocoderControl(_, options);
};<commit_msg>scope mapbox dependency to geocoder<commit_after>'use strict';

var L = require('leaflet');

if(!L.mapbox) {
  console.error('Mapbox is not defined.  Geocoder control will not be available');
  module.exports.MaterialGeocoderControl = undefined;
  module.exports.materialGeocoderControl = undefined;

  return;
}

var MaterialGeocoderControl = L.mapbox.GeocoderControl.extend({
  onAdd: function(map) {
    var container = L.DomUtil.create('div', 'leaflet-control-geocoder-mdl leaflet-bar-mdl leaflet-control'),
        form = L.DomUtil.create('form', 'geocoder-form', container),
        textField = L.DomUtil.create('div', 'mdl-textfield mdl-js-textfield mdl-shadow--2dp', form),
        input = L.DomUtil.create('input', 'mdl-textfield__input', textField),
        label = L.DomUtil.create('label', 'mdl-textfield__label', textField),
        icon = L.DomUtil.create('i', 'material-icons', label),
        clearSearchButton = L.DomUtil.create('button', 'mdl-button mdl-js-button mdl-button--icon clear-search', textField),
        clearSearchIcon = L.DomUtil.create('i', 'material-icons', clearSearchButton),
        progressBar = L.DomUtil.create('div', 'mdl-progress mdl-js-progress mdl-progress__indeterminate', textField),
        results = L.DomUtil.create('div', 'leaflet-control-geocoder-results-mdl mdl-shadow--2dp', container);

    icon.innerHTML = 'search';
    clearSearchIcon.innerHTML = 'close';
    label.setAttribute('for', 'mapbox-geocoder-mdl')
    input.setAttribute('id', 'mapbox-geocoder-mdl')
    input.type = 'text';
    input.setAttribute('placeholder', 'Search...');

    // L.DomUtil.addClass(results, 'show-results');
    // this._map.on('click', this._closeResults, this);

    clearSearchButton.type = "button";
    L.DomEvent.addListener(clearSearchButton, 'click', this._clearSearch, this);

    L.DomEvent.addListener(form, 'submit', this._geocode, this);
    L.DomEvent.addListener(input, 'keyup', this._autocomplete, this);
    L.DomEvent.disableClickPropagation(container);

    this._textField = textField;
    this._map = map;
    this._results = results;
    this._input = input;
    this._form = form;

    return container;
  },
  _clearSearch: function() {
    this._input.value = null;
    L.DomUtil.removeClass(this._textField, 'is-dirty');
    this._closeResults();
  },
  _closeResults: function() {
    L.DomUtil.removeClass(this._results, 'show-results');
    // have results fade out before removing result elements
    if (this._results.childNodes.length > 0) {
      setTimeout(function(results) {
        while (results.firstChild) {
            results.removeChild(results.firstChild);
        };
      }, 500, this._results);
    }
  },
  _displayResults: function(features) {
    L.DomUtil.addClass(this._results, 'show-results');
    if (features.length === 0) {
      var message = L.DomUtil.create('span', 'no-results-message', this._results);
      message.innerHTML = 'No results found.';
    } else {
      L.mapbox.GeocoderControl.prototype._displayResults.apply(this, [features]);
    }
  },
});

module.exports.MaterialGeocoderControl = MaterialGeocoderControl;

module.exports.materialGeocoderControl = function(_, options) {
  return new MaterialGeocoderControl(_, options);
};
<|endoftext|>


<commit_before>
/**
 * Create a pseudorandom number generator, with a seed.
 * @param {array} seed "128-bit" integer, composed of 4x32-bit integers in big endian order.
 */
function XorShift(seed) {
  if (!Array.isArray(seed) || seed.length !== 4) {
    throw new TypeError('seed must be array with 4 elements');
  }

  // uint64_t s = [seed ...]
  this._state0U = seed[0] | 0;
  this._state0L = seed[1] | 0;
  this._state1U = seed[2] | 0;
  this._state1L = seed[3] | 0;
}

// There is nothing particularly scientific about this seed, it is just
// based on the clock.
module.exports = new XorShift([
  0, Date.now() / 65536,
  0, Date.now() % 65536
]);
/**
 * Returns a random number normalized [0, 1[, just like Math.random()
 * @return {number}
 */
XorShift.prototype.randint = function() {
  // uint64_t s1 = s[0]
  var s1U = this._state0U, s1L = this._state0L;
  // uint64_t s0 = s[1]
  var s0U = this._state1U, s0L = this._state1L;

  // s[0] = s0
  this._state0U = s0U;
  this._state0L = s0L;

  // - t1 = [0, 0]
  var t1U = 0, t1L = 0;
  // - t2 = [0, 0]
  var t2U = 0, t2L = 0;

  // s1 ^= s1 << 23;
  // :: t1 = s1 << 23
  var a1 = 23;
  var m1 = 0xFFFFFFFF << (32 - a1);
  t1U = (s1U << a1) | ((s1L & m1) >>> (32 - a1));
  t1L = s1L << a1;
  // :: s1 = s1 ^ t1
  s1U = s1U ^ t1U;
  s1L = s1L ^ t1L;

  // t1 = ( s1 ^ s0 ^ ( s1 >> 17 ) ^ ( s0 >> 26 ) )
  // :: t1 = s1 ^ s0
  t1U = s1U ^ s0U;
  t1L = s1L ^ s0L;
  // :: t2 = s1 >> 17
  var a2 = 17;
  var m2 = 0xFFFFFFFF >>> (32 - a2);
  t2U = s1U >>> a2;
  t2L = (s1L >>> a2) | ((s1U & m2) << (32 - a2));
  // :: t1 = t1 ^ t2
  t1U = t1U ^ t2U;
  t1L = t1L ^ t2L;
  // :: t2 = s0 >> 26
  var a3 = 26;
  var m3 = 0xFFFFFFFF >>> (32 - a3);
  t2U = s0U >>> a3;
  t2L = (s0L >>> a3) | ((s0U & m3) << (32 - a3));
  // :: t1 = t1 ^ t2
  t1U = t1U ^ t2U;
  t1L = t1L ^ t2L;

  // s[1] = t1
  this._state1U = t1U;
  this._state1L = t1L;

  // return (t1 + s0) / 2**64
  // :: t2 = t1 + s0
  var sumL = (t1L >>> 0) + (s0L >>> 0);
  t2U = (t1U + s0U + (sumL / 2 >>> 31)) >>> 0;
  t2L = sumL >>> 0;

  // :: ret t2
  return [t2U, t2L];
};

/**
 * Returns a random number normalized [0, 1[, just like Math.random()
 * @return {number}
 */
XorShift.prototype.rand = function() {
  var t2 = this.randint();

  // :: ret t2 / 2**64
  return (t2[0] * 4294967296 + t2[1]) / 18446744073709551616;
};
<commit_msg>[fix] minor comment fix<commit_after>
/**
 * Create a pseudorandom number generator, with a seed.
 * @param {array} seed "128-bit" integer, composed of 4x32-bit integers in big endian order.
 */
function XorShift(seed) {
  if (!Array.isArray(seed) || seed.length !== 4) {
    throw new TypeError('seed must be array with 4 elements');
  }

  // uint64_t s = [seed ...]
  this._state0U = seed[0] | 0;
  this._state0L = seed[1] | 0;
  this._state1U = seed[2] | 0;
  this._state1L = seed[3] | 0;
}

// There is nothing particularly scientific about this seed, it is just
// based on the clock.
module.exports = new XorShift([
  0, Date.now() / 65536,
  0, Date.now() % 65536
]);
/**
 * Returns a random number normalized [0, 1[, just like Math.random()
 * @return {number}
 */
XorShift.prototype.randint = function() {
  // uint64_t s1 = s[0]
  var s1U = this._state0U, s1L = this._state0L;
  // uint64_t s0 = s[1]
  var s0U = this._state1U, s0L = this._state1L;

  // s[0] = s0
  this._state0U = s0U;
  this._state0L = s0L;

  // - t1 = [0, 0]
  var t1U = 0, t1L = 0;
  // - t2 = [0, 0]
  var t2U = 0, t2L = 0;

  // s1 ^= s1 << 23;
  // :: t1 = s1 << 23
  var a1 = 23;
  var m1 = 0xFFFFFFFF << (32 - a1);
  t1U = (s1U << a1) | ((s1L & m1) >>> (32 - a1));
  t1L = s1L << a1;
  // :: s1 = s1 ^ t1
  s1U = s1U ^ t1U;
  s1L = s1L ^ t1L;

  // t1 = ( s1 ^ s0 ^ ( s1 >> 17 ) ^ ( s0 >> 26 ) )
  // :: t1 = s1 ^ s0
  t1U = s1U ^ s0U;
  t1L = s1L ^ s0L;
  // :: t2 = s1 >> 17
  var a2 = 17;
  var m2 = 0xFFFFFFFF >>> (32 - a2);
  t2U = s1U >>> a2;
  t2L = (s1L >>> a2) | ((s1U & m2) << (32 - a2));
  // :: t1 = t1 ^ t2
  t1U = t1U ^ t2U;
  t1L = t1L ^ t2L;
  // :: t2 = s0 >> 26
  var a3 = 26;
  var m3 = 0xFFFFFFFF >>> (32 - a3);
  t2U = s0U >>> a3;
  t2L = (s0L >>> a3) | ((s0U & m3) << (32 - a3));
  // :: t1 = t1 ^ t2
  t1U = t1U ^ t2U;
  t1L = t1L ^ t2L;

  // s[1] = t1
  this._state1U = t1U;
  this._state1L = t1L;

  // return t1 + s0
  // :: t2 = t1 + s0
  var sumL = (t1L >>> 0) + (s0L >>> 0);
  t2U = (t1U + s0U + (sumL / 2 >>> 31)) >>> 0;
  t2L = sumL >>> 0;

  // :: ret t2
  return [t2U, t2L];
};

/**
 * Returns a random number normalized [0, 1[, just like Math.random()
 * @return {number}
 */
XorShift.prototype.rand = function() {
  var t2 = this.randint();

  // :: ret t2 / 2**64
  return (t2[0] * 4294967296 + t2[1]) / 18446744073709551616;
};
<|endoftext|>


<commit_before>require('./header')('Flush');

var settings = require('./settings').sharedInstance(),
    Mongo = require('./mongo'),
    Memcached = require('memcached'),
    readline = require('readline'),
    Redis = require('ioredis'),
    logger = require('npmlog');

var run = function() {
    var rl = readline.createInterface(process.stdin, process.stdout);
    console.log(['',
                 '',
                 ' Heads up! You are about to destroy all precached data and reset all item\'s',
                 ' database state. After cleaning up memcached and resetting the database, this',
                 ' will enqueue all items back to the Prefetch process, which may cause delays',
                 ' on new posted items. Running this operation is only recommended after',
                 ' upgrading D3 Digest to another upper version (when required!), or after',
                 ' a power failure, or catastrophic data loss on Memcached or the database.'].join('\n'));
    console.log('')
    rl.setPrompt('Do you really want to continue? (y/N) ');
    rl.prompt();
    rl.on('line', (answer) => {
        if(answer.toString().toLowerCase() === 'y') {
            logger.info('Flush', 'Performing...');
            perform();
        } else {
            logger.info('Flush', 'Aborted.');
            process.exit(0);
        }
    }).on('close', () => {
        logger.info('Flush', 'Aborted.');
        process.exit(0)
    });
};

var perform = function() {
    var redis, memcached, collection, items,
        memcachedUrl = `${settings.memcachedHost}:${settings.memcachedPort}`;

    logger.info('Flush', `Preparing: Connecting to Redis @ ${settings.redisUrl}`);
    redis = new Redis(settings.redisUrl);
    logger.info('Flush', `Preparing: Connecting to Memcached @ ${memcachedUrl}`);
    memcached = new Memcached(memcachedUrl);
    collection = Mongo.sharedInstance().collection('items');

    logger.info('Flush', 'Performing step 1/4: Obtaining database items...');
    collection
        .find({ ready: true })
        .project({ ts: 1 })
        .toArray()
        .then(arr => arr.map(i => i.ts))
        .then(result => {
            if(result.length < 1) {
                logger.info('Flush', 'No database items were aquired.');
                process.exit(0);
                return;
            }
            items = result;
            return result;
        })
        .then(result => {
            logger.info('Flush', 'Performing step 2/4: Resetting database state...');
            return collection.updateMany({ ts: { $in: result } }, { $set: { ready: false } });
        })
        .then(() => {
            logger.info('Flush', 'Performing step 3/4: Cleaning memcached state...');
            return new Promise((resolve, reject) => {
                memcached.flush((err) => {
                    if(err) {
                        reject(err);
                    } else {
                        resolve();
                    }
                });
            });
        })
        .then(() => {
            logger.info('Flush', 'Performing step 4/4: Enqueuing items to Prefetch process...');
            var pipe = redis.pipeline();
            items
                .map(i => ({ type: 'prefetch_item', ts: i }))
                .map(JSON.stringify)
                .forEach(i => pipe.rpush(settings.prefetchQueueName, i));
            return pipe.exec();
        })
        .then(() => {
            logger.info('Flush', 'Done.');
            process.exit(0);
        })
        .catch((ex) => {
            logger.error('Flush', 'Error: ');
            logger.error('Flush', ex);
            logger.warn('Flush', 'System may be in an inconsistent state.');
            process.exit(1);
        });
};

Mongo.prepare()
    .then(run)
    .catch((ex) => {
        logger.error('Flush', 'Error: ', ex)
        process.exit(1);
    });
<commit_msg>Update flush utility to use new Memcached helper<commit_after>require('./header')('Flush');

var settings = require('./settings').sharedInstance(),
    Mongo = require('./mongo'),
    memcached = require('./memcached').sharedInstance(),
    readline = require('readline'),
    Redis = require('ioredis'),
    logger = require('npmlog');

var run = function() {
    var rl = readline.createInterface(process.stdin, process.stdout);
    console.log(['',
                 '',
                 ' Heads up! You are about to destroy all precached data and reset all item\'s',
                 ' database state. After cleaning up memcached and resetting the database, this',
                 ' will enqueue all items back to the Prefetch process, which may cause delays',
                 ' on new posted items. Running this operation is only recommended after',
                 ' upgrading D3 Digest to another upper version (when required!), or after',
                 ' a power failure, or catastrophic data loss on Memcached or the database.'].join('\n'));
    console.log('')
    rl.setPrompt('Do you really want to continue? (y/N) ');
    rl.prompt();
    rl.on('line', (answer) => {
        if(answer.toString().toLowerCase() === 'y') {
            logger.info('Flush', 'Performing...');
            perform();
        } else {
            logger.info('Flush', 'Aborted.');
            process.exit(0);
        }
    }).on('close', () => {
        logger.info('Flush', 'Aborted.');
        process.exit(0)
    });
};

var perform = function() {
    var redis, collection, items,
        memcachedUrl = `${settings.memcachedHost}:${settings.memcachedPort}`;

    logger.info('Flush', `Preparing: Connecting to Redis @ ${settings.redisUrl}`);
    redis = new Redis(settings.redisUrl);
    collection = Mongo.sharedInstance().collection('items');

    logger.info('Flush', 'Performing step 1/4: Obtaining database items...');
    collection
        .find({ ready: true })
        .project({ ts: 1 })
        .toArray()
        .then(arr => arr.map(i => i.ts))
        .then(result => {
            if(result.length < 1) {
                logger.info('Flush', 'No database items were aquired.');
                process.exit(0);
                return;
            }
            items = result;
            return result;
        })
        .then(result => {
            logger.info('Flush', 'Performing step 2/4: Resetting database state...');
            return collection.updateMany({ ts: { $in: result } }, { $set: { ready: false } });
        })
        .then(() => {
            logger.info('Flush', 'Performing step 3/4: Cleaning memcached state...');
            return memcached.flush();
        })
        .then(() => {
            logger.info('Flush', 'Performing step 4/4: Enqueuing items to Prefetch process...');
            var pipe = redis.pipeline();
            items
                .map(i => ({ type: 'prefetch_item', ts: i }))
                .map(JSON.stringify)
                .forEach(i => pipe.rpush(settings.prefetchQueueName, i));
            return pipe.exec();
        })
        .then(() => {
            logger.info('Flush', 'Done.');
            process.exit(0);
        })
        .catch((ex) => {
            logger.error('Flush', 'Error: ');
            logger.error('Flush', ex);
            logger.warn('Flush', 'System may be in an inconsistent state.');
            process.exit(1);
        });
};

Mongo.prepare()
    .then(run)
    .catch((ex) => {
        logger.error('Flush', 'Error: ', ex)
        process.exit(1);
    });
<|endoftext|>


<commit_before>import fs from 'fs-extra'
import os from 'os'
import path from 'path'
import readdirp from 'readdirp'
import url from 'url'
import {
  filteredReplication as filterSDK,
  device
} from 'cozy-device-sdk'
import fetch from 'node-fetch'
import printit from 'printit'
let log = printit({
  prefix: 'Cozy Desktop  ',
  date: true
})

import { Console } from 'console'
import { EventEmitter } from 'events'

import Config from './config'
import Pouch from './pouch'
import Ignore from './ignore'
import Merge from './merge'
import Prep from './prep'
import Local from './local'
import Remote from './remote'
import Sync from './sync'

let Permissions = {
  'File': {
    'description': 'Useful to synchronize your files'
  },
  'Folder': {
    'description': 'Useful to synchronize your folders'
  },
  'Binary': {
    'description': 'Useful to synchronize the content of your files'
  },
  'send mail from user': {
    'description': 'Useful to send issues by mail to the cozy team'
  }
}

// App is the entry point for the CLI and GUI.
// They both can do actions and be notified by events via an App instance.
let MAX_LOG_SIZE
class App {
  static initClass () {
    // When a log file weights more than 0.5Mo, rotate it
    MAX_LOG_SIZE = 500000
  }

  // basePath is the directory where the config and pouch are saved
  constructor (basePath, fetch_ = fetch) {
    this.lang = 'fr'
    if (basePath == null) { basePath = os.homedir() }
    basePath = path.resolve(basePath)
    this.basePath = path.join(basePath, '.cozy-desktop')
    this.config = new Config(this.basePath)
    this.pouch = new Pouch(this.config)
    this.events = new EventEmitter()
    this.fetch = fetch_
  }

  // This method is here to be surcharged by the UI
  // to ask its password to the user
  //
  // callback is a function that takes two parameters: error and password
  askPassword (callback) {
    callback(new Error('Not implemented'), null)
  }

  // This method is here to be surcharged by the UI
  // to ask for a confirmation before doing something that can't be cancelled
  //
  // callback is a function that takes two parameters: error and a boolean
  askConfirmation (callback) {
    callback(new Error('Not implemented'), null)
  }

  // Configure a file to write logs to
  writeLogsTo (logfile) {
    this.logfile = logfile
    this.writeToLogfile()
    if (this.logsInterval) { clearInterval(this.logsInterval) }
    this.logsInterval = setInterval(this.rotateLogfile, 10000)
  }

  // Write logs in a file, by overriding the global console
  writeToLogfile () {
    let out = fs.createWriteStream(this.logfile, {flags: 'a+', mode: 0o0644})
    printit.console = new Console(out, out)
  }

  // Rotate the log file if it's too heavy
  rotateLogfile () {
    fs.stat(this.logfile, (err, stats) => {
      if (err || (stats.size < MAX_LOG_SIZE)) { return }
      fs.rename(this.logfile, `${this.logfile}.old`, this.writeToLogfile)
    })
  }

  // Parse the URL
  parseCozyUrl (cozyUrl) {
    if (cozyUrl.indexOf(':') === -1) {
      if (cozyUrl.indexOf('.') === -1) {
        cozyUrl += '.cozycloud.cc'
      }
      cozyUrl = `https://${cozyUrl}`
    }
    return url.parse(cozyUrl)
  }

  // Check that the URL belongs to a cozy
  async pingCozy (cozyUrl) {
    let parsed = this.parseCozyUrl(cozyUrl)
    if (!['http:', 'https:'].includes(parsed.protocol) || !parsed.hostname) {
      let err = new Error('Your URL looks invalid')
      log.warn(err)
      return Promise.reject(err)
    }
    cozyUrl = url.format(parsed)

    let resp, body
    resp = await this.fetch(cozyUrl + 'status')

    if (resp.status !== 200) {
      throw new Error(`Unexpected response status code: ${resp.status}`)
    }

    body = await resp.json()
    let dumpBody = () => JSON.stringify(body)

    switch (body.message) {
      case 'OK':
        return cozyUrl
      case 'KO':
        throw new Error(`Cozy is KO: ${dumpBody()}`)
      default:
        throw new Error(`Cannot extract message: ${dumpBody()}`)
    }
  }

  // Register a device on the remote cozy
  registerRemote (cozyUrl, deviceName, callback) {
    let parsed = this.parseCozyUrl(cozyUrl)
    cozyUrl = url.format(parsed)
    if (!['http:', 'https:'].includes(parsed.protocol) || !parsed.hostname) {
      let err = new Error(`Your URL looks invalid: ${cozyUrl}`)
      log.warn(err)
      callback(err)
      return
    }
    if (deviceName == null) { deviceName = os.hostname() || 'desktop' }
    this.askPassword(function (_, password) {
      let register = device.registerDeviceSafe
      register(cozyUrl, deviceName, password, Permissions, function (err, res) {
        if (err) { callback(err) }
        let config = {file: true};
        ({ deviceName } = res);
        ({ password } = res)
        let setDesignDoc = filterSDK.setDesignDoc.bind(filterSDK)
        setDesignDoc(cozyUrl, deviceName, password, config, err => callback(err, res))
      })
    })
  }

  // Save the config with all the informations for synchonization
  saveConfig (cozyUrl, syncPath, deviceName, password, callback) {
    fs.ensureDir(syncPath, err => {
      if (err) {
        callback(err)
      } else {
        let options = {
          path: path.resolve(syncPath),
          url: cozyUrl,
          deviceName,
          password
        }
        this.config.addRemoteCozy(options)
        log.info('The remote Cozy has properly been configured ' +
                 'to work with current device.')
        callback(null)
      }
    })
  }

  // Register current device to remote Cozy and then save related informations
  // to the config file (used by CLI, not GUI)
  addRemote (cozyUrl, syncPath, deviceName, callback) {
    this.registerRemote(cozyUrl, deviceName, (err, credentials) => {
      if (err) {
        log.error('An error occured while registering your device.')
        let parsed = this.parseCozyUrl(cozyUrl)
        if (err.code === 'ENOTFOUND') {
          log.warn(`The DNS resolution for ${parsed.hostname} failed.`)
          log.warn('Are you sure the domain is OK?')
        } else if (err === 'Bad credentials') {
          log.warn(err)
          log.warn('Are you sure there are no typo on the password?')
        } else {
          log.error(err)
          if (parsed.protocol === 'http:') {
            log.warn('Did you try with an httpS URL?')
          }
        }
        __guardFunc__(callback, f => f(err))
      } else {
        ({ deviceName } = credentials)
        let { password } = credentials
        log.info(`Device ${deviceName} has been added to ${cozyUrl}`)
        this.saveConfig(cozyUrl, syncPath, deviceName, password, err => __guardFunc__(callback, f1 => f1(err, credentials)))
      }
    })
  }

  // Unregister current device from remote Cozy and then remove remote from
  // the config file
  removeRemote (deviceName, callback) {
    if (callback == null) { callback = function () {} }
    let conf = this.config.getDevice()
    let cozyUrl = conf.url
    let { password } = conf
    device.unregisterDevice(cozyUrl, deviceName, password, err => {
      if (err && (err.message !== 'Request unauthorized')) {
        log.error('An error occured while unregistering your device.')
        log.error(err)
        callback(err)
      } else {
        log.info('Current device properly removed from remote cozy.')
        fs.remove(this.basePath, callback)
      }
    })
  }

  // Send an issue by mail to the support
  sendMailToSupport (content, callback) {
    let conf = this.config.getDevice()
    let cozyUrl = conf.url
    let { deviceName } = conf
    let { password } = conf
    let mail = {
      to: 'log-desktop@cozycloud.cc',
      subject: 'Ask support for cozy-desktop',
      content
    }
    if (this.logfile) {
      let attachment = {
        content: fs.readFileSync(this.logfile, 'utf-8'),
        filename: path.basename(this.logfile),
        contentType: 'application/text'
      }
      mail.attachments = [attachment]
    }
    device.sendMailFromUser(cozyUrl, deviceName, password, mail, callback)
  }

  // Load ignore rules
  loadIgnore () {
    let ignored
    try {
      let syncPath = this.config.getDevice().path
      ignored = fs.readFileSync(path.join(syncPath, '.cozyignore'))
      ignored = ignored.toString().split('\n')
    } catch (error) {
      ignored = []
    }
    this.ignore = new Ignore(ignored).addDefaultRules()
  }

  // Instanciate some objects before sync
  instanciate () {
    this.loadIgnore()
    this.merge = new Merge(this.pouch)
    this.prep = new Prep(this.merge, this.ignore)
    this.local = this.merge.local = new Local(this.config, this.prep, this.pouch, this.events)
    this.remote = this.merge.remote = new Remote(this.config, this.prep, this.pouch, this.events)
    this.sync = new Sync(this.pouch, this.local, this.remote, this.ignore, this.events)
    this.sync.getDiskSpace = this.getDiskSpace
  }

  // Start the synchronization
  startSync (mode, callback) {
    this.config.setMode(mode)
    log.info('Run first synchronisation...')
    this.sync.start(mode, err => {
      this.sync.stop(function () {})
      if (err) {
        log.error(err)
        if (err.stack) { log.error(err.stack) }
      }
      __guardFunc__(callback, f => f(err))
    })
  }

  // Stop the synchronisation
  stopSync (callback) {
    if (callback == null) { callback = function () {} }
    if (this.sync) {
      this.sync.stop(callback)
    } else {
      callback()
    }
  }

  // Start database sync process and setup file change watcher
  synchronize (mode, callback) {
    let conf = this.config.getDevice()
    if ((conf.deviceName != null) && (conf.url != null) && (conf.path != null)) {
      this.instanciate()
      this.startSync(mode, callback)
    } else {
      log.error('No configuration found, please run add-remote-cozy' +
                'command before running a synchronization.')
      __guardFunc__(callback, f => f(new Error('No config')))
    }
  }

  // Display a list of watchers for debugging purpose
  debugWatchers () {
    __guard__(this.local, x => x.watcher.debug())
  }

  // Call the callback for each file
  walkFiles (args, callback) {
    this.loadIgnore()
    let options = {
      root: this.config.getDevice().path,
      directoryFilter: '!.cozy-desktop',
      entryType: 'both'
    }
    readdirp(options)
      .on('warn', err => log.warn(err))
      .on('error', err => log.error(err))
      .on('data', data => {
        let doc = {
          _id: data.path,
          docType: data.stat.isFile() ? 'file' : 'folder'
        }
        if (this.ignore.isIgnored(doc) === (args.ignored != null)) {
          callback(data.path)
        }
      })
  }

  // Recreate the local pouch database
  resetDatabase (callback) {
    this.askConfirmation((err, ok) => {
      if (err) {
        log.error(err)
      } else if (ok) {
        log.info('Recreates the local database...')
        this.pouch.resetDatabase(function () {
          log.info('Database recreated')
          __guardFunc__(callback, f => f())
        })
      } else {
        log.info('Abort!')
      }
    })
  }

  // Return the whole content of the database
  allDocs (callback) {
    this.pouch.db.allDocs({include_docs: true}, callback)
  }

  // Return all docs for a given query
  query (query, callback) {
    this.pouch.db.query(query, {include_docs: true}, callback)
  }

  // Get disk space informations from the cozy
  getDiskSpace (callback) {
    let conf = this.config.getDevice()
    device.getDiskSpace(conf.url, conf.deviceName, conf.password, callback)
  }
}
App.initClass()

export default App

function __guardFunc__ (func, transform) {
  return typeof func === 'function' ? transform(func) : undefined
}
function __guard__ (value, transform) {
  return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined
}
<commit_msg>app: Disable functions that don't work with cozy-stack v3<commit_after>import fs from 'fs-extra'
import os from 'os'
import path from 'path'
import readdirp from 'readdirp'
import url from 'url'
// TODO: Remove cozy-device-sdk dependency
import { device } from 'cozy-device-sdk'
import fetch from 'node-fetch'
import printit from 'printit'
let log = printit({
  prefix: 'Cozy Desktop  ',
  date: true
})

import { Console } from 'console'
import { EventEmitter } from 'events'

import Config from './config'
import Pouch from './pouch'
import Ignore from './ignore'
import Merge from './merge'
import Prep from './prep'
import Local from './local'
import Remote from './remote'
import Sync from './sync'

// TODO: App.Permissions v3
// eslint-disable-next-line no-unused-vars
let Permissions = {
  'File': {
    'description': 'Useful to synchronize your files'
  },
  'Folder': {
    'description': 'Useful to synchronize your folders'
  },
  'Binary': {
    'description': 'Useful to synchronize the content of your files'
  },
  'send mail from user': {
    'description': 'Useful to send issues by mail to the cozy team'
  }
}

// App is the entry point for the CLI and GUI.
// They both can do actions and be notified by events via an App instance.
let MAX_LOG_SIZE
class App {
  static initClass () {
    // When a log file weights more than 0.5Mo, rotate it
    MAX_LOG_SIZE = 500000
  }

  // basePath is the directory where the config and pouch are saved
  constructor (basePath, fetch_ = fetch) {
    this.lang = 'fr'
    if (basePath == null) { basePath = os.homedir() }
    basePath = path.resolve(basePath)
    this.basePath = path.join(basePath, '.cozy-desktop')
    this.config = new Config(this.basePath)
    this.pouch = new Pouch(this.config)
    this.events = new EventEmitter()
    this.fetch = fetch_
  }

  // This method is here to be surcharged by the UI
  // to ask its password to the user
  //
  // callback is a function that takes two parameters: error and password
  askPassword (callback) {
    callback(new Error('Not implemented'), null)
  }

  // This method is here to be surcharged by the UI
  // to ask for a confirmation before doing something that can't be cancelled
  //
  // callback is a function that takes two parameters: error and a boolean
  askConfirmation (callback) {
    callback(new Error('Not implemented'), null)
  }

  // Configure a file to write logs to
  writeLogsTo (logfile) {
    this.logfile = logfile
    this.writeToLogfile()
    if (this.logsInterval) { clearInterval(this.logsInterval) }
    this.logsInterval = setInterval(this.rotateLogfile, 10000)
  }

  // Write logs in a file, by overriding the global console
  writeToLogfile () {
    let out = fs.createWriteStream(this.logfile, {flags: 'a+', mode: 0o0644})
    printit.console = new Console(out, out)
  }

  // Rotate the log file if it's too heavy
  rotateLogfile () {
    fs.stat(this.logfile, (err, stats) => {
      if (err || (stats.size < MAX_LOG_SIZE)) { return }
      fs.rename(this.logfile, `${this.logfile}.old`, this.writeToLogfile)
    })
  }

  // Parse the URL
  parseCozyUrl (cozyUrl) {
    if (cozyUrl.indexOf(':') === -1) {
      if (cozyUrl.indexOf('.') === -1) {
        cozyUrl += '.cozycloud.cc'
      }
      cozyUrl = `https://${cozyUrl}`
    }
    return url.parse(cozyUrl)
  }

  // Check that the URL belongs to a cozy
  async pingCozy (cozyUrl) {
    let parsed = this.parseCozyUrl(cozyUrl)
    if (!['http:', 'https:'].includes(parsed.protocol) || !parsed.hostname) {
      let err = new Error('Your URL looks invalid')
      log.warn(err)
      return Promise.reject(err)
    }
    cozyUrl = url.format(parsed)

    let resp, body
    resp = await this.fetch(cozyUrl + 'status')

    if (resp.status !== 200) {
      throw new Error(`Unexpected response status code: ${resp.status}`)
    }

    body = await resp.json()
    let dumpBody = () => JSON.stringify(body)

    switch (body.message) {
      case 'OK':
        return cozyUrl
      case 'KO':
        throw new Error(`Cozy is KO: ${dumpBody()}`)
      default:
        throw new Error(`Cannot extract message: ${dumpBody()}`)
    }
  }

  // Register a device on the remote cozy
  registerRemote (cozyUrl, deviceName, callback) {
    let parsed = this.parseCozyUrl(cozyUrl)
    cozyUrl = url.format(parsed)
    if (!['http:', 'https:'].includes(parsed.protocol) || !parsed.hostname) {
      let err = new Error(`Your URL looks invalid: ${cozyUrl}`)
      log.warn(err)
      callback(err)
      return
    }
    if (deviceName == null) { deviceName = os.hostname() || 'desktop' }
    this.askPassword(function (_, password) {
      // TODO: App.registerRemote() v3
      callback(null, {deviceName, password})
    })
  }

  // Save the config with all the informations for synchonization
  saveConfig (cozyUrl, syncPath, deviceName, password, callback) {
    fs.ensureDir(syncPath, err => {
      if (err) {
        callback(err)
      } else {
        let options = {
          path: path.resolve(syncPath),
          url: cozyUrl,
          deviceName,
          password
        }
        this.config.addRemoteCozy(options)
        log.info('The remote Cozy has properly been configured ' +
                 'to work with current device.')
        callback(null)
      }
    })
  }

  // Register current device to remote Cozy and then save related informations
  // to the config file (used by CLI, not GUI)
  addRemote (cozyUrl, syncPath, deviceName, callback) {
    this.registerRemote(cozyUrl, deviceName, (err, credentials) => {
      if (err) {
        log.error('An error occured while registering your device.')
        let parsed = this.parseCozyUrl(cozyUrl)
        if (err.code === 'ENOTFOUND') {
          log.warn(`The DNS resolution for ${parsed.hostname} failed.`)
          log.warn('Are you sure the domain is OK?')
        } else if (err === 'Bad credentials') {
          log.warn(err)
          log.warn('Are you sure there are no typo on the password?')
        } else {
          log.error(err)
          if (parsed.protocol === 'http:') {
            log.warn('Did you try with an httpS URL?')
          }
        }
        __guardFunc__(callback, f => f(err))
      } else {
        ({ deviceName } = credentials)
        let { password } = credentials
        log.info(`Device ${deviceName} has been added to ${cozyUrl}`)
        this.saveConfig(cozyUrl, syncPath, deviceName, password, err => __guardFunc__(callback, f1 => f1(err, credentials)))
      }
    })
  }

  // Unregister current device from remote Cozy and then remove remote from
  // the config file
  removeRemote (deviceName, callback) {
    if (callback == null) { callback = function () {} }
    let conf = this.config.getDevice()
    let cozyUrl = conf.url
    let { password } = conf
    device.unregisterDevice(cozyUrl, deviceName, password, err => {
      if (err && (err.message !== 'Request unauthorized')) {
        log.error('An error occured while unregistering your device.')
        log.error(err)
        callback(err)
      } else {
        log.info('Current device properly removed from remote cozy.')
        fs.remove(this.basePath, callback)
      }
    })
  }

  // Send an issue by mail to the support
  sendMailToSupport (content, callback) {
    let conf = this.config.getDevice()
    let cozyUrl = conf.url
    let { deviceName } = conf
    let { password } = conf
    let mail = {
      to: 'log-desktop@cozycloud.cc',
      subject: 'Ask support for cozy-desktop',
      content
    }
    if (this.logfile) {
      let attachment = {
        content: fs.readFileSync(this.logfile, 'utf-8'),
        filename: path.basename(this.logfile),
        contentType: 'application/text'
      }
      mail.attachments = [attachment]
    }
    device.sendMailFromUser(cozyUrl, deviceName, password, mail, callback)
  }

  // Load ignore rules
  loadIgnore () {
    let ignored
    try {
      let syncPath = this.config.getDevice().path
      ignored = fs.readFileSync(path.join(syncPath, '.cozyignore'))
      ignored = ignored.toString().split('\n')
    } catch (error) {
      ignored = []
    }
    this.ignore = new Ignore(ignored).addDefaultRules()
  }

  // Instanciate some objects before sync
  instanciate () {
    this.loadIgnore()
    this.merge = new Merge(this.pouch)
    this.prep = new Prep(this.merge, this.ignore)
    this.local = this.merge.local = new Local(this.config, this.prep, this.pouch, this.events)
    this.remote = this.merge.remote = new Remote(this.config, this.prep, this.pouch, this.events)
    this.sync = new Sync(this.pouch, this.local, this.remote, this.ignore, this.events)
    this.sync.getDiskSpace = this.getDiskSpace
  }

  // Start the synchronization
  startSync (mode, callback) {
    this.config.setMode(mode)
    log.info('Run first synchronisation...')
    this.sync.start(mode, err => {
      this.sync.stop(function () {})
      if (err) {
        log.error(err)
        if (err.stack) { log.error(err.stack) }
      }
      __guardFunc__(callback, f => f(err))
    })
  }

  // Stop the synchronisation
  stopSync (callback) {
    if (callback == null) { callback = function () {} }
    if (this.sync) {
      this.sync.stop(callback)
    } else {
      callback()
    }
  }

  // Start database sync process and setup file change watcher
  synchronize (mode, callback) {
    let conf = this.config.getDevice()
    if ((conf.deviceName != null) && (conf.url != null) && (conf.path != null)) {
      this.instanciate()
      this.startSync(mode, callback)
    } else {
      log.error('No configuration found, please run add-remote-cozy' +
                'command before running a synchronization.')
      __guardFunc__(callback, f => f(new Error('No config')))
    }
  }

  // Display a list of watchers for debugging purpose
  debugWatchers () {
    __guard__(this.local, x => x.watcher.debug())
  }

  // Call the callback for each file
  walkFiles (args, callback) {
    this.loadIgnore()
    let options = {
      root: this.config.getDevice().path,
      directoryFilter: '!.cozy-desktop',
      entryType: 'both'
    }
    readdirp(options)
      .on('warn', err => log.warn(err))
      .on('error', err => log.error(err))
      .on('data', data => {
        let doc = {
          _id: data.path,
          docType: data.stat.isFile() ? 'file' : 'folder'
        }
        if (this.ignore.isIgnored(doc) === (args.ignored != null)) {
          callback(data.path)
        }
      })
  }

  // Recreate the local pouch database
  resetDatabase (callback) {
    this.askConfirmation((err, ok) => {
      if (err) {
        log.error(err)
      } else if (ok) {
        log.info('Recreates the local database...')
        this.pouch.resetDatabase(function () {
          log.info('Database recreated')
          __guardFunc__(callback, f => f())
        })
      } else {
        log.info('Abort!')
      }
    })
  }

  // Return the whole content of the database
  allDocs (callback) {
    this.pouch.db.allDocs({include_docs: true}, callback)
  }

  // Return all docs for a given query
  query (query, callback) {
    this.pouch.db.query(query, {include_docs: true}, callback)
  }

  // Get disk space informations from the cozy
  getDiskSpace (callback) {
    // TODO: App.getDiskSpace() v3
    callback(null, {})
  }
}
App.initClass()

export default App

function __guardFunc__ (func, transform) {
  return typeof func === 'function' ? transform(func) : undefined
}
function __guard__ (value, transform) {
  return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined
}
<|endoftext|>


<commit_before>'use strict'

/**
 * Dependencies
 * @ignore
 */
const crypto = require('@trust/webcrypto')
const base64url = require('base64url')
const { JWA } = require('@trust/jwa')

/**
 * Module Dependencies
 * @ignore
 */
const { DataError } = require('./errors')

/**
 * JWK
 * @ignore
 */
class JWK {

  /**
   * constructor
   *
   * @class JWK
   *
   * @description
   * JSON Web Key ([IETF RFC7517](https://tools.ietf.org/html/rfc7517))
   *
   * @param  {Object} data
   * @param  {Object} [options={}] - Additional JWK metadata.
   */
  constructor (data, options = {}) {
    if (!data) {
      throw new DataError('Invalid JWK')
    }

    // Handle string input
    if (typeof data === 'string') {
      try {
        data = JSON.parse(data)
      } catch (error) {
        throw new DataError('Invalid JWK JSON String')
      }
    }

    // Handle options input
    let metadata = Object.keys(options)
      .filter(key => {
        let dataValue = data[key]
        let optionsValue = options[key]

        if (dataValue && optionsValue && dataValue !== optionsValue) {
          throw new DataError(`Conflicting '${key}' option`)
        }

        return !dataValue && optionsValue
      })
      .reduce((state, key) => {
        state[key] = options[key]
        return state
      }, {})

    // Assign input
    Object.assign(this, data, metadata)

    // Enforce required properties
    if (!this.alg) {
      throw new DataError('Valid \'alg\' required for JWK')
    }

    if (!this.kid) {
      throw new DataError('Valid \'kid\' required for JWK')
    }
  }

  /**
   * importKey
   *
   * @description
   * Import a JWK from JSON String or a JS Object.
   *
   * @param  {(String|Object)} data
   * @param  {Object} [options] - Additional JWK metadata.
   * @return {Promise.<JWK>} A promise that resolves the JWK instance.
   */
  static importKey (data, options) {
    return Promise.resolve()

      // Calculate Thumbprint
      .then(() => this.thumbprint(data))

      // Create instance
      .then(hash => {
        if (hash && !data.kid && !(options && options.kid)) {
          data.kid = hash
        }

        return new JWK(data, options)
      })

      // Import CryptoKey and assign to JWK
      .then(jwk => JWA.importKey(jwk).then(({ cryptoKey }) => {
        Object.defineProperty(jwk, 'cryptoKey', {
          value: cryptoKey,
          enumerable: false,
          configurable: false
        })

        return jwk
      }))
  }

  /**
   * fromCryptoKey
   *
   * @description
   * Import a JWK from a [WebCrypto CryptoKey](https://github.com/anvilresearch/webcrypto).
   *
   * @param  {CryptoKey} key - [WebCrypto CryptoKey]{@link https://github.com/anvilresearch/webcrypto}.
   * @param  {Object} [options] - Additional JWK metadata.
   * @return {Promise.<JWK>} A promise that resolves the JWK instance.
   */
  static fromCryptoKey (key, options) {
    let rawJwk

    // Export JWK
    return JWA.exportKey('jwk', key)
      .then(data => {
        rawJwk = data
        return this.thumbprint(rawJwk)
      })

      // Create JWK instance and assign CryptoKey
      .then(hash => {
        if (!rawJwk.kid && !(options && options.kid)) {
          rawJwk.kid = hash
        }

        let jwk = new JWK(rawJwk, options)

        Object.defineProperty(jwk, 'cryptoKey', {
          value: key,
          enumerable: false,
          configurable: false
        })

        return jwk
      })
  }

  /**
   * thumbprint
   *
   * @description
   * Calculate the SHA-256 JWK Thumbprint according to [RFC7638]{@link https://tools.ietf.org/html/rfc7638}.
   * This method is used to create a unique `kid` if none is specified.
   *
   * @example
SHA-256 Thumbprint

   * JWK.thumbprint(jwk)
   *   .then(console.log)
   * //
   * // (line breaks for display only)
   * //
   * // => "45BLsBiWcghaEf_NF70Gf5oQcYLHaA
   * //     tks0C48tT5SJ4"
   *
   * @param  {Object} jwk
   * @return {Promise.<String>} A promise that resolves the JWK Thumbprint String.
   */
  static thumbprint (jwk) {
    let { kty } = jwk
    let data

    // RSA JWK Thumbprint Fields
    if (kty === 'RSA') {
      let { e, n } = jwk
      data = { e, kty, n }

    // ECDSA JWK Thumbprint Fields
    } else if (kty === 'EC') {
      let { crv, x, y } = jwk
      data = { crv, kty, x, y }

    // Symmetric JWK Thumbprint Fields
    } else if (kty === 'oct') {
      let { k } = jwk
      data = { k, kty }

    // Invalid kty
    } else {
      return Promise.reject(new DataError('Invalid \'kty\''))
    }

    let json = JSON.stringify(data)
    return crypto.subtle.digest({ name: 'SHA-256' }, json)
      .then(hash => base64url(hash))
  }

  /**
   * sign
   *
   * @description
   * Sign arbitrary data using the JWK.
   *
   * @example
Signing the string "test"

   * privateJwk.sign('test')
   *   .then(console.log)
   * //
   * // (line breaks for display only)
   * //
   * // => "MEUCIQCHwnGM8IsOJgfQsoPgs3hMd8
   * //     ahfWHM9ZNvj1K6i2yhKQIgWGOuXX43
   * //     lSTo-U8Pa8sURR53lv6Osjw-dtoLse
   * //     lftqQ"
   *
   * @param  {(String|Buffer)} data - The data to sign.
   * @return {Promise.<String>} A promise that resolves the base64url encoded signature string.
   */
  sign (data) {
    let { alg, cryptoKey } = this
    return JWA.sign(alg, cryptoKey, data)
  }

  /**
   * verify
   *
   * @description
   * Verify a signature using the JWK.
   *
   * @example
Verify a signature of the string "test"

   * // base64url encoded signature string
   * let signature = `MEUCIQCHwnGM8IsOJgfQsoPgs3hMd8ahfWHM9ZN
   * vj1K6i2yhKQIgWGOuXX43lSTo-U8Pa8sURR53lv6Osjw-dtoLselftqQ`
   *
   * publicJwk.verify('test', signature)
   *   .then(console.log)
   * // => true
   *
   * @param  {(String|Buffer)} data - The data to verify.
   * @param  {String} signature - A base64url signature string.
   * @return {Promise.<Boolean>} A promise that resolves the boolean result of the signature verification.
   */
  verify (data, signature) {
    let { alg, cryptoKey } = this
    return JWA.verify(alg, cryptoKey, signature, data)
  }

  /**
   * encrypt
   *
   * @description
   * Encrypt arbitrary data using the JWK.
   *
   * @example
Encrypt the string "data"

   * secretJwk.encrypt('data')
   *   .then(console.log)
   * // => { iv: 'u0l3ttqUFDQ8mcRboHv5Vw',
   * //      ciphertext: 'yq3K4w',
   * //      tag: 'fHlZ__uuUnHn0ac-Lnrr-A' }
   *
   * @param  {(String|Object)} data - The data to encrypt.
   * @param  {(String|Buffer)} [aad] - Additional non-encrypted integrity protected data (AES-GCM).
   * @return {Promise.<Object>} A promise that resolves an object containing the base64url encoded `iv`, `ciphertext` and `tag` (AES-GCM).
   */
  encrypt (data, aad) {
    let { alg, cryptoKey } = this
    return JWA.encrypt(alg, cryptoKey, data, aad)
  }

  /**
   * decrypt
   *
   * @description
   * Decrypt data using the JWK.
   *
   * @example
Decrypt encrypted string "test"

   * // base64url encoded data
   * let ciphertext = 'yq3K4w'
   * let iv = 'u0l3ttqUFDQ8mcRboHv5Vw'
   * let tag = 'fHlZ__uuUnHn0ac-Lnrr-A'
   *
   * secretJwk.decrypt(ciphertext, iv, tag)
   *   .then(console.log)
   * // => "data"
   *
   * @param  {(String|Buffer)} ciphertext - The encrypted data to decrypt.
   * @param  {(String|Buffer)} iv - The initialization vector.
   * @param  {(String|Buffer)} [tag] - The authorization tag (AES-GCM).
   * @param  {(String|Buffer)} [aad] - Additional non-encrypted integrity protected data (AES-GCM).
   * @return {Promise.<String>} A promise that resolves the plaintext data.
   */
  decrypt (ciphertext, iv, tag, aad) {
    let { alg, cryptoKey } = this
    return JWA.decrypt(alg, cryptoKey, ciphertext, iv, tag, aad)
  }

  /**
   * thumbprint
   *
   * @description
   * Calculate the SHA-256 JWK Thumbprint according to [RFC7638]{@link https://tools.ietf.org/html/rfc7638}.
   * This method is used to create a unique `kid` if none is specified.
   *
   * @example
SHA-256 Thumbprint

   * jwk.thumbprint()
   *   .then(console.log)
   * //
   * // (line breaks for display only)
   * //
   * // => "45BLsBiWcghaEf_NF70Gf5oQcYLHaA
   * //     tks0C48tT5SJ4"
   *
   * @return {Promise.<String>} A promise that resolves the JWK Thumbprint String.
   */
  thumbprint () {
    return JWK.thumbprint(this)
  }

  /**
   * getProtectedHeader
   *
   * @description
   * Use key metadata to generate a JWS protected header object.
   *
   * @example
Basic JWS Header with JWC

   * jwk.getProtectedHeader({ jwc: 'base64url encoded compact jwc' })
   * // => { alg: 'RS256',
   * //      kid: 'abcd123$',
   * //      jwc: 'base64url encoded compact jwc' }
   *
   * @example
Basic JWS Header with JKU

   * jwk.getProtectedHeader({ jku: 'https://example.com/jwks' })
   * // => { alg: 'RS256',
   * //      kid: 'abcd123$',
   * //      jku: 'https://example.com/jwks' }
   *
   * @param  {Object} params - Additional properties to include in header.
   * @return {Object} JWS Header
   */
  getProtectedHeader (params) {
    let { alg, kid, key_ops, use } = this
    let header = Object.assign({}, { alg, kid }, params)

    // Check key_ops or use
    if (!(Array.isArray(key_ops) && key_ops.includes('sign')) && !(use && use === 'sig')) {
      throw new DataError('Invalid key usage option')
    }

    // Check for mandatory properties
    if (!header.alg) {
      throw new DataError('\'alg\' is required')
    }

    if (!header.kid) {
      throw new DataError('\'kid\' is required')
    }

    if (!header.jku && !header.jwc) {
      throw new DataError('Either \'jku\' or \'jwc\' is required')
    }

    return header
  }
}

/**
 * Exports
 * @ignore
 */
module.exports = JWK
<commit_msg>fix(JWK): `kid` should not be included with `jwc`<commit_after>'use strict'

/**
 * Dependencies
 * @ignore
 */
const crypto = require('@trust/webcrypto')
const base64url = require('base64url')
const { JWA } = require('@trust/jwa')

/**
 * Module Dependencies
 * @ignore
 */
const { DataError } = require('./errors')

/**
 * JWK
 * @ignore
 */
class JWK {

  /**
   * constructor
   *
   * @class JWK
   *
   * @description
   * JSON Web Key ([IETF RFC7517](https://tools.ietf.org/html/rfc7517))
   *
   * @param  {Object} data
   * @param  {Object} [options={}] - Additional JWK metadata.
   */
  constructor (data, options = {}) {
    if (!data) {
      throw new DataError('Invalid JWK')
    }

    // Handle string input
    if (typeof data === 'string') {
      try {
        data = JSON.parse(data)
      } catch (error) {
        throw new DataError('Invalid JWK JSON String')
      }
    }

    // Handle options input
    let metadata = Object.keys(options)
      .filter(key => {
        let dataValue = data[key]
        let optionsValue = options[key]

        if (dataValue && optionsValue && dataValue !== optionsValue) {
          throw new DataError(`Conflicting '${key}' option`)
        }

        return !dataValue && optionsValue
      })
      .reduce((state, key) => {
        state[key] = options[key]
        return state
      }, {})

    // Assign input
    Object.assign(this, data, metadata)

    // Enforce required properties
    if (!this.alg) {
      throw new DataError('Valid \'alg\' required for JWK')
    }

    if (!this.kid) {
      throw new DataError('Valid \'kid\' required for JWK')
    }
  }

  /**
   * importKey
   *
   * @description
   * Import a JWK from JSON String or a JS Object.
   *
   * @param  {(String|Object)} data
   * @param  {Object} [options] - Additional JWK metadata.
   * @return {Promise.<JWK>} A promise that resolves the JWK instance.
   */
  static importKey (data, options) {
    return Promise.resolve()

      // Calculate Thumbprint
      .then(() => this.thumbprint(data))

      // Create instance
      .then(hash => {
        if (hash && !data.kid && !(options && options.kid)) {
          data.kid = hash
        }

        return new JWK(data, options)
      })

      // Import CryptoKey and assign to JWK
      .then(jwk => JWA.importKey(jwk).then(({ cryptoKey }) => {
        Object.defineProperty(jwk, 'cryptoKey', {
          value: cryptoKey,
          enumerable: false,
          configurable: false
        })

        return jwk
      }))
  }

  /**
   * fromCryptoKey
   *
   * @description
   * Import a JWK from a [WebCrypto CryptoKey](https://github.com/anvilresearch/webcrypto).
   *
   * @param  {CryptoKey} key - [WebCrypto CryptoKey]{@link https://github.com/anvilresearch/webcrypto}.
   * @param  {Object} [options] - Additional JWK metadata.
   * @return {Promise.<JWK>} A promise that resolves the JWK instance.
   */
  static fromCryptoKey (key, options) {
    let rawJwk

    // Export JWK
    return JWA.exportKey('jwk', key)
      .then(data => {
        rawJwk = data
        return this.thumbprint(rawJwk)
      })

      // Create JWK instance and assign CryptoKey
      .then(hash => {
        if (!rawJwk.kid && !(options && options.kid)) {
          rawJwk.kid = hash
        }

        let jwk = new JWK(rawJwk, options)

        Object.defineProperty(jwk, 'cryptoKey', {
          value: key,
          enumerable: false,
          configurable: false
        })

        return jwk
      })
  }

  /**
   * thumbprint
   *
   * @description
   * Calculate the SHA-256 JWK Thumbprint according to [RFC7638]{@link https://tools.ietf.org/html/rfc7638}.
   * This method is used to create a unique `kid` if none is specified.
   *
   * @example
SHA-256 Thumbprint

   * JWK.thumbprint(jwk)
   *   .then(console.log)
   * //
   * // (line breaks for display only)
   * //
   * // => "45BLsBiWcghaEf_NF70Gf5oQcYLHaA
   * //     tks0C48tT5SJ4"
   *
   * @param  {Object} jwk
   * @return {Promise.<String>} A promise that resolves the JWK Thumbprint String.
   */
  static thumbprint (jwk) {
    let { kty } = jwk
    let data

    // RSA JWK Thumbprint Fields
    if (kty === 'RSA') {
      let { e, n } = jwk
      data = { e, kty, n }

    // ECDSA JWK Thumbprint Fields
    } else if (kty === 'EC') {
      let { crv, x, y } = jwk
      data = { crv, kty, x, y }

    // Symmetric JWK Thumbprint Fields
    } else if (kty === 'oct') {
      let { k } = jwk
      data = { k, kty }

    // Invalid kty
    } else {
      return Promise.reject(new DataError('Invalid \'kty\''))
    }

    let json = JSON.stringify(data)
    return crypto.subtle.digest({ name: 'SHA-256' }, json)
      .then(hash => base64url(hash))
  }

  /**
   * sign
   *
   * @description
   * Sign arbitrary data using the JWK.
   *
   * @example
Signing the string "test"

   * privateJwk.sign('test')
   *   .then(console.log)
   * //
   * // (line breaks for display only)
   * //
   * // => "MEUCIQCHwnGM8IsOJgfQsoPgs3hMd8
   * //     ahfWHM9ZNvj1K6i2yhKQIgWGOuXX43
   * //     lSTo-U8Pa8sURR53lv6Osjw-dtoLse
   * //     lftqQ"
   *
   * @param  {(String|Buffer)} data - The data to sign.
   * @return {Promise.<String>} A promise that resolves the base64url encoded signature string.
   */
  sign (data) {
    let { alg, cryptoKey } = this
    return JWA.sign(alg, cryptoKey, data)
  }

  /**
   * verify
   *
   * @description
   * Verify a signature using the JWK.
   *
   * @example
Verify a signature of the string "test"

   * // base64url encoded signature string
   * let signature = `MEUCIQCHwnGM8IsOJgfQsoPgs3hMd8ahfWHM9ZN
   * vj1K6i2yhKQIgWGOuXX43lSTo-U8Pa8sURR53lv6Osjw-dtoLselftqQ`
   *
   * publicJwk.verify('test', signature)
   *   .then(console.log)
   * // => true
   *
   * @param  {(String|Buffer)} data - The data to verify.
   * @param  {String} signature - A base64url signature string.
   * @return {Promise.<Boolean>} A promise that resolves the boolean result of the signature verification.
   */
  verify (data, signature) {
    let { alg, cryptoKey } = this
    return JWA.verify(alg, cryptoKey, signature, data)
  }

  /**
   * encrypt
   *
   * @description
   * Encrypt arbitrary data using the JWK.
   *
   * @example
Encrypt the string "data"

   * secretJwk.encrypt('data')
   *   .then(console.log)
   * // => { iv: 'u0l3ttqUFDQ8mcRboHv5Vw',
   * //      ciphertext: 'yq3K4w',
   * //      tag: 'fHlZ__uuUnHn0ac-Lnrr-A' }
   *
   * @param  {(String|Object)} data - The data to encrypt.
   * @param  {(String|Buffer)} [aad] - Additional non-encrypted integrity protected data (AES-GCM).
   * @return {Promise.<Object>} A promise that resolves an object containing the base64url encoded `iv`, `ciphertext` and `tag` (AES-GCM).
   */
  encrypt (data, aad) {
    let { alg, cryptoKey } = this
    return JWA.encrypt(alg, cryptoKey, data, aad)
  }

  /**
   * decrypt
   *
   * @description
   * Decrypt data using the JWK.
   *
   * @example
Decrypt encrypted string "test"

   * // base64url encoded data
   * let ciphertext = 'yq3K4w'
   * let iv = 'u0l3ttqUFDQ8mcRboHv5Vw'
   * let tag = 'fHlZ__uuUnHn0ac-Lnrr-A'
   *
   * secretJwk.decrypt(ciphertext, iv, tag)
   *   .then(console.log)
   * // => "data"
   *
   * @param  {(String|Buffer)} ciphertext - The encrypted data to decrypt.
   * @param  {(String|Buffer)} iv - The initialization vector.
   * @param  {(String|Buffer)} [tag] - The authorization tag (AES-GCM).
   * @param  {(String|Buffer)} [aad] - Additional non-encrypted integrity protected data (AES-GCM).
   * @return {Promise.<String>} A promise that resolves the plaintext data.
   */
  decrypt (ciphertext, iv, tag, aad) {
    let { alg, cryptoKey } = this
    return JWA.decrypt(alg, cryptoKey, ciphertext, iv, tag, aad)
  }

  /**
   * thumbprint
   *
   * @description
   * Calculate the SHA-256 JWK Thumbprint according to [RFC7638]{@link https://tools.ietf.org/html/rfc7638}.
   * This method is used to create a unique `kid` if none is specified.
   *
   * @example
SHA-256 Thumbprint

   * jwk.thumbprint()
   *   .then(console.log)
   * //
   * // (line breaks for display only)
   * //
   * // => "45BLsBiWcghaEf_NF70Gf5oQcYLHaA
   * //     tks0C48tT5SJ4"
   *
   * @return {Promise.<String>} A promise that resolves the JWK Thumbprint String.
   */
  thumbprint () {
    return JWK.thumbprint(this)
  }

  /**
   * getProtectedHeader
   *
   * @description
   * Use key metadata to generate a JWS protected header object.
   *
   * @example
Basic JWS Header with JWC

   * jwk.getProtectedHeader({ jwc: 'base64url encoded compact jwc' })
   * // => { alg: 'RS256',
   * //      kid: 'abcd123$',
   * //      jwc: 'base64url encoded compact jwc' }
   *
   * @example
Basic JWS Header with JKU

   * jwk.getProtectedHeader({ jku: 'https://example.com/jwks' })
   * // => { alg: 'RS256',
   * //      kid: 'abcd123$',
   * //      jku: 'https://example.com/jwks' }
   *
   * @param  {Object} params - Additional properties to include in header.
   * @return {Object} JWS Header
   */
  getProtectedHeader (params) {
    let { alg, kid, key_ops, use } = this
    let header = Object.assign({ alg }, params)

    // Check key_ops or use
    if (!(Array.isArray(key_ops) && key_ops.includes('sign')) && !(use && use === 'sig')) {
      throw new DataError('Invalid key usage option')
    }

    // Include `kid` in combination with `jku`
    if (header.jku) {
      header.kid = kid
    }

    // Check for mandatory properties
    if (!header.alg) {
      throw new DataError('\'alg\' is required')
    }

    if (!header.kid) {
      throw new DataError('\'kid\' is required')
    }

    return header
  }
}

/**
 * Exports
 * @ignore
 */
module.exports = JWK
<|endoftext|>


<commit_before>var checkoutjs_module = 'paystand';
var api_domain = 'api.paystand.com';
var checkout_domain = 'checkout.paystand.com';
var env = 'live';
var use_sandbox = window.checkoutConfig.payment.paystandmagento.use_sandbox;
if (use_sandbox == '1') {
  checkoutjs_module = 'paystand-sandbox';
  // api_domain = 'api.paystand.co';
  // checkout_domain = 'checkout.paystand.co';
  // env = 'sandbox';
  api_domain = 'localhost:3001/api';
  checkout_domain = 'localhost:3002';
  env = 'local';
}

/*jshint browser:true jquery:true*/
define([
  'require',
  'jquery',
  'Magento_Payment/js/view/payment/iframe',
  'Magento_Checkout/js/model/quote',
  'Magento_Checkout/js/model/error-processor',
  checkoutjs_module
], function (require, $, iframe, quote, errorProcessor, paystand) {
  'use strict';

  /**
   * Load the Paystand checkout
   */
  var loadPaystandCheckout = function () {

    var publishable_key = window.checkoutConfig.payment.paystandmagento.publishable_key;
    console.log("publishable key = " + publishable_key);

    var price = quote.totals().grand_total.toString();
    var quoteId = quote.getQuoteId();
    var billing = quote.billingAddress();

    psCheckout.onComplete(function(data){
      console.log("custom checkout complete:", data);
      $(".submit-trigger").click();
    });
    psCheckout.onError(function(data){
      console.log("custom checkout error:", data);
    });

    function initCheckout(countryISO3)
    {
      psCheckout.onceLoaded(function (data) {
        psCheckout.showCheckout();
      });

      var config = {
        "publishableKey": publishable_key,
        "checkout_domain": "https://" + checkout_domain + "/v4/",
        "env": env,
        "domain": "https://" + api_domain,
        "payment": {
          "amount": price
        },
        "currency": "USD",
        "paymentMethods": [
          'echeck',
          'card'
        ],
        "payer": {
          "name": billing.firstname + ' ' + billing.lastname,
          "email": quote.guestEmail
        },
        "billing": {
          "street": billing.street[0],
          "city": billing.city,
          "postalCode": billing.postcode,
          "subdivisionCode": billing.regionCode,
          "countryCode": countryISO3
        },
        "meta": {
          "source": "magento 2",
          "quote": quoteId,
          "quoteDetails" : quote.totals()
        }
      };

      console.log(config);

      psCheckout.reboot(config);
      // stop observing for mutation events
      window.observer.disconnect();
    }

    if (billing.countryId) {
      $.ajax({
        beforeSend: function (request) {
          request.setRequestHeader("x-publishable-key", publishable_key);
        },
        dataType: "text",
        contentType: "application/json; charset=utf-8",
        url: "https://" + api_domain + "/v3/addresses/countries/iso?code=" + billing.countryId,
        success: function (data) {
          initCheckout(JSON.parse(data).iso3);
        },
        error: function (error) {
          console.log('Unable to get ISO3 code from PayStand!');
        },
      });
    } else {
      initCheckout();
    }
  };

// create an observer instance
  window.observer = new MutationObserver(function (mutations) {
    mutations.forEach(function (mutation) {
      console.log(mutation.type);
      for (var i = 0; i < mutation.addedNodes.length; ++i) {
        var item = mutation.addedNodes[i];
        if (typeof item.getElementsByClassName === "function") {
          if (item.getElementsByClassName('paystand-checkout-form').length > 0) {
            loadPaystandCheckout();
          }
        }
      }
    });
  });

  // configuration of the observer:
  var config = {attributes: true, childList: true, characterData: true};

  // pass in the target node, as well as the observer options
  var total_timeout = 0;
  var recursivelyObserve = function () {
    window.setTimeout(function () {
      total_timeout += 10;

      if (total_timeout < 1000) {
        // select the target node
        var target = $('.payment-method').parent().get(0);

        if (typeof target == "Node") {
          observer.observe(target, config);
        } else {
          recursivelyObserve();
        }
      } else {
        loadPaystandCheckout();
      }
    }, 10);
  };

  recursivelyObserve();

  return this;
});
<commit_msg>tweaking local config<commit_after>var checkoutjs_module = 'paystand';
var core_domain = 'paystand.com';
var api_domain = 'api.paystand.com';
var checkout_domain = 'checkout.paystand.com';
var env = 'live';
var use_sandbox = window.checkoutConfig.payment.paystandmagento.use_sandbox;
if (use_sandbox == '1') {
  checkoutjs_module = 'paystand-sandbox';
  // core_domain = 'paystand.co';
  // api_domain = 'api.paystand.co';
  // checkout_domain = 'checkout.paystand.co';
  // env = 'sandbox';
  core_domain = 'localhost:3001';
  api_domain = 'localhost:3001/api';
  checkout_domain = 'localhost:3002';
  env = 'local';
}

/*jshint browser:true jquery:true*/
define([
  'require',
  'jquery',
  'Magento_Payment/js/view/payment/iframe',
  'Magento_Checkout/js/model/quote',
  'Magento_Checkout/js/model/error-processor',
  checkoutjs_module
], function (require, $, iframe, quote, errorProcessor, paystand) {
  'use strict';

  /**
   * Load the Paystand checkout
   */
  var loadPaystandCheckout = function () {

    var publishable_key = window.checkoutConfig.payment.paystandmagento.publishable_key;
    console.log("publishable key = " + publishable_key);

    var price = quote.totals().grand_total.toString();
    var quoteId = quote.getQuoteId();
    var billing = quote.billingAddress();

    psCheckout.onComplete(function(data){
      console.log("custom checkout complete:", data);
      $(".submit-trigger").click();
    });
    psCheckout.onError(function(data){
      console.log("custom checkout error:", data);
    });

    function initCheckout(countryISO3)
    {
      psCheckout.onceLoaded(function (data) {
        psCheckout.showCheckout();
      });

      var config = {
        "publishableKey": publishable_key,
        "checkout_domain": "https://" + checkout_domain + "/v4/",
        "env": env,
        "domain": "https://" + core_domain,
        "payment": {
          "amount": price
        },
        "currency": "USD",
        "paymentMethods": [
          'echeck',
          'card'
        ],
        "payer": {
          "name": billing.firstname + ' ' + billing.lastname,
          "email": quote.guestEmail
        },
        "billing": {
          "street": billing.street[0],
          "city": billing.city,
          "postalCode": billing.postcode,
          "subdivisionCode": billing.regionCode,
          "countryCode": countryISO3
        },
        "meta": {
          "source": "magento 2",
          "quote": quoteId,
          "quoteDetails" : quote.totals()
        }
      };

      console.log(config);

      psCheckout.reboot(config);
      // stop observing for mutation events
      window.observer.disconnect();
    }

    if (billing.countryId) {
      $.ajax({
        beforeSend: function (request) {
          request.setRequestHeader("x-publishable-key", publishable_key);
        },
        dataType: "text",
        contentType: "application/json; charset=utf-8",
        url: "https://" + api_domain + "/v3/addresses/countries/iso?code=" + billing.countryId,
        success: function (data) {
          initCheckout(JSON.parse(data).iso3);
        },
        error: function (error) {
          console.log('Unable to get ISO3 code from PayStand!');
        },
      });
    } else {
      initCheckout();
    }
  };

// create an observer instance
  window.observer = new MutationObserver(function (mutations) {
    mutations.forEach(function (mutation) {
      console.log(mutation.type);
      for (var i = 0; i < mutation.addedNodes.length; ++i) {
        var item = mutation.addedNodes[i];
        if (typeof item.getElementsByClassName === "function") {
          if (item.getElementsByClassName('paystand-checkout-form').length > 0) {
            loadPaystandCheckout();
          }
        }
      }
    });
  });

  // configuration of the observer:
  var config = {attributes: true, childList: true, characterData: true};

  // pass in the target node, as well as the observer options
  var total_timeout = 0;
  var recursivelyObserve = function () {
    window.setTimeout(function () {
      total_timeout += 10;

      if (total_timeout < 1000) {
        // select the target node
        var target = $('.payment-method').parent().get(0);

        if (typeof target == "Node") {
          observer.observe(target, config);
        } else {
          recursivelyObserve();
        }
      } else {
        loadPaystandCheckout();
      }
    }, 10);
  };

  recursivelyObserve();

  return this;
});
<|endoftext|>


<commit_before>(function($) {
  $.declare('slideCssAnimations', {

    defaults: {
      animationEndEvents: 'animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd',
      animationBaseClass: 'slide-transition',
      itemActiveClass: 'slide-active'
    },

    init: function() {

      // Check for css3 animation support
      if(this.el[0].style.animationName === undefined) {
        //return false;
      }

      var slide = this.el.data('slide');
      this.animating = false;
      this.container = this.el.slide('getContainer');
      this.slides = this.container.children();

      var pageWidth = slide.pageWidth;
      this.slides.each(function(index, el) {
        $(this).css({
          left: '-'+(index*pageWidth)+'px'
        });
      });
      this.slides.eq(0)
        .addClass(this.itemActiveClass);

      this.el.slide('setTransition', this.proxy(this.transition));

    },

    transition: function(current, previous, callback) {
      var self = this;
      var out = 0;

      console.log(current, previous);
      var direction = (previous < current) ? 'next' : 'prev';

      /*
      if(page === 1 && this.currentSlide === this.slides.length) {
        direction = 'next';
      }

      if(page === this.slides.length && this.currentSlide === 1) {
        direction = 'prev';
      }
     */

      this.animating = true;
      $(this.slides).one(this.animationEndEvents, function(){
        self.animating = false;
        self.container.find('.'+self.itemActiveClass).removeClass(self.itemActiveClass);
        //previous slide
        $(self.slides[previous - 1]).removeClass(self.animationBaseClass + '-' + direction + '-out');
        //current slide
        $(self.slides[current - 1]).removeClass(self.animationBaseClass + '-' + direction + '-in').addClass(self.itemActiveClass);

        $(self.slides).unbind(self.animationEndEvents);

        if (callback) {
          callback();
        }

      });

      //previous slide
      $(this.slides[previous - 1]).addClass(this.animationBaseClass + '-' + direction + '-out ' + this.itemActiveClass);
      //current slide
      $(this.slides[current - 1]).addClass(this.animationBaseClass + '-' + direction + '-in ' + this.itemActiveClass);

    }
  });
})(jQuery);
<commit_msg>:doh:  removed console.log<commit_after>(function($) {
  $.declare('slideCssAnimations', {

    defaults: {
      animationEndEvents: 'animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd',
      animationBaseClass: 'slide-transition',
      itemActiveClass: 'slide-active'
    },

    init: function() {

      // Check for css3 animation support
      if(this.el[0].style.animationName === undefined) {
        //return false;
      }

      var slide = this.el.data('slide');
      this.animating = false;
      this.container = this.el.slide('getContainer');
      this.slides = this.container.children();

      var pageWidth = slide.pageWidth;
      this.slides.each(function(index, el) {
        $(this).css({
          left: '-'+(index*pageWidth)+'px'
        });
      });
      this.slides.eq(0)
        .addClass(this.itemActiveClass);

      this.el.slide('setTransition', this.proxy(this.transition));

    },

    transition: function(current, previous, callback) {
      var self = this;
      var out = 0;

      var direction = (previous < current) ? 'next' : 'prev';

      this.animating = true;
      $(this.slides).one(this.animationEndEvents, function(){
        self.animating = false;
        self.container.find('.'+self.itemActiveClass).removeClass(self.itemActiveClass);
        //previous slide
        $(self.slides[previous - 1]).removeClass(self.animationBaseClass + '-' + direction + '-out');
        //current slide
        $(self.slides[current - 1]).removeClass(self.animationBaseClass + '-' + direction + '-in').addClass(self.itemActiveClass);

        $(self.slides).unbind(self.animationEndEvents);

        if (callback) {
          callback();
        }

      });

      //previous slide
      $(this.slides[previous - 1]).addClass(this.animationBaseClass + '-' + direction + '-out ' + this.itemActiveClass);
      //current slide
      $(this.slides[current - 1]).addClass(this.animationBaseClass + '-' + direction + '-in ' + this.itemActiveClass);

    }
  });
})(jQuery);
<|endoftext|>


<commit_before>
/*!--------------------------------*\
   3-Jekyll Theme
   @author Peiwen Lu (P233)
   https://github.com/P233/3-Jekyll
\*---------------------------------*/

// Detect window size, if less than 1280px add class 'mobile' to sidebar therefore it will be auto hide when trigger the pjax request in small screen devices.
if ($(window).width() <= 1280) {
  $('#sidebar').addClass('mobile')
}

// Variables
    tag1       = $('.pl__all'),
    tag2       = $('.Announcement'),
    tag3       = $('.New Features'),
    tag4       = $('.Engineering'),
    tag5       = $('.Members'),
var sidebar    = $('#sidebar'),
    container  = $('#post'),
    content    = $('#pjax'),
    button     = $('#icon-arrow');

// Tags switcher
var clickHandler = function(k) {
  return function() {
    $(this).addClass('active').siblings().removeClass('active');
    tag1.hide();
    window['tag'+k].delay(50).fadeIn(350);
  }
};
for (var i = 1; i <= 5; i++) {
  $('#js-label' + i).on('click', clickHandler(i));
}

// If sidebar has class 'mobile', hide it after clicking.
tag1.on('click', function() {
  $(this).addClass('active').siblings().removeClass('active');
  if (sidebar.hasClass('mobile')) {
    $('#sidebar, #pjax, #icon-arrow').addClass('fullscreen');
  }
});

// Enable fullscreen.
$('#js-fullscreen').on('click', function() {
  if (button.hasClass('fullscreen')) {
    sidebar.removeClass('fullscreen');
    button.removeClass('fullscreen');
    content.delay(300).queue(function(){
      $(this).removeClass('fullscreen').dequeue();
    });
  } else {
    sidebar.addClass('fullscreen');
    button.addClass('fullscreen');
    content.delay(200).queue(function(){
      $(this).addClass('fullscreen').dequeue();
    });
  }
});

$('#mobile-avatar').on('click', function(){
  $('#sidebar, #pjax, #icon-arrow').addClass('fullscreen');
});

// Pjax
$(document).pjax('#avatar, #mobile-avatar, .pl__all', '#pjax', { fragment: '#pjax', timeout: 10000 });
$(document).on({
  'pjax:click': function() {
    content.removeClass('fadeIn').addClass('fadeOut');
    NProgress.start();
  },
  'pjax:start': function() {
    content.css({'opacity':0});
  },
  'pjax:end': function() {
    NProgress.done();
    container.scrollTop(0);
    content.css({'opacity':1}).removeClass('fadeOut').addClass('fadeIn');
    afterPjax();
  }
});

// Codepen embed js
// http://codepen.io/assets/embed/ei.js
// Added on 17 Nov 2013
// var CodePenEmbed={width:"100%",init:function(){this.showCodePenEmbeds(),this.listenToParentPostMessages()},showCodePenEmbeds:function(){var e=document.getElementsByClassName("codepen");for(var t=e.length-1;t>-1;t--){var n=this._getParamsFromAttributes(e[t]);n=this._convertOldDataAttributesToNewDataAttributes(n);var r=this._buildURL(n),i=this._buildIFrame(n,r);this._addIFrameToPage(e[t],i)}},_getParamsFromAttributes:function(e){var t={},n=e.attributes;for(var r=0,i=n.length;r<i;r++)name=n[r].name,name.indexOf("data-")===0&&(t[name.replace("data-","")]=n[r].value);return t},_convertOldDataAttributesToNewDataAttributes:function(e){return e.href&&(e["slug-hash"]=e.href),e.type&&(e["default-tab"]=e.type),e.safe&&(e["safe"]=="true"?e.animations="run":e.animations="stop-after-5"),e},_buildURL:function(e){var t=this._getHost(e),n=e.user?e.user:"anon",r="?"+this._getGetParams(e),i=[t,n,"embed",e["slug-hash"]+r].join("/");return i.replace(/\/\//g,"//")},_getHost:function(e){return e.host?e.host:document.location.protocol=="file:"?"http://codepen.io":"//codepen.io"},_getGetParams:function(e){var t="",n=0;for(var r in e)t!==""&&(t+="&"),t+=r+"="+encodeURIComponent(e[r]);return t},_buildIFrame:function(e,t){var n={id:"cp_embed_"+e["slug-hash"].replace("/","_"),src:t,scrolling:"no",frameborder:"0",height:this._getHeight(e),allowTransparency:"true","class":"cp_embed_iframe",style:"width: "+this.width+"; overflow: hidden;"},r="",r},_getHeight:function(e){return e.height?e["height"]=="auto"?300:e.height:300},_addIFrameToPage:function(e,t){if(e.parentNode){var n=document.createElement("div");n.innerHTML=t,e.parentNode.replaceChild(n,e)}else e.innerHTML=t},listenToParentPostMessages:function(){var eventMethod=window.addEventListener?"addEventListener":"attachEvent",eventListener=window[eventMethod],messageEvent=eventMethod=="attachEvent"?"onmessage":"message";eventListener(messageEvent,function(e){try{var dataObj=eval("("+e.data+")"),iframe=document.getElementById("cp_embed_"+dataObj.hash);iframe&&(iframe.height=dataObj.height)}catch(err){}},!1)}};

// Re-run scripts for post content after pjax
function afterPjax() {
  // Open links in new tab
  $('#post__content a').attr('target','_blank');

  // Embed codepen after pjax
  // CodePenEmbed.init();

  // Generate post TOC for h1 h2 and h3
  var toc = $('#post__toc-ul');
  // Empty TOC and generate an entry for h1
  toc.empty().append('
' + $('#post__title').text() + '
');

  // Generate entries for h2 and h3
  $('#post__content').children('h2,h3').each(function() {
    // Generate random ID for each heading
    $(this).attr('id', function() {
      var ID = "",
          alphabet = "abcdefghijklmnopqrstuvwxyz";

      for(var i=0; i < 5; i++) {
        ID += alphabet.charAt(Math.floor(Math.random() * alphabet.length));
      }
      return ID;
    });

    if ($(this).prop("tagName") == 'H2') {
      toc.append('
' + $(this).text() + '
');
    } else {
      toc.append('
' + $(this).text() + '
');
    }
  });

  // Smooth scrolling
  $('.js-anchor-link').on('click', function() {
    var target = $(this.hash);
    container.animate({scrollTop: target.offset().top + container.scrollTop() - 70}, 500, function() {
      target.addClass('flash').delay(700).queue(function() {
        $(this).removeClass('flash').dequeue();
      });
    });
  });

  // Lazy Loading Disqus
  // http://jsfiddle.net/dragoncrew/SHGwe/1/
  var ds_loaded = false,
      top = $('#disqus_thread').offset().top;
      identifier = $('#post__title').data('identifier');
  window.disqus_shortname = '';
  window.disqus_identifier = identifier;

  function check() {
    if ( !ds_loaded && container.scrollTop() + container.height() > top ) {
      $.ajax({
        type: 'GET',
        url: 'http://' + disqus_shortname + '.disqus.com/embed.js',
        dataType: 'script',
        cache: true
      });
      ds_loaded = true;
    }
  }check();
  container.scroll(check);
}afterPjax();

<commit_msg>Enable disqus<commit_after>
/*!--------------------------------*\
   3-Jekyll Theme
   @author Peiwen Lu (P233)
   https://github.com/P233/3-Jekyll
\*---------------------------------*/

// Detect window size, if less than 1280px add class 'mobile' to sidebar therefore it will be auto hide when trigger the pjax request in small screen devices.
if ($(window).width() <= 1280) {
  $('#sidebar').addClass('mobile')
}

// Variables
    tag1       = $('.pl__all'),
    tag2       = $('.Announcement'),
    tag3       = $('.New Features'),
    tag4       = $('.Engineering'),
    tag5       = $('.Members'),
var sidebar    = $('#sidebar'),
    container  = $('#post'),
    content    = $('#pjax'),
    button     = $('#icon-arrow');

// Tags switcher
var clickHandler = function(k) {
  return function() {
    $(this).addClass('active').siblings().removeClass('active');
    tag1.hide();
    window['tag'+k].delay(50).fadeIn(350);
  }
};
for (var i = 1; i <= 5; i++) {
  $('#js-label' + i).on('click', clickHandler(i));
}

// If sidebar has class 'mobile', hide it after clicking.
tag1.on('click', function() {
  $(this).addClass('active').siblings().removeClass('active');
  if (sidebar.hasClass('mobile')) {
    $('#sidebar, #pjax, #icon-arrow').addClass('fullscreen');
  }
});

// Enable fullscreen.
$('#js-fullscreen').on('click', function() {
  if (button.hasClass('fullscreen')) {
    sidebar.removeClass('fullscreen');
    button.removeClass('fullscreen');
    content.delay(300).queue(function(){
      $(this).removeClass('fullscreen').dequeue();
    });
  } else {
    sidebar.addClass('fullscreen');
    button.addClass('fullscreen');
    content.delay(200).queue(function(){
      $(this).addClass('fullscreen').dequeue();
    });
  }
});

$('#mobile-avatar').on('click', function(){
  $('#sidebar, #pjax, #icon-arrow').addClass('fullscreen');
});

// Pjax
$(document).pjax('#avatar, #mobile-avatar, .pl__all', '#pjax', { fragment: '#pjax', timeout: 10000 });
$(document).on({
  'pjax:click': function() {
    content.removeClass('fadeIn').addClass('fadeOut');
    NProgress.start();
  },
  'pjax:start': function() {
    content.css({'opacity':0});
  },
  'pjax:end': function() {
    NProgress.done();
    container.scrollTop(0);
    content.css({'opacity':1}).removeClass('fadeOut').addClass('fadeIn');
    afterPjax();
  }
});

// Codepen embed js
// http://codepen.io/assets/embed/ei.js
// Added on 17 Nov 2013
// var CodePenEmbed={width:"100%",init:function(){this.showCodePenEmbeds(),this.listenToParentPostMessages()},showCodePenEmbeds:function(){var e=document.getElementsByClassName("codepen");for(var t=e.length-1;t>-1;t--){var n=this._getParamsFromAttributes(e[t]);n=this._convertOldDataAttributesToNewDataAttributes(n);var r=this._buildURL(n),i=this._buildIFrame(n,r);this._addIFrameToPage(e[t],i)}},_getParamsFromAttributes:function(e){var t={},n=e.attributes;for(var r=0,i=n.length;r<i;r++)name=n[r].name,name.indexOf("data-")===0&&(t[name.replace("data-","")]=n[r].value);return t},_convertOldDataAttributesToNewDataAttributes:function(e){return e.href&&(e["slug-hash"]=e.href),e.type&&(e["default-tab"]=e.type),e.safe&&(e["safe"]=="true"?e.animations="run":e.animations="stop-after-5"),e},_buildURL:function(e){var t=this._getHost(e),n=e.user?e.user:"anon",r="?"+this._getGetParams(e),i=[t,n,"embed",e["slug-hash"]+r].join("/");return i.replace(/\/\//g,"//")},_getHost:function(e){return e.host?e.host:document.location.protocol=="file:"?"http://codepen.io":"//codepen.io"},_getGetParams:function(e){var t="",n=0;for(var r in e)t!==""&&(t+="&"),t+=r+"="+encodeURIComponent(e[r]);return t},_buildIFrame:function(e,t){var n={id:"cp_embed_"+e["slug-hash"].replace("/","_"),src:t,scrolling:"no",frameborder:"0",height:this._getHeight(e),allowTransparency:"true","class":"cp_embed_iframe",style:"width: "+this.width+"; overflow: hidden;"},r="",r},_getHeight:function(e){return e.height?e["height"]=="auto"?300:e.height:300},_addIFrameToPage:function(e,t){if(e.parentNode){var n=document.createElement("div");n.innerHTML=t,e.parentNode.replaceChild(n,e)}else e.innerHTML=t},listenToParentPostMessages:function(){var eventMethod=window.addEventListener?"addEventListener":"attachEvent",eventListener=window[eventMethod],messageEvent=eventMethod=="attachEvent"?"onmessage":"message";eventListener(messageEvent,function(e){try{var dataObj=eval("("+e.data+")"),iframe=document.getElementById("cp_embed_"+dataObj.hash);iframe&&(iframe.height=dataObj.height)}catch(err){}},!1)}};

// Re-run scripts for post content after pjax
function afterPjax() {
  // Open links in new tab
  $('#post__content a').attr('target','_blank');

  // Embed codepen after pjax
  // CodePenEmbed.init();

  // Generate post TOC for h1 h2 and h3
  var toc = $('#post__toc-ul');
  // Empty TOC and generate an entry for h1
  toc.empty().append('
' + $('#post__title').text() + '
');

  // Generate entries for h2 and h3
  $('#post__content').children('h2,h3').each(function() {
    // Generate random ID for each heading
    $(this).attr('id', function() {
      var ID = "",
          alphabet = "abcdefghijklmnopqrstuvwxyz";

      for(var i=0; i < 5; i++) {
        ID += alphabet.charAt(Math.floor(Math.random() * alphabet.length));
      }
      return ID;
    });

    if ($(this).prop("tagName") == 'H2') {
      toc.append('
' + $(this).text() + '
');
    } else {
      toc.append('
' + $(this).text() + '
');
    }
  });

  // Smooth scrolling
  $('.js-anchor-link').on('click', function() {
    var target = $(this.hash);
    container.animate({scrollTop: target.offset().top + container.scrollTop() - 70}, 500, function() {
      target.addClass('flash').delay(700).queue(function() {
        $(this).removeClass('flash').dequeue();
      });
    });
  });

  // Lazy Loading Disqus
  // http://jsfiddle.net/dragoncrew/SHGwe/1/
  var ds_loaded = false,
      top = $('#disqus_thread').offset().top;
      identifier = $('#post__title').data('identifier');
  window.disqus_shortname = 'mogaraorg';
  window.disqus_identifier = identifier;

  function check() {
    if ( !ds_loaded && container.scrollTop() + container.height() > top ) {
      $.ajax({
        type: 'GET',
        url: 'http://' + disqus_shortname + '.disqus.com/embed.js',
        dataType: 'script',
        cache: true
      });
      ds_loaded = true;
    }
  }check();
  container.scroll(check);
}afterPjax();

<|endoftext|>


<commit_before>var $ = require('jquery')
annotator = require('annotator');
$ = annotator.$
var substitution = require('./substitution');

var jsonAnnotation = '{ "ranges" : [' +
'{ "start":"//div/p" , "end":"//div/p" },' +
'{ "startOffset":0 , "endOffset":5 }]}';

substitution.Substitution(jsonAnnotation, "this is a substitution text");

<commit_msg>Updated for our standard development environment<commit_after>var $ = require('jquery')
annotator = require('annotator');
$ = annotator.$
var substitution1 = require('./SubstituteAnnotation');
var substitution2 = require('./substitution');
var tags = require('../plugins/tags.coffee');

var css = require('./app.css');
// console.log(css);

OpenAnnotate = ("OpenAnnotate" in window) ? OpenAnnotate : {}

OpenAnnotate.Annotator = function (element) {
    var $ = jQuery, self = this

    this.annotator = $(element).annotator().data('annotator')
    this.currentUser = null

    this.options = {
        user: {}

    }

    $(element).annotator().annotator('addPlugin', 'Substitutions', 'bbk:english');
    $(element).annotator().annotator('addPlugin', 'Tags');

    jQuery(function ($) {
       $(element).annotator()
                .annotator('setupPlugins');
    });

    return this
}

<|endoftext|>