import { createAction } from 'redux-act'

import { CMDS } from 'src/options/imports/constants'
import { IMPORT_CONN_NAME } from './constants'

export const setVisible = createAction('onboarding/setVisible')

export const init = () => dispatch =>
    new ImportsConnHandler(IMPORT_CONN_NAME, dispatch)

/**
 * Background script connection state handler, which sets up the connection and dispatches
 * specific redux actions for specific commands sent from the background script along the connection.
 *
 * @class ImportsConnHandler
 */
class ImportsConnHandler {
    constructor(connName, dispatch) {
        this._port = browser.runtime.connect({ name: connName })
        this._dispatch = dispatch

        this._port.onMessage.addListener(this.handleCmds)
    }

    /**
     * Responds to messages sent from background script over the runtime connection by dispatching
     * appropriate redux actions. Non-handled messages are ignored.
    */
    handleCmds = ({ cmd, ...payload }) => {
        switch (cmd) {
            case CMDS.INIT:
            case CMDS.NEXT:
            case CMDS.COMPLETE:
            default:
                console.log(cmd, payload)
        }
    }
}

import { createAction } from 'redux-act'

import { IMPORT_TYPE as TYPE, CMDS } from 'src/options/imports/constants'
import { IMPORT_CONN_NAME } from './constants'

export const setVisible = createAction('onboarding/setVisible')

export const init = () => dispatch =>
    new ImportsConnHandler(IMPORT_CONN_NAME, dispatch)

/**
 * Background script connection state handler, which sets up the connection and dispatches
 * specific redux actions for specific commands sent from the background script along the connection.
 *
 * @class ImportsConnHandler
 */
class ImportsConnHandler {
    static ONBOARDING_ALLOW_TYPES = {
        [TYPE.HISTORY]: true,
        [TYPE.BOOKMARK]: false,
        [TYPE.OLD]: false,
    }

    constructor(connName, dispatch) {
        this._port = browser.runtime.connect({ name: connName })
        this._dispatch = dispatch

        this._port.onMessage.addListener(this.handleCmds)
    }

    /**
     * Responds to messages sent from background script over the runtime connection by dispatching
     * appropriate redux actions. Non-handled messages are ignored.
    */
    handleCmds = ({ cmd, ...payload }) => {
        switch (cmd) {
            case CMDS.INIT:
                return this._port.postMessage({
                    cmd: CMDS.START,
                    payload: ImportsConnHandler.ONBOARDING_ALLOW_TYPES,
                })
            case CMDS.NEXT:
            case CMDS.COMPLETE:
            default:
                console.log(cmd, payload)
        }
    }
}

Set up autostart of onboarding imports when ready


if (window.location.hostname.substr(0, 5) !== 'smile') {
  window.location.hostname = 'smile.amazon.com'
}

if (window.location.hostname.substr(0, 5) !== 'smile') {
  tmp = window.location.hostname.replace(/^(www\.)?amazon/,"smile.amazon");
  window.location.hostname = tmp
}

Make it usable with international amazon websites


const readline = require('readline');

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

/**
 * 汉诺塔移动函数
 * @param  {Number} levels 本次要移动的层级数
 * @param  {String} from   本次要移动的起始柱
 * @param  {String} cache  本次可以使用的缓存柱
 * @param  {String} dest   本次要移动的目标柱
 * @param  {Array}  steps  步骤暂存数组
 * @return {Array}         需要移动的步骤详情
 */
function hanoi(levels, from = 'left', cache = 'middle', dest = 'right', steps = []) {
  if (levels > 0) {
    steps.push(...hanoi(levels - 1, from, dest, cache));
    steps.push(`${from} => ${dest}`);
    steps.push(...hanoi(levels - 1, cache, from, dest));
  }
  return steps;
}

rl.question('', (levels) => {
  const steps = hanoi(levels);

  console.log(`least: ${steps.length}\nsteps:`);
  steps.forEach(step => console.log(step));

  rl.close();
});

const readline = require('readline');

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

/**
 * 汉诺塔移动函数
 * @param  {Number} levels 本次要移动的层级数
 * @param  {String} from   本次要移动的起始柱
 * @param  {String} cache  本次可以使用的缓存柱
 * @param  {String} dest   本次要移动的目标柱
 * @param  {Array}  steps  步骤暂存数组
 * @return {Array}         需要移动的步骤详情
 */
function hanoi(levels, from = 'left', cache = 'middle', dest = 'right', steps = []) {
  if (levels > 0) {
    hanoi(levels - 1, from, dest, cache, steps);
    steps.push(`${from} => ${dest}`);
    hanoi(levels - 1, cache, from, dest, steps);
  }
  return steps;
}

rl.question('', (levels) => {
  const steps = hanoi(levels);

  console.log(`least: ${steps.length}\nsteps:`);
  steps.forEach(step => console.log(step));

  rl.close();
});

Optimize solution for the chapter 1


(function() {
  'use strict';

  var mod = angular.module('cla.directives');

  mod.directive('callbackStatus', ['AppSettings', '$filter', 'flash', function (AppSettings, filter, flash) {
    return {
      restrict: 'E',
      transclude: true,
      templateUrl: 'directives/callbackStatus.html',
      scope: {
        'case': '='
      },
      link: function (scope, elm) {
        // remove if not enabled
        if (!AppSettings.callMeBackEnabled) {
          elm.remove();
          return;
        }

        var builtInDateFilter = filter('date');

        scope.time = builtInDateFilter(scope.case.getCallbackDatetime(), 'HH:mm \'on\' d MMM yy');

        scope.completeCallback = function() {
          scope.case.$complete_call_me_back().then(function() {
            scope.case.requires_action_at = null;
            scope.case.callback_attempt = 0;

            elm.remove();
            flash('Callback cancelled successfully');
          });
        };

        if (!scope.time) {
          elm.remove();
          return;
        }
      }
    };
  }]);
})();

(function() {
  'use strict';

  var mod = angular.module('cla.directives');

  mod.directive('callbackStatus', ['AppSettings', '$filter', 'flash', 'postal', function (AppSettings, filter, flash, postal) {
    return {
      restrict: 'E',
      transclude: true,
      templateUrl: 'directives/callbackStatus.html',
      scope: {
        'case': '='
      },
      link: function (scope, elm) {
        // remove if not enabled
        if (!AppSettings.callMeBackEnabled) {
          elm.remove();
          return;
        }

        var builtInDateFilter = filter('date');

        scope.time = builtInDateFilter(scope.case.getCallbackDatetime(), 'HH:mm \'on\' d MMM yy');

        scope.completeCallback = function() {
          scope.case.$complete_call_me_back().then(function() {
            scope.case.requires_action_at = null;
            scope.case.callback_attempt = 0;

            elm.remove();
            flash('Callback stopped successfully');
            // refreshing the logs
            postal.publish({
              channel : 'models',
              topic   : 'Log.refresh'
            });
          });
        };

        if (!scope.time) {
          elm.remove();
          return;
        }
      }
    };
  }]);
})();

Refresh logs when callback stopped


Package.describe({
  name: 'blazer:react',
  version: '0.1.1',
  summary: "Blaze add-on: Create stateful components with methods and mixins like in Facebook's React",
  git: 'https://github.com/xamfoo/blazer-react',
  documentation: 'README.md'
});

Package.onUse(function(api) {
  configure(api);
});

Package.onTest(function(api) {
  configure(api);
  api.use('tinytest');
});

function configure (api) {
  api.versionsFrom('1.0.4.1');
  api.use([
    'blaze',
    'reactive-dict',
    'tracker',
    'check',
    'underscore',
    'templating'
  ], 'client');
  api.use('xamfoo:reactive-obj', 'client', {weak: true});

  api.addFiles([
    'lib/blazer.js',
    'lib/component.js',
    'lib/component_static.js',
    'lib/helpers.js',
  ], 'client');

  api.export('Blazer', 'client');
}

Package.describe({
  name: 'blazer:react',
  version: '0.1.1',
  summary: "Blaze add-on: Create stateful components with methods and mixins like in Facebook's React",
  git: 'https://github.com/xamfoo/blazer-react',
  documentation: 'README.md'
});

Package.onUse(function(api) {
  configure(api);
});

Package.onTest(function(api) {
  configure(api);
  api.use('tinytest');
});

function configure (api) {
  api.versionsFrom('1.0.4.1');
  api.use([
    'blaze',
    'reactive-dict',
    'tracker',
    'check',
    'underscore',
    'templating'
  ], 'client');
  api.use('xamfoo:reactive-obj@0.4.0', 'client', {weak: true});

  api.addFiles([
    'lib/blazer.js',
    'lib/component.js',
    'lib/component_static.js',
    'lib/helpers.js',
  ], 'client');

  api.export('Blazer', 'client');
}

Add version constraint for xamfoo:reactive-obj


/*global FormplayerFrontend, Util */

FormplayerFrontend.module("Menus.Collections", function (Collections, FormplayerFrontend, Backbone, Marionette, $) {

    Collections.MenuSelect = Backbone.Collection.extend({

        model: FormplayerFrontend.Menus.Models.MenuSelect,

        commonProperties: [
            'title',
            'type',
            'clearSession',
            'notification',
            'breadcrumbs',
            'appVersion',
            'appId',
            'persistentCaseTile',
        ],

        entityProperties: [
            'action',
            'styles',
            'headers',
            'currentPage',
            'pageCount',
            'titles',
            'numEntitiesPerRow',
            'maxWidth',
            'maxHeight',
        ],

        parse: function (response, request) {
            _.extend(this, _.pick(response, this.commonProperties));

            if (response.commands) {
                return response.commands;
            } else if (response.entities) {
                _.extend(this, _.pick(response, this.entityProperties));
                return response.entities;
            } else if (response.type === "query") {
                return response.displays;
            } else if (response.details) {
                return response.details;
            } else if (response.tree){
                // form entry time, doggy
                FormplayerFrontend.trigger('startForm', response, this.app_id);
            }
        },

        sync: function (method, model, options) {
            Util.setCrossDomainAjaxOptions(options);
            return Backbone.Collection.prototype.sync.call(this, 'create', model, options);
        },
    });
});

/*global FormplayerFrontend, Util */

FormplayerFrontend.module("Menus.Collections", function (Collections, FormplayerFrontend, Backbone, Marionette, $) {

    Collections.MenuSelect = Backbone.Collection.extend({

        model: FormplayerFrontend.Menus.Models.MenuSelect,

        commonProperties: [
            'title',
            'type',
            'clearSession',
            'notification',
            'breadcrumbs',
            'appVersion',
            'appId',
            'persistentCaseTile',
            'tiles',
        ],

        entityProperties: [
            'action',
            'styles',
            'headers',
            'currentPage',
            'pageCount',
            'titles',
            'numEntitiesPerRow',
            'maxWidth',
            'maxHeight',
        ],

        parse: function (response, request) {
            _.extend(this, _.pick(response, this.commonProperties));

            if (response.commands) {
                return response.commands;
            } else if (response.entities) {
                _.extend(this, _.pick(response, this.entityProperties));
                return response.entities;
            } else if (response.type === "query") {
                return response.displays;
            } else if (response.details) {
                return response.details;
            } else if (response.tree){
                // form entry time, doggy
                FormplayerFrontend.trigger('startForm', response, this.app_id);
            }
        },

        sync: function (method, model, options) {
            Util.setCrossDomainAjaxOptions(options);
            return Backbone.Collection.prototype.sync.call(this, 'create', model, options);
        },
    });
});

Include tiles in menu response


#!/usr/bin/env node

var path = require('path');
var fs = require('fs');
var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');

