/*
 * tar-stream.js: Checkout adapter for a tar stream.
 *
 * (C) 2012 Bradley Meck.
 * MIT LICENSE
 *
 */

var tar = require('tar');

//
// ### function download (source, callback)
// #### @source {Object} Source checkout options
// #### @callback {function} Continuation to respond to.
// Downloads the tar stream to the specified `source.destination`.
//
exports.download = function (source, callback) {
  var done = false;

  //
  // Respond to the callback once.
  //
  function finish() {
    if (!done) {
      done = true;
      callback.apply(null, arguments);
    }
  }

  source.stream
    .on('error', finish)
    .pipe(tar.Extract({ path: source.destination }))
    .on('error', finish)
    .on('end', finish);
};

/*
 * tar-stream.js: Checkout adapter for a tar stream.
 *
 * (C) 2012 Bradley Meck.
 * MIT LICENSE
 *
 */

var zlib = require('zlib'),
    tar = require('tar');

//
// ### function download (source, callback)
// #### @source {Object} Source checkout options
// #### @callback {function} Continuation to respond to.
// Downloads the tar stream to the specified `source.destination`.
//
exports.download = function (source, callback) {
  var done = false;

  //
  // Respond to the callback once.
  //
  function finish() {
    if (!done) {
      done = true;
      callback.apply(null, arguments);
    }
  }

  if (source.gzip) {
    source.stream = source.stream
      .on('error', finish)
      .pipe(zlib.createGunzip());
  }

  source.stream
    .on('error', finish)
    .pipe(tar.Extract({ path: source.destination }))
    .on('error', finish)
    .on('end', finish);
};

Allow for optional gzip encoding.


/*jslint node: true, nomen: true, white: true, unparam: true, todo: true*/
/*!
 * Sitegear3
 * Copyright(c) 2014 Ben New, Sitegear.org
 * MIT Licensed
 */

(function (_, path, fs) {
	"use strict";

	module.exports = function (app, dirname) {
		dirname = dirname || path.join(__dirname, '..', 'viewHelpers');
		return function (request, response, next) {
			app.locals.siteName = app.get('site name');
			app.locals.baseUrl = request.secure ? app.get('https url') : app.get('http url');
			app.locals.now = new Date();

			fs.readdir(dirname, function (error, filenames) {
				// TODO Handle error
				_.each(filenames, function (filename) {
					if (/\.js$/.test(filename)) {
						var name = path.basename(filename, '.js');
						app.locals[name] = require('../viewHelpers/' + filename);
					}
				});
				next();
			});
		};
	};
}(require('lodash'), require('path'), require('fs')));

/*jslint node: true, nomen: true, white: true, unparam: true, todo: true*/
/*!
 * Sitegear3
 * Copyright(c) 2014 Ben New, Sitegear.org
 * MIT Licensed
 */

(function (_, path, fs) {
	"use strict";

	module.exports = function (app, dirname) {
		dirname = dirname || path.join(__dirname, '..', 'viewHelpers');
		return function (request, response, next) {
			app.locals.siteName = app.get('site name');
			app.locals.baseUrl = request.secure ? app.get('https url') : app.get('http url');
			app.locals.now = new Date();

			fs.readdir(dirname, function (error, filenames) {
				_.each(filenames, function (filename) {
					if (/\.js$/.test(filename)) {
						var name = path.basename(filename, '.js');
						app.locals[name] = require('../viewHelpers/' + filename);
					}
				});
				next(error);
			});
		};
	};
}(require('lodash'), require('path'), require('fs')));

Handle error in readdir() callback.


/* globals jQuery,QUnit */

QUnit.config.urlConfig.push({ id: 'nocontainer', label: 'Hide container'});
QUnit.config.urlConfig.push({ id: 'nojshint', label: 'Disable JSHint'});
QUnit.config.urlConfig.push({ id: 'doccontainer', label: 'Doc test pane'});
QUnit.config.testTimeout = 60000; //Default Test Timeout 60 Seconds

if (QUnit.notifications) {
  QUnit.notifications({
    icons: {
      passed: '/assets/passed.png',
      failed: '/assets/failed.png'
    }
  });
}

jQuery(document).ready(function() {
  var containerVisibility = QUnit.urlParams.nocontainer ? 'hidden' : 'visible';
  var containerPosition = QUnit.urlParams.doccontainer ? 'absolute' : 'relative';
  document.getElementById('ember-testing-container').style.visibility = containerVisibility;
  document.getElementById('ember-testing-container').style.position = containerPosition;
});

/* globals jQuery,QUnit */

QUnit.config.urlConfig.push({ id: 'nocontainer', label: 'Hide container'});
QUnit.config.urlConfig.push({ id: 'nojshint', label: 'Disable JSHint'});
QUnit.config.urlConfig.push({ id: 'doccontainer', label: 'Dock test pane'});
QUnit.config.testTimeout = 60000; //Default Test Timeout 60 Seconds

if (QUnit.notifications) {
  QUnit.notifications({
    icons: {
      passed: '/assets/passed.png',
      failed: '/assets/failed.png'
    }
  });
}

jQuery(document).ready(function() {
  var containerVisibility = QUnit.urlParams.nocontainer ? 'hidden' : 'visible';
  var containerPosition = QUnit.urlParams.doccontainer ? 'absolute' : 'relative';
  document.getElementById('ember-testing-container').style.visibility = containerVisibility;
  document.getElementById('ember-testing-container').style.position = containerPosition;
});

Correct "Doc test pane" to "Dock test pane"


var log4js = require('log4js');
var PlurkClient = require('plurk').PlurkClient;
var nconf = require('nconf');

nconf.file('config', __dirname + '/config/config.json')
	.file('keywords', __dirname + '/config/keywords.json');

var logging = nconf.get('log').logging;
var log_path = nconf.get('log').log_path;

if (logging) {
	log4js.clearAppenders();
	log4js.loadAppender('file');
	log4js.addAppender(log4js.appenders.file(log_path), 'BOT_LOG');
}

setInterval(checkTL, 500);

function checkTL() {

}

var log4js = require('log4js');
var PlurkClient = require('plurk').PlurkClient;
var nconf = require('nconf');
nconf.file('config', __dirname + '/config/config.json')
	.file('keywords', __dirname + '/config/keywords.json');

var logging = nconf.get('log').logging;
var log_path = nconf.get('log').log_path;

if (logging) {
	log4js.clearAppenders();
	log4js.loadAppender('file');
	log4js.addAppender(log4js.appenders.file(log_path), 'BOT_LOG');
}

var logger = log4js.getLogger('BOT_LOG');

var consumerKey = nconf.get('token').consume_key;
var consumerSecret = nconf.get('token').consume_secret;
var accessToken = nconf.get('token').access_token;
var accessTokenSecret = nconf.get('token').access_token_secret;

var client = new PlurkClient(true, consumerKey, consumerSecret, accessToken, accessTokenSecret);

client.startComet(function (err, data, cometUrl) {

    if (err) {
        logger.error(err);
        return;
    }

    logger.info('Comet channel started.');

});

Rewrite with using startComet function of plurk modules


import notp from 'notp'
import b32 from 'thirty-two'

export function cleanKey (key) {
  return key.replace(/[^a-z2-7]+/g, '').slice(0, 32)
}

export function isValidKey (key) {
  return /^[a-z2-7]{32}$/.test(cleanKey(key))
}

export function getCode (key) {
  return notp.totp.gen(b32.decode(cleanKey(key)), {})
}

import notp from 'notp'
import b32 from 'thirty-two'

export function cleanKey (key) {
  return key.replace(/[^a-z2-7]+/ig, '').toLowerCase()
}

export function isValidKey (key) {
  return /^[a-z2-7]+$/.test(cleanKey(key))
}

export function getCode (key) {
  return notp.totp.gen(b32.decode(cleanKey(key)), {})
}

Support for larger kind of key


export async function up(knex) {
  await knex.schema
    .raw('alter table feeds add column title text')
    .raw('alter table feeds add column ord integer')
    .raw(`alter table feeds drop constraint feeds_unique_feed_names`)
    // Only RiverOfNews feeds can have non-NULL ord or title
    .raw(`alter table feeds add constraint feeds_names_chk
      check (ord is null and title is null or name = 'RiverOfNews')`)
    // User cannot have multiple feeds with same name and ord is NULL
    .raw(`create unique index feeds_unique_names_idx on feeds (user_id, name) where ord is null`)
}

export async function down(knex) {
  // TODO: move extra subscriptions to main feed?
  await knex.schema
    // Remove extra RiverOfNews feeds
    .raw(`delete from feeds where name = 'RiverOfNews' and ord is not null`)
    .raw(`drop index feeds_unique_names_idx`)
    .raw('alter table feeds drop constraint feeds_names_chk')
    .raw('alter table feeds drop column title')
    .raw('alter table feeds drop column ord')
    .raw(`alter table feeds add constraint feeds_unique_feed_names unique(user_id, name)`);
}

export const up = (knex) => knex.schema.raw(`do $$begin
  -- FEEDS TABLE
  alter table feeds add column title text;
  alter table feeds add column ord integer;
  alter table feeds drop constraint feeds_unique_feed_names;

  -- Only RiverOfNews feeds can have non-NULL ord or title
  alter table feeds add constraint feeds_names_chk
    check (ord is null and title is null or name = 'RiverOfNews');

  -- User cannot have multiple feeds with same name and ord is NULL
  create unique index feeds_unique_names_idx on feeds (user_id, name) where ord is null;

  -- HOMEFEED_SUBSCRIPTIONS TABLE
  create table homefeed_subscriptions (
    homefeed_id uuid not null
      references feeds (uid) on delete cascade on update cascade,
    target_user_id uuid not null
      references users (uid) on delete cascade on update cascade,
    primary key (homefeed_id, target_user_id)
  );

  -- Assign all existing subscriptions to the main users homefeeds
  insert into homefeed_subscriptions (homefeed_id, target_user_id)
  select h.uid, f.user_id from
    subscriptions s
      join feeds h on h.user_id = s.user_id and h.name = 'RiverOfNews'
      join feeds f on f.uid = s.feed_id and f.name = 'Posts';

end$$`);

export const down = (knex) => knex.schema.raw(`do $$begin
  -- HOMEFEED_SUBSCRIPTIONS TABLE
    drop table homefeed_subscriptions;

  -- FEEDS TABLE
  -- Remove extra RiverOfNews feeds
  delete from feeds where name = 'RiverOfNews' and ord is not null;
  drop index feeds_unique_names_idx;
  alter table feeds drop constraint feeds_names_chk;
  alter table feeds drop column title;
  alter table feeds drop column ord;
  alter table feeds add constraint feeds_unique_feed_names unique(user_id, name);

end$$`);

Add a homefeed_subscriptions database table


module.exports = {
  "parseResults": function(result) {
    var xmlItems = result.items && result.items.item;
    var items = [];

    if (xmlItems) {
      for (var i = 0; i < xmlItems.length; i++) {
        var game = xmlItems[i];
        items.push({
          "subtype": game.$.subtype,
          "objectid": game.$.objectid,
          "collid": game.$.collid,
          "name": game.name || game.name[0]._,
          "image": game.image || game.image[0],
          "thumbnail": game.thumbnail || game.thumbnail[0]
        });
      }
    }

    return items;
  }
};

module.exports = {
  "parseResults": function(result) {
    var xmlItems = result.items && result.items.item;
    var items = [];

    if (xmlItems) {
      for (var i = 0; i < xmlItems.length; i++) {
        var game = xmlItems[i];
        items.push({
          "subtype": game.$.subtype,
          "objectid": game.$.objectid,
          "collid": game.$.collid,
          "name": game.name && game.name[0]._,
          "image": game.image && game.image[0],
          "thumbnail": game.thumbnail && game.thumbnail[0]
        });
      }
    }

    return items;
  }
};

