/*jshint strict: true, esnext: true, node: true*/
"use strict";
const Wreck = require("wreck");
const qs = require("querystring");

class FBMessenger {
    constructor(token) {
        this._fbBase = "https://graph.facebook.com/v2.6/me/";
        this._token = token;
        this._q = qs.stringify({access_token: token});
        this._wreck = Wreck.defaults({
            baseUrl: this._fbBase
        });
    }

    sendMessage(userid, msg) {
        let payload = "";
        payload = JSON.stringify({
            recipient: { id: userid },
            message: msg
        });
        return new Promise(function(resolve, reject) {
            this._wreck.request(
                "POST",
                `/messages?${this._q}`,
                {
                    payload: payload
                },
                (err, response) => {
                    if(err) {
                        return reject(err);
                    }
                    if(response.body.error) {
                        return reject(response.body.error);
                    }
                    return resolve(response.body);
                }
            );
        });
    }
}

module.exports = FBMessenger;

/*jshint strict: true, esnext: true, node: true*/
"use strict";
const Wreck = require("wreck");
const qs = require("querystring");

class FBMessenger {
    constructor(token) {
        this._fbBase = "https://graph.facebook.com/v2.6/";
        this._token = token;
        this._q = qs.stringify({access_token: token});
        this._wreck = Wreck.defaults({
            baseUrl: this._fbBase
        });
    }

    sendMessage(userid, msg) {
        let payload = "";
        payload = JSON.stringify({
            recipient: { id: userid },
            message: msg
        });
        return this._makeRequest(`/me/messages?${this._q}`,payload);
    }

    getUserProfile(userid, fields) {
        let params = {
            access_token: this._token
        };
        let q = "";
        fields = (Array.isArray(fields)) ? fields.join(",") : fields;
        if(fields) {
            params.fields = fields;
        }
        q = qs.stringify(params);
        return this._makeRequest(`/${userid}?${q}`);
    }

    _makeRequest(url, payload) {
        let method = "GET";
        let opts = {};
        if(payload) {
            method = "POST";
            opts.payload = payload;
        }
        return new Promise((resolve, reject) => {
            this._wreck.request(
                method,
                url,
                opts,
                (err, response) => {
                    if(err) {
                        return reject(err);
                    }
                    if(response.body.error) {
                        return reject(response.body.error);
                    }
                    return resolve(response.body);
                }
            );
        });
    }
}

module.exports = FBMessenger;

Add function to get profile. Refactor.


(function ($) {
    var ms = $.mobiscroll,
        presets = ms.presets.scroller;

    presets.treelist = presets.list;
    ms.presetShort('treelist');

})(jQuery);

(function ($) {
    var ms = $.mobiscroll,
        presets = ms.presets.scroller;

    presets.treelist = presets.list;

    ms.presetShort('list');
    ms.presetShort('treelist');

})(jQuery);

Add missing list shorthand init


var _ = require('lodash')
var fs = require('fs')
var gzip = require('gzip-size')
var filesize = require('filesize')
var cssstats = require('cssstats')

var module = require('tachyons-white-space/package.json')
var moduleCss = fs.readFileSync('node_modules/tachyons-white-space/tachyons-white-space.min.css', 'utf8')
var moduleObj = cssstats(moduleCss)
var moduleSize = filesize(moduleObj.gzipSize)

var srcCSS = fs.readFileSync('./src/_white-space.css', 'utf8')

var template = fs.readFileSync('./templates/docs/white-space/index.html', 'utf8')
var tpl = _.template(template)
var html = tpl({
  moduleVersion: module.version,
  moduleSize: moduleSize,
  moduleObj: moduleObj,
  srcCSS: srcCSS
})

fs.writeFileSync('./docs/typography/white-space/index.html', html)

var _ = require('lodash')
var fs = require('fs')
var gzip = require('gzip-size')
var filesize = require('filesize')
var cssstats = require('cssstats')

var module = require('tachyons-white-space/package.json')
var moduleCss = fs.readFileSync('node_modules/tachyons-white-space/tachyons-white-space.min.css', 'utf8')
var moduleObj = cssstats(moduleCss)
var moduleSize = filesize(moduleObj.gzipSize)

var srcCSS = fs.readFileSync('./src/_white-space.css', 'utf8')
var navDocs = fs.readFileSync('./templates/nav_docs.html', 'utf8')

var template = fs.readFileSync('./templates/docs/white-space/index.html', 'utf8')
var tpl = _.template(template)
var html = tpl({
  moduleVersion: module.version,
  moduleSize: moduleSize,
  moduleObj: moduleObj,
  srcCSS: srcCSS,
  navDocs: navDocs
})

fs.writeFileSync('./docs/typography/white-space/index.html', html)

Update with reference to global nav partial


'use strict';

const { mapValues, omitBy } = require('../../../utilities');

const { defaults } = require('./defaults');

// Apply system-defined defaults to input, including input arguments
const systemDefaults = async function (nextFunc, input) {
  const { serverOpts } = input;

  const argsA = getDefaultArgs({ serverOpts, input });
  const inputA = Object.assign({}, input, { args: argsA });

  const response = await nextFunc(inputA);
  return response;
};

// Retrieve default arguments
const getDefaultArgs = function ({
  serverOpts,
  input,
  input: { command, args },
}) {
  const filteredDefaults = omitBy(
    defaults,
    ({ commands, test: testFunc }, attrName) =>
      // Whitelist by command.name
      (commands && !commands.includes(command.name)) ||
      // Whitelist by tests
      (testFunc && !testFunc({ serverOpts, input })) ||
      // Only if user has not specified that argument
      args[attrName] !== undefined
  );

  // Reduce to a single object
  const defaultArgs = mapValues(filteredDefaults, ({ value }) =>
    (typeof value === 'function' ? value({ serverOpts, input }) : value)
  );

  return defaultArgs;
};

module.exports = {
  systemDefaults,
};

'use strict';

const { mapValues, omitBy } = require('../../../utilities');

const { defaults } = require('./defaults');

// Apply system-defined defaults to input, including input arguments
const systemDefaults = async function (nextFunc, input) {
  const { serverOpts } = input;

  const argsA = getDefaultArgs({ serverOpts, input });
  const inputA = Object.assign({}, input, { args: argsA });

  const response = await nextFunc(inputA);
  return response;
};

// Retrieve default arguments
const getDefaultArgs = function ({
  serverOpts,
  input,
  input: { command, args },
}) {
  const filteredDefaults = omitBy(
    defaults,
    ({ commands, test: testFunc }, attrName) =>
      // Whitelist by command.name
      (commands && !commands.includes(command.name)) ||
      // Whitelist by tests
      (testFunc && !testFunc({ serverOpts, input })) ||
      // Only if user has not specified that argument
      args[attrName] !== undefined
  );

  // Reduce to a single object
  const defaultArgs = mapValues(filteredDefaults, ({ value }) =>
    (typeof value === 'function' ? value({ serverOpts, input }) : value)
  );

  return Object.assign({}, args, defaultArgs);
};

module.exports = {
  systemDefaults,
};

Fix system defaults overriding all args


'use strict';

angular
	.module('webClient.places')
	.factory('PlacesService', PlacesService);

function PlacesService($resource, envService) {
	console.log('Hello :)');
	return $resource(
		envService.read('baseBackendUrl') + '/places',
		{},
		{query: {method:'GET', isArray:true}}
	);
}

'use strict';

angular
	.module('webClient.places')
	.factory('PlacesService', PlacesService);

function PlacesService($resource, envService) {
	return $resource(
		envService.read('baseBackendUrl') + '/places',
		{},
		{query: {method:'GET', isArray:true}}
	);
}

Revert "Story-52 [Marcela, Felipe] Just testing if the new versions are being correctly deployed to Heroku from Snap - this commit will be reverted in the next push"