// the following will be replaced with inline json
var RunConfig = require('$RunConfig$');

var config = require(RunConfig.webPackConfig);

config.entry = [ "webpack-dev-server/client?http://localhost:" + RunConfig.port + "/", "webpack/hot/dev-server", config.entry ];

if (!config.plugins) {
    config.plugins = [];
}
config.plugins.push(new webpack.HotModuleReplacementPlugin());

if (!config.devServer) {
    config.devServer = {};
}

config.devServer.inline = true;

var devServer = new WebpackDevServer(
    webpack(config),
    {
        contentBase: RunConfig.contentPath,
        hot: true,
        setup: function(app) {
            app.get(RunConfig.shutDownPath, function(req, res) {
                res.json({ shutdown: 'ok' });
                devServer.close();
            });
        },
        proxy: {
            "**" : (RunConfig.contentPath) ? {
                target: RunConfig.proxyUrl,
                secure: false,
                bypass: function(req, res, proxyOptions) {
                    var file = path.join(RunConfig.contentPath, req.path);
                    if (fs.existsSync(file)) {
                        return req.path;
                    }
                }
            } : null
        }
    }
);
devServer.listen(RunConfig.port, 'localhost');

#!/usr/bin/env node

var path = require('path');
var fs = require('fs');
var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');

// the following will be replaced with inline json
var RunConfig = require('$RunConfig$');

var config = require(RunConfig.webPackConfig);

config.entry = [ "webpack-dev-server/client?http://localhost:" + RunConfig.port + "/", "webpack/hot/dev-server", config.entry ];

if (!config.plugins) {
    config.plugins = [];
}
config.plugins.push(new webpack.HotModuleReplacementPlugin());

if (!config.devServer) {
    config.devServer = {};
}

config.devServer.inline = true;

var devServer = new WebpackDevServer(
    webpack(config),
    {
        contentBase: RunConfig.contentPath,
        hot: true,
        setup: function(app) {
            app.get(RunConfig.shutDownPath, function(req, res) {
                res.json({ shutdown: 'ok' });
                devServer.close();
            });
        },
        proxy: {
            "**" : (RunConfig.contentPath) ? {
                target: RunConfig.proxyUrl,
                secure: false,
                bypass: function(req, res, proxyOptions) {
                    if (RunConfig.contentPath) {
                        var file = path.join(RunConfig.contentPath, req.path);
                        if (fs.existsSync(file)) {
                            return req.path;
                        }
                    }
                }
            } : null
        }
    }
);
devServer.listen(RunConfig.port, 'localhost');

Fix proxy bypass for missing static case


Package.describe({
  name: "typescript",
  version: "3.6.2-rc182.3",
  summary: "Compiler plugin that compiles TypeScript and ECMAScript in .ts and .tsx files",
  documentation: "README.md"
});

Package.registerBuildPlugin({
  name: "compile-typescript",
  use: ["babel-compiler"],
  sources: ["plugin.js"]
});

Package.onUse(function (api) {
  api.use("isobuild:compiler-plugin@1.0.0");
  // The following api.imply calls should match those in
  // ../ecmascript/package.js.
  api.imply("modules");
  api.imply("ecmascript-runtime");
  api.imply("babel-runtime");
  api.imply("promise");
  // Runtime support for Meteor 1.5 dynamic import(...) syntax.
  api.imply("dynamic-import");
});

Package.onTest(function (api) {
  api.use("tinytest");
  api.use("es5-shim");
  api.use("typescript");
  api.mainModule("tests.ts");
});

Package.describe({
  name: "typescript",
  version: "3.6.3-rc182.3",
  summary: "Compiler plugin that compiles TypeScript and ECMAScript in .ts and .tsx files",
  documentation: "README.md"
});

Package.registerBuildPlugin({
  name: "compile-typescript",
  use: ["babel-compiler"],
  sources: ["plugin.js"]
});

Package.onUse(function (api) {
  api.use("isobuild:compiler-plugin@1.0.0");
  // The following api.imply calls should match those in
  // ../ecmascript/package.js.
  api.imply("modules");
  api.imply("ecmascript-runtime");
  api.imply("babel-runtime");
  api.imply("promise");
  // Runtime support for Meteor 1.5 dynamic import(...) syntax.
  api.imply("dynamic-import");
});

Package.onTest(function (api) {
  api.use("tinytest");
  api.use("es5-shim");
  api.use("typescript");
  api.mainModule("tests.ts");
});

Update typescript plugin patch version to match npm version.


'use strict';
var $ = require('jquery');

var pointers = require('js/pointers');
var AccountClaimer = require('js/accountClaimer.js');
var $osf = require('js/osfHelpers');

// NodeActions is needed for rendering recent logs in nodelists (e.g. regsitrations and forks
// pages
require('js/project');

var node = window.contextVars.node;

new pointers.PointerDisplay('#showLinks');

if (!window.contextVars.currentUser.isContributor) {
    new AccountClaimer('.contributor-unregistered');
}

if (node.isPublic && node.piwikSiteID) {
    $osf.trackPiwik(node.piwikHost, node.piwikSiteID);
}

// Works only with anchors with the id of the element that bootstrap uses
// Buffer is the amount to leave on top
function replaceAnchorScroll (buffer){
    buffer = buffer || 100;
    $(document).on('click', 'a[href^="#"]', function(event){
        if(!$(this).attr('data-model') && $(this).attr('href') !== '#') {
            event.preventDefault();
            // get location of the target
            var target = $(this).attr('href'),
                offset = $(target).offset();
            $(window).scrollTop(offset.top-buffer);
        }
    });
}

$(document).ready(function(){
    replaceAnchorScroll();
});

$.getJSON(node.urls.api, function(data) {
    $('body').trigger('nodeLoad', data);
});

'use strict';
var $ = require('jquery');

var pointers = require('js/pointers');
var AccountClaimer = require('js/accountClaimer.js');
var $osf = require('js/osfHelpers');

// NodeActions is needed for rendering recent logs in nodelists (e.g. regsitrations and forks
// pages
require('js/project');

var node = window.contextVars.node;

new pointers.PointerDisplay('#showLinks');

if (!window.contextVars.currentUser.isContributor) {
    new AccountClaimer('.contributor-unregistered');
}

if (node.isPublic && node.piwikSiteID) {
    $osf.trackPiwik(node.piwikHost, node.piwikSiteID);
}

// Works only with anchors with the id of the element that bootstrap uses
// Buffer is the amount to leave on top
function replaceAnchorScroll (buffer){
    buffer = buffer || 100;
    $(document).on('click', 'a[href^="#"]', function(event){
        if(!$(this).attr('data-model') && $(this).attr('href') !== '#') {
            event.preventDefault();
            // get location of the target
            var target = $(this).attr('href'),
                offset = $(target).offset();
            $(window).scrollTop(offset.top-buffer);
        }
    });
}

// Used for clearing backward/forward cache issues
$(window).unload(function(){
    return "Unload";
});
$(document).ready(function(){
    replaceAnchorScroll();
});

$.getJSON(node.urls.api, function(data) {
    $('body').trigger('nodeLoad', data);
});

Add unload event to force browsers not to use bfcache


var gulp = require('gulp');
var gls = require('gulp-live-server');
var bs = require('browser-sync').create();

gulp.task('default', ['server', 'browser-sync'], function() {
});

gulp.task('server', function() {
  var server = gls('server.js');
  server.start();
})

gulp.task('browser-sync', function() {
  bs.init({
    proxy: "http://localhost:1337"
  });
});

var gulp = require('gulp');
var gls = require('gulp-live-server');
var bs = require('browser-sync').create();

// TODO
// Set up a seperate server for the view layer and have server.js as
// a pure service layer that connects to the RethinkDB driver.
// The view layer will take advantage of Browser Sync's awesomeness
// Possibly use nodemon or pm2 to run the service layer

gulp.task('default', ['server', 'browser-sync'], function() {
});

gulp.task('server', function() {
  var server = gls('server.js');
  server.start();
})

gulp.task('browser-sync', function() {
  bs.init({
    proxy: "http://localhost:1337"
  });
});

Add a TODO to gulp tasks


AutoForm.addHooks('usersEnrollForm', {
  onError (formType, error) {
    // Show the error to end user
    // TODO: add internationalization support for specefic error type(s)
    FlashMessages.sendError(`${error.error}: ${error.reason}`, { autoHide: false });
  }
});

AutoForm.addHooks('usersEnrollForm', {
  onError (formType, error) {
    // Show the error to end user
    // TODO: add internationalization support for specefic error type(s)

    // Make sure there is an error type, otherwise don't show error
    // to prevent 'unknown' or undefined error messages from triggering flash message
    if (error.error) {
      FlashMessages.sendError(`${error.error}: ${error.reason}`, { autoHide: false });
    }
  }
});

Make sure error type exists before flash message


import React from 'react';
import { Title } from '@patternfly/react-core';
import { LinkIcon } from '@patternfly/react-icons';
import { slugger } from '../../helpers/slugger';
import './autoLinkHeader.css';

// "xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "4xl"
const sizes = {
  h1: '3xl',
  h2: '2xl',
  h3: 'xl',
  h4: 'lg',
  h5: 'md',
  h6: 'sm'
}

export const AutoLinkHeader = ({
  id,
  size,
  headingLevel,
  children,
  metaText,
  ...props
}) => {
  const slug = slugger(children);

  return (
    <Title
      id={slug}
      size={sizes[size]}
      headingLevel={headingLevel || size}
      {...props}>
      <a href={`#${slug}`} className="ws-heading-anchor" tabIndex="-1"  aria-hidden>
        <LinkIcon style={{ height: '0.5em', width: '0.5em', verticalAlign: 'middle' }} />
      </a>
      {children} {metaText}
    </Title>
  )
};

import React from 'react';
import { Title } from '@patternfly/react-core';
import { LinkIcon } from '@patternfly/react-icons';
import { slugger } from '../../helpers/slugger';
import './autoLinkHeader.css';

// "xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "4xl"
const sizes = {
  h1: '3xl',
  h2: '2xl',
  h3: 'xl',
  h4: 'lg',
  h5: 'md',
  h6: 'sm'
}

export const AutoLinkHeader = ({
  id,
  size,
  headingLevel,
  children,
  metaText,
  ...props
}) => {
  const slug = slugger(children);

  return (
    <Title
      id={_.uniqueId(slug + '-')}
      size={sizes[size]}
      headingLevel={headingLevel || size}
      {...props}>
      <a href={`#${slug}`} className="ws-heading-anchor" tabIndex="-1"  aria-hidden>
        <LinkIcon style={{ height: '0.5em', width: '0.5em', verticalAlign: 'middle' }} />
      </a>
      {children} {metaText}
    </Title>
  )
};

Create unique id for headings


module.exports = {
  extends: 'eslint:recommended',
  parserOptions: {
    ecmaVersion: 6,
    sourceType: 'module',
    ecmaFeatures: {
      experimentalObjectRestSpread: true
    }
  },
  env: {
    es6: true,
    browser: false,
    node: true
  },
  globals: {
    jasmine: false,
    jest: false,
    describe: false,
    xdescribe: false,
    before: false,
    beforeEach: false,
    it: false,
    xit: false,
    after: false,
    afterEach: false,
    expect: false,
  },
  rules: {
    'no-console': ['warn'],
    'semi': ['error', 'always'],
    'no-var': ['error'],
    'no-unused-vars': ['error', { 'vars': 'all', 'args': 'none', 'caughtErrors': 'none' }],
    'object-curly-spacing': ['error', 'always'],
    'comma-dangle': ['error', 'always-multiline'],
    'arrow-parens': ['error', 'always'],
    'prefer-arrow-callback': ['error', { 'allowNamedFunctions': true }],
    'space-before-function-paren': ['error', 'never']
  }
}

