'use strict';

const path = require('path');
const MinifyPlugin = require('babel-minify-webpack-plugin');

module.exports = {
	devtool: 'source-map',
	entry: './lib/index.js',
	output: {
		path: path.resolve(__dirname, 'web'),
		filename: 'index.js'
	},
	plugins: [
		new MinifyPlugin()
	],
	module: {
		rules: [
			{
				test: /\.js$/,
				include: [
					path.resolve(__dirname, 'lib')
				],
				loader: 'babel-loader',
				options: {
					babelrc: false,
					presets: [
						['env', {
							targets: {
								browser: '>1%'
							}
						}]
					]
				}
			}
		]
	}
};

'use strict';

const path = require('path');
const MinifyPlugin = require('babel-minify-webpack-plugin');
const webpack = require('webpack');

module.exports = {
	devtool: 'source-map',
	entry: './lib/index.js',
	output: {
		path: path.resolve(__dirname, 'web'),
		filename: 'index.js'
	},
	plugins: [
		new webpack.optimize.ModuleConcatenationPlugin(),
		new MinifyPlugin()
	],
	module: {
		rules: [
			{
				test: /\.js$/,
				include: [
					path.resolve(__dirname, 'lib')
				],
				loader: 'babel-loader',
				options: {
					babelrc: false,
					presets: [
						['env', {
							targets: {
								browser: '>1%'
							},
							modules: false
						}]
					]
				}
			}
		]
	}
};

Add module concatenation plugin and not transform to commonjs in web env


/**
 *
 *  Copyright 2016 Netflix, Inc.
 *
 *     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.
 *
 */
import Node from '../base/node';
import NodeViewStandard from '../base/nodeViewStandard';

class RegionNode extends Node {
  constructor (node) {
    node.size = node.size || 60;
    super(node, 'region');
    this.loaded = true;
  }

  isDraggable () {
    return true;
  }

  isInteractive () {
    return true;
  }

  render () {
    // Set the default view renderer
    this.view = new NodeViewStandard(this);
  }
}

export default RegionNode;

/**
 *
 *  Copyright 2016 Netflix, Inc.
 *
 *     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.
 *
 */
import FocusedNodeView from '../focused/focusedNodeView';
import Node from '../base/node';
import NodeViewStandard from '../base/nodeViewStandard';

class RegionNode extends Node {
  constructor (node) {
    node.size = node.size || 60;
    super(node, 'region');
    this.loaded = true;
  }

  isDraggable () {
    return true;
  }

  isInteractive () {
    return true;
  }

  render () {
    // Set the default view renderer
    if (this.nodeView === 'focused') {
      this.view = new FocusedNodeView(this);
    } else {
      this.view = new NodeViewStandard(this);
    }
  }
}

export default RegionNode;

Add ability to set specific nodes as focused nodes


const conf = module.exports = require('skatejs-build/webpack.config');
conf.devtool = 'source-map';
conf.entry = {
  'dist/index-with-deps.js': './src/index.js',
  'dist/index-with-deps.min.js': './src/index.js',
};
conf.output.library = 'skate';

Fix webpacked dist so that it exports skate not skatejs to the global.


/* eslint object-shorthand:0 func-names:0 */

const WebpackIsomorphicToolsPlugin = require('webpack-isomorphic-tools/plugin');

module.exports = {
  // debug: true,
  // webpack_assets_file_path: 'webpack-assets.json',
  // webpack_stats_file_path: 'webpack-stats.json',
  assets: {
    images: {
      extensions: ['png', 'jpg', 'jpeg', 'gif'],
      parser: WebpackIsomorphicToolsPlugin.url_loader_parser,
    },
    fonts: {
      extensions: ['eot', 'ttf', 'woff', 'woff2'],
      parser: WebpackIsomorphicToolsPlugin.url_loader_parser,
    },
    svg: {
      extension: 'svg',
      parser: WebpackIsomorphicToolsPlugin.url_loader_parser,
    },
    style_modules: {
      extensions: ['css', 'scss'],
      filter: function (module, regex, options, log) {
        if (options.development) {
          return WebpackIsomorphicToolsPlugin.style_loader_filter(module, regex, options, log);
        }

        return regex.test(module.name);
      },
      path: function (module, options, log) {
        if (options.development) {
          return WebpackIsomorphicToolsPlugin.style_loader_path_extractor(module, options, log);
        }

        return module.name;
      },
      parser: function (module, options, log) {
        if (options.development) {
          return WebpackIsomorphicToolsPlugin.css_modules_loader_parser(module, options, log);
        }

        return module.source;
      },
    },
  },
};

/* eslint-disable */

var WebpackIsomorphicToolsPlugin = require('webpack-isomorphic-tools/plugin');

module.exports = {
  // debug: true,
  // webpack_assets_file_path: 'webpack-assets.json',
  // webpack_stats_file_path: 'webpack-stats.json',
  assets: {
    images: {
      extensions: ['png', 'jpg', 'jpeg', 'gif'],
      parser: WebpackIsomorphicToolsPlugin.url_loader_parser,
    },
    fonts: {
      extensions: ['eot', 'ttf', 'woff', 'woff2'],
      parser: WebpackIsomorphicToolsPlugin.url_loader_parser,
    },
    svg: {
      extension: 'svg',
      parser: WebpackIsomorphicToolsPlugin.url_loader_parser,
    },
    style_modules: {
      extensions: ['css', 'scss'],
      filter: function (module, regex, options, log) {
        if (options.development) {
          return WebpackIsomorphicToolsPlugin.style_loader_filter(module, regex, options, log);
        }

        return regex.test(module.name);
      },
      path: function (module, options, log) {
        if (options.development) {
          return WebpackIsomorphicToolsPlugin.style_loader_path_extractor(module, options, log);
        }

        return module.name;
      },
      parser: function (module, options, log) {
        if (options.development) {
          return WebpackIsomorphicToolsPlugin.css_modules_loader_parser(module, options, log);
        }

        return module.source;
      },
    },
  },
};

Fix es6 error on node 5


// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

exports.CustomEvent = function(type, eventInitDict) {
    var newEvent = document.createEvent('CustomEvent');
    newEvent.initCustomEvent(type,
                             !!(eventInitDict && eventInitDict.bubbles),
                             !!(eventInitDict && eventInitDict.cancelable),
                             (eventInitDict ? initargs.details : null));
    return newEvent;
};

// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

if (typeof window.CustomEvent !== 'function') {
    exports.CustomEvent = function(type, eventInitDict) {
        var newEvent = document.createEvent('CustomEvent');
        newEvent.initCustomEvent(type,
                                 !!(eventInitDict && eventInitDict.bubbles),
                                 !!(eventInitDict && eventInitDict.cancelable),
                                 (eventInitDict ? initargs.details : null));
        return newEvent;
    };
}

Stop clobbering native CustomEvent constructor in polyfill.


/*   Copyright 2017 Agneta Network Applications, LLC.
 *
 *   Source file: portal/services/boot/git/credentials.js
 *
 *   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.
 */
const nodegit = require('nodegit');
const path = require('path');

module.exports = function(app) {

  var base_dir = process.cwd();

  var sshDir = path.join(base_dir, 'ssh');
  var sshPublicKey = path.join(sshDir, 'id_rsa.pub');
  var sshPrivateKey = path.join(sshDir, 'id_rsa');

  app.git.credentials = function(url, username) {
    return nodegit.Cred.sshKeyNew(username, sshPublicKey, sshPrivateKey, '');
  };

};

/*   Copyright 2017 Agneta Network Applications, LLC.
 *
 *   Source file: portal/services/boot/git/credentials.js
 *
 *   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.
 */
const nodegit = require('nodegit');
const Promise = require('bluebird');

module.exports = function(app) {

  return Promise.resolve()
    .then(function() {

      if (!process.env.GIT_PUB || !process.env.GIT_PUB) {
        throw new Error('Git must have SSH credentials');
      }

      var sshPublicKey = process.env.GIT_PUB;
      var sshPrivateKey = process.env.GIT_KEY.replace(/\\n/g,'\n');

      app.git.credentials = function(url, username) {
        return nodegit.Cred.sshKeyMemoryNew(username, sshPublicKey, sshPrivateKey, '');
      };

    });

};

Use ENV variables for SSH


'use strict';

// Assign the video codec to use
module.exports = function(command, file, options, metadata, details) {
  return new Promise((resolve, reject) => {

    // Assign video codec
    command.videoCodec(options.video.codec);

    // Check for hardware acceleration
    // Detection not yet enabled
    if ( options.hwAccel ) {

      // Enable x264 or hevc transcoding for nvidia 10 series cards
      command.videoCodec(( /264/g.exec(options.video.codec) != null ? 'h264' : 'hevc' ) + '_nvenc');
    }

    return resolve();
  });
};

'use strict';