// DOM Library Utilites
$.parseUrlQuery = function (url) {
    var query = {}, i, params, param;
    if (url.indexOf('?') >= 0) url = url.split('?')[1];
    params = url.split('&');
    for (i = 0; i < params.length; i++) {
        param = params[i].split('=');
        query[param[0]] = param[1];
    }
    return query;
};
$.isArray = function (arr) {
    if (Object.prototype.toString.apply(arr) === '[object Array]') return true;
    else return false;
};
$.unique = function (arr) {
    var unique = [];
    for (var i = 0; i < arr.length; i++) {
        if (unique.indexOf(arr[i]) === -1) unique.push(arr[i]);
    }
    return unique;
};
$.trim = function (str) {
    return str.trim();
};
$.supportTouch = (function () {
    return !!(('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch);
})();
$.fn = Dom7.prototype;

// DOM Library Utilites
$.parseUrlQuery = function (url) {
    var query = {}, i, params, param;
    if (url.indexOf('?') >= 0) url = url.split('?')[1];
    params = url.split('&');
    for (i = 0; i < params.length; i++) {
        param = params[i].split('=');
        query[param[0]] = param[1];
    }
    return query;
};
$.isArray = function (arr) {
    if (Object.prototype.toString.apply(arr) === '[object Array]') return true;
    else return false;
};
$.unique = function (arr) {
    var unique = [];
    for (var i = 0; i < arr.length; i++) {
        if (unique.indexOf(arr[i]) === -1) unique.push(arr[i]);
    }
    return unique;
};
$.trim = function (str) {
    return str.trim();
};
$.serializeObject = function (obj) {
    if (typeof obj === 'string') return obj;
    var resultArray = [];
    var separator = '&';
    for (var prop in obj) {
        if ($.isArray(obj[prop])) {
            var toPush = [];
            for (var i = 0; i < obj[prop].length; i ++) {
                toPush.push(prop + '=' + obj[prop][i]);
            }
            resultArray.push(toPush.join(separator));
        }
        else {
            // Should be string
            resultArray.push(prop + '=' + obj[prop]);
        }
    }

    return resultArray.join(separator);
};
$.fn = Dom7.prototype;

Remove supportTouch, add new serializeObject util


const push = require('push.js');

angular.module('opentok-meet').factory('Push', () => push);

angular.module('opentok-meet').factory('NotificationService', ['$window', 'OTSession', 'Push',
  function NotificationService($window, OTSession, Push) {
    let focused = true;

    $window.addEventListener('blur', () => {
      focused = false;
    });

    $window.addEventListener('focus', () => {
      focused = true;
    });

    const notifyOnConnectionCreated = () => {
      if (!OTSession.session) {
        OTSession.on('init', notifyOnConnectionCreated);
      } else {
        OTSession.session.on('connectionCreated', (event) => {
          if (!focused &&
              event.connection.connectionId !== OTSession.session.connection.connectionId) {
            Push.create('New Participant', {
              body: 'Someone joined your meeting',
              icon: '/icon.png',
              tag: 'new-participant',
              timeout: 5000,
              onClick() {
                $window.focus();
                this.close();
              },
            });
          }
        });
      }
    };
    return {
      init() {
        if (Push.Permission.has()) {
          notifyOnConnectionCreated();
        } else {
          try {
            Push.Permission.request(() => {
              notifyOnConnectionCreated();
            }, (err) => {
              console.warn(err);
            });
          } catch (err) {
            console.warn(err);
          }
        }
      },
    };
  },
]);

const push = require('push.js');

angular.module('opentok-meet').factory('Push', () => push);

angular.module('opentok-meet').factory('NotificationService', ['$window', 'OTSession', 'Push',
  function NotificationService($window, OTSession, Push) {
    let focused = true;

    $window.addEventListener('blur', () => {
      focused = false;
    });

    $window.addEventListener('focus', () => {
      focused = true;
    });

    const notifyOnConnectionCreated = () => {
      if (!OTSession.session) {
        OTSession.on('init', notifyOnConnectionCreated);
      } else {
        OTSession.session.on('connectionCreated', (event) => {
          const visible = $window.document.visibilityState === 'visible';
          if ((!focused || !visible) &&
              event.connection.connectionId !== OTSession.session.connection.connectionId) {
            Push.create('New Participant', {
              body: 'Someone joined your meeting',
              icon: '/icon.png',
              tag: 'new-participant',
              timeout: 5000,
              onClick() {
                $window.focus();
                this.close();
              },
            });
          }
        });
      }
    };
    return {
      init() {
        if (Push.Permission.has()) {
          notifyOnConnectionCreated();
        } else {
          try {
            Push.Permission.request(() => {
              notifyOnConnectionCreated();
            }, (err) => {
              console.warn(err);
            });
          } catch (err) {
            console.warn(err);
          }
        }
      },
    };
  },
]);

Add extra condition to show "user joined" notification


import fileSystem from 'fs';

const JsonFileManager = {
    load(fileName) {
        console.log(`[INFO] Loading data from ${__dirname}/${fileName} file`);
        return JSON.parse(fileSystem.readFileSync(`${__dirname}/${fileName}`));
	},
    save(fileName, json) {
        console.log(`[INFO] Saving data in ${__dirname}/${fileName} file`);
        fileSystem.writeFileSync(`${__dirname}/${fileName}`, JSON.stringify(json));
    }
};

export default JsonFileManager;

import fileSystem from 'fs';

const JsonFileManager = {
    load(path) {
        console.log(`[INFO] Loading data from ${path} file`);
        return JSON.parse(fileSystem.readFileSync(`${path}`));
	},
    save(path, json) {
        console.log(`[INFO] Saving data in ${path} file`);
        fileSystem.writeFileSync(`${path}`, JSON.stringify(json));
    }
};

export default JsonFileManager;

Add some changes in path settings for JsonFIleManager


import { execFile } from 'child_process'
import flow from 'flow-bin'
import { logError, log } from '../util/log'

const errorCodes = {
  TYPECHECK_ERROR: 2
}

export default (saguiOptions) => new Promise((resolve, reject) => {
  const commandArgs = ['check', '--color=always']

  if (saguiOptions.javaScript && saguiOptions.javaScript.typeCheckAll) {
    commandArgs.push('--all')
  }

  execFile(flow, commandArgs, (err, stdout, stderr) => {
    if (err) {
      logError('Type check failed:\n')

      switch (err.code) {
        case errorCodes.TYPECHECK_ERROR:
          console.log(stdout)
          break

        default:
          console.log(err)
      }

      reject()
    } else {
      log('Type check completed without errors')
      resolve()
    }
  })
})

import { execFile } from 'child_process'
import flow from 'flow-bin'
import { logError, logWarning, log } from '../util/log'

const errorCodes = {
  TYPECHECK_ERROR: 2
}

export default (saguiOptions) => new Promise((resolve, reject) => {
  // Currently Facebook does not provide Windows builds.
  // https://github.com/flowtype/flow-bin#flow-bin-
  //
  // Instead of using `flow`, we show a warning and ignore this task
  // For further discussion, you can go to:
  // https://github.com/saguijs/sagui/issues/179
  if (process.platform === 'win32') {
    logWarning('Type checking in Windows is not currently supported')
    log('Official flow builds for Windows are not currently provided.')
    log('We are exploring options in https://github.com/saguijs/sagui/issues/179')

    return resolve()
  }

  const commandArgs = ['check', '--color=always']

  if (saguiOptions.javaScript && saguiOptions.javaScript.typeCheckAll) {
    commandArgs.push('--all')
  }

  execFile(flow, commandArgs, (err, stdout, stderr) => {
    if (err) {
      logError('Type check failed:\n')

      switch (err.code) {
        case errorCodes.TYPECHECK_ERROR:
          console.log(stdout)
          break

        default:
          console.log(err)
      }

      reject()
    } else {
      log('Type check completed without errors')
      resolve()
    }
  })
})

Disable type checking in Windows 😞


var Kc = Kc || {};
Kc.Global = Kc.Global || {};
(function (namespace, $) {
    // set all modals to static behavior (clicking out does not close)
    $.fn.modal.Constructor.DEFAULTS.backdrop = "static";
})(Kc.Global, jQuery);

var Kc = Kc || {};
Kc.Global = Kc.Global || {};
(function (namespace, $) {
    // set all modals to static behavior (clicking out does not close)
    $.fn.modal.Constructor.DEFAULTS.backdrop = "static";

    $(document).on("ready", function(){
        // date conversion for date fields to full leading 0 - for days and months and to full year dates
        $(document).on("blur", ".uif-dateControl", function(){
            var dateFormat = $.datepicker._defaults.dateFormat;
            var date = $(this).val();
            if (!date) {
                return;
            }

            date = date.replace(/-/g, "/");

            if (date && (date.match(/\//g) || []).length === 2) {

                // find the expected position and value of year in the string based on date format
                var year;
                if (dateFormat.indexOf("y") === 0) {
                    year = date.substr(0, date.indexOf("/"));
                }
                else {
                    year = date.substr(date.lastIndexOf("/") + 1, date.length - 1);
                }

                // when year is length of 2 append the first 2 numbers of the current full year (ie 14 -> 2014)
                if (year.length === 2) {
                    var currentDate = new Date();
                    year = currentDate.getFullYear().toString().substr(0,2) + year;
                }

                var dateObj = new Date(date);
                if (isNaN(dateObj.valueOf())) {
                    // not valid abandon conversion
                    return;
                }

                dateObj.setFullYear(year);

                var formattedDate = $.datepicker.formatDate(dateFormat, dateObj);
                $(this).val(formattedDate);
            }
        });
    });
})(Kc.Global, jQuery);

Convert non-full date to full date on loss of focus in js


'use strict';

/**
 * @ngdoc directive
 * @name grafterizerApp.directive:dropRowsFunction
 * @description
 * # dropRowsFunction
 */
angular.module('grafterizerApp')
  .directive('dropRowsFunction', function(transformationDataModel) {
    return {
      templateUrl: 'views/pipelineFunctions/dropRowsFunction.html',
      restrict: 'E',
      link: function postLink(scope, element, attrs) {
        if (!scope.function) {
          scope.function = {
            numberOfRows: 1,
            take: true,
            docstring: null
          };
        }

        scope.$parent.generateCurrFunction = function() {
          return new transformationDataModel.DropRowsFunction(parseInt(
            scope.function.numberOfRows), scope.function.take, scope.function.docstring);
        };

        scope.doGrep = function() {
          scope.$parent.selectefunctionName = 'grep';
        };

        scope.showUsage = false;
        scope.switchShowUsage = function() {
          scope.showUsage = !scope.showUsage;
        };
      }
    };
  });

'use strict';

/**
 * @ngdoc directive
 * @name grafterizerApp.directive:dropRowsFunction
 * @description
 * # dropRowsFunction
 */
angular.module('grafterizerApp')
  .directive('dropRowsFunction', function(transformationDataModel) {
    return {
      templateUrl: 'views/pipelineFunctions/dropRowsFunction.html',
      restrict: 'E',
      link: function postLink(scope, element, attrs) {
        if (!scope.function) {
          scope.function = {
            numberOfRows: 1,
            take: false,
            docstring: null
          };
        }

        scope.$parent.generateCurrFunction = function() {
          return new transformationDataModel.DropRowsFunction(parseInt(
            scope.function.numberOfRows), scope.function.take, scope.function.docstring);
        };

        scope.doGrep = function() {
          scope.$parent.selectefunctionName = 'grep';
        };

        scope.showUsage = false;
        scope.switchShowUsage = function() {
          scope.showUsage = !scope.showUsage;
        };
      }
    };
  });

Use drop row by default


'use strict';

angular.module('publicApp')
  .controller('UsersCtrl', ['$scope', '$http', '$location', '$routeParams', 'UserService', function ($scope, $http, $location, $user, $routeParams) {
    console.log('user id', $routeParams.id);
    if ($user) {
      console.log('user is logged in');
      if (($user.id == $routeParams.id) || $user.admin) {
        console.log('user is allowed to access this resource');
      } else {
        $location.path = '/users/'+$user.id;
      }
    } else {
      $location.path = '/login';
    }

  }]);

'use strict';

angular.module('publicApp')
  .controller('UsersCtrl', ['$scope', '$http', '$location', '$route', '$routeParams', 'UserService', function ($scope, $http, $location, $route, $routeParams, $user) {
    $scope.user = $user;
    if ($user.isLogged) {
      if (($user.id == $routeParams.id) || $user.admin) {
        // do ALL THE THINGS!
        setupDashboard();
        console.log($scope.user);
      } else {
        $location.path('/users/'+$user.id);
      }
    } else {
      $location.path('/login');
    }

    function setupDashboard() {
      $scope.user.balances = [{ currency: 'USD', amount: 1510 }, { currency: 'XAG', amount: 75 }];
      $scope.user.ripple_transactions = [];
      $scope.user.external_transaction = [];
      $scope.user.external_accounts = [];
      $scope.user.ripple_addresses = [];
    }

  }]);

Add user data variables to scope


$(document).ready(function main() {
    $("#add").click(function addRow() {
        var name = $("	").text($("#name").val());
        var up = $("<button></button>").text("Move up").click(moveUp);
        var down = $("<button></button>").text("Move down").click(moveDown);
        var operations = $("	").append(up, down);
        var row = $("

").append(name, operations);
        $("#nameList").append(row);
        $("#name").val("");
    })

})

function moveUp() {

}

function moveDown() {

}

Implement adding rows to the table


let React = require("react")
let {errorList, sizeClassNames, formGroupCx, label} = require("../util.js")
let {div, textarea} = React.DOM
let cx = require("classnames")

export default class extends React.Component {

  static displayName = "Frig.friggingBootstrap.Text"

  static defaultProps = Object.assign(require("../default_props.js"))

  _inputHtml() {
  	return Object.assign({}, this.props.inputHtml, {
  		className: `${this.props.className || ""} form-control`.trim(),
  		valueLink: this.props.valueLink,
  	})
  }

  _cx() {
    return cx({
      "form-group": true,
      "has-error": this.props.errors != null,
      "has-success": this.state.edited && this.props.errors == null,
    })
  }

  _label() {
    if (this.props.label == null) return ""
    return label(this.props.labelHtml, this.props.label)
  }

  render() {
    return div({className: cx(sizeClassNames(this.props))},
      div({className: this._cx()},
        this._label(),
        div({className: "controls"},
          textarea(this._inputHtml()),
        ),
        this._errorList(this.props.errors),
      ),
    )
  }

}

let React = require("react")
let {errorList, sizeClassNames, formGroupCx, label} = require("../util.js")
let {div, textarea} = React.DOM
let cx = require("classnames")

export default class extends React.Component {

  static displayName = "Frig.friggingBootstrap.Text"

  static defaultProps = Object.assign(require("../default_props.js"))

  _inputHtml() {
  	return Object.assign({}, this.props.inputHtml, {
  		className: `${this.props.className || ""} form-control`.trim(),
  		valueLink: this.props.valueLink,
  	})
  }

  _cx() {
    return cx({
      "form-group": true,
      "has-error": this.props.errors != null,
      "has-success": this.state.edited && this.props.errors == null,
    })
  }

  render() {
    return div({className: cx(sizeClassNames(this.props))},
      div({className: this._cx()},
        label(this.props),
        div({className: "controls"},
          textarea(this._inputHtml()),
        ),
        errorList(this.props.errors),
      ),
    )
  }

}

Remove Textarea Label, Use Frig Version Instead


(function (module) {
    module.controller('ProjectHomeController', ProjectHomeController);
    ProjectHomeController.$inject = ["project", "mcmodal", "templates", "$state", "Restangular"];

    function ProjectHomeController(project, mcmodal, templates, $state, Restangular) {
        var ctrl = this;

        ctrl.project = project;
        ctrl.chooseTemplate = chooseTemplate;
        ctrl.chooseExistingProcess = chooseExistingProcess;
        ctrl.templates = templates;
        ctrl.hasFavorites = _.partial(_.any, ctrl.templates, _.matchesProperty('favorite', true));

        /////////////////////////

        function chooseTemplate() {
            mcmodal.chooseTemplate(ctrl.project, templates).then(function (processTemplateName) {
                $state.go('projects.project.processes.create', {process: processTemplateName, process_id: ''});
            });
        }

        function chooseExistingProcess() {
            Restangular.one('v2').one("projects", project.id).one("processes").getList().then(function(processes) {
                mcmodal.chooseExistingProcess(processes).then(function (existingProcess) {
                    var processName = existingProcess.process_name ? existingProcess.process_name : 'TEM';
                    $state.go('projects.project.processes.create', {process: processName, process_id: existingProcess.id});
                });
            });
        }
    }
}(angular.module('materialscommons')));

(function (module) {
    module.controller('ProjectHomeController', ProjectHomeController);
    ProjectHomeController.$inject = ["project", "mcmodal", "templates", "$state", "Restangular"];

    function ProjectHomeController(project, mcmodal, templates, $state, Restangular) {
        var ctrl = this;

        ctrl.project = project;
        ctrl.chooseTemplate = chooseTemplate;
        ctrl.chooseExistingProcess = chooseExistingProcess;
        ctrl.templates = templates;
        ctrl.hasFavorites = _.partial(_.any, ctrl.templates, _.matchesProperty('favorite', true));

        /////////////////////////

        function chooseTemplate() {
            mcmodal.chooseTemplate(ctrl.project, templates).then(function (processTemplateName) {
                $state.go('projects.project.processes.create', {process: processTemplateName, process_id: ''});
            });
        }

        function chooseExistingProcess() {
            Restangular.one('v2').one("projects", project.id).one("processes").getList().then(function(processes) {
                mcmodal.chooseExistingProcess(processes).then(function (existingProcess) {
                    $state.go('projects.project.processes.create',
                        {process: existingProcess.process_name, process_id: existingProcess.id});
                });
            });
        }
    }
}(angular.module('materialscommons')));

Remove the hard coded process name now that the process name has been set.


import Ember from 'ember';

const { inject } = Ember;
const { service } = inject;

export default Ember.Service.extend({
  store: service(),
  getResults(subject, object, objectId){
    return this.get('store').query(
      this.getModel(subject),
      this.getQuery(object, objectId)
    ).then(results => {
      return results.map(result => {
        return this.mapResult(result, object);
      });
    });
  },
  getModel(subject){
    let model = subject.dasherize();
    if(model === 'instructor'){
      model = 'user';
    }
    if(model === 'mesh-term'){
      model = 'mesh-descriptor';
    }

    return model;
  },
  getQuery(object, objectId){
    let query = {
      limit: 1000
    };
    if(object && objectId){
      query.filter = {};
      query.filter[object] = objectId;
    }

    return query;
  },
  mapResult(result, object){
    let titleParam = 'title';
    if(object === 'instructor'){
      titleParam = 'fullName';
    }
    if(object === 'mesh-term'){
      titleParam = 'name';
    }

    return {
      value: result.get(titleParam)
    };
  }
});

import Ember from 'ember';

const { inject } = Ember;
const { service } = inject;

export default Ember.Service.extend({
  store: service(),
  getResults(subject, object, objectId){
    return this.get('store').query(
      this.getModel(subject),
      this.getQuery(object, objectId)
    ).then(results => {
      return results.map(result => {
        return this.mapResult(result, object);
      });
    });
  },
  getModel(subject){
    let model = subject.dasherize();
    if(model === 'instructor'){
      model = 'user';
    }
    if(model === 'mesh-term'){
      model = 'mesh-descriptor';
    }

    return model;
  },
  getQuery(object, objectId){
    let query = {
      limit: 1000
    };
    if(object && objectId){
      query.filters = {};
      query.filters[object] = objectId;
    }

    return query;
  },
  mapResult(result, object){
    let titleParam = 'title';
    if(object === 'instructor'){
      titleParam = 'fullName';
    }
    if(object === 'mesh-term'){
      titleParam = 'name';
    }

    return {
      value: result.get(titleParam)
    };
  }
});

Fix report query to actually query


var data = {"labels": ["12 December", "13 December", "14 December", "15 December", "16 December", "17 December", "18 December"], "datasets": [{"languages": [[], [], [], [], [], ["Python", "Other"], ["Python", "JavaScript", "Bash"]], "pointHighlightFill": "#fff", "fillColor": "rgba(151,187,205,0.2)", "pointHighlightStroke": "rgba(151,187,205,1)", "time": [" 0 secs", " 0 secs", " 0 secs", " 0 secs", " 0 secs", "53 mins", "2 hrs 17 mins"], "pointColor": "rgba(151,187,205,1)", "strokeColor": "rgba(151,187,205,1)", "pointStrokeColor": "#fff", "data": [0.0, 0.0, 0.0, 0.0, 0.0, 0.8852777777777778, 2.2880555555555557], "label": "Dataset"}]};
var totalTime = "3 hours 10 minutes";
var languages = ["Python", "JavaScript", "Bash"];
var ctx = document.getElementById("chart").getContext("2d");
var myLineChart = new Chart(ctx).Line(data, {
    tooltipTemplate = function(valuesObject){
      var label = valuesObject.label;
      var idx = data.labels.indexOf(label);
      var result = data.datasets[0].time[idx];
      if (data.datasets[0].languages[idx] !== 0)
        result += '[' + data.datasets[0].languages[idx].join(", ") + "]"
      return result;
    }
});
document.getElementById("summary").innerHTML = "I  have written code for " + totalTime + " in the the last week in mostly " + languages.join(", ") + ".";

Update dashboard at Fri Dec 18 02:17:47 NPT 2015


import mongoose from "mongoose"

async function createConnection() {
  mongoose.Promise = Promise

  // Don't forget to add a user for this connection
  const connection = await mongoose.connect("mongodb://localhost/twi-test", {
    useMongoClient: true,
    promiseLibrary: Promise
  })

  return connection
}

async function closeConnection() {
  return await mongoose.disconnect()
}

export {
  createConnection,
  closeConnection
}

import mongoose from "mongoose"

async function createConnection() {
  mongoose.Promise = Promise

  // Don't forget to add a user for this connection
  const connection = await mongoose.connect("mongodb://localhost/twi-test", {
    promiseLibrary: Promise
  })

  return connection
}

async function closeConnection() {
  return await mongoose.disconnect()
}

export {
  createConnection,
  closeConnection
}

Fix db connection in tests helper


exports.up = function(knex, Promise) {
  return knex.schema.table('actions', function(table) {
    table.string('event_id').index();
    table.foreign('event_id')
      .references('id')
      .inTable('events')
      .onDelete('RESTRICT')
      .onUpdate('CASCADE');
  })
  .raw(`
    CREATE UNIQUE INDEX
      only_one_check_in_per_event
    ON
      actions(user_id, event_id)
    WHERE action_type_id = 9;
  `);
};
// ALTER TABLE actions ADD CONSTRAINT actions_user_event_uniq UNIQUE (user_id, event_id)
exports.down = function(knex, Promise) {
  return knex.schema.table('actions', function(table) {
    table.dropColumn('event_id');
  });
}

exports.up = function(knex, Promise) {
  return knex.schema.table('actions', function(table) {
    table.string('event_id').index();
    table.foreign('event_id')
      .references('id')
      .inTable('events')
      .onDelete('RESTRICT')
      .onUpdate('CASCADE');
  })
  .raw(`
    CREATE UNIQUE INDEX
      only_one_check_in_per_event
    ON
      actions(user_id, event_id)
    -- Magic number 9 is CHECK_IN_EVENT action type ID.
    WHERE action_type_id = 9;
  `);
};

exports.down = function(knex, Promise) {
  return knex.schema.table('actions', function(table) {
    table.dropColumn('event_id');
  });
}

Comment to explain the magic number


Ember.ComponentLookup.reopen({
  lookupFactory: function(name) {
    var Component = this._super.apply(this, arguments);

    if (!Component) { return; }

    name = name.replace(".","/");
    if (Component.prototype.classNames.indexOf(Ember.COMPONENT_CSS_LOOKUP[name]) > -1){
      return Component;
    }

    return Component.reopen({
      classNames: [Ember.COMPONENT_CSS_LOOKUP[name]]
    });
  },

  componentFor: function(name) {
    var Component = this._super.apply(this, arguments);

    if (!Component) { return; }

    name = name.replace(".","/");

    if (Component.prototype.classNames.indexOf(Ember.COMPONENT_CSS_LOOKUP[name]) > -1){
      return Component;
    }

    return Component.reopen({
      classNames: [Ember.COMPONENT_CSS_LOOKUP[name]]
    });
  }
});

(function() {
  function componentHasBeenReopened(Component, className) {
    return Component.prototype.classNames.indexOf(className) > -1;
  }

  function reopenComponent(_Component, className) {
    var Component = _Component.reopen({
      classNames: [className]
    });

    return Component;
  }

  function ensureComponentCSSClassIncluded(_Component, _name) {
    var Component = _Component;
    var name = name.replace(".","/");
    var className = Ember.COMPONENT_CSS_LOOKUP[name];

    if (!componentHasBeenReopened(Component)) {
      Component = reopenComponent(Component, className);
    }

    return Component;
  }

  Ember.ComponentLookup.reopen({
    lookupFactory: function(name) {
      var Component = this._super.apply(this, arguments);

      if (!Component) { return; }

      return ensureComponentCSSClassIncluded(Component, name);
    },

    componentFor: function(name) {
      var Component = this._super.apply(this, arguments);

      if (!Component) { return; }

      return ensureComponentCSSClassIncluded(Component, name);
    }
  });
})()

Refactor monkey-patch to include less duplication.


// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html

module.exports = function (config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine', '@angular-devkit/build-angular'],
    plugins: [
      require('karma-jasmine'),
      require('karma-chrome-launcher'),
      require('karma-jasmine-html-reporter'),
      require('karma-coverage-istanbul-reporter'),
      require('karma-mocha-reporter'),
      require('@angular-devkit/build-angular/plugins/karma')
    ],
    client: {
      clearContext: false // leave Jasmine Spec Runner output visible in browser
    },
    coverageIstanbulReporter: {
      dir: require('path').join(__dirname, '../coverage'),
      reports: ['html', 'lcovonly'],
      fixWebpackSourcePaths: true
    },
    reporters: ['kjhtml', 'mocha'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    singleRun: false,
    customLaunchers: {
      ChromeHeadlessForDocker: {
        base: 'ChromeHeadless',
        flags: [
          '--disable-web-security',
          '--disable-gpu',
          '--no-sandbox'
        ],
        displayName: 'Chrome Headless for docker'
      }
    },
    browsers: ['Chrome']
  });
};

// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html

module.exports = function (config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine', '@angular-devkit/build-angular'],
    plugins: [
      require('karma-jasmine'),
      require('karma-chrome-launcher'),
      require('karma-jasmine-html-reporter'),
      require('karma-coverage-istanbul-reporter'),
      require('karma-mocha-reporter'),
      require('@angular-devkit/build-angular/plugins/karma')
    ],
    client: {
      clearContext: false // leave Jasmine Spec Runner output visible in browser
    },
    coverageIstanbulReporter: {
      dir: require('path').join(__dirname, '../coverage'),
      reports: ['html', 'lcovonly'],
      fixWebpackSourcePaths: true
    },
    reporters: ['kjhtml', 'mocha'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    singleRun: false,
    customLaunchers: {
      ChromeHeadlessForDocker: {
        base: 'ChromeHeadless',
        flags: [
          '--disable-web-security',
          '--disable-gpu',
          '--no-sandbox'
        ],
        displayName: 'Chrome Headless for docker'
      }
    },
    browsers: ['ChromeHeadlessForDocker']
  });
};

Use headless Chrome for Angular tests again.


var game = require('socket.io-client')('http://192.168.0.24:3000'),
    lobby = require('socket.io-client')('http://192.168.0.24:3030'),
    Chess = require('chess.js').Chess,

    name = 'RndJesus_' + Math.floor(Math.random() * 100);;

lobby.on('connect', function () {
    console.log('Player ' + name + ' is connected to lobby');
});

lobby.on('join', function (gameId) {
    console.log('Player is joining game: ' + gameId);
    game.emit('join', gameId);
});

game.on('connect', function () {
    console.log('Player ' + name + ' is connected to game');
    //game.emit('join', 'test_game');
});

game.on('move', function (gameState) {
    var chess = new Chess();
    chess.load(gameState.board);

    var moves = chess.moves(),
        move = moves[Math.floor(Math.random() * moves.length)];

    console.log(move);
    gameState.move = move;

    game.emit('move', gameState);
});

var game = require('socket.io-client')('http://localhost:3000'),
    lobby = require('socket.io-client')('http://localhost:3030'),
    Chess = require('chess.js').Chess,

    name = 'RndJesus_' + Math.floor(Math.random() * 100);;

lobby.on('connect', function () {
    console.log('Player ' + name + ' is connected to lobby');
});

lobby.on('join', function (gameId) {
    console.log('Player is joining game: ' + gameId);
    game.emit('join', gameId);
});

game.on('connect', function () {
    console.log('Player ' + name + ' is connected to game');
    //game.emit('join', 'test_game');
});

game.on('move', function (gameState) {
    var chess = new Chess();
    chess.load(gameState.board);

    var moves = chess.moves(),
        move = moves[Math.floor(Math.random() * moves.length)];

    console.log(move);
    gameState.move = move;

    game.emit('move', gameState);
});

Use localhost when running locally


'use strict';

// MAIN //

/**
* Computes the minimum time.
*
* @private
* @param {ArrayArray} times - times
* @returns {NonNegativeIntegerArray} minimum time
*/
function min( times ) {
	var out;
	var t;
	var i;

	out = times[ 0 ];
	for ( i = 1; i < times.length; i++ ) {
		t = times[ i ];
		if (
			t[ 0 ] <= out[ 0 ] &&
			t[ 1 ] < out[ 1 ]
		) {
			out = t;
		}
	}
	return out;
} // end FUNCTION min()

// EXPORTS //

module.exports = min;

'use strict';

// MAIN //

/**
* Computes the minimum time.
*
* @private
* @param {ArrayArray} times - times
* @returns {NonNegativeIntegerArray} minimum time
*/
function min( times ) {
	var out;
	var t;
	var i;

	out = times[ 0 ];
	for ( i = 1; i < times.length; i++ ) {
		t = times[ i ];
		if (
			t[ 0 ] < out[ 0 ] ||
			(
				t[ 0 ] === out[ 0 ] &&
				t[ 1 ] < out[ 1 ]
			)
		) {
			out = t;
		}
	}
	return out;
} // end FUNCTION min()

// EXPORTS //

module.exports = min;

Fix bug when calculating minimum time


var st = require('st')
  , http = require('http')
  , js = require('atomify-js')
  , css = require('atomify-css')
  , open = require('open')

module.exports = function (args) {

  var mount = st(args.server.st || process.cwd())
    , port = args.server.port || 1337
    , launch = args.server.open
    , path = args.server.path || ''
    , url = args.server.url || 'http://localhost:' + port + path

  http.createServer(function(req, res) {

    switch (req.url) {
      case args.js.alias || args.js.entry:
        js(args.js, responder('javascript', res))
        break

      case args.css.alias || args.css.entry:
        css(args.css, responder('css', res))
        break

      default:
        mount(req, res)
        break
    }

  }).listen(port)

  if (launch) open(url)
}

function responder (type, res) {
  return function (err, src) {
    if (!res.headersSent) res.setHeader('Content-Type', 'text/' + type)
    res.end(src)
  }
}

var st = require('st')
  , http = require('http')
  , js = require('atomify-js')
  , css = require('atomify-css')
  , open = require('open')

module.exports = function (args) {

  var mount = st(args.server.st || process.cwd())
    , port = args.server.port || 1337
    , launch = args.server.open
    , path = args.server.path || '/'
    , url

  if (path.charAt(0) !== '/') path = '/' + path

  url = args.server.url || 'http://localhost:' + port + path

  http.createServer(function (req, res) {

    switch (req.url) {
      case args.js.alias || args.js.entry:
        js(args.js, responder('javascript', res))
        break

      case args.css.alias || args.css.entry:
        css(args.css, responder('css', res))
        break

      default:
        mount(req, res)
        break
    }

  }).listen(port)

  if (launch) open(url)
}

function responder (type, res) {
  return function (err, src) {
    if (!res.headersSent) res.setHeader('Content-Type', 'text/' + type)
    res.end(src)
  }
}

Add leading slash to path if not provided


$.fn.userAutocomplete = function () {
  'use strict';

  function formatUser(user) {
    return Select2.util.escapeMarkup(user.email);
  }

  this.select2({
    minimumInputLength: 1,
    multiple: true,
    initSelection: function (element, callback) {
      $.get(Spree.routes.users_api, {
        ids: element.val()
      }, function (data) {
        callback(data.users);
      });
    },
    ajax: {
      url: Spree.routes.users_api,
      datatype: 'json',
      params: { "headers": { "X-Spree-Token": Spree.api_key } },
      data: function (term) {
        return {
          m: 'or',
          email_start: term,
          addresses_firstname_start: term,
          addresses_lastname_start: term
        };
      },
      results: function (data) {
        return {
          results: data.users,
          more: data.current_page < data.pages
        };
      }
    },
    formatResult: formatUser,
    formatSelection: formatUser
  });
};

$(document).ready(function () {
  $('.user_picker').userAutocomplete();
});

$.fn.userAutocomplete = function () {
  'use strict';

  function formatUser(user) {
    return Select2.util.escapeMarkup(user.email);
  }

  this.select2({
    minimumInputLength: 1,
    multiple: true,
    initSelection: function (element, callback) {
      Spree.ajax({
        url: Spree.routes.users_api,
        data: {
          ids: element.val()
        },
        success: function(data) {
          callback(data.users);
        }
      });
    },
    ajax: {
      url: Spree.routes.users_api,
      datatype: 'json',
      params: { "headers": { "X-Spree-Token": Spree.api_key } },
      data: function (term) {
        return {
          m: 'or',
          email_start: term,
          addresses_firstname_start: term,
          addresses_lastname_start: term
        };
      },
      results: function (data) {
        return {
          results: data.users,
          more: data.current_page < data.pages
        };
      }
    },
    formatResult: formatUser,
    formatSelection: formatUser
  });
};

$(document).ready(function () {
  $('.user_picker').userAutocomplete();
});

Fix user picker's select2 initial selection


$(document).ready(function () {
    var locale = 'en' !== LOCALE ? LOCALE : '';
    var options = $.extend({}, $.datepicker.regional[locale], $.timepicker.regional[locale], {
        dateFormat: 'dd.mm.yy'
    });

    var init;
    (init = function () {
        $('input.date').datepicker(options);
        $('input.datetime').datetimepicker(options);
        $('input.time').timepicker(options);
    })();

    $(document).bind('ajaxSuccess', init);
});

$(document).ready(function () {
    var locale = 'en' !== LOCALE ? LOCALE : '';
    var options = $.extend({}, $.datepicker.regional[locale], $.timepicker.regional[locale], {
        dateFormat: 'dd.mm.yy'
    });

    var init;
    (init = function () {
        ['date', 'datetime', 'time'].map(function (type) {
            $('input.' + type)[type + 'picker'](options);
        });
    })();

    $(document).bind('ajaxSuccess', init);
});

Simplify date time pickers init JS.


'use strict';

const audioUtil = require('../utils/audio');

const PhysicsSprite = require('./physics-sprite');

class Barrier extends PhysicsSprite {
  constructor(game, x, y) {
    super(game, x, y, 'barrier', 2, true);

    this.animations.add('up', [ 2, 1, 0 ], 10, false);
    this.animations.add('down', [ 0, 1, 2 ], 10, false);

    this.introSfx = audioUtil.addSfx(
      game, 'barrier', game.rnd.integerInRange(0, 2)
    );
  }

  playIntro() {
    this.animations.play('up');

    this.introSfx.play();
  }

  playOutro() {
    this.animations.play('down');
  }
}

module.exports = Barrier;

'use strict';

const audioUtil = require('../utils/audio');

const PhysicsSprite = require('./physics-sprite');

class Barrier extends PhysicsSprite {
  constructor(game, x, y) {
    super(game, x, y, 'barrier', 2, true);

    this.animations.add('up', [ 2, 1, 0 ], 10, false);
    this.animations.add('down', [ 0, 1, 2 ], 10, false);

    this.introSfx = audioUtil.addSfx('barrier', game.rnd.integerInRange(0, 2));
  }

  playIntro() {
    this.animations.play('up');

    this.introSfx.play();
  }

  playOutro() {
    this.animations.play('down');
  }
}

module.exports = Barrier;

Fix the call to addSfx broken by the signature change


import {mobileQuery, responsiveAction} from 'ghost/utils/mobile';

var PostsView = Ember.View.extend({
    target: Ember.computed.alias('controller'),
    classNames: ['content-view-container'],
    tagName: 'section',

    mobileInteractions: function () {
        Ember.run.scheduleOnce('afterRender', this, function () {
            var self = this;

            $(window).resize(function () {
                if (!mobileQuery.matches) {
                    self.send('resetContentPreview');
                }
            });

            // ### Show content preview when swiping left on content list
            $('.manage').on('click', '.content-list ol li', function (event) {
                responsiveAction(event, '(max-width: 800px)', function () {
                    self.send('showContentPreview');
                });
            });

            // ### Hide content preview
            $('.manage').on('click', '.content-preview .button-back', function (event) {
                responsiveAction(event, '(max-width: 800px)', function () {
                    self.send('hideContentPreview');
                });
            });
        });
    }.on('didInsertElement'),
});

export default PostsView;

import {mobileQuery, responsiveAction} from 'ghost/utils/mobile';

var PostsView = Ember.View.extend({
    target: Ember.computed.alias('controller'),
    classNames: ['content-view-container'],
    tagName: 'section',

    mobileInteractions: function () {
        Ember.run.scheduleOnce('afterRender', this, function () {
            var self = this;

            $(window).resize(function () {
                if (!mobileQuery.matches) {
                    self.send('resetContentPreview');
                }
            });

            // ### Show content preview when swiping left on content list
            $('.manage').on('click', '.content-list ol li', function (event) {
                responsiveAction(event, '(max-width: 800px)', function () {
                    self.send('showContentPreview');
                });
            });

            // ### Hide content preview
            $('.manage').on('click', '.content-preview .button-back', function (event) {
                responsiveAction(event, '(max-width: 800px)', function () {
                    self.send('hideContentPreview');
                });
            });
            $('[data-off-canvas]').attr('href', this.get('controller.ghostPaths.blogRoot'));
        });
    }.on('didInsertElement'),
});

export default PostsView;

Fix Ghost icon is not clickable


const fs = require('fs'),
    path = require('path'),
    CACHE_DIR = path.join(__dirname, '/../../.cache'),
    THRESHOLD = 3.4 * 1024 * 1024; // 3.4 MB

describe('bootcode size', function () {
    this.timeout(60 * 1000);

    it('should not exceed the threshold', function (done) {
        fs.readdir(CACHE_DIR, function (err, files) {
            if (err) { return done(err); }

            files.forEach(function (file) {
                var size = fs.statSync(CACHE_DIR + '/' + file).size;

                expect(size, (file + ' threshold exceeded')).to.be.below(THRESHOLD);
            });

            done();
        });
    });
});

const fs = require('fs'),
    path = require('path'),
    CACHE_DIR = path.join(__dirname, '/../../.cache'),
    THRESHOLD = 4 * 1024 * 1024; // 4 MB

describe('bootcode size', function () {
    this.timeout(60 * 1000);

    it('should not exceed the threshold', function (done) {
        fs.readdir(CACHE_DIR, function (err, files) {
            if (err) { return done(err); }

            files.forEach(function (file) {
                var size = fs.statSync(CACHE_DIR + '/' + file).size;

                expect(size, (file + ' threshold exceeded')).to.be.below(THRESHOLD);
            });

            done();
        });
    });
});

Update bootcode threshold to 4 MB


module.exports = function modelLoader(isParent) {
    'use strict';

    if (isParent || process.env.MCTEST) {
        console.log('using mocks/model');
        return require('./mocks/model');
    }

    let ropts = {
        db: 'materialscommons',
        port: 30815
    };

    let r = require('rethinkdbdash')(ropts);
    return require('./db/model')(r);
};

module.exports = function modelLoader(isParent) {
    'use strict';

    if (isParent || process.env.MCTEST) {
        return require('./mocks/model');
    }

    let ropts = {
        db: process.env.MCDB || 'materialscommons',
        port: process.env.MCPORT || 30815
    };

    let r = require('rethinkdbdash')(ropts);
    return require('./db/model')(r);
};

Add environment variables for testing, database and database server port.


var ConfigBuilder = require('../builders/ConfigBuilder');

module.exports.register = function(Handlebars, options) {
    options = options || {};

    var helpers = {
        helper_config: function(key) {
            var config = new ConfigBuilder().build();
            return config.get(key);
        }
    };

    for (var helper in helpers) {
        if (helpers.hasOwnProperty(helper)) {
            Handlebars.registerHelper(helper, helpers[helper]);
        }
    }
}

var ConfigBuilder = require('../builders/ConfigBuilder');

module.exports.register = function(Handlebars, options) {
    options = options || {};

    var helperName = 'helper_config';

    function validateArguments(arguments) {
        if (arguments.length < 2) {
            throw new ReferenceError(`${helperName}: config key must be passed from template; nothing was passed`);
        }
        if (arguments.length === 2 && typeof arguments[0] != 'string') {
            var argType = typeof arguments[0];
            throw new TypeError(`${helperName}: config key must be a string when passed from template; ${argType} was passed`);
        }
    }

    var helpers = {
        [helperName]: function(key, options) {
            validateArguments(arguments);

            var config = new ConfigBuilder({helperName: helperName}).build();
            return config.get(key);
        }
    };

    for (var helper in helpers) {
        if (helpers.hasOwnProperty(helper)) {
            Handlebars.registerHelper(helper, helpers[helper]);
        }
    }
}

Validate arguments passed from template to helper_config


/****************************************************************************
#
# This file is part of the Vilfredo Client.
#
# Copyright © 2009-2014 Pietro Speroni di Fenizio / Derek Paterson.
#
# VilfredoReloadedCore is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation version 3 of the License.
#
# VilfredoReloadedCore is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License
# for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with VilfredoReloadedCore.  If not, see <http://www.gnu.org/licenses/>.
#
****************************************************************************/
var VILFREDO_URL = 'http://0.0.0.0:8080';

var API_VERSION = 'v1';

var ALGORITHM_VERSION = 1;

// Don't edit below
//
var VILFREDO_API = VILFREDO_URL + '/api/' + API_VERSION;

var STATIC_FILES = VILFREDO_URL + '/static';

var Q_READ = 0x1
var Q_VOTE = 0x2
var Q_PROPOSE = 0x4
var Q_INVITE = 0x8

/****************************************************************************
#
# This file is part of the Vilfredo Client.
#
# Copyright © 2009-2014 Pietro Speroni di Fenizio / Derek Paterson.
#
# VilfredoReloadedCore is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation version 3 of the License.
#
# VilfredoReloadedCore is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License
# for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with VilfredoReloadedCore.  If not, see <http://www.gnu.org/licenses/>.
#
****************************************************************************/
var VILFREDO_URL = 'http://0.0.0.0:8080';

var API_VERSION = 'v1';

var ALGORITHM_VERSION = 1;

// radius of circle representing a vote
var radius = 10;

// Don't edit below
//
var VILFREDO_API = VILFREDO_URL + '/api/' + API_VERSION;

var STATIC_FILES = VILFREDO_URL + '/static';

var Q_READ = 0x1
var Q_VOTE = 0x2
var Q_PROPOSE = 0x4
var Q_INVITE = 0x8

Add setting for vote radius


/*!
 * Copyright 2015 Geoscience Australia (http://www.ga.gov.au/copyright.html)
 */

(function(angular) {

'use strict';

angular.module("icsm.toolbar", [])

.directive("icsmToolbar", [function() {
	return {
		controller: 'toolbarLinksCtrl'
	};
}])

/**
 * Override the default mars tool bar row so that a different implementation of the toolbar can be used.
 */
.directive('icsmToolbarRow', [function() {
	return {
		scope:{
			map:"="
		},
		restrict:'AE',
		templateUrl:'icsm/toolbar/toolbar.html'
	};
}])

.directive('icsmToolbarInfo', [function() {
	return {
		templateUrl: 'radwaste/toolbar/toolbarInfo.html'
	};
}])

.controller("toolbarLinksCtrl", ["$scope", "configService", function($scope, configService) {

	var self = this;
	configService.getConfig().then(function(config) {
		self.links = config.toolbarLinks;
	});

	$scope.item = "";
	$scope.toggleItem = function(item) {
		$scope.item = ($scope.item == item) ? "" : item;
	};

}]);

})(angular);

/*!
 * Copyright 2015 Geoscience Australia (http://www.ga.gov.au/copyright.html)
 */

(function(angular) {

'use strict';

angular.module("icsm.toolbar", [])

.directive("icsmToolbar", [function() {
	return {
		controller: 'toolbarLinksCtrl'
	};
}])

/**
 * Override the default mars tool bar row so that a different implementation of the toolbar can be used.
 */
.directive('icsmToolbarRow', [function() {
	return {
		scope:{
			map:"="
		},
		restrict:'AE',
		templateUrl:'icsm/toolbar/toolbar.html'
	};
}])

.directive('icsmToolbarInfo', [function() {
	return {
		templateUrl: 'radwaste/toolbar/toolbarInfo.html'
	};
}])

.controller("toolbarLinksCtrl", ["$scope", "configService", function($scope, configService) {

	var self = this;
	configService.getConfig().then(function(config) {
		self.links = config.toolbarLinks;
	});

	$scope.item = "";
	$scope.toggleItem = function(item) {
		$scope.item = ($scope.item === item) ? "" : item;
	};

}]);

})(angular);

Implement eslint suggestion around equals comparison


var page = require('webpage').create(),
    system = require('system'),
    fs = require('fs');

var userAgent = "Grabshot"

var url  = system.args[1];
var format = system.args[2] || "PNG"

page.viewportSize = {
  width: 1280
  // height: ...
}

function render() {
  result = {
    title: page.evaluate(function() { return document.title }),
    imageData: page.renderBase64(format),
    format: format
  }

  console.log(JSON.stringify(result))
  phantom.exit();
}

page.onError = phantom.onError = function() {
  console.log("PhantomJS error :(");
  phantom.exit(1);
};

page.onLoadFinished = function (status) {
  if (status !== 'success') {
    phantom.exit(1);
  }

  setTimeout(render, 50);
}

page.open(url);

var page = require('webpage').create(),
    system = require('system'),
    fs = require('fs');

var userAgent = "Grabshot"

var url  = system.args[1];
var format = system.args[2] || "PNG"

page.viewportSize = {
  width: 1280
  // height: ...
}

function render() {
  result = {
    title: page.evaluate(function() { return document.title }),
    imageData: page.renderBase64(format),
    format: format
  }

  console.log(JSON.stringify(result))
  phantom.exit();
}

page.onError = phantom.onError = function() {
  console.log("PhantomJS error :(");
  phantom.exit(1);
};

page.onLoadFinished = function (status) {
  if (status !== 'success') {
    phantom.exit(1);
  }

  setTimeout(render, 300);
}

page.open(url);

Increase timeout to give time for loading transitions to complete


function currentLocationMatches(action_path) {
  return $("body.gobierto_participation_" + action_path).length > 0
}

$(document).on('turbolinks:load', function() {

  // Toggle description for Process#show stages diagram
  $('.toggle_description').click(function() {
    $(this).parents('.timeline_row').toggleClass('toggled');

    $(this).find('.fa').toggleClass('fa-caret-right fa-caret-down');
    $(this).siblings('.description').toggle();
  });

  $.fn.extend({
    toggleText: function(a, b){
      return this.text(this.text() == b ? a : b);
    }
  });

  $('.button_toggle').on('click', function() {
    $('.button.hidden').toggleClass('hidden');
  })

  if (currentLocationMatches("processes_poll_answers_new")) {
    window.GobiertoParticipation.process_polls_controller.show();
  } else if (currentLocationMatches("processes_show")) {
    window.GobiertoParticipation.processes_controller.show();
  }

  // fix this to be only in the home
  window.GobiertoParticipation.poll_teaser_controller.show();
});

function currentLocationMatches(action_path) {
  return $("body.gobierto_participation_" + action_path).length > 0
}

$(document).on('turbolinks:load', function() {

  // Toggle description for Process#show stages diagram
  $('.toggle_description').click(function() {
    $(this).parents('.timeline_row').toggleClass('toggled');

    $(this).find('.fa').toggleClass('fa-caret-right fa-caret-down');
    $(this).siblings('.description').toggle();
  });

  $.fn.extend({
    toggleText: function(a, b){
      return this.text(this.text() == b ? a : b);
    }
  });

  $('.button_toggle').on('click', function() {
    $('.button.hidden').toggleClass('hidden');
  })

  if (currentLocationMatches("processes_poll_answers_new")) {
    window.GobiertoParticipation.process_polls_controller.show();
  } else if (currentLocationMatches("processes_show")) {
    window.GobiertoParticipation.processes_controller.show();
  }

  // fix this to be only in the home
  window.GobiertoParticipation.poll_teaser_controller.show();

  // Add active class to menu
  $('nav a[href="' + window.location.pathname + '"]').parent().addClass('active');
});

Add active class in participation navigation menu using JS


import initialState from './initialState';
import {
    LOGO_SPIN_STARTED,
    LOGO_SPIN_ENDED,
    PAGE_SCROLL_STARTED,
    PAGE_SCROLL_ENDED,
    APP_INIT,
    STATUS_UPDATE,
    PROVIDER_CHANGE,
    BOARD_CHANGE,

    SCROLL_HEADER,

    THREAD_REQUESTED,
    BOARD_REQUESTED,
} from '../constants';

export default function (state = initialState.status, action) {
    switch (action.type) {

        // case APP_INIT:
        //     return Object.assign({}, state, {
        //         isMainPage: true
        //     })

        case PAGE_SCROLL_STARTED:
            return Object.assign({}, state, {
                isScrolling: true,
                currentPage: action.payload
            })

        case PAGE_SCROLL_ENDED:
            return Object.assign({}, state, {
                isScrolling: false,
                currentPage: action.payload
            })

        case SCROLL_HEADER:
            return Object.assign({}, state, {
                isHeaderVisible: action.payload
            })

        case PROVIDER_CHANGE:
            return Object.assign({}, state, {
                provider: action.payload
            })

        case BOARD_REQUESTED:
            return Object.assign({}, state, {
                boardID: action.payload
            })

        case THREAD_REQUESTED:
            return Object.assign({}, state, {
                threadID: action.payload
            })

        case STATUS_UPDATE:
            return Object.assign({}, state, {
                statusMessage: action.payload
            })

        default:
            return state
    }
}

import initialState from './initialState';
import {
    LOGO_SPIN_STARTED,
    LOGO_SPIN_ENDED,
    PAGE_SCROLL_STARTED,
    PAGE_SCROLL_ENDED,
    APP_INIT,
    STATUS_UPDATE,
    PROVIDER_CHANGE,
    BOARD_CHANGE,

    SCROLL_HEADER,

    THREAD_REQUESTED,
    THREAD_DESTROYED,

    BOARD_REQUESTED,
    BOARD_DESTROYED,
} from '../constants';

export default function (state = initialState.status, action) {
    switch (action.type) {

        // case APP_INIT:
        //     return Object.assign({}, state, {
        //         isMainPage: true
        //     })

        case PAGE_SCROLL_STARTED:
            return Object.assign({}, state, {
                isScrolling: true,
                currentPage: action.payload
            })

        case PAGE_SCROLL_ENDED:
            return Object.assign({}, state, {
                isScrolling: false,
                currentPage: action.payload
            })

        case SCROLL_HEADER:
            return Object.assign({}, state, {
                isHeaderVisible: action.payload
            })

        case PROVIDER_CHANGE:
            return Object.assign({}, state, {
                provider: action.payload
            })

        case BOARD_REQUESTED:
            return Object.assign({}, state, {
                boardID: action.payload
            })

        case THREAD_REQUESTED:
            return Object.assign({}, state, {
                threadID: action.payload
            })

        case STATUS_UPDATE:
            return Object.assign({}, state, {
                alertMessage: action.payload
            })

        case THREAD_DESTROYED:
            return Object.assign({}, state, {
                threadID: null
            })

        case BOARD_DESTROYED:
            return Object.assign({}, state, {
                boardID: null
            })

        default:
            return state
    }
}

Add reducer cases for thread/board destroyed


/*
 * Copyright 2013 The Polymer Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style
 * license that can be found in the LICENSE file.
 */
/**
 * @module PointerGestures
 */
(function() {
  var thisFile = 'pointergestures.js';
  var scopeName = 'PointerGestures';
  var modules = [
    'src/PointerGestureEvent.js',
    'src/initialize.js',
    'src/sidetable.js',
    'src/pointermap.js',
    'src/dispatcher.js',
    'src/hold.js',
    'src/track.js',
    'src/flick.js',
    'src/tap.js'
  ];

  window[scopeName] = {
    entryPointName: thisFile,
    modules: modules
  };

  var script = document.querySelector('script[src $= "' + thisFile + '"]');
  var src = script.attributes.src.value;
  var basePath = src.slice(0, src.indexOf(thisFile));

  if (!window.PointerEventPolyfill) {
    document.write('<script src="' + basePath + '../PointerEvents/pointerevents.js"></script>');
  }

  if (!window.Loader) {
    var path = basePath + 'tools/loader/loader.js';
    document.write('<script src="' + path + '"></script>');
  }
  document.write('<script>Loader.load("' + scopeName + '")</script>');
})();

/*
 * Copyright 2013 The Polymer Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style
 * license that can be found in the LICENSE file.
 */
/**
 * @module PointerGestures
 */
(function() {
  var thisFile = 'pointergestures.js';
  var scopeName = 'PointerGestures';
  var modules = [
    'src/PointerGestureEvent.js',
    'src/initialize.js',
    'src/sidetable.js',
    'src/pointermap.js',
    'src/dispatcher.js',
    'src/hold.js',
    'src/track.js',
    'src/flick.js',
    'src/tap.js'
  ];

  window[scopeName] = {
    entryPointName: thisFile,
    modules: modules
  };

  var script = document.querySelector('script[src $= "' + thisFile + '"]');
  var src = script.attributes.src.value;
  var basePath = src.slice(0, src.indexOf(thisFile));

  if (!window.PointerEvent) {
    document.write('<script src="' + basePath + '../PointerEvents/pointerevents.js"></script>');
  }

  if (!window.Loader) {
    var path = basePath + 'tools/loader/loader.js';
    document.write('<script src="' + path + '"></script>');
  }
  document.write('<script>Loader.load("' + scopeName + '")</script>');
})();

Use PointerEvent to be check for loading PointerEvents polyfill


'use strict';

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

function testAutoDetection(language) {
  it('should be detected as ' + language, function() {
    var languagePath = utility.buildPath('detect', language),
        examples     = fs.readdirSync(languagePath);

    examples.forEach(function(example) {
      var filename = path.join(languagePath, example),
          content  = fs.readFileSync(filename, 'utf-8'),

          expected = language,
          actual   = hljs.highlightAuto(content).language;

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

describe('hljs', function() {
  describe('.highlightAuto', function() {
    var languages = utility.languagesList();

    languages.forEach(testAutoDetection);
  });
});

'use strict';

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

function testAutoDetection(language) {
  it('should be detected as ' + language, function() {
    var languagePath = utility.buildPath('detect', language),
        examples     = fs.readdirSync(languagePath);

    examples.forEach(function(example) {
      var filename = path.join(languagePath, example),
          content  = fs.readFileSync(filename, 'utf-8'),

          expected = language,
          actual   = hljs.highlightAuto(content).language;

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

describe('hljs', function() {
  describe('.highlightAuto', function() {
    var languages = hljs.listLanguages();

    languages.forEach(testAutoDetection);
  });
});

Use hljs.listLanguages for auto-detection tests


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

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

Remove code from Application.js to remedy errors seen in browser console


core.controller('AbstractController', function ($scope, StorageService) {

	$scope.isAssumed = function() {
		return StorageService.get("assumed");
	};

	$scope.isAssuming = function() {
		return StorageService.get("assuming");
	};

	$scope.isAnonymous = function() {
		return (sessionStorage.role == "ROLE_ANONYMOUS");
	};

	$scope.isUser = function() {
		return (sessionStorage.role == "ROLE_USER");
	};

	$scope.isAnnotator = function() {
		return (sessionStorage.role == "ROLE_ANNOTATOR");
	};

	$scope.isManager = function() {
		return (sessionStorage.role == "ROLE_MANAGER");
	};

	$scope.isAdmin = function() {
		return (sessionStorage.role == "ROLE_ADMIN");
	};

});

core.controller('AbstractController', function ($scope, StorageService) {

	$scope.storage = StorageService;

	$scope.isAssumed = function() {
		return StorageService.get("assumed");
	};

	$scope.isAssuming = function() {
		return StorageService.get("assuming");
	};

	$scope.isAnonymous = function() {
		return (sessionStorage.role == "ROLE_ANONYMOUS");
	};

	$scope.isUser = function() {
		return (sessionStorage.role == "ROLE_USER");
	};

	$scope.isAnnotator = function() {
		return (sessionStorage.role == "ROLE_ANNOTATOR");
	};

	$scope.isManager = function() {
		return (sessionStorage.role == "ROLE_MANAGER");
	};

	$scope.isAdmin = function() {
		return (sessionStorage.role == "ROLE_ADMIN");
	};

});

Put storage service on scope for abstract controller.


import React, { Component } from 'react'
import Affirmations from './analytics/Affirmations'

export default class Dashboard extends Component {
  constructor(props) {
    super(props);
    this.state = {
      data: {
        url: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/98887/d3pack-flare-short.json',
        elementDelay: 100
      },
      startDelay: 2000
    };
  }
  componentDidMount() {
    console.log('rendering');
  }
  render() {
    var style = {
      width: '100%',
      height: '100%'
    };
    return (
      <div style={style}>
        <Affirmations
          startDelay={this.state.startDelay}
          elementDelay={this.state.data.elementDelay}
          json={this.state.data.url}
        />
      </div>
    )
  }
}

import React, { Component } from 'react'
import Circles from './analytics/Circles'

export default class Dashboard extends Component {
  constructor(props) {
    super(props);
    this.state = {
      data: {
        url: './data/sample.json',
        elementDelay: 100
      },
      startDelay: 2000
    };
  }
  componentDidMount() {
    console.log('rendering');
  }
  render() {
    return (
      <div className="dashboard-container">
        <Circles
          startDelay={this.state.startDelay}
          elementDelay={this.state.data.elementDelay}
          json={this.state.data.url}
        />
      </div>
    )
  }
}

Update for circles and new sample data


// @flow

export type Config = {
  disabledPages: string[],
  showUserDomainInput: boolean,
  defaultUserDomain: string,
  showOpenstackCurrentUserSwitch: boolean,
  useBarbicanSecrets: boolean,
  requestPollTimeout: number,
  sourceOptionsProviders: string[],
  instancesListBackgroundLoading: { default: number, [string]: number },
  sourceProvidersWithExtraOptions: Array<string | { name: string, envRequiredFields: string[] }>,
  destinationProvidersWithExtraOptions: Array<string | { name: string, envRequiredFields: string[] }>,
  providerSortPriority: { [providerName: string]: number }
  hiddenUsers: string[],
}

// @flow

export type Config = {
  disabledPages: string[],
  showUserDomainInput: boolean,
  defaultUserDomain: string,
  showOpenstackCurrentUserSwitch: boolean,
  useBarbicanSecrets: boolean,
  requestPollTimeout: number,
  sourceOptionsProviders: string[],
  instancesListBackgroundLoading: { default: number, [string]: number },
  sourceProvidersWithExtraOptions: Array<string | { name: string, envRequiredFields: string[] }>,
  destinationProvidersWithExtraOptions: Array<string | { name: string, envRequiredFields: string[] }>,
  providerSortPriority: { [providerName: string]: number },
  hiddenUsers: string[],
}

Add missing comma to type file


$('body').scrollspy({ target: '#side-menu' , offset : 10});
var disableEvent = function(e) {
    e.preventDefault();
};
$('a[href="#"]').click(disableEvent);
$('button[type="submit"]').click(disableEvent);

// Initialize popovers
$('[data-toggle="popover"]').popover();
$('#openGithub').click(function(e){
    e.preventDefault();
    window.location = "https://github.com/caneruguz/osf-style";
});
$('[data-toggle="tooltip"]').tooltip();

Fix commit mistake with tooltip


angular
    .module('ngVibrant')
    .directive('vibrant', vibrant);

function vibrant($vibrant) {
    var directive = {
        restrict: 'AE',
        scope: {
            model: '=ngModel', //Model
            url: '@?',
            swatch: '@?',
            quality: '@?',
            colors: '@?'
        },
        link: link
    };

    return directive;

    function link(scope, element, attrs) {
        scope.model = [];
        if (angular.isUndefined(attrs.quality)) {
            attrs.quality = $vibrant.getDefaultQuality();
        }
        if (angular.isUndefined(attrs.colors)) {
            attrs.colors = $vibrant.getDefaultColors();
        }
        if (angular.isDefined(attrs.url)) {
            $vibrant.get(attrs.url).then(function(swatches) {
                scope.model = angular.isDefined(attrs.swatch) ? swatches[attrs.swatch] : swatches;
            });
        }else {
            element.on('load', function() {
                var swatches = $vibrant(element[0], attrs.colors, attrs.quality);
                scope.model = angular.isDefined(attrs.swatch) ? swatches[attrs.swatch] : swatches;
            });
        }
    }
}

angular
    .module('ngVibrant')
    .directive('vibrant', vibrant);

function vibrant($vibrant) {
    var directive = {
        restrict: 'AE',
        scope: {
            model: '=ngModel', //Model
            url: '@?',
            swatch: '@?',
            quality: '@?',
            colors: '@?'
        },
        link: link
    };

    return directive;

    function link(scope, element, attrs) {
        scope.model = [];
        if (angular.isUndefined(attrs.quality)) {
            attrs.quality = $vibrant.getDefaultQuality();
        }
        if (angular.isUndefined(attrs.colors)) {
            attrs.colors = $vibrant.getDefaultColors();
        }
        if (angular.isDefined(attrs.url)) {
            $vibrant.get(attrs.url, attrs.colors, attrs.quality).then(function(swatches) {
                scope.model = angular.isDefined(attrs.swatch) ? swatches[attrs.swatch] : swatches;
            });
        }else {
            element.on('load', function() {
                var swatches = $vibrant(element[0], attrs.colors, attrs.quality);
                scope.model = angular.isDefined(attrs.swatch) ? swatches[attrs.swatch] : swatches;
            });
        }
    }
}

Use colors and quality attributes on manual image fetching


const cacheName = 'cache-v4'
const precacheResources = [
  '/',
  '/posts/',
  'index.html',
  '/bundle.min.js',
  '/main.min.css',
  '/fonts/Inter-UI-Bold.woff',
  '/fonts/Inter-UI-Medium.woff',
  '/fonts/Inter-UI-Medium.woff2',
  '/fonts/Inter-UI-Italic.woff',
  '/fonts/Inter-UI-Regular.woff',
  '/fonts/Inter-UI-Italic.woff2',
  '/fonts/Inter-UI-MediumItalic.woff2',
  '/fonts/Inter-UI-MediumItalic.woff',
  '/fonts/Inter-UI-BoldItalic.woff',
  '/fonts/Inter-UI-Regular.woff2',
  '/fonts/Inter-UI-Bold.woff2',
  '/fonts/Inter-UI-BoldItalic.woff2',
  '/static/fonts/icomoon.eot',
  '/static/fonts/icomoon.svg',
  '/static/fonts/icomoon.ttf',
  '/static/fonts/icomoon.woff',
  '/posts/how-to-get-involved-in-open-source/index.html',
  '/posts/i-m-gonna-blog/index.html',
  '/posts/tools-for-effective-rust-development/index.html',
  '/posts/understanding-and-resolving-selinux-denials-on-android/index.html',
  '/posts/teaching-kotlin-kotlin-for-android-java-developers/index.html',
  '/posts/teaching-kotlin-classes-and-objects/index.html',
  '/posts/teaching-kotlin-variables/index.html'
]

self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(cacheName).then(cache => {
      return cache.addAll(precacheResources)
    })
  )
})

self.addEventListener('activate', event => {
  var cacheKeeplist = [cacheName]
  event.waitUntil(
    caches
      .keys()
      .then(keyList => {
        return Promise.all(
          keyList.map(key => {
            if (cacheKeeplist.indexOf(key) === -1) {
              return caches.delete(key)
            }
          })
        )
      })
      .then(self.clients.claim())
  )
})

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request).then(cachedResponse => {
      if (cachedResponse) {
        return cachedResponse
      }
      return fetch(event.request)
    })
  )
})

self.addEventListener('activate', event => {
  event.waitUntil(
    caches
      .keys()
      .then(keyList => {
        return Promise.all(
          keyList.map(key => {
            return caches.delete(key)
          })
        )
      })
      .then(self.clients.claim())
  )
})

navigator.serviceWorker.getRegistrations().then(registrations => {
  registrations.forEach(registration => {
    registration.unregister()
  })
})

Stop caching things and remove existing service worker registrations


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

    port: 9876,

    logLevel: config.LOG_INFO,

    autoWatch: true,
    singleRun: false,
    colors: true,

    plugins: [
      'karma-jasmine',
      'karma-sinon',
      'karma-spec-reporter',
      'karma-phantomjs-launcher',
      'karma-chrome-launcher',
      'karma-firefox-launcher',
      'karma-browserify',
      'karma-sourcemap-loader'
    ],

    browsers: process.env.CI === true ? ['PhantomJS'] : [],

    frameworks: ['jasmine', 'sinon', 'browserify'],

    reporters: ['spec'],

    files: [
      'spec/helpers/**/*.coffee',
      'spec/**/*-behavior.coffee',
      'spec/**/*-spec.coffee'
    ],

    preprocessors: {
      'src/**/*.coffee': ['browserify', 'sourcemap'],
      'spec/**/*.coffee': ['browserify', 'sourcemap']
    },

    browserify: {
      extensions: ['.coffee'],
      transform: ['coffeeify'],
      watch: true,
      debug: true
    }
  });
};

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

    port: 9876,

    logLevel: config.LOG_INFO,

    autoWatch: true,
    singleRun: false,
    colors: true,

    plugins: [
      'karma-jasmine',
      'karma-sinon',
      'karma-spec-reporter',
      'karma-phantomjs-launcher',
      'karma-chrome-launcher',
      'karma-firefox-launcher',
      'karma-browserify',
      'karma-sourcemap-loader'
    ],

    browsers: ['PhantomJS'],

    frameworks: ['jasmine', 'sinon', 'browserify'],

    reporters: ['spec'],

    files: [
      'spec/helpers/**/*.coffee',
      'spec/**/*-behavior.coffee',
      'spec/**/*-spec.coffee'
    ],

    preprocessors: {
      'src/**/*.coffee': ['browserify', 'sourcemap'],
      'spec/**/*.coffee': ['browserify', 'sourcemap']
    },

    browserify: {
      extensions: ['.coffee'],
      transform: ['coffeeify'],
      watch: true,
      debug: true
    }
  });
};