Fix bad error checking - caused name/image/thumbnail to be arrays


/* eslint-env mocha */
const DoublyLinkedList = require('../../src').DataStructures.DoublyLinkedList;
const assert = require('assert');

describe('DoublyLinkedList', () => {
  it('should be empty when initialized', () => {
    const inst = new DoublyLinkedList();
    assert(inst.isEmpty());
    assert.equal(inst.length, 0);
  });
});

/* eslint-env mocha */
const DoublyLinkedList = require('../../src').DataStructures.DoublyLinkedList;
const assert = require('assert');

describe('DoublyLinkedList', () => {
  it('should be empty when initialized', () => {
    const inst = new DoublyLinkedList();
    assert(inst.isEmpty());
    assert.equal(inst.length, 0);
  });

  it('should push node to the front', () => {
    const inst = new DoublyLinkedList();
    assert(inst.isEmpty());

    inst.push(1);
    assert.equal(inst.length, 1);

    inst.push(2);
    assert.equal(inst.length, 2);

    assert.equal(inst.toString(), '2 -> 1 -> null');
  });
});

Test push to the front of the list


'use strict';

/*
 * Use for Async operations before displaying page
 *
module.exports.prepare = function() {
  this.done({});
};
 */

/*
 * Compute Vue parameters
 *
module.exports.vue = function() {
 return {};
};
 */

/*
 * Use as page starting point
 *
module.exports.start = function() {
};
 */

'use strict';

/*
 * Use for Async operations before displaying page
 *
module.exports.prepare = function() {
  this.done({});
};
 */

/*
 * Compute Vue parameters
 *
module.exports.vue = function() {
 return {};
};
 * or
module.exports.vue = {
};
 */

/*
 * Use as page starting point
 *
module.exports.start = function() {
};
 */

Update Layout assets to latest specifications


/**
 * Site Kit by Google, Copyright 2022 Google LLC
 *
 * 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
 *
 *     https://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.
 */

/**
 * Internal dependencies
 */
import SpinnerButton from './SpinnerButton';

const Template = ( args ) => <SpinnerButton { ...args } />;

export const DefaultButton = Template.bind( {} );
DefaultButton.storyName = 'Default Button';
DefaultButton.args = {
	children: 'Default Button',
	onClick() {
		return new Promise( ( resolve ) => setTimeout( resolve, 5000 ) );
	},
};

export default {
	title: 'Components/SpinnerButton',
	component: SpinnerButton,
};

/**
 * Site Kit by Google, Copyright 2022 Google LLC
 *
 * 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
 *
 *     https://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.
 */

/**
 * Internal dependencies
 */
import SpinnerButton from './SpinnerButton';

const Template = ( args ) => <SpinnerButton { ...args } />;

export const DefaultButton = Template.bind( {} );
DefaultButton.storyName = 'Default Button';
DefaultButton.args = {
	children: 'Default Button',
	onClick: () => new Promise( ( resolve ) => setTimeout( resolve, 5000 ) ),
};

export default {
	title: 'Components/SpinnerButton',
	component: SpinnerButton,
};

Fix vrt scenario script issue.


import babel from 'rollup-plugin-babel';
import mergeBaseConfig from './rollup.merge-base-config.js';

const cjsConfig = {
    format: 'umd',
    moduleName: 'knockoutStore',
    plugins: [
        babel({
            exclude: 'node_modules/**'
        })
    ],
    dest: 'dist/knockout-store.js'
};

const finalConfig = mergeBaseConfig(cjsConfig);

export default finalConfig;

import babel from 'rollup-plugin-babel';
import mergeBaseConfig from './rollup.merge-base-config.js';

const cjsConfig = {
    format: 'umd',
    moduleName: 'ko.store',
    plugins: [
        babel({
            exclude: 'node_modules/**'
        })
    ],
    dest: 'dist/knockout-store.js'
};

const finalConfig = mergeBaseConfig(cjsConfig);

export default finalConfig;

Change umd module name to ko.store.


import React from 'react';
import {Segment, Segments} from 'stardust';

describe('Segments', () => {
  it('should render children', () => {
    const [segmentOne, segmentTwo] = render(
      <Segments>
        <Segment>Top</Segment>
        <Segment>Bottom</Segment>
      </Segments>
    ).scryClass('sd-segment');

    segmentOne
      .textContent
      .should.equal('Top');
    segmentTwo
      .textContent
      .should.equal('Bottom');
  });

  it('renders expected number of children', () => {
    const [component] = render(
      <Segments>
        <Segment>Top</Segment>
        <Segment>Middle</Segment>
        <Segment>Bottom</Segment>
      </Segments>
    ).scryClass('sd-segments');

    expect(
      component.querySelectorAll('.sd-segment').length
    ).to.equal(3);
  });
});

import React from 'react';
import {Segment, Segments} from 'stardust';

describe('Segments', () => {
  it('should render children', () => {
    const [segmentOne, segmentTwo] = render(
      <Segments>
        <Segment>Top</Segment>
        <Segment>Bottom</Segment>
      </Segments>
    ).scryClass('sd-segment');

    segmentOne
      .textContent
      .should.equal('Top');
    segmentTwo
      .textContent
      .should.equal('Bottom');
  });

  it('renders expected number of children', () => {
    render(
      <Segments>
        <Segment>Top</Segment>
        <Segment>Middle</Segment>
        <Segment>Bottom</Segment>
      </Segments>
    )
      .scryClass('sd-segment')
      .should.have.a.lengthOf(3);
  });
});

Make use of chai's lengthOf assertion and simplify test


'use strict';
angular.module('arethusa.contextMenu').directive('pluginContextMenu', function () {
  return {
    restrict: 'E',
    scope: true,
    replace: true,
    link: function (scope, element, attrs) {
      scope.plugin = scope.$eval(attrs.name);
    },
    template: '      <div id="{{ plugin.name }}-context-menu"         ng-include="plugin.contextMenuTemplate">      </div>    '
  };
});

'use strict';
angular.module('arethusa.contextMenu').directive('pluginContextMenu', function () {
  return {
    restrict: 'E',
    scope: true,
    replace: true,
    link: function (scope, element, attrs) {
      scope.plugin = scope.$eval(attrs.name);
    },
    template: '\
      <div id="{{ plugin.name }}-context-menu"\
        ng-include="plugin.contextMenuTemplate">\
      </div>\
    '
  };
});

Fix multiline string in pluginContextMenu template


(function() {
  'use strict';

  chrome.app.runtime.onLaunched.addListener(function() {
    chrome.app.window.create('index.html', {
      innerBounds: {
        width: 960,
        height: 600
      },
      state: 'maximized'
    });
  });

}());

(function() {
  'use strict';

  chrome.app.runtime.onLaunched.addListener(function() {
    chrome.app.window.create('index.html', {
      innerBounds: {
        width: 960,
        height: 600,
        minWidth: 960,
        minHeight: 600
      },
      state: 'maximized'
    });
  });

}());

Add minWidth/Height to Chrome app


(function(window){
	window.ITCheck = window.ITCheck || {};

	// Get things rolling
	chrome.alarms.onAlarm.addListener(ITCheck.getUnreadThreadCount);
	chrome.runtime.onStartup.addListener(function () {
		chrome.alarms.create("updater", { "periodInMinutes": 1 });
	});
	chrome.runtime.onInstalled.addListener(function(details) {
		chrome.alarms.create("updater", { "periodInMinutes": 1 });
	});

	// Update number on navigate to a new thread
	chrome.webNavigation.onCompleted.addListener(function (details) {
		getUnreadThreads(null);
	},{url: [{hostSuffix: 'ivorytower.com', pathPrefix: '/IvoryTower/ForumThread.aspx'}]});
})(window);

(function(window){
	window.ITCheck = window.ITCheck || {};

	// Get things rolling
	chrome.alarms.onAlarm.addListener(ITCheck.getUnreadThreadCount);
	chrome.runtime.onStartup.addListener(function () {
		chrome.alarms.create("updater", { "periodInMinutes": 1 });
	});
	chrome.runtime.onInstalled.addListener(function(details) {
		chrome.alarms.create("updater", { "periodInMinutes": 1 });
	});

	// Update number on navigate to a new thread
	chrome.webNavigation.onCompleted.addListener(function (details) {
		ITCheck.getUnreadThreadCount(null);
	},{url: [{hostSuffix: 'ivorytower.com', pathPrefix: '/IvoryTower/ForumThread.aspx'}]});
})(window);

Fix error with navigating to new page


// Abuse reports
//
'use strict';

const Mongoose = require('mongoose');
const Schema   = Mongoose.Schema;

module.exports = function (N, collectionName) {

  let AbuseReport = new Schema({

      // _id of reported content
      src_id: Schema.ObjectId,

      // Content type (FORUM_POST, BLOG_ENTRY, ...)
      type: String,

      // Report text
      text: String,

      // User _id
      from: Schema.ObjectId
    },
    {
      versionKey: false
    });

  N.wire.on('init:models', function emit_init_AbuseReport() {
    return N.wire.emit('init:models.' + collectionName, AbuseReport);
  });

  N.wire.on('init:models.' + collectionName, function init_model_AbuseReport(schema) {
    N.models[collectionName] = Mongoose.model(collectionName, schema);
  });
};

// Abuse reports
//
'use strict';

const Mongoose = require('mongoose');
const Schema   = Mongoose.Schema;

module.exports = function (N, collectionName) {

  let AbuseReport = new Schema({

      // _id of reported content
      src_id: Schema.ObjectId,

      // Content type (FORUM_POST, BLOG_ENTRY, ...)
      type: String,

      // Report text
      text: String,

      // Parser options
      params_ref: Schema.ObjectId,

      // User _id
      from: Schema.ObjectId
    },
    {
      versionKey: false
    });

  N.wire.on('init:models', function emit_init_AbuseReport() {
    return N.wire.emit('init:models.' + collectionName, AbuseReport);
  });

  N.wire.on('init:models.' + collectionName, function init_model_AbuseReport(schema) {
    N.models[collectionName] = Mongoose.model(collectionName, schema);
  });
};

Add parser options to abuse reports


import { createComponent } from 'react-fela'

const View = props => ({
  display: props.hidden && 'none',
  zIndex: props.zIndex,
  position: 'fixed',
  top: 0,
  left: 0,
  bottom: 0,
  right: 0
})

export default createComponent(View)

import { createComponent } from 'react-fela'

const View = props => ({
  display: props.hidden ? 'none' : 'flex',
  zIndex: props.zIndex,
  position: 'fixed',
  top: 0,
  left: 0,
  bottom: 0,
  right: 0
})

export default createComponent(View)

Add flex display to view by default


export default {
  TOGGLE_OVER_18: 'toggleOver18',
  SIDE_NAV_TOGGLE: 'sideNavToggle',
  TOP_NAV_HAMBURGER_CLICK: 'topNavHamburgerClick',
  VOTE: 'vote',
  DROPDOWN_OPEN: 'dropdownOpen',
  COMPACT_TOGGLE: 'compactToggle',
  TOP_NAV_HEIGHT: 45,
  RESIZE: 'resize',
  SCROLL: 'scroll',
  ICON_SHRUNK_SIZE: 16,
  CACHEABLE_COOKIES: ['compact'],
  DEFAULT_API_TIMEOUT: 5000,
};

export default {
  TOGGLE_OVER_18: 'toggleOver18',
  SIDE_NAV_TOGGLE: 'sideNavToggle',
  TOP_NAV_HAMBURGER_CLICK: 'topNavHamburgerClick',
  VOTE: 'vote',
  DROPDOWN_OPEN: 'dropdownOpen',
  COMPACT_TOGGLE: 'compactToggle',
  TOP_NAV_HEIGHT: 45,
  RESIZE: 'resize',
  SCROLL: 'scroll',
  ICON_SHRUNK_SIZE: 16,
  CACHEABLE_COOKIES: ['compact'],
  DEFAULT_API_TIMEOUT: 10000,
};

