// Copyright 2020 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.

import React, { Component } from "react";
import { Redirect } from "react-router-dom";

class Home extends Component {
  constructor(properties) {
    super(properties);
    this.state = {
      isLoggedIn: false,
      logUrl: "",
    };
  }

  componentDidMount() {
    fetch("/api/login-status")
      .then((response) => response.json())
      .then((json) =>
        this.setState({ isLoggedIn: json.isLoggedIn, logUrl: json.logUrl })
      );
  }

  render() {
    if (this.state.isLoggedIn) {
      localStorage.setItem("logOutUrl", this.state.logUrl);
      return <Redirect to="/home" />;
    }
    return (

Recipe Finder

          Login

    );
  }
}
export default Home;

Remove non existing class name


const exec = require('shell-utils').exec;
const _ = require('lodash');

const fix = _.includes(process.argv, '--fix') ? '--fix' : '';

const dirs = [
  'lib/src',
  'integration',
  'e2e',
  'scripts',
  'playground/src'
];

run();

function run() {
  exec.execSync('node -v');
  const paths = _.chain(dirs).map((d) => `'${d}/**/*.[tj]s*'`).join(' ').value();
  exec.execSync(`tslint ${paths} ${fix} --format verbose`);
  assertAllTsFilesInSrc();
  exec.execSync(`jest --coverage`);
}

function assertAllTsFilesInSrc() {
  const allFiles = exec.execSyncRead('find ./lib/src -type f');
  const lines = _.split(allFiles, '\n');
  const offenders = _.filter(lines, (f) => !f.endsWith('.ts') && !f.endsWith('.tsx'));
  if (offenders.length) {
    throw new Error(`\n\nOnly ts/tsx files are allowed:\n${offenders.join('\n')}\n\n\n`);
  }
}

const exec = require('shell-utils').exec;
const _ = require('lodash');

const fix = _.includes(process.argv, '--fix') ? '--fix' : '';

const dirs = [
  'lib/src',
  'integration',
  'e2e',
  'scripts',
  'playground/src'
];

run();

function run() {
  const paths = _.chain(dirs).map((d) => `'${d}/**/*.[tj]s*'`).join(' ').value();
  exec.execSync(`tslint ${paths} ${fix} --format verbose`);
  assertAllTsFilesInSrc();
  exec.execSync(`jest --coverage`);
}

function assertAllTsFilesInSrc() {
  const allFiles = exec.execSyncRead('find ./lib/src -type f');
  const lines = _.split(allFiles, '\n');
  const offenders = _.filter(lines, (f) => !f.endsWith('.ts') && !f.endsWith('.tsx'));
  if (offenders.length) {
    throw new Error(`\n\nOnly ts/tsx files are allowed:\n${offenders.join('\n')}\n\n\n`);
  }
}

Remove node version debug line


Error.stackTraceLimit = Infinity;   // And beyond

require('babel-core/register')(require('./babel-defaults'));

// Must be after babel so our fork overrides the one installed by babel.
// Aren't unresponsive OSS projects fun!.
require('source-map-support').install({
  environment: 'node',
  handleUncaughtExceptions: false,
  hookRequire: true
});

/* eslint-disable no-process-env */
var Path = require('path');

Error.stackTraceLimit = Infinity;   // And beyond

if (!process.env.BABEL_CACHE_PATH) {
  process.env.BABEL_CACHE_PATH = Path.resolve('./.babel.cache.json');
}

require('babel-core/register')(require('./babel-defaults'));

// Must be after babel so our fork overrides the one installed by babel.
// Aren't unresponsive OSS projects fun!.
require('source-map-support').install({
  environment: 'node',
  handleUncaughtExceptions: false,
  hookRequire: true
});

Move babel cache to local project


define([], function () {
  return {
    hasLoggedInUser: function() {
        return !!(this.profile && this.profile.get('username'));
    },
    profile: null
  }
});

define([], function () {
  return {
    hasLoggedInUser: function() {
        return !!(this.profile && this.profile.get('selected_identity'));
    },
    profile: null
  }
});

Use selected_identity for logged in user predicate


const express = require('express');
const path = require('path');
const PORT = process.env.PORT || 4000;
const app = express();

// API endpoints
app.get('/api/hello', (req, res) => {
  res.set('Content-Type', 'application/json');
  res.json({ message: 'Hello from the server!' });
});

// Serve any static files.
app.use(express.static(path.resolve(__dirname, '../client/build')));

// All remaining requests return the React app, so it can handle routing.
app.get('*', (request, response) => {
  response.sendFile(path.resolve(__dirname, '../client/build', 'index.html'));
});

// Enforce HTTPS
function enforceHTTPS(request, response, next) {
  if (process.env.NODE_ENV === 'development') return next();

  if (request.headers['x-forwarded-proto'] !== 'https') {
    const httpsUrl = ['https://', request.headers.host, request.url].join('');
    return response.redirect(httpsUrl);
  }

  return next();
}
app.use(enforceHTTPS);

// Start the server
app.listen(PORT, () => {
  console.log(`Listening on port ${PORT}.`);
});

const express = require('express');
const path = require('path');
const PORT = process.env.PORT || 4000;
const app = express();

// Enforce HTTPS
function enforceHTTPS(request, response, next) {
  if (process.env.NODE_ENV === 'development') return next();

  if (request.headers['x-forwarded-proto'] !== 'https') {
    const httpsUrl = ['https://', request.headers.host, request.url].join('');
    return response.redirect(httpsUrl);
  }

  return next();
}
app.use(enforceHTTPS);

// API endpoints
app.get('/api/hello', (req, res) => {
  res.set('Content-Type', 'application/json');
  res.json({ message: 'Hello from the server!' });
});

// Serve any static files.
app.use(express.static(path.resolve(__dirname, '../client/build')));

// All remaining requests return the React app, so it can handle routing.
app.get('*', (request, response) => {
  response.sendFile(path.resolve(__dirname, '../client/build', 'index.html'));
});

// Start the server
app.listen(PORT, () => {
  console.log(`Listening on port ${PORT}.`);
});

Move https middleware up front


angular.module("omniServices")
	.service("BalanceSocket", ["$rootScope","$location",function BalanceSocketService($rootScope,$location){
		var self = this;
		self.connected =false;
		self.connect = function(){
			self.socket = io.connect($location.protocol()+'://' + $location.host() + ':' + $location.port() + "/balance");
			self.connected=true;
		}

		self.disconnect = function(){
			self.socket.disconnect();
			self.socket=null;
			self.connected =false;
		}

		self.on = function(eventName,callback){
			self.socket.on(eventName,callback);
			// function(msg){
			// 	$rootScope.$apply(function(){callback(msg)});
			// })
		}

		self.emit = function(eventName,data){
			self.socket.emit(eventName,data);
		}
	}])

angular.module("omniServices")
	.service("BalanceSocket", ["$rootScope","$location",function BalanceSocketService($rootScope,$location){
		var self = this;
		self.connected =false;
		self.connect = function(){
			self.socket = io.connect($location.protocol()+'://' + $location.host() + ':' + $location.port() + "/balance", {'force new connection':true});
			self.connected=true;
		}

		self.disconnect = function(){
			self.socket.disconnect();
			self.socket=null;
			self.connected =false;
		}

		self.on = function(eventName,callback){
			self.socket.on(eventName,callback);
			// function(msg){
			// 	$rootScope.$apply(function(){callback(msg)});
			// })
		}

		self.emit = function(eventName,data){
			self.socket.emit(eventName,data);
		}
	}])

Fix weird logout/login websocket connection issue


AccountsTemplates.addFields([
  {
      _id: "username",
      type: "text",
      displayName: "username",
      required: true,
      minLength: 3,
  },
]);

AccountsTemplates.removeField('password');
AccountsTemplates.removeField('email');

AccountsTemplates.addFields([
  {
      _id: "username",
      type: "text",
      displayName: "User name",
      required: true,
      minLength: 3,
  },

  {
      _id: 'email',
      type: 'email',
      required: true,
      displayName: "Email",
      re: /.+@(.+){2,}\.(.+){2,}/,
      errStr: 'Invalid email',
  },

  {
      _id: 'password',
      type: 'password',
      placeholder: {
          signUp: "At least six characters"
      },
      required: true,
      minLength: 6,
      re: /(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,}/,
      errStr: 'At least 1 digit, 1 lowercase and 1 uppercase',
  },

]);

Fix order of fields in registration form.


'use strict';
var React = require('react');

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

module.exports = {
  contextTypes:  function() {
    return {
      getCurrentPathname: React.PropTypes.func.isRequired,
      getCurrentParams: React.PropTypes.func.isRequired,
    };
  },
  getAllPosts: function () {
    return paths.allPosts();
  },
  getAllPages: function() {
    return paths.getPages();
  },
  getPost: function() {
    var params = this.context.getCurrentParams();
    var post = params.post;
    var splat = params.splat;

    if(splat) {
      return this.getPostForPath(splat + '/' + post);
    }

    return this.getPostForPath(post);
  },
  getPage: function() {
    // remove leading slash
    return this.getPageForPath(this.context.getCurrentPath().slice(1));
  },
  getPostForPath: function(path) {
    return paths.postForPath(path);
  },
  getPageForPath: function(path) {
    return paths.pageForPath(path);
  },
  getPageTitle: function() {
    var post = this.getPost();

    if(post && post.name) {
      return post.name;
    }

    var routes = this.context.getCurrentRoutes();
    var routeName = routes[routes.length - 1].name;

    if(routeName) {
      return routeName.replace('/', '');
    }

    return '';
  },
};

'use strict';
var React = require('react');

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

module.exports = {
  contextTypes:  function() {
    return {
      getCurrentPathname: React.PropTypes.func.isRequired,
      getCurrentParams: React.PropTypes.func.isRequired,
    };
  },
  getAllPosts: function () {
    return paths.allPosts();
  },
  getAllPages: function() {
    return paths.getPages();
  },
  getPost: function() {
    var params = this.context.getCurrentParams();
    var post = params.post;
    var splat = params.splat;

    if(splat) {
      return this.getPostForPath(splat + '/' + post);
    }

    return this.getPostForPath(post);
  },
  getPage: function() {
    // remove leading slash
    return this.getPageForPath(this.context.getCurrentPath().slice(1));
  },
  getPostForPath: function(path) {
    return paths.postForPath(path);
  },
  getPageForPath: function(path) {
    return paths.pageForPath(path);
  },
  getPageTitle: function() {
    var post = this.getPost();

    if(post && post.title) {
      return post.title;
    }

    var routes = this.context.getCurrentRoutes();
    var routeName = routes[routes.length - 1].name;

    if(routeName) {
      return routeName.replace('/', '');
    }

    return '';
  },
};

Use post.title for page title


import * as Types from './types'

const initialState = {
  server: {}
}

const mapServerState = (serverState, state) => {
  return {
    ...state,
    server: serverState
  }
}

export default (state = initialState, action) => {
  switch (action.type) {
    case Types.SERVER_STATE_LOADED:
      return mapServerState(action.serverState, state)
      return state;
    case Types.REQUEST_ACCEPTED_BY_SERVER:
      console.log('Request accepted:', action.url);
      return state;
    case Types.SERVER_STATE_LOAD_FAILED:
    case Types.REQUEST_ACCEPTED_BY_SERVER:
      console.error(action.error)
      return state
    default:
      return state
  }
}

import * as Types from './types'

const initialState = {
  server: {}
}

const mapServerState = (serverState, state) => {
  return {
    ...state,
    server: serverState
  }
}

export default (state = initialState, action) => {
  switch (action.type) {
    case Types.SERVER_STATE_LOADED:
      return mapServerState(action.serverState, state)
      return state
    case Types.REQUEST_ACCEPTED_BY_SERVER:
      console.debug('Request accepted:', action.url)
      return state
    case Types.SERVER_STATE_LOAD_FAILED:
    case Types.REQUEST_ACCEPTED_BY_SERVER:
      console.error(action.error)
      return state
    default:
      return state
  }
}

Change log level for 'Request accepted'