Revert "Conditionally set browser for karma"


// Depending on the exact usage, QUnit could be in one of several places, this
// function handles finding it.
module.exports = function requireQUnit( resolve = require.resolve ) {
	try {

		// First we attempt to find QUnit relative to the current working directory.
		const localQUnitPath = resolve( "qunit", { paths: [ process.cwd() ] } );
		delete require.cache[ localQUnitPath ];
		return require( localQUnitPath );
	} catch ( e ) {
		try {

			// Second, we use the globally installed QUnit
			delete require.cache[ resolve( "../../qunit/qunit" ) ];
			// eslint-disable-next-line node/no-missing-require, node/no-unpublished-require
			return require( "../../qunit/qunit" );
		} catch ( e ) {
			if ( e.code === "MODULE_NOT_FOUND" ) {

				// Finally, we use the local development version of QUnit
				delete require.cache[ resolve( "../../dist/qunit" ) ];
				// eslint-disable-next-line node/no-missing-require, node/no-unpublished-require
				return require( "../../dist/qunit" );
			}

			throw e;
		}
	}
};

// Depending on the exact usage, QUnit could be in one of several places, this
// function handles finding it.
module.exports = function requireQUnit( resolve = require.resolve ) {
	try {

		// First we attempt to find QUnit relative to the current working directory.
		const localQUnitPath = resolve( "qunit", {

			// Support: Node 10. Explicitly check "node_modules" to avoid a bug.
			// Fixed in Node 12+. See https://github.com/nodejs/node/issues/35367.
			paths: [ process.cwd() + "/node_modules", process.cwd() ]
		} );
		delete require.cache[ localQUnitPath ];
		return require( localQUnitPath );
	} catch ( e ) {
		try {

			// Second, we use the globally installed QUnit
			delete require.cache[ resolve( "../../qunit/qunit" ) ];
			// eslint-disable-next-line node/no-missing-require, node/no-unpublished-require
			return require( "../../qunit/qunit" );
		} catch ( e ) {
			if ( e.code === "MODULE_NOT_FOUND" ) {

				// Finally, we use the local development version of QUnit
				delete require.cache[ resolve( "../../dist/qunit" ) ];
				// eslint-disable-next-line node/no-missing-require, node/no-unpublished-require
				return require( "../../dist/qunit" );
			}

			throw e;
		}
	}
};