module.exports = {
  extends: 'eslint:recommended',
  parserOptions: {
    ecmaVersion: 6,
    sourceType: 'module',
    ecmaFeatures: {
      experimentalObjectRestSpread: true
    }
  },
  env: {
    es6: true,
    browser: true,
    node: true
  },
  globals: {
    jasmine: false,
    jest: false,
    describe: false,
    xdescribe: false,
    before: false,
    beforeEach: false,
    it: false,
    xit: false,
    after: false,
    afterEach: false,
    expect: false,
  },
  rules: {
    'no-console': ['warn'],
    'semi': ['error', 'always'],
    'no-var': ['error'],
    'no-unused-vars': ['error', { 'vars': 'all', 'args': 'none', 'caughtErrors': 'none' }],
    'object-curly-spacing': ['error', 'always'],
    'comma-dangle': ['error', 'always-multiline'],
    'arrow-parens': ['error', 'always'],
    'prefer-arrow-callback': ['error', { 'allowNamedFunctions': true }],
    'space-before-function-paren': ['error', 'never']
  }
}

Enable browser in env options


import Viget from '../components/viget'
import DOM   from 'react-dom'
import React from 'react'
import { update } from '../actions/circle'

export default function Render (app, el, next) {
  function render (state) {
    DOM.render(<Viget { ...state } />, el)
  }

  render(app.state)

  app.listen(render)

  requestAnimationFrame(function loop() {
    requestAnimationFrame(loop)
    app.push(update)
  })

  next()
}

import Viget from '../components/Viget'
import DOM   from 'react-dom'
import React from 'react'
import { update } from '../actions/circle'

export default function Render (app, el, next) {
  function render (state) {
    DOM.render(<Viget { ...state } />, el)
  }

  render(app.state)

  app.listen(render)

  requestAnimationFrame(function loop() {
    requestAnimationFrame(loop)
    app.push(update)
  })

  next()
}

Use capital letters in viget


import React, { memo } from 'react';
import PropTypes from 'prop-types';
import { Box } from '@strapi/parts/Box';
import styled from 'styled-components';
import ContentTypeCollapses from '../ContentTypeCollapses';
import GlobalActions from '../GlobalActions';

const StyledBox = styled(Box)`
  overflow-x: auto;
`;

const ContentTypes = ({ isFormDisabled, kind, layout: { actions, subjects } }) => {
  const sortedSubjects = [...subjects].sort((a, b) => a.label > b.label);

  return (
    <StyledBox background="neutral0">
      <GlobalActions actions={actions} kind={kind} isFormDisabled={isFormDisabled} />
      <ContentTypeCollapses
        actions={actions}
        isFormDisabled={isFormDisabled}
        pathToData={kind}
        subjects={sortedSubjects}
      />
    </StyledBox>
  );
};

ContentTypes.propTypes = {
  isFormDisabled: PropTypes.bool.isRequired,
  kind: PropTypes.string.isRequired,
  layout: PropTypes.shape({
    actions: PropTypes.array,
    subjects: PropTypes.arrayOf(
      PropTypes.shape({
        uid: PropTypes.string.isRequired,
        label: PropTypes.string.isRequired,
        properties: PropTypes.array.isRequired,
      })
    ),
  }).isRequired,
};

export default memo(ContentTypes);

import React, { memo } from 'react';
import PropTypes from 'prop-types';
import { Box } from '@strapi/parts/Box';
import styled from 'styled-components';
import sortBy from 'lodash/sortBy';
import ContentTypeCollapses from '../ContentTypeCollapses';
import GlobalActions from '../GlobalActions';

const StyledBox = styled(Box)`
  overflow-x: auto;
`;

const ContentTypes = ({ isFormDisabled, kind, layout: { actions, subjects } }) => {
  const sortedSubjects = sortBy([...subjects], 'label');

  return (
    <StyledBox background="neutral0">
      <GlobalActions actions={actions} kind={kind} isFormDisabled={isFormDisabled} />
      <ContentTypeCollapses
        actions={actions}
        isFormDisabled={isFormDisabled}
        pathToData={kind}
        subjects={sortedSubjects}
      />
    </StyledBox>
  );
};

ContentTypes.propTypes = {
  isFormDisabled: PropTypes.bool.isRequired,
  kind: PropTypes.string.isRequired,
  layout: PropTypes.shape({
    actions: PropTypes.array,
    subjects: PropTypes.arrayOf(
      PropTypes.shape({
        uid: PropTypes.string.isRequired,
        label: PropTypes.string.isRequired,
        properties: PropTypes.array.isRequired,
      })
    ),
  }).isRequired,
};

export default memo(ContentTypes);

Fix sort content types in role view


liveblogSyndication
    .directive('lbIncomingSyndication',
        ['$routeParams', 'IncomingSyndicationActions', 'IncomingSyndicationReducers', 'Store',
        function($routeParams, IncomingSyndicationActions, IncomingSyndicationReducers, Store) {
            return {
                templateUrl: 'scripts/liveblog-syndication/views/incoming-syndication.html',
                scope: {
                    lbPostsOnPostSelected: '=',
                    openPanel: '=',
                    syndId: '='
                },
                link: function(scope) {
                    scope.blogId = $routeParams._id;

                    scope.store = new Store(IncomingSyndicationReducers, {
                        posts: {},
                        syndication: {}
                    });

                    scope.store.connect(function(state) {
                        scope.posts = state.posts;
                        scope.syndication = state.syndication;
                    });

                    scope.goBack = function() {
                        scope.openPanel('ingest', null);
                    };

                    IncomingSyndicationActions
                        .getPosts(scope.blogId, scope.syndId);

                    IncomingSyndicationActions
                        .getSyndication(scope.syndId);

                    // On incoming post, we reload all the posts.
                    // Not very fast, but easy to setup
                    scope.$on('posts', function() {
                        IncomingSyndicationActions
                            .getPosts(scope.blogId, scope.syndId);
                    });

                    scope.$on('$destroy', scope.store.destroy);
                }
            };
        }]);

liveblogSyndication
    .directive('lbIncomingSyndication',
        ['$routeParams', 'IncomingSyndicationActions', 'IncomingSyndicationReducers', 'Store',
        function($routeParams, IncomingSyndicationActions, IncomingSyndicationReducers, Store) {
            return {
                templateUrl: 'scripts/liveblog-syndication/views/incoming-syndication.html',
                scope: {
                    lbPostsOnPostSelected: '=',
                    openPanel: '=',
                    syndId: '='
                },
                link: function(scope) {
                    scope.blogId = $routeParams._id;

                    scope.store = new Store(IncomingSyndicationReducers, {
                        posts: {},
                        syndication: {}
                    });

                    scope.store.connect(function(state) {
                        scope.posts = state.posts;
                        scope.syndication = state.syndication;
                    });

                    scope.goBack = function() {
                        scope.openPanel('ingest', null);
                    };

                    IncomingSyndicationActions
                        .getPosts(scope.blogId, scope.syndId);

                    IncomingSyndicationActions
                        .getSyndication(scope.syndId);

                    // On incoming post, we reload all the posts.
                    // Not very fast, but easy to setup
                    scope.$on('posts', function(e, data) {
                        if (data.posts[0].syndication_in)
                            IncomingSyndicationActions
                                .getPosts(scope.blogId, scope.syndId);
                    });

                    scope.$on('$destroy', scope.store.destroy);
                }
            };
        }]);

Remove amount of round trip to the server


// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require bootstrap-sprockets
//= require dataTables/jquery.dataTables
//= require dataTables/bootstrap/3/jquery.dataTables.bootstrap
//= require js-routes
//= require_tree .

$(document).ready(function(){
    $('[data-toggle="tooltip"]').tooltip();

    var table = $('#jobListTable').DataTable();

    $('#jobListTable tbody').on( 'click', 'tr', function () {
        if ( $(this).hasClass('active') ) {
            $(this).removeClass('active');
        }
        else {
            table.$('tr.active').removeClass('active');
            $(this).addClass('active');
        }
    });
});

// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require bootstrap-sprockets
//= require dataTables/jquery.dataTables
//= require dataTables/bootstrap/3/jquery.dataTables.bootstrap
//= require js-routes
//= require_tree .

active_var = function() {
    return $('tr.active').attr('id');
}

$(document).ready(function(){
    $('[data-toggle="tooltip"]').tooltip();

    var table = $('#jobListTable').DataTable();

    $('#jobListTable tbody').on( 'click', 'tr', function () {
        if ( $(this).hasClass('active') ) {
            $(this).removeClass('active');
        }
        else {
            table.$('tr.active').removeClass('active');
            $(this).addClass('active');
        }
    });
});

Add getter for active job


import { helper } from 'ember-helper'
import { defaultLocale, changeLocale, localeHasBeenChanged } from '../utils/locale'
import generateImageURL from '../utils/image'
import faker from 'faker'

faker.locale = defaultLocale

export function fake([signature, ...args], {parse = false, locale, ...opts}) {
  // running in node environment w/ fastboot server
  if ('undefined' !== typeof FastBoot // eslint-disable-line
      // and not to trying to generate an image URL
      && (signature && !/^image/i.test(signature))) return ''

  if (!signature) throw new Error(faker.fake())

  if (localeHasBeenChanged || locale) changeLocale(faker, locale)

  if (parse) {
    return faker.fake(signature.replace(/\[/g, '{{').replace(/\]/g, '}}'))
  } else {
    const [namespace, method] = signature.split('.')

    if ('image' === namespace && 'unsplash' === method) {
      return generateImageURL([namespace, method, ...args], opts)
    }

    return faker[namespace][method].apply(null, args)
  }
}

export default helper(fake)

import { helper } from 'ember-helper'
import { defaultLocale, changeLocale, localeHasBeenChanged } from '../utils/locale'
import generateImageURL from '../utils/image'
import faker from 'faker'

faker.locale = defaultLocale

export function fake([signature, ...args], {parse = false, locale, ...opts}) {
  // running in node environment w/ fastboot server
  if ('undefined' !== typeof FastBoot // eslint-disable-line
      // and not to trying to generate an image URL
      && (signature && !/^image/i.test(signature))) return ''

  if (!signature) throw new Error(faker.fake())

  if (localeHasBeenChanged || locale) changeLocale(faker, locale)

  if (parse) {
    return faker.fake(signature.replace(/\[/g, '{{').replace(/\]/g, '}}'))
  } else {
    const [namespace, method] = signature.split('.')

    if ('image' === namespace && 'unsplash' === method) {
      return generateImageURL([namespace, method, ...args], opts)
    }

    try {
      return faker[namespace][method].apply(null, args)
    } catch (error) {
      if ("Cannot read property 'apply' of undefined" === error.message) {
        throw new ReferenceError(`${namespace}.${method} 不是有效的 faker 方法名称`)
      }
      throw new Error(error)
    }
  }
}

export default helper(fake)

Throw undefined error more friendly


/*jslint nomen: true, vars: true*/
/*global _, angular, ApplicationConfiguration*/
(function () {
    'use strict';
    angular.module('blueprints').controller('SlideBlueprintsController', ['$scope', 'Tags', 'SlideBlueprints',
        function ($scope, Tags, SlideBlueprints) {
            $scope.bluePrintInstance = SlideBlueprints.get({slideId: $scope.Slide._id});
            $scope.possibleTags = [];

            $scope.refreshTags = function (text) {
                return Tags.query({tag: text}, function (result) {
                    $scope.possibleTags = _.map(result, 'value');
                });
            };

            $scope.errMessage = '';
            $scope.save = function () {
                $scope.errMessage = '';
                $scope.bluePrintInstance.slide = [$scope.Slide];
                $scope.bluePrintInstance.$save().then(function () {
                    $scope.$close();
                }, function (err) {
                    $scope.errMessage = err.data.message;
                });
            };
        }]);
}());

