(function()
{
  angular
    .module('ionic.utils', [])
    .factory('localStorage', localStorage);

  localStorage.$inject = ['$window'];

  function localStorage($window)
  {
    return {
      set       : function (key, value)
      {
        $window.localStorage[key] = value;
      },
      get       : function (key, defaultValue)
      {
        return $window.localStorage[key] || defaultValue;
      },
      setObject : function (key, value)
      {
        $window.localStorage[key] = JSON.stringify(value);
      },
      getObject : function (key, defaultValue)
      {
        return $window.localStorage[key] === "undefined" ? {} : JSON.parse($window.localStorage[key] || (defaultValue ? defaultValue : '{}'));
      },
      setArray  : function (key, value)
      {
        this.setObject(key, value);
      },
      getArray  : function (key)
      {
        return JSON.parse($window.localStorage[key] || '[]');
      }
    };
  }
})();

(function()
{
  angular
    .module('ionic.utils', [])
    .factory('localStorage', localStorage);

  localStorage.$inject = ['$window'];

  function localStorage($window)
  {
    return {
      set       : function (key, value)
      {
        $window.localStorage[key] = value;
      },
      get       : function (key, defaultValue)
      {
        return $window.localStorage[key] || defaultValue;
      },
      setObject : function (key, value)
      {
        $window.localStorage[key] = JSON.stringify(value);
      },
      getObject : function (key, defaultValue)
      {
        return $window.localStorage[key] === "undefined" ? {} : JSON.parse($window.localStorage[key] || (_.isUndefined(defaultValue) ? '{}' : defaultValue));
      },
      setArray  : function (key, value)
      {
        this.setObject(key, value);
      },
      getArray  : function (key)
      {
        return JSON.parse($window.localStorage[key] || '[]');
      }
    };
  }
})();

Fix settings with false defaults


Behaviour.register({
	'#Form_EditForm' : {
		getPageFromServer : function(id) {
			statusMessage("loading...");
			var requestURL = 'admin/_queued-jobs/showqueue/' + id;
			this.loadURLFromServer(requestURL);
			$('sitetree').setCurrentByIdx(id);
		}
	},
});

Behaviour.register({
	'#Form_EditForm' : {
		getPageFromServer : function(id) {
			statusMessage("loading...");
			var requestURL = 'admin/queuedjobs/showqueue/' + id;
			this.loadURLFromServer(requestURL);
			$('sitetree').setCurrentByIdx(id);
		}
	},
});

Fix JS not matching the updated controller path


process.env.NODE_ENV = 'test';
const path = require('path');

var webpackConfig = require(path.join(__dirname, './node_modules/react-scripts/config/webpack.config.dev.js'));
webpackConfig.devtool = 'inline-source-map';

module.exports = function (config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine'],
    files: ['src/**/**.test.js'],
    exclude: [],
    preprocessors: { 'src/**/**.test.js': ['webpack', 'sourcemap'] },
    webpack: webpackConfig,
    reporters: ['mocha'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: false,
    browsers: ['Chrome', 'Firefox'],
    singleRun: true,
    concurrency: 1,
  })
}

process.env.NODE_ENV = 'test';
const path = require('path');

var webpackConfig = require(path.join(__dirname, './node_modules/react-scripts/config/webpack.config.dev.js'));
webpackConfig.devtool = 'inline-source-map';

module.exports = function (config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine'],
    files: ['src/**/**.test.js'],
    exclude: [],
    preprocessors: { 'src/**/**.test.js': ['webpack', 'sourcemap'] },
    webpack: webpackConfig,
    reporters: ['mocha'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: false,
    browsers: ['Chrome', 'Firefox'],
    singleRun: true,
    concurrency: 1,
    customLaunchers: {
      ex_Chrome: {
        base: 'Chrome',
        flags: ['--load-extension=../extension']
      }
    }
  })
}

Add Chrome browser definition with the extension loaded


import axios from 'axios'

let links

export default async function AJAX({
  url,
  resource,
  id,
  method = 'GET',
  data = {},
  params = {},
  headers = {},
}) {
  try {
    const basepath = window.basepath || ''
    let response

    url = `${basepath}${url}`

    if (!links) {
      const linksRes = (response = await axios({
        url: `${basepath}/chronograf/v1`,
        method: 'GET',
      }))
      links = linksRes.data
    }

    if (resource) {
      url = id
        ? `${basepath}${links[resource]}/${id}`
        : `${basepath}${links[resource]}`
    }

    response = await axios({
      url,
      method,
      data,
      params,
      headers,
    })

    const {auth} = links

    return {
      ...response,
      auth: {links: auth},
      logout: links.logout,
    }
  } catch (error) {
    const {response} = error

    const {auth} = links
    throw {...response, auth: {links: auth}, logout: links.logout} // eslint-disable-line no-throw-literal
  }
}

import axios from 'axios'

let links

export default async function AJAX({
  url,
  resource,
  id,
  method = 'GET',
  data = {},
  params = {},
  headers = {},
}) {
  try {
    const basepath = window.basepath || ''
    let response

    url = `${basepath}${url}`

    if (!links) {
      const linksRes = (response = await axios({
        url: `${basepath}/chronograf/v1`,
        method: 'GET',
      }))
      links = linksRes.data
    }

    if (resource) {
      url = id
        ? `${basepath}${links[resource]}/${id}`
        : `${basepath}${links[resource]}`
    }

    response = await axios({
      url,
      method,
      data,
      params,
      headers,
    })

    const {auth} = links

    return {
      ...response,
      auth: {links: auth},
      logoutLink: links.logout,
    }
  } catch (error) {
    const {response} = error

    const {auth} = links
    throw {...response, auth: {links: auth}, logout: links.logout} // eslint-disable-line no-throw-literal
  }
}

Fix missing renaming of logout => logoutLink


const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');

const app = express();

app.use(bodyParser.json());
app.use(cors());
app.set('view engine', 'ejs');
app.set('views', './')

app.post('/api/users/:user', (req, res) => {
  return res.json(Object.assign({
    id: req.params.id,
    name: 'Vincenzo',
    surname: 'Chianese',
    age: 27 // Ahi Ahi, getting older
  }, req.body));
});

app.get('/serve-seed.html', (req, res) => {
  res.render('./serve-seed.ejs', {
    url: process.env.APIARY_SEED_URL || 'http://localhost:3000'
  });
});

app.listen(process.env.PORT || 3001);

const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');

const app = express();

app.set('view engine', 'ejs');
app.set('views', './')

app.get('/serve-seed.html', (req, res) => {
  res.render('./serve-seed.ejs', {
    url: process.env.APIARY_SEED_URL || 'http://localhost:3000'
  });
});

app.use(bodyParser.json());
app.use(cors());

app.post('/api/users/:user', (req, res) => {
  return res.json(Object.assign({
    id: req.params.id,
    name: 'Vincenzo',
    surname: 'Chianese',
    age: 27 // Ahi Ahi, getting older
  }, req.body));
});

app.listen(process.env.PORT || 3001);

Change routes and middlewares order


var sudo = require('sudo-prompt');
var cmdFnPath = require.resolve('cmd-fn');

var setName = sudo.setName;

var call = function(options, cb) {
  var cmd = process.execPath + ' ' + cmdFnPath;

  if (process.execPath.indexOf("/Contents/MacOS/Electron") >= 0) {
    // If we're running in Electron then make sure that the process is being
    // run in node mode not as the GUI app.
    cmd = 'ATOM_SHELL_INTERNAL_RUN_AS_NODE=1 ' + cmd;
  }

  if (options.module) {
    cmd = cmd + ' --module ' + options.module;
  } else {
    return cb(new Error('module option is required'));
  }

  if (options.function) {
    cmd = cmd + ' --function ' + options.function;
  }
  if (options.params) {
    cmd = cmd + ' --params \'' + JSON.stringify(options.params) + '\'';
  }
  if (options.cwd) {
    cmd = cmd + ' --cwd \'' + options.cwd + '\'';
  }
  if (options.type) {
    cmd = cmd + ' --' + options.type;
  }

  return sudo.exec(cmd, function(err, val) {
    try {
      val = JSON.parse(val);
    } catch (e) {
      // Do Nothing
    }
    cb(err,val);
  });
};

module.exports = {
  call: call,
  setName: setName
};

var sudo = require('sudo-prompt');
var cmdFnPath = require.resolve('cmd-fn');

var setName = sudo.setName;

var call = function(options, cb) {
  // Put paths inside quotes so spaces don't need to be escaped
  var cmd = '"' + process.execPath +'" "' + cmdFnPath + '"';

  if (process.execPath.indexOf("/Contents/MacOS/Electron") >= 0) {
    // If we're running in Electron then make sure that the process is being
    // run in node mode not as the GUI app.
    cmd = 'ATOM_SHELL_INTERNAL_RUN_AS_NODE=1 ' + cmd;
  }

  if (options.module) {
    cmd = cmd + ' --module ' + options.module;
  } else {
    return cb(new Error('module option is required'));
  }

  if (options.function) {
    cmd = cmd + ' --function ' + options.function;
  }
  if (options.params) {
    cmd = cmd + ' --params \'' + JSON.stringify(options.params) + '\'';
  }
  if (options.cwd) {
    cmd = cmd + ' --cwd \'' + options.cwd + '\'';
  }
  if (options.type) {
    cmd = cmd + ' --' + options.type;
  }

  return sudo.exec(cmd, function(err, val) {
    try {
      val = JSON.parse(val);
    } catch (e) {
      // Do Nothing
    }
    cb(err,val);
  });
};

module.exports = {
  call: call,
  setName: setName
};


// 12: destructuring - object
// To do: make all tests pass, leave the assert lines unchanged!

describe('destructuring objects', () => {

  it('is simple', () => {
    const {x} = {x: 1};
    assert.equal(x, 1);
  });

  describe('nested', () => {
    it('multiple objects', () => {
      const magic = {first: 23, second: 42};
      const {magic: {second}} = {magic};
      assert.equal(second, 42);
    });
    it('object and array', () => {
      const {z:[,x]} = {z: [23, 42]};
      assert.equal(x, 42);
    });
    it('array and object', () => {
      const [,[{lang}]] = [null, [{env: 'browser', lang: 'ES6'}]];
      assert.equal(lang, 'ES6');
    });
  });

  it('missing => undefined', () => {
    const {z} = {x: 1};
    assert.equal(z, void 0);
  });

  it('destructure from builtins (string)', () => {
    const {substr} = '';
    assert.equal(substr, String.prototype.substr);
  });

});

// 12: destructuring - object
// To do: make all tests pass, leave the assert lines unchanged!

describe('destructuring objects', () => {

  it('is simple', () => {
    const x = {x: 1};
    assert.equal(x, 1);
  });

  describe('nested', () => {
    it('multiple objects', () => {
      const magic = {first: 23, second: 42};
      const {magic: [second]} = {magic};
      assert.equal(second, 42);
    });
    it('object and array', () => {
      const {z:[x]} = {z: [23, 42]};
      assert.equal(x, 42);
    });
    it('array and object', () => {
      const [,{lang}] = [null, [{env: 'browser', lang: 'ES6'}]];
      assert.equal(lang, 'ES6');
    });
  });

  describe('interesting', () => {
    it('missing refs become undefined', () => {
      const {z} = {x: 1, z: 2};
      assert.equal(z, void 0);
    });

    it('destructure from builtins (string)', () => {
      const {substr} = 1;
      assert.equal(substr, String.prototype.substr);
    });
  });

});

Make them fail, so it is a real kata now :).


importScripts('common.js');

function continuouslyGenerateRsaKey()
{
    var extractable = false;
    var usages = ['encrypt', 'decrypt'];
    // Note that the modulus length is small.
    var algorithm = {name: "RSAES-PKCS1-v1_5", modulusLength: 512, publicExponent: hexStringToUint8Array("010001")};

    return crypto.subtle.generateKey(algorithm, extractable, usages).then(function(result) {
        // Infinite recursion intentional!
        return continuouslyGenerateRsaKey();
    });
}