// Assign the video codec to use
module.exports = function(command, file, options, metadata, details) {
  return new Promise((resolve, reject) => {

    // Check for hardware acceleration
    if ( options.hwAccel ) {

      // Enable x264 or hevc transcoding for nvidia 10 series cards
      options.video.codec = ( ( /264/g.exec(options.video.codec) != null ? 'h264' : 'hevc' ) + '_nvenc' );

    // Are we copying source?
    } else if ( options.video.codec === 'copy' ) {

      // Get the codec data from the video stream
      options.video.codec = command.streams.video[0].codec_name;
    }

    // Get a list of the available codecs
    command.getAvailableCodecs((err, codecs) => {

      // Check we have this one and can encode it
      if ( ! codecs[options.video.codec] || ! codecs[options.video.codec].canEncode ) {
        return reject('Codec "' + options.video.codec + '" is not available for encoding.');
      }

      // Assign video codec
      command.videoCodec(options.video.codec);

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

Update engine to check for valid video codec


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

/**
 * Populates inputs with current user information.
 */
async function showCurrentUserInfo() {
  const response = await fetch('/profile-update');
  const userData = await response.json();

  const userName = userData['name'];
  const userInterests = userData['interests'];
  const userSkills = userData['skills'];

  $('#name').val(userName);
  populateExisting('interests', userData);
  populateExisting('skills', userData);
}

function populateExisting(className, userData) {
  const existingLabels = userData[className];
  getTagsScriptWithCallback(function() {
    for (const label of existingLabels) {
      $(`#${className}`).append(buildTagWithoutAdder(className, label));
    }
  });
}

$(document).ready(function() {
  showCurrentUserInfo();
  populatePrefilled('interests');
  populatePrefilled('skills');
});

/**
 * Populates inputs with current user information.
 */
async function showCurrentUserInfo() {
  const response = await fetch('/profile-update');
  const userData = await response.json();

  const userName = userData['name'];
  const userInterests = userData['interests'];
  const userSkills = userData['skills'];

  $('#name').val(userName);
  populateExisting('interests', userData);
  populateExisting('skills', userData);
}

function populateExisting(className, userData) {
  const existingLabels = userData[className];
  getTagsScriptWithCallback(function() {
    for (const label of existingLabels) {
      $(`#${className}`).append(buildTagWithoutAdder(className, label));
    }
  });
}

Add profile prefilled interests and skills


var command = require('../lib/command');
var channel = require('../lib/datachannel');
var fs      = require('../lib/fs');

command.add('STOR', 'STOR <sp> pathname', function (path, output, session) {
  var absolutePath = fs.toAbsolute(path, session.cwd);

  fs.open(absolutePath, 'w', function (err, fd) {
    if (err) {
      err = fs.errorMessage(err, path);
      output.write(err.status, err.msg);
    } else {
      var success = channel.create(session, function (socket, done) {
        // @todo Write to file as it is received to avoid memory issues
        var buffers = [];
        var bytes   = 0;
        var offset  = 0;

        socket.on('data', function (buffer) {
          buffers.push(buffer);
          bytes += buffer.length;
        });

        socket.on('end', function (data) {
          for (var i in buffers) {
            fs.writeSync(fd, buffers[i], 0, buffers[i].length, offset);
            offset += buffers[i].length;
          }

          done();

          output.write(226, 'Transfer Complete');
        });
      });

      if (!success) {
        output.write(425, 'Unable to build data connection: Invalid argument');
      } else {
        output.write(150, 'Opening ' + session.transferType.toUpperCase() + ' mode data connection for ' + path);
      }
    }
  });
});

var command = require('../lib/command');
var channel = require('../lib/datachannel');
var fs      = require('../lib/fs');

command.add('STOR', 'STOR <sp> pathname', function (path, output, session) {
  var absolutePath = fs.toAbsolute(path, session.cwd);
  var stream       = fs.createWriteStream(absolutePath, { flags: 'w' });

  stream.on('open', function (fd) {
    var success = channel.create(session, function (socket, done) {
      socket.on('data', function (buffer) {
        stream.write(buffer);
      });

      socket.on('end', function (data) {
        stream.end();
        done();
        output.write(226, 'Transfer Complete');
      });
    });

    if (!success) {
      output.write(425, 'Unable to build data connection: Invalid argument');
    } else {
      output.write(150, 'Opening ' + session.transferType.toUpperCase() + ' mode data connection for ' + path);
    }
  });

  stream.on('error', function (err) {
    err = fs.errorMessage(err, path);
    output.write(err.status, err.msg);
  });
});

Use write streams for STOR


// Renamed functions

dc.abstractBubbleChart = dc.bubbleMixin;
dc.baseChart = dc.baseMixin;
dc.capped = dc.capMixin;
dc.colorChart = dc.colorMixin;
dc.coordinateGridChart = dc.coordinateGridMixin;
dc.marginable = dc.marginMixin;
dc.stackableChart = dc.stackMixin;

return dc;}
    if(typeof define === "function" && define.amd) {
        define(["d3", "crossfilter"], _dc);
    } else if(typeof module === "object" && module.exports) {
        // When using window global, window.crossfilter is a function
        // When using require, the value will be an object with 'crossfilter'
        // field, so we need to access it here.
        module.exports = _dc(require('d3'), require('crossfilter').crossfilter);
    } else {
        this.dc = _dc(d3, crossfilter);
    }
}
)();

// Renamed functions

dc.abstractBubbleChart = dc.bubbleMixin;
dc.baseChart = dc.baseMixin;
dc.capped = dc.capMixin;
dc.colorChart = dc.colorMixin;
dc.coordinateGridChart = dc.coordinateGridMixin;
dc.marginable = dc.marginMixin;
dc.stackableChart = dc.stackMixin;

return dc;}
    if(typeof define === "function" && define.amd) {
        define(["d3", "crossfilter"], _dc);
    } else if(typeof module === "object" && module.exports) {
        var _d3 = require('d3');
        var _crossfilter = require('crossfilter');
        // When using npm + browserify, 'crossfilter' is a function,
        // since package.json specifies index.js as main function, and it
        // does special handling. When using bower + browserify,
        // there's no main in bower.json (in fact, there's no bower.json),
        // so we need to fix it.
        if (typeof _crossfilter !== "function") {
            _crossfilter = _crossfilter.crossfilter;
        }
        module.exports = _dc(_d3, _crossfilter);
    } else {
        this.dc = _dc(d3, crossfilter);
    }
}
)();

Make browserify case work both for npm and bower.


import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';

moduleForComponent('organization-settings-menu', 'Integration | Component | organization settings menu', {
  integration: true
});

test('it renders', function(assert) {
  // Set any properties with this.set('myProperty', 'value');
  // Handle any actions with this.on('myAction', function(val) { ... });"

  this.render(hbs`{{organization-settings-menu}}`);

  assert.equal(this.$().text().trim(), '');

  // Template block usage:"
  this.render(hbs`
    {{#organization-settings-menu}}
      template block text
    {{/organization-settings-menu}}
  `);

  assert.equal(this.$().text().trim(), 'template block text');
});

import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';

moduleForComponent('organization-settings-menu', 'Integration | Component | organization settings menu', {
  integration: true
});

test('it renders with all its required elements', function(assert) {
  assert.expect(3);

  this.render(hbs`{{organization-settings-menu}}`);

  assert.equal(this.$('.organization-settings-menu').length, 1, 'The component itself renders');
  assert.equal(this.$('li').length, 1, 'All the menu items render');
  assert.equal(this.$('li:eq(0) a').text().trim(), "Organization Profile", 'The profile link render');
});

Add organization settings menu test


module.exports = {
  branchPrefix: 'renovate/',
  dryRun: true,
  username: 'renovate-bot',
  gitAuthor: 'Renovate Bot <bot@renovateapp.com>',
  onboarding: true,
  platform: 'github',
  includeForks: false,
  repositories: [
    'joelnb/dockerfiles',
  ],
};

module.exports = {
  branchPrefix: 'renovate/',
  dryRun: false,
  username: 'renovate-bot',
  gitAuthor: 'Renovate Bot <bot@renovateapp.com>',
  onboarding: true,
  platform: 'github',
  includeForks: false,
  repositories: [
    'joelnb/dockerfiles',
  ],
};

Enable actual changes with renovate


import Notify from 'notifyjs';
import WatchingProvider from './WatchingProvider';

let lastModuleStates = {};

let BuildsNotifier = {

  updateModules: function(modules) {
    modules.forEach((module) => {
      let id = { repo: module.repository, branch: module.branch };
      if (WatchingProvider.isWatching(id) === -1) {
        return;
      }

      if (lastModuleStates[id] === 'IN_PROGRESS' && module.inProgressBuildLink === undefined) {
        this.showNotification(module.repository, module.branch, module.module, module.lastBuildState, module.link);
      }

      lastModuleStates[id] = (module.inProgressBuildLink !== undefined ? 'IN_PROGRESS' : module.lastBuildState);
    });
  },

  showNotification: function(repo, branch, module, state, link) {
    let body = `${repo}[${branch}] ${module}: ${state}`;
    var notification = new Notify('Build Complete', {
        body: body,
        icon: '/images/icon.jpg',
        notifyClick: () => {
          window.open(link);
        }
    });
    notification.show();
  }
};

export default BuildsNotifier;

import Notify from 'notifyjs';
import WatchingProvider from './WatchingProvider';

let lastModuleStates = {};

let BuildsNotifier = {

  updateModules: function(modules) {
    modules.forEach((module) => {
      if (WatchingProvider.isWatching({ repo: module.repository, branch: module.branch }) === -1) {
        return;
      }
      let id = JSON.stringify({ repo: module.repository, branch: module.branch, build: module.module });
      if (lastModuleStates[id] === 'IN_PROGRESS' && module.inProgressBuildLink === undefined) {
        this.showNotification(module.repository, module.branch, module.module, module.lastBuildState, module.link);
      }
      lastModuleStates[id] = (module.inProgressBuildLink !== undefined ? 'IN_PROGRESS' : module.lastBuildState);
    });
  },

  showNotification: function(repo, branch, module, state, link) {
    let body = `${repo}[${branch}] ${module}: ${state}`;
    var notification = new Notify('Build Complete', {
        body: body,
        icon: '/images/icon.jpg',
        notifyClick: () => {
          window.open(link);
        }
    });
    notification.show();
  }
};

export default BuildsNotifier;

Fix bug causing extraneous notifications on repos with mixed-state modules


$(function() {
  $('.field select')
    .width(function(index, width) { return $(this).attr('multiple') ? width + 150 : width + 50; })
    .chosen({disable_search_threshold: 50});
});

$(function() {
  $('.b-admin-form-field select')
    .width(function(index, width) { return $(this).attr('multiple') ? width + 150 : width + 50; })
    .chosen({disable_search_threshold: 50});
});

Fix chosen usage in admin


"use strict";

/*
* Creates a version of λ that cannot be executed concurrently.
*
* Expects λ to take no arguments and to return a Promise.
* When concurrent calls get cancelled, the wrapping function returns
* undefined. Otherwise, it returns the Promise returned by λ.
*/
module.exports = λ => {
    let locked = false;

    return () => {
        if (locked) {
            return;
        }

        locked = true;

        λ().then(
            results => {
                locked = false;

                return results;
            },

            error => {
                locked = false;

                return Promise.reject(error);
            }
        );
    };
};

"use strict";

/*
* Creates a version of λ that cannot be executed concurrently.
*
* Returns the Promise returned by λ.
*/
module.exports = λ => {
    let locked  = false;
    let promise = null;

    return () => {
        if (locked) {
            return promise;
        }

        locked = true;

        promise = λ().then(
            results => {
                locked = false;

                return results;
            },

            error => {
                locked = false;

                return Promise.reject(error);
            }
        );

        return promise;
    };
};

Change return value of noConcurrentCalls to make it return the promise return by λ


'use strict';

module.exports = function () {
  const WebSocket = require('ws');
  var ws = new WebSocket(App.config.logserverIP, {headers: {'token': App.config.token}});
  var self = this;

  ws.on('error', (e) => {
    console.log(e);
  });

  ws.on('open', () => {
    self.log = (payload, logLevel) => {
      var obj = {
        type: logLevel,
        payload: payload
      };

      ws.send(JSON.stringify(obj));
    };
  });
};

'use strict';

module.exports = function () {
  const WebSocket = require('ws');
  var ws = null;
  connect();
  var self = this;

  function connect() {
    ws = new WebSocket(App.config.logserverIP, {headers: {'token': App.config.token}});
    ws.on('error', (e) => {
      console.log(e);
    });

    ws.on('close', () => {
      // Attempt reconnects
      setTimeout(connect, 5000);
    });

    ws.on('open', () => {
      self.log = (payload, logLevel) => {
        var obj = {
          type: logLevel,
          payload: payload
        };

        ws.send(JSON.stringify(obj));
      };
    });
  }
};

Reconnect to WS on close


var configuration = require('../../config/configuration.json')
var utility = require('../../public/method/utility')

module.exports = {
  getSubcampaignModel: function (redisClient, subcampaignHashID, callback) {
    var tableName = configuration.TableMASubcampaignModel + subcampaignHashID
    redisClient.hmget(tableName,
      configuration.ConstantSCMMinBudget,
      configuration.ConstantSCMSubcampaignName,
      configuration.ConstantSCMSubcampaignStyle,
      configuration.ConstantSCMSubcampaignPlan,
      configuration.ConstantSCMSubcampaignPrice,
      configuration.ConstantSCMFileURL,
      function (err, replies) {
        if (err) {
          callback(err, null)
          return
        }
        callback(null, replies)
      }
    )
  },
}

Implement Get Subcampaign Model by HMGET


var SearchStore = require('../store/search');

module.exports = new SearchStore();

var SearchStore = require('../store/search');
var QueryStore = require('../store/query');

module.exports = {
    searchStore: new SearchStore(),
    queryStore: new QueryStore()
};

Refactor built in search store


var cobs = require('../..'),
    http = require('http'),
    fs = require('fs');

function compileFile(filename) {
    var text = fs.readFileSync(filename).toString();
    var parser = new cobs.Parser(text);
    var program = parser.parseProgram();
    program.text = program.command.compile(program);
    return program;
};

var program = compileFile('./factorial.cob');

http.createServer(function(req, res) {
    var runtime = {
        display: function() {
            if (arguments && arguments.length)
                for (var k = 0; k < arguments.length; k++)
                    if (arguments[k])
                        res.write(arguments[k].toString());

            res.write('\r\n');
        }
    }

    cobs.run(program.text, runtime, program);

    res.end();
}).listen(8000);

console.log('Server started, listening at port 8000');

