<commit_before>// hide body to prevent FOUC
document.body.style.opacity = 0
window.addEventListener('WebComponentsReady', function () {
  setTimeout(function () {
    document.body.style.opacity = 1
  }, 100)
})

// set the dialog boxes to vars
const editEmployeeDialog = document.querySelector('chassis-overlay[name="editEmployee"]')
const addEmployeeDialog = document.querySelector('chassis-overlay[name="addEmployee"]')

// create the employee model
const Employee = new NGN.DATA.Model({
  autoid: true,
  fields: {
    first: {
      type: String
    },
    last: {
      type: String
    },
    dob: {
      type: Date
    }
  },
  virtuals: {
    fullName: function () {
      return `${this.first} ${this.last}`
    },
    age: function (e) {
      return moment().diff(moment(this.dob), 'years') // eslint-disable-line no-undef
    }
  }
})

// create the employee store
const EmployeeStore = new NGN.DATA.Store({
  model: Employee,
  allowDuplicates: false
})

// pre-populate the employee list
const employees = [{
  first: 'Michael',
  last: 'Skott',
  dob: '1973-08-14'
}, {
  first: 'Jim',
  last: 'Haplert',
  dob: moment().year('1978').format('YYYY-MM-DD') // eslint-disable-line no-undef
}, {
  first: 'Stanley',
  last: 'Hudson',
  dob: '1965-08-14'
}]

/**
 * Set click handlers for an array of elements
 * @param {String} type
 * @param {Array} elements An array of dom elements to add handlers to
 */
function setHandlers (type, elements) {
  elements = elements || []
  elements.forEach(el => {
    el.onclick = (e) => {
      const data = e.currentTarget.dataset
      const model = EmployeeStore.find(data.id)
      if (type === 'undo') {
        model.undo(3) // each update modifies 3 fields
        refreshEmployees()
      } else if (type === 'remove') {
        removeEmployee(data.id)
      } else if (type === 'edit') {
        document.querySelector('input[name="editName"]').value = data.name
        document.querySelector('input[name="editDob"]').value = data.dob
        document.querySelector('input[name="id"]').value = data.id
        editEmployeeDialog.open()
      }
    }
  })
}

/**
 * Refresh the employee list and set the handlers
 */
function refreshEmployees (filter) {
  document.getElementById('employees').innerHTML = getEmployeesHtml(filter)
  // set the onclick handlers for undo and remove buttons
  setHandlers('undo', document.querySelectorAll('button[name="undo"]'))
  setHandlers('remove', document.querySelectorAll('button[name="remove"]'))
  setHandlers('edit', document.querySelectorAll('button[name="editEmployee"]'))
}

/**
 * Edit the employee model and refresh the view
 *
 * @param {String} modelId
 * @param {Object} data
 * @param {String} data.first
 * @param {String} data.last
 * @param {String} data.dob
 */
function editEmployee (modelId, data) {
  const model = EmployeeStore.find(modelId)
  model.first = data.first
  model.last = data.last
  model.dob = data.dob
  refreshEmployees()
}

/**
 * Add an employee model to the store, set all event listeners on the model,
 * and refresh the view
 *
 * @param {Object} data
 * @param {String} data.first
 * @param {String} data.last
 * @param {String} data.dob
 */
function addEmployee (data) {
  addEvents(EmployeeStore.add(data))
  refreshEmployees()
}

/**
 * Remove an employee model from the store and refresh the view
 *
 * @param {String} id
 */
function removeEmployee (id) {
  const model = EmployeeStore.find(id)
  const index = EmployeeStore.indexOf(model)
  EmployeeStore.remove(index)
  refreshEmployees()
}

// Take the provided data, modify the employee, and close the dialog
document.querySelector('button[name="submitEditDialog"]').onclick = function (e) {
  const data = {
    first: document.querySelector('input[name="editName"]').value.split(' ')[0],
    last: document.querySelector('input[name="editName"]').value.split(' ')[1] || '',
    dob: document.querySelector('input[name="editDob"]').value
  }
  editEmployee(document.querySelector('input[name="id"]').value, data)
  editEmployeeDialog.close()
}

// Take the provided data, add the new employee, and close/clear the dialog
document.querySelector('button[name="submitAddDialog"]').onclick = function (e) {
  const data = {
    first: document.querySelector('input[name="addName"]').value.split(' ')[0],
    last: document.querySelector('input[name="addName"]').value.split(' ')[1] || '',
    dob: document.querySelector('input[name="addDob"]').value
  }
  addEmployee(data)
  // close and clear the dialog
  document.querySelector('button[name="closeAddDialog"]').click()
}

// Close the edit dialog
document.querySelector('button[name="closeEditDialog"]').onclick = function (e) {
  editEmployeeDialog.close()
}

// Close the add dialog and clear the values on the input
document.querySelector('button[name="closeAddDialog"]').onclick = function (e) {
  document.querySelector('input[name="addName"]').value = ''
  document.querySelector('input[name="addDob"]').value = ''
  addEmployeeDialog.close()
}

// Add each employee to the store
employees.forEach(function (e) {
  addEmployee(e)
})

/**
 * Add event handling to a givel model
 *
 * @param {Object} model An instance of an NGN model
 */
function addEvents (model) {
  let events = [
    'field.update',
    'field.create',
    'field.remove',
    'field.invalid',
    'validator.add',
    'validator.remove',
    'relationship.create',
    'relationship.remove'
  ]
  events.forEach(function (event) {
    model['on'](event, (e) => {
      console.log(e)
      if (event.search(/field/) >= 0) {
        refreshEmployees()
      }
    })
  })
}

/**
 * Apply filtering and sorting to the data store, before returning
 * the appropriate HTML for refreshing the employees currently clocked in
 *
 * @return {String} HTML
 */
function getEmployeesHtml () {
  EmployeeStore.clearFilters()
  const filterVal = document.querySelector('input[name="filter"]').value
  const regex = new RegExp(filterVal, 'i')
  EmployeeStore.addFilter(function (employee) {
    return !filterVal || employee.first.search(regex) >= 0 || employee.last.search(regex) >= 0
  })
  // This function is broken right now in conjunction with addFilter. We'll do a hacky sort manually
  // EmployeeStore.sort({
  //   asc: 'last'
  // })
  let employees = EmployeeStore.data.sort(function (a, b) {
    return a.last > b.last ? 1 : -1
  })

  let html = ''

  employees.forEach(employee => {
    let htmlClass = 'employeeData'
    let birthday = false
    employee = EmployeeStore.find(employee.id)

    /* eslint-disable no-undef*/
    if (moment().date() === moment(employee.dob).date() &&
      moment().month() === moment(employee.dob).month()) {
      birthday = true
      htmlClass += ' birthday'
    }
    html += `

        Name: ${employee.fullName}

        Dob: ${moment(employee.dob).format('YYYY-MM-DD')}
        ${birthday ? '
 Happy Birthday (' + employee.age + ') ' + employee.first + '!' : ''}

      <button name="editEmployee" data-id="${employee.id}"
        data-name="${employee.fullName}" data-dob="${moment(employee.dob).format('YYYY-MM-DD')}">Edit</button>
      <button name="undo" data-id="${employee.id}">Undo</button>
      <button name="remove" data-id="${employee.id}">Clock Out</button>

`
  })
  return html
  /* eslint-enable */
}

// open the addEmployee dialog
document.getElementById('clockIn').onclick = function () {
  addEmployeeDialog.open()
}

// clear the filter input text box and refresh the employee list
document.querySelector('button[name="clearFilter"]').onclick = function () {
  document.querySelector('input[name="filter"]').value = ''
  refreshEmployees()
}

// filter the employees by name, given a filter input
document.querySelector('input[name="filter"]').onkeyup = function (e) {
  refreshEmployees()
}

// initialize the employee list
refreshEmployees()
<commit_msg>remove ES2015 code - fix sorting / filtering<commit_after>// hide body to prevent FOUC
document.body.style.opacity = 0
window.addEventListener('WebComponentsReady', function () {
  setTimeout(function () {
    document.body.style.opacity = 1
  }, 100)
})

// set the dialog boxes to vars
var editEmployeeDialog = document.querySelector('chassis-overlay[name="editEmployee"]')
var addEmployeeDialog = document.querySelector('chassis-overlay[name="addEmployee"]')

// create the employee model
var Employee = new NGN.DATA.Model({
  autoid: true,
  fields: {
    first: {
      type: String
    },
    last: {
      type: String
    },
    dob: {
      type: Date
    }
  },
  virtuals: {
    fullName: function () {
      return this.first + ' ' + this.last
    },
    age: function (e) {
      return moment().diff(moment(this.dob), 'years') // eslint-disable-line no-undef
    }
  }
})

// create the employee store
var EmployeeStore = new NGN.DATA.Store({
  model: Employee,
  allowDuplicates: false
})

// pre-populate the employee list
var employees = [{
  first: 'Michael',
  last: 'Skott',
  dob: '1973-08-14'
}, {
  first: 'Jim',
  last: 'Haplert',
  dob: moment().year('1978').format('YYYY-MM-DD') // eslint-disable-line no-undef
}, {
  first: 'Stanley',
  last: 'Hudson',
  dob: '1965-08-14'
}]

/**
 * Set click handlers for an array of elements
 * @param {String} type
 * @param {Array} elements An array of dom elements to add handlers to
 */
function setHandlers (type, elements) {
  elements = elements || []
  elements.forEach(function (el) {
    el.onclick = function (e) {
      var data = e.currentTarget.dataset
      var model = EmployeeStore.find(data.id)
      if (type === 'undo') {
        model.undo(3) // each update modifies 3 fields
        refreshEmployees()
      } else if (type === 'remove') {
        removeEmployee(data.id)
      } else if (type === 'edit') {
        document.querySelector('input[name="editName"]').value = data.name
        document.querySelector('input[name="editDob"]').value = data.dob
        document.querySelector('input[name="id"]').value = data.id
        editEmployeeDialog.open()
      }
    }
  })
}

/**
 * Refresh the employee list and set the handlers
 */
function refreshEmployees () {
  document.getElementById('employees').innerHTML = getEmployeesHtml()
  // set the onclick handlers for undo and remove buttons
  setHandlers('undo', document.querySelectorAll('button[name="undo"]'))
  setHandlers('remove', document.querySelectorAll('button[name="remove"]'))
  setHandlers('edit', document.querySelectorAll('button[name="editEmployee"]'))
}

/**
 * Edit the employee model and refresh the view
 *
 * @param {String} modelId
 * @param {Object} data
 * @param {String} data.first
 * @param {String} data.last
 * @param {String} data.dob
 */
function editEmployee (modelId, data) {
  var model = EmployeeStore.find(modelId)
  model.first = data.first
  model.last = data.last
  model.dob = data.dob
  refreshEmployees()
}

/**
 * Add an employee model to the store, set all event listeners on the model,
 * and refresh the view
 *
 * @param {Object} data
 * @param {String} data.first
 * @param {String} data.last
 * @param {String} data.dob
 */
function addEmployee (data) {
  addEvents(EmployeeStore.add(data))
  refreshEmployees()
}

/**
 * Remove an employee model from the store and refresh the view
 *
 * @param {String} id
 */