// Starts a Promise which continually generates new RSA keys.
var unusedPromise = continuouslyGenerateRsaKey();

// Inform the outer script that the worker started.
postMessage("Worker started");

importScripts('common.js');

function continuouslyGenerateRsaKey()
{
    var extractable = false;
    var usages = ['sign', 'verify'];
    // Note that the modulus length is small.
    var algorithm = {name: "RSASSA-PKCS1-v1_5", modulusLength: 512, publicExponent: hexStringToUint8Array("010001"), hash: {name: 'sha-1'}};

    return crypto.subtle.generateKey(algorithm, extractable, usages).then(function(result) {
        // Infinite recursion intentional!
        return continuouslyGenerateRsaKey();
    });
}

// Starts a Promise which continually generates new RSA keys.
var unusedPromise = continuouslyGenerateRsaKey();

// Inform the outer script that the worker started.
postMessage("Worker started");

Remove a lingering usage of RSA-ES.


function only_on_page(name, fn) {
  if ($('body').data('page') === name) {
    fn();
  }
}

function init_on_page(name, fn) {
  $(document).on('turbolinks:load', function () {
    only_on_page(name, function() {
      fn();
    });
  });
}

function applyManyBindings(o) {
  _.each(o, function (vm, el) {
    ko.applyBindings(vm, document.getElementById(el));
  });
}

function only_on_page(name, fn) {
  if ($('body').data('page') === name) {
    fn();
  }
}

function init_on_page(name, fn) {
  $(document).on('turbolinks:load', function () {
    only_on_page(name, function() {
      fn();
    });
  });
}

function applyManyBindings(o) {
  _.each(o, function (vm, id) {
    var el = document.getElementById(id);
    ko.cleanNode(el);
    ko.applyBindings(vm, el);
  });
}

Clean nodes before binding so we can have inner bindings


define(['phaser'], function(Phaser) {
    var state = new Phaser.State();
    var paddle;
    state.create = function() {
        paddle = state.add.sprite(400, 500, 'atlas', 'paddle');
        paddle.anchor.set(0.5);
        state.physics.enable(paddle, Phaser.Physics.ARCADE);
    };
    state.update = function() {
        // Phaser doesn't allow maxvelocity of 0, see physics/arcade/World.js ln 292
        state.physics.arcade.accelerateToPointer(paddle, state.input.activePointer, 1000, 300, 1);
    };
    return state;
});

define(['phaser', 'objects/ball'], function(Phaser, Ball) {
    var state = new Phaser.State();
    var paddle;
    var balls = [];
    state.create = function() {
        paddle = state.add.sprite(400, 500, 'atlas', 'paddle');
        paddle.anchor.set(0.5);
        state.physics.enable(paddle, Phaser.Physics.ARCADE);
        state.physics.arcade.setBoundsToWorld();
        paddle.body.immovable = true;
        balls.push(new Ball(state, 400, 450));
        balls[0].body.velocity.set(200,500);
        paddle.inputEnabled = true;
        paddle.input.enableDrag();
        paddle.input.allowVerticalDrag = false;
    };
    state.update = function() {
        state.physics.arcade.collide(balls,paddle);
    };
    return state;
});

Add ball and paddle/world collision.


import React, { Component } from 'react';
import RequestEditor from '../components/RequestEditor';
import ResponseViewer from '../components/ResponseViewer';
import * as actionCreators from '../actions/project';
import { connect } from 'react-redux';

class EditRequestPage extends Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  findRequest() {
    const project = this.props.projects.filter(p => p.id == this.props.params.projectId).get(0);
    if (!project)
      return null;

    return project.requests.filter(r => r.id == this.props.params.id).get(0);
  }

  onExecuteRequest(request) {
    this.props.executeRequest(request.method, request.url, request.headers.toJS(), request.body);
  }

  render() {
    const request = this.findRequest();
    if (request == null)
      return (<div/>);

    return (
      <div className="edit-request-page">
        <RequestEditor request={ request }
                       onRequestChange={this.props.updateRequest}
                       onRequestDelete={this.props.deleteRequest}
                       onRequestExecute={this.onExecuteRequest.bind(this)}/>

        <ResponseViewer response={ this.props.response }/>
      </div>
    );
  }
}

function mapStateToProps(state) {
  return {
    projects: state.projects,
    response: state.response
  }
}

export default connect(mapStateToProps, actionCreators)(EditRequestPage);

import React, { Component } from 'react';
import RequestEditor from '../components/RequestEditor';
import ResponseViewer from '../components/ResponseViewer';
import * as actionCreators from '../actions/project';
import { connect } from 'react-redux';

class EditRequestPage extends Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  onExecuteRequest(request) {
    this.props.executeRequest(request.method, request.url, request.headers.toJS(), request.body);
  }

  render() {
    if (!this.props.request)
      return null;

    return (
      <div className="edit-request-page">
        <RequestEditor request={ this.props.request }
                       onRequestChange={this.props.updateRequest}
                       onRequestDelete={this.props.deleteRequest}
                       onRequestExecute={this.onExecuteRequest.bind(this)}/>

        <ResponseViewer response={ this.props.response }/>
      </div>
    );
  }
}

function mapStateToProps(state, ownProps) {
  const project = state.projects.filter(p => p.id == ownProps.params.projectId).get(0);
  const request = project && project.requests.filter(r => r.id == ownProps.params.id).get(0);

  return {
    request: request,
    response: state.response
  }
}

export default connect(mapStateToProps, actionCreators)(EditRequestPage);

Select specific request from redux state directly in connect


const whitelist = [
  'style',
  'className',
  'role',
  'id',
  'autocomplete',
  'size',
];

const whitelistRegex = [/^aria-/, /^data-/, /^on[A-Z]\w+/]

export function pick(props, componentClass) {
  let keys = Object.keys(componentClass.propTypes);
  let result = {};
  Object.keys(props).forEach(key => {
    if (keys.indexOf(key) === -1) return
    result[key] = props[key];
  })
  return result
}

export function pickElementProps(component) {
  const props = omitOwn(component);
  const result = {};
  Object.keys(props).forEach(key => {
    if (
      whitelist.indexOf(key) !== -1 ||
      whitelistRegex.some(r => !!key.match(r))
    )
      result[key] = props[key];
  })

  return result;
}

export function omitOwn(component, ...others) {
  let initial = Object.keys(component.constructor.propTypes);
  let keys = others.reduce((arr, compClass) => [
    ...arr,
    ...Object.keys(compClass.propTypes)], initial
  );

  let result = {};
  Object.keys(component.props).forEach(key => {
    if (keys.indexOf(key) !== -1) return
    result[key] = component.props[key];
  })
  return result
}

const whitelist = [
  'style',
  'className',
  'role',
  'id',
  'autocomplete',
  'size',
  'tabIndex',
  'maxLength',
  'name'
];

const whitelistRegex = [/^aria-/, /^data-/, /^on[A-Z]\w+/]

export function pick(props, componentClass) {
  let keys = Object.keys(componentClass.propTypes);
  let result = {};
  Object.keys(props).forEach(key => {
    if (keys.indexOf(key) === -1) return
    result[key] = props[key];
  })
  return result
}

export function pickElementProps(component) {
  const props = omitOwn(component);
  const result = {};
  Object.keys(props).forEach(key => {
    if (
      whitelist.indexOf(key) !== -1 ||
      whitelistRegex.some(r => !!key.match(r))
    )
      result[key] = props[key];
  })

  return result;
}

export function omitOwn(component, ...others) {
  let initial = Object.keys(component.constructor.propTypes);
  let keys = others.reduce((arr, compClass) => [
    ...arr,
    ...Object.keys(compClass.propTypes)], initial
  );

  let result = {};
  Object.keys(component.props).forEach(key => {
    if (keys.indexOf(key) !== -1) return
    result[key] = component.props[key];
  })
  return result
}

Add some more props to whitelist


/* @flow */

export default function users(): Object {
  return {
    profile: (username: string) => {
      const url = `/users/${username}`;

      return this.request({
        url,
        method: "GET"
      });
    },

    photos: (username: string) => {
      const url = `/users/${username}/photos`;

      return this.request({
        url,
        method: "GET"
      });
    },

    likes: (username: string, page: number, perPage: number) => {
      const url = `/users/${username}/likes`;

      let query = {
        page,
        per_page: perPage
      };

      return this.request({
        url,
        method: "GET",
        query
      });
    }
  };
}

/* @flow */

export default function users(): Object {
  return {
    profile: (username: string) => {
      const url = `/users/${username}`;

      return this.request({
        url,
        method: "GET"
      });
    },

    photos: (username: string) => {
      const url = `/users/${username}/photos`;

      return this.request({
        url,
        method: "GET"
      });
    },

    likes: (username: string, page: number = 1, perPage: number = 10) => {
      const url = `/users/${username}/likes`;

      let query = {
        page,
        per_page: perPage
      };

      return this.request({
        url,
        method: "GET",
        query
      });
    }
  };
}

Add default page and perPage values to user likes


const React = window.React;
import RangeInputFixtures from './range-inputs';
import TextInputFixtures from './text-inputs';
import SelectFixtures from './selects';
import TextAreaFixtures from './textareas/';

/**
 * A simple routing component that renders the appropriate
 * fixture based on the location pathname.
 */
const FixturesPage = React.createClass({
  render() {
    switch (window.location.pathname) {
      case '/text-inputs':
        return <TextInputFixtures />;
      case '/range-inputs':
        return <RangeInputFixtures />;
      case '/selects':
        return <SelectFixtures />;
      case '/textareas':
        return <TextAreaFixtures />;
      default:
        return <span />;
    }
  },
});

module.exports = FixturesPage;

const React = window.React;
import RangeInputFixtures from './range-inputs';
import TextInputFixtures from './text-inputs';
import SelectFixtures from './selects';
import TextAreaFixtures from './textareas/';

/**
 * A simple routing component that renders the appropriate
 * fixture based on the location pathname.
 */
const FixturesPage = React.createClass({
  render() {
    switch (window.location.pathname) {
      case '/text-inputs':
        return <TextInputFixtures />;
      case '/range-inputs':
        return <RangeInputFixtures />;
      case '/selects':
        return <SelectFixtures />;
      case '/textareas':
        return <TextAreaFixtures />;
      default:
        return
Please select a text fixture.

    }
  },
});

module.exports = FixturesPage;

Add test prompt message when no fixture is selected


/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @flow
 */
'use strict';

var React = require('react');

class DetailPaneSection extends React.Component {
  render(): React.Element {
    var {
      children,
      hint,
    } = this.props;
    return (

        {this.props.title}
        {hint ? ' ' + hint : null}
        {children}

    );
  }
}

var styles = {
  section: {
    marginBottom: 10,
    flexShrink: 0,
  },
  title: {
    marginRight: 5,
  },
};

module.exports = DetailPaneSection;

/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @flow
 */
'use strict';

var React = require('react');

class DetailPaneSection extends React.Component {
  render(): React.Element {
    var {
      children,
      hint,
    } = this.props;
    return (

        {this.props.title}
        {hint}
        {children}

    );
  }
}

var styles = {
  section: {
    marginBottom: 10,
    flexShrink: 0,
  },
  title: {
    marginRight: 7,
  },
};

module.exports = DetailPaneSection;

Increase margin and remove the padded space


'use strict';

/* Expose. */
module.exports = visit;

visit.CONTINUE = true;
visit.SKIP = 'skip';
visit.EXIT = false;

var is = require('unist-util-is');

/* Visit. */
function visit(tree, test, visitor, reverse) {
  if (typeof test === 'function' && typeof visitor !== 'function') {
    reverse = visitor;
    visitor = test;
    test = null;
  }

  one(tree);

  /* Visit a single node. */
  function one(node, index, parent) {
    var result;

    index = index || (parent ? 0 : null);

    if (!test || node.type === test || is(test, node, index, parent || null)) {
      result = visitor(node, index, parent || null);
    }

    if (result === visit.EXIT) {
      return result;
    }

    if (node.children && result !== visit.SKIP) {
      return all(node.children, node);
    }

    return visit.CONTINUE;
  }

  /* Visit children in `parent`. */
  function all(children, parent) {
    var step = reverse ? -1 : 1;
    var max = children.length;
    var min = -1;
    var index = (reverse ? max : min) + step;
    var child;
    var result;

    while (index > min && index < max) {
      child = children[index];
      result = child && one(child, index, parent);

      if (result === visit.EXIT) {
        return result;
      }

      index += step;
    }

    return visit.CONTINUE;
  }
}