Fix 'qunit' require error on Node 10 if qunit.json exists


Router.configure({
    layoutTemplate : 'layout',
    loadingTemplate : 'loading',
    notFoundTemplate : 'notFound'
    // waitOn : function () {
    //     return Meteor.subscribe('posts');
    // }
});

Router.route('/', {name : 'landing'});
Router.route('/goalsettings', {name : 'showGoals'});
Router.route('/crylevel', {name : 'cryLevel'});

Router.route('/posts/:_id', {
    name : 'postPage',
    data : function() {
        return Posts.findOne(this.params._id);
    }
});

Router.route('/posts/:_id/edit', {
    name : 'postEdit',
    data : function () {
        return Posts.findOne(this.params._id);
    }
})

Router.route('/submit', {name : 'postSubmit'})

var requireLogin = function () {
    if (!Meteor.userId()) {
        if (Meteor.loggingIn()) {
            this.render('loadingTemplate');
        } else {
            Router.go('landing');
        }
        this.stop();
    } else {
        this.next();
    }
}

Router.onBeforeAction('dataNotFound', {only : 'postPage'});
Router.onBeforeAction(requireLogin, {except : 'landing'});

Router.configure({
    layoutTemplate : 'layout',
    loadingTemplate : 'loading',
    notFoundTemplate : 'notFound'
    // waitOn : function () {
    //     return Meteor.subscribe('posts');
    // }
});