export default class TranscodeError extends Error {
  constructor(message, file, additional = '') {
    super();
    this.message = message;
    this.file = file;
    this.additional = additional;
  }

  toString() {
    return [`[TranscodeError: ${this.message}]`, `File: ${this.file}`, this.additional].join("\n");
  }
}

export default class TranscodeError extends Error {
  constructor(message, file, additional = '') {
    super();
    this._message = message;
    this.file = file;
    this.additional = additional;
    this.message = [`[TranscodeError: ${this._message}]`, `File: ${this.file}`, this.additional].join("\n");
  }

  toString() {
    return this.message;
  }
}

Include original error message in TranscodeError.


/*jslint node:true, sloppy:true*/
/*globals define*/

if (!global.evaluated) {
    require('../util/adapter.js');
}

var path = '../dist/amd/strict';
global.modules = [path + '/Class.js', path + '/AbstractClass', path + '/Interface', path + '/FinalClass', path + '/instanceOf', path + '/common/hasDefineProperty'];
global.build = 'amd/strict';
define(['../verifications', '../functional'], function () { });

/*jslint node:true, sloppy:true*/
/*globals define*/

if (!global.evaluated) {
    require('../util/adapter.js');
}

var path = '../dist/amd/strict';
global.modules = [path + '/Class', path + '/AbstractClass', path + '/Interface', path + '/FinalClass', path + '/instanceOf', path + '/common/hasDefineProperty'];
global.build = 'amd/strict';

define(['../verifications', '../functional'], function () { });

Fix Class being loaded 2 times in tests.


const _ = require('lodash');
const imageConfig = require('../config/tmdb.json').images;
const formatApiResponse = require('../utils/formatApiResponse');
const { createTransformMany } = require('./utils');

const ORIGINAL_IMAGE_SIZE = 'original';
const BASE_IMAGE_URL = imageConfig.base_url;

const getImageUrl = (baseUrl, size, imagePath) => `${baseUrl}${size}${imagePath}`;

const getAllImageSizes = (imagePath, type) => {
    const sizes = imageConfig[`${type}_sizes`];

    if (!imagePath) {
        return null;
    }

    if (!sizes) {
        throw new Error(`Incorrect image type ${type} provided`);
    }

    return sizes.filter((size) => size !== ORIGINAL_IMAGE_SIZE)
        .map((size) => ({
            width: parseInt(size.replace(/[^0-9]/g, '')),
            name: imagePath.replace('/', ''),
            url: getImageUrl(BASE_IMAGE_URL, size, imagePath),
        }));
};

const transformMovie = (movie) => {
    return {
        ...movie,
        apiId: movie.tmdbId,
        images: {
            poster: {
                altText: movie.title,
                original: getImageUrl(BASE_IMAGE_URL, ORIGINAL_IMAGE_SIZE, movie.poster),
                sizes: getAllImageSizes(movie.poster, 'poster'),
            },
            backdrop: getAllImageSizes(movie.backdrop, 'backdrop'),
        },
    };
};

module.exports = {
    transformOne: transformMovie,
    transformMany: _.flow(createTransformMany('movies', transformMovie), formatApiResponse.forMany),
};

const _ = require('lodash');
const imageConfig = require('../config/tmdb.json').images;
const formatApiResponse = require('../utils/formatApiResponse');
const { createTransformMany } = require('./utils');

const ORIGINAL_IMAGE_SIZE = 'original';
const BASE_IMAGE_URL = imageConfig.secure_base_url;

const getImageUrl = (baseUrl, size, imagePath) => `${baseUrl}${size}${imagePath}`;

const getAllImageSizes = (imagePath, type) => {
    const sizes = imageConfig[`${type}_sizes`];

    if (!imagePath) {
        return null;
    }

    if (!sizes) {
        throw new Error(`Incorrect image type ${type} provided`);
    }

    return sizes.filter((size) => size !== ORIGINAL_IMAGE_SIZE)
        .map((size) => ({
            width: parseInt(size.replace(/[^0-9]/g, '')),
            name: imagePath.replace('/', ''),
            url: getImageUrl(BASE_IMAGE_URL, size, imagePath),
        }));
};

const transformMovie = (movie) => {
    return {
        ...movie,
        apiId: movie.tmdbId,
        images: {
            poster: {
                altText: movie.title,
                original: getImageUrl(BASE_IMAGE_URL, ORIGINAL_IMAGE_SIZE, movie.poster),
                sizes: getAllImageSizes(movie.poster, 'poster'),
            },
            backdrop: getAllImageSizes(movie.backdrop, 'backdrop'),
        },
    };
};

module.exports = {
    transformOne: transformMovie,
    transformMany: _.flow(createTransformMany('movies', transformMovie), formatApiResponse.forMany),
};

Upgrade image base_url to SSL


const firebase = require("firebase");

async function deleteInstallation() {
  try {
    // [START delete_installation]
    await firebase.installations().delete();
    // [END delete_installation]
  } catch (err) {
    console.error('Unable to delete installation: ', err);
  }
}

async function getInstallationId() {
  try {
    // [START get_installation_id]
    const installationId = await firebase.installations().getId();
    console.log(installationId);
    // [END get_installation_id]
  } catch (err) {
    console.error('Unable to get Installation ID: ', err);
  }
}

async function getAuthenticationToken() {
  try {
    // [START get_auth_token]
    const authToken = await firebase.installations()
        .getToken(/* forceRefresh */ true);
    console.log(authToken);
    // [END get_auth_token]
  } catch (err) {
    console.error('Unable to get auth token: ', err);
  }
}

async function setOnIdChangeHandler() {
  try {
    // [START set_id_change_handler]
    await firebase.installations().onIdChange((newId) => {
      console.log(newId);
      // TODO: Handle new installation ID.
    });
    // [START set_id_change_handler]
  } catch (err) {
    console.error('Unable to set ID change handler: ', err);
  }
}

const firebase = require("firebase");

async function deleteInstallation() {
  try {
    // [START delete_installation]
    await firebase.installations().delete();
    // [END delete_installation]
  } catch (err) {
    console.error('Unable to delete installation: ', err);
  }
}

async function getInstallationId() {
  try {
    // [START get_installation_id]
    const installationId = await firebase.installations().getId();
    console.log(installationId);
    // [END get_installation_id]
  } catch (err) {
    console.error('Unable to get Installation ID: ', err);
  }
}

async function getAuthenticationToken() {
  try {
    // [START get_auth_token]
    const installationToken = await firebase.installations()
        .getToken(/* forceRefresh */ true);
    console.log(installationToken);
    // [END get_auth_token]
  } catch (err) {
    console.error('Unable to get auth token: ', err);
  }
}

async function setOnIdChangeHandler() {
  try {
    // [START set_id_change_handler]
    await firebase.installations().onIdChange((newId) => {
      console.log(newId);
      // TODO: Handle new installation ID.
    });
    // [START set_id_change_handler]
  } catch (err) {
    console.error('Unable to set ID change handler: ', err);
  }
}

Use consistent auth token variable


define(['jquery', 'underscore', 'backbone', 'model_want', 'utils'], function($, _, Backbone, Models, Utils) {
	Models.Wants = Backbone.Collection.extend({
		model: Models.Want,
		initialize: function() {
			this.fetch({
				dataType: 'json',
				success: function(model, response, options) {

				},
				error: function(model, response, options) {
					if (response.status === 403) {
						Utils.logIn();
					}
				}
			});
		},
		parse: function(response) {
			return response.listings;
		},

		comparator: function(first, second) {
			var dateValue1 = first.get("dateStart");
			var dateValue2 = second.get("dateStart");

			var firstDate = Date.parse(dateValue1);
			var secondDate = Date.parse(dateValue2);

			var difference = secondDate - firstDate;
			if (difference > 0) {
				return 1;
			} else if (difference == 0) {
				return 0;
			} else {
				return -1;
			}
		}
	});
	return Models;
});

define(['jquery', 'underscore', 'backbone', 'model_want', 'utils'], function($, _, Backbone, Models, Utils) {
	Models.Wants = Backbone.Collection.extend({
		model: Models.Want,
		initialize: function() {
			this.fetch({
				dataType: 'json',
				success: function(model, response, options) {

				},
				error: function(model, response, options) {
					if (response.status === 403) {
						Utils.logIn();
					}
				}
			});
		},
		parse: function(response) {
			return response.listings;
		},

		comparator: function(first, second) {
			var dateValue1 = first.get("dateStart").replace(/ /, 'T');
			var dateValue2 = second.get("dateStart").replace(/ /, 'T');

			var firstDate = Date.parse(dateValue1);
			var secondDate = Date.parse(dateValue2);

			var difference = secondDate - firstDate;
			if (difference > 0) {
				return 1;
			} else if (difference === 0) {
				return 0;
			} else {
				return -1;
			}
		}
	});
	return Models;
});

Replace space with "T" in date strings, to allow parsing.


module.path.unshift('js');

(function(){
	module('import class ui.TabContainer');
	module('import class marcuswestin.Layout');
	module('import class lib.navigationManager');
	module('import class marcuswestin.views.factory');

	var layout = new marcuswestin.Layout();
	var tabContainer = new ui.TabContainer();

	document.body.appendChild(layout.getElement());
	layout.getHeader().appendChild(tabContainer.getElement());

	tabContainer.addTab('All');
	tabContainer.addTab('Blog');
	tabContainer.addTab('Twitter');
	tabContainer.addTab('Youtube');
	tabContainer.addTab('Projects');
	tabContainer.addTab('Bio & Resume');

	lib.navigationManager.subscribe('Navigate', function(destination, items){
		var content = layout.getContent();
		content.innerHTML = '';

		for (var i=0, item; item = items[i]; i++) {
			var view = marcuswestin.views.factory.getView(item);
			content.appendChild(view.getElement());
		}
		layout.onResize();
	});
})();

module.path.unshift('js');

(function(){
	module('import class ui.TabContainer');
	module('import class marcuswestin.Layout');
	module('import class lib.navigationManager');
	module('import class marcuswestin.views.factory');

	var layout = new marcuswestin.Layout();
	var tabContainer = new ui.TabContainer();

	document.body.appendChild(layout.getElement());
	layout.getHeader().appendChild(tabContainer.getElement());

	tabContainer.addTab('All');
	tabContainer.addTab('Blog');
	tabContainer.addTab('Github');
	tabContainer.addTab('Twitter');
	tabContainer.addTab('Youtube');
	tabContainer.addTab('Projects');
	tabContainer.addTab('Bio & Resume');

	lib.navigationManager.subscribe('Navigate', function(destination, items){
		var content = layout.getContent();
		content.innerHTML = '';

		for (var i=0, item; item = items[i]; i++) {
			var view = marcuswestin.views.factory.getView(item);
			content.appendChild(view.getElement());
		}
		layout.onResize();
	});
})();

Add github to list of streams


import {spawn} from 'child_process';

const requiresHarmonyFlag = parseInt(/^v(\d+)\./.exec(process.version)[1], 10) < 7;
const harmonyProxies = requiresHarmonyFlag ? ['--harmony_proxies'] : [];
const args = [
  ...harmonyProxies,
  'node_modules/jest/bin/jest',
  // First two args are always node and the script running, i.e. ['node', './tools/testCi.js', ...]
  ...process.argv.slice(2)
];

const testCi = spawn('node', args);
const consoleLogger = data => console.log(`${data}`);

testCi.stdout.on('data', consoleLogger);
testCi.stderr.on('data', consoleLogger);

import {spawn} from 'child_process';

const requiresHarmonyFlag = parseInt(/^v(\d+)\./.exec(process.version)[1], 10) < 7;
const harmonyProxies = requiresHarmonyFlag ? ['--harmony_proxies'] : [];
const args = [
  ...harmonyProxies,
  'node_modules/jest/bin/jest',
  // First two args are always node and the script running, i.e. ['node', './tools/testCi.js', ...]
  ...process.argv.slice(2)
];

const testCi = spawn('node', args);
const consoleLogger = data => console.log(`${data}`); // eslint-disable-line no-console