var cobs = require('../..'),
    http = require('http'),
    fs = require('fs');

function compileFile(filename) {
    return cobs.compileProgramFile(filename);
};

var program = compileFile('./factorial.cob');

http.createServer(function(req, res) {
    var runtime = {
        display: function() {
            if (arguments && arguments.length)
                for (var k = 0; k < arguments.length; k++)
                    if (arguments[k])
                        res.write(arguments[k].toString());

            res.write('\r\n');
        }
    }

    program.run(runtime);

    res.end();
}).listen(8000);

console.log('Server started, listening at port 8000');

Refactor factorial web sample to use compileProgramFile


document.addEventListener("DOMContentLoaded", function () {
  var qr_cellsize = 8;
  var qr_margin = 2 * qr_cellsize;
  var qr_levels = ["M", "L"];

  var createImage = function(payload) {
    for (var levelIndex in qr_levels) {
      for (var typeNum = 1; typeNum <= 10; typeNum++) {
        try {
          var qr = qrcode(typeNum, qr_levels[levelIndex]);
          qr.addData(payload);
          qr.make();
          return qr.createImgTag(qr_cellsize, qr_margin);
        } catch(e) {
          if (strStartsWith(e.message, "code length overflow")) {
            // ignore and try to use bigger QR code format
          } else {
            throw e;
          }
        }
      }
    }
  };

  var updateImage = function() {
    payload = document.getElementById("textbox").value;
    document.getElementById("insert-qrcode-here").innerHTML =
      createImage(payload) || "Error. URL too long?";
  };

  var strStartsWith = function(string, prefix) {
    return !string.indexOf(prefix);
  };

  document.getElementById("close").onclick = function() {
    window.close();
  };

  document.getElementById("textbox").onchange = function() {
    updateImage();
  };

  chrome.tabs.getSelected(null, function(tab) {
    document.getElementById("textbox").value = tab.url;
    document.getElementById("textbox").select();
    updateImage();
  });
});

document.addEventListener("DOMContentLoaded", function () {
  var qr_cellsize = 8;
  var qr_margin = 2 * qr_cellsize;
  var qr_levels = ["M", "L"];

  var createImage = function(payload) {
    for (var levelIndex in qr_levels) {
      for (var typeNum = 1; typeNum <= 10; typeNum++) {
        try {
          var qr = qrcode(typeNum, qr_levels[levelIndex]);
          qr.addData(payload);
          qr.make();
          return qr.createImgTag(qr_cellsize, qr_margin);
        } catch(e) {
          if (strStartsWith(e.message, "code length overflow")) {
            // ignore and try to use bigger QR code format
          } else {
            throw e;
          }
        }
      }
    }
  };

  var updateImage = function() {
    payload = document.getElementById("textbox").value;
    document.getElementById("insert-qrcode-here").innerHTML =
      createImage(payload) || "Error. URL too long?";
  };

  var strStartsWith = function(string, prefix) {
    return !string.indexOf(prefix);
  };

  document.getElementById("close").onclick = function() {
    window.close();
  };

  document.getElementById("textbox").onchange = function() {
    updateImage();
  };

  document.getElementById("textbox").onclick = function() {
    this.select();
  };

  chrome.tabs.getSelected(null, function(tab) {
    document.getElementById("textbox").value = tab.url;
    document.getElementById("textbox").select();
    updateImage();
  });
});

Select complete textbox content on click


import React from "react";
import ReactDOM from "react-dom";

import tabify from "../../utils/tabify";
import * as d3 from "d3";
import "./BarGraph.css";

export default class BarGraph extends React.Component {
    render() {

        const { response, configuration, onBarClick } = this.props;

        if (!response || response.error)
            return;

        const data = tabify(response.results);
        const properties = configuration.data;

        return (
            <div className="bar-graph">
                {JSON.stringify(data)}
            </div>
        );
    }
}

BarGraph.propTypes = {
  configuration: React.PropTypes.object,
  response: React.PropTypes.object
};

import React from "react";
import ReactDOM from "react-dom";

import tabify from "../../utils/tabify";
import * as d3 from "d3";
import "./BarGraph.css";

export default class BarGraph extends React.Component {
    render() {

        const { response, configuration, onBarClick } = this.props;
        const { width, height } = this.props;

        if (!response || response.error)
            return;

        const data = tabify(response.results);
        const properties = configuration.data;

        return (
            <div className="bar-graph">
                <svg width={width} height={height}>
                    {data.map((d, i) => (
                        <rect
                            x={i*50}
                            y="0"
                            width="40"
                            height="40"
                        />
                    ))}
                </svg>
            </div>
        );
    }
}

BarGraph.propTypes = {
  configuration: React.PropTypes.object,
  response: React.PropTypes.object
};

Use JSX to render rectangles for bar chart


// We only need to import the modules necessary for initial render
import CoreLayout from '../layouts/CoreLayout/CoreLayout'
import Home from './Home'
import CounterRoute from './Counter'
import TogglRoute from './Toggl'

/*  Note: Instead of using JSX, we recommend using react-router
    PlainRoute objects to build route definitions.   */

export const createRoutes = (store) => ({
  path: '/',
  component: CoreLayout,
  indexRoute: TogglRoute(store),
  childRoutes: [ ]
})

/*  Note: childRoutes can be chunked or otherwise loaded programmatically
    using getChildRoutes with the following signature:

    getChildRoutes (location, cb) {
      require.ensure([], (require) => {
        cb(null, [
          // Remove imports!
          require('./Counter').default(store)
        ])
      })
    }

    However, this is not necessary for code-splitting! It simply provides
    an API for async route definitions. Your code splitting should occur
    inside the route `getComponent` function, since it is only invoked
    when the route exists and matches.
*/

export default createRoutes

// We only need to import the modules necessary for initial render
import CoreLayout from '../layouts/CoreLayout/CoreLayout'
import TogglRoute from './Toggl'

/*  Note: Instead of using JSX, we recommend using react-router
    PlainRoute objects to build route definitions.   */

export const createRoutes = (store) => ({
  path: '/',
  component: CoreLayout,
  indexRoute: TogglRoute(store),
  childRoutes: [ ]
})

/*  Note: childRoutes can be chunked or otherwise loaded programmatically
    using getChildRoutes with the following signature:

    getChildRoutes (location, cb) {
      require.ensure([], (require) => {
        cb(null, [
          // Remove imports!
          require('./Counter').default(store)
        ])
      })
    }

    However, this is not necessary for code-splitting! It simply provides
    an API for async route definitions. Your code splitting should occur
    inside the route `getComponent` function, since it is only invoked
    when the route exists and matches.
*/

export default createRoutes

Remove import of old routes


/*
 * ASCII sparklines.
 */

(function(root) {
    var ticks = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'];

    var spark = function (ints) {
        var max = Math.max.apply(null, ints),
            min = Math.min.apply(null, ints);

        var steps = ints.map(function (tick) {
            var index = Math.round((tick - min) / max * (ticks.length -1));
            return ticks[index];
        });

        return steps.join("");
    }

    if(typeof exports !== 'undefined') {
        if (typeof module !== 'undefined' && module.exports) {
            module.exports = spark;
        }
        exports.spark = spark;
    } else {
        root.spark = spark;
    }

})(this || window);

/*
 * ASCII sparklines.
 */

(function(root) {
    var ticks = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'];

    var spark = function (ints) {
        // Ignore impossible numbers when making graphs
        var pureNumbers = ints.filter(function (num) {
            return (!isNaN(num)) && (!(num === Infinity || num === -Infinity));
        });

        // Find min and max
        var max = Math.max.apply(null, pureNumbers),
            min = Math.min.apply(null, pureNumbers);

        // Convert to chars
        var steps = ints.map(function (tick) {
            if (isNaN(tick)) { return " "; }
            if (tick === Infinity || tick === -Infinity) { return "∞"; }

            var index = Math.round((tick - min) / max * (ticks.length -1));
            return ticks[index];
        });

        return steps.join("");
    }

    if(typeof exports !== 'undefined') {
        if (typeof module !== 'undefined' && module.exports) {
            module.exports = spark;
        }
        exports.spark = spark;
    } else {
        root.spark = spark;
    }

})(this || window);

Work around NaN, Infinity and -Infinity.


var isWindows = /^win/.test(process.platform)

if(isWindows){
  spawn = require('child_process').exec
  spawn(__dirname + "/node_modules/.bin/cjsx.cmd -o build -cw source")
  spawn(__dirname + "/node_modules/.bin/cjsx.cmd -cw test/index.cjsx")
  spawn(__dirname + "/node_modules/.bin/watchify.cmd test/index.js -o test/bundle.js")
}
else{
  spawn = require('child_process').spawn
  spawn(__dirname + "/node_modules/.bin/cjsx -o build -cw source", {stdio:'inherit', stderr:'inherit'})
  spawn(__dirname + "/node_modules/.bin/cjsx -cw test/index.cjsx", {stdio:'inherit', stderr:'inherit'})
  spawn(__dirname + "/node_modules/.bin/watchify test/index.js -o test/bundle.js", {stdio:'inherit', stderr:'inherit'})
}

var isWindows = /^win/.test(process.platform)

if(isWindows){
  spawn = require('child_process').exec
  spawn(__dirname + "/node_modules/.bin/cjsx.cmd -o build -cw source")
  spawn(__dirname + "/node_modules/.bin/cjsx.cmd -cw test/index.cjsx")
  spawn(__dirname + "/node_modules/.bin/watchify.cmd test/index.js -o test/bundle.js")
}
else{
  spawn = require('child_process').spawn
  spawn("./node_modules/.bin/cjsx", ["-o", "build", "-cw", "source"], {stdio:'inherit', stderr:'inherit'})
  spawn("./node_modules/.bin/cjsx", ["-cw", "test/index.cjsx"], {stdio:'inherit', stderr:'inherit'})
  spawn("./node_modules/.bin/watchify", ["test/index.js", "-o", "test/bundle.js"], {stdio:'inherit', stderr:'inherit'})
}

Fix build process on linux


import styled from 'styled-components';

import { navbarHeight } from '../../common-styles/layout';

export const Wrapper = styled.nav`
  align-items: center;
  background-color: #fff;
  color: #666;
  display: flex;
  flex-direction: row;
  font-family: Raleway, 'Open Sans', Helvetica, sans-serif;
  font-size: 14px;
  font-weight: 600;
  height: ${navbarHeight}px;
  justify-content: flex-start;
  left: 0;
  position: fixed;
  right: 0;
  text-transform: uppercase;
  top: 0;
  width: 100%;
`;

export const Logo = styled.div`
  align-items: center;
  background-image: url(${props => props.imgUrl});
  background-size: contain;
  background-repeat: no-repeat;
  border: 2px solid #fff;
  color: #18e28c;
  display: flex;
  flex-direction: row;
  height: ${navbarHeight}px;
  max-height: ${navbarHeight}px;
  width: ${navbarHeight * 2}px;

  & p {
    position: relative;
    left: 80px;
  }
`;

export const ProfileLink = styled.div`
  border-bottom: 2px solid transparent;
  flex: 1;
  text-align: center;
`;

import styled from 'styled-components';

import { navbarHeight } from '../../common-styles/layout';

export const Wrapper = styled.nav`
  align-items: center;
  background-color: #fff;
  color: #666;
  display: flex;
  flex-direction: row;
  font-family: Raleway, 'Open Sans', Helvetica, sans-serif;
  font-size: 14px;
  font-weight: 600;
  height: ${navbarHeight}px;
  justify-content: flex-start;
  left: 0;
  position: fixed;
  right: 0;
  text-transform: uppercase;
  top: 0;
  width: 100%;
  z-index: 1;
`;