Router.route('/', {name : 'landing'});
Router.route('/goal-settings', {name : 'showGoals'});
Router.route('/cry-level', {name : 'cryLevel'});

Router.route('/posts/:_id', {
    name : 'postPage',
    data : function() {
        return Posts.findOne(this.params._id);
    }
});

Router.route('/posts/:_id/edit', {
    name : 'postEdit',
    data : function () {
        return Posts.findOne(this.params._id);
    }
})

Router.route('/submit', {name : 'postSubmit'})

var requireLogin = function () {
    if (!Meteor.userId()) {
        if (Meteor.loggingIn()) {
            this.render('loadingTemplate');
        } else {
            Router.go('landing');
        }
        this.stop();
    } else {
        this.next();
    }
}

Router.onBeforeAction('dataNotFound', {only : 'postPage'});
Router.onBeforeAction(requireLogin, {except : 'landing'});

Add dashes to route paths


Counter = function (name, cursor, interval) {
  this.name = name;
  this.cursor = cursor;
  this.interval = interval || 1000 * 10;
  this._collectionName = 'counters-collection';
}

// every cursor must provide a collection name via this method
Counter.prototype._getCollectionName = function() {
  return "counter-" + this.name;
};

// the api to publish
Counter.prototype._publishCursor = function(sub) {
  var self = this;
  var count = self.cursor.count();
  sub.added(self._collectionName, self.name, {count: count});

  var handler = Meteor.setInterval(function() {
    var count = self.cursor.count();
    sub.changed(self._collectionName, self.name, {count: count});
  }, this.interval);

  sub.onStop(function() {
    Meteor.clearTimeout(handler);
  });
};