testCi.stdout.on('data', consoleLogger);
testCi.stderr.on('data', consoleLogger);

Remove linting warning in build script


import { Command } from 'substance'

/*
  This command intended to switch view and scroll to the selected node.
  Command state becoming active only for certain type of custom selection,
  e.g. if you want to use it, provide config with selectionType property.
*/
export default class EditEntityCommand extends Command {
  getCommandState (params, context) {
    let sel = params.selection
    let newState = {
      disabled: true
    }

    if (sel.isCustomSelection()) {
      if (sel.customType === this.config.selectionType) {
        newState.disabled = false
        newState.nodeId = sel.nodeId
      }
    }

    return newState
  }

  execute (params, context) {
    const appState = context.appState
    const viewName = appState.get('viewName')
    if (viewName !== 'metadata') {
      context.editor.send('updateViewName', 'metadata')
      context.articlePanel.refs.content.send('executeCommand', this.name, params)
    } else {
      const api = context.api
      const editorSession = params.editorSession
      const doc = editorSession.getDocument()
      const sel = params.selection
      const node = doc.get(sel.nodeId)
      const newSel = api._selectFirstRequiredPropertyOfMetadataCard(node)
      api._setSelection(newSel)
    }
  }
}

import { Command } from 'substance'

/*
  This command intended to switch view and scroll to the selected node.
  Command state becoming active only for certain type of custom selection,
  e.g. if you want to use it, provide config with selectionType property.
*/
export default class EditEntityCommand extends Command {
  getCommandState (params, context) {
    let sel = params.selection
    let newState = {
      disabled: true
    }

    if (sel.isCustomSelection()) {
      if (sel.customType === this.config.selectionType) {
        newState.disabled = false
        newState.nodeId = sel.nodeId
      }
    }

    return newState
  }

  execute (params, context) {
    const appState = context.appState
    const viewName = appState.get('viewName')
    if (viewName !== 'metadata') {
      const sel = params.selection
      context.editor.send('updateViewName', 'metadata')
      context.articlePanel.refs.content.send('scrollTo', { nodeId: sel.nodeId })
    }
  }
}

Use scrollTo action for edit entity commands.


TC.control = TC.control || {};

if (!TC.Control) {
    TC.syncLoadJS(TC.apiLocation + 'TC/Control');
}

TC.control.Click = function () {
    var self = this;

    TC.Control.apply(self, arguments);

    self.callback = (self.options && self.options.callback) || function (coord, point) {
        console.log('[Click][' + coord[0] + ', ' + coord[1] + '][' + point[0] + ', ' + point[1] + ']');
    };

    self.wrap = new TC.wrap.control.Click(self);
};

TC.inherit(TC.control.Click, TC.Control);

(function () {
    var ctlProto = TC.control.Click.prototype;

    ctlProto.CLASS = 'tc-ctl-click';

    ctlProto.register = function (map) {
        var self = this;
        self.wrap.register(map);
        TC.Control.prototype.register.call(self, map);
    };

    ctlProto.activate = function () {
        var self = this;
        TC.Control.prototype.activate.call(self);
        self.wrap.activate();
    };

    ctlProto.deactivate = function () {
        var self = this;
        self.wrap.deactivate();
        TC.Control.prototype.deactivate.call(self);
    };

})();

TC.control = TC.control || {};

if (!TC.Control) {
    TC.syncLoadJS(TC.apiLocation + 'TC/Control');
}

TC.control.Click = function () {
    var self = this;

    TC.Control.apply(self, arguments);

    if (self.options && self.options.callback) {
        self.callback = self.options.callback;
    }

    self.wrap = new TC.wrap.control.Click(self);
};

TC.inherit(TC.control.Click, TC.Control);

(function () {
    var ctlProto = TC.control.Click.prototype;

    ctlProto.CLASS = 'tc-ctl-click';

    ctlProto.register = function (map) {
        var self = this;
        self.wrap.register(map);
        TC.Control.prototype.register.call(self, map);
    };

    ctlProto.activate = function () {
        var self = this;
        TC.Control.prototype.activate.call(self);
        self.wrap.activate();
    };

    ctlProto.deactivate = function () {
        var self = this;
        self.wrap.deactivate();
        TC.Control.prototype.deactivate.call(self);
    };

    ctlProto.callback = function (coord, point) {
        console.log('[Click][' + coord[0] + ', ' + coord[1] + '][' + point[0] + ', ' + point[1] + ']');
    };
})();

Move callback method to prototype


Fix uri in secto_identifier js


'use strict';

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

module.exports = function () {
  var output         = [require('../../package.json').version];
  var gitPath        = path.join(__dirname, '..','..','.git');
  var headFilePath   = path.join(gitPath, 'HEAD');

  try {
    if (fs.existsSync(headFilePath)) {
      var branchSHA;
      var headFile = fs.readFileSync(headFilePath, {encoding: 'utf8'});
      var branchName = headFile.split('/').slice(-1)[0].trim();
      var refPath = headFile.split(' ')[1];

      if (refPath) {
        var branchPath = path.join(gitPath, refPath.trim());
        branchSHA  = fs.readFileSync(branchPath);
      } else {
        branchSHA = branchName;
      }

      output.push(branchName);

      output.push(branchSHA.slice(0,10));
    }
  } catch (err) {
    console.error(err.stack);
  }

  return output.join('-');
};

'use strict';

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

module.exports = function () {
  var packageVersion = require('../../package.json').version;
  var output         = [packageVersion];
  var gitPath        = path.join(__dirname, '..','..','.git');
  var headFilePath   = path.join(gitPath, 'HEAD');

  if (packageVersion.indexOf('+') > -1) {
    try {
      if (fs.existsSync(headFilePath)) {
        var headFile = fs.readFileSync(headFilePath, {encoding: 'utf8'});
        var branchName = headFile.split('/').slice(-1)[0].trim();
        var refPath = headFile.split(' ')[1];
        var branchSHA;

        if (refPath) {
          var branchPath = path.join(gitPath, refPath.trim());
          branchSHA  = fs.readFileSync(branchPath);
        } else {
          branchSHA = branchName;
        }

        output.push(branchSHA.slice(0,10));
      }
    } catch (err) {
      console.error(err.stack);
    }

    return output.join('.');
  } else {
    return packageVersion;
  }
};

Update version calculation to match Ember.js.


var log = {
    add: function(eventType, payload) {
        var timestamp = new Date();
        var event = {
            type: eventType,
            instanceID: Florence.instanceID,
            created: timestamp.toISOString(),
            timestamp: timestamp.getTime(),
            payload: payload || null
        }
        websocket.send("log:" + JSON.stringify(event));
    },
    eventTypes: {
        appInitialised: "APP_INITIALISED",
        requestSent: "REQUEST_SENT",
        requestReceived: "REQUEST_RECEIVED",
        requestFailed: "REQUEST_FAILED",
        pingSent: "PING_SENT",
        pingReceived: "PING_RECEIVED",
        pingFailed: "PING_FAILED",
        socketOpen: "SOCKET_OPEN",
        socketBufferFull: "SOCKET_BUFFER_FULL",
        socketError: "SOCKET_ERROR",
        unexpectedRuntimeError: "UNEXPECTED_RUNTIME_ERROR",
        runtimeWarning: "RUNTIME_WARNING"
    }
}

var log = {
    add: function(eventType, payload) {
        var timestamp = new Date();
        var event = {
            type: eventType,
            instanceID: Florence.instanceID,
            created: timestamp.toISOString(),
            timestamp: timestamp.getTime(),
            payload: payload || null
        }

        // Add unrecognised log type to log event
        if (!log.eventTypesMap[eventType]) {
            console.log("Unrecognised log type: ", eventType, payload);
            event.type = log.eventTypes.unrecognisedLogType;
        }

        websocket.send("log:" + JSON.stringify(event));
    },
    eventTypes: {
        appInitialised: "APP_INITIALISED",
        requestSent: "REQUEST_SENT",
        requestReceived: "REQUEST_RECEIVED",
        requestFailed: "REQUEST_FAILED",
        pingSent: "PING_SENT",
        pingReceived: "PING_RECEIVED",
        pingFailed: "PING_FAILED",
        socketOpen: "SOCKET_OPEN",
        socketBufferFull: "SOCKET_BUFFER_FULL",
        socketError: "SOCKET_ERROR",
        unexpectedRuntimeError: "UNEXPECTED_RUNTIME_ERROR",
        runtimeWarning: "RUNTIME_WARNING",
        unrecognisedLogType: "UNRECOGNISED_LOG_TYPE"
    },
    eventTypesMap: {}
}

// Create a map for event type values to property name
// so that we can detect when we're logging an unrecognised event
for (var eventType in log.eventTypes) {
    log.eventTypesMap[log.eventTypes[eventType]] = eventType;
}

$.ajaxSetup({
    beforeSend: function(request) {
        var requestID = (S4() + S4());
        request.setRequestHeader("X-Request-ID", requestID);
        request.uniqueID = requestID;
        log.add(
            log.eventTypes.requestSent, {
                requestID
            }
        );
    }
});

$(document).ajaxSuccess(function(event, request, settings) {
    log.add(
        log.eventTypes.requestReceived, {
            requestID: request.uniqueID,
            status: request.statusText
        }
    );
});

$(document).ajaxError(function(event, request, settings) {
    log.add(
        log.eventTypes.requestReceived, {
            requestID: request.uniqueID,
            status: request.statusText
        }
    );
});

Add request ID to every ajax in legacy Florence


// If you try printing the below you would see "[object Object]"" which is not that useful.

// Instead, you can click "Inspect it" to see the result in the JavaScript console where you can look inside the object.
// That result is not displayed in the editor window -- it is only displayed in the console.

// To open the console in various browsers, see:
// https://webmasters.stackexchange.com/questions/8525/how-do-i-open-the-javascript-console-in-different-browsers

// You can also try selecting just parts of the object (like the array) and use "Inspect It" or "Print it".

const myObject = {
    hello: "world",
    coding: "is fun!",
    todo: ["learn Mithril.js", "learn Tachyons.js"],
    snippetIndex: 3
}

myObject

// If you try printing the below you would see "[object Object]"" which is not that useful.

// Instead, you can click "Inspect it" to see the result in the JavaScript console where you can look inside the object.
// That result is not displayed in the editor window -- it is only displayed in the console.

// To open the console in various browsers, see:
// https://webmasters.stackexchange.com/questions/8525/how-do-i-open-the-javascript-console-in-different-browsers

// You can also try selecting just parts of the object (like the array) and use "Inspect It" or "Print it".

const myObject = {
    hello: "world",
    coding: "is fun!",
    todo: ["learn Mithril.js", "learn Tachyons.css"],
    snippetIndex: 3
}

myObject

Update inspecitng an object example


angular
  .module('search', ['angucomplete-alt'])
  .controller('SearchController', ['SEARCH_DATA', '$window', function (searchData, $window) {
      var me = this;

      me.docs = searchData.data;
      me.src = searchData.src;

      me.search = function (selected) {
          var doc,
              path,
              url;

          if (selected) {
              doc = selected.originalObject;
              me.src.forEach(function (src) {
                  if (src[src.length-1] === '/') {
                    src = src.slice(0, src.length-1);
                  }

                  if (doc.meta.path.indexOf(src) === 0) {
                      path = doc.meta.path.slice(src.length);
                  }
              });

              if (path !== undefined) {
                url = '/' + path.split('/')
                                       .concat([doc.meta.filename])
                                       .filter(function (p) { return p; })
                                       .join('_')  + '.html#line' + doc.meta.lineno;

                $window.location = url;
              }
          }
      };
  }]);