export const Logo = styled.div`
  align-items: center;
  background-image: url(${props => props.imgUrl});
  background-size: contain;
  background-repeat: no-repeat;
  border: 2px solid #fff;
  color: #18e28c;
  display: flex;
  flex-direction: row;
  height: ${navbarHeight}px;
  max-height: ${navbarHeight}px;
  width: ${navbarHeight * 2}px;

  & p {
    position: relative;
    left: 80px;
  }
`;

export const ProfileLink = styled.div`
  border-bottom: 2px solid transparent;
  flex: 1;
  text-align: center;
`;

Add z-index to the Header


module.exports = {
  'extends': './index.js',

  'plugins': [
    'react'
  ],

  'ecmaFeatures': {
    'jsx': true
  },

  'rules': {
    'react/display-name': 0,
    'react/jsx-boolean-value': 2,
    'react/jsx-no-undef': 2,
    'react/jsx-uses-react': 1,
    'react/jsx-uses-vars': 1,
    'react/no-did-mount-set-state': [1, 'allow-in-func'],
    'react/no-did-update-set-state': 1,
    'react/no-multi-comp': 1,
    'react/no-unknown-property': 2,
    'react/prop-types': 2,
    'react/react-in-jsx-scope': 1,
    'react/self-closing-comp': 2,
    'react/sort-comp': 2,
    'react/wrap-multilines': 2,
  }
};

module.exports = {
  'extends': './index.js',

  'plugins': [
    'react'
  ],

  'ecmaFeatures': {
    'jsx': true
  },

  'rules': {
    'react/display-name': 0,
    'react/forbid-prop-types': 0,
    'react/jsx-boolean-value': [2, 'never'],
    'react/jsx-curly-spacing': [2, 'never'],
    'react/jsx-indent-props': [2, 2],
    'react/jsx-max-props-per-line': 0,
    'react/jsx-no-duplicate-props': [2, { ignoreCase: true }],
    'react/jsx-no-literals': 0,
    'react/jsx-no-undef': 2,
    'react/jsx-uses-react': 1,
    'react/jsx-uses-vars': 1,
    'react/no-did-mount-set-state': [1, 'allow-in-func'],
    'react/no-did-update-set-state': 1,
    'react/no-multi-comp': 1,
    'react/no-unknown-property': 2,
    'react/prop-types': 2,
    'react/react-in-jsx-scope': 1,
    'react/self-closing-comp': 2,
    'react/sort-comp': 2,
    'react/wrap-multilines': 2,
  }
};

Enable some new React rules


function $_global_sputils_user () {
  (function (window) {
    'use strict';

    var loginAsAnotherUser = function () {
      window.location.href = "/_layouts/closeconnection.aspx?loginasanotheruser=true";
    };

    var logoutUser = function () {
      window.location.href = "/_layouts/closeconnection.aspx";
    };

    // Returns a promise with the current spuser object.
    var getCurrentUser = function () {
      var deferred = $.Deferred();

      var clientContext = new SP.ClientContext.get_current();
      var currentWeb = clientContext.get_web();
      clientContext.load(currentWeb);
      var currentUser = web.get_currentUser();

      //Load currentUser to the context in order to retrieve the user data.
      clientContext.load(currentUser);

      var successHandler = function (currentUser) {
        deferred.resolve(currentUser);
      };

      var errorHandler = function () {
        deferred.reject('error');
      };

      clientContext.executeQueryAsync(successHandler, errorHandler);

      return deferred.promise();
    };

    window.sputils = window.sputils || {};
    window.sputils.user = {
      loginAsAnotherUser: loginAsAnotherUser,
      logoutUser: logoutUser
    };
  })(window);
}
$_global_sputils_user();

function $_global_sputils_user () {
  (function (window) {
    'use strict';

    var loginAsAnotherUser = function () {
      window.location.href = "/_layouts/closeconnection.aspx?loginasanotheruser=true";
    };

    var logoutUser = function () {
      window.location.href = "/_layouts/closeconnection.aspx";
    };

    // Returns a promise with the current spuser object.
    var getCurrentUser = function () {
      var deferred = $.Deferred();

      var clientContext = new SP.ClientContext.get_current();
      var currentWeb = clientContext.get_web();
      var currentUser = web.get_currentUser();

      // Load currentUser to the context in order to retrieve the user data.
      clientContext.load(currentUser);

      // Execute the query. Takes two functions: success and failed that returns the SPUser object or an error message.
      clientContext.executeQueryAsync(function () {
        deferred.resolve(currentUser);
      }, function (sender, args) {
        deferred.reject(args.get_message());
      });

      return deferred.promise();
    };

    window.sputils = window.sputils || {};
    window.sputils.user = {
      loginAsAnotherUser: loginAsAnotherUser,
      logoutUser: logoutUser,
      getCurrentUser: getCurrentUser
    };
  })(window);
}
$_global_sputils_user();

Add comments and tight-ify the code.


import screenSizes from '../../../lib/screen-sizes'

const KapWindow = () => (
  <div className="window">
    <img
      src={require('../../../static/images/kap-3@2x.png?inline')}
      alt="The Kap Window"
    />
    <style jsx>{`
      img {
        max-width: 464px;
        height: auto;
        opacity: 1;
        user-select: none;
        user-drag: none;
      }

      .window {
        z-index: 100;
        max-width: 464px;
        width: 100%;
        height: auto;
        box-shadow: 0 20px 40px 0 rgba(0, 0, 0, 0.1), 0 0 1px rgba(0, 0, 0, 0.4);
        background-color: white;
        border-radius: 4px;
        position: relative;
        margin: 0 32px;
        margin-bottom: -32px;
      }

      @media only screen and (max-width: ${screenSizes.phone}) {
        .window,
        img {
          width: 100% !important;
          height: auto;
          margin-bottom: -12px;
        }
      }
    `}</style>
  </div>
)

export default KapWindow

import screenSizes from '../../../lib/screen-sizes'

const KapWindow = () => (
  <div className="window">
    <img
      src={require('../../../static/images/kap-3@2x.png?inline')}
      alt="The Kap Window"
    />
    <style jsx>{`
      img {
        max-width: 464px;
        height: auto;
        opacity: 1;
        user-select: none;
        user-drag: none;
      }

      .window {
        z-index: 100;
        max-width: 464px;
        max-height: 64px;
        width: 100%;
        height: auto;
        box-shadow: 0 20px 40px 0 rgba(0, 0, 0, 0.1), 0 0 1px rgba(0, 0, 0, 0.4);
        background-color: white;
        border-radius: 4px;
        position: relative;
        margin: 0 32px;
        margin-bottom: -32px;
      }

      @media only screen and (max-width: ${screenSizes.phone}) {
        .window,
        img {
          width: 100% !important;
          height: auto;
          margin-bottom: -12px;
        }
      }
    `}</style>
  </div>
)

export default KapWindow

Set max height for kap window


import React from 'react'

const PoemList = (props) => {
  return (

      {props.store.poems.map((poem, i) => {
        return (

{poem.body}
            <button onClick={() => { props.actions.delete(poem) }}>
              delete
            </button>

        )
      })}

  )
}

export default PoemList

Change list tag to div


import Service from './Service';

module.exports = class Application extends Service {
  get isApplication() {
    return !this.labels.DCOS_PACKAGE_IS_FRAMEWORK;
  }
};

import Service from './Service';

module.exports = class Application extends Service {};

Remove application struct type getter


import isNumber from 'lodash/lang/isNumber';

function ToastFactory($mdToast) {
    return {
        hide() {
            $mdToast.hide();
        },
        show(message, delay) {
            let toast = $mdToast.simple()
                .content(message)
                .action('OK')
                .position('bottom right')
                .hideDelay(isNumber(delay) ? delay : Math.pow(2, 31) - 1);

            $mdToast.show(toast);
        }
    };
}

ToastFactory.$inject = ['$mdToast'];

export default ToastFactory;

function ToastFactory($mdToast) {
    return {
        hide() {
            $mdToast.hide();
        },
        show(message, delay) {
            let toast = $mdToast.simple()
                .content(message)
                .action('OK')
                .position('bottom right')
                .hideDelay(angular.isNumber(delay) ? delay : Math.pow(2, 31) - 1);

            $mdToast.show(toast);
        }
    };
}

ToastFactory.$inject = ['$mdToast'];

export default ToastFactory;

Replace Lodash isNumber with Angular isNumber


//     telegram.link
//     Copyright 2014 Enrico Stara 'enrico.stara@gmail.com'
//     Released under the MIT License
//     https://github.com/enricostara/telegram-mt-node

//     AuthKey class
//
// This class represents the Authentication Key

function AuthKey(id, value, serverSalt) {
    this._id = id;
    this._value = value;
    this._serverSalt = serverSalt;
}

// This method returns the id
AuthKey.prototype.getId = function () {
    return this._id;
};

// This method returns the id
AuthKey.prototype.getValue = function () {
    return this._value;
};

// This method returns the associated serverSalt
AuthKey.prototype.getServerSalt = function () {
    return this._serverSalt;
};

// This method returns the associated serverSalt
AuthKey.prototype.toString = function () {
    return '{ id:' + this._id.toString('hex') +
        ', value:' + this._value.toString('hex') +
        ', serverSalt:' + this._serverSalt.toString('hex') +
        ' }';
};

// Export the class
module.exports = exports = AuthKey;

//     telegram.link
//     Copyright 2014 Enrico Stara 'enrico.stara@gmail.com'
//     Released under the MIT License
//     https://github.com/enricostara/telegram-mt-node

//     AuthKey class
//
// This class represents the Authentication Key

function AuthKey(id, value, serverSalt) {
    this.id = id;
    this.value = value;
    this.serverSalt = serverSalt;
}

AuthKey.prototype.toString = function () {
    return '{ id:' + this.id.toString('hex') +
        ', value:' + this.value.toString('hex') +
        ', serverSalt:' + this.serverSalt.toString('hex') +
        ' }';
};

// Export the class
module.exports = exports = AuthKey;

Remove the getters from the AuthKey class


import configureStore from '../../app/store/configureStore';
const store = configureStore({counter: {count: 0}}, true);

chrome.app.runtime.onLaunched.addListener(function() {
  chrome.app.window.create('window.html', {
    'state': 'maximized'
    // More parameters: https://developer.chrome.com/apps/app_window#CreateWindowOptions
  });
});

Use redux store in chrome app


import React, { Component } from 'react';

import moment from 'moment';

import { kelvinToFahrenheit } from '../util';

import 'weather-icons-tmp/css/weather-icons-wind.css';
import 'weather-icons-tmp/css/weather-icons.css';

import '../stylesheets/components/ForecastItem.scss';

class ForecastItem extends Component {
    render() {
        let temperature = kelvinToFahrenheit(this.props.forecast.main.temp);
        let forecastDate = moment(this.props.forecast.dt_txt);
        let weatherSymbolId = 'wi wi-owm-' + this.props.forecast.weather[0].id;

        return (

{forecastDate.format('MMM Do')}

                    {temperature}

                    {this.props.forecast.weather[0].description}

        );
    }
}

ForecastItem.propTypes = {
    forecast: React.PropTypes.object
};

export default ForecastItem;

import React, { Component } from 'react';

import moment from 'moment';

import { kelvinToFahrenheit } from '../util';

import 'weather-icons-tmp/css/weather-icons.css';
import '../stylesheets/components/ForecastItem.scss';

class ForecastItem extends Component {
    render() {
        let temperature = kelvinToFahrenheit(this.props.forecast.main.temp);
        let forecastDate = moment(this.props.forecast.dt_txt);
        let weatherSymbolId = 'wi wi-owm-' + this.props.forecast.weather[0].id;

        return (

{forecastDate.format('MMM Do')}

                    {temperature}

                    {this.props.forecast.weather[0].description}

        );
    }
}

ForecastItem.propTypes = {
    forecast: React.PropTypes.object
};