Counter = function (name, cursor, interval) {
  this.name = name;
  this.cursor = cursor;
  this.interval = interval || 1000 * 10;
  this._collectionName = 'counters-collection';
}

// every cursor must provide a collection name via this method
Counter.prototype._getCollectionName = function() {
  return "counter-" + this.name;
};

// the api to publish
Counter.prototype._publishCursor = function(sub) {
  var self = this;
  var count = self.cursor.count();
  sub.added(self._collectionName, self.name, {count: count});

  var handler = Meteor.setInterval(function() {
    var count = self.cursor.count();
    sub.changed(self._collectionName, self.name, {count: count});
  }, this.interval);

  sub.onStop(function() {
    Meteor.clearTimeout(handler);
  });

  return {
    stop: sub.onStop.bind(sub)
  }
};

Make _publishCursor return a handle with a stop method


var nodegit = require("../"),
    path = require("path");

// This code walks the history of the master branch and prints results
// that look very similar to calling `git log` from the command line

nodegit.Repository.open(path.resolve(__dirname, "../.git"))
  .then(function(repo) {
    return repo.getMasterCommit();
  })
  .then(function(firstCommitOnMaster){
    // History returns an event.
    var history = firstCommitOnMaster.history(nodegit.Revwalk.SORT.Time);

    // History emits "commit" event for each commit in the branch's history
    history.on("commit", function(commit) {
      console.log("commit " + commit.sha());
      console.log("Author:", commit.author().name() +
        " <" + commit.author().email() + ">");
      console.log("Date:", commit.date());
      console.log("\n    " + commit.message());
    });

    // Don't forget to call `start()`!
    history.start();
  })
  .done();