function removeEmployee (id) {
  var model = EmployeeStore.find(id)
  var index = EmployeeStore.indexOf(model)
  EmployeeStore.remove(index)
  refreshEmployees()
}

// Take the provided data, modify the employee, and close the dialog
document.querySelector('button[name="submitEditDialog"]').onclick = function (e) {
  var data = {
    first: document.querySelector('input[name="editName"]').value.split(' ')[0],
    last: document.querySelector('input[name="editName"]').value.split(' ')[1] || '',
    dob: document.querySelector('input[name="editDob"]').value
  }
  editEmployee(document.querySelector('input[name="id"]').value, data)
  editEmployeeDialog.close()
}

// Take the provided data, add the new employee, and close/clear the dialog
document.querySelector('button[name="submitAddDialog"]').onclick = function (e) {
  var data = {
    first: document.querySelector('input[name="addName"]').value.split(' ')[0],
    last: document.querySelector('input[name="addName"]').value.split(' ')[1] || '',
    dob: document.querySelector('input[name="addDob"]').value
  }
  addEmployee(data)
  // close and clear the dialog
  document.querySelector('button[name="closeAddDialog"]').click()
}

// Close the edit dialog
document.querySelector('button[name="closeEditDialog"]').onclick = function (e) {
  editEmployeeDialog.close()
}

// Close the add dialog and clear the values on the input
document.querySelector('button[name="closeAddDialog"]').onclick = function (e) {
  document.querySelector('input[name="addName"]').value = ''
  document.querySelector('input[name="addDob"]').value = ''
  addEmployeeDialog.close()
}

// Add each employee to the store
employees.forEach(function (e) {
  addEmployee(e)
})

/**
 * Add event handling to a givel model
 *
 * @param {Object} model An instance of an NGN model
 */
function addEvents (model) {
  var events = [
    'field.update',
    'field.create',
    'field.remove',
    'field.invalid',
    'validator.add',
    'validator.remove',
    'relationship.create',
    'relationship.remove'
  ]
  events.forEach(function (event) {
    model['on'](event, function (e) {
      console.log(e)
      if (event.search(/field/) >= 0) {
        refreshEmployees()
      }
    })
  })
}

function filterAndSortRecords () {
  EmployeeStore.clearFilters()
  var filterVal = document.querySelector('input[name="filter"]').value
  var regex = new RegExp(filterVal, 'i')
  EmployeeStore.addFilter(function (employee) {
    return !filterVal || employee.first.search(regex) >= 0 || employee.last.search(regex) >= 0
  })
  EmployeeStore.sort({
    last: 'asc',
    first: 'asc'
  })
}

/**
 * Apply filtering and sorting to the data store, before returning
 * the appropriate HTML for refreshing the employees currently clocked in
 *
 * @return {String} HTML
 */