Increase API timeout to 10s


module.exports = function stringReplaceAsync(
  string,
  searchValue,
  replaceValue
) {
  try {
    if (typeof replaceValue === "function") {
      // Step 1: Call native `replace` one time to acquire arguments for
      // `replaceValue` function
      // Step 2: Collect all return values in an array
      // Step 3: Run `Promise.all` on collected values to resolve them
      // Step 4: Call native `replace` the second time, replacing substrings
      // with resolved values in order of occurance!
      var promises = [];
      String.prototype.replace.call(string, searchValue, function () {
        promises.push(replaceValue.apply(undefined, arguments));
        return "";
      });
      return Promise.all(promises).then(function (values) {
        return String.prototype.replace.call(string, searchValue, function () {
          return values.shift();
        });
      });
    } else {
      return Promise.resolve(
        String.prototype.replace.call(string, searchValue, replaceValue)
      );
    }
  } catch (error) {
    return Promise.reject(error);
  }
};

module.exports = function stringReplaceAsync(
  string,
  searchValue,
  replaceValue
) {
  try {
    if (typeof replaceValue === "function") {
      // 1. Run fake pass of `replace`, collect values from `replaceValue` calls
      // 2. Resolve them with `Promise.all`
      // 3. Run `replace` with resolved values
      var values = [];
      String.prototype.replace.call(string, searchValue, function () {
        values.push(replaceValue.apply(undefined, arguments));
        return "";
      });
      return Promise.all(values).then(function (resolvedValues) {
        return String.prototype.replace.call(string, searchValue, function () {
          return resolvedValues.shift();
        });
      });
    } else {
      return Promise.resolve(
        String.prototype.replace.call(string, searchValue, replaceValue)
      );
    }
  } catch (error) {
    return Promise.reject(error);
  }
};

Clarify the code a bit


// cavy-cli reporter class.
export default class CavyReporter {
  // Internal: Creates a websocket connection to the cavy-cli server.
  onStart() {
    const url = 'ws://127.0.0.1:8082/';
    this.ws = new WebSocket(url);
  }

  // Internal: Send report to cavy-cli over the websocket connection.
  onFinish(report) {
    // WebSocket.readyState 1 means the web socket connection is OPEN.
    if (this.ws.readyState == 1) {
      try {
        this.ws.send(JSON.stringify(report));
        console.log('Cavy test report successfully sent to cavy-cli');
      } catch (e) {
        console.group('Error sending test results');
        console.warn(e.message);
        console.groupEnd();
      }
    } else {
      // If cavy-cli is not running, let people know in a friendly way
      const message = "Skipping sending test report to cavy-cli - if you'd " +
      'like information on how to set up cavy-cli, check out the README ' +
      'https://github.com/pixielabs/cavy-cli';

      console.log(message);
    }
  }
}

// Internal: CavyReporter is responsible for sending the test results to
// the CLI.
export default class CavyReporter {
  // Internal: Creates a websocket connection to the cavy-cli server.
  onStart() {
    const url = 'ws://127.0.0.1:8082/';
    this.ws = new WebSocket(url);
  }

  // Internal: Send report to cavy-cli over the websocket connection.
  onFinish(report) {
    // WebSocket.readyState 1 means the web socket connection is OPEN.
    if (this.ws.readyState == 1) {
      try {
        this.ws.send(JSON.stringify(report));
        console.log('Cavy test report successfully sent to cavy-cli');
      } catch (e) {
        console.group('Error sending test results');
        console.warn(e.message);
        console.groupEnd();
      }
    } else {
      // If cavy-cli is not running, let people know in a friendly way
      const message = "Skipping sending test report to cavy-cli - if you'd " +
      'like information on how to set up cavy-cli, check out the README ' +
      'https://github.com/pixielabs/cavy-cli';

      console.log(message);
    }
  }
}

Improve top level class description


require('./lib/metro-transit')

// Create the handler that responds to the Alexa Request.
exports.handler = function (event, context) {
    // Create an instance of the MetroTransit skill.
    var dcMetro = new MetroTransit();
    dcMetro.execute(event, context);
};

var MetroTransit = require('./lib/metro-transit');

// Create the handler that responds to the Alexa Request.
exports.handler = function (event, context) {
    MetroTransit.execute(event, context);
};

Update driver file for new module interfaces


import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import styles from './text_component.module.scss';

function setWeight( { semibold, light } ) {
  if ( semibold ) return styles.semibold;
  if ( light ) return styles.light;

  return styles.regular;
}

function TextComponent( props ) {
  const {
    tag,
    children,
    type,
    className,
  } = props;

  return React.createElement(
    tag,
    { className: classnames(
      styles[`typography-${type}`],
      setWeight( props ),
      className
    ) },
    [ ...children ]
  );
}

TextComponent.propTypes = {
  /**
   * You can use any html element text tag
   */
  tag: PropTypes.string,

  /**
   * determines typography class
   */
  type: PropTypes.number,

  /**
   * Font weight: 300
   */
  light: PropTypes.bool,

  /**
   * Font weight: 400
   */
  regular: PropTypes.bool,

  /**
   * Font weight: 600
   */
  semibold: PropTypes.bool,

  /**
   * adds class name to class set
   */
  className: PropTypes.string
};

TextComponent.defaultProps = {
  tag: 'p',
  type: 6
};

export default TextComponent;

import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import styles from './text_component.module.scss';

function setWeight( { semibold, light } ) {
  if ( semibold ) return styles.semibold;
  if ( light ) return styles.light;

  return styles.regular;
}

function TextComponent( props ) {
  const {
    tag,
    children,
    type,
    className,
  } = props;

  const kids = typeof(children.type) === "function" ? children.type( children.props ) : [ ...children ];

  return React.createElement(
    tag,
    { className: classnames(
      styles[`typography-${type}`],
      setWeight( props ),
      className
    ) },
    kids
  );
}

TextComponent.propTypes = {
  /**
   * You can use any html element text tag
   */
  tag: PropTypes.string,

  /**
   * determines typography class
   */
  type: PropTypes.number,

  /**
   * Font weight: 300
   */
  light: PropTypes.bool,

  /**
   * Font weight: 400
   */
  regular: PropTypes.bool,

  /**
   * Font weight: 600
   */
  semibold: PropTypes.bool,

  /**
   * adds class name to class set
   */
  className: PropTypes.string
};

TextComponent.defaultProps = {
  tag: 'p',
  type: 6
};

export default TextComponent;

Update Text Component to allow React components to render as child


'use strict';

var R = require('ramda');
var isEmptyObj = R.pipe(R.keys, R.isEmpty);

function ModelRenderer(model) {
  this.modelName = model.modelName;
  this.attrs = model.attrs;
}

function attrToString(key, value) {
  var output = '- ' + key;
  if (R.has('primaryKey', value)) {
    output += ' (primaryKey)';
  }
  return output;
}

ModelRenderer.prototype.toJSON = function toJSON() {
  return this;
};

ModelRenderer.prototype.toString = function toString() {
    var output = this.modelName;

    if (!isEmptyObj(this.attrs)) {
      output += ':\n';
      output += R.join('\n', R.map(R.apply(attrToString), R.toPairs(this.attrs)));
    }
    return output;
};

module.exports = ModelRenderer;

'use strict';

var R = require('ramda');
var isEmptyObj = R.pipe(R.keys, R.isEmpty);

function ModelRenderer(model) {
  this.modelName = model.modelName;
  this.attrs = model.attrs;
}

function attrToString(key, value) {
  var output = '- ' + key;
  if (R.has('primaryKey', value)) {
    output += ' (primaryKey)';
  }
  return output;
}

ModelRenderer.prototype.toJSON = function toJSON() {
  return R.mapObj(R.identity, this);
};

ModelRenderer.prototype.toString = function toString() {
    var output = this.modelName;

    if (!isEmptyObj(this.attrs)) {
      output += ':\n';
      output += R.join('\n', R.map(R.apply(attrToString), R.toPairs(this.attrs)));
    }
    return output;
};

module.exports = ModelRenderer;

Create plain object in toJSON method


process.title = 'Feedback-App';

var app = require('./server/routes');

var server = app.listen(3000, function() {
  console.log('Listening on port %d', server.address().port);
});

/**
 * Shutdown the server process
 *
 * @param {String}  signal    Signal to send, such as 'SIGINT' or 'SIGHUP'
 */
var close = function(signal) {
  console.log('Server shutting down');
  process.kill(process.pid, signal);
};

process.stdin.resume();

// Attach event listeners
process.on('SIGINT', function() {
  close('SIGINT');
});

process.on('SIGHUP', function() {
  close('SIGHUP');
});

process.on('SIGTERM', function() {
  close('SIGTERM');
});

Add shutdown notification and process title


/**
 * Searches the first loaded style sheet for an @keyframes animation by name.
 * FYI: If you use "to" or "from" they'll get converted to percentage values, so use them for manipulating rules.
 * Source (Optimized for flapper): http://jsfiddle.net/russelluresti/RHhBz/3/.
 **/
function findKeyframesRule(name) {
  var stylesheet = document.styleSheets[0];
  for (var i = 0; i < stylesheet.cssRules.length; i++) {
    var cssRule = stylesheet.cssRules[i];
    if (cssRule.name == name) return cssRule;
  }
  return null;
}

/**
 * Searches the first loaded style sheet for a CSS rule by name.
 **/
 function findCSSRule(name) {
   var stylesheet = document.styleSheets[0];
   console.log("Stylesheet: " + stylesheet);
   for (var i = 0; i < stylesheet.cssRules.length; i++) {
     var cssRule = stylesheet.cssRules[i];
     console.log(`cssRule(${i}): ${cssRule.selectorText}`)
     if (cssRule.selectorText == name) {
       console.log("Found it");
       return cssRule;
     }
   }
   return null;
 }

/**
 * Searches the first loaded style sheet for an @keyframes animation by name.
 * FYI: If you use "to" or "from" they'll get converted to percentage values, so use them for manipulating rules.
 * Source (Optimized for flapper): http://jsfiddle.net/russelluresti/RHhBz/3/.
 **/
function findKeyframesRule(name) {
  for (var x = 0; x < document.styleSheets.length; x++) {
    var stylesheet = document.styleSheets[x];
    for (var i = 0; i < stylesheet.cssRules.length; i++) {
      var rule = stylesheet.cssRules[i];
      if (rule.name == name) {
        return rule;
      }
    }
  }
  return null;
}

/**
 * Searches the first loaded style sheet for a CSS rule by name.
 **/
 function findCSSRule(name) {
   for (var x = 0; x < document.styleSheets.length; x++) {
     var stylesheet = document.styleSheets[x];
     for (var i = 0; i < stylesheet.cssRules.length; i++) {
       var rule = stylesheet.cssRules[i];
       if (rule.selectorText == name) {
         return rule;
       }
     }
   }
   return null;
 }

Fix uBlock Origin breaking flapper by injecting CSS stylesheet


'use strict';

const semver = require('semver');
const fetch = require('node-fetch');
const url = require('url');

module.exports = pkg => {
  if (typeof pkg === 'string') {
    pkg = {
      name: pkg
    }
  }

  if (!pkg || !pkg.name) {
    throw new Error('You must provide a package name');
  }

  pkg.version = pkg.version || '*';

  if (!semver.validRange(pkg.version)) {
    throw new Error('That is not a valid package version range');
  }

  pkg.registry = pkg.registry || 'http://registry.npmjs.com/';

  return fetch(url.resolve(pkg.registry, pkg.name))
    .then(response => response.json())
    .then(data => semver.maxSatisfying(Object.keys(data.versions || {}), pkg.version))
    .catch(err => { throw new Error(err); });
}

'use strict';

const semver = require('semver');
const fetch = require('node-fetch');
const url = require('url');