'use strict';

module.exports = visit;

var is = require('unist-util-is');

var CONTINUE = true;
var SKIP = 'skip';
var EXIT = false;

visit.CONTINUE = CONTINUE;
visit.SKIP = SKIP;
visit.EXIT = EXIT;

function visit(tree, test, visitor, reverse) {
  if (typeof test === 'function' && typeof visitor !== 'function') {
    reverse = visitor;
    visitor = test;
    test = null;
  }

  one(tree);

  /* Visit a single node. */
  function one(node, index, parent) {
    var result;

    index = index || (parent ? 0 : null);

    if (!test || node.type === test || is(test, node, index, parent || null)) {
      result = visitor(node, index, parent || null);
    }

    if (result === EXIT) {
      return result;
    }

    if (node.children && result !== SKIP) {
      return all(node.children, node);
    }

    return CONTINUE;
  }

  /* Visit children in `parent`. */
  function all(children, parent) {
    var step = reverse ? -1 : 1;
    var max = children.length;
    var min = -1;
    var index = (reverse ? max : min) + step;
    var child;
    var result;

    while (index > min && index < max) {
      child = children[index];
      result = child && one(child, index, parent);

      if (result === EXIT) {
        return result;
      }

      index += step;
    }

    return CONTINUE;
  }
}

Refactor internal use of constants


#!/usr/bin/env node
"use strict";
const meow = require("meow");
const Promise = require("pinkie-promise");
const Xray = require("x-ray"), xray = Xray();

const periods = ['day', 'week', 'month'];

const cli = meow(`
  Usage
    $ pkgstats -u <user> -n <package name> -p <day|week|month>

  Options
    -u, --user      npm user
    -n, --name      pkg name
    -p, --period    time period
`);

#!/usr/bin/env node
'use strict';
const Promise = require('pinkie-promise');
const Xray = require('x-ray'), xray = Xray();
const cli = require('./cli');

const getUsrPkgs = function(username) {
  return new Promise(function(resolve, reject) {
    var url = 'https://www.npmjs.com/~' + username + '/';
    xray(url, '.collaborated-packages', ['li'], 'a')(function(err, pkgs) {
      if (err) reject(err);
      // resolve(data);
      var usrPkgs = [];
      pkgs.forEach(function(pkg) {
        usrPkgs.push(pkg.trim().split('- ')[0].trim());
      });
      resolve(usrPkgs);
    });
  });
}

getUsrPkgs(cli.flags['u']).then(function(pkgs) {
  return pkgs;
});

Add scraper for user packages


/* jshint node: true */
'use strict';

module.exports = {
  name: 'ember-cli-medium-editor',

  included: function(app) {
    this._super.included(app);
    var options = app.options.mediumEditorOptions || {};

    this.app.import(app.bowerDirectory + '/medium-editor/dist/js/medium-editor.js');

    if (options.theme && options.excludeBaseStyles !== false) {
      this.app.import(app.bowerDirectory + '/medium-editor/dist/css/medium-editor.css');
    }

    if (options.theme && options.theme !== false) {
      this.app.import(app.bowerDirectory + '/medium-editor/dist/css/themes/' + options.theme + '.css');
    }
  }
};

/* jshint node: true */
'use strict';

module.exports = {
  name: 'ember-cli-medium-editor',

  included: function(app) {
    this._super.included(app);
    var options = app.options.mediumEditorOptions || {};

    this.app.import(app.bowerDirectory + '/medium-editor/dist/js/medium-editor.js');

    if (!options.excludeBaseStyles) {
      this.app.import(app.bowerDirectory + '/medium-editor/dist/css/medium-editor.css');
    }

    if (options.theme) {
      this.app.import(app.bowerDirectory + '/medium-editor/dist/css/themes/' + options.theme + '.css');
    } else if (options.theme !== false) {
      this.app.import(app.bowerDirectory + '/medium-editor/dist/css/themes/default.css');
    }
  }
};

Fix logic for style overrides.


'use strict';

var stemmer;

/**
 * Module dependencies.
 */

stemmer = require('lancaster-stemmer');

/**
 * `changetextinside` handler;
 *
 * @this Node
 */

function onchangetextinside() {
    var value;

    value = this.toString();

    this.data.stem = value ? stemmer(value) : null;
}

/**
 * Define `lancasterStemmer`.
 *
 * @param {Retext} retext - Instance of Retext.
 */

function lancasterStemmer(retext) {
    var WordNode;

    WordNode = retext.parser.TextOM.WordNode;

    WordNode.on('changetextinside', onchangetextinside);
    WordNode.on('removeinside', onchangetextinside);
    WordNode.on('insertinside', onchangetextinside);
}

/**
 * Expose `lancasterStemmer`.
 */

module.exports = lancasterStemmer;

'use strict';

var stemmer;

/**
 * Module dependencies.
 */

stemmer = require('lancaster-stemmer');

/**
 * `changetextinside` handler;
 *
 * @this Node
 */

function onchangeinside() {
    var value;

    value = this.toString();

    this.data.stem = value ? stemmer(value) : null;
}

/**
 * Define `lancasterStemmer`.
 *
 * @param {Retext} retext - Instance of Retext.
 */

function lancasterStemmer(retext) {
    retext.TextOM.WordNode.on('changeinside', onchangeinside);
}

/**
 * Expose `lancasterStemmer`.
 */

module.exports = lancasterStemmer;

Refactor to use generic `changeinside` in new retext


const config = require('./config');
const express = require('express');
const cors = require('cors');
const importer = require('./middleware/importer');
const mongoose = require('mongoose');
const dbHelper = require('./lib/db');

// import routers
const bitcoinRouter = require('./router/bitcoin');
const defaultRouter = require('./router/default');

// activate configured cron jobs
const updateDataCronJobs = require('./cronjobs/updateData');
const deleteOldDataCronJobs = require('./cronjobs/deleteOldData');

// create Express server
var server = express();

// add CORS middleware
server.use(cors());

// connect to MongoDB
var mongoDbUri = dbHelper.getMongoDbUri(config.dbParams);
var mongoDbOptions = {
    useMongoClient: true
};
mongoose.connect(mongoDbUri, mongoDbOptions);

// Check if there is a need for data import/sync initiation
// (initiated if number of months is more than the monthly averages in the DB)
importer.checkDBcompleteness();

// apply /bitcoin routes
server.use(config.urlPrefix + '/bitcoin', bitcoinRouter);

// apply default router to catch ANY other requested route
server.use(config.urlPrefix + '/', defaultRouter);

// start the server
server.listen(config.serverPort, function () {
    console.log('Server is running on port: ' + config.serverPort);
});

const config = require('./config');
const express = require('express');
const cors = require('cors');
const importer = require('./middleware/importer');
const mongoose = require('mongoose');
const dbHelper = require('./lib/db');
const tools = require('./lib/tools');

// import routers
const bitcoinRouter = require('./router/bitcoin');
const defaultRouter = require('./router/default');

// activate configured cron jobs
const updateDataCronJobs = require('./cronjobs/updateData');
const deleteOldDataCronJobs = require('./cronjobs/deleteOldData');

// create Express server
var server = express();

// add CORS middleware
server.use(cors());

// connect to MongoDB
var mongoDbUri = dbHelper.getMongoDbUri(config.dbParams);
var mongoDbOptions = {
    useMongoClient: true
};
mongoose.connect(mongoDbUri, mongoDbOptions);

// Check if there is a need for data import/sync initiation
// (initiated if number of months is more than the monthly averages in the DB)
importer.checkDBcompleteness();

// log time and ip address of request
server.use(function(req, res, next) {
    console.log(tools.getCurrentDatetimeString(), req.ip);
    next();
});

// apply /bitcoin routes
server.use(config.urlPrefix + '/bitcoin', bitcoinRouter);

// apply default router to catch ANY other requested route
server.use(config.urlPrefix + '/', defaultRouter);

// start the server
server.listen(config.serverPort, function () {
    console.log('Server is running on port: ' + config.serverPort);
});

Add logging info for request


'use strict'

module.exports = visit

var visitParents = require('unist-util-visit-parents')

var CONTINUE = visitParents.CONTINUE
var SKIP = visitParents.SKIP
var EXIT = visitParents.EXIT

visit.CONTINUE = CONTINUE
visit.SKIP = SKIP
visit.EXIT = EXIT

function visit(tree, test, visitor, reverse) {
  if (typeof test === 'function' && typeof visitor !== 'function') {
    reverse = visitor
    visitor = test
    test = null
  }

  visitParents(tree, test, overload, reverse)

  function overload(node, parents) {
    var parent = parents[parents.length - 1]
    var index = parent ? parent.children.indexOf(node) : null
    return visitor(node, index, parent)
  }
}

'use strict'

module.exports = visit

var visitParents = require('unist-util-visit-parents')

visit.CONTINUE = visitParents.CONTINUE
visit.SKIP = visitParents.SKIP
visit.EXIT = visitParents.EXIT

function visit(tree, test, visitor, reverse) {
  if (typeof test === 'function' && typeof visitor !== 'function') {
    reverse = visitor
    visitor = test
    test = null
  }

  visitParents(tree, test, overload, reverse)

  function overload(node, parents) {
    var parent = parents[parents.length - 1]
    var index = parent ? parent.children.indexOf(node) : null
    return visitor(node, index, parent)
  }
}

Refactor to improve bundle size


var explode = require('turf-explode');
var point = require('turf-point');

/**
 * Calculates the centroid of a polygon Feature or
 * FeatureCollection using the geometric mean of all vertices.
 * This lessens the effect of small islands and artifacts when calculating
 * the centroid of a set of polygons.
 *
 * @module turf/centroid
 * @param {FeatureCollection} fc a {@link Feature} or FeatureCollection of any type
 * @return {Point} a Point showing the centroid of the input feature(s)
 * @example
 * var poly = turf.polygon([[[0,0], [0,10], [10,10] , [10,0]]])
 * var centroidPt = turf.centroid(poly)
 */
module.exports = function(features){
  var vertices = explode(features).features,
    xSum = 0,
    ySum = 0,
    len = vertices.length;

  for (var i = 0; i < len; i++) {
    xSum += vertices[i].geometry.coordinates[0];
    ySum += vertices[i].geometry.coordinates[1];
  }

  return point(xSum / len, ySum / len);
};

var explode = require('turf-explode');
var point = require('turf-point');

/**
 * Calculates the centroid of a polygon Feature or
 * FeatureCollection using the geometric mean of all vertices.
 * This lessens the effect of small islands and artifacts when calculating
 * the centroid of a set of polygons.
 *
 * @module turf/centroid
 * @param {FeatureCollection} fc a {@link Feature} or FeatureCollection of any type
 * @return {Point} a Point showing the centroid of the input feature(s)
 * @example
 * var poly = turf.polygon([[[0,0], [0,10], [10,10] , [10,0], [0, 0]]]);
 * var centroidPt = turf.centroid(poly);
 *
 * var result = turf.featurecollection([poly, centroidPt]);
 *
 * //=result
 */
module.exports = function(features){
  var vertices = explode(features).features,
    xSum = 0,
    ySum = 0,
    len = vertices.length;

  for (var i = 0; i < len; i++) {
    xSum += vertices[i].geometry.coordinates[0];
    ySum += vertices[i].geometry.coordinates[1];
  }

  return point(xSum / len, ySum / len);
};

Fix polygon, add semicolons, rpl-ize


module.exports = function (App) {
	if (typeof (App) == 'undefined') {
		console.error('ERROR: Seminarjs not detected');
		process.exit(-1);
	}

	try {

		App.express.use('/plugins/chat/', function (req, res, next) {
			if (req.method !== 'GET') {
				next();
				return;
			}
			res.sendFile(__dirname + "/public/" + req.path);
		});

		var Chat = require('./src/chat-server.js');
		Chat(App.io);

	} catch (e) {
		console.error(e.getMessage());
	}
};