export default ForecastItem;

Remove wind-related weathericons css rules


const assert = require('assert');
const http = require('http');
const concat = require('concat-stream');

function createServer() {
  return http.createServer((req, res) => {
    const {method, url, headers} = req;
    if (method === 'GET' && url === '/') {
      res.statusCode = 200;
      res.setHeader('FoO', 'bar');
      res.end('body');
      return;
    }
    if (method === 'GET' && url === '/?foo=baz') {
      res.statusCode = 200;
      res.setHeader('FoO', 'baz');
      res.end('body');
      return;
    }
    if (method === 'POST' && url === '/') {
      assert(headers['content-type'] === 'application/json');
      req.pipe(concat(body => {
        assert(JSON.parse(body.toString()).foo === 'baz');
        res.statusCode = 200;
        res.end('json body');
      }));
      return;
    }
  }).listen(3000);
}

module.exports = createServer;

const assert = require('assert');
const http = require('http');
const concat = require('concat-stream');

function createServer() {
  return http.createServer((req, res) => {
    if (req.method === 'GET' && req.url === '/') {
      res.statusCode = 200;
      res.setHeader('FoO', 'bar');
      res.end('body');
      return;
    }
    if (req.method === 'GET' && req.url === '/?foo=baz') {
      res.statusCode = 200;
      res.setHeader('FoO', 'baz');
      res.end('body');
      return;
    }
    if (req.method === 'POST' && req.url === '/') {
      assert(req.headers['content-type'] === 'application/json');
      req.pipe(concat(body => {
        assert(JSON.parse(body.toString()).foo === 'baz');
        res.statusCode = 200;
        res.end('json body');
      }));
      return;
    }
  }).listen(3000);
}

module.exports = createServer;

Fix tests on node v4


import * as THREE from 'three';
import UberObject from './UberObject';

const Mesh = UberObject(THREE.Mesh);

class ZClippedMesh extends Mesh {
  constructor(geometry, material) {
    super(geometry, material);
    this.castShadow = true;
    this.receiveShadow = true;
  }

  static _mvLength = new THREE.Vector3();

  static _center = new THREE.Vector3();

  static _modelView = new THREE.Matrix4();

  _onBeforeRender(renderer, scene, camera) {
    Mesh.prototype._onBeforeRender.call(this, renderer, scene, camera);

    const geo = this.geometry;
    const { material } = this;
    if (!geo.zClip || !material.uberOptions) {
      return;
    }

    const zClipCoef = 0.5;

    let modelView = ZClippedMesh._modelView;
    const mvLength = ZClippedMesh._mvLength;
    let center = ZClippedMesh._center;

    modelView = modelView.multiplyMatrices(this.matrixWorld, camera.matrixWorldInverse);
    const s = mvLength.setFromMatrixColumn(modelView, 0).length();
    center = center.copy(geo.boundingSphere.center);

    this.localToWorld(center);
    material.uberOptions.zClipValue = camera.position.z - center.z
      - s * (zClipCoef * geo.boundingSphere.radius);
  }
}

export default ZClippedMesh;

import * as THREE from 'three';
import UberObject from './UberObject';

const Mesh = UberObject(THREE.Mesh);

class ZClippedMesh extends Mesh {
  constructor(geometry, material) {
    super(geometry, material);
    this.castShadow = true;
    this.receiveShadow = true;
  }

  static _mvLength = new THREE.Vector3();

  static _center = new THREE.Vector3();

  static _modelView = new THREE.Matrix4();

  _onBeforeRender(renderer, scene, camera) {
    Mesh.prototype._onBeforeRender.call(this, renderer, scene, camera);

    const geo = this.geometry;
    const { material } = this;
    if (!geo.zClip || !material.uberOptions) {
      return;
    }

    const zClipCoef = 0.5;

    const modelView = ZClippedMesh._modelView;
    const mvLength = ZClippedMesh._mvLength;
    const center = ZClippedMesh._center;

    modelView.multiplyMatrices(this.matrixWorld, camera.matrixWorldInverse);
    const s = mvLength.setFromMatrixColumn(modelView, 0).length();
    center.copy(geo.boundingSphere.center);

    this.localToWorld(center);
    material.uberOptions.zClipValue = camera.position.z - center.z
      - s * (zClipCoef * geo.boundingSphere.radius);
  }
}

export default ZClippedMesh;

Remove unnecessary assignment of static variables


import ApiService from '../../../services/Api'

const addHousehold = (household) => {
  return {
    type: 'ADD_HOUSEHOLD_SUCCESS',
    household
  }
}

export const convertLead = (id, monthlyRate, history) => {
  return dispatch => {
    return ApiService.post(`/households/${id}/convert`, monthlyRate)
      .then(client => {
        dispatch(convertLeadComplete(client))
        history.push(`/households/${client.id}`)
      })
  }
}

export const fetchHouseholdsComplete = households => {
  return {
    type: 'FETCH_HOUSEHOLDS_SUCCESS',
    households
  }
}

const convertLeadComplete = (client) => {
  return {
    type: 'CONVERT_LEAD_COMPLETE',
    client
  }
}

export const createHousehold = (household, history) => {
  return dispatch => {
    return ApiService.post(`/households`, household)
      .then(data => {
        dispatch(addHousehold(data))
        history.replace('/households/leads')
      })
  }
}

export const fetchHouseholds = () => {
  return dispatch => {
    return ApiService.get(`/households`)
      .then(data => {
        dispatch(fetchHouseholdsComplete(data))
      })

  }
}

import ApiService from '../../../services/Api'

const addHousehold = (household) => {
  return {
    type: 'ADD_HOUSEHOLD_SUCCESS',
    household
  }
}

export const convertLead = (id, monthlyRate, history) => {
  return dispatch => {
    return ApiService.post(`/households/${id}/convert`, monthlyRate)
      .then(client => {
        dispatch(convertLeadComplete(client))
        history.push(`/households/clients/${client.id}`)
      })
  }
}

export const fetchHouseholdsComplete = households => {
  return {
    type: 'FETCH_HOUSEHOLDS_SUCCESS',
    households
  }
}

const convertLeadComplete = (client) => {
  return {
    type: 'CONVERT_LEAD_COMPLETE',
    client
  }
}

export const createHousehold = (household, history) => {
  return dispatch => {
    return ApiService.post(`/households`, household)
      .then(data => {
        dispatch(addHousehold(data))
        history.replace('/households/leads')
      })
  }
}

export const fetchHouseholds = () => {
  return dispatch => {
    return ApiService.get(`/households`)
      .then(data => {
        dispatch(fetchHouseholdsComplete(data))
      })

  }
}

Change redirect on conversion of lead


/**
 * public/src/js/controllers/search.js - delish
 *
 * Licensed under MIT license.
 * Copyright (C) 2017 Karim Alibhai.
 */

import sock from '../socket'
import debounce from 'debounce'
import getCurrentLocation from '../location'

export default ['$scope', '$searchParams', ($scope, $searchParams) => {
  $scope.search = debounce(query => {
    if (!query) {
      $scope.hint = ''
      return $scope.$apply()
    }

    let { lat, lng } = getCurrentLocation()

    sock.emit('autocomplete', {
      query,
      lat,
      lng,
      radius: $searchParams.radius
    })
  }, 500)

  $scope.prediction = () => {
    return $scope.hint && $scope.query ? $scope.query + $scope.hint.substr($scope.query.length) : ''
  }

  sock.on('autocomplete:results', result => {
    $scope.hint = result
    $scope.$apply()
  })
}]

/**
 * public/src/js/controllers/search.js - delish
 *
 * Licensed under MIT license.
 * Copyright (C) 2017 Karim Alibhai.
 */

import sock from '../socket'
import debounce from 'debounce'
import { getCurrentLocation } from '../location'
import { getMap } from '../map'

export default ['$scope', ($scope) => {
  $scope.search = debounce(query => {
    if (!query) {
      $scope.hint = ''
      return $scope.$apply()
    }

    let { lat, lng } = getCurrentLocation()

    sock.emit('autocomplete', {
      query,
      lat,
      lng,
      radius: getMap().radius()
    })
  }, 500)

  $scope.prediction = () => {
    return $scope.hint && $scope.query ? $scope.query + $scope.hint.substr($scope.query.length) : ''
  }

  $scope.complete = evt => {
    if (evt.which === 39) {
      $scope.query = $scope.prediction()
    }
  }

  sock.on('autocomplete:results', result => {
    $scope.hint = result
    $scope.$apply()
  })
}]

Update autocomplete feature with API changes


import Component from '@ember/component';
import { next } from '@ember/runloop';
import { get, set, observer } from '@ember/object'
import layout from './template';
import { inject as service } from '@ember/service';
import C from 'shared/utils/constants';

export default Component.extend({
  settings: service(),

  layout,

  value: '',
  mode:  'automatic',

  init() {
    this._super(...arguments);

    const xip = get(this, `settings.${ C.SETTING.INGRESS_IP_DOMAIN }`);
    const host = get(this, 'value');

    if ( host && host === xip ) {
      set(this, 'mode', 'automatic');
    } else if ( host ) {
      set(this, 'mode', 'manual');
    }

    next(() => {
      this.modeChanged();
    });
  },

  modeChanged: observer('mode', function() {
    const mode = get(this, 'mode');

    const xip = get(this, `settings.${ C.SETTING.INGRESS_IP_DOMAIN }`);

    if ( mode === 'automatic' ) {
      set(this, 'value', xip);
    } else {
      if ( get(this, 'value') === xip ) {
        set(this, 'value', '');
      }
    }
  }),
});

import Component from '@ember/component';
import { get, set, observer } from '@ember/object'
import layout from './template';
import { inject as service } from '@ember/service';
import C from 'shared/utils/constants';

export default Component.extend({
  settings: service(),

  layout,

  value: '',
  mode:  'automatic',

  init() {
    this._super(...arguments);

    const xip = get(this, `settings.${ C.SETTING.INGRESS_IP_DOMAIN }`);
    const host = get(this, 'value');

    if ( host && host === xip ) {
      set(this, 'mode', 'automatic');
    } else {
      set(this, 'mode', 'manual');
    }
  },

  modeChanged: observer('mode', function() {
    const mode = get(this, 'mode');

    const xip = get(this, `settings.${ C.SETTING.INGRESS_IP_DOMAIN }`);

    if ( mode === 'automatic' ) {
      set(this, 'value', xip);
    } else {
      if ( get(this, 'value') === xip ) {
        set(this, 'value', '');
      }
    }
  }),
});

Fix hostname answer issue in catalog


const notifications = {
  namespaced: true,
  state: {
    isFetching: false,
    isErrored: false,
    error: [],
    notifications: []

  },
  computed: {},
  getters: {},
  mutations: {
    setNotifications (state, notifications) {
      state.notifications = notifications;
      state.isFetching = false;
      state.isErrored = false;
      state.error = [];
    },

    setRequestError (state, error) {
      state.isFetching = false;
      state.isErrored = true;
      state.error = error;
      state.notifications = [];
    },

    setFetchingState (state) {
      state.isFetching = true;
      state.isErrored = false;
      state.error = [];
      state.notifications = [];
    }
  },
  actions: {
    fetchNotifications (context, options) {
      context.commit('setFetchingState');
      context.rootState.client.getConfig(function (err, _, data) {
        if (err) {
          context.commit('setRequestError', err);
          return;
        }

        context.commit('setNotifications', data.unfiltered_organization_notifications);
        context.dispatch('markNotificationsAsRead', options);
      });
    },
    markNotificationsAsRead (context, options) {
      const now = new Date();
      context.state.notifications.forEach(notification => {
        const {
          userId,
          apiKey
        } = options;
        if (!notification.read_at) {
          const notificationCopy = Object.assign({}, notification);
          notificationCopy.read_at = now.toISOString();
          context.rootState.client.updateNotification(userId, apiKey, notificationCopy);
        }
      });
    }
  }
};