function getEmployeesHtml () {
  filterAndSortRecords();
  // This function is broken right now in conjunction with addFilter. We'll do a hacky sort manually
  var employees = EmployeeStore.records;

  var html = ''

  employees.forEach(function (employee) {
    var htmlClass = 'employeeData'
    var birthday = false
    employee = EmployeeStore.find(employee.id)

    /* eslint-disable no-undef*/
    if (moment().date() === moment(employee.dob).date() &&
      moment().month() === moment(employee.dob).month()) {
      birthday = true
      htmlClass += ' birthday'
    }

    html += '
' +
      '
' +
        'Name: ' + employee.fullName +'
' +
        'Dob: ' + moment(employee.dob).format('YYYY-MM-DD') +
        (birthday ? '
 Happy Birthday (' + employee.age + ') ' + employee.first + '!' : '') +
      '
' +
      '<button name="editEmployee" data-id="' + employee.id + '"' +
        'data-name="' + employee.fullName +'" data-dob="' + moment(employee.dob).format('YYYY-MM-DD') +'">Edit</button>' +
      '<button name="undo" data-id="' + employee.id + '">Undo</button>' +
      '<button name="remove" data-id="' + employee.id + '">Clock Out</button>' +
   '
'
  })
  return html
  /* eslint-enable */
}

// open the addEmployee dialog
document.getElementById('clockIn').onclick = function () {
  addEmployeeDialog.open()
}

// clear the filter input text box and refresh the employee list
document.querySelector('button[name="clearFilter"]').onclick = function () {
  document.querySelector('input[name="filter"]').value = ''
  refreshEmployees()
}

// filter the employees by name, given a filter input
document.querySelector('input[name="filter"]').onkeyup = function (e) {
  refreshEmployees()
}

// initialize the employee list
refreshEmployees()
<|endoftext|>


<commit_before>var $ = require('jquery-browserify');
var _ = require('underscore');
var Backbone = require('backbone');
var templates = require('../../dist/templates');

module.exports = Backbone.View.extend({
  template: _.template(templates.sidebar.branches),

  initialize: function(options) {
    this.model = options.model;
    this.repo = options.repo;
    this.router = options.router;
    this.branch = options.branch || this.repo.get('master_branch');

    this.listenTo(this.model, 'sync', this.render, this);
  },

  render: function() {
    // only render branches selector if two or more branches
    if (this.model.length < 2) return;

    this.$el.html(this.template());

    var tmpl = _.template(templates.sidebar.branch);
    this.model.each((function(branch, index) {
      this.$el.find('select').append(tmpl(_.extend(branch.attributes, {
        selected: this.branch && this.branch === branch.get('name')
      })));
    }).bind(this));

    // TODO: why does this causes object undefined errors on navigation events?
    this.$el.find('.chzn-select').chosen().change(function() {
      this.router.navigate($(this).val(), true);
    });

    this.$el.addClass('open');

    return this;
  }
});
<commit_msg>remove logging<commit_after>var $ = require('jquery-browserify');
var _ = require('underscore');
var Backbone = require('backbone');
var templates = require('../../dist/templates');

module.exports = Backbone.View.extend({
  template: _.template(templates.sidebar.branches),

  initialize: function(options) {
    this.model = options.model;
    this.repo = options.repo;
    this.router = options.router;
    this.branch = options.branch || this.repo.get('master_branch');

    this.listenTo(this.model, 'sync', this.render, this);
  },

  render: function() {
    // only render branches selector if two or more branches
    if (this.model.length < 2) return;

    this.$el.html(this.template());

    var tmpl = _.template(templates.sidebar.branch);
    this.model.each((function(branch, index) {
      this.$el.find('select').append(tmpl(_.extend(branch.attributes, {
        selected: this.branch && this.branch === branch.get('name')
      })));
    }).bind(this));

    // TODO: why does this causes object undefined errors on navigation events?
    this.$el.find('.chzn-select').chosen().change(function() {
      this.router.navigate($(this).val(), true);
    });

    this.$el.addClass('open');

    return this;
  },

  remove: function() {
    console.log('Branches.remove()');
    Backbone.View.prototype.remove.call(this);
  }
});
<|endoftext|>


<commit_before>"use strict";

const pathUtil = require("path");
const fs = require("./utils/fs");
const validate = require("./utils/validate");
const dir = require("./dir");

const validateInput = (methodName, symlinkValue, path) => {
  const methodSignature = `${methodName}(symlinkValue, path)`;
  validate.argument(methodSignature, "symlinkValue", symlinkValue, ["string"]);
  validate.argument(methodSignature, "path", path, ["string"]);
};

// ---------------------------------------------------------
// Sync
// ---------------------------------------------------------

const symlinkSync = (symlinkValue, path) => {
  try {
    fs.symlinkSync(symlinkValue, path);
  } catch (err) {
    if (err.code === "ENOENT") {
      // Parent directories don't exist. Just create them and rety.
      dir.createSync(pathUtil.dirname(path));
      fs.symlinkSync(symlinkValue, path);
    } else {
      throw err;
    }
  }
};

// ---------------------------------------------------------
// Async
// ---------------------------------------------------------

const symlinkAsync = (symlinkValue, path) => {
  return new Promise((resolve, reject) => {
    fs.symlink(symlinkValue, path)
      .then(resolve)
      .catch(err => {
        if (err.code === "ENOENT") {
          // Parent directories don't exist. Just create them and rety.
          dir
            .createAsync(pathUtil.dirname(path))
            .then(() => {
              return fs.symlink(symlinkValue, path);
            })
            .then(resolve, reject);
        } else {
          reject(err);
        }
      });
  });
};

// ---------------------------------------------------------
// API
// ---------------------------------------------------------

exports.validateInput = validateInput;
exports.sync = symlinkSync;
exports.async = symlinkAsync;
<commit_msg>Fix typo in `lib/symlink.js`<commit_after>"use strict";

const pathUtil = require("path");
const fs = require("./utils/fs");
const validate = require("./utils/validate");
const dir = require("./dir");

const validateInput = (methodName, symlinkValue, path) => {
  const methodSignature = `${methodName}(symlinkValue, path)`;
  validate.argument(methodSignature, "symlinkValue", symlinkValue, ["string"]);
  validate.argument(methodSignature, "path", path, ["string"]);
};

// ---------------------------------------------------------
// Sync
// ---------------------------------------------------------

const symlinkSync = (symlinkValue, path) => {
  try {
    fs.symlinkSync(symlinkValue, path);
  } catch (err) {
    if (err.code === "ENOENT") {
      // Parent directories don't exist. Just create them and retry.
      dir.createSync(pathUtil.dirname(path));
      fs.symlinkSync(symlinkValue, path);
    } else {
      throw err;
    }
  }
};

// ---------------------------------------------------------
// Async
// ---------------------------------------------------------

const symlinkAsync = (symlinkValue, path) => {
  return new Promise((resolve, reject) => {
    fs.symlink(symlinkValue, path)
      .then(resolve)
      .catch(err => {
        if (err.code === "ENOENT") {
          // Parent directories don't exist. Just create them and retry.
          dir
            .createAsync(pathUtil.dirname(path))
            .then(() => {
              return fs.symlink(symlinkValue, path);
            })
            .then(resolve, reject);
        } else {
          reject(err);
        }
      });
  });
};

// ---------------------------------------------------------
// API
// ---------------------------------------------------------

exports.validateInput = validateInput;
exports.sync = symlinkSync;
exports.async = symlinkAsync;
<|endoftext|>


<commit_before>'use babel'

import { client } from '../connection'
import { CompositeDisposable } from 'atom'

let { searchdocs } = client.import({rpc: ['searchdocs']})

let ink, pane

export function activate(_ink) {
  ink = _ink

  pane = ink.DocPane.fromId('Documentation')
  pane.setItems([])

  pane.search = (text, mod, exported) => {
    client.boot()
    return searchdocs({query: text, mod, exported})
  }

  subs = new CompositeDisposable()

  subs.add(atom.commands.add('atom-workspace', 'julia-client:show-docpane', () => {
    pane.open()
  }))
}
<commit_msg>handle the texifying here<commit_after>'use babel'

import { client } from '../connection'
import { CompositeDisposable } from 'atom'

let { searchdocs } = client.import({rpc: ['searchdocs']})

let ink, pane

export function activate(_ink) {
  ink = _ink

  pane = ink.DocPane.fromId('Documentation')
  pane.setItems([])

  pane.process = (item) => {item.html = ink.KaTeX.texify(item.html); return item}

  pane.search = (text, mod, exported) => {
    client.boot()
    return searchdocs({query: text, mod, exported})
  }

  subs = new CompositeDisposable()

  subs.add(atom.commands.add('atom-workspace', 'julia-client:show-docpane', () => {
    pane.open()
  }))
}
<|endoftext|>


<commit_before>const path = require('path');
const ip = require('ip');
const webpack = require('webpack');
const CopyPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const {mergeConfig, getLibFile} = require('../utils');

module.exports = (env = {}) => {
  const resolvePath = path.resolve.bind(path, __dirname);
  const myIpAddress = ip.address();
  const appServerPort = 5000;
  const devServerPort = 8080;

  const libFile = getLibFile({
    productionOnly: Boolean(env.production),
    verbose: Boolean(env.verbose),
  });

  if (!libFile) {
    process.exit(1);
  }

  const devConfig = {
    logLevel: 'warn',
    reduxLoggerEnabled: false,
    reduxDevToolsEnabled: false,
  };

  if (!env.production && env.extDevConfig) {
    mergeConfig(devConfig, resolvePath('webpack.dev.js'));
  }

  const productionEntry = [
    '../lib/polyfills.js',
    'whatwg-fetch',
    './src/client/index.js',
  ];

  const developmentEntry = [
    '../lib/polyfills.js',
    '../lib/polyfills-extra.js', // Needed when importing cfxnes directly from its source code, not as a bundled library
    'whatwg-fetch',
    './src/client/index.js',
  ];

  const commonRules = [
    {
      test: /\.(js|jsx)$/,
      exclude: /node_modules/,
      loader: 'babel-loader',
    },
    {
      test: /\.(svg|woff|woff2|eot|ttf)(\?\S*)?$/,
      loader: 'file-loader?name=[name].[ext]',
    },
  ];

  const productionRules = [
    ...commonRules,
    {
      test: /\.css$/,
      use: [
        MiniCssExtractPlugin.loader,
        'css-loader?importLoaders=1', // =1 means postcss-loader
        'postcss-loader',
      ],
    },
  ];

  const developmentRules = [
    ...commonRules,
    {
      test: /\.(js|jsx)$/,
      loader: 'source-map-loader',
      enforce: 'pre',
    },
    {
      test: /\.css$/,
      use: [
        'style-loader',
        'css-loader?importLoaders=1', // =1 means postcss-loader
        'postcss-loader',
      ],
    },
  ];

  const commonPlugins = [
    new webpack.DefinePlugin({
      __DEVELOPMENT__: JSON.stringify(!env.production),
      __REDUX_LOGGER_ENABLED__: devConfig.reduxLoggerEnabled,
      __REDUX_DEVTOOLS_ENABLED__: devConfig.reduxDevToolsEnabled,
      __LOG_LEVEL__: JSON.stringify(env.production ? 'warn' : devConfig.logLevel),
    }),
    new webpack.IgnorePlugin(/^fs$/), // cfxnes.debug.js contains unused required('fs') call
    new CopyPlugin([
      {from: 'src/client/index.html'},
      {from: 'src/client/static'},
    ]),
  ];

  const productionPlugins = [
    ...commonPlugins,
    new MiniCssExtractPlugin({filename: 'bundle.css'}),
  ];

  const developmentPlugins = [
    ...commonPlugins,
    new webpack.HotModuleReplacementPlugin(),
  ];

  return {
    mode: env.production ? 'production' : 'development',
    context: __dirname,
    entry: env.production
      ? productionEntry
      : developmentEntry,
    output: {
      path: resolvePath('dist/static'),
      pathinfo: !env.production,
      filename: 'bundle.js',
    },
    module: {
      rules: env.production ? productionRules : developmentRules,
    },
    resolve: {
      extensions: ['.js', '.jsx'],
      alias: {
        cfxnes: libFile,
      },
    },
    plugins: env.production ? productionPlugins : developmentPlugins,
    devtool: env.production ? 'nosources-source-map' : 'eval-source-map',
    watch: !env.production,
    watchOptions: {
      poll: 1000,
    },
    performance: {
      hints: false,
    },
    devServer: {
      compress: true,
      historyApiFallback: true,
      host: '0.0.0.0',
      hot: true,
      inline: true,
      open: true,
      openPage: '',
      port: devServerPort,
      proxy: {
        '/api': `http://${myIpAddress}:${appServerPort}`,
      },
      public: `${myIpAddress}:${devServerPort}`,
      useLocalIp: true,
    },
  };
};
<commit_msg>Use sideEffects:false in wepack config<commit_after>const path = require('path');
const ip = require('ip');
const webpack = require('webpack');
const CopyPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const {mergeConfig, getLibFile} = require('../utils');

module.exports = (env = {}) => {
  const resolvePath = path.resolve.bind(path, __dirname);
  const myIpAddress = ip.address();
  const appServerPort = 5000;
  const devServerPort = 8080;

  const libFile = getLibFile({
    productionOnly: Boolean(env.production),
    verbose: Boolean(env.verbose),
  });

  if (!libFile) {
    process.exit(1);
  }

  const devConfig = {
    logLevel: 'warn',
    reduxLoggerEnabled: false,
    reduxDevToolsEnabled: false,
  };

  if (!env.production && env.extDevConfig) {
    mergeConfig(devConfig, resolvePath('webpack.dev.js'));
  }

  const productionEntry = [
    '../lib/polyfills.js',
    'whatwg-fetch',
    './src/client/index.js',
  ];

  const developmentEntry = [
    '../lib/polyfills.js',
    '../lib/polyfills-extra.js', // Needed when importing cfxnes directly from its source code, not as a bundled library
    'whatwg-fetch',
    './src/client/index.js',
  ];

  const commonRules = [
    {
      sideEffects: false,
    },
    {
      test: /\.(js|jsx)$/,
      exclude: /node_modules/,
      loader: 'babel-loader',
    },
    {
      test: /\.(svg|woff|woff2|eot|ttf)(\?\S*)?$/,
      loader: 'file-loader?name=[name].[ext]',
    },
  ];

  const productionRules = [
    ...commonRules,
    {
      test: /\.css$/,
      use: [
        MiniCssExtractPlugin.loader,
        'css-loader?importLoaders=1', // =1 means postcss-loader
        'postcss-loader',
      ],
    },
  ];

  const developmentRules = [
    ...commonRules,
    {
      test: /\.(js|jsx)$/,
      loader: 'source-map-loader',
      enforce: 'pre',
    },
    {
      test: /\.css$/,
      use: [
        'style-loader',
        'css-loader?importLoaders=1', // =1 means postcss-loader
        'postcss-loader',
      ],
    },
  ];

  const commonPlugins = [
    new webpack.DefinePlugin({
      __DEVELOPMENT__: JSON.stringify(!env.production),
      __REDUX_LOGGER_ENABLED__: devConfig.reduxLoggerEnabled,
      __REDUX_DEVTOOLS_ENABLED__: devConfig.reduxDevToolsEnabled,
      __LOG_LEVEL__: JSON.stringify(env.production ? 'warn' : devConfig.logLevel),
    }),
    new webpack.IgnorePlugin(/^fs$/), // cfxnes.debug.js contains unused required('fs') call
    new CopyPlugin([
      {from: 'src/client/index.html'},
      {from: 'src/client/static'},
    ]),
  ];

  const productionPlugins = [
    ...commonPlugins,
    new MiniCssExtractPlugin({filename: 'bundle.css'}),
  ];

  const developmentPlugins = [
    ...commonPlugins,
    new webpack.HotModuleReplacementPlugin(),
  ];

  return {
    mode: env.production ? 'production' : 'development',
    context: __dirname,
    entry: env.production
      ? productionEntry
      : developmentEntry,
    output: {
      path: resolvePath('dist/static'),
      pathinfo: !env.production,
      filename: 'bundle.js',
    },
    module: {
      rules: env.production ? productionRules : developmentRules,
    },
    resolve: {
      extensions: ['.js', '.jsx'],
      alias: {
        cfxnes: libFile,
      },
    },
    plugins: env.production ? productionPlugins : developmentPlugins,
    devtool: env.production ? 'nosources-source-map' : 'eval-source-map',
    watch: !env.production,
    watchOptions: {
      poll: 1000,
    },
    performance: {
      hints: false,
    },
    devServer: {
      compress: true,
      historyApiFallback: true,
      host: '0.0.0.0',
      hot: true,
      inline: true,
      open: true,
      openPage: '',
      port: devServerPort,
      proxy: {
        '/api': `http://${myIpAddress}:${appServerPort}`,
      },
      public: `${myIpAddress}:${devServerPort}`,
      useLocalIp: true,
    },
  };
};
<|endoftext|>


<commit_before>var packager = require('electron-packager');
var config = require('./package.json');

packager({
    dir: './',
    out: '../dist',
    name: config.name,
    // platform: 'darwin,win32',
    platform: 'darwin,win32',
    arch: 'x64',
    version: '0.34.1',
    // version: '0.30.0',
    icon: './resources/icon', //<- アプリアイコン

    'app-bundle-id': 'edu.wisc.geology.wiscsims', //<- 自分のドメインなどを使用してください

    'app-version': config.version,
    'helper-bundle-id': 'edu.wisc.geology.wiscsims', //<- 自分のドメインなどを使用してください
    overwrite: true,
    asar: false,
    prune: false,
    // ignore: "node_modules/(electron-packager|electron-prebuilt|\.bin)|release\.js|\.DS_Store",
    'version-string': {
        CompanyName: 'WiscSIMS',
        FileDescription: 'Cycle by cycle plot for isotope monkeys',
        OriginalFilename: config.name,
        FileVersion: config.version,
        ProductVersion: config.version,
        ProductName: config.name,
        InternalName: config.name
    }
}, function done (err, appPath) {
    if(err) {
        throw new Error(err);
    }
    console.log('Done!!');
});
<commit_msg>Modify build conditions<commit_after>var packager = require('electron-packager');
var config = require('./package.json');

packager({
    dir: './',
    out: '../dist',
    name: config.name,
    // platform: 'darwin,win32',
    platform: 'darwin,win32',
    arch: 'x64',
    version: '0.34.1',
    // version: '0.30.0',
    icon: './resources/icon', //<- アプリアイコン

    'app-bundle-id': 'edu.wisc.geology.wiscsims', //<- 自分のドメインなどを使用してください

    'app-version': config.version,
    'helper-bundle-id': 'edu.wisc.geology.wiscsims', //<- 自分のドメインなどを使用してください
    overwrite: true,
    asar: false,
    prune: false,
    ignore: "node_modules/(electron-packager|electron-prebuilt|\.bin)|release\.js|\.DS_Store",
    'version-string': {
        CompanyName: 'WiscSIMS',
        FileDescription: 'Cycle by cycle plot for Stable Isotope MonkeyS',
        OriginalFilename: config.name,
        FileVersion: config.version,
        ProductVersion: config.version,
        ProductName: config.name,
        InternalName: config.name
    }
}, function done (err, appPath) {
    if(err) {
        throw new Error(err);
    }
    console.log('Done!!');
});
<|endoftext|>


<commit_before>let output = '';
if (Application('iTunes').running()) {
    const track = Application('iTunes').currentTrack;
    const artist = track.artist();
    const title = track.name();
    output = (`${title} - ${artist}`).substr(0, 50);
} /*else if (Application('Spotify').running()) {
    const track = Application('Spotify').currentTrack;
    const artist = track.artist();
    const title = track.name();
    output = (`♫ ${title} - ${artist}`).substr(0, 50);
}*/

output;
<commit_msg>uncomment spotify<commit_after>let output = '';
if (Application('iTunes').running()) {
    const track = Application('iTunes').currentTrack;
    const artist = track.artist();
    const title = track.name();
    output = (`${title} - ${artist}`).substr(0, 50);
} else if (Application('Spotify').running()) {
    const track = Application('Spotify').currentTrack;
    const artist = track.artist();
    const title = track.name();
    output = (`${title} - ${artist}`).substr(0, 50);
}

output;
<|endoftext|>


<commit_before>const {app, ipcMain, dialog} = require('electron');
const fs = require('fs');
const path = require('path');
const os = require('os');

const logsPath = path.normalize(path.join(app.getPath('documents'), 'KolpaqueClient.log'));

function writeLine(text) {
    text.unshift(new Date());

    fs.appendFileSync(logsPath, text.join(' ') + os.EOL);
}

module.exports = writeLine;
<commit_msg>Change.<commit_after>const {app, ipcMain, dialog} = require('electron');
const fs = require('fs');
const path = require('path');
const os = require('os');

const logsPath = path.normalize(path.join(app.getPath('documents'), 'KolpaqueClient.log'));

function writeLine(logs) {
    logs.unshift(new Date());

    fs.appendFileSync(logsPath, logs.join(' ') + os.EOL);
}

module.exports = writeLine;
<|endoftext|>


<commit_before>'use strict';

const { EventEmitter } = require('events');
const util = require('util');
const sinon = require('sinon');

const stub = {};

Object.assign(stub, {
  write: sinon.stub().returns(stub),
  moveCursor: sinon.stub().returns(stub),
  setPrompt: sinon.stub().returns(stub),
  close: sinon.stub().returns(stub),
  pause: sinon.stub().returns(stub),
  resume: sinon.stub().returns(stub),
  _getCursorPos: sinon.stub().returns({
    cols: 0,
    rows: 0,
  }),
  output: {
    end: sinon.stub(),
    mute: sinon.stub(),
    unmute: sinon.stub(),
    __raw__: '',
    write(str) {
      this.__raw__ += str;
    },
  },
});

const ReadlineStub = function () {
  this.line = '';
  this.input = new EventEmitter();
  EventEmitter.apply(this, arguments);
};

util.inherits(ReadlineStub, EventEmitter);
Object.assign(ReadlineStub.prototype, stub);

module.exports = ReadlineStub;
<commit_msg>Replace util.inherits with class extends<commit_after>'use strict';

const { EventEmitter } = require('events');
const sinon = require('sinon');
class ReadlineStub extends EventEmitter {
  constructor() {
    super();
    this.line = '';
    this.input = new EventEmitter();
  }
}

const stub = {};

Object.assign(stub, {
  write: sinon.stub().returns(stub),
  moveCursor: sinon.stub().returns(stub),
  setPrompt: sinon.stub().returns(stub),
  close: sinon.stub().returns(stub),
  pause: sinon.stub().returns(stub),
  resume: sinon.stub().returns(stub),
  _getCursorPos: sinon.stub().returns({
    cols: 0,
    rows: 0,
  }),
  output: {
    end: sinon.stub(),
    mute: sinon.stub(),
    unmute: sinon.stub(),
    __raw__: '',
    write(str) {
      this.__raw__ += str;
    },
  },
});

Object.assign(ReadlineStub.prototype, stub);

module.exports = ReadlineStub;
<|endoftext|>


<commit_before>var MultiChannel = require("../..")
    , net = require("net")
    , MuxDemux = require("mux-demux")
    , channel = MultiChannel()

net.createServer(function (con) {
    var mdm = MuxDemux({
            error: false
        })

    mdm.on("connection", function (stream) {
        channel(stream, {
            streamName: stream.meta
        })
    })

    con.pipe(mdm).pipe(con)
}).listen(8642)<commit_msg>updated example<commit_after>var MultiChannel = require("../..")
    , net = require("net")
    , MuxDemux = require("mux-demux")
    , channel = MultiChannel()

net.createServer(function (con) {
    var mdm = MuxDemux({
        error: false
    })

    mdm.on("connection", function (stream) {
        channel(stream, {
            streamName: stream.meta
        })
    })

    con.pipe(mdm).pipe(con)
}).listen(8642)<|endoftext|>


<commit_before>const getLanguage      = require('../language').get;
const localize         = require('../localize').localize;
const getPropertyValue = require('../utility').getPropertyValue;

let currencies_config = {};

const formatMoney = (currency_value, amount, exclude_currency, decimals = 0, minimumFractionDigits = 0) => {
    let money = amount;
    if (money) money = String(money).replace(/,/g, '');
    const sign           = money && Number(money) < 0 ? '-' : '';
    const decimal_places = decimals || getDecimalPlaces(currency_value);

    money = isNaN(money) ? 0 : Math.abs(money);
    if (typeof Intl !== 'undefined') {
        const options = {
            minimumFractionDigits: minimumFractionDigits || decimal_places,
            maximumFractionDigits: decimal_places,
        };
        money = new Intl.NumberFormat(getLanguage().toLowerCase().replace('_', '-'), options).format(money);
    } else {
        money = addComma(money, decimal_places);
    }

    return sign + (exclude_currency ? '' : formatCurrency(currency_value)) + money;
};

const formatCurrency = currency => `<span class="symbols ${(currency || '').toLowerCase()}"></span>`; // defined in binary-style

const addComma = (num, decimal_points, is_crypto) => {
    let number = String(num || 0).replace(/,/g, '');
    if (typeof decimal_points !== 'undefined') {
        number = (+number).toFixed(decimal_points);
    }
    if (is_crypto) {
        number = parseFloat(+number);
    }

    return number.toString().replace(/(^|[^\w.])(\d{4,})/g, ($0, $1, $2) => (
        $1 + $2.replace(/\d(?=(?:\d\d\d)+(?!\d))/g, '$&,')
    ));
};

const calcDecimalPlaces = (currency) => isCryptocurrency(currency) ? 8 : 2;

const getDecimalPlaces = (currency) => (
    // need to check currencies_config[currency] exists instead of || in case of 0 value
    currencies_config[currency] ? getPropertyValue(currencies_config, [currency, 'fractional_digits']) : calcDecimalPlaces(currency)
);

const setCurrencies = (website_status) => {
    currencies_config = website_status.currencies_config;
};

// (currency in crypto_config) is a back-up in case website_status doesn't include the currency config, in some cases where it's disabled
const isCryptocurrency = currency => /crypto/i.test(getPropertyValue(currencies_config, [currency, 'type'])) || (currency in CryptoConfig.get());

const CryptoConfig = (() => {
    let crypto_config;

    const initCryptoConfig = () => ({
        BTC: { name: localize('Bitcoin'),       min_withdrawal: 0.002, pa_max_withdrawal: 5,    pa_min_withdrawal: 0.002 },
        BCH: { name: localize('Bitcoin Cash'),  min_withdrawal: 0.002, pa_max_withdrawal: 5,    pa_min_withdrawal: 0.002 },
        ETH: { name: localize('Ether'),         min_withdrawal: 0.002, pa_max_withdrawal: 5,    pa_min_withdrawal: 0.002 },
        ETC: { name: localize('Ether Classic'), min_withdrawal: 0.002, pa_max_withdrawal: 5,    pa_min_withdrawal: 0.002 },
        LTC: { name: localize('Litecoin'),      min_withdrawal: 0.002, pa_max_withdrawal: 5,    pa_min_withdrawal: 0.002 },
        UST: { name: localize('Tether'),        min_withdrawal: 0.02,  pa_max_withdrawal: 2000, pa_min_withdrawal: 10 },
    });

    return {
        get: () => {
            if (!crypto_config) {
                crypto_config = initCryptoConfig();
            }
            return crypto_config;
        },
    };
})();

const getMinWithdrawal = currency => (isCryptocurrency(currency) ? (getPropertyValue(CryptoConfig.get(), [currency, 'min_withdrawal']) || 0.002) : 1);

/**
 * Returns the transfer limits for the account.
 * @param currency
 * @param which
 * @returns {min: numeric, ?max: numeric}
 */
const getTransferLimits = (currency, which) => {
    const transfer_limits = getPropertyValue(currencies_config, [currency, 'transfer_between_accounts', 'limits']) || getMinWithdrawal(currency);
    const decimals     = getDecimalPlaces(currency);

    switch (which) {
        case 'max':
            return transfer_limits.max ? transfer_limits.max.toFixed(decimals) : undefined;
        case 'all':
            return transfer_limits;
        default:
            return transfer_limits.min ? transfer_limits.min.toFixed(decimals) : transfer_limits;
    }
};

const getTransferFee = (currency_from, currency_to) => {
    const transfer_fee = getPropertyValue(currencies_config, [currency_from, 'transfer_between_accounts', 'fees', currency_to]);
    return `${typeof transfer_fee === 'undefined' ? '1' : transfer_fee}%`;
};

// returns in a string format, e.g. '0.00000001'
const getMinimumTransferFee = (currency) => {
    const decimals = getDecimalPlaces(currency);
    return `${currency} ${(1 / Math.pow(10, decimals)).toFixed(decimals)}`; // we need toFixed() so that it doesn't display in scientific notation, e.g. 1e-8 for currencies with 8 decimal places
};

// @param {String} limit = max|min
const getPaWithdrawalLimit = (currency, limit) => {
    if (isCryptocurrency(currency)) {
        return getPropertyValue(CryptoConfig.get(), [currency, `pa_${limit}_withdrawal`]); // pa_min_withdrawal and pa_max_withdrawal used here
    }
    return limit === 'max' ? 2000 : 10; // limits for fiat currency
};

const getCurrencyName = currency => getPropertyValue(CryptoConfig.get(), [currency, 'name']) || '';

const getMinPayout = currency => getPropertyValue(currencies_config, [currency, 'stake_default']);

module.exports = {
    formatMoney,
    formatCurrency,
    addComma,
    getDecimalPlaces,
    setCurrencies,
    isCryptocurrency,
    getCurrencyName,
    getMinWithdrawal,
    getTransferLimits,
    getTransferFee,
    getMinimumTransferFee,
    getMinPayout,
    getPaWithdrawalLimit,
    getCurrencies: () => currencies_config,
};
<commit_msg>Better failsafe and more consistent api.<commit_after>const getLanguage      = require('../language').get;
const localize         = require('../localize').localize;
const getPropertyValue = require('../utility').getPropertyValue;

let currencies_config = {};

const formatMoney = (currency_value, amount, exclude_currency, decimals = 0, minimumFractionDigits = 0) => {
    let money = amount;
    if (money) money = String(money).replace(/,/g, '');
    const sign           = money && Number(money) < 0 ? '-' : '';
    const decimal_places = decimals || getDecimalPlaces(currency_value);

    money = isNaN(money) ? 0 : Math.abs(money);
    if (typeof Intl !== 'undefined') {
        const options = {
            minimumFractionDigits: minimumFractionDigits || decimal_places,
            maximumFractionDigits: decimal_places,
        };
        money = new Intl.NumberFormat(getLanguage().toLowerCase().replace('_', '-'), options).format(money);
    } else {
        money = addComma(money, decimal_places);
    }

    return sign + (exclude_currency ? '' : formatCurrency(currency_value)) + money;
};

const formatCurrency = currency => `<span class="symbols ${(currency || '').toLowerCase()}"></span>`; // defined in binary-style

const addComma = (num, decimal_points, is_crypto) => {
    let number = String(num || 0).replace(/,/g, '');
    if (typeof decimal_points !== 'undefined') {
        number = (+number).toFixed(decimal_points);
    }
    if (is_crypto) {
        number = parseFloat(+number);
    }

    return number.toString().replace(/(^|[^\w.])(\d{4,})/g, ($0, $1, $2) => (
        $1 + $2.replace(/\d(?=(?:\d\d\d)+(?!\d))/g, '$&,')
    ));
};

const calcDecimalPlaces = (currency) => isCryptocurrency(currency) ? 8 : 2;

const getDecimalPlaces = (currency) => (
    // need to check currencies_config[currency] exists instead of || in case of 0 value
    currencies_config[currency] ? getPropertyValue(currencies_config, [currency, 'fractional_digits']) : calcDecimalPlaces(currency)
);

const setCurrencies = (website_status) => {
    currencies_config = website_status.currencies_config;
};

// (currency in crypto_config) is a back-up in case website_status doesn't include the currency config, in some cases where it's disabled
const isCryptocurrency = currency => /crypto/i.test(getPropertyValue(currencies_config, [currency, 'type'])) || (currency in CryptoConfig.get());

const CryptoConfig = (() => {
    let crypto_config;

    const initCryptoConfig = () => ({
        BTC: { name: localize('Bitcoin'),       min_withdrawal: 0.002, pa_max_withdrawal: 5,    pa_min_withdrawal: 0.002 },
        BCH: { name: localize('Bitcoin Cash'),  min_withdrawal: 0.002, pa_max_withdrawal: 5,    pa_min_withdrawal: 0.002 },
        ETH: { name: localize('Ether'),         min_withdrawal: 0.002, pa_max_withdrawal: 5,    pa_min_withdrawal: 0.002 },
        ETC: { name: localize('Ether Classic'), min_withdrawal: 0.002, pa_max_withdrawal: 5,    pa_min_withdrawal: 0.002 },
        LTC: { name: localize('Litecoin'),      min_withdrawal: 0.002, pa_max_withdrawal: 5,    pa_min_withdrawal: 0.002 },
        UST: { name: localize('Tether'),        min_withdrawal: 0.02,  pa_max_withdrawal: 2000, pa_min_withdrawal: 10 },
    });

    return {
        get: () => {
            if (!crypto_config) {
                crypto_config = initCryptoConfig();
            }
            return crypto_config;
        },
    };
})();

const getMinWithdrawal = currency => (isCryptocurrency(currency) ? (getPropertyValue(CryptoConfig.get(), [currency, 'min_withdrawal']) || 0.002) : 1);

/**
 * Returns the transfer limits for the account.
 * @param currency
 * @param which
 * @returns numeric - all -> {min: numeric, ?max: numeric}
 */
const getTransferLimits = (currency, which) => {
    const transfer_limits = getPropertyValue(currencies_config, [currency, 'transfer_between_accounts', 'limits']) || getMinWithdrawal(currency);
    const decimals     = getDecimalPlaces(currency);
    if (!(transfer_limits instanceof Object)) {
        return transfer_limits;
    }

    switch (which) {
        case 'max':
            return transfer_limits.max ? transfer_limits.max.toFixed(decimals) : undefined;
        case 'all':
            return transfer_limits;
        default:
            return transfer_limits.min ? transfer_limits.min.toFixed(decimals) : undefined;
    }
};

const getTransferFee = (currency_from, currency_to) => {
    const transfer_fee = getPropertyValue(currencies_config, [currency_from, 'transfer_between_accounts', 'fees', currency_to]);
    return `${typeof transfer_fee === 'undefined' ? '1' : transfer_fee}%`;
};

// returns in a string format, e.g. '0.00000001'
const getMinimumTransferFee = (currency) => {
    const decimals = getDecimalPlaces(currency);
    return `${currency} ${(1 / Math.pow(10, decimals)).toFixed(decimals)}`; // we need toFixed() so that it doesn't display in scientific notation, e.g. 1e-8 for currencies with 8 decimal places
};

// @param {String} limit = max|min
const getPaWithdrawalLimit = (currency, limit) => {
    if (isCryptocurrency(currency)) {
        return getPropertyValue(CryptoConfig.get(), [currency, `pa_${limit}_withdrawal`]); // pa_min_withdrawal and pa_max_withdrawal used here
    }
    return limit === 'max' ? 2000 : 10; // limits for fiat currency
};

const getCurrencyName = currency => getPropertyValue(CryptoConfig.get(), [currency, 'name']) || '';

const getMinPayout = currency => getPropertyValue(currencies_config, [currency, 'stake_default']);

module.exports = {
    formatMoney,
    formatCurrency,
    addComma,
    getDecimalPlaces,
    setCurrencies,
    isCryptocurrency,
    getCurrencyName,
    getMinWithdrawal,
    getTransferLimits,
    getTransferFee,
    getMinimumTransferFee,
    getMinPayout,
    getPaWithdrawalLimit,
    getCurrencies: () => currencies_config,
};
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import charts from 'utils/charts';
import SVGIcon from 'utils/svgIcon';
import { urls } from 'js/config';

export default class VegaChart extends Component {
  constructor(props) {
    super(props);

    this.state = {
      isError: false,
      errorMsg: null,
      showDownloadOptions: false,
      downloadOptions: [],
      chartDownloadTitle: 'analysis.png',
      chartImgDownloadUrl: null,
    };
  }

  handleError(errorMsg) {
    this.setState({ isError: true, errorMsg });
    if (this.props.setLoading) {
      this.props.setLoading();
    }
  }

  componentDidMount() {
    if (this.props.results.hasOwnProperty('error')) {
      this.handleError();
    } else {
      const config = this.props.results.data.attributes.widgetConfig;
      const {setLoading, language} = this.props;
      if (config.data[0].url.indexOf('?&') > -1){
        const urlPieces = config.data[0].url.split('?&');
        config.data[0].url = urlPieces[0] + '?' + urlPieces[1];
      }
      fetch(config.data[0].url).then(res => {
        if (res.status !== 200) {
          this.handleError('Error creating analysis.');
        } else {
          res.json().then(json => {
                if (json.data.attributes.value.length > 400){
                  return this.handleError('Geometry is too large for custom stats/download. Please try again with a smaller area of interest.');
                }
                console.log('json', json);
                charts.makeVegaChart(this.chart, config, language, setLoading, this.addChartDownload);
                const downloadOptions = [];
                const downloadUrls = json.data.attributes.downloadUrls;
                if (downloadUrls && !config.title) {
                  const label = 'csv';
                  downloadOptions.push({label, url: downloadUrls[label]});
                }
                const chartDownloadTitle = json.data.type + '-analysis.png';
                this.setState({ downloadOptions, chartDownloadTitle });
              });
          }
        })
        .catch(() => this.handleError('Error creating analysis.'));
      }
  }

  addChartDownload = (url) => {
    this.setState({ chartImgDownloadUrl: url });
  };

  renderdownloadOptions = (option, i) => {
    const baseUrl = urls.analysisDataBaseUrl;
    return (
      <a href={baseUrl + option.url} download key={`option-${i}`}>
        Download data as {option.label}

    );
  };

  render() {
    const { isError, errorMsg, showDownloadOptions, downloadOptions, chartDownloadTitle, chartImgDownloadUrl } = this.state;
    const { results } = this.props;

    if (isError) {
      return (

{results.message || errorMsg}

      );
    } else {
      return (

          { showDownloadOptions &&

 this.setState({ showDownloadOptions: false })}>
 }

 this.setState({showDownloadOptions: !showDownloadOptions})}><SVGIcon id={'icon-menu'} />

            { showDownloadOptions &&

 this.setState({showDownloadOptions: !showDownloadOptions})}>
                {downloadOptions.map(this.renderdownloadOptions)}
                {this.chart &&

                    Download data as PNG

                }

 }

 { this.chart = chart; }}>

      );
    }
  }
}
<commit_msg>Remove previous error handling and replace with opening csv in new tab<commit_after>import React, { Component } from 'react';
import charts from 'utils/charts';
import SVGIcon from 'utils/svgIcon';
import { urls } from 'js/config';