angular
  .module('search', ['angucomplete-alt'])
  .controller('SearchController', ['SEARCH_DATA', '$window', function (searchData, $window) {
      var me = this;

      me.docs = searchData.data;
      me.src = searchData.src;

      me.search = function (selected) {
          var doc,
              path,
              url;

          if (selected) {
              doc = selected.originalObject;
              me.src.forEach(function (src) {
                  if (src[src.length-1] === '/') {
                    src = src.slice(0, src.length-1);
                  }

                  if (doc.meta.path.indexOf(src) === 0) {
                      path = doc.meta.path.slice(src.length);
                  }
              });

              if (path !== undefined) {
                url = path.split('/')
                          .concat([doc.meta.filename])
                          .filter(function (p) { return p; })
                          .join('_')  + '.html#line' + doc.meta.lineno;

                $window.location = url;
              }
          }
      };
  }]);

Fix url redirection to relative


/*
 * `server` task
 * =============
 *
 * Creates a Browsersync Web server instance for live development. Makes use of
 * some Webpack middlewares to enable live reloading features.
 */

import browsersync from 'browser-sync';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';
import config from '../config/browsersync';
import webpack from '../lib/webpack';

const server = browsersync.create();

const serve = () => {
  const compiler = webpack('development');

  config.middleware = [
    webpackDevMiddleware(compiler, {
      quiet: true,
      stats: {
        colors: true
      }
    }),
    webpackHotMiddleware(compiler, {
      log: () => {}
    })
  ];

  server.init(config);
};
serve.description = `Create a Browsersync instance for live development.`;

export default serve;

/*
 * `server` task
 * =============
 *
 * Creates a Browsersync Web server instance for live development. Makes use of
 * some Webpack middlewares to enable live reloading features.
 */

import browsersync from 'browser-sync';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';
import config from '../config/browsersync';
import webpack from '../lib/webpack';

const server = browsersync.create();

const serve = () => {
  const compiler = webpack('development');

  config.middleware = [
    webpackDevMiddleware(compiler, {
      quiet: true,
      stats: {
        colors: true,
        modules: false
      }
    }),
    webpackHotMiddleware(compiler, {
      log: () => {}
    })
  ];

  server.init(config);
};
serve.description = `Create a Browsersync instance for live development.`;

export default serve;

Make Webpack logs a bit less verbose.


function setup(){
  // Defining Initial Configuration
  var DIFF_INIT = 4;
  createCanvas(windowWidth - DIFF_INIT, windowHeight - DIFF_INIT);
}

function draw(){
  background(0);
}

var height = windowHeight;
var width = windowWidth;

function setup(){
  // Defining Initial Configuration
  var DIFF_INIT = 4;
  createCanvas(windowWidth - DIFF_INIT, windowHeight - DIFF_INIT);
}

function draw(){
  background(0);
  ship = new Ship();
  ship.render();
}

function Ship(){
  this.pos = createVector(width/2, height/2)
  this.r = 20; // Radius
  this.heading = 0; // Heading of the Ship
  // RENDER SHIP
  this.render = function(){
    noFill();
    stroke(255);
    translate(this.pos.x, this.pos.y);
    triangle(-this.r, this.r, this.r, this.r, 0, -this.r);
  }

}

Create ship structure. Render function


var FESTAPP_REALM = 'Basic realm="festapp-server"';

function validateBasicAuth(accounts, req, res, next) {
  if (req.headers.authorization && req.headers.authorization.search('Basic ') === 0) {
    if (accounts.indexOf(new Buffer(req.headers.authorization.split(' ')[1], 'base64').toString()) !== -1) {
      next();
    } else {
      res.header('WWW-Authenticate', FESTAPP_REALM);
      res.send('Wrong username or password', 401);
    }
  } else {
    res.header('WWW-Authenticate', FESTAPP_REALM);
    res.send(401);
  }
}

module.exports = {
  authAPI: function(accounts, apiVersion) {
    // Only allow GET, OPTIONS and HEAD-requests to /api-calls without
    // HTTP Basic authentication
    return function accessFilter(req, res, next) {
      var matchStar = new RegExp(apiVersion+'/events/\\w+/star.*').test(req.path);
      if (req.method === 'GET' || req.method === 'OPTIONS' || req.method === 'HEAD' || matchStar) {
	next();
      } else {
	validateBasicAuth(accounts, req, res, next);
      }
    };
  },

  authAdminUI: function(accounts) {
    return function(req, res, next) {
      validateBasicAuth(accounts, req, res, next);
    };
  }
};

var FESTAPP_REALM = 'Basic realm="conference-server"';

function validateBasicAuth(accounts, req, res, next) {
  if (req.headers.authorization && req.headers.authorization.search('Basic ') === 0) {
    if (accounts.indexOf(new Buffer(req.headers.authorization.split(' ')[1], 'base64').toString()) !== -1) {
      next();
    } else {
      res.header('WWW-Authenticate', FESTAPP_REALM);
      res.send('Wrong username or password', 401);
    }
  } else {
    res.header('WWW-Authenticate', FESTAPP_REALM);
    res.send(401);
  }
}

module.exports = {
  authAPI: function(accounts, apiVersion) {
    // Only allow GET, OPTIONS and HEAD-requests to /api-calls without
    // HTTP Basic authentication
    return function accessFilter(req, res, next) {
      var matchStar = new RegExp(apiVersion+'/events/\\w+/star.*').test(req.path);
      if (req.method === 'GET' || req.method === 'OPTIONS' || req.method === 'HEAD' || matchStar) {
	next();
      } else {
	validateBasicAuth(accounts, req, res, next);
      }
    };
  },

  authAdminUI: function(accounts) {
    return function(req, res, next) {
      validateBasicAuth(accounts, req, res, next);
    };
  }
};

Update basic auth realm from festapp-server to conference-server


module.exports = {
  __module: {
    provides: {
      use_scripts: {},
      use_stylesheets: {},
      register_assets_dir: {after: ['assetManager/bootstrap', 'assetManager/hadron', 'assetManager/hadron-theme-nodus']},
      register_views_dir: {after: ['assetManager/bootstrap', 'assetManager/hadron', 'assetManager/hadron-theme-nodus']}
    }
  },

  register_assets_dir: function() {
    return __dirname + "/../assets";
  },

  register_views_dir: function() {
    return __dirname + "/../views";
  },

  use_scripts: function() {
    return {};
  },

  use_stylesheets: function() {
    return {};
  }
};

module.exports = {
  __module: {
    provides: {
      "assetManager/use_scripts": {},
      "assetManager/use_stylesheets": {},
      "assetManager/register_assets_dir": {
        after: ['assetManager/bootstrap', 'assetManager/hadron', 'assetManager/hadron-theme-nodus']
      },
      "assetManager/register_views_dir": {
        after: ['assetManager/bootstrap', 'assetManager/hadron', 'assetManager/hadron-theme-nodus']
      }
    }
  },

  register_assets_dir: function() {
    return __dirname + "/../assets";
  },

  register_views_dir: function() {
    return __dirname + "/../views";
  },

  use_scripts: function() {
    return {};
  },

  use_stylesheets: function() {
    return {};
  }
};

Update services to use full namespace (Scatter 0.7)


"use strict";

module.exports = {
    loadUninitialized: "You must first call store.load",
    undefinedValue: "Value must not be undefined.",
    invalidNamespace: "namespace can not contain a hash character (#)",
    nonexistentKey: "Key does not exist",
    invalidKey: "Key must be a string."
};

"use strict";

module.exports = {
    loadUninitialized: "You must first call store.load or store.clear.",
    undefinedValue: "Value must not be undefined.",
    invalidNamespace: "namespace can not contain a hash character (#).",
    nonexistentKey: "Key does not exist.",
    invalidKey: "Key must be a string."
};

Clean up some error message.


'use strict';

var browserify = require('browserify');
var gulp = require('gulp');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var uglify = require('gulp-uglify');
//var sourcemaps = require('gulp-sourcemaps');
var gutil = require('gulp-util');

gulp.task('default', function() {
    // place code for your default task here
    console.log("default task (does nothing!)");
});

gulp.task('js', function () {
  // call from command line as 'gulp js'
  // set up the browserify instance on a task basis
  var b = browserify({
    entries: './stylist.js',
    //entries: './vg.data.stash.js',
    debug: true
  });

  return b.bundle()
    .pipe(source('stylist-bundle.js'))
    .pipe(buffer())
    /*
    .pipe(sourcemaps.init({loadMaps: true}))
        // Add transformation tasks to the pipeline here.
        .pipe(uglify())
        .on('error', gutil.log)
    .pipe(sourcemaps.write('./'))
    */
    .pipe(gulp.dest('./'));
});

'use strict';

var browserify = require('browserify');
var gulp = require('gulp');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var uglify = require('gulp-uglify');
//var sourcemaps = require('gulp-sourcemaps');
var gutil = require('gulp-util');

gulp.task('default', function() {
    // place code for your default task here
    console.log("default task (does nothing!)");
});

gulp.task('js', function () {
  // call from command line as 'gulp js'
  // set up the browserify instance on a task basis
  var b = browserify({
    entries: './stylist.js',
    //entries: './vg.data.stash.js',
    debug: true  // enables sourcemaps
  });

  return b.bundle()
    .pipe(source('stylist-bundle.js'))
    .pipe(buffer())
    /*
    .pipe(sourcemaps.init({loadMaps: true}))
        // Add transformation tasks to the pipeline here.
        .pipe(uglify())
        .on('error', gutil.log)
    .pipe(sourcemaps.write('./'))
    */
    .pipe(gulp.dest('./'));
});

Clarify use of 'debug' option


/*
 * config.js: Configuration information for your Loggly account.
 *            This information is only used for require('loggly')./\.+/ methods
 *
 * (C) 2010 Nodejitsu Inc.
 * MIT LICENSE
 *
 */

//
// function createConfig (defaults)
//   Creates a new instance of the configuration
//   object based on default values
//
exports.createConfig = function (defaults) {
  return new Config(defaults);
};

//
// Config (defaults)
//   Constructor for the Config object
//
var Config = exports.Config = function (defaults) {
  if (!defaults.subdomain) {
    throw new Error('Subdomain is required to create an instance of Config');
  }

  this.subdomain = defaults.subdomain;
  this.json = defaults.json || null;
  this.auth = defaults.auth || null;
};

Config.prototype = {
  get subdomain () {
   return this._subdomain;
  },

  set subdomain (value) {
    this._subdomain = value;
  },

  get logglyUrl () {
    return 'https://' + [this._subdomain, 'loggly', 'com'].join('.') + '/api';
  },

  get inputUrl () {
    return 'https://logs.loggly.com/inputs/';
  }
};

Replace Unicode space (U+00A0) with ASCII space


var ResponseCache = function() {
	this.cache = {};
};

ResponseCache.prototype.buildCacheKey = function(request) {
    return request.files.join(',') + (request.minify ? '|min' : '');
};

ResponseCache.prototype.get = function(request) {
    var cacheKey = this.buildCacheKey(request);
    return this.cache[cacheKey];
};

ResponseCache.prototype.put = function(request, response) {
    var self = this,
        cacheKey = this.buildCacheKey(request);
    this.cache[cacheKey] = response;

    /*
    for (i = 0; i < request.fileDataList.length; i++) {
        if (!self.cacheFileDependencies[request.fileDataList[i].filePath]) {
            self.cacheFileDependencies[request.fileDataList[i].filePath] = [];
            (function(filePath) {
                fs.watchFile(filePath, function(curr, prev) {
                    var cacheKey = self.cacheFileDependencies[filePath];
                    //delete self.cache[cacheKey];
                });
            })(request.fileDataList[i].filePath);
        }
        self.cacheFileDependencies[request.fileDataList[i].filePath].push(cacheKey);
    }
    */
};

module.exports.ResponseCache = ResponseCache;

var ResponseCache = function() {
	this.cache = {};
};

ResponseCache.prototype.buildCacheKey = function(request) {
	var i, filePaths = [];
	for (i = 0; i < request.files.length; i++) {
		filePaths.push(request.files[i].path);
	}
    return filePaths.sort().join(',') + (request.minify ? '|min' : '');
};

ResponseCache.prototype.get = function(request) {
    var cacheKey = this.buildCacheKey(request);
    return this.cache[cacheKey];
};