var nodegit = require("../"),
    path = require("path");

// This code walks the history of the master branch and prints results
// that look very similar to calling `git log` from the command line

nodegit.Repository.open(path.resolve(__dirname, "../.git"))
  .then(function(repo) {
    return repo.getMasterCommit();
  })
  .then(function(firstCommitOnMaster){
    // History returns an event.
    var history = firstCommitOnMaster.history(nodegit.Revwalk.SORT.TIME);

    // History emits "commit" event for each commit in the branch's history
    history.on("commit", function(commit) {
      console.log("commit " + commit.sha());
      console.log("Author:", commit.author().name() +
        " <" + commit.author().email() + ">");
      console.log("Date:", commit.date());
      console.log("\n    " + commit.message());
    });

    // Don't forget to call `start()`!
    history.start();
  })
  .done();

Fix incorrect api usage, not Time rather TIME


describe("m.withAttr()", function () {
	"use strict"

	it("calls the handler with the right value/context without callbackThis", function () { // eslint-disable-line
		var spy = sinon.spy()
		var object = {}
		m.withAttr("test", spy).call(object, {currentTarget: {test: "foo"}})
		expect(spy).to.be.calledOn(object).and.calledWith("foo")
	})

	it("calls the handler with the right value/context with callbackThis", function () { // eslint-disable-line
		var spy = sinon.spy()
		var object = {}
		m.withAttr("test", spy, object)({currentTarget: {test: "foo"}})
		expect(spy).to.be.calledOn(object).and.calledWith("Bfoo")
	})
})