module.exports = function (seminarjs) {
	if (typeof (seminarjs) == 'undefined') {
		console.error('[ERROR] Seminarjs not detected');
		process.exit(-1);
	}

	try {

		seminarjs.app.use('/plugins/chat/', function (req, res, next) {
			if (req.method !== 'GET') {
				next();
				return;
			}
			res.sendFile(__dirname + "/public/" + req.path);
		});

		var Chat = require('./src/chat-server.js');
		Chat(seminarjs.io);

	} catch (e) {
		console.error(e.getMessage());
	}
};

Upgrade to new Seminarjs plugin system


'use strict';

module.exports = isJsObject;

function isJsObject(obj) {
  return /^\[object (?:object|global)\]$/i
    .test(Object.prototype.toString.call(obj));
}

'use strict';

module.exports = isJsObject;

function isJsObject(obj) {
  return /^\[object (?:object|global|window)\]$/i
    .test(Object.prototype.toString.call(obj));
}

Make it work in Firefox


var PATH_SEPARATOR = require('path').sep;
var requirejs = exports.requirejs = require('requirejs');
var getStackTrace = require('./stack-trace');

requirejs.config({baseUrl: process.cwd()});

var loadedModules = Object.create(null);

var toModuleId = PATH_SEPARATOR === '/' ?
  function(fileName) { return fileName; } :
  function(fileName) { return fileName.split(PATH_SEPARATOR).join('/'); };

function define() {
  var fileName = getStackTrace(1, define)[0].getFileName();
  if (!(loadedModules[fileName])) {
    require.cache[fileName].exports = requirejs(toModuleId(fileName));
  }
}

global.define = define;

var PATH_SEPARATOR = require('path').sep;
var requirejs = exports.requirejs = require('requirejs');
var getStackTrace = require('./stack-trace');

var config = {baseUrl: process.cwd()};
var loadedModules = Object.create(null);

var toModuleId = PATH_SEPARATOR === '/' ?
  function(fileName) { return fileName; } :
  function(fileName) { return fileName.split(PATH_SEPARATOR).join('/'); };

function define() {
  var fileName = getStackTrace(1, define)[0].getFileName();
  if (!(loadedModules[fileName])) {
    var module = require.cache[fileName];
    config.nodeRequire = module.require.bind(module);
    module.exports = requirejs.config(config)(toModuleId(fileName));
  }
}

global.define = define;

Reconfigure RequireJS with the module-local `require` for each call


var EventEmitter = require('events').EventEmitter;
var inherits = require('inherits');

module.exports = function(fn) {

  inherits(fn, EventEmitter);

  var self = new fn();

  self.addEventListener = function(ev, cb) {
    self.on(ev,cb);
  };

  global.postMessage = // unfortunately global for worker (no namespaces?)
  self.postMessage = function(msg) {
    self.emit('message', {data:msg});
  };

  return self;
};

var EventEmitter = require('events').EventEmitter;
var inherits = require('inherits');

module.exports = function(fn) {

  inherits(fn, EventEmitter);

  fn.prototype.addEventListener = function(ev, cb) {
    this.on(ev,cb);
  };

  var self = new fn();

  global.postMessage = // unfortunately global for worker (no namespaces?)
  self.postMessage = function(msg) {
    self.emit('message', {data:msg});
  };

  return self;
};

Move addEventListener back to prototype so webworkers can call it on themselves in constructor


'use strict';

var bluebird = require('bluebird');
var fs       = bluebird.promisifyAll(require('fs'));
var glob     = require('glob');
var hljs     = require('../../build');
var path     = require('path');
var utility  = require('../utility');

function testLanguage(language) {
  describe(language, function() {
    var filePath  = utility.buildPath('markup', language, '*.expect.txt'),
        filenames = glob.sync(filePath);

    filenames.forEach(function(filename) {
      var testName   = path.basename(filename, '.expect.txt'),
          sourceName = filename.replace(/\.expect/, '');

      it('should markup ' + testName, function(done) {
        var sourceFile   = fs.readFileAsync(sourceName, 'utf-8'),
            expectedFile = fs.readFileAsync(filename, 'utf-8');

        bluebird.join(sourceFile, expectedFile, function(source, expected) {
          var actual = hljs.highlight(language, source).value;

          actual.should.equal(expected);
          done();
        });
      });
    });
  });
}

describe('markup generation test', function() {
  var languages = fs.readdirSync(utility.buildPath('markup'));

  languages.forEach(testLanguage);
});

'use strict';

var _        = require('lodash');
var bluebird = require('bluebird');
var fs       = bluebird.promisifyAll(require('fs'));
var glob     = require('glob');
var hljs     = require('../../build');
var path     = require('path');
var utility  = require('../utility');

function testLanguage(language) {
  describe(language, function() {
    var filePath  = utility.buildPath('markup', language, '*.expect.txt'),
        filenames = glob.sync(filePath);

    _.each(filenames, function(filename) {
      var testName   = path.basename(filename, '.expect.txt'),
          sourceName = filename.replace(/\.expect/, '');

      it('should markup ' + testName, function(done) {
        var sourceFile   = fs.readFileAsync(sourceName, 'utf-8'),
            expectedFile = fs.readFileAsync(filename, 'utf-8');

        bluebird.join(sourceFile, expectedFile, function(source, expected) {
          var actual = hljs.highlight(language, source).value;

          actual.should.equal(expected);
          done();
        });
      });
    });
  });
}

describe('markup generation test', function() {
  var languages = fs.readdirSync(utility.buildPath('markup'));

  _.each(languages, testLanguage, this);
});

Change forEach to lodash each functions


module.exports = ({ env }) => ({
  host: env('HOST'),
  port: env.int('PORT'),
});

module.exports = ({ env }) => ({
  host: env('HOST', '0.0.0.0'),
  port: env.int('PORT', 1337),
});

Use default host and port


// JavaScript Document

// Scripts written by __gulp_init__author_name @ __gulp_init__author_company

import Slideout from "slideout";

// get the elements
const PANEL = document.getElementById("page-container");
const MENU  = document.getElementById("mobile-menu");
const TOGGLE  = document.querySelector("[data-toggle=mobile-menu]");

// verify that the elements exist
if (PANEL !== null && MENU !== null && TOGGLE !== null) {
    // initialize the menu
    const MOBILE_MENU = new Slideout({
        duration: 250,
        menu:     MENU,
        panel:    PANEL,
    });

    // toggle the menu when clicking on the toggle
    TOGGLE.addEventListener("click", (e) => {
        e.preventDefault();
        MOBILE_MENU.toggle();
    });

    // close the menu when it's open and the content is clicked
    PANEL.addEventListener("click", (e) => {
        if (e.target !== TOGGLE && MOBILE_MENU.isOpen()) {
            e.preventDefault();
            MOBILE_MENU.close();
        }
    });
}

// JavaScript Document

// Scripts written by __gulp_init__author_name @ __gulp_init__author_company

import Slideout from "slideout";

// get the elements
const PANEL        = document.getElementById("page-container");
const MENU         = document.getElementById("mobile-menu");
const TOGGLE       = document.querySelector("[data-toggle=mobile-menu]");
const MOBILE_WIDTH = 768;

// verify that the elements exist
if (PANEL !== null && MENU !== null && TOGGLE !== null) {
    // initialize the menu
    let mobile_menu = null;

    // toggle the menu when clicking on the toggle
    TOGGLE.addEventListener("click", (e) => {
        e.preventDefault();

        if (mobile_menu !== null) {
            mobile_menu.toggle();
        }
    });

    // close the menu when it's open and the content is clicked
    PANEL.addEventListener("click", (e) => {
        if (mobile_menu !== null && e.target !== TOGGLE && mobile_menu.isOpen()) {
            e.preventDefault();
            mobile_menu.close();
        }
    });

    const CONSTRUCT_MENU = () => {
        return new Slideout({
            duration: 250,
            menu: MENU,
            panel: PANEL,
        });
    };

    // destroy the menu on desktop
    window.addEventListener("load", () => {
        if (mobile_menu === null && document.documentElement.clientWidth < MOBILE_WIDTH) {
            mobile_menu = CONSTRUCT_MENU();
        }
    });

    // destroy the menu on desktop
    window.addEventListener("resize", () => {
        const CLIENT_WIDTH = document.documentElement.clientWidth;

        if (mobile_menu !== null && CLIENT_WIDTH >= MOBILE_WIDTH) {
            mobile_menu.destroy();
            mobile_menu = null;
        } else if (mobile_menu === null && CLIENT_WIDTH < MOBILE_WIDTH) {
            mobile_menu = CONSTRUCT_MENU();
        }
    });
}

Disable the mobile menu on desktop


var test = require('tape')
var parse = require('../parser')

test('partial input', function (t) {
  // These should all produce syntax errors at the end of the input
  var examples = [
    { input: "echo ( this will fail", position: 5 }
  ]

  examples.forEach(function (example) {
    try {
      parse(example.input)
    } catch (err) {
      t.equal(err.constructor, parse.SyntaxError, 'got a SyntaxError');
      t.equal(example.position, err.offset);
    }
  })

  t.end()
})

var test = require('tape')
var parse = require('../parser')

test('partial input', function (t) {
  // These should all produce syntax errors at the given position
  var examples = [
    { input: "echo ( this will fail", position: 5 }
  ]

  examples.forEach(function (example) {
    t.test('SyntaxError test "' + example.input + '"', function (t) {
      try {
        parse(example.input)
        t.fail("did not throw expected SyntaxError")
      } catch (err) {

        t.equal(err.constructor, parse.SyntaxError, 'threw expected SyntaxError')
        t.equal(example.position, err.offset, 'SyntaxError has expected offset')
        try {
          parse(example.input.substr(err.offset), 'continuationStart')
          t.fail('was successfully parsed as a continuationStart')
        } catch (e) {
          t.pass('could not parse continuationStart')
        }
      }
    })
  })

  t.end()
})

Test parse errors aren't treated as partial input


import BaseWorker from '../../base'
import twilio from 'twilio'
import path from 'path'

const client = new twilio.RestClient(
  process.env.TWILIO_SID,
  process.env.TWILIO_TOKEN
)

export class TextMessage extends BaseWorker {
  constructor (rsmq) {
    super('text_message', rsmq)
  }

  body (message) {
    return this.render(
      path.join(__dirname, '../templates', 'plain_text.ejs'),
      message
    )
  }

  async process (message, next) {
    try {
      await client.sendMessage({
        body: this.body(message),
        to: process.env.TWILIO_TO_NUMBER,
        from: process.env.TWILIO_FROM_NUMBER
      })
      next()
    } catch (err) {
      next(err)
    }
  }
}

export default TextMessage

import BaseWorker from '../../base'
import twilio from 'twilio'
import path from 'path'

export class TextMessage extends BaseWorker {
  constructor (rsmq) {
    super('text_message', rsmq)

    this.client = new twilio.RestClient(
      process.env.TWILIO_SID,
      process.env.TWILIO_TOKEN
    )
  }

  body (message) {
    return this.render(
      path.join(__dirname, '../templates', 'plain_text.ejs'),
      message
    )
  }

  async process (message, next) {
    try {
      await this.client.sendMessage({
        body: this.body(message),
        to: process.env.TWILIO_TO_NUMBER,
        from: process.env.TWILIO_FROM_NUMBER
      })
      next(null)
    } catch (err) {
      next(err)
    }
  }
}

export default TextMessage

Change the way text message egress initializes


import React from 'react'
import styled from 'styled-components'

const Wrapper = styled.div`
  margin-top: 2rem;
`

const Key = styled.label`
  font-weight: 700;
`

const Value = styled.label`
  word-break: break-all;
`

export const FileDetails = ({ file }) => {
  const { name, size } = file

  return (
    <Wrapper>

        <Key>Name: </Key>
        <Value>{name}</Value>

        <Key>Size: </Key>
        <Value>{size} bytes</Value>

    </Wrapper>
  )
}