ResponseCache.prototype.put = function(request, response) {
    var self = this,
        cacheKey = this.buildCacheKey(request);
    this.cache[cacheKey] = response;

    /*
    for (i = 0; i < request.fileDataList.length; i++) {
        if (!self.cacheFileDependencies[request.fileDataList[i].filePath]) {
            self.cacheFileDependencies[request.fileDataList[i].filePath] = [];
            (function(filePath) {
                fs.watchFile(filePath, function(curr, prev) {
                    var cacheKey = self.cacheFileDependencies[filePath];
                    //delete self.cache[cacheKey];
                });
            })(request.fileDataList[i].filePath);
        }
        self.cacheFileDependencies[request.fileDataList[i].filePath].push(cacheKey);
    }
    */
};

module.exports.ResponseCache = ResponseCache;

Fix bug in buildCacheKey function causing caching to miss every time.


// Copyright 2015, EMC, Inc.

'use strict';

module.exports = {
    friendlyName: 'Bootstrap Ubuntu',
    injectableName: 'Task.Linux.Bootstrap.Ubuntu',
    implementsTask: 'Task.Base.Linux.Bootstrap',
    options: {
        kernelFile: 'vmlinuz-3.13.0-32-generic',
        initrdFile: 'initrd.img-3.13.0-32-generic',
        kernelUri: '{{ api.server }}/common/{{ options.kernelFile }}',
        initrdUri: '{{ api.server }}/common/{{ options.initrdFile }}',
        basefs: 'common/base.trusty.3.13.0-32-generic.squashfs.img',
        overlayfs: 'common/discovery.overlay.cpio.gz',
        profile: 'linux.ipxe',
        comport: 'ttyS0'
    },
    properties: {
        os: {
            linux: {
                distribution: 'ubuntu',
                release: 'trusty',
                kernel: '3.13.0-32-generic'
            }
        }
    }
};

// Copyright 2015, EMC, Inc.

'use strict';

module.exports = {
    friendlyName: 'Bootstrap Ubuntu',
    injectableName: 'Task.Linux.Bootstrap.Ubuntu',
    implementsTask: 'Task.Base.Linux.Bootstrap',
    options: {
        kernelFile: 'vmlinuz-3.16.0-25-generic',
        initrdFile: 'initrd.img-3.16.0-25-generic',
        kernelUri: '{{ api.server }}/common/{{ options.kernelFile }}',
        initrdUri: '{{ api.server }}/common/{{ options.initrdFile }}',
        basefs: 'common/base.trusty.3.16.0-25-generic.squashfs.img',
        overlayfs: 'common/discovery.overlay.cpio.gz',
        profile: 'linux.ipxe',
        comport: 'ttyS0'
    },
    properties: {
        os: {
            linux: {
                distribution: 'ubuntu',
                release: 'trusty',
                kernel: '3.16.0-25-generic'
            }
        }
    }
};

Update kernel version of ubuntu microkernel to 3.16.0-25-generic.


// BetterDiscord pls.
try {
    window.localStorage = bdPluginStorage;
} catch (e) {
    // Non-BD localStorage shim
    if(window.localStorage === undefined) {
        var iframe = document.createElement('iframe');
        iframe.style.display = 'none';
        document.body.appendChild(iframe);
        window.localStorage = iframe.contentWindow.localStorage;
    }
}

require('./updates/updates.js');
//require('./settings/settings.js');
require('./core/core.js');
require('./search/search.js');

// BetterDiscord pls.
try {
    window.localStorage = bdPluginStorage;
} catch (e) {
    // Non-BD localStorage shim
    if(window.localStorage === undefined) {
        var iframe = document.createElement('iframe');
        iframe.style.display = 'none';
        document.body.appendChild(iframe);
        window.localStorage = iframe.contentWindow.localStorage;
    }
}

// require('./updates/updates.js');
//require('./settings/settings.js');
require('./core/core.js');
require('./search/search.js');

Remove update notifications, broken due to new CSP header


'use strict'
const tap = require('tap')
const nock = require('nock')
const Bot = require('../')

tap.test('bot.getProfile() - successful request', (t) => {
  let bot = new Bot({
    token: 'foo'
  })

  let response = {
    first_name: 'Cool',
    last_name: 'Kid',
    profile_pic: 'url'
  }

  nock('https://graph.facebook.com').get('/v2.6/1').query({
    fields: 'first_name,last_name,profile_pic',
    access_token: 'foo'
  }).reply(200, response)

  bot.getProfile(1, (err, profile) => {
    t.error(err, 'response should not be error')
    t.deepEquals(profile, response, 'response is correct')
    t.end()
  })
})

'use strict'
const tap = require('tap')
const nock = require('nock')
const Bot = require('../')

tap.test('bot.getProfile() - successful request', (t) => {
  let bot = new Bot({
    token: 'foo'
  })

  let response = {
    first_name: 'Cool',
    last_name: 'Kid',
    profile_pic: 'url',
    locale: 'en',
    timezone: 'PST',
    gender: 'M'
  }

  nock('https://graph.facebook.com').get('/v2.6/1').query({
    fields: 'first_name,last_name,profile_pic',
    access_token: 'foo'
  }).reply(200, response)

  bot.getProfile(1, (err, profile) => {
    t.error(err, 'response should not be error')
    t.deepEquals(profile, response, 'response is correct')
    t.end()
  })
})

Update test for getProfile with new profile fields


enyo.kind({
	name: "CurrentLocation",
	kind: "Component",
	events: {
		onSuccess: "",
		onFailure: ""
	},
	destroy: function() {
		this.stopTracking();
		this.inherited(arguments);
	},
	stopTracking: function() {
		if (this._watchId) {
			navigator.geolocation.clearWatch(this._watchId);
		}
	},
	go: function() {
		this._watchId = navigator.geolocation.watchPosition(
			enyo.bind(this, "doSuccess"),
			enyo.bind(this, "doFailure"),
			{maximumAge: 600, timeout: 10000});
	}
});

enyo.kind({
	name: "CurrentLocation",
	kind: "Component",
	events: {
		onSuccess: "",
		onFailure: "doFailure"
    },
	doFailure: function(error) {
        console.log("Geolocation error #" + error.code + ": " + error.message);
    },
    destroy: function() {
		this.stopTracking();
		this.inherited(arguments);
	},
	stopTracking: function() {
		if (this._watchId) {
			navigator.geolocation.clearWatch(this._watchId);
		}
	},
	go: function() {
        if(!!navigator.geolocation) {
            this._watchId = navigator.geolocation.watchPosition(
                enyo.bind(this, "doSuccess"),
                enyo.bind(this, "doFailure"),
                {maximumAge: 600, timeout: 10000});
        } else {
            console.log("Geolocation error: Browser does not support navigator.geolocation!");
        }
    }
});

Add logging for geolocation failure cases


/*global Promise:true*/
'use strict';

var EventSource = global.EventSource || require('eventsource')
  , Promise = require('rsvp').Promise
  ;

var _source;

var Streamable = {
  listen: {
    value: function () {
      return new Promise(function (resolve, reject) {
        var source
          , url = this.url;

        if (this.token) {
          url = url + '?x-auth-token=' + this.token.token;
        }

        try {
          source = _source = this.cacheStream && _source || new EventSource(url);
        } catch (e) {
          return reject(e);
        }
        return resolve(source);
      }.bind(this));
    }
  }
};

module.exports = Streamable;

/*global Promise:true*/
'use strict';

var EventSource = global.EventSource || require('eventsource')
  , Promise = require('rsvp').Promise
  ;

var _source;

var Streamable = {
  listen: {
    value: function () {
      return new Promise(function (resolve, reject) {
        var source
          , url = this.url;

        if (this.token && this.token.token) {
          url = url + '?x-auth-token=' + this.token.token;
        }

        try {
          source = _source = this.cacheStream && _source || new EventSource(url);
        } catch (e) {
          return reject(e);
        }
        return resolve(source);
      }.bind(this));
    }
  }
};

module.exports = Streamable;

Fix stream sending undefined as an x-auth-token


import React, { Component, PropTypes } from 'react';
import {connect} from 'react-redux';
import {Link} from 'react-router';

@connect(state => ({ routerState: state.router }))
export const App = class App extends Component {
  static propTypes = {
    children: PropTypes.node
  }

  render() {
    const links = [
      '/',
      '/parent?foo=bar',
      '/parent/child?bar=baz',
      '/parent/child/123?baz=foo'
    ].map((l, i) =>

        <Link to={l}>{l}</Link>

    );

    return (

App Container

        {links}
        {this.props.children}

    );
  }
};

export const Parent = class Parent extends Component {
  static propTypes = {
    children: PropTypes.node
  }

  render() {
    return (

Parent

        {this.props.children}

    );
  }
};

export const Child = class Child extends Component {
  render() {
    return (

Child

    );
  }
};

import React, { Component, PropTypes } from 'react';
import {connect} from 'react-redux';
import {Link} from 'react-router';

@connect(state => ({ routerState: state.router }))
export const App = class App extends Component {
  static propTypes = {
    children: PropTypes.node
  }

  render() {
    const links = [
      '/',
      '/parent?foo=bar',
      '/parent/child?bar=baz',
      '/parent/child/123?baz=foo'
    ].map((l, i) =>

        <Link to={l}>{l}</Link>

    );

    return (

App Container

        {links}
        {this.props.children}

    );
  }
};

export const Parent = class Parent extends Component {
  static propTypes = {
    children: PropTypes.node
  }

  render() {
    return (

Parent

        {this.props.children}

    );
  }
};

export const Child = class Child extends Component {
  render() {
    const { params: { id }} = this.props;

    return (

Child

          {id &&
{id}
}

    );
  }
};

Update server example to match client example


/**
 * Module dependencies.
 */

var _ = require('lodash');
var async = require('async');

/**
 * exposeGlobals()
 *
 * Expose certain global variables
 * (if config says so)
 *
 * @api private
 */

module.exports = function exposeGlobals() {
  var sails = this;

  sails.log.verbose('Exposing global variables... (you can disable this by modifying the properties in `sails.config.globals`)');

  // Globals explicitly disabled
  if (sails.config.globals === false) {
    return;
  }

  sails.config.globals = sails.config.globals || {};

  // Provide global access (if allowed in config)
  if (sails.config.globals._) {
    global['_'] = _;
  }
  if (sails.config.globals.async !== false) {
    global['async'] = async;
  }
  if (sails.config.globals.sails !== false) {
    global['sails'] = sails;
  }

  // `services` hook takes care of globalizing services (if enabled)

  // `orm` hook takes care of globalizing models and adapters (if enabled)

};

/**
 * Module dependencies.
 */

var _ = require('lodash');
var async = require('async');

/**
 * exposeGlobals()
 *
 * Expose certain global variables
 * (if config says so)
 *
 * @api private
 */

module.exports = function exposeGlobals() {
  var sails = this;

  sails.log.verbose('Exposing global variables... (you can disable this by modifying the properties in `sails.config.globals`)');

  // Globals explicitly disabled
  if (sails.config.globals === false) {
    return;
  }

  sails.config.globals = sails.config.globals || {};

  // Provide global access (if allowed in config)
  if (sails.config.globals._ !== false) {
    global['_'] = _;
  }
  if (sails.config.globals.async !== false) {
    global['async'] = async;
  }
  if (sails.config.globals.sails !== false) {
    global['sails'] = sails;
  }

  // `services` hook takes care of globalizing services (if enabled)

  // `orm` hook takes care of globalizing models and adapters (if enabled)

};

Fix expose Lodash global by default.


(function($) {
    $(document).ready(function() {
        $('.datepicker').datepicker();
    });
})(jQuery);

(function($) {
    $(document).ready(function() {
        $('.datepicker').datepicker({
            dateFormat: 'yy-mm-dd'
                                    });
    });
})(jQuery);

Use correct date format for datepicker


// https://gist.github.com/adamalbrecht/7226278

// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
angular.module('ngDebounce', []).factory('$debounce', function($timeout, $q) {
  return function(func, wait, immediate) {
    var timeout;
    var deferred = $q.defer();
    return function() {
      var context = this, args = arguments;
      var later = function() {
        timeout = null;
        if(!immediate) {
          deferred.resolve(func.apply(context, args));
          deferred = $q.defer();
        }
      };
      var callNow = immediate && !timeout;
      if ( timeout ) {
        $timeout.cancel(timeout);
      }
      timeout = $timeout(later, wait);
      if (callNow) {
        deferred.resolve(func.apply(context,args));
        deferred = $q.defer();
      }
      return deferred.promise;
    };
  };
});/**
 * Created by jsansbury on 1/24/14.
 */

Remove junk from bottom of file.


/* DistrictBuilder Javascript Utilities */

/* DistrictBuilder Javascript Utilities used in multiple
 * Javascript files in the DistrictBuilder codebase.
 *
 * Instantiate db_util object to protect global namespace
 */

var db_util = {};

/* Checks whether a string starts with a substring
 *
 * Parameters:
 *   s -- string to compare
 *   substring -- characters to check if s starts with
 */

db_util.startsWith = function(s, substring) {
    return (s.indexOf(substring) == 0);
}

/* Checks whether a string contains a given substring
 *
 * Parameters:
 *   s -- string to compare
 *   substring -- characters to check if s contains
 */

db_util.contains = function(s, substring) {
    return (s.indexOf(substring) != -1);
};

/* DistrictBuilder Javascript Utilities */

/* DistrictBuilder Javascript Utilities used in multiple
 * Javascript files in the DistrictBuilder codebase.
 *
 * Instantiate DB object to protect global namespace
 */

var DB = {};
DB.util = {};

/* Checks whether a string starts with a substring
 *
 * Parameters:
 *   s -- string to compare
 *   substring -- characters to check if s starts with
 */

DB.util.startsWith = function(s, substring) {
    return (s.indexOf(substring) == 0);
}

/* Checks whether a string contains a given substring
 *
 * Parameters:
 *   s -- string to compare
 *   substring -- characters to check if s contains
 */

DB.util.contains = function(s, substring) {
    return (s.indexOf(substring) != -1);
};

Rename db_util javascript object to DB.util


import React from 'react';
import classnames from 'classnames';

function Thumbnail({ src, label, circle }) {
  const className = classnames('ui-thumbnail', {
    'ui-thumbnail-circle': circle,
  });

  return (

      {label &&
{label}
}

  );
}

Thumbnail.propTypes = {
  src: React.PropTypes.string.isRequired,
  label: React.PropTypes.string,
  circle: React.PropTypes.bool,
};

export default Thumbnail;

import React from 'react';
import classnames from 'classnames';

function Thumbnail({ src, label, circle, width }) {
  const className = classnames('ui-thumbnail', {
    'ui-thumbnail-circle': circle,
  });

  const style = {};

  if (width) {
    style.width = `${width}px`;
  }

  return (

      {label &&
{label}
}

  );
}

Thumbnail.propTypes = {
  src: React.PropTypes.string.isRequired,
  label: React.PropTypes.string,
  circle: React.PropTypes.bool,
  width: React.PropTypes.number,
};

export default Thumbnail;

Add a width prop to thumbnails


/**
 * GET home page
 */

module.exports = function (app, options) {
    "use strict";

    var underscore = require('underscore');
    var userGroupsFetcher = options.userGroupsFetcher;
    var restProxy = options.restProxy;

    /**
     * GET list of routes (index page)
     */
    var getRoutesIndex = function (req, res) {

        var userGroups = req.userGroups || [];
        var userDefaultRouteFetchQuery = (!!req.signedCookies) ? req.signedCookies['userDefaultRouteFetchQuery_' + req.session.userId] : undefined;

        var onCompleteOmraderRequest = function (data) {
            var areas = data.documents;
            var renderOptions = {
                title: 'Mine turer',
                areas: JSON.stringify(areas),
                userData: JSON.stringify(req.session.user),
                userGroups: JSON.stringify(userGroups),
                userDefaultRouteFetchQuery: JSON.stringify(userDefaultRouteFetchQuery),
                authType: req.session.authType,
                itemType: 'tur'
            };

            res.render('routes/index', renderOptions);
        };

        restProxy.makeApiRequest('/områder/?limit=100&fields=navn,_id&sort=navn', req, undefined, onCompleteOmraderRequest);
    };

    app.get('/turer', userGroupsFetcher);
    app.get('/turer', getRoutesIndex);

};

/**
 * GET home page
 */

module.exports = function (app, options) {
    "use strict";

    var underscore = require('underscore');
    var userGroupsFetcher = options.userGroupsFetcher;
    var restProxy = options.restProxy;

    /**
     * GET list of routes (index page)
     */
    var getRoutesIndex = function (req, res) {

        var userGroups = req.userGroups || [];
        var userDefaultRouteFetchQuery = (!!req.signedCookies) ? req.signedCookies['userDefaultRouteFetchQuery_' + req.session.userId] : undefined;

        var onCompleteOmraderRequest = function (data) {
            var areas = data.documents;
            var renderOptions = {
                title: 'Mine turer',
                areas: JSON.stringify(areas),
                userData: JSON.stringify(req.session.user),
                userGroups: JSON.stringify(userGroups),
                userDefaultRouteFetchQuery: JSON.stringify(userDefaultRouteFetchQuery),
                authType: req.session.authType,
                itemType: 'tur'
            };

            res.render('routes/index', renderOptions);
        };

        restProxy.makeApiRequest('/områder/?limit=100&fields=navn,_id&sort=navn&tilbyder=DNT&status=Offentlig', req, undefined, onCompleteOmraderRequest);
    };

    app.get('/turer', userGroupsFetcher);
    app.get('/turer', getRoutesIndex);

};

Adjust områder request query params to only get published and DNT official områder


'use strict';
var exec = require('child_process').exec;

exports.buildProduction = function (buildTool, err) {
    var buildCmd = 'mvn package -Pprod -DskipTests=true';

    if (buildTool === 'gradle') {
        buildCmd = './gradlew -Pprod bootRepackage -x test';
    }

    var child = exec(buildCmd, err);

    return child.stdout;
};

'use strict';
var exec = require('child_process').exec;

exports.buildProduction = function (buildTool, err) {
    var buildCmd = 'mvn package -Pprod -DskipTests=true -B';

    if (buildTool === 'gradle') {
        buildCmd = './gradlew -Pprod bootRepackage -x test';
    }

    var child = exec(buildCmd, err);

    return child.stdout;
};

Fix 'stdout maxBuffer exceeded' error when deploy to AWS


import moment from "moment";

export const dateFormat = 'MMM D, YYYY';
export const datetimeFormat = 'MMM D, YYYY HH:MM';

function getDateParamsFromString(dateString) {
  return {
    year: dateString.substring(0, 4),
    month: dateString.substring(4, 6),
    date: dateString.substring(6, 8),
  };
}

export function getDateForMemoriesRequest(dateString) {
  const { year, month, date } = getDateParamsFromString(dateString);
  return new Date(year, Number(month) - 1, Number(date) + 1);
}

export function formatDateFromShortString(dateString) {
  const { year, month, date } = getDateParamsFromString(dateString);
  const fullDate = new Date(year, Number(month) - 1, date);
  return moment(fullDate).format(dateFormat);
}

import moment from "moment";

export const dateFormat = 'MMM D, YYYY';
export const datetimeFormat = 'MMM D, YYYY HH:mm';

function getDateParamsFromString(dateString) {
  return {
    year: dateString.substring(0, 4),
    month: dateString.substring(4, 6),
    date: dateString.substring(6, 8),
  };
}

export function getDateForMemoriesRequest(dateString) {
  const { year, month, date } = getDateParamsFromString(dateString);
  return new Date(year, Number(month) - 1, Number(date) + 1);
}

export function formatDateFromShortString(dateString) {
  const { year, month, date } = getDateParamsFromString(dateString);
  const fullDate = new Date(year, Number(month) - 1, date);
  return moment(fullDate).format(dateFormat);
}

Fix datetime format string (use minutes instead of months)


class DbConst {
    // no support for static contants, declare then after the class definition
}
DbConst.DB_NAME = "FormHistoryControl";
DbConst.DB_VERSION = 1;
DbConst.DB_STORE_TEXT = 'text_history';
DbConst.DB_STORE_ELEM = 'elem_history';

DbConst.DB_TEXT_IDX_FIELD = 'by_fieldkey';
DbConst.DB_TEXT_IDX_NAME = 'by_name';
DbConst.DB_TEXT_IDX_LAST = 'by_last';
DbConst.DB_TEXT_IDX_HOST = 'by_host';

DbConst.DB_ELEM_IDX_FIELD = 'by_fieldkey';
DbConst.DB_ELEM_IDX_SAVED = 'by_saved';

class DbConst {
    // no support for static contants, declare then after the class definition
}
DbConst.DB_NAME = "FormHistoryControl";
DbConst.DB_VERSION = 1;
DbConst.DB_STORE_TEXT = 'text_history';
DbConst.DB_STORE_ELEM = 'elem_history';

DbConst.DB_TEXT_IDX_FIELD = 'by_fieldkey';
DbConst.DB_TEXT_IDX_NAME = 'by_name';
DbConst.DB_TEXT_IDX_LAST = 'by_last';
DbConst.DB_TEXT_IDX_HOST = 'by_host';
DbConst.DB_TEXT_IDX_HOST_NAME = "by_host_plus_name";

DbConst.DB_ELEM_IDX_FIELD = 'by_fieldkey';
DbConst.DB_ELEM_IDX_SAVED = 'by_saved';

Enable storing multiple versions for the same multiline field


export async function up(knex) {
  await knex.schema.table('users', (table) => {
    table.jsonb('preferences').defaultTo('{}').notNullable();
  });
  // Migrate from the 'frontend_preferences'
  await knex.raw(`
    update users set preferences = jsonb_set(
      preferences,
      '{hideCommentsOfTypes}',
      coalesce((frontend_preferences::jsonb) #> '{net.freefeed,comments,hiddenTypes}', '[]'),
      true
    )
  `);
}

export async function down(knex) {
  await knex.schema.table('users', (table) => {
    table.dropColumn('preferences');
  });
}

export async function up(knex) {
  await knex.schema.table('users', (table) => {
    table.jsonb('preferences').defaultTo('{}').notNullable();
  });
  // Migrate from the 'frontend_preferences'
  await knex.raw(`
    update users set
      preferences = jsonb_set(
          preferences, '{hideCommentsOfTypes}',
          coalesce(frontend_preferences::jsonb #> '{net.freefeed,comments,hiddenTypes}', '[]'),
          true
        ),
      frontend_preferences = (
        frontend_preferences::jsonb #- '{net.freefeed,comments,hiddenTypes}'
      )::text
  `);
}

export async function down(knex) {
  // Migrate back to the 'frontend_preferences'
  await knex.raw(`
    update users set
      frontend_preferences = jsonb_set(
          frontend_preferences::jsonb, '{net.freefeed,comments,hiddenTypes}',
          coalesce(preferences #> '{hideCommentsOfTypes}', '[]'),
          true
        )::text
  `);
  await knex.schema.table('users', (table) => {
    table.dropColumn('preferences');
  });
}

Move data from frontend_prefs to prefs and back during migration


Template.newActivity.created = function () {
  this.subscribe('allCurrentResidents');
  this.subscribe('allHomes');
  this.subscribe('allActivityTypes');
  this.subscribe('allRolesExceptAdmin');
};

Template.newActivity.helpers({
  select2Options () {
    return {
      multiple: true
    }
  }
});

Template.newActivity.created = function () {
  this.subscribe('allCurrentResidents');
  this.subscribe('allHomes');
  this.subscribe('allActivityTypes');
  this.subscribe('allRolesExceptAdmin');
};