describe("m.withAttr()", function () {
	"use strict"

	it("calls the handler with the right value/context without callbackThis", function () { // eslint-disable-line
		var spy = sinon.spy()
		var object = {}
		m.withAttr("test", spy).call(object, {currentTarget: {test: "foo"}})
		expect(spy).to.be.calledOn(object).and.calledWith("foo")
	})

	it("calls the handler with the right value/context with callbackThis", function () { // eslint-disable-line
		var spy = sinon.spy()
		var object = {}
		m.withAttr("test", spy, object)({currentTarget: {test: "foo"}})
		expect(spy).to.be.calledOn(object).and.calledWith("foo")
	})
})

Fix the test of the new suite


let origBot, origGuild;
// A dummy message object so ESLint doesn't complain
class Message {}
class Client {
    constructor(bot, guild) {
        origBot = bot;
        origGuild = guild;
    }

    get guilds() {
        return origBot.guilds.size
    }

    get users() {
        return origBot.users.size
    }

    waitForMessage(authorId, channelId, timeout, check) {
        if (!authorId || !channelId) return Promise.reject("Missing author/channel ID");
        if (!check || typeof check !== "function") check = () => true;
        let c = msg => {
            if (msg.author.id == authorId && msg.channel.id == channelId
                && msg.channel.guild && msg.channel.guild.id == origGuild.id
                && check()) return true;
            else return false;
        };
        origBot.waitForEvent("messageCreate", timeout, c)
            .then((eventReturn) => new Message(eventReturn[0]));
    }
}
module.exports = Client;

let origBot, origGuild;
// A dummy message object so ESLint doesn't complain
class Message {}
class Client {
    constructor(bot, guild) {
        origBot = bot;
        origGuild = guild;
        // TODO: sandboxed user
        // this.user = bot.user
        // TODO: sandboxed guild
        // this.currentGuild = guild;
    }

    get guilds() {
        return origBot.guilds.size;
    }

    get users() {
        return origBot.users.size;
    }

    waitForMessage(authorId, channelId, timeout, check) {
        if (!authorId || !channelId) return Promise.reject("Missing author/channel ID");
        if (!check || typeof check !== "function") check = () => true;
        let c = msg => {
            if (msg.author.id == authorId && msg.channel.id == channelId
                && msg.channel.guild && msg.channel.guild.id == origGuild.id
                && check()) return true;
            else return false;
        };
        origBot.waitForEvent("messageCreate", timeout, c)
            .then((eventReturn) => new Message(eventReturn[0]));
    }
}
module.exports = Client;

Add new properties, to be sandboxed


  describe('icon-toggle tests', function () {

    it('Should have MaterialIconToggle globally available', function () {
      expect(MaterialIconToggle).to.be.a('function');
    });

    it('Should be upgraded to a MaterialIconToggle successfully', function () {
      var el = document.createElement('div');
      el.innerHTML = '<input type="checkbox" class="wsk-icon-toggle__input">';
      componentHandler.upgradeElement(el, 'MaterialIconToggle');
      var upgraded = el.getAttribute('data-upgraded');
      expect(upgraded).to.contain('MaterialIconToggle');
    });
  });

  describe('icon-toggle tests', function () {

    it('Should have MaterialIconToggle globally available', function () {
      expect(MaterialIconToggle).to.be.a('function');
    });

    it('Should be upgraded to a MaterialIconToggle successfully', function () {
      var el = document.createElement('div');
      el.innerHTML = '<input type="checkbox" class="wsk-icon-toggle__input">';
      componentHandler.upgradeElement(el, 'MaterialIconToggle');
      expect($(el)).to.have.data('upgraded', ',MaterialIconToggle');
    });
  });

Update MaterialIconToggle unit test with chai-jquery


'use strict';

const _ = require('underscore');

function buildSlots(slots) {
  const res = {};
  _.each(slots, (value, key) => {
    if ( _.isString(value)) {
      res[key] = {
        name: key,
        value: value
      };
    } else {
      res[key] = {
        name: key,
        ...value
      };
    }
  });
  return res;
}

function buildSession(e) {
  return e ? e.sessionAttributes : {};
}

function init(options) {
  let isNew = true;

  // public API

  const api = {
    init,
    build
  };

  function build(intentName, slots, prevEvent) {
    if (!options.appId) throw String('AppId not specified. Please run events.init(appId) before building a Request');
    const res = { // override more stuff later as we need
      session: {
        sessionId: options.sessionId,
        application: {
          applicationId: options.appId
        },
        attributes: buildSession(prevEvent),
        user: {
          userId: options.userId,
          accessToken: options.accessToken
        },
        new: isNew
      },
      request: {
        type: 'IntentRequest',
        requestId: options.requestId,
        locale: options.locale,
        timestamp: (new Date()).toISOString(),
        intent: {
          name: intentName,
          slots: buildSlots(slots)
        }
      },
      version: '1.0'
    };
    isNew = false;
    return res;
  }

  return api;
}

module.exports = {init};

'use strict';

const _ = require('underscore');

function buildSlots(slots) {
  const res = {};
  _.each(slots, (value, key) => {
    if ( _.isString(value)) {
      res[key] = {
        name: key,
        value: value
      };
    } else {
      res[key] = {
        ...value,
        name: key
      };
    }
  });
  return res;
}

function buildSession(e) {
  return e ? e.sessionAttributes : {};
}

function init(options) {
  let isNew = true;

  // public API

  const api = {
    init,
    build
  };

  function build(intentName, slots, prevEvent) {
    if (!options.appId) throw String('AppId not specified. Please run events.init(appId) before building a Request');
    const res = { // override more stuff later as we need
      session: {
        sessionId: options.sessionId,
        application: {
          applicationId: options.appId
        },
        attributes: buildSession(prevEvent),
        user: {
          userId: options.userId,
          accessToken: options.accessToken
        },
        new: isNew
      },
      request: {
        type: 'IntentRequest',
        requestId: options.requestId,
        locale: options.locale,
        timestamp: (new Date()).toISOString(),
        intent: {
          name: intentName,
          slots: buildSlots(slots)
        }
      },
      version: '1.0'
    };
    isNew = false;
    return res;
  }

  return api;
}

module.exports = {init};

Make sure to keep the property 'name'


"use strict";
/* eslint-disable no-console */
/* eslint-disable import/default */
var chalk = require('chalk');
var app = require('../app.js'); // This initializes the Express application
var config = require('../../config.js');

var port = config.port || 5000;
var server = app.listen(port, err => {
  if (err) {
    console.log(chalk.red(err));
  } else {
    console.log(chalk.green('Server listening on port'), chalk.blue(port));
  }
});

module.exports = server;

"use strict";
/* eslint-disable no-console */
/* eslint-disable import/default */
var chalk = require('chalk');
var app = require('../app.js'); // This initializes the Express application
// var config = require('../../config.js');

var port = process.env.PORT || 5000;
var server = app.listen(port, err => {
  if (err) {
    console.log(chalk.red(err));
  } else {
    console.log(chalk.green('Server listening on port'), chalk.blue(port));
  }
});

module.exports = server;

Move npm dotenv to dependencies instead of dev-dependencies