export default notifications;

const notifications = {
  namespaced: true,
  state: {
    isFetching: false,
    isErrored: false,
    error: [],
    notifications: []

  },
  computed: {},
  getters: {},
  mutations: {
    setNotifications (state, notifications) {
      state.notifications = notifications;
      state.isFetching = false;
      state.isErrored = false;
      state.error = [];
    },

    setRequestError (state, error) {
      state.isFetching = false;
      state.isErrored = true;
      state.error = error;
      state.notifications = [];
    },

    setFetchingState (state) {
      state.isFetching = true;
      state.isErrored = false;
      state.error = [];
      state.notifications = [];
    }
  },
  actions: {
    fetchNotifications (context, options) {
      context.commit('setFetchingState');
      context.rootState.client.getConfig(function (err, _, data) {
        if (err) {
          context.commit('setRequestError', err);
          return;
        }

        context.commit('setNotifications', data.unfiltered_organization_notifications);
        context.dispatch('markNotificationsAsRead', options);
      });
    },
    markNotificationsAsRead (context, options) {
      const now = new Date();
      context.state.notifications.forEach(notification => {
        const { userId, apiKey } = options;
        if (!notification.read_at) {
          const notificationCopy = { ...notification, read_at: now.toISOString() };
          context.rootState.client.updateNotification(userId, apiKey, notificationCopy);
        }
      });
    }
  }
};

export default notifications;

Use spread operator to copy notification


'use strict';
require('../helpers');
const assert  = require('assert');
const Ironium = require('../..');

describe('Queue with delay', function() {

  const captureQueue = Ironium.queue('capture');

  // Capture processed jobs here.
  const processed = [];

  before(function() {
    captureQueue.eachJob(function(job) {
      processed.push(job);
      return Promise.resolve();
    });
  });

  before(function() {
    return captureQueue.delayJob('delayed', '2s');
  });
  before(Ironium.runOnce);

  it('should not process immediately', function() {
    assert.equal(processed.length, 0);
  });

  describe('after short delay', function() {
    before(function(done) {
      setTimeout(done, 1500);
    });
    before(Ironium.runOnce);

    it('should not process job', function() {
      assert.equal(processed.length, 0);
    });
  });

  describe('after sufficient delay', function() {
    before(function(done) {
      setTimeout(done, 1000);
    });
    before(Ironium.runOnce);

    it('should process job', function() {
      assert.equal(processed.length, 1);
      assert.equal(processed[0], 'delayed');
    });
  });

});

'use strict';
require('../helpers');
const assert  = require('assert');
const Ironium = require('../..');

describe('Queue with delay', function() {

  const captureQueue = Ironium.queue('capture');

  // Capture processed jobs here.
  const processed = [];

  before(function() {
    Ironium.configure({ concurrency: 1 });
  });

  before(function() {
    captureQueue.eachJob(function(job) {
      processed.push(job);
      return Promise.resolve();
    });
  });

  before(function() {
    return captureQueue.delayJob('delayed', '2s');
  });
  before(Ironium.runOnce);

  it('should not process immediately', function() {
    assert.equal(processed.length, 0);
  });

  describe('after short delay', function() {
    before(function(done) {
      setTimeout(done, 1500);
    });
    before(Ironium.runOnce);

    it('should not process job', function() {
      assert.equal(processed.length, 0);
    });
  });

  describe('after sufficient delay', function() {
    before(function(done) {
      setTimeout(done, 1000);
    });
    before(Ironium.runOnce);

    it('should process job', function() {
      assert.equal(processed.length, 1);
      assert.equal(processed[0], 'delayed');
    });
  });

});

Fix configuration leaking out from another test.


import test from 'tape';
import React from 'react';

import {PostLikes} from 'src/components/post-likes';
import sd from 'skin-deep';

const renderLikes = (likes, omittedLikes = 0) => {
  const post = {omittedLikes};
  const me = {id: 123};

  const tree = sd.shallowRender(React.createElement(PostLikes, {likes, post, me}));
  return tree.getRenderOutput().props.children[1].props.children;
};

const getRenderedOmmitedLikes = (likes, omittedLikes) => {

  const likeList = renderLikes(likes, omittedLikes);

  const lastLike = likeList[likeList.length - 1];

  const ommitedLikesNode = lastLike.props.children[0];

  const ommitedLikesNumber = ommitedLikesNode.props.children[0];

  return ommitedLikesNumber;
};

test('PostLikes renders all likes', t => {

  let likes = [];

  for (var i = 0; i < 6; i++) {
    likes.push({});

    const renderedLikes = renderLikes(likes);

    t.equals(renderedLikes.length, likes.length);
  }

  t.end();
});

test('PostLikes renders omitted likes number', t => {

  const ommitLikes = 10;

  const ommitedLikes = getRenderedOmmitedLikes([{}], ommitLikes);

  t.equals(ommitedLikes, ommitLikes);

  t.end();
});

import test from 'tape';
import React from 'react';

import {PostLikes} from 'src/components/post-likes';
import sd from 'skin-deep';

const renderLikes = (likes, omittedLikes = 0) => {
  const post = {omittedLikes};
  const me = {id: 123};

  const tree = sd.shallowRender(React.createElement(PostLikes, {likes, post, me}));
  return tree.getRenderOutput().props.children[1].props.children;
};

const getRenderedOmmitedLikes = (likes, omittedLikes) => {

  const likeList = renderLikes(likes, omittedLikes);

  const lastLike = likeList[likeList.length - 1];

  const ommitedLikesNode = lastLike.props.children[0];

  const ommitedLikesNumber = ommitedLikesNode.props.children[0].props.children[0];

  return ommitedLikesNumber;
};

test('PostLikes renders all likes', t => {

  let likes = [];

  for (var i = 0; i < 6; i++) {
    likes.push({});

    const renderedLikes = renderLikes(likes);

    t.equals(renderedLikes.length, likes.length);
  }

  t.end();
});

test('PostLikes renders omitted likes number', t => {

  const ommitLikes = 10;

  const ommitedLikes = getRenderedOmmitedLikes([{}], ommitLikes);

  t.equals(ommitedLikes, ommitLikes);

  t.end();
});

Fix tests for post likes


( function () {
	// Extend QUnit.module to provide a fixture element. This used to be in tests/index.html, but
	// dynamic test runners like Karma build their own web page.
	( function () {
		var orgModule = QUnit.module;

		QUnit.module = function ( name, localEnv ) {
			localEnv = localEnv || {};
			orgModule( name, {
				setup: function () {
					this.fixture = document.createElement( 'div' );
					this.fixture.id = 'qunit-fixture';
					document.body.appendChild( this.fixture );

					if ( localEnv.setup ) {
						localEnv.setup.call( this );
					}
				},
				teardown: function () {
					if ( localEnv.teardown ) {
						localEnv.teardown.call( this );
					}

					this.fixture.parentNode.removeChild( this.fixture );
				}
			} );
		};
	}() );

	/**
	 * Utility for creating iframes.
	 * @return {HTMLElement}
	 */
	QUnit.tmpIframe = function () {
		var iframe = document.createElement( 'iframe' );
		// Will be removed automatically by module teardown
		document.getElementById( 'qunit-fixture' ).appendChild( iframe );
		return iframe;
	};

}() );

( function () {
	// Extend QUnit.module to provide a fixture element. This used to be in tests/index.html, but
	// dynamic test runners like Karma build their own web page.
	( function () {
		var orgModule = QUnit.module;

		QUnit.module = function ( name, localEnv ) {
			localEnv = localEnv || {};
			orgModule( name, {
				beforeEach: function () {
					this.fixture = document.createElement( 'div' );
					this.fixture.id = 'qunit-fixture';
					document.body.appendChild( this.fixture );

					if ( localEnv.name ) {
						localEnv.name.call( this );
					}
				},
				afterEach: function () {
					if ( localEnv.afterEach ) {
						localEnv.afterEach.call( this );
					}

					this.fixture.parentNode.removeChild( this.fixture );
				}
			} );
		};
	}() );

	/**
	 * Utility for creating iframes.
	 * @return {HTMLElement}
	 */
	QUnit.tmpIframe = function () {
		var iframe = document.createElement( 'iframe' );
		// Will be removed automatically by afterEach
		document.getElementById( 'qunit-fixture' ).appendChild( iframe );
		return iframe;
	};

}() );

Fix broken setup/teardown in tests/index.html


var assert = require("assert");

var JSZip = require("jszip");

var zipfile = require("../../lib/zipfile");
var styleMap = require("../../lib/docx/style-map");
var test = require("../testing").test;

describe("zipfile", function() {
    test('embedded style map can be read after being written', function() {
        var zip = emptyZipFile();

        styleMap.writeStyleMap(zip, "p => h1");

        return styleMap.readStyleMap(zip).then(function(contents) {
            assert.equal(contents, "p => h1");
        });
    });
});

function emptyZipFile() {
    var zip = new JSZip();
    var buffer = zip.generate({type: "arraybuffer"});
    return zipfile.openArrayBuffer(buffer);
}

var assert = require("assert");

var JSZip = require("jszip");

var zipfile = require("../../lib/zipfile");
var styleMap = require("../../lib/docx/style-map");
var test = require("../testing").test;

describe("zipfile", function() {
    test('embedded style map can be read after being written', function() {
        var zip = emptyZipFile();

        styleMap.writeStyleMap(zip, "p => h1");

        return styleMap.readStyleMap(zip).then(function(contents) {
            assert.equal(contents, "p => h1");
        });
    });

    test('embedded style map is written to separate file', function() {
        var zip = emptyZipFile();

        styleMap.writeStyleMap(zip, "p => h1");

        return zip.read("mammoth/style-map", "utf8").then(function(contents) {
            assert.equal(contents, "p => h1");
        });
    });
});

function emptyZipFile() {
    var zip = new JSZip();
    var buffer = zip.generate({type: "arraybuffer"});
    return zipfile.openArrayBuffer(buffer);
}

Test that embedded style map is written to separate file


'use strict';

const browsers = [
  'last 1 Chrome versions',
  'last 1 Firefox versions',
  'last 1 Safari versions'
];

const isCI = !!process.env.CI;
const isProduction = process.env.EMBER_ENV === 'production';

if (isCI || isProduction) {
  browsers.push('ie 11');
}

module.exports = {
  browsers
};

Fix 'use strict' linting error NotLikeThis


/**
 * Default hooks
 *
 * (order still matters for now for some of these-
 *  but mostly not, due to ouruse of events...
 *  ...but for a few core hooks, e.g. `moduleloader`,
 *  it still does.)
 */

module.exports = {
  'moduleloader': true,
  'logger': true,
  'request': true,
  'orm': 'sails-hook-orm',
  'views': true,
  'blueprints': true,
  'responses': true,
  'helpers': 'sails-hook-helpers',
  'sockets': 'sails-hook-sockets',
  'pubsub': true,
  'policies': true,
  'services': true,
  'security': true,
  'i18n': true,
  'userconfig': true,
  'session': true,
  'grunt': true,
  'http': true,
  'userhooks': true
};

/**
 * Default hooks
 *
 * (order still matters for now for some of these-
 *  but mostly not, due to ouruse of events...
 *  ...but for a few core hooks, e.g. `moduleloader`,
 *  it still does.)
 */