Template.autoForm.onRendered(function () {
  const instance = this;

  // Make sure the new activity form is being rendered
  if (instance.data.id === "newActivityForm") {
    // Get the user agent type
    var deviceAgent = navigator.userAgent.toLowerCase();

    // Check if the device is iOS
    var iOS = deviceAgent.match(/(iPad|iPhone|iPod)/i);

    // If the device is not iOS, render the chosen select widget
    if (!iOS) {
      // Get a reference to the resident select field
      const residentSelect = $('[name=residentIds]');

      // Activate the improved multi-select widget
      residentSelect.chosen();
    };
  }
});

Remove select2Options; render Chosen when AutoForm renders


'use strict';
var cp = require('child_process');
var child = cp.fork(__dirname + '/child.js');
var passport = require('passport');
var DropboxOAuth2Strategy = require('passport-dropbox-oauth2').Strategy;
var active = false;

module.exports = function (options) {
  child.send({
    options: options.dropbox
  });
  return {
    initialize: function () {
      active = true;
      passport.use(new DropboxOAuth2Strategy({
        clientID: options.dropbox.id,
        clientSecret: options.dropbox.secret,
        callbackURL: 'https://jsbin.dev/auth/dropbox/callback'
      }, function (accessToken, refreshToken, profile, done) {
        done(null, profile);
      }
    ));
    }
  };
};

module.exports.saveBin = function (bin, user) {
  if (!active) {
    return;
  }
  child.send({
    bin: bin,
    user: user
  });
};

'use strict';
var cp = require('child_process');
var child = cp.fork(__dirname + '/child.js');
var passport = require('passport');
var DropboxOAuth2Strategy = require('passport-dropbox-oauth2').Strategy;
var active = false;

module.exports = function (options) {
  child.send({
    options: options.dropbox
  });
  return {
    initialize: function () {
      active = true;
      passport.use(new DropboxOAuth2Strategy({
        clientID: options.dropbox.id,
        clientSecret: options.dropbox.secret,
        callbackURL: 'https://jsbin.dev/auth/dropbox/callback'
      }, function (accessToken, refreshToken, profile, done) {
        done(null, profile);
      }
    ));
    }
  };
};

module.exports.saveBin = function (file, fileName, user) {
  if (!active) {
    return;
  }
  child.send({
    file: file,
    fileName: fileName,
    user: user
  });
};

Update dropbox module api to take file, fileNAme & user consider taking oauth_token rather than user?


exports.conf = {
  nick: 'treslek',
  host: 'irc.freenode.net',
  ircOptions: {
    port: 6667,
    channels: ['##bullpeen'],
    userName: 'treslek',
    realName: 'treslek',
    autoConnect: false,
    floodProtection: true,
    floodProtectionDelay: 100
  },
  ignored: ['doslek'],
  redis: {
    host: 'localhost',
    port: '6379',
    prefix: 'treslek'
  },
  admins: ['jirwin'],
  plugins_dir: "/home/jirwin/projects/treslek/plugins/"
}

exports.conf = {
  nick: 'treslek',
  host: 'irc.freenode.net',
  ircOptions: {
    port: 6667,
    channels: ['##bullpeen'],
    userName: 'treslek',
    realName: 'treslek',
    autoConnect: false,
    floodProtection: true,
    floodProtectionDelay: 100
  },
  ignored: ['doslek'],
  redis: {
    host: '127.0.0.1',
    port: '6379',
    prefix: 'treslek'
  },
  admins: ['jirwin'],
  plugins_dir: "/home/jirwin/projects/treslek/plugins/"
}

Use 127.0.0.1 for redis instead of localhost.


import { getWrapper } from '../shared';
import NavigationBar from '../../src/components/NavigationBar';
import SettingsPanel from '../../src/components/SettingsPanel';
import CallingSettings from '../../src/components/CallingSettingsPanel';
import LinkLine from '../../src/components/LinkLine';
import Button from '../../src/components/Button';

let wrapper = null;
let panel = null;
let callingSettings = null;
beforeEach(async () => {
  jasmine.DEFAULT_TIMEOUT_INTERVAL = 32000;
  wrapper = await getWrapper();
  const navigationBar = wrapper.find(NavigationBar).first();
  await navigationBar.props().goTo('/settings');
  panel = wrapper.find(SettingsPanel).first();
  const callingLinkLine = panel.find(LinkLine).at(0);
  await callingLinkLine.props().onClick();
  callingSettings = wrapper.find(CallingSettings).first();
});

describe('calling settings', () => {
  test('initial state', async () => {
    expect(callingSettings.find('div.label').first().props().children).toEqual('Calling');
  });

  test('button state', () => {
    const saveButton = callingSettings.find(Button).first();
    expect(saveButton.props().disabled).toEqual(true);
  });
});

import { getWrapper } from '../shared';
import NavigationBar from '../../src/components/NavigationBar';
import SettingsPanel from '../../src/components/SettingsPanel';
import CallingSettings from '../../src/components/CallingSettingsPanel';
import LinkLine from '../../src/components/LinkLine';
import Button from '../../src/components/Button';

let wrapper = null;
let panel = null;
let callingSettings = null;
beforeEach(async () => {
  jasmine.DEFAULT_TIMEOUT_INTERVAL = 32000;
  wrapper = await getWrapper();
  const navigationBar = wrapper.find(NavigationBar).first();
  await navigationBar.props().goTo('/settings');
  panel = wrapper.find(SettingsPanel).first();
  const callingLinkLine = panel.find(LinkLine).at(0);
  await callingLinkLine.props().onClick();
  callingSettings = wrapper.find(CallingSettings).first();
});

describe('calling settings', () => {
  test('initial state', async () => {
    expect(callingSettings.find('div.label').first().props().children).toEqual('Calling');
  });

  test('button state', async () => {
    const saveButton = callingSettings.find(Button).first();
    expect(saveButton.props().disabled).toEqual(true);

    const items = callingSettings.find('.dropdownItem');
    const lastItem = items.at(items.length - 1);
    await lastItem.simulate('click');
    expect(saveButton.props().disabled).toEqual(false);
  });
});

Test calling settings button state


(function(){
    var app = angular.module('runnersNotes',[]);

    app.controller('NoteController',['$http',function($http){
        var rn = this;
        rn.success = false;
        rn.errors = [];
        rn.notes = [];
        rn.note = {};
        $http.get('http://localhost:8080/notes').success(function(data){
            	rn.notes = data.items;
        });

        this.addNote = function() {
            rn.note.created = new Date(rn.note.date).getTime();
            $http.post('http://localhost:8080/notes',rn.note).
                then(function(response){
                    //if (response.status === 201) {
                        rn.notes.push(rn.note);
                        rn.note = {};
                        rn.success = true;
                        rn.errors = [];
                        $("#addForm").collapse('hide');
                    //}
                }, function(response) {
                    rn.success = false;
                    for (var i=0; i < response.data.length; i++) {
                        rn.errors.push(response.data[i]);
                    }
                    console.log(response);
                });
        };
    }]);
})();

(function(){
    var app = angular.module('runnersNotes',[]);

    app.controller('NoteController',['$http',function($http){
        var rn = this;
        rn.success = false;
        rn.errors = [];
        rn.notes = [];
        rn.note = {};
        $http.get('http://localhost:8080/notes').success(function(data){
            	rn.notes = data.items;
        });

        this.clearAlerts = function() {
            rn.errors = [];
            rn.success = false;
        };

        this.addNote = function() {
            rn.note.created = new Date(rn.note.date).getTime();
            $http.post('http://localhost:8080/notes',rn.note).
                then(function(response){
                    rn.notes.push(rn.note);
                    rn.note = {};
                    rn.success = true;
                    rn.errors = [];
                    $("#addForm").collapse('hide');
                }, function(response) {
                    rn.success = false;
                    for (var i=0; i < response.data.length; i++) {
                        rn.errors.push(response.data[i]);
                    }
                });
        };
    }]);
})();

Remove alerts when clicking link or button


import React from 'react';
import Helmet from 'react-helmet';
import { prefixLink } from 'gatsby-helpers';

const BUILD_TIME = new Date().getTime();

module.exports = React.createClass({
  displayName: 'HTML',
  propTypes: {
    body: React.PropTypes.string,
  },
  render() {
    const { body } = this.props;
    const { title } = Helmet.rewind();
    const font = <link href="https://fonts.googleapis.com/css?family=Roboto:400,400italic,500,700&subset=latin,cyrillic" rel="stylesheet" type="text/css" />;
    let css;
    if (process.env.NODE_ENV === 'production') {
      // eslint-disable-next-line import/no-webpack-loader-syntax
      css = <style dangerouslySetInnerHTML={{ __html: require('!raw!./public/styles.css') }} />;
    }

    return (
      <html lang="en">
        <head>
          <meta charSet="utf-8" />
          <meta httpEquiv="X-UA-Compatible" content="IE=edge" />
          <meta name="viewport" content="width=device-width, initial-scale=1.0 maximum-scale=5.0" />
          { title.toComponent() }
          { font }
          { css }
        </head>
        <body>
          <div id="react-mount" dangerouslySetInnerHTML={{ __html: this.props.body }} />
          <script src={prefixLink(`/bundle.js?t=${BUILD_TIME}`)} />
        </body>
      </html>
    );
  },
});

import React from 'react';
import Helmet from 'react-helmet';
import { prefixLink } from 'gatsby-helpers';

const BUILD_TIME = new Date().getTime();

module.exports = React.createClass({
  displayName: 'HTML',
  propTypes: {
    body: React.PropTypes.string,
  },
  render() {
    const { body } = this.props;
    const { title } = Helmet.rewind();
    const font = <link href="https://fonts.googleapis.com/css?family=Roboto:400,400italic,500,700&subset=latin,cyrillic" rel="stylesheet" type="text/css" />;
    let css;
    if (process.env.NODE_ENV === 'production') {
      // eslint-disable-next-line import/no-webpack-loader-syntax
      css = <link rel="stylesheet" type="text/css" href="/styles.css" />;
    }

    return (
      <html lang="zh">
        <head>
          <meta charSet="utf-8" />
          <meta httpEquiv="X-UA-Compatible" content="IE=edge" />
          <meta name="viewport" content="width=device-width, initial-scale=1.0 maximum-scale=5.0" />
          { title.toComponent() }
          { font }
          { css }
        </head>
        <body>
          <div id="react-mount" dangerouslySetInnerHTML={{ __html: this.props.body }} />
          <script src={prefixLink(`/bundle.js?t=${BUILD_TIME}`)} />
        </body>
      </html>
    );
  },
});

Change inline css to href


var app = require('app'); // Module controlling application life cyle.
var BrowserWindow = require('browser-window'); // Creating native browser windows.

// Report crashes to the server (local node process?).
require('crash-reporter').start();

// Keep a global reference to the main window; otherwise, when JavaScript garbage collects the instance,
// it will destroy the window referred to by the instance. :) (How difficult would that be to debug?)
var mainWindow = null;

// Quit when the user closes all windows.
app.on('window-all-closed', function() {
  // On OS X, it is common for applications and their menu bar to remain active until the user explicitly
  // closes the application using Cmd-Q.
  if (process.platform != 'darwin') {
    app.quit();
  }
})

// When electron has finished initialization and is ready to create browser windows, we call the following:
app.on('ready', function() {
  // Create the browser window.
  mainWindow = new BrowserWindow({width: 800, height: 600});

  // Load the index page for the app.
  mainWindow.loadUrl('file://' + __dirname + '/index.html');

  // Open the development tools. (What is the result of this call?)

  // When main window closed
  mainWindow.on('closed', function() {
    // Deference the window object. If you app supports multiple windows, you would typically store them
    // in an array. In this situation, you would delete the corresponding element of the array.
    mainWindow = null;
  })
})

var app = require('app'); // Module controlling application life cyle.
var BrowserWindow = require('browser-window'); // Creating native browser windows.

// Report crashes to the server (local node process?).
require('crash-reporter').start();

// Keep a global reference to the main window; otherwise, when JavaScript garbage collects the instance,
// it will destroy the window referred to by the instance. :) (How difficult would that be to debug?)
var mainWindow = null;

// Quit when the user closes all windows.
app.on('window-all-closed', function() {
  // On OS X, it is common for applications and their menu bar to remain active until the user explicitly
  // closes the application using Cmd-Q.
  if (process.platform != 'darwin') {
    app.quit();
  }
})