/*jslint nomen: true, vars: true*/
/*global _, angular, ApplicationConfiguration*/
(function () {
    'use strict';
    angular.module('blueprints').controller('SlideBlueprintsController', ['$scope', 'Tags', 'SlideBlueprints',
        function ($scope, Tags, SlideBlueprints) {
            $scope.bluePrintInstance = SlideBlueprints.get({slideId: $scope.Slide._id});
            $scope.possibleTags = [];

            $scope.refreshTags = function (text) {
                return Tags.query({tag: text}, function (result) {
                    $scope.possibleTags = _.map(result, 'value');
                });
            };

            $scope.errMessage = '';
            $scope.save = function () {
                $scope.errMessage = '';
                $scope.bluePrintInstance.slide = [$scope.Slide];
                $scope.bluePrintInstance.created = Date.now();
                $scope.bluePrintInstance.$save().then(function () {
                    $scope.$close();
                }, function (err) {
                    $scope.errMessage = err.data.message;
                });
            };
        }]);
}());

Fix [IQSLDSH-107] - the MongoDB server date was used as default, now user date is used.


define([
  'common/views/visualisations/journey-graph/journey-graph',
  'common/collections/journey',
  'extensions/collections/collection'
],
function (JourneyGraph, JourneyCollection, Collection) {

  describe('JourneyGraph', function () {
    var graph;

    beforeEach(function () {
      graph = new JourneyGraph({
        collection: new JourneyCollection([{}])
      });
      graph.valueAttr = 'uniqueEvents';
    });

    describe('calcYScale', function () {

      describe('domain', function () {

        it('is set to [0,1] if there is no data', function () {
          expect(graph.calcYScale().domain()).toEqual([0, 1]);
        });

        it('is set to the range of the data', function () {
          graph.collection.at(0).set('values', new Collection([
            { uniqueEvents: 137 },
            { uniqueEvents: 100 }
          ]));
          expect(graph.calcYScale().domain()).toEqual([0, 137]);
        });

      });
    });

  });

});

define([
  'common/views/visualisations/journey-graph/journey-graph',
  'common/collections/journey_series'
],
function (JourneyGraph, JourneyCollection) {

  describe('JourneyGraph', function () {
    var graph;

    beforeEach(function () {
      graph = new JourneyGraph({
        collection: new JourneyCollection()
      });
      graph.valueAttr = 'uniqueEvents';
    });

    describe('calcYScale', function () {

      describe('domain', function () {

        it('is set to [0,1] if there is no data', function () {
          expect(graph.calcYScale().domain()).toEqual([0, 1]);
        });

        it('is set to the range of the data', function () {
          graph.collection.reset([
            { uniqueEvents: 137 },
            { uniqueEvents: 100 }
          ]);
          expect(graph.calcYScale().domain()).toEqual([0, 137]);
        });

      });
    });

  });

});

Fix journey graph tests following removal of the matrix


import {fromJS, Record} from 'immutable';

import {
  STORE_ACTIVITIES,
  UPDATE_ACTIVITY
} from './actions';

const Activity = Record({
  id: null,
  actor: null,
  type: '',
  object: {},
  status: {}
});

export const initialState = fromJS({
  byId: {}
});

export default function reducer(state = initialState, action) {
  switch (action.type) {
    case STORE_ACTIVITIES: {
      const activities = {};
      action.payload.activities.forEach((a) => {
        activities[a.id] = new Activity(a);
      });
      return state.mergeIn(['byId'], activities);
    }
    case UPDATE_ACTIVITY: {
      const {activity} = action.payload;
      return state.mergeDeepIn(['byId', activity.id], activity);
    }
    default:
      return state;
  }
}

import {Record} from 'immutable';

import {
  STORE_ACTIVITIES,
  UPDATE_ACTIVITY
} from './actions';

const Activity = Record({
  id: null,
  actor: null,
  type: '',
  object: {},
  status: {}
});

const InitialState = Record({
  byId: {}
});

export const initialState = new InitialState();

export default function reducer(state = initialState, action) {
  switch (action.type) {
    case STORE_ACTIVITIES: {
      const activities = {};
      action.payload.activities.forEach((a) => {
        activities[a.id] = new Activity(a);
      });
      return state.mergeIn(['byId'], activities);
    }
    case UPDATE_ACTIVITY: {
      const {activity} = action.payload;
      return state.mergeDeepIn(['byId', activity.id], activity);
    }
    default:
      return state;
  }
}

Update to use Records for initialState


'use strict';

/**
* Compute a sample standard deviation incrementally.
*
* @module @stdlib/math/generics/statistics/incrstdev
*
* @example
* var incrstdev = require( '@stdlib/math/generics/statistics/incrstdev' );
*
* var accumulator = incrstdev();
*
* var s2 = accumulator();
* // returns 0.0
*
* s2 = accumulator( 2.0 );
* // returns 0.0
*
* s2 = accumulator( -5.0 );
* // returns ~4.95
*
* s2 = accumulator();
* // returns ~4.95
*/

// MODULES //

var incrstdev = require( './incrstdev.js' );

// EXPORTS //

module.exports = incrstdev;

'use strict';

/**
* Compute a sample standard deviation incrementally.
*
* @module @stdlib/math/generics/statistics/incrstdev
*
* @example
* var incrstdev = require( '@stdlib/math/generics/statistics/incrstdev' );
*
* var accumulator = incrstdev();
*
* var s = accumulator();
* // returns 0.0
*
* s = accumulator( 2.0 );
* // returns 0.0
*
* s = accumulator( -5.0 );
* // returns ~4.95
*
* s = accumulator();
* // returns ~4.95
*/

// MODULES //

var incrstdev = require( './incrstdev.js' );

// EXPORTS //

module.exports = incrstdev;

Use s instead of s2 for sample standard deviation


module.exports = {
  checkEligibilityScreenTitle: 'See if you qualify for Premium Assistance',
  incomeDataRequired: 'Please provide income information',
  incomeDataMaxLength: 'Too many characters',
  ageGroupRequired: 'Please specify age group',
  spouseCommonLawStatusRequired: 'Please specify spouse/common-law status',
  continueButton: 'Continue to apply for financial assistance',
  disabilityInfo: 'Add disability information',
  childrenInfo: 'Add children information',
  invalidNumber: 'Must use a valid number'
}

module.exports = {
  checkEligibilityScreenTitle: 'See if you qualify for financial assistance to pay for public health care coverage',
  incomeDataRequired: 'Please provide income information',
  incomeDataMaxLength: 'Too many characters',
  ageGroupRequired: 'Please specify age group',
  spouseCommonLawStatusRequired: 'Please specify spouse/common-law status',
  continueButton: 'Continue to apply for financial assistance',
  disabilityInfo: 'Add disability information',
  childrenInfo: 'Add children information',
  invalidNumber: 'Must use a valid number'
}

Update screen title as per Sarah's edits


var argscheck = require('cordova/argscheck'),
    utils = require('cordova/utils'),
    exec = require('cordova/exec');

var ApplePay = {

    canMakePayments: function(successCallback, errorCallback) {
        exec(successCallback, errorCallback, 'ApplePay', 'canMakePayments', []);
    },

    makePaymentRequest: function(order, successCallback, errorCallback) {
        exec(successCallback, errorCallback, 'ApplePay', 'makePaymentRequest', [order]);
    },

    completeLastTransaction: function(status, successCallback, errorCallback) {
        exec(successCallback, errorCallback, 'ApplePay', 'completeLastTransaction', [order]);
    }

};

module.exports = ApplePay;

var argscheck = require('cordova/argscheck'),
    utils = require('cordova/utils'),
    exec = require('cordova/exec');

var ApplePay = {

    canMakePayments: function(successCallback, errorCallback) {
      return new Promise(function(resolve, reject) {
          exec(function(message) {
            successCallback(message);
            resolve(message);
          }, function(message) {
            errorCallback(message);
            reject(message);
          }, 'ApplePay', 'canMakePayments', []);
      });

    },

    makePaymentRequest: function(order, successCallback, errorCallback) {

      return new Promise(function(resolve, reject) {
          exec(function(message) {
            successCallback(message);
            resolve(message);
          }, function(message) {
            errorCallback(message);
            reject(message);
          }, 'ApplePay', 'makePaymentRequest', [order]);
      });

    },

    completeLastTransaction: function(status, successCallback, errorCallback) {

      return new Promise(function(resolve, reject) {
          exec(function(message) {
            successCallback(message);
            resolve(message);
          }, function(message) {
            errorCallback(message);
            reject(message);
          }, 'ApplePay', 'completeLastTransaction', [status]);
      });

    }

};

module.exports = ApplePay;

Correct status variable. All methods now return promises, as well as execute the callback.


import program from 'commander'
import sagui from './index'
import { InvalidPath } from './plugins/path'
import { logError, log } from './util/log'

program.command('build')
  .description('Build the project')
  .action(function (options) {
    sagui.build(options)
  })

program.command('dist')
  .description('Builds an optimized distribution of the project')
  .action(function (options) {
    sagui.dist(options)
  })

program.command('develop')
  .description('Run development environment')
  .action(function (options) {
    sagui.develop(options)
  })

program.command('test')
  .description('Run tests')
  .option('-w, --watch', 'Run tests on any file change')
  .action(function (options) {
    sagui.test(options)
  })

program.command('install')
  .description('Install or update sagui in the current project')
  .action(function (options) {
    sagui.install(options)
    log('installed in the project')
  })

export default function cli (argv) {
  try {
    program.parse(argv)
  } catch (e) {
    if (e instanceof InvalidPath) {
      logError('Must be executed in target project\'s package.json path')
    } else {
      logError('Error starting up')
      logError(e.stack || e)
    }
  }
  if (!argv.slice(2).length) program.outputHelp()
}

import program from 'commander'
import sagui from './index'
import { InvalidPath } from './configure/path'
import { logError, log } from './util/log'

program.command('build')
  .description('Build the project')
  .action(function (options) {
    sagui.build(options)
  })

program.command('dist')
  .description('Builds an optimized distribution of the project')
  .action(function (options) {
    sagui.dist(options)
  })

program.command('develop')
  .description('Run development environment')
  .action(function (options) {
    sagui.develop(options)
  })

program.command('test')
  .description('Run tests')
  .option('-w, --watch', 'Run tests on any file change')
  .action(function (options) {
    sagui.test(options)
  })

program.command('install')
  .description('Install or update sagui in the current project')
  .action(function (options) {
    sagui.install(options)
    log('installed in the project')
  })

export default function cli (argv) {
  try {
    program.parse(argv)
  } catch (e) {
    if (e instanceof InvalidPath) {
      logError('Must be executed in target project\'s package.json path')
    } else {
      logError('Error starting up')
      logError(e.stack || e)
    }
  }
  if (!argv.slice(2).length) program.outputHelp()
}

Fix broken require after refactoring


import ua from 'universal-analytics'
import { machineIdSync } from 'node-machine-id'
import { version as appVersion } from '../package.json'
import config from './config'

const DEFAULT_CATEGORY = 'Cerebro App'

const trackingEnabled = process.env.NODE_ENV === 'production' && config.get('trackingEnabled')
let visitor