import React from 'react'
import styled from 'styled-components'

const Wrapper = styled.dl`
  margin-top: 2rem;
`

const Key = styled.dt`
  font-weight: 700;
`

const Value = styled.dd`
  word-break: break-all;
  margin-bottom: 0.5rem;
`

export const FileDetails = ({ file }) => {
  const { name, size } = file

  return (
    <Wrapper>
      <Key>Name</Key>
      <Value>{name}</Value>
      <Key>Size</Key>
      <Value>{size} bytes</Value>
    </Wrapper>
  )
}

Use dt and dd elements for displaying file metadata


import Measurement from 'esri/dijit/Measurement';
import React, {
  Component,
  PropTypes
} from 'react';

export default class InfoWindow extends Component {
  static contextTypes = {
    map: PropTypes.object.isRequired
  }

  initialized = false

  componentWillUpdate(prevProps) {
    if (
      this.context.map.loaded
      // the Measurement tool depends on navigationManager so we
      // need to explicitly check for that before starting the widget
      && this.context.map.navigationManager
      && !this.initialized
    ) {
      this.initialized = true;

      const measurementDiv = document.createElement('DIV');
      this.measurementContainer.appendChild(measurementDiv);

      this.measurement = new Measurement({
        map: this.context.map
      }, measurementDiv);
      this.measurement.startup();
      this.measurement.on('measure-end', (event) => {
        // deactivate the tool after drawing
        const toolName = event.toolName;
        this.measurement.setTool(toolName, false);
      });
    }

    if (prevProps.activeWebmap !== undefined && prevProps.activeWebmap !== this.props.activeWebmap) {
      if (this.context.map.destroy && this.initialized) {
        this.measurement.destroy();
        this.initialized = false;
      }
    }

  }

  componentWillUnmount() {
    this.measurement.destroy();
  }

  render () {
    return <div
      ref={(div) => { this.measurementContainer = div; }}
      className='measurement-container'
    />;
  }
}

import Measurement from 'esri/dijit/Measurement';
import React, {
  Component,
  PropTypes
} from 'react';

export default class InfoWindow extends Component {
  static contextTypes = {
    map: PropTypes.object.isRequired
  }

  initialized = false

  componentWillUpdate(prevProps) {
    if (
      this.context.map.loaded
      // the Measurement tool depends on navigationManager so we
      // need to explicitly check for that before starting the widget
      && this.context.map.navigationManager
      && !this.initialized
    ) {
      this.initialized = true;

      const measurementDiv = document.createElement('DIV');
      this.measurementContainer.appendChild(measurementDiv);

      this.measurement = new Measurement({
        map: this.context.map
      }, measurementDiv);
      this.measurement.startup();
      this.measurement.on('measure-end', (event) => {
        // deactivate the tool after drawing
        const toolName = event.toolName;
        this.measurement.setTool(toolName, false);
      });
    }

    if (prevProps.activeWebmap !== undefined && prevProps.activeWebmap !== this.props.activeWebmap) {
      if (this.context.map.destroy && this.initialized) {
        this.measurement.clearResult();
        this.initialized = false;
      }
    }

  }

  componentWillUnmount() {
    this.measurement.destroy();
  }

  render () {
    return <div
      ref={(div) => { this.measurementContainer = div; }}
      className='measurement-container'
    />;
  }
}

Use clearResult instead of destroy on measurement so that user's selection will clear when switching tools or when the user clicks same tool again


'use strict';

var isError = require('es5-ext/lib/Error/is-error');

module.exports = function (t, a) {
	var week = t.create('Enumweektest', {
		options: ['MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU']
	});

	a(week('MO'), 'MO', "Valid");
	a.throws(function () { week('FOO'); }, "Invalid");

	return {
		"Is": function (a) {
			a(week.is({ toString: function () { return 'MO'; } }), false,
				"Not string");
			a(week.is('TU'), true, "Option");
			a(week.is('FOO'), false, "Other string");
		},
		"Normalize": function () {
			a(week.normalize('MO'), 'MO', "Valid");
			a(week.normalize('FOO'), null, "Invalid");
			a(week.normalize({ toString: function () { return 'MO'; } }), 'MO',
				"Coercible");
			a(week.normalize({}), null, "Invalid #2");
		},
		"Validate": function () {
			a(week.validate('MO'), null, "Valid");
			a(isError(week.validate('FOO')), true, "Invalid");
			a(week.validate({ toString: function () { return 'MO'; } }), null,
				"Coercible");
			a(isError(week.validate({})), true, "Invalid #2");
		}
	};
};

'use strict';

var isError = require('es5-ext/lib/Error/is-error');

module.exports = function (t, a) {
	var week = t.create('Enumweektest',
		['MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU']);

	a(week('MO'), 'MO', "Valid");
	a.throws(function () { week('FOO'); }, "Invalid");

	return {
		"Is": function (a) {
			a(week.is({ toString: function () { return 'MO'; } }), false,
				"Not string");
			a(week.is('TU'), true, "Option");
			a(week.is('FOO'), false, "Other string");
		},
		"Normalize": function () {
			a(week.normalize('MO'), 'MO', "Valid");
			a(week.normalize('FOO'), null, "Invalid");
			a(week.normalize({ toString: function () { return 'MO'; } }), 'MO',
				"Coercible");
			a(week.normalize({}), null, "Invalid #2");
		},
		"Validate": function () {
			a(week.validate('MO'), null, "Valid");
			a(isError(week.validate('FOO')), true, "Invalid");
			a(week.validate({ toString: function () { return 'MO'; } }), null,
				"Coercible");
			a(isError(week.validate({})), true, "Invalid #2");
		}
	};
};

Fix tests up to recent Enum change


frappe.listview_settings['Employee Separation'] = {
	add_fields: ["boarding_status", "employee_name", "date_of_joining", "department"],
	filters:[["boarding_status","=", "Pending"]],
	get_indicator: function(doc) {
		return [__(doc.boarding_status), frappe.utils.guess_colour(doc.boarding_status), "status,=," + doc.boarding_status];
	}
};

frappe.listview_settings['Employee Separation'] = {
	add_fields: ["boarding_status", "employee_name", "department"],
	filters:[["boarding_status","=", "Pending"]],
	get_indicator: function(doc) {
		return [__(doc.boarding_status), frappe.utils.guess_colour(doc.boarding_status), "status,=," + doc.boarding_status];
	}
};

Remove date_of_joining from field list


Meteor.publish('categories', function (name) {
    return Categories.find({
        name: { $regex: name }
    });
});

Categories.allow({
    insert: function(userId, doc){
        return true;
    },
    update:  function(userId, doc, fieldNames, modifier){
        return false;
    },
    remove:  function(userId, doc){
        return true;
    }
});

Meteor.publish('categories', function (text) {
    return Categories.find({
        $or: [
            {name: { $regex: text }},
            {description: { $regex: text }}
        ]
    });
});

Categories.allow({
    insert: function(userId, doc){
        return true;
    },
    update:  function(userId, doc, fieldNames, modifier){
        return false;
    },
    remove:  function(userId, doc){
        return true;
    }
});

Add description to category search


import express from 'express';
import Firebase from 'firebase';

const router = express.Router();

router.post('/dump', (req, res) => {
  const distance = req.body.distance;
  const remoteAddress = req.body.remoteAddress;

  console.log(distance, remoteAddress);

  const firebase = new Firebase('https://parkviz.firebaseio.com/');

  firebase.set({
    remoteAddress: {
      distance: distance
    }
  });
});

export default router;

import express from 'express';
import Firebase from 'firebase';

const router = express.Router();

router.post('/dump', (req, res) => {
  const distance = req.body.distance;
  const remoteAddress = req.body.remoteAddress;

  console.log(distance, remoteAddress);

  const firebase = new Firebase('https://parkviz.firebaseio.com/');

  let data = {};
  data[remoteAddress] = { distance: distance };

  firebase.set(data);
});

export default router;

Fix node name when saving to Firebase


import React from 'react';

export default function PartyMatchViz({partyAbbr, partyFit, partyLogo}) {
  const indicatorStyle = {
    backgroundImage: `url(${partyLogo})`,
  };
  return (

{partyFit}

);
}

Fix party logo mouseover text


import _merge from 'lodash/merge'
import _template from 'lodash/template'

/**
 * Send a request using axios. Designed to be used by Model and their instance
 *
 * @param  {Object} caller Object that call this function
 * @param  {Object} config Axios config
 * @param  {Object} [options]
 * @param  {string} [options.on] Which route ('collection' or 'member')
 * @return {Promise} Axios promise
 */
export function request (caller, config, options = {}) {
  const interpolate = caller.getOption('urlParamPattern')
  const params = caller.toParam()
  const route = caller.getRoute(options.on)

  // set variables pre request
  caller._pending = true
  caller._routeParams = {}

  // merge default config (api) with config
  config = _merge({}, caller.axios(), config)

  // prepend route to url and replace variables
  config.url = _template([route, config.url].join(''), { interpolate })(params)

  // send request and handle responses
  return caller.getGlobal('axios')(config)

    .then((response) => {
      caller._pending = false

      return response
    })

    .catch((error) => {
      caller._pending = false

      throw error
    })
}

import _merge from 'lodash/merge'
import _template from 'lodash/template'

/**
 * Send a request using axios. Designed to be used by Model and their instance
 *
 * @param  {Object} caller Object that call this function
 * @param  {Object} config Axios config
 * @param  {Object} [options]
 * @param  {string} [options.on] Which route ('collection' or 'member')
 * @return {Promise} Axios promise
 */
export function request (caller, config, options = {}) {
  const interpolate = caller.getOption('urlParamPattern')
  const params = caller.toParam()
  const route = caller.getRoute(options.on)

  // set variables pre request
  caller._pending = true
  caller._routeParams = {}

  // merge default config (api) with config
  config = _merge({}, caller.axios(), config)

  // prepend route to url and interpolate url and baseURL
  config.url = _template([route, config.url].join(''), { interpolate })(params)
  config.baseURL = _template(config.baseURL, { interpolate })(params)

  // send request and handle responses
  return caller.getGlobal('axios')(config)

    .then((response) => {
      caller._pending = false

      return response
    })

    .catch((error) => {
      caller._pending = false

      throw error
    })
}

Interpolate parameters in baseURL as well


/**
 * @copyright © 2015, Rick Wong. All rights reserved.
 */
"use strict";

var React  = require("./react");
var assign = React.__spread;

/**
 * @function render
 */
module.exports = function (Component, props, targetDOMNode, callback) {
	var myProps = assign({}, props, window.__reactTransmitPacket || {});

	React.render(React.createElement(Component, myProps), targetDOMNode, callback);
};

/**
 * @copyright © 2015, Rick Wong. All rights reserved.
 */
"use strict";

var React  = require("./react");
var assign = React.__spread;

/**
 * @function render
 */
module.exports = function (Component, props, targetDOMNode, callback) {
	var myProps = assign({}, props, window.__reactTransmitPacket || {});

	if (window.__reactTransmitPacket) {
		delete window.__reactTransmitPacket;
	}

	React.render(React.createElement(Component, myProps), targetDOMNode, callback);
};

Delete `window.__reactTransmitPacket` after first use.


import assert from 'assert';
import { parse } from 'graphql';
import { validate } from 'graphql/validation';
import { buildASTSchema } from 'graphql/utilities/buildASTSchema';

import { FieldsHaveDescriptions } from '../../src/rules/fields_have_descriptions';

describe('FieldsHaveDescriptions rule', () => {
  it('catches fields that have no description', () => {
    const ast = parse(`
      type QueryRoot {
        withoutDescription: String
        withoutDescriptionAgain: String!

        # Description
        withDescription: String
      }

      schema {
        query: QueryRoot
      }
    `);

    const schema = buildASTSchema(ast);
    const errors = validate(schema, ast, [FieldsHaveDescriptions]);

    assert.equal(errors.length, 2);

    assert.equal(errors[0].ruleName, 'fields-have-descriptions');
    assert.equal(
      errors[0].message,
      'The field `QueryRoot.withoutDescription` is missing a description.'
    );
    assert.deepEqual(errors[0].locations, [{ line: 3, column: 9 }]);

    assert.equal(errors[1].ruleName, 'fields-have-descriptions');
    assert.equal(
      errors[1].message,
      'The field `QueryRoot.withoutDescriptionAgain` is missing a description.'
    );
    assert.deepEqual(errors[1].locations, [{ line: 4, column: 9 }]);
  });
});