// When electron has finished initialization and is ready to create browser windows, we call the following:
app.on('ready', function() {
  // Create the browser window.
  mainWindow = new BrowserWindow({width: 800, height: 600});

  // Load the index page for the app.
  mainWindow.loadUrl('file://' + __dirname + '/index.html');

  // Open the development tools.
  mainWindow.openDevTools();

  // When main window closed
  mainWindow.on('closed', function() {
    // Deference the window object. If you app supports multiple windows, you would typically store them
    // in an array. In this situation, you would delete the corresponding element of the array.
    mainWindow = null;
  })
})

Add call to open development tools.


import { Mongo } from 'meteor/mongo';
import { SimpleSchema } from 'meteor/aldeed:simple-schema';

class AcadCohortCollection extends Mongo.Collection{
  insert(acadCohortData, callBack){
    const cohortDocument = acadCohortData;
    let result;
    //validate document
    return super.insert( cohortDocument, callBack);
  };

  update(selector, modifier){
    const result = super.update(selector, modifier);

    return result;
  };

  remove(selector){
    const result = super.remove(selector);
    return result;
  };
}

const acadCohortSchema ={
  cohortName:{
    type: String,
    regEx: /AY\s\d\d\d\d\/\d\d\d\d/
  },
  cohortFocusAreaID:{
    type: [String],
    optional: true
  }
}

export const AcademicCohort = new AcadCohortCollection("AcademicCohort");
AcademicCohort.attachSchema(acadCohortSchema);

import { Mongo } from 'meteor/mongo';
import { SimpleSchema } from 'meteor/aldeed:simple-schema';

class AcadCohortCollection extends Mongo.Collection{
  insert(acadCohortData, callBack){
    const cohortDocument = acadCohortData;
    let result;
    //validate document
    return super.insert( cohortDocument, callBack);
  };

  update(selector, modifier){
    const result = super.update(selector, modifier);

    return result;
  };

  remove(selector){
    const result = super.remove(selector);
    return result;
  };
}

const acadCohortSchema ={
  cohortName:{
    type: String,
    regEx: /AY\s\d\d\d\d\/\d\d\d\d/
  },
  cohortFocusAreaID:{
    type: [String],
    optional: true
  },
  cohortGradRequirementID: {
    type: [String],
    optional: true
  }
}

export const AcademicCohort = new AcadCohortCollection("AcademicCohort");
AcademicCohort.attachSchema(acadCohortSchema);

MODIFY academic cohort method and schema to include acad year


import fetch               from 'utils/fetch';

import { getSettings }     from 'models/settings';
import parseResponse       from 'utils/parseResponse';
import { updateHeaders }   from 'actions/headers';

export const SIGN_OUT = 'SIGN_OUT';
export const SIGN_OUT_COMPLETE = 'SIGN_OUT_COMPLETE';
export const SIGN_OUT_ERROR = 'SIGN_OUT_ERROR';

function signOutStart() {
  return { type: SIGN_OUT };
}

function signOutComplete() {
  return { type: SIGN_OUT_COMPLETE };
}

function signOutError(errors) {
  return { type: SIGN_OUT_ERROR, errors };
}

export function signOut() {
  return (dispatch, getState) => {
    const { backend } = getSettings(getState());

    dispatch(signOutStart());
    dispatch(updateHeaders());

    if (!backend.signOutPath) {
      dispatch(signOutComplete());

      return Promise.resolve();
    }

    return dispatch(fetch(backend.signOutPath, { method: 'delete' }))
      .then(parseResponse)
      .then(() => {
        dispatch(signOutComplete());

        return Promise.resolve();
      })
      .catch((er) => {
        dispatch(signOutError(er));

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

import fetch               from 'utils/fetch';

import { getSettings }     from 'models/settings';
import parseResponse       from 'utils/parseResponse';
import { updateHeaders }   from 'actions/headers';

export const SIGN_OUT = 'SIGN_OUT';
export const SIGN_OUT_COMPLETE = 'SIGN_OUT_COMPLETE';
export const SIGN_OUT_ERROR = 'SIGN_OUT_ERROR';

function signOutStart() {
  return { type: SIGN_OUT };
}

function signOutComplete() {
  return { type: SIGN_OUT_COMPLETE };
}

function signOutError(errors) {
  return { type: SIGN_OUT_ERROR, errors };
}

export function signOut() {
  return (dispatch, getState) => {
    const { backend } = getSettings(getState());

    dispatch(signOutStart());

    if (!backend.signOutPath) {
      dispatch(updateHeaders());
      dispatch(signOutComplete());

      return Promise.resolve();
    }

    return dispatch(fetch(backend.signOutPath, { method: 'delete' }))
      .then(parseResponse)
      .then(() => {
        dispatch(updateHeaders());
        dispatch(signOutComplete());

        return Promise.resolve();
      })
      .catch((er) => {
        dispatch(updateHeaders());
        dispatch(signOutError(er));

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

Update headers after sign_out response


var browserify = require('browserify');
var brfs = require('brfs');
var through = require('through');

require('browserify/lib/builtins').fs = 'create-webfs.js'; // TODO: find a better way to replace this module

var b = browserify();

// directly include rfile TODO: use brfs, but it replaces fs.readFileSync
b.transform(function(s) {
  var data = '';
  return through(write, end);

  function write(buf) { data += buf; }
  function end() {
    var fs = require('fs');
    var s = data;
    var includedFile = fs.readFileSync('node_modules//browserify/node_modules/umd/template.js'); // TODO: encoding? string?
    s = s.replace(/rfile\('.\/template.js'\)/, JSON.stringify(includedFile));
    this.queue(s);
    this.queue(null);
  };
}, { global: true });

b.add('./demo.js');
b.bundle().pipe(process.stdout);

var browserify = require('browserify');
var brfs = require('brfs');
var through = require('through');

require('browserify/lib/builtins').fs = 'create-webfs.js'; // TODO: find a better way to replace this module

var b = browserify();

// directly include rfile TODO: use brfs, but it replaces fs.readFileSync
b.transform(function(s) {
  var data = '';
  return through(write, end);

  function write(buf) { data += buf; }
  function end() {
    var fs = require('fs');
    var s = data;
    var includedFile = fs.readFileSync('node_modules//browserify/node_modules/umd/template.js', 'utf8');
    s = s.replace(/rfile\('.\/template.js'\)/, JSON.stringify(includedFile));
    this.queue(s);
    this.queue(null);
  };
}, { global: true });

b.add('./demo.js');
b.bundle().pipe(process.stdout);

Read template str as UTF-8 string


'use strict';

var ClocketteApp = require('./ClocketteApp');
var React = require('react');
var Router = require('react-router');
var Route = Router.Route;

var Routes = (
  <Route handler={ClocketteApp}>
    <Route name="/" handler={ClocketteApp}/>
  </Route>
);

Router.run(Routes, function (Handler) {
  React.render(<Handler/>, document.getElementById('ClocketteApp'));
});

'use strict';

var ClocketteApp = require('./ClocketteApp');
var React = require('react');
var Router = require('react-router');
var Route = Router.Route;

var Routes = (
  <Route handler={ClocketteApp}>
    <Route name="/" handler={ClocketteApp}/>
  </Route>
);

Router.run(Routes, function(Handler) {
  React.render(<Handler/>, document.getElementById('ClocketteApp'));
});

Make code more homogeneous. Follow styleguide like AirBnb's one


var $soloModal = null // Used when allowMultiple = false.

// The public API.
Modal = {

	allowMultiple: false,

	show: function(templateName, data, options){

		if($soloModal == null || this.allowMultiple){

			var parentNode = document.body

			var view = Blaze.renderWithData(Template[templateName], data, parentNode)

			var domRange = view._domrange // TODO: Don't violate against the public API.

			var $modal = domRange.$('.modal')

			$modal.on('shown.bs.modal', function(event){
				$modal.find('[autofocus]').focus()
			})

			$modal.on('hidden.bs.modal', function(event){
				Blaze.remove(view)
				$soloModal = null
			})

			$soloModal = $modal

			$modal.modal(options)

		}

	},

	hide: function(/* optional */ template){

		if(template instanceof Blaze.TemplateInstance){

			template.$('.modal').modal('hide')

		}else if($soloModal != null){

			$soloModal.modal('hide')

		}

	}

}

var $soloModal = null // Used when allowMultiple = false.

// The public API.
Modal = {

	allowMultiple: false,

	show: function(templateName, data, options){

		if($soloModal == null || this.allowMultiple){

			var parentNode = document.body

			var view = Blaze.renderWithData(Template[templateName], data, parentNode)

			var domRange = view._domrange // TODO: Don't violate against the public API.

			var $modal = domRange.$('.modal')

			$modal.on('shown.bs.modal', function(event){
				$modal.find('[autofocus]').focus()
			})

			$modal.on('hidden.bs.modal', function(event){
				Blaze.remove(view)
				$soloModal = null
			})

      if (options && options.events) {
        Object.keys(options.events).forEach(function(eventName) {
          $modal.on(eventName, options.events[eventName]);
        });
      }

			$soloModal = $modal

			$modal.modal(options)

		}

	},

	hide: function(/* optional */ template){

		if(template instanceof Blaze.TemplateInstance){

			template.$('.modal').modal('hide')

		}else if($soloModal != null){

			$soloModal.modal('hide')

		}

	}

}

Allow event handlers to be passed in


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

const ANIMATION_PERIOD = 500;

const AnimatedPageWrapper = styled.div`
  align-items: center;
  bottom: 0;
  display: flex;
  justify-content: center;
  left: 0;
  opacity: ${({ opacity }) => opacity};
  padding: 1em;
  position: fixed;
  right: 0;
  text-align: center;
  top: 0;
  transition: opacity ${ ANIMATION_PERIOD }ms ease-in-out;
  width: 100%;
`;

class AnimatedPage extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      show: false
    };
  }

  componentWillAppear(callback) {
    // Called on fresh load.
    this.animateIn(callback);
  }

  componentWillEnter(callback) {
    // Called during transition between routes.
    this.animateIn(callback);
  }

  componentWillLeave(callback) {
    this.animateOut(callback);
  }

  animateIn(callback) {
    setTimeout(() => {
      this.setState({ show: true }, callback);
    }, ANIMATION_PERIOD);
  }

  animateOut(callback) {
    this.setState({ show: false }, () => {
      setTimeout(callback, ANIMATION_PERIOD)
    });
  }

  render() {
    return (
      <AnimatedPageWrapper
        opacity={this.state.show ? 1 : 0}
      >
        {this.props.children}
      </AnimatedPageWrapper>
    );
  }
};

export default AnimatedPage;

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

const ANIMATION_PERIOD = 500;

const AnimatedPageWrapper = styled.div`
  align-items: center;
  bottom: 0;
  display: flex;
  justify-content: center;
  left: 0;
  opacity: ${({ opacity }) => opacity};
  padding: 2em;
  position: fixed;
  right: 0;
  text-align: center;
  top: 0;
  transition: opacity ${ ANIMATION_PERIOD }ms ease-in-out;
  width: 100%;
`;

class AnimatedPage extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      show: false
    };
  }

  componentWillAppear(callback) {
    // Called on fresh load.
    this.animateIn(callback);
  }

  componentWillEnter(callback) {
    // Called during transition between routes.
    this.animateIn(callback);
  }

  componentWillLeave(callback) {
    this.animateOut(callback);
  }

  animateIn(callback) {
    setTimeout(() => {
      this.setState({ show: true }, callback);
    }, ANIMATION_PERIOD);
  }

  animateOut(callback) {
    this.setState({ show: false }, () => {
      setTimeout(callback, ANIMATION_PERIOD)
    });
  }

  render() {
    return (
      <AnimatedPageWrapper
        opacity={this.state.show ? 1 : 0}
      >
        {this.props.children}
      </AnimatedPageWrapper>
    );
  }
};

export default AnimatedPage;

Adjust padding on animated page wrapper