export default class VegaChart extends Component {
  constructor(props) {
    super(props);

    this.state = {
      isError: false,
      errorMsg: null,
      showDownloadOptions: false,
      downloadOptions: [],
      chartDownloadTitle: 'analysis.png',
      chartImgDownloadUrl: null,
    };
  }

  handleError(errorMsg) {
    this.setState({ isError: true, errorMsg });
    if (this.props.setLoading) {
      this.props.setLoading();
    }
  }

  componentDidMount() {
    if (this.props.results.hasOwnProperty('error')) {
      this.handleError();
    } else {
      const config = this.props.results.data.attributes.widgetConfig;
      const {setLoading, language} = this.props;
      if (config.data[0].url.indexOf('?&') > -1){
        const urlPieces = config.data[0].url.split('?&');
        config.data[0].url = urlPieces[0] + '?' + urlPieces[1];
      }
      fetch(config.data[0].url).then(res => {
        if (res.status !== 200) {
          this.handleError('Error creating analysis.');
        } else {
          res.json().then(json => {
                charts.makeVegaChart(this.chart, config, language, setLoading, this.addChartDownload);
                const downloadOptions = [];
                const downloadUrls = json.data.attributes.downloadUrls;
                if (downloadUrls && !config.title) {
                  const label = 'csv';
                  downloadOptions.push({label, url: downloadUrls[label]});
                }
                const chartDownloadTitle = json.data.type + '-analysis.png';
                this.setState({ downloadOptions, chartDownloadTitle });
              });
          }
        })
        .catch(() => this.handleError('Error creating analysis.'));
      }
  }