import { FieldsHaveDescriptions } from '../../src/rules/fields_have_descriptions';
import { expectFailsRule } from '../assertions';

describe('FieldsHaveDescriptions rule', () => {
  it('catches fields that have no description', () => {
    expectFailsRule(
      FieldsHaveDescriptions,
      `
      type QueryRoot {
        withoutDescription: String
        withoutDescriptionAgain: String!

        # Description
        withDescription: String
      }

      schema {
        query: QueryRoot
      }
    `,
      [
        {
          message:
            'The field `QueryRoot.withoutDescription` is missing a description.',
          locations: [{ line: 3, column: 9 }],
        },
        {
          message:
            'The field `QueryRoot.withoutDescriptionAgain` is missing a description.',
          locations: [{ line: 4, column: 9 }],
        },
      ]
    );
  });
});

Use test helpers in FieldsHaveDescriptions tests


import buble from 'rollup-plugin-buble';

export default {
  input: "src/codemirror.js",
  output: {
    banner: `// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE

// This is CodeMirror (https://codemirror.net), a code editor
// implemented in JavaScript on top of the browser's DOM.
//
// You can find some technical background for some of the code below
// at http://marijnhaverbeke.nl/blog/#cm-internals .
  `,
    format: "umd",
    file: "lib/codemirror.js",
    name: "CodeMirror"
  },
  plugins: [ buble({namedFunctionExpressions: false}) ]
};

Fix trailing whitespace in build output


const glob = require(`glob`);

const buildArticleHtml = require(`./build/article-html.js`);
const buildBaseCss = require(`./build/base-css.js`);
const buildBaseHtml = require(`./build/base-html.js`);
const extractArticleData = require(`./lib/extract-article-data.js`);

const articleFiles = glob.sync(`resources/articles/*.md`).reverse();

const defaultData = {
  css: `<link rel="stylesheet" href="/base/css/global.css">`,
  articles: [],
};

articleFiles.forEach((fileName) => {
  const data = JSON.parse(JSON.stringify(defaultData));
  Object.assign(data, extractArticleData(fileName));
  defaultData.articles.push(data);

  buildArticleHtml(fileName, data);
});

buildBaseHtml(defaultData);
buildBaseCss();

const glob = require(`glob`);

const buildArticleHtml = require(`./build/article-html.js`);
const buildBaseCss = require(`./build/base-css.js`);
const buildBaseHtml = require(`./build/base-html.js`);
const extractArticleData = require(`./lib/extract-article-data.js`);

const articleFiles = glob.sync(`resources/articles/*.md`).reverse();

const defaultData = {
  css: `<link rel="stylesheet" href="/base/css/global.css">`,
  articles: [],
};

articleFiles.forEach((fileName) => {
  const articleData = Object.assign({}, defaultData, extractArticleData(fileName));
  defaultData.articles.push(articleData);

  buildArticleHtml(fileName, articleData);
});

buildBaseHtml(defaultData);
buildBaseCss();

Use Object.assign to dereference object.


const FS = require('fs');
const Child = require('child_process').execSync;

// Copy local package files
Child('npm run clean');
Child('npm run compile');
Child('npm run compile:module');
Child('npm run compile:rollup');
Child('npm run compile:rollup-browser');
Child('mkdir -p dist/npm');
Child('cp -r dist/cjs/* dist/npm/');
Child('cp dist/es/index.module.js dist/npm/module.js');
Child('cp dist/es/index-browser.module.js dist/npm/browser-module.js');
Child(`cp ${__dirname}/../*.md dist/npm`);
Child('npm run compile:webpack');

// Create package.json file
const Pkg = JSON.parse(FS.readFileSync('package.json'));
FS.writeFileSync(
    'dist/npm/package.json',
    JSON.stringify(
        {
            ...Pkg,
            browser: {
                ...Pkg.browser,
                './module.js': './browser-module.js'
            },
            devDependencies: undefined,
            'jsnext:main': './module.js',
            main: './index.js',
            module: './module.js',
            private: false,
            scripts: undefined,
            sideEffects: false,
            typings: './index'
        },
        null,
        2
    )
);

// Create package bundle
Child('cd dist/npm && npm pack');
Child(`mv dist/npm/*.tgz ${__dirname}/../dist/${Pkg.name}.tgz`);

const FS = require('fs');
const Child = require('child_process').execSync;

// Copy local package files
Child('npm run clean');
Child('npm run compile');
Child('npm run compile:module');
Child('npm run compile:rollup');
Child('npm run compile:rollup-browser');
Child('mkdir dist/npm');
Child('cp -r dist/cjs/* dist/npm/');
Child('cp dist/es/index.module.js dist/npm/module.js');
Child('cp dist/es/index-browser.module.js dist/npm/browser-module.js');
Child(`cp ${__dirname}/../*.md dist/npm`);
Child('npm run compile:webpack');

// Create package.json file
const Pkg = JSON.parse(FS.readFileSync('package.json'));
FS.writeFileSync(
    'dist/npm/package.json',
    JSON.stringify(
        {
            ...Pkg,
            browser: {
                ...Pkg.browser,
                './module.js': './browser-module.js'
            },
            devDependencies: undefined,
            'jsnext:main': './module.js',
            main: './index.js',
            module: './module.js',
            private: false,
            scripts: undefined,
            sideEffects: false,
            typings: './index'
        },
        null,
        2
    )
);

// Create package bundle
Child('cd dist/npm && npm pack');
Child(`mv dist/npm/*.tgz ${__dirname}/../dist/${Pkg.name}.tgz`);

Remove unneeded -p from mkdir call


export function toggleHelpMenu () {
  // TODO
}

export function toggleSaka () {
  try {
    browser.runtime.sendMessage(
      'nbdfpcokndmapcollfpjdpjlabnibjdi',
      'toggleSaka'
    )
  } catch (e) {
    console.error(
      'Install Saka at https://chrome.google.com/webstore/detail/saka/nbdfpcokndmapcollfpjdpjlabnibjdi'
    )
  }
}
export function passOneKey (event) {
  // preventDefault() to suppress keypress event
  // no way to suppress keyup event, so Pass mode must ignore
  // first keyup event
  event.preventDefault()
  event.passKeyType = 'one'
  return 'Pass'
}

export function passAllKeys (event) {
  event.preventDefault()
  event.passKeyType = 'all'
  return 'Pass'
}

export function toggleHelpMenu () {
  // TODO
}

export function toggleSaka () {
  try {
    const extensionId =
      SAKA_PLATFORM === 'chrome'
        ? 'nbdfpcokndmapcollfpjdpjlabnibjdi'
        : '{7d7cad35-2182-4457-972d-5a41a2051240}'
    console.log('ID: ', extensionId)
    browser.runtime.sendMessage(extensionId, 'toggleSaka')
  } catch (e) {
    console.error(
      'Install Saka at https://chrome.google.com/webstore/detail/saka/nbdfpcokndmapcollfpjdpjlabnibjdi'
    )
  }
}
export function passOneKey (event) {
  // preventDefault() to suppress keypress event
  // no way to suppress keyup event, so Pass mode must ignore
  // first keyup event
  event.preventDefault()
  event.passKeyType = 'one'
  return 'Pass'
}

export function passAllKeys (event) {
  event.preventDefault()
  event.passKeyType = 'all'
  return 'Pass'
}

Fix for opening Saka in chrome and firefox


import md from 'components/md'

const WhenToUseAttrs = () => md`
  ## When to use attrs?

  You can pass in attributes to styled components using [attrs](/docs/basics#attaching-additional-props), but
  it is not always sensible to do so.

  The rule of thumb is to use \`attrs\` when you want every instance of a styled
  component to have that prop, and pass props directly when every instance needs a
  different one:

  \`\`\`js
  const PasswordInput = styled.input.attrs({
    // Every <PasswordInput /> should be type="password"
    type: 'password'
  })\`\`

  // This specific one is hidden, so let's set aria-hidden
  <PasswordInput aria-hidden="true" />
  \`\`\`
`

export default WhenToUseAttrs

import md from 'components/md'

const WhenToUseAttrs = () => md`
  ## When to use attrs?

  You can pass in attributes to styled components using [attrs](/docs/basics#attaching-additional-props), but
  it is not always sensible to do so.

  The rule of thumb is to use \`attrs\` when you want every instance of a styled
  component to have that prop, and pass props directly when every instance needs a
  different one:

  \`\`\`js
  const PasswordInput = styled.input.attrs({
    // Every <PasswordInput /> should be type="password"
    type: 'password'
  })\`\`

  // This specific one is hidden, so let's set aria-hidden
  <PasswordInput aria-hidden="true" />
  \`\`\`

  The same goes for props that can be inferred based on the "mode" of another prop.
  In this case you can set a property on \`attrs\` to a function that computes
  that prop based on other props.
`

export default WhenToUseAttrs

Add a paragraph on computed props


import {
  createMessage,
} from '../api/messaging';

// Usage, assuming using Fluorine
// dispatch(executeCell(id,source)(channels))

export default function kernelInfo(channels) {
  if (!channels || !channels.shell) {
    throw new Error('shell channel not available for sending kernel info request');
  }
  const { shell } = channels;

  const message = createMessage('kernel_info_request');
  return shell
    .childOf(message)
    .map(msg => msg.content)
    .first()
    .toPromse();
}

import {
  createMessage,
} from '../api/messaging';

export default function kernelInfo(channels) {
  if (!channels || !channels.shell) {
    throw new Error('shell channel not available for sending kernel info request');
  }
  const { shell } = channels;

  const message = createMessage('kernel_info_request');
  return shell
    .childOf(message)
    .map(msg => msg.content)
    .first()
    .toPromse();
}

Move copy pasta'ed comment out.


function insert_version_links() {
    var labels = ['dev', '0.7', '0.6', '0.5', '0.4', '0.3'];

    for (i = 0; i < labels.length; i++){
        open_list = '
'
        if (typeof(DOCUMENTATION_OPTIONS) !== 'undefined') {
            if ((DOCUMENTATION_OPTIONS['VERSION'] == labels[i]) ||
                (DOCUMENTATION_OPTIONS['VERSION'].match(/dev$/) && (i == 0))) {
                open_list = '
'
            }
        }
        document.write(open_list);
        document.write('skimage VERSION
\n'
                        .replace('VERSION', labels[i])
                        .replace('URL', 'http://skimage.org/docs/' + labels[i]));
    }
}

function insert_version_links() {
    var labels = ['dev', '0.7.0', '0.6', '0.5', '0.4', '0.3'];

    for (i = 0; i < labels.length; i++){
        open_list = '
'
        if (typeof(DOCUMENTATION_OPTIONS) !== 'undefined') {
            if ((DOCUMENTATION_OPTIONS['VERSION'] == labels[i]) ||
                (DOCUMENTATION_OPTIONS['VERSION'].match(/dev$/) && (i == 0))) {
                open_list = '
'
            }
        }
        document.write(open_list);
        document.write('skimage VERSION
\n'
                        .replace('VERSION', labels[i])
                        .replace('URL', 'http://skimage.org/docs/' + labels[i]));
    }
}

Fix doc version 0.7 link


const { JSDOM } = require('jsdom');