if (trackingEnabled) {
  try {
    visitor = ua('UA-87361302-1', machineIdSync(), { strictCidFormat: false })
  } catch (err) {
    console.log('[machine-id error]', err)
    visitor = ua('UA-87361302-1')
  }
}

export const screenView = (screenName) => {
  if (trackingEnabled) {
    visitor.screenview(screenName, 'Cerebro', appVersion, process.platform)
  }
}

export const trackEvent = ({ category, event, label, value }) => {
  if (trackingEnabled) {
    visitor.event(category || DEFAULT_CATEGORY, event, label, value).send()
  }
}

import ua from 'universal-analytics'
import { machineIdSync } from 'node-machine-id'
import { version as appVersion } from '../package.json'
import config from './config'

const DEFAULT_CATEGORY = 'Cerebro App'

const isTrackingEnabled = () => (
  process.env.NODE_ENV === 'production' && config.get('trackingEnabled')
)

let visitorCache = null

const visitor = () => {
  if (visitorCache) {
    return visitorCache
  }
  if (isTrackingEnabled()) {
    try {
      visitorCache = ua('UA-87361302-1', machineIdSync(), { strictCidFormat: false })
    } catch (err) {
      console.log('[machine-id error]', err)
      visitorCache = ua('UA-87361302-1')
    }
  }
  return visitorCache
}

export const screenView = (screenName) => {
  if (isTrackingEnabled()) {
    visitor().screenview(screenName, 'Cerebro', appVersion, process.platform)
  }
}

export const trackEvent = ({ category, event, label, value }) => {
  if (isTrackingEnabled()) {
    visitor().event(category || DEFAULT_CATEGORY, event, label, value).send()
  }
}

Fix production build broken by tracking


import fs from 'fs';
import { parse } from './parser';

export function extractIntl(files) {
  return new Promise((resolve, reject) => {
    if (!Array.isArray(files)) {
      files = [files];
    }

    const fileContents = files.map(readFile);

    fileContents[0].then(data => {
      return resolve(parse(data));
    }).catch(err => {
      return reject(err);
    });
  });
}

function readFile(filePath) {
  return new Promise((resolve, reject) => {
    fs.readFile(filePath, 'utf-8', (err, data) => {
      if (err) {
        return reject(err);
      }

      return resolve(data);
    });
  });
}

import fs from 'fs';
import { parse } from './parser';

export function extractIntl(files) {
  return new Promise((resolve, reject) => {
    if (!Array.isArray(files)) {
      files = [files];
    }

    return Promise.all(
      files.map(readFile)
    ).then(fileContents => {
      return resolve(
        fileContents
          .map(parse)
          .reduce((accumulator, messages) => Object.assign(accumulator, messages), {})
      );
    }).catch(err => {
      return reject(err);
    });
  });
}

function readFile(filePath) {
  return new Promise((resolve, reject) => {
    fs.readFile(filePath, 'utf-8', (err, data) => {
      if (err) {
        return reject(err);
      }

      return resolve(data);
    });
  });
}

Read from multiple files, not just the first one


var alert = new Alert({
    'class': 'alert-success fade in',
    content: "Oh yeah! That's just lookin' fine",
    closable: true
}, 'my-alert');

alert.startup();

alert.on('close', function (ev) {
    console.info('Alert about to be closed');
});

alert.on('closed', function (ev) {
    console.info('Alert is now closed');
});

var alert = new Alert({
    'class': 'alert fade in',
    content: "Holy guacamole! Best check yo self, you're not looking too good.",
    closable: true
}, 'my-alert');

alert.startup();

alert.on('close', function (ev) {
    console.info('Alert about to be closed');
});

alert.on('closed', function (ev) {
    console.info('Alert is now closed');
});

Adjust Alert snippets to make for equal outcome between programmatic and declarative instantiation


define(['jquery', 'Templates', 'IndicatorFilter'], function($, Templates, IndicatorFilter) {
   /**
    * Renders the page indicators specified by spec.page_indicators
    * into the container specified by spec.page_indicator_box
    */
   var constructor = function(spec, indicatorContainer) {
      var indicatorFilter = new IndicatorFilter(spec, null);

      return {
         handle: function(pulls) {
            indicatorContainer.empty();
            indicatorFilter.filter(pulls, indicatorContainer, function(elem, filterName) {
               // Render the 'indicator' template into the indicators element
               return Templates.renderIntoContainer('global_indicator', {name: filterName}, elem);
            });
         }
      };
   };
   return constructor;
});

define(['jquery', 'Templates', 'IndicatorFilter'], function($, Templates, IndicatorFilter) {
   /**
    * Renders the page indicators specified by spec.page_indicators
    * into the container specified by spec.page_indicator_box
    */
   var constructor = function(spec, indicatorContainer) {
      var indicatorFilter = new IndicatorFilter(spec, null);

      return {
         handle: function(pulls) {
            indicatorContainer.empty();
            indicatorFilter.filter(pulls, $('body'), function(elem, filterName) {
               // elem will be the body tag. We'll ignore in in here.
               // Render the 'global_indicator' template into the indicators element
               return Templates.renderIntoContainer('global_indicator', {name: filterName}, indicatorContainer);
               // Notice that we render using the indicatorContainer as the
               // holder. That's because that's where autocreated indicators
               // are supposed to be rendered into
            });
         }
      };
   };
   return constructor;
});

Add specific indicator functionality to page indicators


define(["app/app"], function(App) {
  "use strict";

  App.Attachment = DS.Model.extend({
    file: DS.attr('file'), // FormData File object
    url: DS.attr('string'),
    thumbnailUrl: DS.attr('string'),
    fileName: DS.attr('string'),
    fileSize: DS.attr('number'),
    mediaType: DS.attr('string'),

    createdAt: DS.attr('string'),
    updatedAt: DS.attr('string'),

    createdBy: DS.belongsTo('user'),
    post: DS.belongsTo('post'),

    isImage: function() {
      return this.get('mediaType') === 'image' ||
        this.get('mediaType') === null
    }.property('mediaType'),

    isGeneral: function() {
      return this.get('mediaType') === 'general'
    }.property('mediaType'),

    isAudio: function() {
      return this.get('mediaType') === 'audio'
    }.property('mediaType'),

    formatSize: function() {
      var decimals = 1
      var bytes = this.get('fileSize')

      if (bytes == 0) return '0 Byte'
      var k = 1000
      var dm = decimals + 1 || 3
      var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
      var i = Math.floor(Math.log(bytes) / Math.log(k))
      return (bytes / Math.pow(k, i)).toPrecision(dm) + ' ' + sizes[i]
    }.property('fileSize')
  })
})

define(["app/app"], function(App) {
  "use strict";

  App.Attachment = DS.Model.extend({
    file: DS.attr('file'), // FormData File object
    url: DS.attr('string'),
    thumbnailUrl: DS.attr('string'),
    fileName: DS.attr('string'),
    fileSize: DS.attr('number'),
    mediaType: DS.attr('string'),

    createdAt: DS.attr('string'),
    updatedAt: DS.attr('string'),

    createdBy: DS.belongsTo('user'),
    post: DS.belongsTo('post'),

    isImage: function() {
      return this.get('mediaType') === 'image' ||
        Ember.isEmpty(this.get('mediaType'))
    }.property('mediaType'),

    isGeneral: function() {
      return this.get('mediaType') === 'general'
    }.property('mediaType'),

    isAudio: function() {
      return this.get('mediaType') === 'audio'
    }.property('mediaType'),

    formatSize: function() {
      var decimals = 1
      var bytes = this.get('fileSize')

      if (bytes == 0) return '0 Byte'
      var k = 1000
      var dm = decimals + 1 || 3
      var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
      var i = Math.floor(Math.log(bytes) / Math.log(k))
      return (bytes / Math.pow(k, i)).toPrecision(dm) + ' ' + sizes[i]
    }.property('fileSize')
  })
})

Fix a check for an empty mediaType


'use strict';

const BaseRepository = require('@gfcc/mongo-tenant-repository/BaseRepository');

/**
 * @class AccountRepository
 */
class AccountRepository extends BaseRepository {
  /**
   * @inheritDoc
   */
  getSchemaDefinition() {
    return {
      email: {
        type: String,
        unique: true,
        required: true
      },
      username: {
        type: String,
        unique: true,
        required: true
      },
      passwordHash: String,
    };
  }

  /**
   * @param email
   * @param callback
   * @returns {AccountRepository}
   */
  findOneByEmail(email, callback) {
    this.findOne({
      email: email,
    }, callback);

    return this;
  }

  /**
   * Find an account by its usernam.
   *
   * @param {string} username
   * @param {function} callback
   * @return {AccountRepository}
   */
  findOneByUsername(username, callback) {
    this.findOne({
      username: username,
    }, callback);

    return this;
  }
}

module.exports = AccountRepository;

'use strict';

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

/**
 * @class AccountRepository
 */
class AccountRepository extends AbstractRepository {
  /**
   * @inheritDoc
   */
  getSchemaDefinition() {
    return {
      email: {
        type: String,
        unique: true,
        required: true
      },
      username: {
        type: String,
        unique: true,
        required: true
      },
      passwordHash: String,
    };
  }

  /**
   * @param email
   * @returns {Promise}
   */
  findOneByEmail(email) {
    return this.findOne({
      email: email,
    });
  }

  /**
   * Find an account by its username.
   *
   * @param {string} username
   * @return {Promise}
   */
  findOneByUsername(username) {
    return this.findOne({
      username: username,
    }, callback);
  }
}

module.exports = AccountRepository;

Migrate account repository to promise based version.


var contextClass = (window.AudioContext ||
  window.webkitAudioContext ||
  window.mozAudioContext ||
  window.oAudioContext ||
  window.msAudioContext);
if (contextClass) {
  var audioContext = new contextClass();
  server.send("audio Context created!"); // For development testing only.
} else {
  server.send("web audio is not enabled, sorry."); // For development testing only.
};

var streamLoaded = function(stream) {
  var process = function(){
    setInterval(function(){
      analyser.getFloatFrequencyData(FFTData);
      var bucket = findMaxWithIndex(FFTData);
      var targetRange = bucket[1][0];
      var lowDifference = ((bucket[1][1])-(bucket[0][1]));
      var highDifference = ((bucket[1][1])-(bucket[2][1]));
      var shift = (lowDifference < highDifference ? -(highDifference - lowDifference) : (lowDifference - highDifference));
      var adjShift = (shift*0.5)*0.1;
      var hz = Math.round((targetRange + adjShift) / analyser.frequencyBinCount * (audioContext.sampleRate * 0.5));
      // $('body').html("hz " + hz);
      state.hz = hz
      var data = {
        hz: hz,
        amp: 0
      }
      server.emit('audio',data);
    },60);
  };

  var findMaxWithIndex = function(array) {
    var max = Math.max.apply(Math, array);
    var index = Array.prototype.indexOf.call(array,max);
    return [[index-1,array[index-1]],[index,max],[index+1,array[index+1]]]
  };

  // Initalization loop will get ambient room sound for noise canceling

  var microphone = audioContext.createMediaStreamSource(stream);
  var analyser = audioContext.createAnalyser();
  var hiPass = audioContext.createBiquadFilter();
  hiPass.type = hiPass.HIGHPASS;
  hiPass.frequency.value = 200;
  var loPass = audioContext.createBiquadFilter();
  loPass.type = loPass.LOWPASS;
  loPass.frequency.value = 1200;
  analyser.fftSize = 2048;
  analyser.smoothingTimeConstant = 0.9;
  var FFTData = new Float32Array(analyser.frequencyBinCount);
  FFTData.indexOf = Array.prototype.indexOf;
  microphone.connect(hiPass);
  hiPass.connect(loPass);
  loPass.connect(analyser);
  process();
};

navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia;
navigator.getUserMedia( {audio:true}, streamLoaded );

Use pitch detection to send pitch events to server in hz. Uses optimized method on top of WebAudio FFT analysis to get pitch accurate to 1hz (depending on accuracy of microphone and ambient noise. Todo: noise canceling.)


var hostname = location.hostname.replace(/^www\./, '')

$.ajax({
	url: chrome.extension.getURL('styles/' + hostname + '.css'),
	dataType: 'text',
	success: function(data){
		var stylesheet = document.createElement('style');
		stylesheet.type = 'text/css';
		stylesheet.innerHTML = data;
		document.body.appendChild(stylesheet);
	}
})

$.ajax({
	url: chrome.extension.getURL('scripts/' + hostname + '.js'),
	dataType: 'text',
	success: function(data){
		eval(data);
	},
});

$.ajax({
	url: chrome.extension.getURL('scripts/default.js'),
	dataType: 'text',
	success: function(data){
		eval(data);
	},
});

var hostname = location.hostname.replace(/^www\./, '')

var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = chrome.extension.getURL('styles/' + hostname + '.css');
document.documentElement.insertBefore(link);

var script = document.createElement('script');
script.src = chrome.extension.getURL('scripts/' + hostname + '.js');
document.documentElement.insertBefore(script);

var defaultScript = document.createElement('script');
defaultScript.src = chrome.extension.getURL('scripts/default.js');
document.documentElement.insertBefore(defaultScript);

Append a link to the file(s) instead of evaluating them


var eng = require('./node/engine');

var integrate = module.exports = function (func, lower, upper, options, callback){
  eng.runPython('single', func, options, callback, lower, upper);
};

var eng = require('./node/engine');

var integrate = module.exports = function (func, range, options, callback){
  eng.runPython('multi', func, options, callback, range);
};

integrate.univariate = function (func, lower, upper, options, callback){
  eng.runPython('single', func, options, callback, lower, upper);
}

Swap integrate as the generalized version taking any number of variables


function hasRender(Class) {
  var prototype = Class.prototype;
  if (!prototype) {
    return false;
  }

  return typeof prototype.render === 'function';
}

function descendsFromReactComponent(Class, React) {
  if (!React.Component) {
    return false;
  }

  var Base = Object.getPrototypeOf(Class);

  while (Base) {
    if (Base === React.Component) {
      return true;
    }

    Base = Object.getPrototypeOf(Base);
  }

  return false;
}

function isReactClassish(Class, React) {
  if (!Class) {
    return false;
  }

  // React 0.13
  if (hasRender(Class) || descendsFromReactComponent(Class, React)) {
    return true;
  }

  // React 0.12 and earlier
  if (Class.type && hasRender(Class.type)) {
    return true;
  }

  return false;
}

module.exports = isReactClassish;

function hasRender(Class) {
  var prototype = Class.prototype;
  if (!prototype) {
    return false;
  }

  return typeof prototype.render === 'function';
}

function descendsFromReactComponent(Class, React) {
  if (!React.Component) {
    return false;
  }

  var Base = Object.getPrototypeOf(Class);
  while (Base) {
    if (Base === React.Component) {
      return true;
    }

    Base = Object.getPrototypeOf(Base);
  }

  return false;
}

function isReactClassish(Class, React) {
  if (typeof Class !== 'function') {
    return false;
  }

  // React 0.13
  if (hasRender(Class) || descendsFromReactComponent(Class, React)) {
    return true;
  }

  // React 0.12 and earlier
  if (Class.type && hasRender(Class.type)) {
    return true;
  }

  return false;
}

module.exports = isReactClassish;

Fix crash with React 0.13


document.addEventListener('DOMContentLoaded', () => {
  const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);
  if ($navbarBurgers.length > 0) {
    $navbarBurgers.forEach(el => {
      el.addEventListener('click', () => {
        const target = el.dataset.target;
        const $target = document.getElementById(target);
        el.classList.toggle('is-active');
        $target.classList.toggle('is-active');
      });
    });
  }
});

document.addEventListener('DOMContentLoaded', () => {
  const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);
  if ($navbarBurgers.length > 0) {
    $navbarBurgers.forEach(el => {
      el.addEventListener('click', () => {
        const target = el.dataset.target;
        const $target = document.getElementById(target);
        el.classList.toggle('is-active');
        $target.classList.toggle('is-active');
      });
    });
  }

  const $linkableHeadings = document.querySelectorAll('main h2[id], main h3[id]');
  $linkableHeadings.forEach(el => {
    const anchor = document.createElement('a');
    anchor.setAttribute('href', '#' + el.id);
    anchor.innerText = ' #';
    el.appendChild(anchor);
  });
});

Add anchor icons to all headings with ids


var Layer = require('../models/layer');

exports.id = 'create-initial-osm-layer';

exports.up = function(done) {
  console.log('\nCreating open street map layer...');

  var osm = {
    name: "Open Street Map",
    type: "Imagery",
    format: "XYZ",
    base: true,
    url: "http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
  };

  Layer.create(osm, done);
};

exports.down = function(done) {
  Layer.Model.remove({name: "Open Street Map"}, done);
};

var api = require('../api');

exports.id = 'create-initial-osm-layer';

exports.up = function(done) {
  console.log('\nCreating open street map layer...');

  var osm = {
    name: "Open Street Map",
    type: "Imagery",
    format: "XYZ",
    base: true,
    url: "http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
  };

  new api.Layer().create(osm)
    .then(() => done())
    .catch(err => done(err));
};

exports.down = function(done) {
  new api.Layer().remove({name: "Open Street Map"})
    .then(() => done())
    .catch(err => done(err));
};

Update layer migration to use promises


(function() {
define("ember/load-initializers",
  [],
  function() {
    "use strict";

    return {
      'default': function(app, prefix) {
        var initializersRegExp = new RegExp(prefix + '/initializers');

        Ember.keys(requirejs._eak_seen).filter(function(key) {
          return initializersRegExp.test(key);
        }).forEach(function(moduleName) {
          var module = require(moduleName, null, null, true);
          if (!module) { throw new Error(moduleName + ' must export an initializer.'); }
          app.initializer(module['default']);
        });
      }
    }
  }
);
})();

(function() {
define("ember/load-initializers",
  [],
  function() {
    "use strict";

    return {
      'default': function(app, prefix) {
        var initializersRegExp = new RegExp('^' + prefix + '/initializers');

        Ember.keys(requirejs._eak_seen).filter(function(key) {
          return initializersRegExp.test(key);
        }).forEach(function(moduleName) {
          var module = require(moduleName, null, null, true);
          if (!module) { throw new Error(moduleName + ' must export an initializer.'); }
          app.initializer(module['default']);
        });
      }
    }
  }
);
})();

Add caret to start of regex


module.exports = {
    className: 'input-block',
    template: require('./index.html'),
    lazy: false,
    data: {
        name: 'Input',
        icon: '/images/blocks_input.png',
        attributes: {
            inputType: {
                label: 'Input Type',
                type: 'dropdownChoice',
                options: ['Single Line Text', 'Long Text'],
                value: 0,
                skipAutoRender: true
            },
            label: {
                label: 'Title',
                type: 'string',
                value: '',
                placeholder: 'Your title goes here',
                skipAutoRender: true
            },
            color: {
               label: 'Title Text Color',
               type: 'color',
               value: '#638093'
           }
        }
    },
    methods: {
        reportDataChange: function(self) {
            self.$dispatch('dataChange',
                this.$index,
                self.$el.value
            );
        }
    },
    ready: function() {
        var self = this;

        if(self.isEditing) {
            if (self.$el.querySelector('input')) self.$el.querySelector('input').disabled = 'disabled';
            if (self.$el.querySelector('textarea')) self.$el.querySelector('textarea').disabled = 'disabled';
        } else {
            // register block on data object
            self.$dispatch('dataChange',
                self.$index,
                '',
                self.$data.attributes.label.value
            );
        }
    }
};

module.exports = {
    className: 'input-block',
    template: require('./index.html'),
    lazy: false,
    data: {
        name: 'Input',
        icon: '/images/blocks_input.png',
        attributes: {
            inputType: {
                label: 'Input Type',
                type: 'dropdownChoice',
                options: ['Single Line Text', 'Long Text'],
                value: 0,
                skipAutoRender: true
            },
            label: {
                label: 'Title',
                type: 'string',
                value: '',
                placeholder: 'Your title goes here',
                skipAutoRender: true
            },
            color: {
                label: 'Title Text Color',
                type: 'color',
                value: '#638093'
            }
        }
    },
    methods: {
        reportDataChange: function (self) {
            self.$dispatch('dataChange',
                this.$index,
                self.$el.value
            );
        }
    },
    ready: function () {
        var self = this;

        if (self.isEditing) {
            if (self.$el.querySelector('input')) {
                self.$el.querySelector('input').disabled = 'disabled';
            }

            if (self.$el.querySelector('textarea')) {
                self.$el.querySelector('textarea').disabled = 'disabled';
            }
        } else {
            // register block on data object
            self.$dispatch('dataChange',
                self.$index,
                '',
                self.$data.attributes.label.value
            );
        }
    }
};

Resolve style errors on input block


"use strict";

exports.up = (knex, Promise) => {
  return knex.schema.createTable("dailyplans_events", (table) => {
    table.increments();
    table.integer("dailyplan_id")
      .notNullable()
      .references("id")
      .inTable("dailyplans")
      .onDelete("CASCADE");
    table.dateTime("event_time").notNullable().defaultTo(knex.fn.now());
    table.varchar("event", 255);
    table.integer("lesson_id").notNullable().defaultTo(0);
    table.timestamps(true, true);
  })
};

exports.down = (knex, Promise) => knex.schema.dropTable("dailyplans_events");

"use strict";

exports.up = (knex, Promise) => {
  return knex.schema.createTable("dailyplans_events", (table) => {
    table.increments();
    table.integer("dailyplan_id")
      .notNullable()
      .references("id")
      .inTable("dailyplans")
      .onDelete("CASCADE");
    table.dateTime("event_time").notNullable().defaultTo(knex.fn.now());
    table.varchar("plan", 255);
    table.integer("lesson_id").notNullable().defaultTo(0);
    table.timestamps(true, true);
  })
};

exports.down = (knex, Promise) => knex.schema.dropTable("dailyplans_events");

Change event to plan in migration file to avoid using a JS keyword... oops


const path = require('path')
const stat = require('./stat')
const readdir = require('./readdir')

// based on http://stackoverflow.com/a/38314404/2533525
const getFolders = function (dir, filterFn) {
  // default filter function accepts all folders
  filterFn = filterFn || function () { return true }

  return readdir(dir).then(list => {
    return Promise.all(list.map(file => {
      file = path.resolve(dir, file)
      return stat(file).then(stat => {
        if (stat.isDirectory()) {
          return filterFn(file) ? file : ''
        }
      })
    })).then(results => {
      return results.filter(f => {
        return !!f
      })
    })
  }).then(results => {
    // flatten the array of arrays
    return Array.prototype.concat.apply([], results)
  })
}

module.exports = getFolders

const path = require('path')
const stat = require('./stat')
const readdir = require('./readdir')

// based on http://stackoverflow.com/a/38314404/2533525
const getFolders = function (dir, filterFn) {
  // default filter function accepts all folders
  filterFn = filterFn || function () { return true }

  return readdir(dir).then(list => {
    return Promise.all(list.map(file => {
      file = path.resolve(dir, file)
      return stat(file).then(stat => {
        if (stat.isDirectory()) {
          return filterFn(file) ? file : ''
        }
      }).catch(err => {
        // stat failed for this folder
        console.error(err.message)
      })
    })).then(results => {
      return results.filter(f => {
        return !!f
      })
    })
  }).then(results => {
    // flatten the array of arrays
    return Array.prototype.concat.apply([], results)
  })
}