  addChartDownload = (url) => {
    this.setState({ chartImgDownloadUrl: url });
  };

  renderdownloadOptions = (option, i) => {
    const baseUrl = urls.analysisDataBaseUrl;
    return (
      <a href={baseUrl + option.url} target="_blank" download key={`option-${i}`}>
        Download data as {option.label}

    );
  };

  render() {
    const { isError, errorMsg, showDownloadOptions, downloadOptions, chartDownloadTitle, chartImgDownloadUrl } = this.state;
    const { results } = this.props;

    if (isError) {
      return (

{results.message || errorMsg}

      );
    } else {
      return (

          { showDownloadOptions &&

 this.setState({ showDownloadOptions: false })}>
 }

 this.setState({showDownloadOptions: !showDownloadOptions})}><SVGIcon id={'icon-menu'} />

            { showDownloadOptions &&

 this.setState({showDownloadOptions: !showDownloadOptions})}>
                {downloadOptions.map(this.renderdownloadOptions)}
                {this.chart &&

                    Download data as PNG

                }

 }

 { this.chart = chart; }}>

      );
    }
  }
}
<|endoftext|>


<commit_before>var should   = require('should/as-function');
var Album    = require('../../src/model/album');
var fixtures = require('../fixtures');

describe('Album', function() {

  it('sanitises album titles for the file name', function() {
    var a = new Album('hello & world');
    should(a.filename).eql('helloworld');
  });

  describe('stats', function() {

    describe('single level', function() {

      it('has no nested albums', function() {
        var a = new Album('single');
        a.finalize();
        should(a.stats.albums).eql(0);
      })

      it('calculates counts for a single level', function() {
        var a = new Album('single');
        a.files = [
          fixtures.photo(), fixtures.photo(),
          fixtures.photo(), fixtures.photo(),
          fixtures.video(), fixtures.video(),
        ];
        a.finalize();
        should(a.stats.photos).eql(4);
        should(a.stats.videos).eql(2);
      });

      it('calculates dates', function() {
        var a = new Album('single');
        a.files = [
          fixtures.photo({date: '2016-09-14'}),
          fixtures.photo({date: '2016-09-02'}),
          fixtures.photo({date: '2016-10-21'}),
        ];
        a.finalize();
        should(a.stats.fromDate).eql(fixtures.date('2016-09-02'));
        should(a.stats.toDate).eql(fixtures.date('2016-10-21'));
      });

    });

    describe('nested albums', function() {

      it('calculates the depth of every album', function() {
        var a = new Album('single');
        var b = new Album('single');
        var c = new Album('single');
        var d = new Album('single');
        a.albums = [b, c];
        c.albums = [d];
        a.finalize();
        should(a.depth).eql(0);
        should(b.depth).eql(1);
        should(c.depth).eql(1);
        should(d.depth).eql(2);
      });

      it('sets the home flag on the top-level album', function() {
        var a = new Album('single');
        var b = new Album('single');
        var c = new Album('single');
        var d = new Album('single');
        a.albums = [b, c];
        c.albums = [d];
        a.finalize();
        should(a.home).eql(true);
        should(b.home).eql(false);
        should(c.home).eql(false);
        should(d.home).eql(false);
      });

    });

    describe('summary', function() {

      it('creates a summary with a single photos', function() {
        var a = new Album('single');
        a.files = [
          fixtures.photo()
        ];
        a.finalize();
        should(a.summary).eql('1 photo')
      });

      it('creates a summary with a single video', function() {
        var a = new Album('single');
        a.files = [
          fixtures.video()
        ];
        a.finalize();
        should(a.summary).eql('1 video')
      });

      it('creates a summary with several photos', function() {
        var a = new Album('single');
        a.files = [
          fixtures.photo(), fixtures.photo(),
        ];
        a.finalize();
        should(a.summary).eql('2 photos')
      });

      it('creates a summary with several videos', function() {
        var a = new Album('single');
        a.files = [
          fixtures.video(), fixtures.video(),
        ];
        a.finalize();
        should(a.summary).eql('2 videos')
      });

      it('creates a summary with several photos and videos', function() {
        var a = new Album('single');
        a.files = [
          fixtures.photo(), fixtures.photo(),
          fixtures.video(), fixtures.video(),
        ];
        a.finalize();
        should(a.summary).eql('2 photos, 2 videos')
      });

    });

    describe('previews', function() {

      it('adds <missing> thumbnails to fill the 2x2 grid', function() {
        var a = new Album({files: [
          fixtures.photo(), fixtures.photo(),
        ]});
        a.finalize();
        should(a.previews).have.length(4);
        should(a.previews[2].urls.thumb).eql('public/missing.png');
        should(a.previews[3].urls.thumb).eql('public/missing.png');
      });

      it('uses files from nested albums too', function() {
        var a = new Album({
          title: 'a',
          albums: [
            new Album({
              title: 'b',
              files: [fixtures.photo(), fixtures.photo()]
            }),
            new Album({
              title: 'c',
              files: [fixtures.photo(), fixtures.photo()]
            })
          ]
        });
        a.finalize();
        should(a.previews).have.length(4);
        for (var i = 0; i < 4; ++i) {
          should(a.previews[i].urls.thumb).not.eql('public/missing.png');
        }
      });

    });

  });

});
<commit_msg>Move album.previews() test one level up<commit_after>var should   = require('should/as-function');
var Album    = require('../../src/model/album');
var fixtures = require('../fixtures');