function getBrowserGlobals(html) {
  const dom = new JSDOM(html);
  const { window } = dom;
  const noop = function noop() {};

  const EventSource = noop;

  const navigator = {
    userAgent: 'loki',
  };

  let localStorageData = {};
  const localStorage = {
    setItem(id, val) {
      localStorageData[id] = String(val);
    },
    getItem(id) {
      return localStorageData[id] ? localStorageData[id] : undefined;
    },
    removeItem(id) {
      delete localStorageData[id];
    },
    clear() {
      localStorageData = {};
    },
  };

  const matchMedia = () => ({ matches: true });

  window.EventSource = EventSource;

  const globals = Object.assign({}, window, {
    navigator,
    localStorage,
    matchMedia,
    EventSource,
    setInterval: noop,
    console: {
      log: noop,
      warn: noop,
      error: noop,
    },
  });
  globals.global = globals;

  return globals;
}

module.exports = getBrowserGlobals;

const { JSDOM } = require('jsdom');

function getBrowserGlobals(html) {
  const dom = new JSDOM(html);
  const { window } = dom;
  const noop = function noop() {};

  const EventSource = noop;

  const navigator = {
    userAgent: 'loki',
  };

  let localStorageData = {};
  const localStorage = {
    setItem(id, val) {
      localStorageData[id] = String(val);
    },
    getItem(id) {
      return localStorageData[id] ? localStorageData[id] : undefined;
    },
    removeItem(id) {
      delete localStorageData[id];
    },
    clear() {
      localStorageData = {};
    },
  };

  const matchMedia = () => ({ matches: true });

  const globals = Object.assign({}, window, {
    navigator,
    localStorage,
    matchMedia,
    EventSource,
    requestAnimationFrame: noop,
    requestIdleCallback: noop,
    console: {
      log: noop,
      warn: noop,
      error: noop,
    },
  });
  globals.window = globals;
  globals.global = globals;

  return globals;
}

module.exports = getBrowserGlobals;

Add rAF and rIC to jsdom browser globals


/**
	_moon.Button_ is an enyo.Button with Moonstone
	styling applied. The color of the button may be customized by specifying a
	background color.

	For more information, see the documentation on
	Buttons in the
	Enyo Developer Guide.
*/

enyo.kind({
	name		: 'moon.Button',
	kind		: 'enyo.Button',
	classes		: 'moon-button enyo-unselectable',
	spotlight	: true,

	handlers: {
		onSpotlightSelect	: 'depress',
		onSpotlightKeyUp	: 'undepress'
	},

	depress: function() {
		this.addClass('pressed');
	},

	undepress: function() {
		this.removeClass('pressed');
	}
});

/**
	_moon.Button_ is an enyo.Button with Moonstone
	styling applied. The color of the button may be customized by specifying a
	background color.

	For more information, see the documentation on
	Buttons in the
	Enyo Developer Guide.
*/

enyo.kind({
	name		: 'moon.Button',
	kind		: 'enyo.Button',
	publiched	: {
		small	: false,
	},
	classes		: 'moon-button enyo-unselectable',
	spotlight	: true,

	handlers: {
		onSpotlightSelect	: 'depress',
		onSpotlightKeyUp	: 'undepress'
	},

	depress: function() {
		this.addClass('pressed');
	},

	undepress: function() {
		this.removeClass('pressed');
	},

	smallChanged: function() {
		this.addRemoveClass(this.small, 'small');
	}
});

Add small property and changed method


// DOM Manipulation Challenge

// I worked on this challenge [by myself, with: ].

// Add your JavaScript calls to this page:

// Release 0:

// Release 1:

// Release 2:

// Release 3:

// Release 4:

// Release 5:

// DOM Manipulation Challenge

// I worked on this challenge [by myself, with: ].

// Add your JavaScript calls to this page:

// Release 0:

// Release 1:

document.getElementById("release-0").className = 'done';

// Release 2:

document.getElementById("release-1").style.display = "none";

// Release 3:

var h1 = document.getElementsByTagName("h1")[0].innerHTML = "I completed Release 2";

// Release 4:

document.getElementById("release-3").style.backgroundColor = "#955251";

// Release 5:

var release_4 = document.getElementsByClassName("release-4");
for (var i; i < release_4.length; i++) {
  release_4[i].style.fontSize = "2em";
}

// Relase 6:

var hidden = document.getElementById("hidden");
document.body.appendChild(hidden.content.cloneNode(true));

Complete first pass of dom-manipulation


import React from 'react';
import Enzyme, {mount} from 'enzyme';
import {ProcessAttachments} from '../components/ProcessAttachments'; //import redux-independent component
import Adapter from '@wojtekmaj/enzyme-adapter-react-17';

jest.mock('../containers/theme');

describe("ProcessAttachments suite", () => {
  it("should render with no problems", () => {
    Enzyme.configure({ adapter: new Adapter() });

    //given
    const attachments = [processAttachment(3), processAttachment(2), processAttachment(1)]
    const processId = "proc1"
    const processVersionId = 1

    //when
    const mountedProcessAttachments = mount(
      <ProcessAttachments attachments={attachments} processId={processId} processVersionId={processVersionId} t={key => key}/>
    )
    //then
    expect(mountedProcessAttachments.find('.download-attachment').length).toBe(3)
  })

  const processAttachment = (id) => {
    return {
      id: `${id}`,
      processId: "proc1",
      processVersionId: 1,
      createDate: "2016-10-10T12:39:44.092",
      user: "TouK",
      fileName: `file ${id}`
    }
  }

});

import React from 'react';
import Enzyme, {mount} from 'enzyme';
import {ProcessAttachments} from '../components/ProcessAttachments'; //import redux-independent component
import Adapter from '@wojtekmaj/enzyme-adapter-react-17';

jest.mock('../containers/theme');

describe("ProcessAttachments suite", () => {
  it("should render with no problems", () => {
    Enzyme.configure({ adapter: new Adapter() });

    //given
    const attachments = [processAttachment(3), processAttachment(2), processAttachment(1)]
    const processId = "proc1"
    const processVersionId = 1

    //when
    const mountedProcessAttachments = mount(
      <ProcessAttachments attachments={attachments} processId={processId} processVersionId={processVersionId} t={key => key} capabilities={{write: true}}/>
    )
    //then
    expect(mountedProcessAttachments.find('.download-attachment').length).toBe(3)
  })

  const processAttachment = (id) => {
    return {
      id: `${id}`,
      processId: "proc1",
      processVersionId: 1,
      createDate: "2016-10-10T12:39:44.092",
      user: "TouK",
      fileName: `file ${id}`
    }
  }

});

Fix for: comments and attachments adding only available for user with write permission - test fix


/* global assert */
module.exports = function(runner, options) {
    var called = {};

    runner.on('setup', function (next) {
        console.log('called "setup"');
        called.setup = true;
        next(null);
    });
    runner.on('new client', function (client, next) {
        console.log('called "new client"');
        called.newClient = true;
        next(null);
    });
    runner.on('report', function (failures, next) {
        console.log('called "report"');
        called.report = true;
        next(null, []);
    });
    runner.on('done', function (failures, next) {
        console.log('called "done"');
        assert(called.setup, 'should have called "setup" event');
        assert(called.newClient, 'should have called "new client" event');
        assert(called.report, 'should have called "report" event');
        console.log('did call all events');
    });
};

/* global assert */
module.exports = function(runner, options) {
    var called = {};

    runner.on('setup', function (next) {
        console.log('called "setup"');
        called.setup = true;
        next(null);
    });
    runner.on('new client', function (client, next) {
        console.log('called "new client"');
        called.newClient = true;
        next(null);
    });
    runner.on('report', function (failures, next) {
        console.log('called "report"');
        called.report = true;
        next(null, []);
    });
    runner.on('done', function (failures, next) {
        console.log('called "done"');
        assert(called.setup, 'should have called "setup" event');
        assert(called.newClient, 'should have called "new client" event');
        assert(called.report, 'should have called "report" event');
        console.log('did call all events');
        next();
    });
};

Fix sample event listener should call next


import PropTypes from 'proptypes'

export default {
  any: {
    options: {
      of: PropTypes.array.isRequired
    },
    parse(options, typeBuilders, schema) {
      const containsTypes = options.of.map(typeDef => {
        const typeBuilder = typeBuilders[typeDef.type]
        if (!typeBuilder) {
          throw new Error(`Invalid type: ${typeDef.type}.`)
        }
        return typeBuilder(typeDef, typeBuilders, schema)
      })
      return {of: containsTypes}
    }
  },
  reference: {
    primitive: 'string',
    options: {
      title: PropTypes.string,
      to: PropTypes.oneOfType([
        PropTypes.object,
        PropTypes.array
      ]).isRequired
    },
    parse(options, typeBuilders, schema) {
      const toTypeDefs = Array.isArray(options.to) ? options.to : [options.to]
      const toTypes = toTypeDefs.map(typeDef => {
        const typeBuilder = typeBuilders[typeDef.type]
        return typeBuilder(typeDef, typeBuilders, schema)
      })
      return {to: toTypes}
    }
  },
  text: {
    primitive: 'string',
    options: {
      title: PropTypes.string,
      format: PropTypes.oneOf(['markdown', 'html', 'plain']),
      maxLength: PropTypes.number
    },
    defaultOptions: {
      format: 'plain'
    }
  }
}

import PropTypes from 'proptypes'

export default {
  any: {
    options: {
      of: PropTypes.array.isRequired
    },
    parse(options, typeBuilders, schema) {
      const containsTypes = options.of.map(typeDef => {
        const typeBuilder = typeBuilders[typeDef.type]
        if (!typeBuilder) {
          throw new Error(`Invalid type: ${typeDef.type}.`)
        }
        return typeBuilder(typeDef, typeBuilders, schema)
      })
      return {of: containsTypes}
    }
  },
  reference: {
    primitive: 'string',
    options: {
      title: PropTypes.string,
      to: PropTypes.oneOfType([
        PropTypes.object,
        PropTypes.array
      ]).isRequired
    },
    parse(options, typeBuilders, schema) {
      const toTypeDefs = Array.isArray(options.to) ? options.to : [options.to]
      const toTypes = toTypeDefs.map(typeDef => {
        const typeBuilder = typeBuilders[typeDef.type]
        if (!typeBuilder) {
          throw new Error(
            `Missing type builder for ${typeDef.type}. Did you forget to declare the type "${typeDef.type}" in the schema?`
          )
        }
        return typeBuilder(typeDef, typeBuilders, schema)
      })
      return {to: toTypes}
    }
  },
  text: {
    primitive: 'string',
    options: {
      title: PropTypes.string,
      format: PropTypes.oneOf(['markdown', 'html', 'plain']),
      maxLength: PropTypes.number
    },
    defaultOptions: {
      format: 'plain'
    }
  }
}

Improve error message when encountering missing type builders


// ==UserScript==
// @name         SFcomic Viewer
// @namespace
// @version      0.2.1
// @description  Auto load comic picture.
// @author       AntonioTsai
// @match        http://comic.sfacg.com/*
// @include      http://comic.sfacg.com/*
// @grant        none
// @downloadURL  https://github.com/AntonioTsai/SFcomic-Viewer/raw/master/SFcomicViewer.user.js
// ==/UserScript==

(() => {
	var imgtable = document.getElementsByTagName("table")[0].getElementsByTagName("tbody")[0];
	var tempTr = document.createElement("tr");
	var imgs = document.createElement("img");
	tempTr.appendChild(document.createElement("td"));
	tempTr.children[0].appendChild(imgs);

	// remove original image
	var tr = document.querySelector("tr");
	tr.parentElement.removeChild(tr);

	// generate all images
	for (var i = 0; i < picCount; i++) {
		var imgTr = tempTr.cloneNode(true);
		imgTr.appendChild(document.createElement("img"));
		imgTr.children[0].children[0].src = picAy[i];
		imgtable.appendChild(imgTr);
	}

})();

// ==UserScript==
// @name         SFcomic Viewer
// @namespace
// @version      0.2.1
// @description  Auto load comic picture.
// @author       AntonioTsai
// @match        http://comic.sfacg.com/*
// @include      http://comic.sfacg.com/*
// @grant        none
// @downloadURL  https://github.com/AntonioTsai/SFcomic-Viewer/raw/master/SFcomicViewer.user.js
// ==/UserScript==

/**
 * picAy: The array store all image's url of the volume
 * hosts: The array store possible host
 * getHost(): Get the current host index
 */