module.exports = pkg => {
  if (typeof pkg === 'string') {
    pkg = {
      name: pkg
    }
  }

  if (!pkg || !pkg.name) {
    throw new Error('You must provide a package name');
  }

  pkg.version = pkg.version || '*';

  if (!semver.validRange(pkg.version)) {
    throw new Error('That is not a valid package version range');
  }

  pkg.registry = pkg.registry || 'http://registry.npmjs.com/';

  return fetch(url.resolve(pkg.registry, pkg.name))
    .then(response => response.json())
    .then(data => {
      let v = semver.maxSatisfying(Object.keys(data.versions || {}), pkg.version);
      return (pkg.full) ? data.versions[v] : v;
    })
    .catch(err => { throw new Error(err); });
}

Add 'full' option to get all package details not just version number.


var _ = require('underscore');
var utils = require('./lib/utils');
var middleware = require('./lib/middleware');
var fixture = require('./lib/fixture');
var service = require('./lib/service');
var transform = require('./lib/transform');
var view = require('./lib/view');
/**
 * Reads in route and service definitions from JSON and configures
 * express routes with the appropriate middleware for each.
 * @module router
 */
module.exports = function(app, routes, services){
  _.each(routes, function(methods, route){
    _.each(methods, function(config, method){
      app[method](route, utils.loadStack(
        config,
        middleware,
        fixture,
        service.bind(null, services),
        transform,
        view
      ));
    });
  });
};

require('es6-promise').polyfill();

var _ = require('lodash');
var utils = require('./lib/utils');

/**
 * Reads in route and service definitions from JSON and configures
 * express routes with the appropriate middleware for each.
 * @module router
 * @param {object} app - an Express instance
 * @param {object} routes - a routes config
 * @param {object} services - a services config
 */
module.exports = function(app, routes, services){
  _.each(routes, function(methods, route){
    _.each(methods, function(configs, method){
      app[method.toLowerCase()](route, utils.loadStack(configs, services));
    });
  });
};

Update reducto module to work with new config schema.


'use babel'

import {React} from 'react-for-atom'

export default React.createClass({

  propTypes: {
    readyCount: React.PropTypes.number,
    totalCount: React.PropTypes.number
  },

  render () {
    if (isNaN(this.props.readyCount) || isNaN(this.props.totalCount)) {
      return
    } else {
      return (

            Reading {this.props.readyCount} of {this.props.totalCount} notes

          <progress className='inline-block' max={this.props.totalCount} value={this.props.readyCount} />

)
    }
  },

  _onHover () {
    atom.notifications.addInfo('Reading files', {
      description: 'This is necesary to populate the search index. It is only necessary the first time, the notes will be cached for next session.',
      dismissible: true
    })
  }
})

Move tooltip to only show on hovering info-icon


const request = require('request');
const fs = require('fs');
const thenify = require('thenify').withCallback;

const download = (url, file, callback) => {
  const stream = fs.createWriteStream(file);
  stream.on('finish', () => {
    callback(null, file);
  });
  stream.on('error', error => {
    callback(error);
  });
  request
  .get(url)
  .pipe(stream);
};

module.exports = thenify(download);

const request = require('request');
const fs = require('fs');
const thenify = require('thenify').withCallback;

const download = (url, file, callback) => {
  const stream = fs.createWriteStream(file);
  stream.on('finish', () => {
    callback(null, file);
  });
  stream.on('error', error => {
    callback(error);
  });
  request
  .get(url)
  .on('error', error => {
    callback(error);
  })
  .pipe(stream);
};

module.exports = thenify(download);

Handle errors that occure during request


module.exports = function (protocol) {
	protocol = protocol || 'http';
	var url = 'http://vicopo.selfbuild.fr/search/';
	switch (protocol) {
		case 'https':
			url = 'https://www.selfbuild.fr/vicopo/search/';
			break;
		case 'http':
			break;
		default:
			throw new Error(protocol + ' protocol not supported');
	}
	var transport = require(protocol);
	return function (search, done) {
		transport.get(url + encodeURIComponent(search), function (res) {
			var data = '';
			res.setEncoding('utf8');
		  res.on('data', (chunk) => {
		    data += chunk;
		  });
		  res.on('end', () => {
				var result;
				try {
			    result = JSON.parse(data);
				} catch(e) {
					return done(new Error('Cannot parse Vicopo answear:\n' + e + '\n' + data));
				}
				if (result.cities) {
					done(null, result.cities);
				} else {
					done(new Error('The answear should contains a cities list:\n' + data));
				}
		  });
		})
		.on('error', done);
	};
};

module.exports = function (protocol) {
	protocol = protocol || 'http';
	var url = 'http://vicopo.selfbuild.fr/search/';
	switch (protocol) {
		case 'https':
			url = 'https://www.selfbuild.fr/vicopo/search/';
			break;
		case 'http':
			break;
		default:
			throw new Error(protocol + ' protocol not supported');
	}
	var transport = require(protocol);
	return function (search, done) {
		transport.get(url + encodeURIComponent(search), function (res) {
			var data = '';
			res.setEncoding('utf8');
			res.on('data', function (chunk) {
				data += chunk;
			});
			res.on('end', function () {
				var result;
				try {
					result = JSON.parse(data);
				} catch(e) {
					return done(new Error('Cannot parse Vicopo answear:\n' + e + '\n' + data));
				}
				if (result.cities) {
					done(null, result.cities);
				} else {
					done(new Error('The answear should contains a cities list:\n' + data));
				}
			});
		})
		.on('error', done);
	};
};

Make it compatible with older node.js


"use strict";

// modified from https://github.com/es-shims/es6-shim
var keys = require('object-keys');
var isObject = function (obj) {
	return typeof obj !== 'undefined' && obj !== null;
};

var assignShim = function assign(target, source) {
	var s, i, props;
	if (!isObject(target)) { throw new TypeError('target must be an object'); }
	target = Object(target);
	for (s = 1; s < arguments.length; ++s) {
		source = arguments[s];
		if (!isObject(source)) { throw new TypeError('source ' + s + ' must be an object'); }
		props = keys(Object(source));
		for (i = 0; i < props.length; ++i) {
			target[props[i]] = source[props[i]];
		}
	}
	return target;
};

assignShim.shim = function shimObjectAssign() {
	if (!Object.assign) {
		Object.assign = assignShim;
	}
	return Object.assign || assignShim;
};

module.exports = assignShim;

"use strict";

// modified from https://github.com/es-shims/es6-shim
var keys = Object.keys || require('object-keys');
var isObject = function (obj) {
	return typeof obj !== 'undefined' && obj !== null;
};

var assignShim = function assign(target, source) {
	var s, i, props;
	if (!isObject(target)) { throw new TypeError('target must be an object'); }
	target = Object(target);
	for (s = 1; s < arguments.length; ++s) {
		source = arguments[s];
		if (!isObject(source)) { throw new TypeError('source ' + s + ' must be an object'); }
		props = keys(Object(source));
		for (i = 0; i < props.length; ++i) {
			target[props[i]] = source[props[i]];
		}
	}
	return target;
};

assignShim.shim = function shimObjectAssign() {
	if (!Object.assign) {
		Object.assign = assignShim;
	}
	return Object.assign || assignShim;
};

module.exports = assignShim;

Use the native Object.keys if it's available.


"use strict";

module.exports = {
    rules: {
        "sort-object-props": function(context) {
            var ignoreCase = context.options[0].ignoreCase;
            var ignoreMethods = context.options[0].ignoreMethods;
            var MSG = "Property names in object literals should be sorted";
            return {
                "ObjectExpression": function(node) {
                    node.properties.reduce(function(lastProp, prop) {
                        if (ignoreMethods &&
                                prop.value.type === "FunctionExpression") {
                            return prop;
                        }
                        var lastPropId, propId;
                        if (prop.key.type === "Identifier") {
                            lastPropId = lastProp.key.name;
                            propId = prop.key.name;
                        } else if (prop.key.type === "Literal") {
                            lastPropId = lastProp.key.value;
                            propId = prop.key.value;
                        }
                        if (ignoreCase) {
                            if (propId == null)
                              console.log(prop);
                            lastPropId = lastPropId.toLowerCase();
                            propId = propId.toLowerCase();
                        }
                        if (propId < lastPropId) {
                            context.report(prop, MSG);
                        }
                        return prop;
                    }, node.properties[0]);
                }
            };
        }
    },
    rulesConfig: {
        "sort-object-props": [ 1, { ignoreCase: true, ignoreMethods: false } ]
    }
};

"use strict";

module.exports = {
    rules: {
        "sort-object-props": function(context) {
            var caseSensitive = context.options[0].caseSensitive;
            var ignoreMethods = context.options[0].ignoreMethods;
            var MSG = "Property names in object literals should be sorted";
            return {
                "ObjectExpression": function(node) {
                    node.properties.reduce(function(lastProp, prop) {
                        if (ignoreMethods &&
                                prop.value.type === "FunctionExpression") {
                            return prop;
                        }
                        var lastPropId, propId;
                        if (prop.key.type === "Identifier") {
                            lastPropId = lastProp.key.name;
                            propId = prop.key.name;
                        } else if (prop.key.type === "Literal") {
                            lastPropId = lastProp.key.value;
                            propId = prop.key.value;
                        }
                        if (caseSensitive) {
                            lastPropId = lastPropId.toLowerCase();
                            propId = propId.toLowerCase();
                        }
                        if (propId < lastPropId) {
                            context.report(prop, MSG);
                        }
                        return prop;
                    }, node.properties[0]);
                }
            };
        }
    },
    rulesConfig: {
        "sort-object-props": [ 1, { caseSensitive: false, ignoreMethods: false } ]
    }
};

Rename option to caseSensitive to avoid double negation


'use strict';

var Q = require('q');
var request = Q.denodeify(require('request'));