describe('Album', function() {

  it('sanitises album titles for the file name', function() {
    var a = new Album('hello & world');
    should(a.filename).eql('helloworld');
  });

  describe('stats', function() {

    describe('single level', function() {

      it('has no nested albums', function() {
        var a = new Album('single');
        a.finalize();
        should(a.stats.albums).eql(0);
      })

      it('calculates counts for a single level', function() {
        var a = new Album('single');
        a.files = [
          fixtures.photo(), fixtures.photo(),
          fixtures.photo(), fixtures.photo(),
          fixtures.video(), fixtures.video(),
        ];
        a.finalize();
        should(a.stats.photos).eql(4);
        should(a.stats.videos).eql(2);
      });

      it('calculates dates', function() {
        var a = new Album('single');
        a.files = [
          fixtures.photo({date: '2016-09-14'}),
          fixtures.photo({date: '2016-09-02'}),
          fixtures.photo({date: '2016-10-21'}),
        ];
        a.finalize();
        should(a.stats.fromDate).eql(fixtures.date('2016-09-02'));
        should(a.stats.toDate).eql(fixtures.date('2016-10-21'));
      });

    });

    describe('nested albums', function() {

      it('calculates the depth of every album', function() {
        var a = new Album('single');
        var b = new Album('single');
        var c = new Album('single');
        var d = new Album('single');
        a.albums = [b, c];
        c.albums = [d];
        a.finalize();
        should(a.depth).eql(0);
        should(b.depth).eql(1);
        should(c.depth).eql(1);
        should(d.depth).eql(2);
      });

      it('sets the home flag on the top-level album', function() {
        var a = new Album('single');
        var b = new Album('single');
        var c = new Album('single');
        var d = new Album('single');
        a.albums = [b, c];
        c.albums = [d];
        a.finalize();
        should(a.home).eql(true);
        should(b.home).eql(false);
        should(c.home).eql(false);
        should(d.home).eql(false);
      });

    });

    describe('summary', function() {

      it('creates a summary with a single photos', function() {
        var a = new Album('single');
        a.files = [
          fixtures.photo()
        ];
        a.finalize();
        should(a.summary).eql('1 photo')
      });

      it('creates a summary with a single video', function() {
        var a = new Album('single');
        a.files = [
          fixtures.video()
        ];
        a.finalize();
        should(a.summary).eql('1 video')
      });

      it('creates a summary with several photos', function() {
        var a = new Album('single');
        a.files = [
          fixtures.photo(), fixtures.photo(),
        ];
        a.finalize();
        should(a.summary).eql('2 photos')
      });

      it('creates a summary with several videos', function() {
        var a = new Album('single');
        a.files = [
          fixtures.video(), fixtures.video(),
        ];
        a.finalize();
        should(a.summary).eql('2 videos')
      });

      it('creates a summary with several photos and videos', function() {
        var a = new Album('single');
        a.files = [
          fixtures.photo(), fixtures.photo(),
          fixtures.video(), fixtures.video(),
        ];
        a.finalize();
        should(a.summary).eql('2 photos, 2 videos')
      });

    });

  });

  describe('previews', function() {

    it('adds <missing> thumbnails to fill the 2x2 grid', function() {
      var a = new Album({files: [
        fixtures.photo(), fixtures.photo(),
      ]});
      a.finalize();
      should(a.previews).have.length(4);
      should(a.previews[2].urls.thumb).eql('public/missing.png');
      should(a.previews[3].urls.thumb).eql('public/missing.png');
    });

    it('uses files from nested albums too', function() {
      var a = new Album({
        title: 'a',
        albums: [
          new Album({
            title: 'b',
            files: [fixtures.photo(), fixtures.photo()]
          }),
          new Album({
            title: 'c',
            files: [fixtures.photo(), fixtures.photo()]
          })
        ]
      });
      a.finalize();
      should(a.previews).have.length(4);
      for (var i = 0; i < 4; ++i) {
        should(a.previews[i].urls.thumb).not.eql('public/missing.png');
      }
    });

  });

});
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { ProgressBar } from 'react-bootstrap';

import ButtonShuffle from './ButtonShuffle.react';
import ButtonRepeat  from './ButtonRepeat.react';

import app   from '../../constants/app';
import utils from '../../utils/utils';

/*
|--------------------------------------------------------------------------
| Header - PlayingBar
|--------------------------------------------------------------------------
*/

export default class PlayingBar extends Component {

    constructor(props) {

        super(props);
        this.state = {
            elapsed: 0
        }
        this.tick = this.tick.bind(this);
    }

    render() {

        var playlist       = this.props.playlist;
        var playlistCursor = this.props.playlistCursor;
        var trackPlaying   = playlist[playlistCursor];
        var playingBar;

        if(playlistCursor === null) {
            playingBar =

;
        } else {

            if(this.state.elapsed < trackPlaying.duration && app.audio.paused === false) {
                var elapsedPercent = this.state.elapsed * 100 / trackPlaying.duration;
            }

            playingBar = (

                            <ButtonRepeat repeat={ this.props.repeat } />
                            <ButtonShuffle playlist={ this.props.playlist } shuffle={ this.props.shuffle } />

                                { trackPlaying.title }

                             by

                                { trackPlaying.artist.join(', ') }

                             on

                                { trackPlaying.album }

                            { utils.parseDuration(this.state.elapsed) } / { utils.parseDuration(trackPlaying.duration) }

                        <ProgressBar now={ elapsedPercent } onMouseDown={ this.jumpAudioTo.bind(this) } />

            );
        }

        return playingBar;
    }

    componentDidMount() {
        this.timer = setInterval(this.tick, 350);
    }

    componentWillUnmount() {
        clearInterval(this.timer);
    }

    tick() {
        this.setState({ elapsed: app.audio.currentTime });
    }

    jumpAudioTo(e) {

        var playlist       = this.props.playlist;
        var playlistCursor = this.props.playlistCursor;
        var trackPlaying   = playlist[playlistCursor];

        var bar = document.querySelector('.now-playing-bar');
        var percent = ((e.pageX - (bar.offsetLeft + bar.offsetParent.offsetLeft)) / bar.offsetWidth) * 100;

        var jumpTo = (percent * trackPlaying.duration) / 100;

        app.audio.currentTime = jumpTo;
    }
}
<commit_msg>Fix click on PlayingBar when paused<commit_after>import React, { Component } from 'react';
import { ProgressBar } from 'react-bootstrap';

import ButtonShuffle from './ButtonShuffle.react';
import ButtonRepeat  from './ButtonRepeat.react';

import app   from '../../constants/app';
import utils from '../../utils/utils';

/*
|--------------------------------------------------------------------------
| Header - PlayingBar
|--------------------------------------------------------------------------
*/

export default class PlayingBar extends Component {