module.exports = {
  'moduleloader': true,//<< TODO: absorb into core (i.e. federate its methods out to the places where they are used and remove support for `sails.modules`)
  'logger': true,//<< TODO: absorb into core (i.e. like what we did w/ the controllers hook -- can live in `lib/app/private/log-ship.js`, and the rest can be inlined)
  'request': true,
  'orm': 'sails-hook-orm',
  'views': true,
  'blueprints': true,
  'responses': true,
  'helpers': 'sails-hook-helpers',//<< TODO: pull this into a built-in core hook in `lib/hooks/helpers` -- then replace README in sails-hook-helpers repo with link and short explanation, and publish major version of sails-hook-helpers on NPM
  'sockets': 'sails-hook-sockets',
  'pubsub': true,//<< TODO: **merge into sails-hook-sockets -- i.e. if orm hook available, then sails-hook-sockets decorates models with RPS methods**
  'policies': true,
  'services': true,
  'security': true,
  'i18n': true,
  'userconfig': true,//<< TODO: absorb into core (i.e. like what we did w/ the controllers hook -- can live in `lib/app/configuration`)
  'session': true,
  'grunt': true,//<< TODO: extrapolate (see https://github.com/boogerlad/sails-hook-grunt/issues/1 and https://github.com/mikermcneil/sails-hook-grunt)
  'http': true,
  'userhooks': true//<< TODO: absorb into core (i.e. like what we did w/ the controllers hook -- its logic can live in `lib/app/private`, and be driven by `lib/hooks/index.js`)
};

Add TODOs explaining short summary of plan for each core hook.


import Ember from 'ember';

export default Ember.Mixin.create({

  /**
      Parse the links in the JSONAPI response and convert to a meta-object
  */
  normalizeQueryResponse(store, clazz, payload) {
    const result = this._super(...arguments);
    result.meta = result.meta || {};

    if (payload.links) {
      result.meta.pagination = this.createPageMeta(payload.links);
    }

    return result;
  },

  /**
     Transforms link URLs to objects containing metadata
     E.g.
     links: {
         previous: '/streets?page[number]=1&page[size]=10
         next: '/streets?page[number]=3&page[size]=10
     }

     will be converted to

     meta: {
         previous: { number: 1, size: 10 },
         next: { number: 3, size: 10 }
     }
   */
  createPageMeta(data) {
    let meta = {};

    Object.keys(data).forEach(type => {
      const link = data[type];
      meta[type] = {};
      let a = document.createElement('a');
      a.href = link;

      a.search.slice(1).split('&').forEach(pairs => {
        const [param, value] = pairs.split('=');

        if (param === 'page[number]') {
          meta[type].number = parseInt(value);
        }
        if (param === 'page[size]') {
          meta[type].size = parseInt(value);
        }

      });
      a = null;
    });

    return meta;
  }

});

import Ember from 'ember';

export default Ember.Mixin.create({

  /**
      Parse the links in the JSONAPI response and convert to a meta-object
  */
  normalizeQueryResponse(store, clazz, payload) {
    const result = this._super(...arguments);
    result.meta = result.meta || {};

    if (payload.links) {
      result.meta.pagination = this.createPageMeta(payload.links);
    }

    return result;
  },

  /**
     Transforms link URLs to objects containing metadata
     E.g.
     links: {
         previous: '/streets?page[number]=1&page[size]=10&sort=name
         next: '/streets?page[number]=3&page[size]=10&sort=name
     }

     will be converted to

     meta: {
         previous: { number: 1, size: 10 },
         next: { number: 3, size: 10 },
         sort: 'name'
     }
   */
  createPageMeta(data) {
    let meta = {};

    Object.keys(data).forEach(type => {
      const link = data[type];
      meta[type] = {};
      let a = document.createElement('a');
      a.href = link;

      a.search.slice(1).split('&').forEach(pairs => {
        const [param, value] = pairs.split('=');

        if (param === 'page[number]') {
          meta[type].number = parseInt(value);
        } else if (param === 'page[size]') {
          meta[type].size = parseInt(value);
        } else {
          meta[param] = value;
        }

      });
      a = null;
    });

    return meta;
  }

});

Add parsing of simple key-value query params (sort, filter, etc.)


'use strict';
/**
 * Task runner for unit tests
 *
 * How To:
 *
 * Using Gulp.js to check your code quality
 * https://marcofranssen.nl/using-gulp-js-to-check-your-code-quality/
 */

var gulp = require('gulp');
var phpunit = require('gulp-phpunit');
var notify  = require('gulp-notify');

gulp.task('phpunit', function () {
    var options = {debug: false, notify: true};
    gulp.src('phpunit.xml')
      .pipe(phpunit('vendor/bin/phpunit --verbose tests', options))
      .on('error', notify.onError({
        title: "Failed Tests!",
        message: "Error(s) occurred during testing..."
      }));
});

gulp.task('watch', function () {
  gulp.watch(['composer.json', 'phpunit.xml', './**/*.php', './**/*.inc', '!vendor/**/*', '!node_modules/**/*'],
    function (event) {
        console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
    });
  gulp.watch('composer.json', ['dump-autoload']);
  gulp.watch(['phpunit.xml', './**/*.php', './**/*.inc', '!vendor/**/*', '!node_modules/**/*'], ['phpunit']);
});

gulp.task('default', ['phpunit', 'watch']);

'use strict';
/**
 * Task runner for unit tests
 *
 * How To:
 *
 * Using Gulp.js to check your code quality
 * https://marcofranssen.nl/using-gulp-js-to-check-your-code-quality/
 */

var gulp = require('gulp');
var phplint = require('phplint').lint;
var phpunit = require('gulp-phpunit');
var notify  = require('gulp-notify');

gulp.task('phplint', function (cb) {
    phplint(['./src/*.php', '!node_modules/**/*', '!vendor/**/*'],  { limit: 10 }, function (err, stdout, stderr) {
      if (err) {
        cb(err);
        process.exit(1);
      }
      cb();
    });
});

gulp.task('phpunit', function () {
    var options = {debug: false, notify: true};
    gulp.src('phpunit.xml')
      .pipe(phpunit('vendor/bin/phpunit --verbose tests', options))
      .on('error', notify.onError({
        title: "Failed Tests!",
        message: "Error(s) occurred during testing..."
      }));
});

gulp.task('watch', function () {
  gulp.watch(['composer.json', 'phpunit.xml', './**/*.php', './**/*.inc', '!vendor/**/*', '!node_modules/**/*'],
    function (event) {
        console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
    });
  gulp.watch('composer.json', ['dump-autoload']);
  gulp.watch(['phpunit.xml', './**/*.php', './**/*.inc', '!vendor/**/*', '!node_modules/**/*'], ['phplint', 'phpunit']);
});

gulp.task('default', ['phplint', 'phpunit', 'watch']);
gulp.task('test', ['phplint', 'phpunit']);

Add phplint into gulp pipline


import user from './mutations'

const defaults = {
    photoURL: 'http://gravatar.com/avatar?d=mm&s=100'
}

const state = {
    uid:            null,
    email:          null,
    displayName:    null,
    photoURL:       defaults.photoURL
}

const mutations = {
    [user.RESET]: (state) => {
        state.uid           = null
        state.email         = null
        state.displayName   = null
        state.photoURL      = defaults.photoURL
    },
    [user.CHANGED]: (state, user) => {
        // Update only what's changed
        for (const key in state) {
            if (key in user) {
                state[key] = user[key]
            }
        }
    }
}

export default {
    state,
    mutations
}

import user from './mutations'

const defaults = {
    photoURL: 'https://gravatar.com/avatar?d=mm&s=100'
}

const state = {
    uid:            null,
    email:          null,
    displayName:    null,
    photoURL:       defaults.photoURL
}

const mutations = {
    [user.RESET]: (state) => {
        state.uid           = null
        state.email         = null
        state.displayName   = null
        state.photoURL      = defaults.photoURL
    },
    [user.CHANGED]: (state, user) => {
        // Update only what's changed
        for (const key in state) {
            if (key in user) {
                state[key] = user[key]
            }
        }
    }
}

export default {
    state,
    mutations
}

Use SSL for external resources.


const semver = require('semver')
const BaseChecker = require('./../base-checker')

const ruleId = 'compiler-version'
const meta = {
  type: 'security',

  docs: {
    description: `Compiler version must satisfy a semver requirement.`,
    category: 'Security Rules'
  },

  isDefault: false,
  recommended: true,
  defaultSetup: ['error', '^0.5.7'],

  schema: [
    {
      type: 'array',
      items: [{ type: 'string' }],
      uniqueItems: true,
      minItems: 2
    }
  ]
}

class CompilerGTChecker extends BaseChecker {
  constructor(reporter, config) {
    super(reporter, ruleId, meta)

    this.requirement = (config && config.getString(ruleId, '^0.5.8')) || '^0.5.8'
  }

  exitVersionConstraint(ctx) {
    const versionNode =
      (this.isVersionOperator(ctx.children[0]) && ctx.children[1]) || ctx.children[0]

    if (!semver.satisfies(versionNode.getText(), this.requirement)) {
      this.error(
        ctx,
        `Compiler version ${versionNode.getText()} does not satisfy the ${
          this.requirement
        }semver requirement`
      )
    }
  }

  isVersionOperator(ctx) {
    return ctx.constructor.name.includes('VersionOperator')
  }
}

module.exports = CompilerGTChecker

const semver = require('semver')
const BaseChecker = require('./../base-checker')

const ruleId = 'compiler-version'
const meta = {
  type: 'security',

  docs: {
    description: `Compiler version must satisfy a semver requirement.`,
    category: 'Security Rules'
  },

  isDefault: false,
  recommended: true,
  defaultSetup: ['error', '^0.5.7'],

  schema: [
    {
      type: 'array',
      items: [{ type: 'string' }],
      uniqueItems: true,
      minItems: 2
    }
  ]
}

class CompilerVersionChecker extends BaseChecker {
  constructor(reporter, config) {
    super(reporter, ruleId, meta)

    this.requirement = (config && config.getString(ruleId, '^0.5.8')) || '^0.5.8'
  }

  exitVersionConstraint(ctx) {
    const versionNode =
      (this.isVersionOperator(ctx.children[0]) && ctx.children[1]) || ctx.children[0]

    if (!semver.satisfies(versionNode.getText(), this.requirement)) {
      this.error(
        ctx,
        `Compiler version ${versionNode.getText()} does not satisfy the ${
          this.requirement
        } semver requirement`
      )
    }
  }

  isVersionOperator(ctx) {
    return ctx.constructor.name.includes('VersionOperator')
  }
}

module.exports = CompilerVersionChecker

Change class name to CompilerVersionChecker and add space in the description error


'use strict';

/**
* Typed array constructor which returns a typed array representing an array of twos-complement 16-bit signed integers in the platform byte order.
*
* @module @stdlib/array/int16
*
* @example
* var ctor = require( '@stdlib/array/int16' );
*
* var arr = new ctor( 10 );
* // returns <Int16Array>
*/

// MODULES //

var hasInt16ArraySupport = require( '@stdlib/utils/detect-int16array-support' );

// MAIN //

var ctor;
if ( hasInt16ArraySupport() ) {
	ctor = require( './int16array.js' );
} else {
	ctor = require( './polyfill.js' );
}

// EXPORTS //

module.exports = ctor;

'use strict';

/**
* Typed array constructor which returns a typed array representing an array of twos-complement 16-bit signed integers in the platform byte order.
*
* @module @stdlib/array/int16
*
* @example
* var ctor = require( '@stdlib/array/int16' );
*
* var arr = new ctor( 10 );
* // returns <Int16Array>
*/

// MODULES //

var hasInt16ArraySupport = require( '@stdlib/utils/detect-int16array-support' );
var builtin = require( './int16array.js' );
var polyfill = require( './polyfill.js' );

// MAIN //

var ctor;
if ( hasInt16ArraySupport() ) {
	ctor = builtin;
} else {
	ctor = polyfill;
}

// EXPORTS //

module.exports = ctor;