module.exports = getFolders

Handle one or more folders being inaccessible while listing


module.exports = {
  plugins: [
    require('babel-plugin-minify-constant-folding'),
    require('babel-plugin-minify-dead-code-elimination'),
    require('babel-plugin-minify-mangle-names'),
    require('babel-plugin-minify-replace'),
    require('babel-plugin-minify-simplify')
  ]
};

module.exports = {
  minified: true,
  plugins: [
    require('babel-plugin-minify-constant-folding'),
    require('babel-plugin-minify-dead-code-elimination'),
    require('babel-plugin-minify-mangle-names'),
    require('babel-plugin-minify-replace'),
    require('babel-plugin-minify-simplify')
  ]
};

Set minified true in the babel preset


Package.describe({
  summary: "Makes your Cordova application use the Crosswalk WebView \
instead of the System WebView on Android",
  version: '1.7.0',
  documentation: null
});

Cordova.depends({
  'cordova-plugin-crosswalk-webview': '2.1.0'
});

Package.describe({
  summary: "Makes your Cordova application use the Crosswalk WebView \
instead of the System WebView on Android",
  version: '1.7.0',
  documentation: null
});

Cordova.depends({
  'cordova-plugin-crosswalk-webview': '2.2.0'
});

Update Crosswalk plugin to latest version


var qs = require('qs');

function combineLoaders(loaders) {
  return loaders.map(function(loaderEntry) {
    var query = qs.stringify(loaderEntry.query, { arrayFormat: 'brackets' });
    if (query) {
      query = '?' + query;
    }
    return loaderEntry.loader + query;
  }).join('!');
}

module.exports = combineLoaders;

var qs = require('qs');

function combineLoaders(loaders) {
  return loaders.map(function(loaderEntry) {
    var query = qs.stringify(loaderEntry.query, { arrayFormat: 'brackets', encode: false });
    if (query) {
      query = '?' + query;
    }
    return loaderEntry.loader + query;
  }).join('!');
}

module.exports = combineLoaders;

Disable encoding loader string due to error in Babel 6


// LinkedIn:
$('title').remove();
$('#name-container').hide();
$('.profile-picture').hide();

// Logged out:
$('h1#name').hide();
$('.reg-upsell').hide();
$('.name-search').hide();

// These should appear only if you're already connected, but just in case:
$('.endorse-v2').hide();
$('.write-endorsement').hide();

// Search results:
$('li.mod > a.result-image').hide();
$('li.mod > .bd > h3 > a.main-headline').text('Person');
$('.profile-actions > .katy-button-group').hide();

Hide 'Send [NAME] InMail' button


'use strict';

var isObject = require('es5-ext/object/is-object')
  , forEach  = require('es5-ext/object/for-each')
  , toPlain  = require('es5-ext/object/to-plain-object')

  , isArray = Array.isArray;

module.exports = function (options) {
	options = toPlain(options);
	forEach(options, function (value, name) {
		if (isArray(value)) {
			options[name] = value.map(String);
			return;
		}
		if ((value == null) || isObject(value)) {
			delete options[name];
		}
	});
	return options;
};

'use strict';

var isObject  = require('es5-ext/object/is-object')
  , forEach   = require('es5-ext/object/for-each')
  , normalize = require('es5-ext/object/normalize-options')

  , isArray = Array.isArray;

module.exports = function (options) {
	options = normalize(options);
	forEach(options, function (value, name) {
		if (isArray(value)) {
			options[name] = value.map(String);
			return;
		}
		if ((value == null) || isObject(value)) {
			delete options[name];
		}
	});
	return options;
};

Update up to changes in es5-ext


var RateLimiter = function(sourceName, globalDailyLimit, globalHourlyLimit, userDailyLimit, userHourlyLimit) {

	// Initialize instance variables
	this.sourceName = sourceName || "undefinedSource";
	this.globalDailyLimit = globalDailyLimit || Number.MAX_VALUE;
	this.globalHourlyLimit = globalHourlyLimit || Number.MAX_VALUE;
	this.userDailyLimit = userDailyLimit || Number.MAX_VALUE;
	this.userHourlyLimit = userHourlyLimit || Number.MAX_VALUE;

	// Handles requests coming into rate limiter
	// If the request with userID is allowed, callback_allowed is called
	// Otherwise callback_blocked is called
	this.request = function(userID, callback_allowed, callback_blocked) {

	};

	// Increments Redis bucket counters for given userID
	this.increment = function(userID) {

	};

};

module.exports = RateLimiter;

var redis = require('redis'),
	client = redis.createClient();

var RateLimiter = function(sourceName, globalDailyLimit, globalHourlyLimit, userDailyLimit, userHourlyLimit) {

	// Initialize instance variables
	this.sourceName = sourceName || "undefinedSource";
	this.globalDailyLimit = globalDailyLimit || Number.MAX_VALUE;
	this.globalHourlyLimit = globalHourlyLimit || Number.MAX_VALUE;
	this.userDailyLimit = userDailyLimit || Number.MAX_VALUE;
	this.userHourlyLimit = userHourlyLimit || Number.MAX_VALUE;

	// Initialize key names
	var globalDailyKey = sourceName + ":global:daily";
	var globalHourlyKey = sourceName + ":global:hourly";
	var userDailyKey = sourceName + ":" + userID + ":daily";
	var userHourlyKey = sourceName + ":" + userID + ":hourly";

	// Handles requests coming into rate limiter
	// If the request with userID is allowed, callback_allowed is called
	// Otherwise callback_blocked is called
	this.request = function(userID, callback_allowed, callback_blocked) {

	};

	// Increments Redis bucket counters for given userID
	this.increment = function(userID) {

	};

};

module.exports = RateLimiter;

Add redis and key names


/* Copyright 2017 Streampunk Media Ltd.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

var dgram = require('dgram');
var argv = require('yargs')
  .default('p', 6789)
  .default('a', '234.5.6.7')
  .demandOption(['i'])
  .number('p')
  .usage('Join a multicast group. Useful for wireshark testing.\n' +
    'Usage: $0 ')
  .help()
  .describe('a', 'Address of multicast group to join.')
  .describe('g', 'Port of multicast group to join.')
  .describe('i', 'Multicast interface.')
  .argv;

var socket = dgram.createSocket('udp4');

var addr = process.argv[2];
var port = +process.argv[3];

socket.addMembership(argv.a, argv.i);

socket.on('listening', () => {
  console.log('Multicast group subscribed and listening.');
});

socket.on('error', console.error);

socket.bind(argv.p);

/* Copyright 2017 Streampunk Media Ltd.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

var dgram = require('dgram');
var argv = require('yargs')
  .default('p', 6789)
  .default('a', '234.5.6.7')
  .number('p')
  .usage('Join a multicast group. Useful for wireshark testing.\n' +
    'Usage: $0 ')
  .help()
  .describe('a', 'Address of multicast group to join.')
  .describe('p', 'Port of multicast group to join.')
  .describe('i', 'Multicast interface.')
  .argv;

var socket = dgram.createSocket('udp4');

socket.addMembership(argv.a, argv.i);

socket.on('listening', () => {
  console.log('Multicast group subscribed and listening.');
});

socket.on('error', console.error);

socket.bind(argv.p);

Fix command line options for join group.


/* eslint-disable no-var */

var path = require('path');
var merge = require('webpack-merge');
var HtmlWebpackPlugin = require('html-webpack-plugin');

var common = require('./webpack.common');

module.exports = merge(common, {
  module: {
    preLoaders: [],
    loaders: [
      {
        test: /\.js$/,
        include: [
          path.resolve(__dirname, '../app'),
          path.resolve(__dirname, '../tests'),
        ],
        loaders: ['babel'],
      },
      {
        test: /\.css$/,
        loader: 'style!css!postcss-loader',
      },
      {
        test: /\.scss$/,
        loader: 'style!css!postcss-loader!sass',
      },
    ],
    noParse: [
      /\/sinon.js/,
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      favicon: './app/assets/images/favicon.ico',
      inject: true,
      template: './conf/template.html',
    }),
  ],
});

/* eslint-disable no-var */

var path = require('path');
var merge = require('webpack-merge');
var HtmlWebpackPlugin = require('html-webpack-plugin');

var common = require('./webpack.common');

module.exports = merge(common, {
  module: {
    preLoaders: [],
    loaders: [
      {
        test: /\.js$/,
        include: [
          path.resolve(__dirname, '../app'),
          path.resolve(__dirname, '../tests'),
        ],
        loaders: ['babel'],
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      favicon: './app/assets/images/favicon.ico',
      inject: true,
      template: './conf/template.html',
    }),
  ],
});

Remove unnecessary stuff from webpack test confs


/* eslint-env node */
var path = require("path");
var ConcatSource = require("webpack-sources").ConcatSource;
var LicenseWebpackPlugin = require("license-webpack-plugin");

module.exports = {
  context: __dirname,
  entry: {
    mozjexl: "./node_modules/mozjexl/",
  },
  output: {
    path: path.resolve(__dirname, "vendor/"),
    filename: "[name].js",
    library: "[name]",
    libraryTarget: "this",
  },
  plugins: [
    /**
     * Plugin that appends "this.EXPORTED_SYMBOLS = ["libname"]" to assets
     * output by webpack. This allows built assets to be imported using
     * Cu.import.
     */
    function ExportedSymbols() {
      this.plugin("emit", function(compilation, callback) {
        for (const libraryName in compilation.entrypoints) {
          const assetName = `${libraryName}.js`; // Matches output.filename
          compilation.assets[assetName] = new ConcatSource(
            compilation.assets[assetName],
            `this.EXPORTED_SYMBOLS = ["${libraryName}"];` // Matches output.library
          );
        }
        callback();
      });
    },
    new LicenseWebpackPlugin({
      pattern: /^(MIT|ISC|MPL.*|Apache.*|BSD.*)$/,
      filename: `LICENSE_THIRDPARTY`,
    }),
  ],
};

/* eslint-env node */
var path = require("path");
var ConcatSource = require("webpack-sources").ConcatSource;
var LicenseWebpackPlugin = require("license-webpack-plugin");

module.exports = {
  context: __dirname,
  entry: {
    mozjexl: "./node_modules/mozjexl/",
  },
  output: {
    path: path.resolve(__dirname, "vendor/"),
    filename: "[name].js",
    library: "[name]",
    libraryTarget: "this",
  },
  plugins: [
    /**
     * Plugin that appends "this.EXPORTED_SYMBOLS = ["libname"]" to assets
     * output by webpack. This allows built assets to be imported using
     * Cu.import.
     */
    function ExportedSymbols() {
      this.plugin("emit", function(compilation, callback) {
        for (const libraryName in compilation.entrypoints) {
          const assetName = `${libraryName}.js`; // Matches output.filename
          compilation.assets[assetName] = new ConcatSource(
            "/* eslint-disable */", // Disable linting
            compilation.assets[assetName],
            `this.EXPORTED_SYMBOLS = ["${libraryName}"];` // Matches output.library
          );
        }
        callback();
      });
    },
    new LicenseWebpackPlugin({
      pattern: /^(MIT|ISC|MPL.*|Apache.*|BSD.*)$/,
      filename: `LICENSE_THIRDPARTY`,
    }),
  ],
};

Disable lints for built vendor files.


'use strict';

const net = require('net');
const process = require('process');