    constructor(props) {

        super(props);
        this.state = {
            elapsed: 0
        }
        this.tick = this.tick.bind(this);
    }

    render() {

        var playlist       = this.props.playlist;
        var playlistCursor = this.props.playlistCursor;
        var trackPlaying   = playlist[playlistCursor];
        var playingBar;

        if(playlistCursor === null) {
            playingBar =

;
        } else {

            if(this.state.elapsed < trackPlaying.duration) {
                var elapsedPercent = this.state.elapsed * 100 / trackPlaying.duration;
            }

            playingBar = (

                            <ButtonRepeat repeat={ this.props.repeat } />
                            <ButtonShuffle playlist={ this.props.playlist } shuffle={ this.props.shuffle } />

                                { trackPlaying.title }

                             by

                                { trackPlaying.artist.join(', ') }

                             on

                                { trackPlaying.album }

                            { utils.parseDuration(this.state.elapsed) } / { utils.parseDuration(trackPlaying.duration) }

                        <ProgressBar now={ elapsedPercent } onMouseDown={ this.jumpAudioTo.bind(this) } />

            );
        }

        return playingBar;
    }

    componentDidMount() {
        this.timer = setInterval(this.tick, 350);
    }

    componentWillUnmount() {
        clearInterval(this.timer);
    }

    tick() {
        this.setState({ elapsed: app.audio.currentTime });
    }

    jumpAudioTo(e) {

        var playlist       = this.props.playlist;
        var playlistCursor = this.props.playlistCursor;
        var trackPlaying   = playlist[playlistCursor];

        var bar = document.querySelector('.now-playing-bar');
        var percent = ((e.pageX - (bar.offsetLeft + bar.offsetParent.offsetLeft)) / bar.offsetWidth) * 100;

        var jumpTo = (percent * trackPlaying.duration) / 100;

        app.audio.currentTime = jumpTo;
    }
}
<|endoftext|>


<commit_before>var chai = require('chai')
  , expect = chai.expect
  , sinon = require('sinon')
  , sinonChai = require('sinon-chai')
  , utils = require('../utils.js')
  ;
chai.use(sinonChai);

describe('Page', function () {
  var Page = utils.models.Page,
    User   = utils.models.User,
    conn   = utils.mongoose.connection,
    createdPages,
    createdUsers;

  before(function (done) {
    Promise.resolve().then(function() {
      var userFixture = [
        {name: 'Anon 0', username: 'anonymous0', email: 'anonymous0@example.com'},
        {name: 'Anon 1', username: 'anonymous1', email: 'anonymous1@example.com'}
      ];

      return testDBUtil.generateFixture(conn, 'User', userFixture);
    }).then(function(testUsers) {
      createdUsers = testUsers;
      var testUser0 = testUsers[0];

      var fixture = [
        {
          path: '/user/anonymous/memo',
          grant: Page.GRANT_RESTRICTED,
          grantedUsers: [testUser0],
          creator: testUser0
        },
        {
          path: '/grant/public',
          grant: Page.GRANT_PUBLIC,
          grantedUsers: [testUser0],
          creator: testUser0
        },
        {
          path: '/grant/restricted',
          grant: Page.GRANT_RESTRICTED,
          grantedUsers: [testUser0],
          creator: testUser0
        },
        {
          path: '/grant/specified',
          grant: Page.GRANT_SPECIFIED,
          grantedUsers: [testUser0],
          creator: testUser0
        },
        {
          path: '/grant/owner',
          grant: Page.GRANT_OWNER,
          grantedUsers: [testUser0],
          creator: testUser0,
        },
        {
          path: '/page/for/extended',
          grant: Page.GRANT_PUBLIC,
          creator: testUser0,
          extended: {hoge: 1}
        },
      ];

      return testDBUtil.generateFixture(conn, 'Page', fixture)
      .then(function(pages) {
        createdPages = pages;
        done();
      });
    });
  });

  describe('.isPublic', function () {
    context('with a public page', function() {
      it('should return true', function(done) {
        Page.findOne({path: '/grant/public'}, function(err, page) {
          expect(err).to.be.null;
          expect(page.isPublic()).to.be.equal(true);
          done();
        });
      });
    });

    ['restricted', 'specified', 'owner'].forEach(function(grant) {
      context('with a ' + grant + ' page', function() {
        it('should return false', function(done) {
          Page.findOne({path: '/grant/' + grant}, function(err, page) {
            expect(err).to.be.null;
            expect(page.isPublic()).to.be.equal(false);
            done();
          });
        });
      });
    });
  });

  describe('.getDeletedPageName', function() {
    it('should return trash page name', function() {
      expect(Page.getDeletedPageName('/hoge')).to.be.equal('/trash/hoge');
      expect(Page.getDeletedPageName('hoge')).to.be.equal('/trash/hoge');
    });
  });
  describe('.getRevertDeletedPageName', function() {
    it('should return reverted trash page name', function() {
      expect(Page.getRevertDeletedPageName('/hoge')).to.be.equal('/hoge');
      expect(Page.getRevertDeletedPageName('/trash/hoge')).to.be.equal('/hoge');
      expect(Page.getRevertDeletedPageName('/trash/hoge/trash')).to.be.equal('/hoge/trash');
    });
  });

  describe('.isDeletableName', function() {
    it('should decide deletable or not', function() {
      expect(Page.isDeletableName('/hoge')).to.be.true;
      expect(Page.isDeletableName('/user/xxx')).to.be.false;
      expect(Page.isDeletableName('/user/xxx123')).to.be.false;
      expect(Page.isDeletableName('/user/xxx/')).to.be.true;
      expect(Page.isDeletableName('/user/xxx/hoge')).to.be.true;
    });
  });

  describe('.isCreatableName', function() {
    it('should decide creatable or not', function() {
      expect(Page.isCreatableName('/hoge')).to.be.true;

      // edge cases
      expect(Page.isCreatableName('/me')).to.be.false;
      expect(Page.isCreatableName('/me/')).to.be.false;
      expect(Page.isCreatableName('/me/x')).to.be.false;
      expect(Page.isCreatableName('/meeting')).to.be.true;
      expect(Page.isCreatableName('/meeting/x')).to.be.true;

      // end with "edit"
      expect(Page.isCreatableName('/meeting/edit')).to.be.false;

      // under score
      expect(Page.isCreatableName('/_')).to.be.false;
      expect(Page.isCreatableName('/_r/x')).to.be.false;
      expect(Page.isCreatableName('/_api')).to.be.false;
      expect(Page.isCreatableName('/_apix')).to.be.false;
      expect(Page.isCreatableName('/_api/x')).to.be.false;

      expect(Page.isCreatableName('/hoge/xx.md')).to.be.false;

      // start with https?
      expect(Page.isCreatableName('/http://demo.crowi.wiki/user/sotarok/hoge')).to.be.false;
      expect(Page.isCreatableName('/https://demo.crowi.wiki/user/sotarok/hoge')).to.be.false;
      expect(Page.isCreatableName('http://demo.crowi.wiki/user/sotarok/hoge')).to.be.false;
      expect(Page.isCreatableName('https://demo.crowi.wiki/user/sotarok/hoge')).to.be.false;

      expect(Page.isCreatableName('/ the / path / with / space')).to.be.false;

      var forbidden = ['installer', 'register', 'login', 'logout', 'admin', 'files', 'trash', 'paste', 'comments'];
      for (var i = 0; i < forbidden.length ; i++) {
        var pn = forbidden[i];
        expect(Page.isCreatableName('/' + pn + '')).to.be.false;
        expect(Page.isCreatableName('/' + pn + '/')).to.be.false;
        expect(Page.isCreatableName('/' + pn + '/abc')).to.be.false;
      }

      var forbidden = ['bookmarks', 'comments', 'activities', 'pages', 'recent-create', 'recent-edit'];
      for (var i = 0; i < forbidden.length ; i++) {
        var pn = forbidden[i];
        expect(Page.isCreatableName('/user/aoi/' + pn)).to.be.false;
        expect(Page.isCreatableName('/user/aoi/x/' + pn)).to.be.true;
      }
    });
  });

  describe('.isCreator', function() {
    context('with creator', function() {
      it('should return true', function(done) {
        User.findOne({email: 'anonymous0@example.com'}, function(err, user) {
          if (err) { done(err); }

          Page.findOne({path: '/user/anonymous/memo'}, function(err, page) {
            expect(page.isCreator(user)).to.be.equal(true);
            done();
          })
        });
      });
    });

    context('with non-creator', function() {
      it('should return false', function(done) {
        User.findOne({email: 'anonymous1@example.com'}, function(err, user) {
          if (err) { done(err); }

          Page.findOne({path: '/user/anonymous/memo'}, function(err, page) {
            expect(page.isCreator(user)).to.be.equal(false);
            done();
          })
        });
      });
    });
  });

  describe('.isGrantedFor', function() {
    context('with a granted user', function() {
      it('should return true', function(done) {
        User.findOne({email: 'anonymous0@example.com'}, function(err, user) {
          if (err) { done(err); }

          Page.findOne({path: '/user/anonymous/memo'}, function(err, page) {
            if (err) { done(err); }

            expect(page.isGrantedFor(user)).to.be.equal(true);
            done();
          });
        });
      });
    });

    context('with a public page', function() {
      it('should return true', function(done) {
        User.findOne({email: 'anonymous1@example.com'}, function(err, user) {
          if (err) { done(err); }

          Page.findOne({path: '/grant/public'}, function(err, page) {
            if (err) { done(err); }

            expect(page.isGrantedFor(user)).to.be.equal(true);
            done();
          });
        });
      });
    });

    context('with a restricted page and an user who has no grant', function() {
      it('should return false', function(done) {
        User.findOne({email: 'anonymous1@example.com'}, function(err, user) {
          if (err) { done(err); }

          Page.findOne({path: '/grant/restricted'}, function(err, page) {
            if (err) { done(err); }

            expect(page.isGrantedFor(user)).to.be.equal(false);
            done();
          });
        });
      });
    });
  });

  describe('Extended field', function () {
    context('Slack Channel.', function() {
      it('should be empty', function(done) {
        Page.findOne({path: '/page/for/extended'}, function(err, page) {
          expect(page.extended.hoge).to.be.equal(1);
          expect(page.getSlackChannel()).to.be.equal('');
          done();
        })
      });

      it('set slack channel and should get it and should keep hoge ', function(done) {
        Page.findOne({path: '/page/for/extended'}, function(err, page) {
          page.updateSlackChannel('slack-channel1')
          .then(function(data) {
            Page.findOne({path: '/page/for/extended'}, function(err, page) {
              expect(page.extended.hoge).to.be.equal(1);
              expect(page.getSlackChannel()).to.be.equal('slack-channel1');
              done();
            });
          })
        });
      });

    });
  });

  describe('Normalize path', function () {
    context('Normalize', function() {
      it('should start with slash', function(done) {
        expect(Page.normalizePath('hoge/fuga')).to.equal('/hoge/fuga');
        done();
      });

      it('should trim spaces of slash', function(done) {
        expect(Page.normalizePath('/ hoge / fuga')).to.equal('/hoge/fuga');
        done();
      });
    });
  });

  describe('.findPage', function () {
    context('findPageById', () => {
      it('should find page', function(done) {
        const pageToFind = createdPages[0];
        Page.findPageById(pageToFind._id)
        .then(pageData => {
          expect(pageData.path).to.equal(pageToFind.path);
          done();
        });
      });
    });

    context('findPageByIdAndGrantedUser', function() {
      it('should find page', function(done) {
        const pageToFind = createdPages[0];
        const grantedUser = createdUsers[0];
        Page.findPageByIdAndGrantedUser(pageToFind._id, grantedUser)
        .then(pageData => {
          expect(pageData.path).to.equal(pageToFind.path);
          done();
        });
      });

      it('should error by grant', done => {
        const pageToFind = createdPages[0];
        const grantedUser = createdUsers[1];
        Page.findPageByIdAndGrantedUser(pageToFind._id, grantedUser)
        .then(pageData => {
        }).catch(err => {
          expect(err).to.instanceof(Error);
          done();
        });
      });
    });
  });

});
<commit_msg>Replace to arrow function<commit_after>var chai = require('chai')
  , expect = chai.expect
  , sinon = require('sinon')
  , sinonChai = require('sinon-chai')
  , utils = require('../utils.js')
  ;