var chromecastHomeURL = 'https://clients3.google.com/cast/chromecast/home/v/c9541b08';
var initJSONStateRegex = /(JSON\.parse\(.+'\))/;

var parseChromecastHome = function(htmlString) {
    var JSONParse = htmlString.match(initJSONStateRegex)[1];
    var initState = eval(JSONParse); // I don't know why this is ok but JSON.parse fails.
    var parsedBackgrounds = [];
    for (var i in initState[1]) {
        var backgroundEntry = {
            url: initState[1][i][1],
            author: initState[1][i][2]
        };
        parsedBackgrounds.push(backgroundEntry);
    }
    return parsedBackgrounds;
};

module.exports = function() {
    return request(chromecastHomeURL).then(function(requestResult) {
        return parseChromecastHome(requestResult[1]);
    });
};

'use strict';

var Q = require('q');
var request = Q.denodeify(require('request'));

var chromecastHomeURL = 'https://clients3.google.com/cast/chromecast/home/v/c9541b08';
var initJSONStateRegex = /(JSON\.parse\(.+'\))/;

var parseChromecastHome = function(htmlString) {
    var JSONParse = htmlString.match(initJSONStateRegex)[1];
    var initState = eval(JSONParse); // I don't know why this is ok but JSON.parse fails.
    var parsedBackgrounds = [];
    for (var i in initState[0]) {
        var backgroundEntry = {
            url: initState[0][i][0],
            author: initState[0][i][1]
        };
        parsedBackgrounds.push(backgroundEntry);
    }
    return parsedBackgrounds;
};

module.exports = function() {
    return request(chromecastHomeURL).then(function(requestResult) {
        return parseChromecastHome(requestResult[1]);
    });
};

Fix parsing of initState JSON as noticed by @aawc


// Enable promise error logging
require('promise/lib/rejection-tracking').enable();

var express = require('express');
var app = express();
var certificate = require('./src/certificate')

app.set('port', (process.env.PORT || 5000));

app.use(express.static(__dirname + '/public'));

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

app.get('/', function(request, response) {
  var promise = certificate.getCertificationData();

  certificate.getCertificationData().then(function(data) {
    responseData = {
      certInfo: JSON.stringify(data),
      runDate: new Date().toDateString()
    }

    if(request.headers['content-type'] == 'application/json') {
      response.json(responseData);
    } else {
      response.render('pages/index', responseData);
    }
  });
});

app.listen(app.get('port'), function() {
  console.log('Node app is running on port', app.get('port'));
});

// Enable promise error logging
require('promise/lib/rejection-tracking').enable();

var express = require('express');
var app = express();
var certificate = require('./src/certificate')

app.set('port', (process.env.PORT || 5000));

app.use(express.static(__dirname + '/public'));

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

app.get('/', function(request, response) {
  certificate.getCertificationData().then(function(data) {
    responseData = {
      certInfo: JSON.stringify(data),
      runDate: new Date().toDateString()
    }

    if(request.headers['content-type'] == 'application/json') {
      response.json(responseData);
    } else {
      response.render('pages/index', responseData);
    }
  });
});

app.listen(app.get('port'), function() {
  console.log('Node app is running on port', app.get('port'));
});

Remove redundant call to getCertificationData()


var express = require("express"),
    mongoose = require("mongoose"),
    app = express();

mongoose.connect("mongodb://localhost/test", function (err) {
  if (!err) {
    console.log("Connected to MongoDB");
  } else {
    console.error(err);
  }
});

app.get("/", function (req, res) {
  res.send("Hey buddy!");
});

var Thing = require("./model");

app.get("/:name", function (req, res) {
  Thing.find({ name: req.params.name }, function (err, t) {
    if (t.length < 1) {
      var thing = new Thing();
      thing.name = req.params.name;
      thing.save();
      res.send("Created a new thing with name " + thing.name);
    } else {
      res.send(t);
    }
  });
});

app.listen(3000);

var express = require("express"),
    mongoose = require("mongoose"),
    app = express();

mongoose.connect("mongodb://localhost/test", function (err) {
  if (!err) {
    console.log("Connected to MongoDB");
  } else {
    console.error(err);
  }
});

app.get("/", function (req, res) {
  res.send("Hey buddy!");
});

var Thing = require("./model");

app.get("/:name", function (req, res) {
  Thing.find({ name: req.params.name }, function (err, t) {
    if (t.length < 1) {
      var thing = new Thing();
      thing.name = req.params.name;
      thing.save(function(err) {
        if (err) {
          res.send(500);
        } else {
          res.send("Created a new thing with name " + thing.name);
        }
      });
    } else {
      res.send(t);
    }
  });
});

app.listen(3000);

Use callbacks to ensure save is completed before returning a result


import BlogPost from '../database/models/blog-post';
import log from '../log';

const PUBLIC_API_ATTRIBUTES = [
  'id',
  'title',
  'link',
  'description',
  'date_updated',
  'date_published'
];

const DEFAULT_ORDER = [
  ['date_published', 'DESC']
];

export function getAll() {
  return new Promise((resolve, reject) => {
    BlogPost.findAll({
      attributes: PUBLIC_API_ATTRIBUTES,
      order: DEFAULT_ORDER,
      raw: true
    })
      .then(resolve)
      .catch(error => {
        log.error({ error }, 'Error getting list of all posts');
        reject(error);
      });
  });
}

export function getById(id) {
  return new Promise((resolve, reject) => {
    BlogPost.findOne({
      attributes: PUBLIC_API_ATTRIBUTES,
      where: { id },
      raw: true
    })
      .then(resolve)
      .catch(error => {
        log.error({ error }, 'Error getting blog post by id');
        reject(error);
      });
  });
}

export function getPage(pageNumber, pageSize) {
  return new Promise((resolve, reject) => {
    BlogPost.findAll({
      attributes: PUBLIC_API_ATTRIBUTES,
      order: DEFAULT_ORDER,
      offset: pageNumber * pageSize,
      limit: pageSize,
      raw: true
    })
      .then(resolve)
      .catch(error => {
        log.error({ error }, 'Error getting page of posts');
        reject(error);
      });
  });
}

import BlogPost from '../database/models/blog-post';
import log from '../log';

const PUBLIC_API_ATTRIBUTES = [
  'id',
  'title',
  'link',
  'description',
  'date_updated',
  'date_published',
  'author_id'
];

const DEFAULT_ORDER = [
  ['date_published', 'DESC']
];

export function getAll() {
  return new Promise((resolve, reject) => {
    BlogPost.findAll({
      attributes: PUBLIC_API_ATTRIBUTES,
      order: DEFAULT_ORDER,
      raw: true
    })
      .then(resolve)
      .catch(error => {
        log.error({ error }, 'Error getting list of all posts');
        reject(error);
      });
  });
}

export function getById(id) {
  return new Promise((resolve, reject) => {
    BlogPost.findOne({
      attributes: PUBLIC_API_ATTRIBUTES,
      where: { id },
      raw: true
    })
      .then(resolve)
      .catch(error => {
        log.error({ error }, 'Error getting blog post by id');
        reject(error);
      });
  });
}

export function getPage(pageNumber, pageSize) {
  return new Promise((resolve, reject) => {
    BlogPost.findAll({
      attributes: PUBLIC_API_ATTRIBUTES,
      order: DEFAULT_ORDER,
      offset: pageNumber * pageSize,
      limit: pageSize,
      raw: true
    })
      .then(resolve)
      .catch(error => {
        log.error({ error }, 'Error getting page of posts');
        reject(error);
      });
  });
}

Allow author_id on public API


describe('pingPong', function() {
  it("is true for a number that is divisible by 3", function() {
    expect(pingPong(6)).to.equal(true);
  });

  it("is true for a number that is divisible by 5", function() {
    expect(pingPong(10)).to.equal(True)
  }

});

describe('pingPong', function() {
  it("is true for a number that is divisible by 3", function() {
    expect(pingPong(6)).to.equal(true);
  });

  it("is true for a number that is divisible by 5", function() {
    expect(pingPong(10)).to.equal(True)
  });

});

Fix syntax error on previous commit


'use strict';

angular.module('Teem')
  .directive('participate', function() {
    return {
      controller: [
      '$scope', '$element', '$attrs', 'SessionSvc', '$timeout', 'CommunitiesSvc',
      function($scope, $element, $attrs, SessionSvc, $timeout, CommunitiesSvc) {
        $scope.participateCopyOn  = $attrs.participateCopyOn;
        $scope.participateCopyOff = $attrs.participateCopyOff;

        $element.on('click', function($event) {
          SessionSvc.loginRequired($scope, function() {
            if ($scope.community.toggleParticipant) {
              $timeout(function() {
                $scope.community.toggleParticipant();
              });
            } else {
              CommunitiesSvc.find($scope.community.id).then(function(community) {
                $timeout(function() {
                  community.toggleParticipant();
                  angular.extend($scope.community, community);
                });
              });
            }
          });
          $event.stopPropagation();
        });
      }],
      templateUrl: 'participate.html'
    };
  });

'use strict';

angular.module('Teem')
  .directive('participate', function() {
    return {
      controller: [
      '$scope', '$element', '$attrs', 'SessionSvc', '$timeout', 'CommunitiesSvc',
      function($scope, $element, $attrs, SessionSvc, $timeout, CommunitiesSvc) {
        $scope.participateCopyOn  = $attrs.participateCopyOn;
        $scope.participateCopyOff = $attrs.participateCopyOff;

        $element.on('click', function($event) {
          SessionSvc.loginRequired($scope, function() {
            if ($scope.community.toggleParticipant) {
              $timeout(function() {
                $scope.community.toggleParticipant();
              });
            } else {
              CommunitiesSvc.find($scope.community.id).then(function(community) {
                $timeout(function() {
                  community.toggleParticipant();
                  $scope.community.participants = community.participants;
                });
              });
            }
          });
          $event.stopPropagation();
        });
      }],
      templateUrl: 'participate.html'
    };
  });

Copy only the participants list instead of using .extend()


var path = require('path');
var fs = require('fs');
var mime = require('mime');

var pattern = function (file, included) {
    return {pattern: file, included: included, served: true, watched: false};
};

var framework = function (files) {
    files.push(pattern(path.join(__dirname, 'framework.js'), false));
    files.push(pattern(path.join(__dirname, 'runner.js'), true));
};

var createProxyForDirectory = function (directory) {
    return function (request, response, next) {
        var filePath = path.join(directory, request.url.replace('/base/', ''));
        fs.exists(filePath, function (exists) {
            if (exists) {
                response.writeHead(200, {
                    "Content-Type": mime.lookup(filePath)
                });
                fs.createReadStream(filePath).pipe(response);
            } else {
                next();
            }
        });
    }
};

var proxyBowerComponentsMiddlewareFactory = function () {
    return createProxyForDirectory('bower_components');
};

var proxyNodeModulesMiddlewareFactory = function () {
    return createProxyForDirectory('node_modules');
};

framework.$inject = ['config.files'];
module.exports = {
    'framework:web-components': ['factory', framework],
    'middleware:proxy-bower-components': ['factory', proxyBowerComponentsMiddlewareFactory],
    'middleware:proxy-node-modules': ['factory', proxyNodeModulesMiddlewareFactory]
};

var path = require('path');
var fs = require('fs');
var mime = require('mime');

var pattern = function (file, included) {
    return {pattern: file, included: included, served: true, watched: false};
};

var framework = function (files) {
    files.push(pattern(path.join(__dirname, 'framework.js'), false));
    files.push(pattern(path.join(__dirname, 'runner.js'), true));
};

var createProxyForDirectory = function (directory) {
    return function (request, response, next) {
        if (!request.url.startsWith('/base/')) {
            var filePath = path.join(directory, request.url);
            fs.exists(filePath, function (exists) {
                if (exists) {
                    response.writeHead(200, {
                        "Content-Type": mime.lookup(filePath)
                    });
                    fs.createReadStream(filePath).pipe(response);
                } else {
                    next();
                }
            });
        } else {
            next();
        }
    }
};

var proxyBowerComponentsMiddlewareFactory = function () {
    return createProxyForDirectory('bower_components');
};

var proxyNodeModulesMiddlewareFactory = function () {
    return createProxyForDirectory('node_modules');
};

framework.$inject = ['config.files'];
module.exports = {
    'framework:web-components': ['factory', framework],
    'middleware:proxy-bower-components': ['factory', proxyBowerComponentsMiddlewareFactory],
    'middleware:proxy-node-modules': ['factory', proxyNodeModulesMiddlewareFactory]
};

Fix proxy incorrectly proxying requests for files a level deeper than intended


var Xray = require('x-ray'),
    fs = require('fs')

var x = Xray()

var baseURLPrefix = "http://www.thaischool.in.th/sitemap.php?school_area=&province_id=",
    baseURLSuffix = "&schooltype_id=&txtsearch=",
    provinces = require('./provinces.json')

function appendObject(obj){
  var resultFile = fs.readFileSync('./result/result.json')
  var result = JSON.parse(resultFile)
  result.push(obj)
  var resultJSON = JSON.stringify(result)
  fs.writeFileSync('./result/result.json', resultJSON)
}

provinces.map(function(province){
  x(baseURLPrefix + province['id'] + baseURLSuffix, 'form', ['td > a'])
    .paginate('a[title="Next"]@href')(function(err, arr) {
      appendObject({
        province: province['name'],
        schools: arr
      })
    })
})

var Xray = require('x-ray'),
    fs = require('fs')

var x = Xray()

var baseURLPrefix = "http://www.thaischool.in.th/sitemap.php?school_area=&province_id=",
    baseURLSuffix = "&schooltype_id=&txtsearch=",
    provinces = require('./provinces.json')

function appendObject(obj){
  var resultFile = fs.readFileSync('./result/result.json')
  var result = JSON.parse(resultFile)
  result.push(obj)
  var resultJSON = JSON.stringify(result)
  fs.writeFileSync('./result/result.json', resultJSON)
}

function appendArray(arr){
  var resultFile = fs.readFileSync('./result/schools.json')
  var result = JSON.parse(resultFile)
  Array.prototype.push.apply(result, obj);
  var resultJSON = JSON.stringify(result)
  fs.writeFileSync('./result/schools.json', resultJSON)
}

provinces.map(function(province){
  x(baseURLPrefix + province['id'] + baseURLSuffix, 'form', ['td > a'])
    .paginate('a[title="Next"]@href')(function(err, arr) {
      appendObject({
        province: province['name'],
        schools: arr
      })
      appendArray(arr)
    })
})

Save result as array of school name only


/**
 * Initialize Pattern builder plugin.
 */
;(function ($) {

    var pluginName = 'patternMaker',
        pluginDefaults = {
            palette: []
        };

    /**
     * Constructor.
     *
     * @param {dom}     element     Drawing board
     * @param {object}  options     Plugin options
     */
    function Plugin(element, options)
    {
        this.element = element;
        this.$element = $(element);
        this.options = $.extend({}, pluginDefaults, options);
        this.init();
    }

    /**
     * Launch it
     */
    Plugin.prototype.init = function()
    {
        this._palette = new RandomColor(this.options.palette);
    };

    /**
     * Attach to jQuery plugin namespace.
     *
     * @param  {object}     options     Initialization options
     */
    $.fn[pluginName] = function(options) {
        return this.each(function () {
            if (!$.data(this, "plugin_" + pluginName)) {
                $.data(this, "plugin_" + pluginName);
                new Plugin(this, options);
            }
        });
    };

})(jQuery);

/**
 * Initialize Pattern builder plugin.
 */
;(function () {

    var pluginName = 'patternMaker',
        pluginDefaults = {
            palette: []
        };

    /**
     * Constructor.
     *
     * @param {dom}     element     Drawing board
     * @param {object}  options     Plugin options
     */
    function Plugin(element, options)
    {
        this.element = element;
        this.$element = $(element);
        this.options = $.extend({}, pluginDefaults, options);
        this.init();
    }

    /**
     * Launch it
     */
    Plugin.prototype.init = function()
    {
        this._palette = new RandomColor(this.options.palette);
    };

    /**
     * Attach to jQuery plugin namespace.
     *
     * @param  {object}     options     Initialization options
     */
    jQuery.fn[pluginName] = function(options) {
        return this.each(function () {
            if (!jQuery.data(this, "plugin_" + pluginName)) {
                jQuery.data(this, "plugin_" + pluginName);
                new Plugin(this, options);
            }
        });
    };

})();

Use jQuery directly instead of $ in plugin


'use strict';

var gulp = require('gulp');
var browserSync = require('browser-sync');

gulp.task('browser-sync', function () {
  browserSync({
    server: {
      baseDir: './dist'
    }
  });
});

gulp.task('watch', ['build', 'browser-sync'], function () {
  gulp.watch('./app/**/*.html', ['html']);
  gulp.watch('./app/**/*.js', ['js']);

  gulp.watch('./dist/**/*').on('change', function () {
    browserSync.reload();
  });
});

'use strict';

var gulp = require('gulp');
var browserSync = require('browser-sync');

gulp.task('watch', ['build', 'serve'], function () {
  gulp.watch('./app/**/*.html', ['html']);
  gulp.watch('./app/**/*.js', ['js']);

  gulp.watch('./dist/**/*').on('change', function () {
    browserSync.reload();
  });
});

gulp.task('serve', function () {
  browserSync({
    server: {
      baseDir: './dist'
    }
  });
});

Rename browser-sync task to serve.


(function () {
  'use strict';

  moj.Modules.Conditional = {
    el: '.js-Conditional',

    init: function () {
      var _this = this;

      this.cacheEls();
      this.bindEvents();

      this.$conditionals.each(function () {
        _this.toggleEl($(this));
      });
    },

    bindEvents: function () {
      var _this = this;

      // set focused selector to parent label
      this.$conditionals
        .on('change', function () {
          _this.toggleEl($(this));

          // trigger a deselect event
          $('input[name="' + $(this).attr('name') + '"]').not($(this)).trigger('deselect');
        })
        .on('deselect', function () {
          _this.toggleEl($(this));
        });
    },

    cacheEls: function () {
      this.$conditionals = $(this.el);
    },

    toggleEl: function (el) {
      var $el = el,
          $conditionalEl = $('#' + $el.data('conditionalEl'));

      if ($el.data('conditionalEl')) {
        $el.attr('aria-control', $el.data('conditionalEl'));

        if($el.is(':checked')){
          $conditionalEl.show();
          $conditionalEl.attr('aria-expanded', 'true').attr('aria-hidden', 'false');
        } else {
          $conditionalEl.hide();
          $conditionalEl.attr('aria-expanded', 'false').attr('aria-hidden', 'true');
        }
      }
    }
  };
}());

/* globals _ */

(function () {
  'use strict';

  moj.Modules.Conditional = {
    el: '.js-Conditional',

    init: function () {
      _.bindAll(this, 'render');
      this.cacheEls();
      this.bindEvents();
    },

    bindEvents: function () {
      this.$conditionals.on('change deselect', this.toggle);
      moj.Events.on('render', this.render);
    },

    cacheEls: function () {
      this.$conditionals = $(this.el);
    },

    render: function () {
      this.$conditionals.each(this.toggle);
    },

    toggle: function (e) {
      var $el = $(this),
          $conditionalEl = $('#' + $el.data('conditionalEl'));

      // trigger a deselect event if a change event occured
      if (e.type === 'change') {
        $('input[name="' + $el.attr('name') + '"]').not($el).trigger('deselect');
      }

      // if a conditional element has been set, run the checks
      if ($el.data('conditionalEl')) {
        $el.attr('aria-control', $el.data('conditionalEl'));

        // if checked show/hide the extra content
        if($el.is(':checked')){
          $conditionalEl.show();
          $conditionalEl.attr('aria-expanded', 'true').attr('aria-hidden', 'false');
        } else {
          $conditionalEl.hide();
          $conditionalEl.attr('aria-expanded', 'false').attr('aria-hidden', 'true');
        }
      }
    }
  };
}());

Add module for conditional content


import * as types from "../actions/types";
import { chooseDisplayComponentFromURL } from "../actions/navigation";
import { hasExtension, getExtension } from "../util/extensions";

/* the store for cross-cutting state -- that is, state
not limited to <App>
*/

const getFirstPageToDisplay = () => {
  if (hasExtension("entryPage")) {
    return getExtension("entryPage");
  }
  return chooseDisplayComponentFromURL(window.location.pathname);
};

const general = (state = {
  displayComponent: getFirstPageToDisplay(),
  errorMessage: undefined,
  pathname: window.location.pathname // keep a copy of what the app "thinks" the pathname is
}, action) => {
  switch (action.type) {
    case types.PAGE_CHANGE:
      return Object.assign({}, state, {
        pathname: action.path,
        displayComponent: action.displayComponent,
        errorMessage: action.errorMessage
      });
    case types.UPDATE_PATHNAME:
      return Object.assign({}, state, {
        pathname: action.pathname
      });
    default:
      return state;
  }
};

export default general;

import * as types from "../actions/types";
import { chooseDisplayComponentFromURL } from "../actions/navigation";
import { hasExtension, getExtension } from "../util/extensions";

/* the store for cross-cutting state -- that is, state
not limited to <App>
*/

const getFirstPageToDisplay = () => {
  if (hasExtension("entryPage")) {
    return getExtension("entryPage");
  }
  return chooseDisplayComponentFromURL(window.location.pathname);
};

const general = (state = {
  displayComponent: getFirstPageToDisplay(),
  errorMessage: undefined,
  pathname: window.location.pathname // keep a copy of what the app "thinks" the pathname is
}, action) => {
  switch (action.type) {
    case types.PAGE_CHANGE:
      const stateUpdate = {
        displayComponent: action.displayComponent,
        errorMessage: action.errorMessage
      };
      if (action.path) {
        stateUpdate.pathname = action.path;
      }
      return Object.assign({}, state, stateUpdate);
    case types.UPDATE_PATHNAME:
      return Object.assign({}, state, {
        pathname: action.pathname
      });
    default:
      return state;
  }
};

export default general;

Fix bugs in redux state of pathname


var enums = require("./enum.js");

describe("Enum", function() {
    it("can have symbols with custom properties", function() {
        var color = new enums.Enum({
            red: { de: "rot" },
            green: { de: "grün" },
            blue: { de: "blau" },
        });
        function translate(c) {
            return c.de;
        }
        expect(translate(color.green)).toEqual("grün");
    });
    it("can check for symbol membership", function() {
        var color = new enums.Enum("red", "green", "blue");
        var fruit = new enums.Enum("apple", "banana");
        expect(color.contains(color.red)).toBeTruthy();
        expect(color.contains(fruit.apple)).toBeFalsy();
    });
});

var enums = require("./enums.js");

describe("Enum", function() {
    it("can have symbols with custom properties", function() {
        var color = new enums.Enum({
            red: { de: "rot" },
            green: { de: "grün" },
            blue: { de: "blau" },
        });
        function translate(c) {
            return c.de;
        }
        expect(translate(color.green)).toEqual("grün");
    });
    it("can check for symbol membership", function() {
        var color = new enums.Enum("red", "green", "blue");
        var fruit = new enums.Enum("apple", "banana");
        expect(color.contains(color.red)).toBeTruthy();
        expect(color.contains(fruit.apple)).toBeFalsy();
    });
});

Use correct file name to require enums.js


var TimeSegments = {

  // Segment an array of events by scale
  segment(events, scale, options) {
    scale = scale || 'weeks';
    options = options || {};
    _.defaults(options, {
      startAttribute: 'start',
      endAttribute: 'end'
    });
    events = _.chain(events).clone().sortBy(options.startAttribute).value();
    var segments = {};

    // Clone our events so that we're not modifying the original
    // objects. Loop through them, inserting the events into the
    // corresponding segments
    _.each(_.clone(events), function(e) {

      // Calculate the duration of the event; this determines
      // how many segments it will be in
      var startMoment = moment.utc(e[options.startAttribute]).startOf(scale);
      var endMoment = moment.utc(e[options.endAttribute]).endOf(scale).add(1, 'milliseconds');

      // For each duration, add the event to the corresponding segment
      var segmentStart;
      for(var i = 0, duration = endMoment.diff(startMoment, scale); i < duration; i++) {
        segmentStart = moment.utc(startMoment).add(i, scale).unix();
        if (!segments[segmentStart]) { segments[segmentStart] = []; }
        segments[segmentStart].push(_.clone(e));
      }
    });
    return segments;
  }
};

export default TimeSegments;

var TimeSegments = {

  // Segment an array of events by scale
  segment(events, scale, options) {
    scale = scale || 'weeks';
    options = options || {};
    _.defaults(options, {
      startAttribute: 'start',
      endAttribute: 'end'
    });
    events = _.chain(events).clone().sortBy(options.startAttribute).value();
    var segments = {};

    // Clone our events so that we're not modifying the original
    // objects. Loop through them, inserting the events into the
    // corresponding segments
    _.each(_.clone(events), e => {

      // Calculate the duration of the event; this determines
      // how many segments it will be in
      var startMoment = moment.utc(e[options.startAttribute]).startOf(scale);
      var endMoment = moment.utc(e[options.endAttribute]).endOf(scale).add(1, 'milliseconds');

      // For each duration, add the event to the corresponding segment
      var segmentStart;
      for(var i = 0, duration = endMoment.diff(startMoment, scale); i < duration; i++) {
        segmentStart = moment.utc(startMoment).add(i, scale).unix();
        if (!segments[segmentStart]) { segments[segmentStart] = []; }
        segments[segmentStart].push(_.clone(e));
      }
    });
    return segments;
  }
};

export default TimeSegments;

Refactor source to use more ES6 syntax.


export const formatURL = (url) =>  (
  url.length ? url.replace(/^https?:\/\//, '') : ''
)

export const getTitle = (data) => (
  data.about && data.about.name
    ? data.about.name
    : data.totalItems + " Entries"
)

export default {
  getTitle, formatURL
}

export const formatURL = (url) =>  (
  url.length ? url.replace(/^https?:\/\//, '') : ''
)

export const getTitle = (data) => (
  data.about && (data.about.name || data.about['@id'])
    ? data.about.name || data.about['@id']
    : data.totalItems + " Entries"
)

export default {
  getTitle, formatURL
}

Fix title of resources without name


export let baseUrl = 'http://rosettastack.com';
export let sites = [{
  name: 'Programmers',
  slug: 'programmers',
  seUrl: 'http://programmers.stackexchange.com/',
  langs: ['en', 'fr'],
  db: 'localhost/programmers',
}, {
  name: 'Coffee',
  slug: 'coffee',
  seUrl: 'http://coffee.stackexchange.com/',
  langs: ['en', 'fr'],
  db: 'localhost/coffee',
}, {
  name: 'Server Fault',
  slug: 'serverfault',
  seUrl: 'http://serverfault.com/',
  langs: ['en', 'fr'],
  db: 'localhost/serverfault',
}, {
  name: 'Super User',
  slug: 'superuser',
  seUrl: 'http://superuser.com/',
  langs: ['en', 'fr'],
  db: 'localhost/superuser',
}, {
  name: 'Stack Overflow',
  slug: 'stackoverflow',
  seUrl: 'http://stackoverflow.com/',
  langs: ['en', 'fr'],
  db: 'localhost/programmers',
}, {
  name: 'Android',
  slug: 'android',
  seUrl: 'http://android.stackexchange.com/',
  langs: ['en', 'fr'],
  db: 'localhost/android',
}, {
  name: 'Tor',
  slug: 'tor',
  seUrl: 'http://tor.stackexchange.com/',
  langs: ['en', 'fr'],
  db: 'localhost/tor',
}];

export let baseUrl = 'http://rosettastack.com';
export let sites = [{
  name: 'Programmers',
  slug: 'programmers',
  seUrl: 'http://programmers.stackexchange.com/',
  langs: ['en', 'fr'],
  db: 'localhost/programmers',
}, {
  name: 'Coffee',
  slug: 'coffee',
  seUrl: 'http://coffee.stackexchange.com/',
  langs: ['en', 'fr'],
  db: 'localhost/coffee',
}, {
  name: 'Server Fault',
  slug: 'serverfault',
  seUrl: 'http://serverfault.com/',
  langs: ['en', 'fr'],
  db: 'localhost/serverfault',
}, {
  name: 'Super User',
  slug: 'superuser',
  seUrl: 'http://superuser.com/',
  langs: ['en', 'fr'],
  db: 'localhost/superuser',
}, {
  name: 'Stack Overflow',
  slug: 'stackoverflow',
  seUrl: 'http://stackoverflow.com/',
  langs: ['en', 'fr'],
  db: 'localhost/stackoverflow',
}, {
  name: 'Android',
  slug: 'android',
  seUrl: 'http://android.stackexchange.com/',
  langs: ['en', 'fr'],
  db: 'localhost/android',
}, {
  name: 'Tor',
  slug: 'tor',
  seUrl: 'http://tor.stackexchange.com/',
  langs: ['en', 'fr'],
  db: 'localhost/tor',
}];

Fix db ref for SO


// make is a function that takes an instance of the
// elm runtime
// returns an object where:
//      keys are names to be accessed in pure Elm
//      values are either functions or values
function make(elm) {
    // If Native isn't already bound on elm, bind it!
    elm.Native = elm.Native || {}
    // then the same for our module
    elm.Native.Spruce = elm.Native.Spruce || {}

    // `values` is where the object returned by make ends up internally
    // return if it's already set, since you only want one definition of
    // values for speed reasons
    if (elm.Native.Spruce.values) return elm.Native.Spruce.values

    // return the object of your module's stuff!
    return elm.Native.Spruce.values = {
        listen(address, server) {
            console.log(address, server)

            return server
        }
    }
}

// setup code for Spruce
// Elm.Native.Spruce should be an object with
// a property `make` which is specified above
Elm.Native.Spruce = {};
Elm.Native.Spruce.make = make;

var _binary_koan$elm_spruce$Native_Spruce = function() {

function listen(address, program) {
    console.log(address)

    return program
}

return {
	toHtml: F2(listen)
}

}()

Update native module to 0.18 style


"use strict";

var
    // redis   = require("redis"),
    // client  = redis.createClient(),
    _       = require("underscore"),
    Fetcher = require("l2b-price-fetchers");

module.exports.getBookDetails = function (isbn, cb) {

  var f = new Fetcher();
  f.fetch(
    {vendor: "foyles", isbn: isbn },
    function (err, data) {
      if (err) { return cb(err); }
      var returnData = _.pick(data, "isbn", "authors", "title");
      cb(null, returnData);
    }
  );
};

"use strict";

var
    // redis   = require("redis"),
    // client  = redis.createClient(),
    _       = require("underscore"),
    Fetcher = require("l2b-price-fetchers");

function getBookDetails (isbn, cb) {
  fetchFromScrapers(
    {vendor: "foyles", isbn: isbn },
    function (err, results) {
      if (err) { return cb(err); }
      var returnData = _.pick(results, "isbn", "authors", "title");
      cb(null, returnData);
    }
  );
}

function fetchFromScrapers (options, cb) {
  var f = new Fetcher();
  f.fetch(
    options,
    function (err, data) {
      if (err) { return cb(err); }
      cb(null, data);
    }
  );
}

module.exports = {
  getBookDetails: getBookDetails,
};

Split up the book detail fetching into smaller chunks


'use strict'

let fs = require('fs')
let dotenv = require('dotenv')
let express = require('express')
let session = require('express-session')
let cookieParser = require('cookie-parser')
let bodyParser = require('body-parser')
let morgan = require('morgan')
let routes = require('./api/routes')

dotenv.config({silent: true})

let port = process.env.PORT || 3000
let host = process.env.HOST || 'localhost'
let app = express()

app.use(morgan('dev'))
app.use(cookieParser())
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({
  extended: true
}))

// Load routes
routes(app)

let server = app.listen(port, () => {

  console.log('Mission Control listening at http://%s:%s', host, port)

})

module.exports = server

'use strict'

let fs = require('fs')
let dotenv = require('dotenv')
let express = require('express')
let session = require('express-session')
let cookieParser = require('cookie-parser')
let bodyParser = require('body-parser')
let morgan = require('morgan')
let routes = require('./api/routes')
let extensions = require('./extensions/registry')

dotenv.config({silent: true})

let port = process.env.PORT || 3000
let host = process.env.HOST || 'localhost'
let app = express()

app.use(morgan('dev'))
app.use(cookieParser())
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({
  extended: true
}))

app.locals.ext = extensions.load()

// Load routes
routes(app)

let server = app.listen(port, () => {

  console.log('Mission Control listening at http://%s:%s', host, port)
  console.log('app locals', app.locals.ext)

})

module.exports = server

Load extensions into express app


var vows = require('vows'),
    assert = require('assert'),
    events = require('events'),
    load = require('./load'),
    suite = vows.describe('brushModes');

function d3Parcoords() {
  var promise = new(events.EventEmitter);
  load(function(d3) {
    promise.emit('success', d3.parcoords());
  });
  return promise;
}

suite.addBatch({
  'd3.parcoords': {
    'has by default': {
       topic: d3Parcoords(),
       'three brush modes': function(pc) {
         assert.strictEqual(pc.brushModes().length, 3);
       },
       'the brush mode "None"': function(pc) {
         assert.notStrictEqual(pc.brushModes().indexOf("None"), -1);
       },
       'the brush mode "1D-axes"': function(pc) {
         assert.notStrictEqual(pc.brushModes().indexOf("1D-axes"), -1);
       },
       'the brush mode "2D-strums"': function(pc) {
         assert.notStrictEqual(pc.brushModes().indexOf("2D-strums"), -1);
       }
    },
  }
});

suite.export(module);

var vows = require('vows'),
    assert = require('assert'),
    events = require('events'),
    load = require('./load'),
    suite = vows.describe('brushModes');

function d3Parcoords() {
  var promise = new(events.EventEmitter);
  load(function(d3) {
    promise.emit('success', d3.parcoords());
  });
  return promise;
}

suite.addBatch({
  'd3.parcoords': {
    'has by default': {
       topic: d3Parcoords(),
       'four brush modes': function(pc) {
         assert.strictEqual(pc.brushModes().length, 4);
       },
       'the brush mode "None"': function(pc) {
         assert.notStrictEqual(pc.brushModes().indexOf("None"), -1);
       },
       'the brush mode "1D-axes"': function(pc) {
         assert.notStrictEqual(pc.brushModes().indexOf("1D-axes"), -1);
       },
       'the brush mode "2D-strums"': function(pc) {
         assert.notStrictEqual(pc.brushModes().indexOf("2D-strums"), -1);
       },
       'the brush mode "angular"': function(pc) {
         assert.notStrictEqual(pc.brushModes().indexOf("angular"), -1);
       }
    },
  }
});

suite.export(module);

Add angular brush mode to test


Package.describe({
    summary: "Login service for VKontakte accounts (https://vk.com)",
    version: "0.1.4",
    git: "https://github.com/alexpods/meteor-accounts-vk",
    name: "mrt:accounts-vk"
});

Package.on_use(function(api) {
    api.versionsFrom('METEOR@0.9.0');
    api.use('accounts-base', ['client', 'server']);
    api.imply('accounts-base', ['client', 'server']);
    api.use('accounts-oauth', ['client', 'server']);
    api.imply('accounts-oauth', ['client', 'server']);

    api.use('oauth2', ['client', 'server']);
    api.use('oauth', ['client', 'server']);
    api.use('http', ['server']);
    api.use('underscore', 'server');
    api.use('random', 'client');
    api.use('service-configuration', ['client', 'server']);
    api.use('templating', 'client');

    api.add_files("lib/accounts_vk.js");
    api.add_files('lib/vk_client.js', 'client');
    api.add_files('lib/vk_server.js', 'server');

    api.export('VK');

    api.add_files(['lib/vk_configure.html', 'lib/vk_configure.js', 'lib/vk_styles.css'], 'client');

});

Package.describe({
    summary: "Login service for VKontakte accounts (https://vk.com)",
    version: "0.2.0",
    git: "https://github.com/alexpods/meteor-accounts-vk",
    name: "mrt:accounts-vk"
});

Package.on_use(function(api) {
    api.versionsFrom('METEOR@0.9.0');
    api.use('accounts-base', ['client', 'server']);
    api.imply('accounts-base', ['client', 'server']);
    api.use('accounts-oauth', ['client', 'server']);
    api.imply('accounts-oauth', ['client', 'server']);
    api.use('service-configuration', ['client', 'server']);
    api.imply('service-configuration', ['client', 'server']);

    api.use('oauth2', ['client', 'server']);
    api.use('oauth', ['client', 'server']);
    api.use('http', ['server']);
    api.use('underscore', 'server');
    api.use('random', 'client');
    api.use('templating', 'client');

    api.add_files("lib/accounts_vk.js");
    api.add_files('lib/vk_client.js', 'client');
    api.add_files('lib/vk_server.js', 'server');

    api.export('VK');

    api.add_files(['lib/vk_configure.html', 'lib/vk_configure.js', 'lib/vk_styles.css'], 'client');

});

Add 'imply' service-configuration. Increment version number to 0.2.0


Package.describe({
  summary: "Reactive bootstrap modals for meteor",
  version: "1.0.5",
  git: "https://github.com/jchristman/reactive-modal.git",
  name: "jchristman:reactive-modal"
});

Package.on_use(function (api) {
  if(api.versionsFrom){
    api.versionsFrom('METEOR@1.0');
  }
  api.use(['underscore', 'jquery','templating', 'reactive-var@1.0.6'], 'client');
  api.add_files(['lib/reactive-modal.html', 'lib/reactive-modal.js', 'lib/ev.js'], "client");
  api.export('ReactiveModal', ['client']);
});

Package.describe({
  summary: "Reactive bootstrap modals for meteor",
  version: "1.0.5",
  git: "https://github.com/jchristman/reactive-modal.git",
  name: "jchristman:reactive-modal"
});

Package.on_use(function (api) {
  if(api.versionsFrom){
    api.versionsFrom('METEOR@1.0');
  }
  api.use(['underscore', 'jquery','templating', 'reactive-var@1.0.5'], 'client');
  api.add_files(['lib/reactive-modal.html', 'lib/reactive-modal.js', 'lib/ev.js'], "client");
  api.export('ReactiveModal', ['client']);
});

Update depedencies and version number


jQuery.noConflict(); // Allow the test to run with other libs or jQuery's.

// load testswarm agent
(function() {
	var url = window.location.search;
	url = decodeURIComponent( url.slice( url.indexOf("swarmURL=") + 9 ) );
	if ( !url || url.indexOf("http") !== 0 ) {
		return;
	}
	document.write("<scr" + "ipt src='http://testswarm.com/js/inject.js?" + (new Date).getTime() + "'></scr" + "ipt>");
})();

Handle auto-running of the TestSwarm injection script in the test suite.


import { createAuthMiddleware } from '../src'

describe('auth middleware', () => {
  const middleware = createAuthMiddleware()
  const nextHandler = middleware({})
  const actionHandler = nextHandler()

  test('it returns a function that handles store', () => {
    expect(middleware).toBeInstanceOf(Function)
    expect(middleware.length).toBe(1)
  })

  test('store handler returns function that handles next', () => {
    expect(nextHandler).toBeInstanceOf(Function)
    expect(nextHandler.length).toBe(1)
  })

  test('action handler returns a function that handles action', () => {
    expect(actionHandler).toBeInstanceOf(Function)
    expect(actionHandler.length).toBe(1)
  })
})

import { createAuthMiddleware } from '../src'

describe('auth middleware', () => {
  const middleware = createAuthMiddleware()

  it('returns a function that handles {getState, dispatch}', () => {
    expect(middleware).toBeInstanceOf(Function)
    expect(middleware.length).toBe(1)
  })

  describe('store handler', () => {
    it('returns function that handles next', () => {
      const nextHandler = middleware({})

      expect(nextHandler).toBeInstanceOf(Function)
      expect(nextHandler.length).toBe(1)
    })
  })

  describe('action handler', () => {
    it('action handler returns a function that handles action', () => {
      const nextHandler = middleware({})
      const actionHandler = nextHandler()

      expect(actionHandler).toBeInstanceOf(Function)
      expect(actionHandler.length).toBe(1)
    })
  })
})

Use it instead of test. Small structure change


#!/usr/bin/env nodejs

// Autoformatter for Turing

const fs = require('fs'),
      argv = require('minimist')(process.argv.slice(2), {boolean:true});

var files = argv._;

// Check for flags, and process them
var flags = argv.flags || null;

var format = require('./format.js')(flags);

console.log(argv)

/**
 * Main procedure
 */
files.forEach(file => {
    fs.readFile(file, 'utf8', (err, data) => {
        var ext = (argv.e || argv.extension) || "";
        var out = (argv.o || argv.out) || file + ext;

        if (err) throw err;

        data = format.format(data);

        if (argv.s || argv.stdio) {
            console.log(data);
        } else {
            fs.writeFile(out, data, err => {
                if (err) throw err;
                console.log(out + " has been saved");
            });
        }
    });
});

#!/usr/bin/env nodejs

// Autoformatter for Turing

const fs = require('fs'),
      argv = require('minimist')(process.argv.slice(2), {boolean:true});

var files = argv._;

// Check for flags, and process them
var flags = argv.flags || null;

var format = require('./format.js')(flags);

/**
 * Main procedure
 */
files.forEach(file => {
    fs.readFile(file, 'utf8', (err, data) => {
        var ext = (argv.e || argv.extension) || "";
        var out = (argv.o || argv.out) || file + ext;

        if (err) throw err;

        data = format.format(data);

        if ((argv.e || argv.extension) || (argv.o || argv.out) || (argv.r || argv.replace)) {
            fs.writeFile(out, data, err => {
                if (err) throw err;
                console.log(out + " has been saved");
            });
        } else {
            console.log(data);
        }
    });
});

Change default output behaviour to output to STDIO


import React, {PropTypes} from 'react';
import ReactDOM from 'react-dom';

class Portal extends React.Component {
  componentDidMount() {
    this.nodeEl = document.createElement('div');
    document.body.appendChild(this.nodeEl);
    this.renderChildren(this.props);
  }

  componentWillUnmount() {
    ReactDOM.unmountComponentAtNode(this.nodeEl);
    document.body.removeChild(this.nodeEl);
  }

  componentWillReceiveProps(nextProps) {
    this.renderChildren(nextProps);
  }

  renderChildren(props) {
    let {children} = props;

    if (children == null) {
      children = <div />;
    }

    ReactDOM.render(children, this.nodeEl, this.props.onRender);
  }

  render() {
    return null;
  }
}

Portal.propTypes = {
  children: PropTypes.node,
  onRender: PropTypes.func
};

module.exports = Portal;

import React, {PropTypes} from 'react';
import ReactDOM from 'react-dom';

class Portal extends React.Component {
  componentDidMount() {
    this.nodeEl = document.createElement('div');
    document.body.appendChild(this.nodeEl);
    this.renderChildren(this.props);
  }

  componentWillUnmount() {
    ReactDOM.unmountComponentAtNode(this.nodeEl);
    document.body.removeChild(this.nodeEl);
  }

  componentWillReceiveProps(nextProps) {
    this.renderChildren(nextProps);
  }

  renderChildren(props) {
    ReactDOM.render(props.children, this.nodeEl, props.onRender);
  }

  render() {
    return null;
  }
}

Portal.propTypes = {
  children: PropTypes.node.isRequired,
  onRender: PropTypes.func
};

module.exports = Portal;

Remove default rendered child element


'use strict';

var BaseView = require('../base-view');

function BasePickerView() {
  BaseView.apply(this, arguments);

  this._initialize();
}

BasePickerView.prototype = Object.create(BaseView.prototype);
BasePickerView.prototype.constructor = BasePickerView;

BasePickerView.prototype._initialize = function () {
  this.element = document.createElement('div');

  this.element.className = 'braintree-dropin__picker-view';
  this.element.setAttribute('tabindex', '0');

  this.element.addEventListener('click', this._onSelect.bind(this), false);
  this.element.addEventListener('keydown', function (event) {
    if (event.which === 13) { this._onSelect(); }
  }.bind(this));
};

BasePickerView.prototype._onSelect = function () {};

module.exports = BasePickerView;

'use strict';

var BaseView = require('../base-view');

function BasePickerView() {
  BaseView.apply(this, arguments);

  this._initialize();
}

BasePickerView.prototype = Object.create(BaseView.prototype);
BasePickerView.prototype.constructor = BasePickerView;

BasePickerView.prototype._initialize = function () {
  this.element = document.createElement('div');

  this.element.className = 'braintree-dropin__picker-view';
  this.element.setAttribute('tabindex', '0');

  this.element.addEventListener('click', this._onSelect.bind(this), false);
  this.element.addEventListener('keydown', function (event) {
    if (event.which === 13) { this._onSelect(); }
  }.bind(this));
};

module.exports = BasePickerView;

Remove _onSelect noop from BasePickerView


import { expect } from '../spec_helper'
import jsdom from 'jsdom'
import {
  getRestrictedSize,
  // orientImage,
} from '../../src/helpers/file_helper'

function getDOMString() {
  // 1x1 transparent gif
  const src = 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='
  const landscape = `<img class="landscape" src=${src} width=16 height=9 />`
  return `<html><body>${landscape}</body></html>`
}

describe('file helpers', () => {
  context('#getRestrictedSize', () => {
    it('does not restrict the width and height', () => {
      const { width, height } = getRestrictedSize(800, 600, 2560, 1440)
      expect(width).to.equal(800)
      expect(height).to.equal(600)
    })

    it('restricts the width and height', () => {
      // The width/height are double maxWidth/maxHeight to keep ratios simple
      const { width, height } = getRestrictedSize(5120, 2880, 2560, 1440)
      expect(width).to.equal(2560)
      expect(height).to.equal(1440)
    })
  })

  context('#orientImage', () => {
    const document = jsdom.jsdom(getDOMString())
    const landscapeImage = document.body.querySelector('.landscape')

    it('found the correct assets for testing', () => {
      expect(landscapeImage.width).to.equal(16)
      expect(landscapeImage.height).to.equal(9)
    })

    it('needs to test orienting of images')
    it('needs to test processing of images')
  })
})

import { expect } from '../spec_helper'
import { getRestrictedSize } from '../../src/helpers/file_helper'

describe('file helpers', () => {
  context('#getRestrictedSize', () => {
    it('does not restrict the width and height', () => {
      const { width, height } = getRestrictedSize(800, 600, 2560, 1440)
      expect(width).to.equal(800)
      expect(height).to.equal(600)
    })

    it('restricts the width and height', () => {
      // The width/height are double maxWidth/maxHeight to keep ratios simple
      const { width, height } = getRestrictedSize(5120, 2880, 2560, 1440)
      expect(width).to.equal(2560)
      expect(height).to.equal(1440)
    })
  })
})

Remove tests around canvas objects


// http://unheap.com

$(function() {
  $('.browsehappy').on('click', function() {
    $(this).slideUp('fast');
  });
});

// http://unheap.com

$(function() {
  $('.browsehappy').click(function() {
    $(this).slideUp();
  });
});

Make browse happy slide up cleaner


import App from './app';
import 'babel-polyfill';
common.app = new App(common);
common.app.start();

import App from './app';
common.app = new App(common);
common.app.start();

Remove babel-polyfill import in man.js


// Karma configuration

module.exports = function (config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: './',

    // testing frameworks to use
    frameworks: ['mocha', 'chai', 'sinon'],

    // list of files / patterns to load in the browser. order matters!
    files: [
      // angular source
      'client/lib/angular/angular.js',
      'client/lib/angular-route/angular-route.js',
      'client/lib/angular-mocks/angular-mocks.js',

      // our app code
      'client/app/**/*.js',

      // our spec files - in order of the README
      'specs/client/authControllerSpec.js',
      'specs/client/servicesSpec.js',
      'specs/client/linksControllerSpec.js',
      'specs/client/shortenControllerSpec.js',
      'specs/client/routingSpec.js'
    ],

    // test results reporter to use
    reporters: ['nyan','unicorn'],

    // start these browsers. PhantomJS will load up in the background
    browsers: ['PhantomJS'],

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: false,

    // if true, Karma exits after running the tests.
    singleRun: true

  });
};

// Karma configuration

module.exports = function (config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: './',

    // testing frameworks to use
    frameworks: ['mocha', 'chai', 'sinon'],

    // list of files / patterns to load in the browser. order matters!
    files: [
      // angular source
      'client/lib/angular/angular.js',
      'client/lib/angular-route/angular-route.js',
      'client/lib/angular-mocks/angular-mocks.js',

      // our app code
      'client/app/**/*.js',

      // our spec files - in order of the README
      'specs/client/authControllerSpec.js',
      'specs/client/routingSpec.js'
    ],

    // test results reporter to use
    reporters: ['nyan','unicorn'],

    // start these browsers. PhantomJS will load up in the background
    browsers: ['PhantomJS'],

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: false,

    // if true, Karma exits after running the tests.
    singleRun: true

  });
};

Remove untested files from karma