if (process.argv.length < 4) {
  console.log('Usage: node mcping16.js <host> <port>');
  process.exit(1);
}

const host = process.argv[2];
const port = parseInt(process.argv[3]);

const MAGIC = new Buffer('\xfe\xfd');
const REQUEST_CHALLENGE = new Buffer('\x09');
const REQUEST_STATUS = new Buffer('\x00');

const socket = net.connect(port, host);
socket.on('connect', () => {
  console.log('connected');

  //TODO rquest challenge token
});
socket.on('end', () => {
  console.log('ended');
});

'use strict';

const net = require('net');
const process = require('process');
const ProtoDef = require('protodef').ProtoDef;

if (process.argv.length < 4) {
  console.log('Usage: node mcping16.js <host> <port>');
  process.exit(1);
}

const host = process.argv[2];
const port = parseInt(process.argv[3]);

const proto = new ProtoDef();

const socket = net.connect(port, host);
socket.on('connect', () => {
  console.log('connected');

  // request challenge token
  // TODO: encode/decode using protodef
  socket.write(new Buffer('fefd090000000000000000', 'hex'));
});
socket.on('end', () => {
  console.log('ended');
});
socket.on('data', (raw) => {
  console.log('data',raw);
  console.log(raw.toString());
});

Send a \xFE\xFD challenge request


module.exports = function(grunt) {
  grunt.initConfig({
    lint: {
      files: ['grunt.js', 'lib/**/*.js', 'spec/**/*.js']
    },
    min: {
      dist: {
        src: 'lib/rivets.js',
        dest: 'lib/rivets.min.js'
      }
    },
    watch: {
      files: 'src/rivets.coffee',
      tasks: 'build'
    },
  });

  grunt.registerTask('default', 'watch');

  grunt.registerTask('compile', 'Compiles CoffeeScript source into JavaScript.', function(){
    var coffee = require('coffee-script');
    var js = coffee.compile(grunt.file.read('src/rivets.coffee'));
    if (js) grunt.file.write('lib/rivets.js', js);
  });

  grunt.registerTask('build', 'compile min');
};

module.exports = function(grunt) {
  grunt.initConfig({
    pkg: '<json:package.json>',
    meta: {
      banner:
        '// rivets.js\n' +
        '// version: <%= pkg.version %>\n' +
        '// author: <%= pkg.author %>\n' +
        '// license: <%= pkg.licenses[0].type %>'
    },
    lint: {
      files: ['grunt.js', 'lib/**/*.js', 'spec/**/*.js']
    },
    min: {
      dist: {
        src: ['<banner:meta.banner>', 'lib/rivets.js'],
        dest: 'lib/rivets.min.js'
      }
    },
    watch: {
      files: 'src/rivets.coffee',
      tasks: 'build'
    },
  });

  grunt.registerTask('default', 'watch');

  grunt.registerTask('compile', 'Compiles CoffeeScript source into JavaScript.', function(){
    var coffee = require('coffee-script');
    var js = coffee.compile(grunt.file.read('src/rivets.coffee'));
    var banner = grunt.task.directive('<banner:meta.banner>', function() { return null; });
    if (js) grunt.file.write('lib/rivets.js', banner + js);
  });

  grunt.registerTask('build', 'compile min');
};

Add a banner to prepend to builds.


util = exports;

util.mergeDeep = function (A, B, depth) {
	var forever = depth == null;
	for (var p in B) {
		if (B[p] != null && B[p].constructor==Object && (forever || depth > 0)) {
			A[p] = util.mergeDeep(
				A.hasOwnProperty(p) ? A[p] : {},
				B[p],
				forever ? null : depth-1
			);
		} else {
			A[p] = B[p];
		}
	}
	return A;
}

util.merge = function(A, B) {
	return util.mergeDeep(A, B, 0);
}

util.mergeCopy = function(A, B, depth) {
	var A_copy = util.mergeDeep({}, A);
	return util.mergeDeep(A_copy, B, depth);
}

var Util = exports || {};

Util.mergeDeep = function (A, B, depth) {
	var forever = depth == null;
	for (var p in B) {
		if (B[p] != null && B[p].constructor==Object && (forever || depth > 0)) {
			A[p] = Util.mergeDeep(
				A.hasOwnProperty(p) ? A[p] : {},
				B[p],
				forever ? null : depth-1
			);
		} else {
			A[p] = B[p];
		}
	}
	return A;
}

Util.merge = function(A, B) {
	return Util.mergeDeep(A, B, 0);
}

Util.mergeCopy = function(A, B, depth) {
	var A_copy = Util.mergeDeep({}, A);
	return Util.mergeDeep(A_copy, B, depth);
}

Make Util usable in the browser


import React, { Component } from 'react';
import { connect } from 'react-redux';
import { assetClick } from '../../actions/assets';
import map from 'lodash/map';
import ColorPicker from './colorpicker';

const getImage = (asset, profile, assets) => {
  if ( ! assets.data) return;
  const color = profile[asset.id].color;
  const fileIndex = profile[asset.id].fileIndex;
  const fileName = assets.data[asset.id].colors[color].files[fileIndex];
  return `svg/${asset.id}/${color}/${fileName}`;
};

class Profile extends Component {
  render() {
    const { dispatch, profile, assets } = this.props;
    if ( ! assets.data) return
;
    const data = map(assets.data);
    data.sort((a, b) => a.sortOrder - b.sortOrder);

    return (

          {map(data, asset => (
             dispatch(assetClick(asset.id))}
            />
          ))}

        <ColorPicker />

    );
  }
}

export default connect(({ profile, assets }) => ({ profile, assets }))(Profile);

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { assetClick } from '../../actions/assets';
import map from 'lodash/map';
import ColorPicker from './colorpicker';

const getImage = (asset, profile, assets) => {
  if ( ! assets.data) return;
  const color = profile[asset.id].color;
  const files = assets.data[asset.id].colors[color].files;
  const fileIndex = profile[asset.id].fileIndex % files.length;
  const fileName = files[fileIndex];
  return `svg/${asset.id}/${color}/${fileName}`;
};

class Profile extends Component {
  render() {
    const { dispatch, profile, assets } = this.props;
    if ( ! assets.data) return
;
    const data = map(assets.data);
    data.sort((a, b) => a.sortOrder - b.sortOrder);

    return (

          {map(data, asset => (
             dispatch(assetClick(asset.id))}
            />
          ))}

        <ColorPicker />

    );
  }
}

export default connect(({ profile, assets }) => ({ profile, assets }))(Profile);

Fix undefined images when there is file index out of range of available files


const gulp = require('gulp');
const gutil = require('gulp-util');
const uglify = require('gulp-uglify');
const concat = require('gulp-concat');
const server = require('karma').Server;

gulp.task('concat', function() {
  return gulp.src([
  	'./src/utils/globals.js',
  	'./src/utils/svg.js',
  	'./src/charts/classes.js'
  	])
    .pipe(concat('proteus-charts.js'))
    .pipe(gulp.dest('./dist/'));
});

gulp.task('minify', function() {
  return gulp.src('./dist/es5/proteus-charts.js')
    .pipe(uglify().on('error', gutil.log))
    .pipe(gulp.dest('./min'));
});

gulp.task('test', function (done) {
	new server({
		configFile: __dirname + '/karma.conf.js',
		singleRun: true
	}, done).start();
});

gulp.task('default', ['concat', 'minify']);

const gulp = require('gulp');
const gutil = require('gulp-util');
const uglify = require('gulp-uglify');
const concat = require('gulp-concat');
const server = require('karma').Server;

gulp.task('concat', function() {
  return gulp.src([
  	'./src/utils/globals.js',
  	'./src/utils/svg.js',
  	'./src/charts/classes.js'
  	])
    .pipe(concat('proteus-charts.js'))
    .pipe(gulp.dest('./dist/'));
});

gulp.task('minify', function() {
  return gulp.src('./dist/es5/proteus-charts.js')
    .pipe(uglify().on('error', gutil.log))
    .pipe(gulp.dest('./min'));
});

gulp.task('test', function (done) {
	new server({
		configFile: __dirname + '/karma.conf.js',
		singleRun: true
	}, done).start();
});

gulp.task('default', ['test', 'concat', 'minify']);

Add test task to Gulp default task.


var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);

app.get('/', function(req, res){
  res.sendfile('./public/index.html');
});

io.on('connection', function(socket){
  console.log('a user connected');
  var x = 1;
  setInterval(function () {
    console.log('Send shit:', ++x);
    socket.emit('message', x);
  }, 1000);
});

http.listen(3000, function(){
  console.log('listening on *:3000');
});

const app = require('express')();
const http = require('http').Server(app);
const io = require('socket.io')(http);

const path = require('path');

app.get('/', function(req, res){
  res.sendFile(path.join(__dirname, '/public/index.html'));
});

io.on('connection', function(socket){
  console.log('a user connected');
  var x = 1;
  setInterval(function () {
    console.log('Send shit:', ++x);
    socket.emit('message', x);
  }, 1000);
});

http.listen(3000, function(){
  console.log('listening on *:3000');
});

Switch to sendFile; squash deprecation notices


var digio_api = require('../digio-api')
, config = require('./config')

var api = new digio_api(config.token)

api.droplets.list_droplets(function (err, data) {
  console.log(data);
});

var digio_api = require('../digio-api')
, config = require('./config')

var api = new digio_api(config.token)

// api.droplets.list_droplets(function (err, data) {
//   console.log(data);
// });
//
// api.domains.list_all_domains(function (err, data) {
//   console.log(data);
// })
//
// api.domains.create_domain_record('tmn2.io', '191.168.0.1', function (err, data) {
//   console.log(data);
// })

Add calls to domains functionality


//
// Test that a Markdown cell is rendered to HTML.
//
casper.notebookTest(function () {
    var output = this.evaluate(function() {
        // Does it make more sense to test the UI or the JS API here?
        //
        // $('#cell_type').val('markdown');
        // $('#cell_type').change();
        // $('#run_b').click();
        //
        // $('#to_markdown').click(); // fails via jQuery UI menubar
        // $('#run_cell').click(); // fails via jQuery UI menubar
        IPython.notebook.to_markdown();
        var cell = IPython.notebook.get_selected_cell();
        cell.set_text('# Foo');
        cell.render();
        return cell.get_rendered();
    });
    this.test.assertEquals(output, '
Foo
', 'Markdown converted to HTML.');
});

//
// Test that a Markdown cell is rendered to HTML.
//
casper.notebookTest(function () {
    // Test JavaScript models.
    var output = this.evaluate(function () {
        IPython.notebook.to_markdown();
        var cell = IPython.notebook.get_selected_cell();
        cell.set_text('# Foo');
        cell.render();
        return cell.get_rendered();
    });
    this.test.assertEquals(output, '
Foo
', 'Markdown JS API works.');

    // Test menubar entries.
    output = this.evaluate(function () {
        $('#to_code').mouseenter().click();
        $('#to_markdown').mouseenter().click();
        var cell = IPython.notebook.get_selected_cell();
        cell.set_text('# Foo');
        $('#run_cell').mouseenter().click();
        return cell.get_rendered();
    });
    this.test.assertEquals(output, '
Foo
', 'Markdown menubar items work.');

    // Test toolbar buttons.
    output = this.evaluate(function () {
        $('#cell_type').val('code').change();
        $('#cell_type').val('markdown').change();
        var cell = IPython.notebook.get_selected_cell();
        cell.set_text('# Foo');
        $('#run_b').click();
        return cell.get_rendered();
    });
    this.test.assertEquals(output, '
Foo
', 'Markdown toolbar items work.');
});

Add tests for Markdown toolbar and menubar entries.