chai.use(sinonChai);

describe('Page', () => {
  var Page = utils.models.Page,
    User   = utils.models.User,
    conn   = utils.mongoose.connection,
    createdPages,
    createdUsers;

  before(done => {
    Promise.resolve().then(() => {
      var userFixture = [
        {name: 'Anon 0', username: 'anonymous0', email: 'anonymous0@example.com'},
        {name: 'Anon 1', username: 'anonymous1', email: 'anonymous1@example.com'}
      ];

      return testDBUtil.generateFixture(conn, 'User', userFixture);
    }).then(testUsers => {
      createdUsers = testUsers;
      var testUser0 = testUsers[0];

      var fixture = [
        {
          path: '/user/anonymous/memo',
          grant: Page.GRANT_RESTRICTED,
          grantedUsers: [testUser0],
          creator: testUser0
        },
        {
          path: '/grant/public',
          grant: Page.GRANT_PUBLIC,
          grantedUsers: [testUser0],
          creator: testUser0
        },
        {
          path: '/grant/restricted',
          grant: Page.GRANT_RESTRICTED,
          grantedUsers: [testUser0],
          creator: testUser0
        },
        {
          path: '/grant/specified',
          grant: Page.GRANT_SPECIFIED,
          grantedUsers: [testUser0],
          creator: testUser0
        },
        {
          path: '/grant/owner',
          grant: Page.GRANT_OWNER,
          grantedUsers: [testUser0],
          creator: testUser0,
        },
        {
          path: '/page/for/extended',
          grant: Page.GRANT_PUBLIC,
          creator: testUser0,
          extended: {hoge: 1}
        },
      ];

      return testDBUtil.generateFixture(conn, 'Page', fixture)
      .then(pages => {
        createdPages = pages;
        done();
      });
    });
  });

  describe('.isPublic', () => {
    context('with a public page', () => {
      it('should return true', done => {
        Page.findOne({path: '/grant/public'}, (err, page) => {
          expect(err).to.be.null;
          expect(page.isPublic()).to.be.equal(true);
          done();
        });
      });
    });

    ['restricted', 'specified', 'owner'].forEach(grant => {
      context('with a ' + grant + ' page', function() {
        it('should return false', done => {
          Page.findOne({path: '/grant/' + grant}, (err, page) => {
            expect(err).to.be.null;
            expect(page.isPublic()).to.be.equal(false);
            done();
          });
        });
      });
    });
  });

  describe('.getDeletedPageName', function() {
    it('should return trash page name', function() {
      expect(Page.getDeletedPageName('/hoge')).to.be.equal('/trash/hoge');
      expect(Page.getDeletedPageName('hoge')).to.be.equal('/trash/hoge');
    });
  });
  describe('.getRevertDeletedPageName', function() {
    it('should return reverted trash page name', function() {
      expect(Page.getRevertDeletedPageName('/hoge')).to.be.equal('/hoge');
      expect(Page.getRevertDeletedPageName('/trash/hoge')).to.be.equal('/hoge');
      expect(Page.getRevertDeletedPageName('/trash/hoge/trash')).to.be.equal('/hoge/trash');
    });
  });

  describe('.isDeletableName', function() {
    it('should decide deletable or not', function() {
      expect(Page.isDeletableName('/hoge')).to.be.true;
      expect(Page.isDeletableName('/user/xxx')).to.be.false;
      expect(Page.isDeletableName('/user/xxx123')).to.be.false;
      expect(Page.isDeletableName('/user/xxx/')).to.be.true;
      expect(Page.isDeletableName('/user/xxx/hoge')).to.be.true;
    });
  });

  describe('.isCreatableName', function() {
    it('should decide creatable or not', function() {
      expect(Page.isCreatableName('/hoge')).to.be.true;

      // edge cases
      expect(Page.isCreatableName('/me')).to.be.false;
      expect(Page.isCreatableName('/me/')).to.be.false;
      expect(Page.isCreatableName('/me/x')).to.be.false;
      expect(Page.isCreatableName('/meeting')).to.be.true;
      expect(Page.isCreatableName('/meeting/x')).to.be.true;

      // end with "edit"
      expect(Page.isCreatableName('/meeting/edit')).to.be.false;

      // under score
      expect(Page.isCreatableName('/_')).to.be.false;
      expect(Page.isCreatableName('/_r/x')).to.be.false;
      expect(Page.isCreatableName('/_api')).to.be.false;
      expect(Page.isCreatableName('/_apix')).to.be.false;
      expect(Page.isCreatableName('/_api/x')).to.be.false;

      expect(Page.isCreatableName('/hoge/xx.md')).to.be.false;

      // start with https?
      expect(Page.isCreatableName('/http://demo.crowi.wiki/user/sotarok/hoge')).to.be.false;
      expect(Page.isCreatableName('/https://demo.crowi.wiki/user/sotarok/hoge')).to.be.false;
      expect(Page.isCreatableName('http://demo.crowi.wiki/user/sotarok/hoge')).to.be.false;
      expect(Page.isCreatableName('https://demo.crowi.wiki/user/sotarok/hoge')).to.be.false;

      expect(Page.isCreatableName('/ the / path / with / space')).to.be.false;

      var forbidden = ['installer', 'register', 'login', 'logout', 'admin', 'files', 'trash', 'paste', 'comments'];
      for (var i = 0; i < forbidden.length ; i++) {
        var pn = forbidden[i];
        expect(Page.isCreatableName('/' + pn + '')).to.be.false;
        expect(Page.isCreatableName('/' + pn + '/')).to.be.false;
        expect(Page.isCreatableName('/' + pn + '/abc')).to.be.false;
      }

      var forbidden = ['bookmarks', 'comments', 'activities', 'pages', 'recent-create', 'recent-edit'];
      for (var i = 0; i < forbidden.length ; i++) {
        var pn = forbidden[i];
        expect(Page.isCreatableName('/user/aoi/' + pn)).to.be.false;
        expect(Page.isCreatableName('/user/aoi/x/' + pn)).to.be.true;
      }
    });
  });

  describe('.isCreator', function() {
    context('with creator', function() {
      it('should return true', done => {
        User.findOne({email: 'anonymous0@example.com'}, (err, user) => {
          if (err) { done(err); }

          Page.findOne({path: '/user/anonymous/memo'}, (err, page) => {
            expect(page.isCreator(user)).to.be.equal(true);
            done();
          })
        });
      });
    });

    context('with non-creator', function() {
      it('should return false', done => {
        User.findOne({email: 'anonymous1@example.com'}, (err, user) => {
          if (err) { done(err); }

          Page.findOne({path: '/user/anonymous/memo'}, (err, page) => {
            expect(page.isCreator(user)).to.be.equal(false);
            done();
          })
        });
      });
    });
  });

  describe('.isGrantedFor', function() {
    context('with a granted user', function() {
      it('should return true', done => {
        User.findOne({email: 'anonymous0@example.com'}, (err, user) => {
          if (err) { done(err); }

          Page.findOne({path: '/user/anonymous/memo'}, (err, page) => {
            if (err) { done(err); }

            expect(page.isGrantedFor(user)).to.be.equal(true);
            done();
          });
        });
      });
    });

    context('with a public page', function() {
      it('should return true', done => {
        User.findOne({email: 'anonymous1@example.com'}, (err, user) => {
          if (err) { done(err); }

          Page.findOne({path: '/grant/public'}, (err, page) => {
            if (err) { done(err); }

            expect(page.isGrantedFor(user)).to.be.equal(true);
            done();
          });
        });
      });
    });

    context('with a restricted page and an user who has no grant', function() {
      it('should return false', done => {
        User.findOne({email: 'anonymous1@example.com'}, (err, user) => {
          if (err) { done(err); }

          Page.findOne({path: '/grant/restricted'}, (err, page) => {
            if (err) { done(err); }

            expect(page.isGrantedFor(user)).to.be.equal(false);
            done();
          });
        });
      });
    });
  });

  describe('Extended field', () => {
    context('Slack Channel.', () => {
      it('should be empty', done => {
        Page.findOne({path: '/page/for/extended'}, (err, page) => {
          expect(page.extended.hoge).to.be.equal(1);
          expect(page.getSlackChannel()).to.be.equal('');
          done();
        })
      });

      it('set slack channel and should get it and should keep hoge ', done => {
        Page.findOne({path: '/page/for/extended'}, (err, page) => {
          page.updateSlackChannel('slack-channel1')
          .then(data => {
            Page.findOne({path: '/page/for/extended'}, (err, page) => {
              expect(page.extended.hoge).to.be.equal(1);
              expect(page.getSlackChannel()).to.be.equal('slack-channel1');
              done();
            });
          })
        });
      });

    });
  });

  describe('Normalize path', () => {
    context('Normalize', function() {
      it('should start with slash', done => {
        expect(Page.normalizePath('hoge/fuga')).to.equal('/hoge/fuga');
        done();
      });

      it('should trim spaces of slash', done => {
        expect(Page.normalizePath('/ hoge / fuga')).to.equal('/hoge/fuga');
        done();
      });
    });
  });

  describe('.findPage', () => {
    context('findPageById', () => {
      it('should find page', done => {
        const pageToFind = createdPages[0];
        Page.findPageById(pageToFind._id)
        .then(pageData => {
          expect(pageData.path).to.equal(pageToFind.path);
          done();
        });
      });
    });

    context('findPageByIdAndGrantedUser', function() {
      it('should find page', done => {
        const pageToFind = createdPages[0];
        const grantedUser = createdUsers[0];
        Page.findPageByIdAndGrantedUser(pageToFind._id, grantedUser)
        .then(pageData => {
          expect(pageData.path).to.equal(pageToFind.path);
          done();
        });
      });

      it('should error by grant', done => {
        const pageToFind = createdPages[0];
        const grantedUser = createdUsers[1];
        Page.findPageByIdAndGrantedUser(pageToFind._id, grantedUser)
        .then(pageData => {
        }).catch(err => {
          expect(err).to.instanceof(Error);
          done();
        });
      });
    });
  });

});
<|endoftext|>