Refactor to avoid dynamic module resolution


const DEFAULT_ENV = {}

module.exports = {
  branches: {
    "development": {
      gradleCommand: 'assembleDev',
      envFile: ".env.dev",
      env: Object.assign({}, DEFAULT_ENV, {}),
    },
    "master": {
      gradleCommand: 'assembleRelease',
      envFile: ".env.production",
      env: Object.assign({}, DEFAULT_ENV, {}),
    }
  }
}

const DEFAULT_ENV = {}

module.exports = {
  branches: {
    "development": {
      gradleCommand: 'assembleRelease',
      envFile: ".env.dev",
      env: Object.assign({}, DEFAULT_ENV, {}),
    },
    "master": {
      gradleCommand: 'assembleRelease',
      envFile: ".env.production",
      env: Object.assign({}, DEFAULT_ENV, {}),
    }
  }
}

Switch to assembleRelease on development


var multiline = require('multiline');
var docopt = require('docopt');
var Liftoff = require('liftoff');
var tildify = require('tildify');
var chalk = require('chalk');
var server = require('./server');
var pkg = require('../package');
// Unpack
var docopt = docopt.docopt;
var version = 'Gesso ' + pkg.version;

// CLI documentation and options
var doc = multiline(function(){/*
Usage:
  gesso [options] [<path>]

Options:
  -h --help         Show this screen.
  --version         Show version.
*/});

function globalMain() {
  new Liftoff({
    name: 'gesso'
  }).launch({}, function(env) {
    // Check for local installation
    if (!env.modulePath) {
      // Show --help or --version and exit
      docopt(doc, {version: version});
      // Show local installation message and exit
      console.log(chalk.red('No local installation of gesso found at'), chalk.magenta(tildify(env.cwd)));
      console.log(chalk.red('Try running: npm install gesso'));
      process.exit(1);
    }

    // Run local installation
    require(env.modulePath).main();
  });
}

function main(argv) {
  var options = docopt(doc, {argv: argv, version: version});

  // TODO: Build command

  // Run server
  server.serve(options['<path>']);
}

module.exports = {
  globalMain: globalMain,
  main: main
};

var multiline = require('multiline');
var docopt = require('docopt');
var Liftoff = require('liftoff');
var tildify = require('tildify');
var chalk = require('chalk');
var server = require('./server');
var pkg = require('../package');
// Unpack
var docopt = docopt.docopt;
var version = 'Gesso ' + pkg.version;

// CLI documentation and options
var doc = multiline(function(){/*
Usage:
  gesso [options] [<path>]

Options:
  -h --help         Show this screen.
  --version         Show version.
*/});

function globalMain() {
  new Liftoff({
    name: 'gesso'
  }).launch({}, function(env) {
    // Check for local installation
    if (!env.modulePath) {
      // TODO: Check for package.json and either warn that there's no project or show help instead

      // Show --help or --version and exit
      docopt(doc, {version: version});
      // Show local installation message and exit
      console.log(chalk.red('No local installation of gesso found at'), chalk.magenta(tildify(env.cwd)));
      console.log(chalk.red('Try running: npm install gesso'));
      process.exit(1);
    }

    // Run local installation
    require(env.modulePath).main();
  });
}

function main(argv) {
  var options = docopt(doc, {argv: argv, version: version});

  // TODO: Build command

  // Run server
  server.serve(options['<path>']);
}

module.exports = {
  globalMain: globalMain,
  main: main
};


/**
 * Modal Footer
 *
 * This is the default modal footer of a typical <Modal /> in the folio system.
 */

import React from 'react';
import PropTypes from 'prop-types';
import { deprecated } from 'prop-types-extra';
import Button, { propTypes as buttonPropTypes } from '../Button/Button';
import css from './ModalFooter.css';

const ModalFooter = ({ children, primaryButton, secondaryButton }) => {
  // Rendered buttons are identical except for the label and buttonStyle
  const renderButton = (buttonStyle, props) => {
    const { label, ...buttonProps } = props;
    return (
      <Button
        {...buttonProps}
        buttonStyle={buttonStyle}
      >
        {label}
      </Button>
    );
  };

  return (
    <div className={css.modalFooterButtons}>
      {primaryButton ? renderButton('primary', primaryButton) : null}
      {secondaryButton ? renderButton('default', secondaryButton) : null}
      {children}
    </div>
  );
};

ModalFooter.propTypes = {
  children: (props) => {
    let error = null;
    React.Children.forEach(props.children, (child) => {
      if (child.type !== Button) {
        error = new Error('`ModalFooter` children should be of type `Button`.');
      }
    });
    return error;
  },
  primaryButton: deprecated(PropTypes.shape({
    ...buttonPropTypes,
    label: PropTypes.node,
  }), 'Pass in children instead.'),
  secondaryButton: deprecated(PropTypes.shape({
    ...buttonPropTypes,
    label: PropTypes.node,
  }), 'Pass in children instead.'),
};

export default ModalFooter;

/**
 * Modal Footer
 *
 * This is the default modal footer of a typical <Modal /> in the folio system.
 */

import React from 'react';
import PropTypes from 'prop-types';
import { deprecated } from 'prop-types-extra';
import Button, { propTypes as buttonPropTypes } from '../Button/Button';
import css from './ModalFooter.css';

const ModalFooter = ({ children, primaryButton, secondaryButton }) => {
  // Rendered buttons are identical except for the label and buttonStyle
  const renderButton = (buttonStyle, props) => {
    const { label, ...buttonProps } = props;
    return (
      <Button
        {...buttonProps}
        buttonStyle={buttonStyle}
      >
        {label}
      </Button>
    );
  };

  return (
    <div className={css.modalFooterButtons}>
      {primaryButton ? renderButton('primary', primaryButton) : null}
      {secondaryButton ? renderButton('default', secondaryButton) : null}
      {children}
    </div>
  );
};

ModalFooter.propTypes = {
  children: PropTypes.oneOfType([
    PropTypes.arrayOf(Button),
    PropTypes.objectOf(Button),
  ]),
  primaryButton: deprecated(PropTypes.shape({
    ...buttonPropTypes,
    label: PropTypes.node,
  }), 'Pass in children instead.'),
  secondaryButton: deprecated(PropTypes.shape({
    ...buttonPropTypes,
    label: PropTypes.node,
  }), 'Pass in children instead.'),
};

export default ModalFooter;

Improve children prop validation in modal footer


function assert(assertion, msg) {
    if (!assertion) {
        throw new Error(msg);
    }
}

function isUndefOrNull(val) {
    return val === null || (typeof val === 'undefined');
}

function isString(val) {
    return typeof val === 'string';
}

var nowOffset = Date.now();
if (window.performance.timing && window.performance.timing.navigationStart){
    nowOffset = window.performance.timing.navigationStart;
}

exports.assert = assert;
exports.isUndefOrNull = isUndefOrNull;
exports.isString = isString;
exports.nowOffset = nowOffset;

exports.assert = assert;
exports.isUndefOrNull = isUndefOrNull;
exports.isString = isString;
exports.nowOffset = nowOffset;

function assert(assertion, msg) {
    if (!assertion) {
        throw new Error(msg);
    }
}

function isUndefOrNull(val) {
    return val === null || (typeof val === 'undefined');
}

function isString(val) {
    return typeof val === 'string';
}

var nowOffset = Date.now();
if (window.performance.timing && window.performance.timing.navigationStart){
    nowOffset = window.performance.timing.navigationStart;
}

Put 'module.exports' in the beginning of the file


#!/usr/bin/env node

var cordova = require(process.env.framework + '/bin/cli-cordova');
var global = require(process.env.framework + '/bin/cli-global');
var shell = require('shelljs');

module.exports = {
    /**
     * @method build
     */
    build: function() {
        if (this.isInstalled()) {
            cordova.copy();
            cordova.exec(['build', 'android']);
        } else {
            console.log('Cordova platform Android is not installed.');
            process.exit(0);
        }
    },
    /**
     * @method install
     */
    install: function() {
        cordova.exec(['platform', 'remove', 'android']);
        cordova.exec(['platform', 'add', 'android']);
        cordova.exec(['plugin', 'remove', 'cordova-plugin-crosswalk-webview']);
        cordova.exec(['plugin', 'add', 'cordova-plugin-crosswalk-webview']);
        this.build();
    },
    /**
     * @method isInstalled
     * @returns {Boolean}
     */
    isInstalled: function() {
        return shell.test('-d', global.project + '/cordova/platforms/android/cordova');
    },
    /**
     * @method run
     */
    run: function() {
        if (this.isInstalled()) {
            cordova.copy();
            cordova.exec(['run', 'android']);
        } else {
            console.log('Cordova platform Android is not installed.');
            process.exit(0);
        }
    }
};

#!/usr/bin/env node

var cordova = require(process.env.framework + '/bin/cli-cordova');
var global = require(process.env.framework + '/bin/cli-global');
var shell = require('shelljs');

module.exports = {
    /**
     * @method build
     */
    build: function() {
        if (this.isInstalled()) {
            cordova.copy();
            cordova.exec(['build', 'android']);
        } else {
            console.log('Cordova platform Android is not installed.');
            process.exit(0);
        }
    },
    /**
     * @method copy
     */
    copy: function() {
        shell.cp('-rf', global.project + '/app/cordova/config.xml', global.project + '/cordova');
    },
    /**
     * @method install
     */
    install: function() {
        cordova.exec(['platform', 'remove', 'android']);
        cordova.exec(['platform', 'add', 'android']);
        cordova.exec(['plugin', 'remove', 'cordova-plugin-crosswalk-webview']);
        cordova.exec(['plugin', 'add', 'cordova-plugin-crosswalk-webview']);
        this.build();
    },
    /**
     * @method isInstalled
     * @returns {Boolean}
     */
    isInstalled: function() {
        return shell.test('-d', global.project + '/cordova/platforms/android/cordova');
    },
    /**
     * @method run
     */
    run: function() {
        if (this.isInstalled()) {
            this.copy();
            cordova.copy();
            cordova.exec(['run', 'android']);
        } else {
            console.log('Cordova platform Android is not installed.');
            process.exit(0);
        }
    }
};

Copy application specific cordova config.xml on build.


import compression from 'compression';
import express from 'express';
import path from 'path';
import open from 'open';

// tslint:disable:no-console

const port = 3000;
const app = express();

app.use(express.static('dist'));
app.use(compression());

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

app.get('/users', function(req, res) {
    // Hard coding for simplicity. Pretend this hits a real database
    res.json([
        {"id": 1,"firstName":"Bob","lastName":"Smith","email":"bob@gmail.com"},
        {"id": 2,"firstName":"Tammy","lastName":"Norton","email":"tnorton@yahoo.com"},
        {"id": 3,"firstName":"Tina","lastName":"Lee","email":"lee.tina@hotmail.com"}
    ]);
});

app.listen(port, function(err) {
    if (err) {
        console.log(err);
    } else {
        open(`http://localhost:${port}`);
    }
});

import compression from 'compression';
import express from 'express';
import path from 'path';
import open from 'open';

// tslint:disable:no-console

const port = 3000;
const app = express();

app.use(compression());
app.use(express.static('dist'));

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

app.get('/users', function(req, res) {
    // Hard coding for simplicity. Pretend this hits a real database
    res.json([
        {"id": 1,"firstName":"Bob","lastName":"Smith","email":"bob@gmail.com"},
        {"id": 2,"firstName":"Tammy","lastName":"Norton","email":"tnorton@yahoo.com"},
        {"id": 3,"firstName":"Tina","lastName":"Lee","email":"lee.tina@hotmail.com"}
    ]);
});

app.listen(port, function(err) {
    if (err) {
        console.log(err);
    } else {
        open(`http://localhost:${port}`);
    }
});

Fix GZip issue in script.