(() => {
	var imgtable = document.getElementsByTagName("table")[0].getElementsByTagName("tbody")[0];
	var tempTr = document.createElement("tr");
	var imgs = document.createElement("img");
	tempTr.appendChild(document.createElement("td"));
	tempTr.children[0].appendChild(imgs);

	// remove original image
	var tr = document.querySelector("tr");
	tr.parentElement.removeChild(tr);

	// generate all images
	for (var i = 0; i < picCount; i++) {
		var imgTr = tempTr.cloneNode(true);
		imgTr.appendChild(document.createElement("img"));
		imgTr.children[0].children[0].src = picAy[i];
		imgtable.appendChild(imgTr);
	}

})();

Add comment how to use variable from the site


import { createAction } from 'redux-actions';
import { createThunkAction } from 'utils/redux';
import qs from 'query-string';

const fetchGhgEmissionsInit = createAction('fetchGhgEmissionsInit');
const fetchGhgEmissionsFail = createAction('fetchGhgEmissionsFail');

const fetchGhgEmissionsDataReady = createAction('fetchGhgEmissionsDataReady');
const fetchGhgEmissionsData = createThunkAction(
  'fetchGhgEmissionsData',
  filters => dispatch => {
    dispatch(fetchGhgEmissionsInit());
    fetch(`/api/v1/emissions?${qs.stringify(filters)}`)
      .then(response => {
        if (response.ok) return response.json();
        throw Error(response.statusText);
      })
      .then(data => {
        dispatch(fetchGhgEmissionsDataReady(data));
      })
      .catch(error => {
        console.warn(error);
        dispatch(fetchGhgEmissionsFail());
      });
  }
);

export default {
  fetchGhgEmissionsInit,
  fetchGhgEmissionsFail,
  fetchGhgEmissionsData,
  fetchGhgEmissionsDataReady
};

import { createAction } from 'redux-actions';
import { createThunkAction } from 'utils/redux';
import qs from 'query-string';

const fetchGhgEmissionsInit = createAction('fetchGhgEmissionsInit');
const fetchGhgEmissionsFail = createAction('fetchGhgEmissionsFail');

const fetchGhgEmissionsDataReady = createAction('fetchGhgEmissionsDataReady');
const fetchGhgEmissionsData = createThunkAction(
  'fetchGhgEmissionsData',
  filters => dispatch => {
    dispatch(fetchGhgEmissionsInit());
    fetch(`/api/v1/emissions?${qs.stringify(filters)}`)
      .then(response => {
        if (response.ok) return response.json();
        throw Error(response.statusText);
      })
      .then(data => {
        const parsedData = data.map(d => ({
          ...d,
          gas: d.gas.trim(),
          sector: d.sector.trim(),
          location: d.location.trim()
        }));
        dispatch(fetchGhgEmissionsDataReady(parsedData));
      })
      .catch(error => {
        console.warn(error);
        dispatch(fetchGhgEmissionsFail());
      });
  }
);

export default {
  fetchGhgEmissionsInit,
  fetchGhgEmissionsFail,
  fetchGhgEmissionsData,
  fetchGhgEmissionsDataReady
};

Trim fetch data to remove errors with extra spaces in data


'use strict';

angular.module('arachne.widgets.directives')

.directive('con10tSearchQuery', function() {
return {
    restrict: 'A',
    link: function(scope, element, attrs) {

        attrs.$observe('con10tSearchQuery', function(value) {
            scope.q = value;
            updateHref();
        });

        attrs.$observe('con10tSearchFacet', function(value) {
            scope.fq = value;
            updateHref();
        });

        function updateHref() {
            var href = "http://arachne.dainst.org/search?q=" + scope.q;
            if (scope.fq) {
                var fqs = scope.fq.split(',');
                fqs.forEach(function(fq) {
                    var split = fq.split(':');
                    href += '&fq='+split[0]+':"'+split[1]+'"';
                });
            }
            element.attr("href", href);
        }

    }
}});

'use strict';

angular.module('arachne.widgets.directives')

.directive('con10tSearchQuery', function() {
return {
    restrict: 'A',
    link: function(scope, element, attrs) {

        attrs.$observe('con10tSearchQuery', function(value) {
            scope.q = value;
            updateHref();
        });

        attrs.$observe('con10tSearchFacet', function(value) {
            scope.fq = value;
            updateHref();
        });

        function updateHref() {
            var href = "http://arachne.dainst.org/search?q=" + scope.q;
            if (scope.fq) {
                var split, fqs = scope.fq.split(',');
                fqs.forEach(function(fq) {
                    split = fq.split(':');
                    href += '&fq='+split[0]+':"'+split[1]+'"';
                });
            }
            element.attr("href", href);
        }

    }
}});

Move variable declaration out of loop.


introJs()
  .start()
  .onexit(function() {
    window.location = "../../newtab/newtab.html";
  })
  .oncomplete(function() {
    window.location = "../../newtab/newtab.html";
  });

introJs()
  .start()
  .onchange(function(targetElement)  {
    switch(this._currentStep) {
      case 1:
        break;
      case 2:
        break;
      default:
        break;
    }
  })
  .onexit(function() {
    window.location = "../../newtab/newtab.html";
  })
  .oncomplete(function() {
    window.location = "../../newtab/newtab.html";
  });

Make framework for running JS between steps


var Invoice = DS.Model.extend({

  tax: DS.attr('number'),
  invoice_number: DS.attr('number', {default: new Date()}),
  issued: DS.attr('date'),
  items: DS.hasMany('InvoiceItem', {inverse: 'invoice'}),
  customer: DS.belongsTo('Company'),
  issuer: DS.belongsTo('Company'),

  sub_total: function(){
    return this.get('items').getEach('total').reduce(function(sum, partial) {
      return sum + partial;
    }, 0);
  }.property("items.@each.total"),

  tax_total: function(){
    return this.get("sub_total")/100.0*this.get("tax");
  }.property("sub_total"),

  total: function(){
    return this.get("sub_total")+this.get("tax_total");
  }.property("sub_total", "tax_total")
});

export default Invoice;

var Invoice = DS.Model.extend({

  tax: DS.attr('number'),
  invoice_number: DS.attr('number', {default: new Date()}),
  issued: DS.attr('date'),
  items: DS.hasMany('InvoiceItem', {inverse: 'invoice'}),
  customer: DS.belongsTo('Company'),
  issuer: DS.belongsTo('Company'),

  sub_total: function(){
    return this.get('items').getEach('total').reduce(function(sum, partial) {
      return sum + partial;
    }, 0);
  }.property("items.@each.total"),

  tax_total: function(){
    return this.get("sub_total")/100.0*this.get("tax");
  }.property("sub_total", "tax"),

  total: function(){
    return this.get("sub_total")+this.get("tax_total");
  }.property("sub_total", "tax_total")
});

export default Invoice;

Add tax to tax_total property in Invoice model


(function () {

  document.addEventListener('mousedown', function (event) {
    if (event.target && event.target.className.indexOf('ripple') !== -1) {
      let $div = document.createElement('div'),
        btnRect = event.target.getBoundingClientRect(),
        height = event.target.clientHeight,
        xPos = event.pageX - (btnRect.left + window.pageXOffset),
        yPos = event.pageY - (btnRect.top + window.pageYOffset);

      $div.className = 'ripple-effect';
      $div.style.height = `${height}px`;
      //noinspection JSSuspiciousNameCombination
      $div.style.width = `${height}px`;
      $div.style.top = `${yPos - (height / 2)}px`;
      $div.style.left = `${xPos - (height / 2)}px`;

      event.target.appendChild($div);

      window.setTimeout(() => event.target.removeChild($div), 2000);
    }
  });

})();

(function () {

  document.addEventListener('mousedown', function (event) {
    if (event.target &&
      event.target.className &&
      event.target.className.indexOf &&
      event.target.className.indexOf('ripple') !== -1) {
      let $div = document.createElement('div'),
        btnRect = event.target.getBoundingClientRect(),
        height = event.target.clientHeight,
        xPos = event.pageX - (btnRect.left + window.pageXOffset),
        yPos = event.pageY - (btnRect.top + window.pageYOffset);

      $div.className = 'ripple-effect';
      $div.style.height = `${height}px`;
      //noinspection JSSuspiciousNameCombination
      $div.style.width = `${height}px`;
      $div.style.top = `${yPos - (height / 2)}px`;
      $div.style.left = `${xPos - (height / 2)}px`;

      event.target.appendChild($div);

      window.setTimeout(() => event.target.removeChild($div), 2000);
    }
  });

})();

Fix for error when clicking on browser market share pie charts


"use strict";

const webpack = require("webpack");
const path = require("path");
const ExtractTextPlugin = require("extract-text-webpack-plugin");

module.exports = {
    context: path.resolve("./src"),

    entry: {
        vendor: [ "jquery" ],
        nm: [ "./nm/index.js", "./nm/resource/index.less" ]
    },

    output: {
        path: path.resolve("./assets"),
        publicPath: "/assets",
        filename: "[name]/bundle.js"
    },

    module: {
        loaders: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: "babel-loader"
            },
            {
                test: /\.less$/,
                loader: ExtractTextPlugin.extract("style-loader", "css-loader!less-loader")
            }
        ]
    },

    plugins: [
        new webpack.ProvidePlugin({
            "$": "jquery",
            "jQuery": "jquery"
        }),
        new webpack.optimize.CommonsChunkPlugin({
            name: "vendor",
            filename: "vendor.js",
            minChunks: Infinity
        }),
        new ExtractTextPlugin("./[name]/resource/bundle.css")
    ],

    devServer:
    {
        proxy: {
            "/api/*": {
                target: "http://music.163.com/",
                host: "music.163.com",
                secure: false
            }
        }
    }
};

"use strict";

const webpack = require("webpack");
const path = require("path");
const ExtractTextPlugin = require("extract-text-webpack-plugin");

module.exports = {
    context: path.resolve("./src"),

    entry: {
        vendor: [ "jquery" ],
        nm: [ "./nm/index.js", "./nm/resource/index.less" ]
    },

    output: {
        path: path.resolve("./assets"),
        publicPath: "/assets",
        filename: "[name]/bundle.js"
    },

    module: {
        loaders: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: "babel-loader"
            },
            {
                test: /\.less$/,
                loader: ExtractTextPlugin.extract("style-loader", "css-loader!less-loader")
            }
        ]
    },

    plugins: [
        new webpack.ProvidePlugin({
            "$": "jquery",
            "jQuery": "jquery"
        }),
        new webpack.optimize.CommonsChunkPlugin({
            name: "vendor",
            filename: "vendor.js",
            minChunks: Infinity
        }),
        new ExtractTextPlugin("./[name]/resource/bundle.css")
    ],

    devServer:
    {
        proxy: {
            "/api/*": {
                target: "http://music.163.com/",
                host: "music.163.com",
                secure: false,
                headers: {
                    "Referer": "http://music.163.com"
                }
            }
        }
    }
};

Add Referer to proxy server.


var webpack = require('webpack');
var path = require('path');

module.exports = {
	context: __dirname + "/app",
	entry: "./index.js",

	output: {
		path: __dirname + '/bin',
		publicPath: "/",
		filename: 'bundle.js',
	},

	stats: {
		colors: true,
		progress: true,
	},

	resolve: {
		extensions: ['', '.webpack.js', '.js'],
	},

	module: {
		loaders: [
			{
				test: /\.js$/,
				exclude: /node_modules/,
				query: {
					presets: ['es2015'],
				},
				loader: 'babel',
			},
		],
	},
};

var webpack = require('webpack');
var path = require('path');

module.exports = {
  context: __dirname + '/app',
  entry: './index.js',

  output: {
    path: __dirname + '/bin',
    publicPath: '/',
    filename: 'bundle.js',
  },

  stats: {
    colors: true,
    progress: true,
  },

  resolve: {
    extensions: ['', '.webpack.js', '.js'],
  },

  module: {
    loaders: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        query: {
          presets: ['es2015'],
        },
        loader: 'babel',
      },
    ],
  },
};

Update web pack to using spaces