'use strict';

const EditTransform = require('wikimedia-page-library').EditTransform;
const addHrefToEditButton = require('./addHrefToEditButton');
const domUtil = require('../../domUtil');

/**
 * Adds a page header with title, lead section edit button, and optional description and
 * button for the title pronunciation.
 * @param {!Document} doc Parsoid DOM document
 * @param {!object} meta meta object with the following properties:
 *   {!string} displayTitle the title of the page as displayed
 *   {!string} linkTitle the title of the page that can be used to link to it
 *   {boolean} descriptionEditable
 *   {?string} description
 *   {?boolean} hasPronunciation
 */
module.exports = (doc, meta) => {
    const body = doc.body;
    const headerElement = doc.createElement('header');
    body.insertBefore(headerElement, body.firstChild);
    const pronunciationUrl = meta.parsoid.meta.pronunciation && meta.parsoid.meta.pronunciation.url;

    const content = EditTransform.newEditLeadSectionHeader(doc, meta.mw.displaytitle,
        meta.mw.description, 'Add a description', meta.mw.description_source === 'central',
        true, pronunciationUrl);

    headerElement.appendChild(content);
    addHrefToEditButton(headerElement, 0, domUtil.getParsoidLinkTitle(doc));
};

'use strict';

const EditTransform = require('wikimedia-page-library').EditTransform;
const addHrefToEditButton = require('./addHrefToEditButton');
const domUtil = require('../../domUtil');

/**
 * Adds a page header with title, lead section edit button, and optional description and
 * button for the title pronunciation.
 * @param {!Document} doc Parsoid DOM document
 * @param {!object} meta meta object with the following properties:
 *   {!string} displayTitle the title of the page as displayed
 *   {!string} linkTitle the title of the page that can be used to link to it
 *   {boolean} descriptionEditable
 *   {?string} description
 *   {?boolean} hasPronunciation
 */
module.exports = (doc, meta) => {
    const body = doc.body;
    const headerElement = doc.createElement('header');
    body.insertBefore(headerElement, body.firstChild);
    const pronunciationUrl = meta.parsoid.meta.pronunciation && meta.parsoid.meta.pronunciation.url;

    const content = EditTransform.newEditLeadSectionHeader(doc, meta.mw.displaytitle,
        meta.mw.description, 'Add a description', meta.mw.description === undefined || meta.mw.description_source === 'central',
        true, pronunciationUrl);

    headerElement.appendChild(content);
    addHrefToEditButton(headerElement, 0, domUtil.getParsoidLinkTitle(doc));
};

Allow editing if title description is undefined


// It's only ES3 compatible
// You don't have a console.log, you need send a "debug"

function play(params) {
  var return_obj = {
    'debug': 'true',
    'action': 'shoot',
    'params': params
  };
  return_obj['params']['my_own'] = 'custom param to debug';
  return return_obj;
}

// It's only ES3 compatible
// This script will be compile by "execjs"
// You don't have a console.log, you need send a "debug"
// You have 5 seconds to do 3 moves and shoot in each turn.

function play(params) {
  // The params object has:
  // * name      - the name of player
  // * enemies   - array has values with the distance of enemies alive.
  //               If the value is < 0, than the enemie is to the left
  //               If the value is >= 0, than the enemie is to the right
  // * direction - if -1 than you see to the left, otherwise, you see to the right
  // * pos_x     - your position in the x axis
  // * pos_y     - your position in the y axis

  // The actions disponible:
  // * shoot      - shoot and go to next enemie
  // * aim_up     - move the aim to up angle
  // * aim_down   - move the aim to down angle
  // * walk_left  - move to the left
  // * walk_right - move to the right
  var action = 'shoot';

  var return_obj = {
    'debug': 'true',
    'action': action,
    'params': params
  };
  return_obj['params']['my_own'] = 'custom param to debug';
  return return_obj;
}

Update the CPU js file


/**
 * Created by xubt on 5/26/16.
 */

kanbanApp.directive('boardBanner', function () {
    return {
        restrict: 'E',
        templateUrl: 'component/board/partials/board-banner.html',
        replace: true,
        controller: ['$scope', '$location', 'boardsService', 'localStorageService', function ($scope, $location, boardsService, localStorageService) {
            var boardLink = localStorageService.get("boardLink");

            var boardPromise = boardsService.loadBoardByLink(boardLink);
            boardPromise.then(function (_board) {
                $scope.board = _board;
            });
            $scope.toBoards = function () {
                $location.path('/boards');
            };
        }]
    };
});

/**
 * Created by xubt on 5/26/16.
 */

kanbanApp.directive('boardBanner', function () {
    return {
        restrict: 'E',
        templateUrl: 'component/board/partials/board-banner.html',
        replace: true,
        controller: ['$scope', '$location', 'boardsService', 'localStorageService', function ($scope, $location, boardsService, localStorageService) {
            var boardLink = localStorageService.get("boardLink");
            var boardsLink = localStorageService.get("identity.userName") + '/boards';

            var boardPromise = boardsService.loadBoardByLink(boardLink);
            boardPromise.then(function (_board) {
                $scope.board = _board;
            });
            $scope.toBoards = function () {
                $location.path(boardsLink);
            };
        }]
    };
});

Load boardsLink from local storage.


/* global chrome */

chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
  var endiURL = 'http://www.elnuevodia.com';
  var startsWithURL = tab.url.slice(0, endiURL.length) === endiURL;

  if (changeInfo.status === 'loading' && startsWithURL) {
    chrome.cookies.set({
      url: 'http://www.elnuevodia.com',
      name: 'end_interstitial',
      value: 'Interstitial-General',
      expirationDate: (new Date().getTime() / 1000) + 3600
    });
  }
});

/* global chrome */

chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
  var endiURL = 'http://www.elnuevodia.com';
  var startsWithURL = tab.url.slice(0, endiURL.length) === endiURL;

  if (changeInfo.status === 'loading' && startsWithURL) {
    chrome.cookies.set({
      url: 'http://www.elnuevodia.com',
      domain: '.elnuevodia.com',
      name: 'end_interstitial',
      value: 'Interstitial-General',
      expirationDate: (new Date().getTime() / 1000) + (3600 * 24)
    });
  }
});

Add domain and increase expiration


// Used to trigger opera.isReady() functions
var deferredComponentsLoadStatus = {
  'WIDGET_API_LOADED': false,
  'WIDGET_PREFERENCES_LOADED': false
  // ...etc
};

// Events to delay until window 'load' event has been
// fired by opera.isReady() below
var delayedExecuteEvents = [
  // Example:
  // { 'target': opera.extension, 'eventName': 'message', 'eventObj': event }
];

// Used to trigger opera.isReady() functions
var deferredComponentsLoadStatus = {
  // ...etc
};

// Events to delay until window 'load' event has been
// fired by opera.isReady() below
var delayedExecuteEvents = [
  // Example:
  // { 'target': opera.extension, 'eventName': 'message', 'eventObj': event }
];

Remove defunct deferredComponentsLoadStatus items from popup load process


var spawn = require("cross-spawn");

export default function (cmd, subcmd = [], options = {}) {
    return new Promise((resolve, reject) => {
        options.maxBuffer = 1024 * 1024;
        let cp = spawn(cmd, subcmd, options);
        let stdout = [], stderr = [];
        let error = "";
        let setup = (stream, msgs) => {
            if (stream) {
                stream.on("data", buf => msgs.push(buf));
            }
        };
        setup(cp.stdout, stdout);
        setup(cp.stderr, stderr);

        cp.on("error", err => {
            error = err;
        });
        let concat = (msgs) => 0 < msgs.length ? Buffer.concat(msgs).toString() : "";
        cp.on("close", code => {
            let result = {
                error: error,
                errorcode: code,
                stdout: concat(stdout),
                stderr: concat(stderr)
            };
            if (code) {
                reject(result);
            } else {
                resolve(result);
            }
        });
    });
}

var spawn = require("cross-spawn");

export default function (cmd, subcmd = [], options = {}) {
    return new Promise((resolve, reject) => {
        options.maxBuffer = 1024 * 1024;
        let cp = spawn(cmd, subcmd, options);
        let stdout = [], stderr = [];
        let error = "";
        let setup = (stream, msgs) => {
            if (stream) {
                stream.on("data", buf => msgs.push(buf));
            }
        };
        setup(cp.stdout, stdout);
        setup(cp.stderr, stderr);

        cp.on("error", err => {
            error = err;
        });
        let concat = (msgs) => 0 < msgs.length ? Buffer.concat(msgs).toString() : "";
        cp.on("close", code => {
            let result = {
                error: error,
                errorcode: code,
                stdout: concat(stdout),
                stderr: concat(stderr)
            };
            if (code != 0 && code != 1) {
                reject(result);
            } else {
                resolve(result);
            }
        });
    });
}

Fix exit code handling in Promise.


'use strict';

angular.module('seblucas.slGridListToggle', [])
  .directive('slGridListToggle', function() {
    return {
      restrict: 'E',
      require: '^ngModel',
      scope: {
        templatePrefix: '='
      },
      template:
'
 \
  <button ng-repeat="item in toggles" type="button" class="btn btn-default" ng-class="{active: isTemplateActive(item)}" ng-click="toggleTemplate(item)"> \
     \
  </button> \

',
      link: function(scope, element, attrs, ngModel) {
        scope.toggles = ['th', 'list'];
        ngModel.$render = function() {
            scope.currentTemplate = ngModel.$modelValue;
        };

        scope.toggleTemplate = function(value) {
          scope.currentTemplate = scope.templatePrefix + value +'.html';
          ngModel.$setViewValue(scope.currentTemplate);
        };

        scope.isTemplateActive = function(value) {
          return scope.currentTemplate.indexOf (value) > 0;
        };
      }
    };
  });

'use strict';

angular.module('seblucas.slGridListToggle', [])
  .directive('slGridListToggle', function() {
    return {
      restrict: 'E',
      require: '^ngModel',
      scope: {
        templatePrefix: '='
      },
      template:
'
 \
  <button ng-repeat="item in toggles" type="button" class="btn btn-default" ng-class="{active: isTemplateActive(item)}" ng-click="toggleTemplate(item)"> \
     \
  </button> \

',
      link: function(scope, element, attrs, ngModel) {
        scope.toggles = ['th', 'list'];
        ngModel.$render = function() {
            scope.currentTemplate = ngModel.$modelValue;
        };

        scope.toggleTemplate = function(value) {
          scope.currentTemplate = scope.templatePrefix + value +'.html';
          ngModel.$setViewValue(scope.currentTemplate);
        };

        scope.isTemplateActive = function(value) {
          return scope.currentTemplate.indexOf ('.' + value + '.html') > 0;
        };
      }
    };
  });

Fix the current active template detection


/*
* ---
* HIDE CUSTOM PROFILE CSS MODULE
* ---
*
* Disables custom CSS on a profile (imagine that!)
*
* Interacts with the Page Action popup as well as the profileCSSHandler
* content script in order to keep a running list of profiles that have their
* CSS disabled (stored in Chrome's storage).
*
*/

(function () {
  'use strict';

  var headEl = document.getElementsByTagName('head')[0];
  var styleEl = headEl.getElementsByTagName('style')[0];

  // These events are fired by the profileCSSHandler content script
  window.addEventListener('disable-css', function () {
    styleEl.remove();
  });
  window.addEventListener('enable-css', function () {
    headEl.appendChild(styleEl);
  });

  // Asks the content script for the status of the current profile's css
  var requestCSSEvent = new CustomEvent('request-css-event');
  window.dispatchEvent(requestCSSEvent);
})();

/*
* ---
* HIDE CUSTOM PROFILE CSS MODULE
* ---
*
* Disables custom CSS on a profile (imagine that!)
*
* Interacts with the Page Action popup as well as the profileCSSHandler
* content script in order to keep a running list of profiles that have their
* CSS disabled (stored in Chrome's storage).
*
*/

(function () {
  'use strict';
  var $style = $("style");
  var $head = $("head");
  var $window = $(window);
  // These events are fired by the profileCSSHandler content script
  $window.on('disable-css', function () {
    $style.remove();
  });
  $window.on('enable-css', function () {
    $head.append($style);
  });

  // Asks the content script for the status of the current profile's css
  var requestCSSEvent = new CustomEvent('request-css-event');
  window.dispatchEvent(requestCSSEvent);
})();

Use jQuery 'cause it's loaded anyways


'use strict';

angular.module('yeoMeanApp')
  .controller('UmmCtrl', function ($scope, $http) {
    $scope.movieList = [];

    //Update movieList to have the same data that's in the database on the sever
    $http.get('/api/movies').success(function(movieList) {
        $scope.movieList = movieList;
    });

    $scope.addMovie = function() {
        if($scope.newMovie === ''  || $scope.newRating === '') {
            return;
        }
        $http.post('/api/movies', { name: $scope.newMovie, rating: $scope.newRating });
        $scope.newMovie = '';
        $scope.newRating = '';
        //Update movieList to have the same data that's in the database on the sever
        $http.get('/api/movies').success(function(movieList) {
            $scope.movieList = movieList;
        });
    };

    $scope.deleteMovie = function(movie) {
        $http.delete('/api/movies/' + movie._id);
        //Update movieList to have the same data that's in the database on the sever
        $http.get('/api/movies').success(function(movieList) {
            $scope.movieList = movieList;
        });
    };
  });

'use strict';

angular.module('yeoMeanApp')
  .controller('UmmCtrl', function ($scope, $http) {
    $scope.movieList = [];

    //Update movieList to have the same data that's in the database on the sever
    $http.get('/api/movies').success(function(movieList) {
        $scope.movieList = movieList;
    });

    $scope.addMovie = function() {
        if($scope.newMovie === ''  || $scope.newRating === '') {
            return;
        }
        $http.post('/api/movies', { name: $scope.newMovie, rating: $scope.newRating }).success(function(){
            //Update movieList to have the same data that's in the database on the sever
            $http.get('/api/movies').success(function(movieList) {
                $scope.movieList = movieList;
            });
            $scope.newMovie = '';
            $scope.newRating = '';
        });
    };

    $scope.deleteMovie = function(movie) {
        $http.delete('/api/movies/' + movie._id).success(function(){
            //Update movieList to have the same data that's in the database on the sever
            $http.get('/api/movies').success(function(movieList) {
                $scope.movieList = movieList;
            });
        });
    };
  });

Put the get requests for updating the light into a success to keep things safe.


// ----------------------------------------------------------------
// Content Class

class Content {
  static get TYPE_NAME_KEY() {
    return 0;
  }

  static get CONTENT() {
    return 'content';
  }

  static get ITEM() {
    return 'content-item';
  }

  static get ITEM_HEADER() {
    return 'content-item-header';
  }

  static get ITEM_NAME() {
    return 'content-item-name';
  }

  static get ITEM_KEYS() {
    return 'content-item-keys';
  }

  static get ITEM_KEY() {
    return 'content-item-key';
  }

  static createContentItem(
    _type = Content.TYPE_NAME_KEY,
    _name = null,
    ..._key = []
  ) {
    let result = '';

    return result;
  }
}

// ----------------------------------------------------------------
// Content Class

class Content {
  static get TYPE_NAME_KEY() {
    return 0;
  }

  static get CONTENT() {
    return 'content';
  }

  static get ITEM() {
    return 'content-item';
  }

  static get ITEM_HEADER() {
    return 'content-item-header';
  }

  static get ITEM_NAME() {
    return 'content-item-name';
  }

  static get ITEM_KEYS() {
    return 'content-item-keys';
  }

  static get ITEM_KEY() {
    return 'content-item-key';
  }

  static createContentItem(
    _type = Content.TYPE_NAME_KEY,
    _header = null,
    _name = null,
    ..._key = []
  ) {
    let result = '';

    return result;
  }
}

Update argument to createContentItem in Content


#!/usr/bin/env node

/*global nodeca*/

"use strict";

var app = require('nlib').Application.create({
  name: 'fontomas',
  root: __dirname
});

//
// Preset application version
//

nodeca.runtime.version = require('./package.json').version;

//
// Catch unexpected exceptions
//

process.on('uncaughtException', function (err) {
  nodeca.logger.warn('Uncaught exception');
  nodeca.logger.error(err);
});

//
// Run application
//

app.run();

#!/usr/bin/env node

/*global nodeca*/

"use strict";

var app = require('nlib').Application.create({
  name: 'fontomas',
  root: __dirname
});

//
// Preset application version
//

nodeca.runtime.version = require('./package.json').version;

//
// Catch unexpected exceptions
//

process.on('uncaughtException', function (err) {
  try {
    nodeca.logger.warn('Uncaught exception');
    nodeca.logger.error(err);
  } catch (err) {
    // THIS SHOULD NEVER-EVER-EVER HAPPEN -- THIS IS A WORST CASE SCENARIO
    // USAGE: ./fontello.js 2>/var/log/fontello-cf.log
    console.error(err.stack || err.toString());
  }
});

//
// Run application
//

app.run();

Add try/catch into unhandled exception handler


/*

       ##    #####      Copyright (c) - Kevin McGinty
     # _ #  ###
    #   #  #            AtomicFrameworks

*/

function getParam(param) {
  var query = new RegExp('[?|&]'+param+'(=)?(.*?)(&|#|$)');
  var search = document.URL.match(query);
  if (search){
    return decodeURIComponent(search[2]);
  }
}

/*

       ##    #####      Copyright (c) - Kevin McGinty
     # _ #  ###
    #   #  #            AtomicFrameworks

*/

function getParam(param) {
  if (param) {
     var query = new RegExp('[?|&]'+param+'(=)?(.*?)(&|#|$)');
     var search = document.URL.match(query);
     if (search){
       return decodeURIComponent(search[2]);
     }
  }
}

Make sure we have an arg


import React from 'react';

const style = {
  position: 'fixed',
  top: 0,
  left: 0,
  bottom: 0,
  right: 0,
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'center',
};

export default function(storyFn) {
  return
{storyFn()}
;
}

import React from 'react';

const style = {
  position: 'fixed',
  top: 0,
  left: 0,
  bottom: 0,
  right: 0,
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'center',
  overflow: 'auto',
};

const innerStyle = {
  margin: 'auto',
}

export default function(storyFn) {
  return

{storyFn()}

;
}

Fix display of large components


var express = require('express');
var router = express.Router();
var constants = require('../lib/constants')
var windspeed = require('../middleware/windspeed')

router.use(windspeed)

/* GET home page. */
router.get('/', function(req, res) {

  var statusTerm = constants.WIND_STATUS_TERMS[req.wind.status]

  res.render('index', {
  	bodyClass: "home wind-level-" + req.wind.status.level,
  	wind: req.wind,
  	status: statusTerm
  });
});

module.exports = router;

var express = require('express');
var router = express.Router();
var constants = require('../lib/constants')
var windspeed = require('../middleware/windspeed')

router.use(windspeed)

/* GET home page. */
router.get('/', function(req, res) {

  var statusTerm = constants.WIND_STATUS_TERMS[req.wind.status]

  res.render('index', {
  	bodyClass: "home wind-level-" + req.wind.status.level,
  	wind: req.wind,
  	status: statusTerm,
    location: req.location
  });
});

module.exports = router;

Fix missing template variable for Location Name


import "whatwg-fetch";
import { checkStatus, parseJSON } from "../common";

const config = {
    path: "/src/configs/nuage/configurations/",
    cachingTime: 30000, // (ms) -> default 30s
}

const fetchConfiguration = function (id, configType) {
    let url = config.path + configType + "/" + id + ".json";
    return fetch(url)
        .then(checkStatus)
        .then(parseJSON);
}

export const ConfigurationService = {
    id: "configuration",
    fetch: fetchConfiguration,
    config: config,
}

import "whatwg-fetch";
import { checkStatus, parseJSON } from "../common";

const config = {
    path: process.env.PUBLIC_URL + "/configurations/",
    cachingTime: 30000, // (ms) -> default 30s
}

const fetchConfiguration = function (id, configType) {
    let url = config.path + configType + "/" + id + ".json";

    return fetch(url)
        .then(checkStatus)
        .then(parseJSON);
}

export const ConfigurationService = {
    id: "configuration",
    fetch: fetchConfiguration,
    config: config,
}

Update service to get configuration files from public folder


export default ({title="", meta="", links="", content="", initialState={}, env={}, base_path="" }) => `

        <meta charset="utf-8">
        ${title}
        <meta name="viewport" content="width=device-width, initial-scale=1">
        ${meta}
        ${links}
        <link href="${base_path}/static/app.css" rel=stylesheet>

${content}

`;

Include fetch polyfill by default


describe("About Classes", function () {

  describe("Basic Declaration", function () {

    it("should understand base usage", function () {

      //Sugar Syntax for car.prototype
      class Car {
          constructor(make, model) {
            this.make = make;
            this.model = model;
          }

          getFullName() {
            return this.make + ' ' + this.model;
          }
      }

      let car = new Car('Ford', 'Mustang');

      expect(car instanceof Car).toEqual(FILL_ME_IN);
      expect(car instanceof Object).toEqual(FILL_ME_IN);

      expect(typeof car.getFullName).toEqual(FILL_ME_IN);
      expect(car.getFullName()).toEqual(FILL_ME_IN);

      let Bike = class {
        constructor(make, model) {
          this.make = make;
          this.model = model;
        }

        getFullName() {
          return this.make + ' ' + this.model;
        }
      }

      let bike = new Bike('Ducati', 'Monster');

      expect(typeof bike.getFullName).toEqual(FILL_ME_IN);
      expect(bike.getFullName()).toEqual(FILL_ME_IN);

    });
  });
});

describe("About Classes", function () {

  describe("Basic Declaration", function () {

    it("should understand base usage", function () {

      //Sugar Syntax for car.prototype
      class Car {
          constructor(make, model) {
            this.make = make;
            this.model = model;
          }

          getFullName() {
            return this.make + ' ' + this.model;
          }
      }

      let car = new Car('Ford', 'Mustang');

      // expect(car instanceof Car).toEqual(FILL_ME_IN);
      // expect(car instanceof Object).toEqual(FILL_ME_IN);
      //
      // expect(typeof car.getFullName).toEqual(FILL_ME_IN);
      // expect(car.getFullName()).toEqual(FILL_ME_IN);

      let Bike = class {
        constructor(make, model) {
          this.make = make;
          this.model = model;
        }

        getFullName() {
          return this.make + ' ' + this.model;
        }
      }

      let bike = new Bike('Ducati', 'Monster');

      // expect(typeof bike.getFullName).toEqual(FILL_ME_IN);
      // expect(bike.getFullName()).toEqual(FILL_ME_IN);

    });
  });

  describe('Classes as First-Class Citizens', function() {

      function constructCarClass(classObj, make, model) {
        return new classObj(make, model);
      }

      var car = constructCarClass(class {
          constructor(make, model) {
            this.make = make;
            this.model = model;
          }

          getFullName() {
            return this.make + ' ' + this.model;
          }
      }, 'Ford', 'Mustang');

      expect(car.getFullName()).toEqual(FILL_ME_IN);
  })

});

Add in first-class citizen test


(function(Modules) {
  "use strict";

  let isSixDigitHex = value => value.match(/^#[0-9A-F]{6}$/i);
  let colourOrWhite = value => isSixDigitHex(value) ? value : '#FFFFFF';

  Modules.ColourPreview = function() {

    this.start = component => {

      this.$input = $('input', component);

      $(component).append(
        this.$preview = $('')
      );

      this.$input
        .on('change keyup', this.update)
        .trigger('change');

    };

    this.update = () => this.$preview.css(
      'background', colourOrWhite(this.$input.val())
    );

  };

})(window.GOVUK.Modules);

(function(Modules) {
  "use strict";

  let isSixDigitHex = value => value.match(/^#[0-9A-F]{6}$/i);
  let colourOrWhite = value => isSixDigitHex(value) ? value : '#FFFFFF';

  Modules.ColourPreview = function() {

    this.start = component => {

      this.$input = $('input', component);

      $(component).append(
        this.$preview = $('')
      );

      this.$input
        .on('input', this.update)
        .trigger('change');

    };

    this.update = () => this.$preview.css(
      'background', colourOrWhite(this.$input.val())
    );

  };

})(window.GOVUK.Modules);

Use input event not keyup


import Users from "meteor/vulcan:users";
import { addCallback } from 'meteor/vulcan:core';

const TRUSTLEVEL1_THRESHOLD = 2000

function updateTrustedStatus ({newDocument, vote}) {

  const user = Users.findOne(newDocument.userId)
  if (user.karma >= TRUSTLEVEL1_THRESHOLD && (!Users.getGroups(user).includes('trustLevel1'))) {
    Users.update(user._id, {$push: {groups: 'trustLevel1'}});
    const updatedUser = Users.findOne(newDocument.userId)
    //eslint-disable-next-line no-console
    console.info("User gained trusted status", updatedUser.username, updatedUser._id, updatedUser.karma, updatedUser.groups)
  }
}

addCallback("votes.smallUpvote.async", updateTrustedStatus);
addCallback("votes.bigUpvote.async", updateTrustedStatus);

function maybeSendVerificationEmail (modifier, user)
{
  if(modifier.$set.whenConfirmationEmailSent
      && (!user.whenConfirmationEmailSent
          || user.whenConfirmationEmailSent.getTime() !== modifier.$set.whenConfirmationEmailSent))
  {
    Accounts.sendVerificationEmail(user._id);
  }
}

addCallback("users.edit.sync", maybeSendVerificationEmail);

import Users from "meteor/vulcan:users";
import { addCallback } from 'meteor/vulcan:core';

const TRUSTLEVEL1_THRESHOLD = 2000

function updateTrustedStatus ({newDocument, vote}) {

  const user = Users.findOne(newDocument.userId)
  if (user.karma >= TRUSTLEVEL1_THRESHOLD && (!Users.getGroups(user).includes('trustLevel1'))) {
    Users.update(user._id, {$push: {groups: 'trustLevel1'}});
    const updatedUser = Users.findOne(newDocument.userId)
    //eslint-disable-next-line no-console
    console.info("User gained trusted status", updatedUser.username, updatedUser._id, updatedUser.karma, updatedUser.groups)
  }
}

addCallback("votes.smallUpvote.async", updateTrustedStatus);
addCallback("votes.bigUpvote.async", updateTrustedStatus);

function maybeSendVerificationEmail (modifier, user)
{
  if(modifier.$set.whenConfirmationEmailSent
      && (!user.whenConfirmationEmailSent
          || user.whenConfirmationEmailSent.getTime() !== modifier.$set.whenConfirmationEmailSent.getTime()))
  {
    Accounts.sendVerificationEmail(user._id);
  }
}

addCallback("users.edit.sync", maybeSendVerificationEmail);

Fix broken test in maybeSendVerificationEmail


var gulp = require('gulp'),
    $ = require('gulp-load-plugins')(),
    browserSync = require('browser-sync'),
    reload = browserSync.reload,
    config = {
        // destinations
        tmp: '.tmp',
        app: 'app',
        dist: 'dist',
        // globs
        sass: 'app/**/*.scss',
    };

gulp.task('copy', function() {
    gulp.src('app/**/*.html')
        .pipe($.watch())
        .pipe(gulp.dest('./dist'))
});

gulp.task('sass', function() {
    return gulp.src(config.sass)
               .pipe($.plumber())
               .pipe($.sass())
               .pipe($.autoprefixer())
               /*.pipe($.minifyCss())*/
               .pipe(gulp.dest(config.tmp))
               .pipe(reload({stream: true}));
});

gulp.task('browser-sync', function() {
    return browserSync({
        server: {
            baseDir: [config.tmp, config.app]
        }
    })
});

gulp.task('watch', function() {
    $.watch({glob: config.sass, name: 'Sass'}, ['sass']);
});

gulp.task('default', ['sass', 'copy', 'watch', 'browser-sync']);

var gulp = require('gulp'),
    $ = require('gulp-load-plugins')(),
    browserSync = require('browser-sync'),
    reload = browserSync.reload,
    config = {
        // destinations
        tmp: '.tmp',
        app: 'app',
        dist: 'dist',
        // globs
        sass: 'app/**/*.scss',
    };

gulp.task('sass', function() {
    return gulp.src(config.sass)
               .pipe($.plumber())
               .pipe($.sass())
               .pipe($.autoprefixer())
               /*.pipe($.minifyCss())*/
               .pipe(gulp.dest(config.tmp))
               .pipe(reload({stream: true}));
});

gulp.task('browser-sync', function() {
    return browserSync({
        server: {
            baseDir: [config.tmp, config.app]
        }
    })
});

gulp.task('watch', function() {
    $.watch({glob: config.sass, name: 'Sass'}, ['sass']);
});

gulp.task('default', ['sass', 'watch', 'browser-sync']);

Copy task no longer in use. May come back in the future though.


var gulp = require('gulp');
var uglify = require('gulp-uglify');
var mocha = require('gulp-mocha');
var coveralls = require('gulp-coveralls');
var rename = require('gulp-rename');
var lint = require('gulp-jshint');

gulp.task('build', function() {
    return gulp.src('src/*.js')
        .pipe(uglify())
        .pipe(rename(function(path) {
            path.extname = ".min.js";
        }))
        .pipe(gulp.dest('build'));
});

gulp.task('test', function() {
    return gulp.src('spec/*.js', {read: false})
        .pipe(mocha({reporter: 'nyan'}));
});

gulp.task('coveralls', function() {
    return gulp.src('./coverage/lcov.info')
        .pipe(coveralls());
});

gulp.task('lint', function() {
    return gulp.src('src/*.js')
        .pipe(lint())
	.pipe(lint.reporter('jshint-stylish'));
});

gulp.task('watch', function() {
    gulp.watch('src/*.js', ['build', 'lint', 'test']);
    gulp.watch('spec/*.js', ['test']);
});

gulp.task('default', ['build', 'lint', 'test', 'coveralls']);

var gulp = require('gulp');
var uglify = require('gulp-uglify');
var mocha = require('gulp-mocha');
var coveralls = require('gulp-coveralls');
var rename = require('gulp-rename');
var lint = require('gulp-jshint');

gulp.task('build', function() {
    return gulp.src('src/*.js')
        .pipe(uglify())
        .pipe(rename(function(path) {
            path.extname = ".min.js";
        }))
        .pipe(gulp.dest('build'));
});

gulp.task('upload', function() {
    return gulp.src('src/*.js')
        .pipe(uglify())
        .pipe(rename(function(path) {
            path.extname = ".min.js";
        }))
        .pipe(gulp.dest('../chezstov.github.io'));
});

gulp.task('test', function() {
    return gulp.src('spec/*.js', {read: false})
        .pipe(mocha({reporter: 'nyan'}));
});

gulp.task('coveralls', function() {
    return gulp.src('./coverage/lcov.info')
        .pipe(coveralls());
});

gulp.task('lint', function() {
    return gulp.src('src/*.js')
        .pipe(lint())
	.pipe(lint.reporter('jshint-stylish'));
});

gulp.task('watch', function() {
    gulp.watch('src/*.js', ['build', 'upload', 'lint', 'test']);
    gulp.watch('spec/*.js', ['test']);
});

gulp.task('default', ['build', 'lint', 'test', 'coveralls']);

Add update build task for gulp


require('babel-register')({
  only: 'node_modules/grrr-gulpfile'
});
require('./gulpfile.babel.js');

Fix babel problems in node modules


function recipeSearch(input) {
  $(".drop-down").remove();
  $.ajax({
    url: "/recipes",
    data: input,
  }).done(function(recipeItemPartial) {
    $(recipeItemPartial).appendTo(".main-searchbar");
  });
}

var throttledSearch = _.throttle(recipeSearch, 300);

$( document ).ready(function() {
    // console.log( "ready!" );
    $(".form-control").keyup(function(event){
      if (event.keyCode != 13) {
        // console.log(event.keyCode);
        event.preventDefault();
        var input = $(this).serialize();
        throttledSearch(input);
      }
    });

    $("mark").on("mouseover", function(){
      $("div").removeClass("hovered-term");
      text = $(this).text();
      termCard = $(".terms-box").find("#"+text);
      termCard.addClass("hovered-term");
    });

});

function recipeSearch(input) {
  $.ajax({
    url: "/recipes",
    data: input,
  }).done(function(recipeItemPartial) {
    $(recipeItemPartial).appendTo(".main-searchbar");
  });
}

var throttledSearch = _.throttle(recipeSearch, 300);

$( document ).ready(function() {
    $(".form-control").keyup(function(event){
        $(".drop-down").remove();
        event.preventDefault();
        var input = $(this).serialize();
        if ($(this).val().length > 1) {
          throttledSearch(input);
        }
    });

    $("mark").on("mouseover", function(){
      $("div").removeClass("hovered-term");
      text = $(this).text();
      termCard = $(".terms-box").find("#"+text);
      termCard.addClass("hovered-term");
    });

});

Fix search bar, not render if fewer 1 or 0 characters


'use strict'
/*
 * App config for production.
 */

module.exports = {
  environment: 'production',
  APIBaseURL: 'https://ifc.ds.io/',
  dataHubURL: 'https://ifc.ds.io/',
  dataHubOrganization: 'offgrid-market-opportunities',
  basePath: '/ifc-demand-tool/'
}

'use strict'
/*
 * App config for production.
 */

module.exports = {
  environment: 'production',
  APIBaseURL: 'https://ifc.ds.io/',
  dataHubURL: 'https://ifc.ds.io/',
  dataHubOrganization: 'offgrid-market-opportunities',
  basePath: '/market-opportunities/'
}

Update basepath to new repo


import CompositeClosureHelper from '../../../Common/Core/CompositeClosureHelper';

// ----------------------------------------------------------------------------
// Annotation Store Provider
// ----------------------------------------------------------------------------

function annotationStoreProvider(publicAPI, model) {
  if (!model.annotationStore) {
    model.annotationStore = {};
  }

  publicAPI.getStoreAnnotationNames = () => {
    const val = Object.keys(model.annotationStore);
    val.sort();
    return val;
  };

  publicAPI.getStoreAnnotation = (name) => model.annotationStore[name];

  publicAPI.getStoreAnnotations = () => model.annotationStore;

  publicAPI.setStoreAnnotation = (name, annotation) => {
    model.annotationStore[name] = annotation;
    publicAPI.fireStoreAnnotationChange(name, annotation);
  };
}

// ----------------------------------------------------------------------------
// Object factory
// ----------------------------------------------------------------------------

const DEFAULT_VALUES = {
  // annotationStore: null,
};

// ----------------------------------------------------------------------------

export function extend(publicAPI, model, initialValues = {}) {
  Object.assign(model, DEFAULT_VALUES, initialValues);

  CompositeClosureHelper.destroy(publicAPI, model);
  CompositeClosureHelper.isA(publicAPI, model, 'AnnotationStoreProvider');
  CompositeClosureHelper.event(publicAPI, model, 'StoreAnnotationChange');

  annotationStoreProvider(publicAPI, model);
}

// ----------------------------------------------------------------------------

export const newInstance = CompositeClosureHelper.newInstance(extend);

// ----------------------------------------------------------------------------

export default { newInstance, extend };

import CompositeClosureHelper from '../../../Common/Core/CompositeClosureHelper';

// ----------------------------------------------------------------------------
// Annotation Store Provider
// ----------------------------------------------------------------------------

function annotationStoreProvider(publicAPI, model) {
  if (!model.annotationStore) {
    model.annotationStore = {};
  }

  publicAPI.getStoredAnnotationNames = () => {
    const val = Object.keys(model.annotationStore);
    val.sort();
    return val;
  };

  publicAPI.getStoredAnnotation = (name) => model.annotationStore[name];

  publicAPI.getStoredAnnotations = () => model.annotationStore;

  publicAPI.setStoredAnnotation = (name, annotation) => {
    model.annotationStore[name] = annotation;
    publicAPI.fireStoreAnnotationChange(name, annotation);
  };

  publicAPI.deleteStoredAnnotation = name => {
    delete model.annotationStore[name];
    publicAPI.fireStoreAnnotationChange(name);
  };
}

// ----------------------------------------------------------------------------
// Object factory
// ----------------------------------------------------------------------------

const DEFAULT_VALUES = {
  // annotationStore: null,
};

// ----------------------------------------------------------------------------

export function extend(publicAPI, model, initialValues = {}) {
  Object.assign(model, DEFAULT_VALUES, initialValues);

  CompositeClosureHelper.destroy(publicAPI, model);
  CompositeClosureHelper.isA(publicAPI, model, 'AnnotationStoreProvider');
  CompositeClosureHelper.event(publicAPI, model, 'StoreAnnotationChange');

  annotationStoreProvider(publicAPI, model);
}

// ----------------------------------------------------------------------------

export const newInstance = CompositeClosureHelper.newInstance(extend);

// ----------------------------------------------------------------------------

export default { newInstance, extend };

Add a delete method, and also improve method names on publicAPI


#!/usr/bin/env node

var argv = require('minimist')(process.argv.slice(2));
var injection = require('./services/injection');

var port = argv.port || process.env.PORT || 9080;
var gitUrl = argv.git || process.env.GIT_URL;
var dbUrl = argv.db || process.env.DB_URL;

if (!gitUrl || !dbUrl) {
    console.log('Usage: allcountjs --git <Application Git URL> --db <Application MongoDB URL> -port [Application HTTP port]');
    process.exit(0);
}

require('./allcount-server.js');
injection.bindFactory('port', port);
injection.bindFactory('dbUrl', dbUrl);
if (dbUrl.indexOf('postgres') !== -1) {
    injection.bindFactory('storageDriver', require('./services/sql-storage-driver'));
    injection.bindFactory('dbClient', 'pg');
}
injection.bindFactory('gitRepoUrl', gitUrl);

injection.installModulesFromPackageJson("package.json");

var server = injection.inject('allcountServerStartup');
server.startup(function (errors) {
    if (errors) {
        throw new Error(errors.join('\n'));
    }
});

#!/usr/bin/env node

var argv = require('minimist')(process.argv.slice(2));
var injection = require('./services/injection');
var _ = require('lodash');

var port = argv.port || process.env.PORT || 9080;
var gitUrl = argv.git || process.env.GIT_URL;
var dbUrl = argv.db || process.env.DB_URL;

if (!gitUrl || !dbUrl) {
    console.log('Usage: allcountjs --git <Application Git URL> --db <Application MongoDB URL> -port [Application HTTP port]');
    process.exit(0);
}

require('./allcount-server.js');
injection.bindFactory('port', port);
injection.bindFactory('dbUrl', dbUrl);
if (dbUrl.indexOf('postgres') !== -1) {
    injection.bindFactory('storageDriver', require('./services/sql-storage-driver'));
    injection.bindFactory('dbClient', 'pg');
}
injection.bindFactory('gitRepoUrl', gitUrl);

injection.installModulesFromPackageJson("package.json");

var server = injection.inject('allcountServerStartup');
server.startup(function (errors) {
    if (errors) {
        if (_.isObject(errors)) {
            throw new Error(errors);
        } else {
            throw new Error(errors.join('\n'));
        }
    }
});

Fix exception when one error is thrown during startup


(function() {

Discourse.Dialect.inlineBetween({
    start: '\[yuml\]',
    stop: '\[\/yuml\]',
    rawContents: true,
    emitter: function(contents) {
        var uri = "http://yuml.me/diagram/scruffy/class/" + encodeURIComponent(contents.replace(/\n/g, ","));
        return "<img src=\"" + uri + "\" />";
    }
})

})();

(function() {

Discourse.Dialect.inlineBetween({
    start: '[yuml]',
    stop: '[/yuml]',
    rawContents: true,
    emitter: function(contents) {
        var uri = "http://yuml.me/diagram/scruffy/class/" + encodeURIComponent(contents.replace(/\n/g, ","));
        return "<img src=\"" + uri + "\" />";
    }
})

})();

Remove regex pattern from start and stop


var gulp = require('gulp');
var apigen = require('gulp-apigen');
var phpunit = require('gulp-phpunit');
var spawn = require('child_process').spawn;
var jasmine = require('gulp-jasmine');

gulp.task('apigen', function() {
  gulp.src('apigen.neon').pipe(apigen('./vendor/bin/apigen'));
});

gulp.task('phpunit', function() {
  gulp.src('phpunit.xml').pipe(phpunit('./vendor/bin/phpunit'));
});

gulp.task('phpcs', function () {
  spawn('vendor/bin/phpcs', [], {stdio: 'inherit'});
});

gulp.task('php', ['phpcs','apigen','phpunit'], function () {
});

gulp.task('jasmine', function() {
    gulp.src('test/js/*Spec.js')
        .pipe(jasmine());
});

gulp.task('default', function() {
  // place code for your default task here
});

var gulp = require('gulp');
var apigen = require('gulp-apigen');
var phpunit = require('gulp-phpunit');
var spawn = require('child_process').spawn;
var jasmine = require('gulp-jasmine');
var cover = require('gulp-coverage');

gulp.task('apigen', function() {
  gulp.src('apigen.neon').pipe(apigen('./vendor/bin/apigen'));
});

gulp.task('phpunit', function() {
  gulp.src('phpunit.xml').pipe(phpunit('./vendor/bin/phpunit'));
});

gulp.task('phpcs', function () {
  spawn('vendor/bin/phpcs', [], {stdio: 'inherit'});
});

gulp.task('php', ['phpcs','apigen','phpunit'], function () {
});

gulp.task('jasmine', function() {
    gulp.src('test/js/*Spec.js')
        .pipe(cover.instrument({
            pattern: ['src/webroot/js/*.js']//,
//            debugDirectory: 'debug'
        }))
        .pipe(jasmine())
        .pipe(cover.gather())
        .pipe(cover.format())
        .pipe(gulp.dest('test/js/cover'));;
});

gulp.task('default', function() {
  // place code for your default task here
});

Adjust jasmine task to generate coverage report


'use strict';

var gulp = require('gulp'),
    browserify = require('browserify'),
    transform = require('vinyl-transform'),
    uglify = require('gulp-uglify'),
    sourcemaps = require('gulp-sourcemaps'),
    jasmine = require('gulp-jasmine');

gulp.task('default', ['javascript']);

gulp.task('javascript', function () {
    var browserified = transform(function (filename) {
        var b = browserify({ entries: filename, debug: true });
        return b.bundle();
    });

    return gulp.src('./src/js/bowler.js')
        .pipe(browserified)
        .pipe(sourcemaps.init({ loadMaps: true }))
        .pipe(uglify())
        .pipe(sourcemaps.write('./'))
        .pipe(gulp.dest('./dist/js'));
});

gulp.task('test', function () {
    var browserified = transform(function (filename) {
        var b = browserify({ entries: filename, debug: true });
        return b.bundle();
    });

    return gulp.src('./spec/**/*.js')
        .pipe(browserified)
        .pipe(jasmine());
});

'use strict';

var gulp = require('gulp'),
    browserify = require('browserify'),
    transform = require('vinyl-transform'),
    uglify = require('gulp-uglify'),
    sourcemaps = require('gulp-sourcemaps'),
    jasmine = require('gulp-jasmine');

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

gulp.task('watch', function () {
    gulp.watch('./src/js/**/*.js', ['test']);
});

gulp.task('build', function () {
    var browserified = transform(function (filename) {
        var b = browserify({ entries: filename, debug: true });
        return b.bundle();
    });

    return gulp.src('./src/js/bowler.js')
        .pipe(browserified)
        .pipe(sourcemaps.init({ loadMaps: true }))
        .pipe(uglify())
        .pipe(sourcemaps.write('./'))
        .pipe(gulp.dest('./dist/js'));
});

gulp.task('test', ['build'], function () {
    return gulp.src('./spec/**/*.js')
        .pipe(jasmine());
});

Add gulp watcher Rejigged gulp tasks to remove repeated code to run browserify


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

gulp.task('default', function() {
  browserSync.init({
    server: {
      baseDir: "./app"
    }
  });
});

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

gulp.task('default', function() {
  browserSync.init({
    server: {
      baseDir: "./app",
      routes: {
        "/lib": "bower_components"
      }
    }
  });
});

Add bower_components path as lib to browser-sync


var gulp = require('gulp');

var script_name = "xut.js";

gulp.task('download-deps', function (callback) {
  var tsd = require('gulp-tsd');
  tsd({
        command: 'reinstall',
        config: './tsd.json'
    }, callback);
});

gulp.task('compilets', function () {
    var ts = require('gulp-typescript');
    var concat = require('gulp-concat');
    var sourcemaps = require('gulp-sourcemaps');

    var tsresult = gulp.src('script/**/*.ts')
         .pipe(sourcemaps.init())
         .pipe(ts({
            target: 'ES5',
            sortOutput: true
         }));

    return tsresult.js
        .pipe(concat(script_name))
        .pipe(sourcemaps.write())
        .pipe(gulp.dest('build'));
});

gulp.watch('script/**/*.ts', ['compilets']);

gulp.task('default', ['compilets']);

var gulp = require('gulp');

gulp.task('download-deps', function (callback) {
  var tsd = require('gulp-tsd');
  tsd({
        command: 'reinstall',
        config: './tsd.json'
    }, callback);
});

var compileTypescript = function(scriptname, projectdir) {
    var ts = require('gulp-typescript');
    var concat = require('gulp-concat');
    var sourcemaps = require('gulp-sourcemaps');

    var tsresult = gulp.src(projectdir + '/**/*.ts')
         .pipe(sourcemaps.init())
         .pipe(ts({
            target: 'ES5',
            sortOutput: true
         }));

    return tsresult.js
        .pipe(concat(scriptname))
        .pipe(sourcemaps.write())
        .pipe(gulp.dest('build'));
}

gulp.task('compilets-script', function () {
  compileTypescript("xut.js", 'script');
});

gulp.watch('script/**/*.ts', ['compilets-script']);

gulp.task('compilets-angular', function () {
  compileTypescript("xut-angular.js", 'webapp/angular');
});

gulp.watch('webapp/angular/**/*.ts', ['compilets-script']);

gulp.task('default', ['compilets-script', 'compilets-angular']);

Add autogenerate code for angular app


'use strict';

const gulp = require('gulp'),

      config = require('./gulp/config.json');

let tasks = Object.keys(config.task),
    defaultTasks = tasks.filter(taskName => {
        return config.task[taskName].default;
    });

tasks.map(file => {
    require('./gulp/tasks/' + file + '.js');
});

gulp.task('default', defaultTasks);

'use strict';

const gulp = require('gulp'),

      config = require('./gulp/config.json');

let tasks = Object.keys(config.task),
    defaultTasks = tasks.filter(taskName => {
        return config.task[taskName].default;
    });

tasks.map(file => {
    let path = `./gulp/tasks/${file}.js`;

    require(path);
});

gulp.task('default', defaultTasks);

Put argument values to variables


const gulp = require('gulp');
const gulp_ts = require('gulp-typescript');
const gulp_tslint = require('gulp-tslint');
const gulp_sourcemaps = require('gulp-sourcemaps');
const tslint = require('tslint');
const del = require('del');
const path = require('path');

const project = gulp_ts.createProject('tsconfig.json');
const linter = tslint.Linter.createProgram('tsconfig.json');

gulp.task('default', ['lint', 'build']);

gulp.task('lint', () => {
	gulp.src('./src/**/*.ts')
		.pipe(gulp_tslint({
			configuration: 'tslint.json',
			formatter: 'prose',
			program: linter
		}))
		.pipe(gulp_tslint.report());
})

gulp.task('build', () => {
	del.sync(['./bin/**/*.*']);
	const tsCompile = gulp.src('./src/**/*.ts')
		.pipe(gulp_sourcemaps.init())
		.pipe(project());

	gulp.src('./src/**/*.js')
		.pipe(gulp.dest('bin/'));

	gulp.src('./src/**/*.json')
		.pipe(gulp.dest('bin/'));

	return tsCompile.js
		.pipe(gulp_sourcemaps.write({
			sourceRoot: file => path.relative(path.join(file.cwd, file.path), file.base)
		}))
		.pipe(gulp.dest('bin/'));
});

const gulp = require('gulp');
const gulp_ts = require('gulp-typescript');
const gulp_tslint = require('gulp-tslint');
const gulp_sourcemaps = require('gulp-sourcemaps');
const tslint = require('tslint');
const del = require('del');
const path = require('path');

const project = gulp_ts.createProject('tsconfig.json');
const linter = tslint.Linter.createProgram('tsconfig.json');

gulp.task('default', ['lint', 'build']);

gulp.task('lint', () => {
	gulp.src('./src/**/*.ts')
		.pipe(gulp_tslint({
			configuration: 'tslint.json',
			formatter: 'prose',
			program: linter
		}))
		.pipe(gulp_tslint.report());
})

gulp.task('build', () => {
	del.sync(['./bin/**/*.*']);
	const tsCompile = gulp.src('./src/**/*.ts')
		.pipe(gulp_sourcemaps.init())
		.pipe(project());

	tsCompile.pipe(gulp.dest('bin/'));

	gulp.src('./src/**/*.js')
		.pipe(gulp.dest('bin/'));

	gulp.src('./src/**/*.json')
		.pipe(gulp.dest('bin/'));

	return tsCompile.js
		.pipe(gulp_sourcemaps.write({
			sourceRoot: file => path.relative(path.join(file.cwd, file.path), file.base)
		}))
		.pipe(gulp.dest('bin/'));
});

Fix definition files not generating


'use strict';

var gulp = require('gulp');
var eslint = require('gulp-eslint');
var htmlExtract = require('gulp-html-extract');
var stylelint = require('gulp-stylelint');

gulp.task('lint', ['lint:js', 'lint:html', 'lint:css']);

gulp.task('lint:js', function() {
  return gulp.src([
    '*.js',
    'test/*.js'
  ])
  .pipe(eslint())
  .pipe(eslint.format())
  .pipe(eslint.failAfterError());
});

gulp.task('lint:html', function() {
  return gulp.src([
    '*.html',
    'demo/*.html',
    'test/*.html'
  ])
  .pipe(htmlExtract({
    sel: 'script, code-example code'
  }))
  .pipe(eslint())
  .pipe(eslint.format())
  .pipe(eslint.failAfterError());
});

gulp.task('lint:css', function() {
  return gulp.src([
    '*.html',
    'demo/*.html',
    'test/*.html'
  ])
  .pipe(htmlExtract({
    sel: 'style'
  }))
  .pipe(stylelint({
    reporters: [
      {formatter: 'string', console: true}
    ]
  }));
});

'use strict';

var gulp = require('gulp');
var eslint = require('gulp-eslint');
var htmlExtract = require('gulp-html-extract');
var stylelint = require('gulp-stylelint');

gulp.task('lint', ['lint:js', 'lint:html', 'lint:css']);

gulp.task('lint:js', function() {
  return gulp.src([
    '*.js',
    'test/**/*.js'
  ])
  .pipe(eslint())
  .pipe(eslint.format())
  .pipe(eslint.failAfterError());
});

gulp.task('lint:html', function() {
  return gulp.src([
    '*.html',
    'demo/**/*.html',
    'test/**/*.html'
  ])
  .pipe(htmlExtract({
    sel: 'script, code-example code'
  }))
  .pipe(eslint())
  .pipe(eslint.format())
  .pipe(eslint.failAfterError());
});

gulp.task('lint:css', function() {
  return gulp.src([
    '*.html',
    'demo/**/*.html',
    'test/**/*.html'
  ])
  .pipe(htmlExtract({
    sel: 'style'
  }))
  .pipe(stylelint({
    reporters: [
      {formatter: 'string', console: true}
    ]
  }));
});

Include subfolders of `demo` and `test` folders for linting


var gulp = require('gulp');
var sourcemaps = require('gulp-sourcemaps');
var stylus = require('gulp-stylus');
var rename = require('gulp-rename');
var nib = require('nib');
var cssnano = require('gulp-cssnano');

var mainStyl = './styl/main.styl';

gulp.task('build:dev', function() {
    gulp.src(mainStyl)
        .pipe(sourcemaps.init())
        .pipe(stylus({ use: nib() }))
        .pipe(sourcemaps.write())
        .pipe(rename('baremetal.css'))
        .pipe(gulp.dest('./build/development'));
});

gulp.task('build:prod', function() {
    gulp.src(mainStyl)
        .pipe(stylus({ use: nib() }))
        .pipe(cssnano())
        .pipe(rename('baremetal.min.css'))
        .pipe(gulp.dest('./build/production'));
    gulp.src(mainStyl)
        .pipe(stylus({ use: nib() }))
        .pipe(rename('baremetal.css'))
        .pipe(gulp.dest('./build/production'));
});

gulp.task('stylus:watch', function() {
    gulp.watch('./styl/**/*.styl', ['build:dev']);
});

gulp.task('watch', ['build:dev', 'stylus:watch']);

gulp.task('default', ['build:dev', 'build:prod']);

var gulp = require('gulp');
var sourcemaps = require('gulp-sourcemaps');
var stylus = require('gulp-stylus');
var rename = require('gulp-rename');
var nib = require('nib');
var cssnano = require('gulp-cssnano');

var mainStyl = './styl/main.styl';

gulp.task('build:dev', function() {
    gulp.src(mainStyl)
        .pipe(sourcemaps.init())
        .pipe(stylus({ use: nib() }))
        .pipe(sourcemaps.write())
        .pipe(rename('baremetal.css'))
        .pipe(gulp.dest('./build/development'));
});

gulp.task('build:prod', function() {
    gulp.src(mainStyl)
        .pipe(stylus({ use: nib() }))
        .pipe(cssnano({ discardComments: false }))
        .pipe(rename('baremetal.min.css'))
        .pipe(gulp.dest('./build/production'));
    gulp.src(mainStyl)
        .pipe(stylus({ use: nib() }))
        .pipe(rename('baremetal.css'))
        .pipe(gulp.dest('./build/production'));
});

gulp.task('stylus:watch', function() {
    gulp.watch('./styl/**/*.styl', ['build:dev']);
});

gulp.task('watch', ['build:dev', 'stylus:watch']);

gulp.task('default', ['build:dev', 'build:prod']);

Disable comment stripping within cssnano


'use strict';

// Utils
const { print, logError } = require('../../utils/log');

// Bot
const bot = require('../../bot');

// DB
const middlewareHandler = async ({ chat, message }, next) => {
	process.env.NODE_ENV === 'development' && message && print(message);
	if (
		message &&
		message.text &&
		/^\/\w+/.test(message.text) &&
		chat.type !== 'private'
	) {
		try {
			await bot.telegram.deleteMessage(chat.id, message.message_id);
		} catch (err) {
			logError(err);
		}
	}
	return next();
};

module.exports = middlewareHandler;

'use strict';

// Utils
const { print, logError } = require('../../utils/log');

// Bot
const bot = require('../../bot');

// DB
const middlewareHandler = async ({ chat, message }, next) => {
	process.env.NODE_ENV === 'development' && message && print(message);
	if (
		message &&
		message.text &&
		message.entities[0].type !== 'code' &&
		/^\/\w+/.test(message.text) &&
		chat.type !== 'private'
	) {
		try {
			await bot.telegram.deleteMessage(chat.id, message.message_id);
		} catch (err) {
			logError(err);
		}
	}
	return next();
};

module.exports = middlewareHandler;

Fix deletion of code-formatted commands


// @flow

import {AppRegistry, YellowBox} from 'react-native'
import App from './app'

YellowBox.ignoreWarnings(['Invalid prop `containerTagName`'])

AppRegistry.registerComponent('AllAboutOlaf', () => App)

// @flow

import {AppRegistry, YellowBox} from 'react-native'
import App from './app'

YellowBox.ignoreWarnings([
	'Failed prop type: Invalid prop `containerTagName` of type `object` supplied to `ReactMarkdown`, expected `function`',
])

AppRegistry.registerComponent('AllAboutOlaf', () => App)

Update ignored string to be more specific


import request from "browser-request";

const url = "http://beta.accesslint.com/api/v1/reports";

export default function (message) {
  let violations = message.violations;

  if (violations.length > 0) {
    request({
      method: "POST",
      url: url,
      json: message,
    }, function() {});

    console.error(message);
    throw new Error(`AccessLintError: ${violations.length} violations.`);
  }
}

import request from "browser-request";

const url = "http://beta.accesslint.com/api/v1/reports";

export default function (message) {
  const violations = message.violations;

  if (violations.length > 0) {
    let descriptions = [];

    violations.map((violation) => {
      descriptions.push(violation.help);
    });

    request({
      method: "POST",
      url: url,
      json: message,
    }, function() {});

    throw new Error(
      `AccessLintError: ${violations.length} violations:` + "\n" +
      descriptions.join(",\n")
    );
  }
}

Add descriptions for error message


var babel = require('babel-jest'),
    jsPath = /.*\/react\-seed\/src\/js(?:\/[a-z]+)?(?:\/__tests__)?\/[a-zA-Z]+(?:\-test)?\.jsx?$/,
    lessPath = /.*\/react\-seed\/src\/less\/[a-z]+\/[a-zA-Z]+\.less$/;

module.exports = {

    process: function(src, filename) {

        var dummyLessModule = 'module.exports = {dummy: true};';

        if (jsPath.test(filename)) {

            src = src.replace(/^.*require\(\s*'styles\/\w+\/\w+\.less'\s*\);/g, '');
            src = src.replace(/^\s*import\s+'styles\/\w+\/\w+\.less'\s*;/g, '');

            return babel.process(src, filename);
        } else if (lessPath.test(filename)) {
            return dummyLessModule;
        } else {
            return src;
        }
    }
};

var babel = require('babel-jest'),
    jsPath = /.*\/react\-seed\/src\/js(?:\/[a-z]+)?(?:\/__tests__)?\/[a-zA-Z]+(?:\-test)?\.jsx?$/,
    lessPath = /.*\/react\-seed\/src\/less\/[a-z]+\/[a-zA-Z]+\.less$/;

module.exports = {

    process: function(src, filename) {

        var dummyLessModule = 'module.exports = {dummy: true};';

        if (jsPath.test(filename)) {

            src = src.replace(/^.*require\(\s*'styles(?:\/\w+)?\/\w+\.less'\s*\);/g, '');
            src = src.replace(/^\s*import\s+'styles(?:\/\w+)?\/\w+\.less'\s*;/g, '');

            return babel.process(src, filename);
        } else if (lessPath.test(filename)) {
            return dummyLessModule;
        } else {
            return src;
        }
    }
};

Fix regex so that it even passes for App.jsx


"use strict";

var Cache = require('./Cache'),
    inherits = require('inherits');

var MemCache = function () {
    this._cache = { };
    this._timestamps = { };
    this._expireTime = 1000 * 60 * 10;
};

inherits(MemCache, Cache);

MemCache.prototype.setData = function (tag, data) {
    if (tag)
        this.timestamps[tag] = getCurrentTimestamp();
};

MemCache.prototype.getData = function (tag) {
    if (!tag)
        return null;
    if (getCurrentTimestamp() - this.timestamps[tag] < this.expireTime)
        return this.cache[tag];
    return null;
};

MemCache.prototype.expire = function (tag) {
    if (tag) {
        this.cache[tag] = null;
        this.timestamps[tag] = null;
    }
};

MemCache.prototype.expireAll = function () {
    this.cache = [];
    this.timestamps = [];
};

MemCache.prototype.setExpireTime = function (milliseconds) {
    this.expireTime = milliseconds;
};

function getCurrentTimestamp() {
    return Date.now();
}

module.exports = MemCache;

"use strict";

var Cache = require('./Cache'),
    inherits = require('inherits');

var MemCache = function () {
    this._cache = { };
    this._timestamps = { };
    this._expireTime = 1000 * 60 * 10;
};

inherits(MemCache, Cache);

MemCache.prototype.setData = function (tag, data) {
    if (tag) {
        this._timestamps[tag] = getCurrentTimestamp();
        this._cache = data;
    }
};

MemCache.prototype.getData = function (tag) {
    if (!tag)
        return null;
    if (getCurrentTimestamp() - this._timestamps[tag] < this._expireTime)
        return this._cache[tag];
    return null;
};

MemCache.prototype.expire = function (tag) {
    if (tag) {
        this._cache[tag] = null;
        this._timestamps[tag] = null;
    }
};

MemCache.prototype.expireAll = function () {
    this._cache = [];
    this._timestamps = [];
};

MemCache.prototype.setExpireTime = function (milliseconds) {
    this._expireTime = milliseconds;
};

function getCurrentTimestamp() {
    return Date.now();
}

module.exports = MemCache;

Edit field encapsulation for cache


import "bootstrap/dist/css/bootstrap.css"; //Bootstrap
import "./index.css"; //NWB Styles
import "./theme.css"; //Cayman Theme
import "../../node_modules/highlight.js/styles/tomorrow-night.css";

import React from "react";
import { render } from "react-dom";
import { BrowserRouter as Browser } from "react-router-dom";

import App from "./App";

render(
  <Browser>
    <App />
  </Browser>,
  document.querySelector("#app")
);

import "bootstrap/dist/css/bootstrap.css"; //Bootstrap
import "./index.css"; //NWB Styles
import "./theme.css"; //Cayman Theme
import "../../node_modules/highlight.js/styles/tomorrow-night.css";

import injectTapEventPlugin from 'react-tap-event-plugin';
injectTapEventPlugin();

import React from "react";
import { render } from "react-dom";
import { BrowserRouter as Browser } from "react-router-dom";

import App from "./App";

render(
  <Browser>
    <App />
  </Browser>,
  document.querySelector("#app")
);

Add injectTapEventPlugin to prevent console error about onTapTouch


const MotifulClient = require('./structures/MotifulClient');
const CommandController = require('./controllers/CommandController');
const DiscordAPIError = require('discord.js/src/rest/DiscordAPIError');

const client = new MotifulClient();
const controller = new CommandController();

client
	.once('ready', () => {
		client.user.setStatus('invisible');
		client.logger.info(`motiful ready! (As ${client.user.tag})`);
	})
	.on('message', message => controller.handleCommand(message))
	.login(client.config.token);

process.on('unhandledRejection', err => {
	if (!err) return;

	const errorString = 'Uncaught Promise Error:\n';
	if (err instanceof DiscordAPIError) {
		return client.logger.error(`${errorString}${err.code} - ${err.message}`);
	}

	if (err.method && err.path && err.text) {
		return client.logger.error(`${errorString}${err.method}: ${err.path}\n${err.text}`);
	}

	return client.logger.error(errorString + err.stack);
});

const MotifulClient = require('./structures/MotifulClient');
const CommandController = require('./controllers/CommandController');
const DiscordAPIError = require('discord.js/src/rest/DiscordAPIError');

const client = new MotifulClient({
	disabledEvents: ['GUILD_SYNC', 'GUILD_CREATE', 'GUILD_DELETE', 'GUILD_UPDATE', 'GUILD_MEMBER_ADD', 'GUILD_MEMBER_REMOVE', 'GUILD_MEMBER_UPDATE',
		'GUILD_MEMBERS_CHUNK', 'GUILD_ROLE_CREATE', 'GUILD_ROLE_DELETE', 'GUILD_ROLE_UPDATE', 'GUILD_BAN_ADD', 'GUILD_BAN_REMOVE', 'CHANNEL_CREATE',
		'CHANNEL_DELETE', 'CHANNEL_UPDATE', 'CHANNEL_PINS_UPDATE', 'USER_UPDATE', 'USER_NOTE_UPDATE', 'USER_SETTINGS_UPDATE', 'PRESENCE_UPDATE',
		'VOICE_STATE_UPDATE', 'TYPING_START', 'VOICE_SERVER_UPDATE', 'RELATIONSHIP_ADD', 'RELATIONSHIP_REMOVE']
});
const controller = new CommandController();

client
	.once('ready', () => {
		client.user.setStatus('invisible');
		client.logger.info(`motiful ready! (As ${client.user.tag})`);
	})
	.on('message', message => controller.handleCommand(message))
	.login(client.config.token);

process.on('unhandledRejection', err => {
	if (!err) return;

	const errorString = 'Uncaught Promise Error:\n';
	if (err instanceof DiscordAPIError) {
		return client.logger.error(`${errorString}${err.code} - ${err.message}`);
	}

	if (err.method && err.path && err.text) {
		return client.logger.error(`${errorString}${err.method}: ${err.path}\n${err.text}`);
	}

	return client.logger.error(errorString + err.stack);
});

Disable a lot of unneeded events to save RAM


import React, { Component } from 'react';
import { connect } from 'react-redux'
import { push } from 'connected-react-router';

import { selectors } from '@openchemistry/redux'
import { calculations, molecules } from '@openchemistry/redux'

import { isNil } from 'lodash-es';

import Calculations from '../components/calculations';

class CalculationsContainer extends Component {

  componentDidMount() {
    this.props.dispatch(molecules.loadMolecules());
    this.props.dispatch(calculations.loadCalculations());
  }

  onOpen = (id) => {
    this.props.dispatch(push(`/calculations/${id}?mo=homo`));
  }

  render() {
    const { calculations, molecules } = this.props;
    if (isNil(calculations)) {
      return null;
    }
    return (
      <Calculations calculations={calculations} molecules={molecules} onOpen={this.onOpen} />
    );
  }
}

CalculationsContainer.propTypes = {
}

CalculationsContainer.defaultProps = {
}

function mapStateToProps(state, ownProps) {
  let calculations = selectors.calculations.getCalculations(state);
  let molecules = selectors.molecules.getMoleculesById(state);
  return { calculations, molecules };
}

export default connect(mapStateToProps)(CalculationsContainer)

import React, { Component } from 'react';
import { connect } from 'react-redux'
import { push } from 'connected-react-router';

import { selectors } from '@openchemistry/redux'
import { calculations, molecules } from '@openchemistry/redux'

import { isNil } from 'lodash-es';

import Calculations from '../components/calculations';

class CalculationsContainer extends Component {

  componentDidMount() {
    const options = { limit: 25, offset: 0, sort: '_id', sortdir: -1 }
    this.props.dispatch(molecules.loadMolecules(options));
    this.props.dispatch(calculations.loadCalculations(options));
  }

  onOpen = (id) => {
    this.props.dispatch(push(`/calculations/${id}?mo=homo`));
  }

  render() {
    const { calculations, molecules } = this.props;
    if (isNil(calculations)) {
      return null;
    }
    return (
      <Calculations calculations={calculations} molecules={molecules} onOpen={this.onOpen} />
    );
  }
}

CalculationsContainer.propTypes = {
}

CalculationsContainer.defaultProps = {
}

function mapStateToProps(state, ownProps) {
  let calculations = selectors.calculations.getCalculations(state);
  let molecules = selectors.molecules.getMoleculesById(state);
  return { calculations, molecules };
}

export default connect(mapStateToProps)(CalculationsContainer)

Add example of using sorting options for calcs


'use strict';
// Import Wit.Ai
const botlogic = require('./botlogic/botlogic.js');
const facebook = require('./channels/facebook.js');

var messageReceived;

module.exports = {
    receivedMessage: function(message) {
        receivedMessage(message);
    }
};

/*
*   Fonction appelée par les channels lorsque l'on reçoit un message
*/
function receivedMessage(message) {
    /*  Traitement
    */
    messageReceived = message; // Variable globale
    runLogicLayer(message);
}

/*
*   Fonction qu lance la couche logique avec NLP
*/
function runLogicLayer(message) {
    botlogic.defineCallback(callbackLogicLayer);
    if(message.sender != undefined && message.text != undefined) {
        botlogic.sendMessage(message.text, `session-${message.sender}`, {});
    }
}

/*
*   Fonction de callback appelée par la couche logique
*/
function callbackLogicLayer(request, response){
    messageReceived.text = response.text
    console.log('Custom callback Wit : ', JSON.stringify(response));
    console.log('Message to send : ', JSON.stringify(messageReceived));
    sendMessage(messageReceived);
}

/*
*   Fonction qui appelle la fonction d'envoie de message
*/
function sendMessage(message) {
    if(message.channel == "Facebook") {
        facebook.sendMessage(message);
    }
}

'use strict';
// Import Wit.Ai
const botlogic = require('./botlogic/botlogic.js');
var facebook = require('./channels/facebook.js');

var messageReceived;

module.exports = {
    receivedMessage: function(message) {
        receivedMessage(message);
    }
};

/*
*   Fonction appelée par les channels lorsque l'on reçoit un message
*/
function receivedMessage(message) {
    /*  Traitement
    */
    messageReceived = message; // Variable globale
    runLogicLayer(message);
}

/*
*   Fonction qu lance la couche logique avec NLP
*/
function runLogicLayer(message) {
    botlogic.defineCallback(callbackLogicLayer);
    if(message.sender != undefined && message.text != undefined) {
        botlogic.sendMessage(message.text, `session-${message.sender}`, {});
    }
}

/*
*   Fonction de callback appelée par la couche logique
*/
function callbackLogicLayer(request, response){
    messageReceived.text = response.text
    console.log('Custom callback Wit : ', JSON.stringify(response));
    console.log('Message to send : ', JSON.stringify(messageReceived));
    sendMessage(messageReceived);
}

/*
*   Fonction qui appelle la fonction d'envoie de message
*/
function sendMessage(message) {
    if(message.channel == "Facebook") {
        facebook.sendMessage(message);
    }
}

Test en passant d'un const en var


/**
 * Japanese translation for foundation-datepicker
 * Norio Suzuki <https://github.com/suzuki/>
 */
;(function($){
	$.fn.fdatepicker.dates['ja'] = {
	    days: ["日曜日", "月曜日", "火曜日", "水曜日", "木曜日", "金曜日", "土曜日"],
	    daysShort: ["日", "月", "火", "水", "木", "金", "土"],
	    daysMin: ["日", "月", "火", "水", "木", "金", "土"],
	    months: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"],
	    monthsShort: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"],
	    today: "今日",
	    format: "yyyy/mm/dd",
	    titleFormat: "yyyy年 MM"
	};
}(jQuery));

Cut out the extra character


var path = require('path');

module.exports = function (start, opts) {
    var modules = opts.moduleDirectory || 'node_modules';
    var splitRe = process.platform === 'win32' ? /[\/\\]/ : /\/+/;
    var parts = start.split(splitRe);

    var dirs = [];
    for (var i = parts.length - 1; i >= 0; i--) {
        if (parts[i] === modules) continue;
        var dir = path.join(
            path.join.apply(path, parts.slice(0, i + 1)),
            modules
        );
        if (!parts[0].match(/([A-Za-z]:)/)) {
            dir = '/' + dir;
        }
        dirs.push(dir);
    }
    return dirs.concat(opts.paths);
}

var path = require('path');

module.exports = function (start, opts) {
    var modules = opts.moduleDirectory || 'node_modules';
    var prefix = '/';
    if (/^([A-Za-z]:)/.test(start)) {
        prefix = '';
    } else if (/^\\\\/.test(start)) {
        prefix = '\\\\';
    }
    var splitRe = process.platform === 'win32' ? /[\/\\]/ : /\/+/;
    var parts = start.split(splitRe);

    var dirs = [];
    for (var i = parts.length - 1; i >= 0; i--) {
        if (parts[i] === modules) continue;
        var dir = path.join(
            path.join.apply(path, parts.slice(0, i + 1)),
            modules
        );
        dirs.push(prefix + dir);
    }
    return dirs.concat(opts.paths);
}

Fix prefix for windows azure


/* eslint-disable global-require, import/no-dynamic-require */

import React, { Component, PropTypes } from 'react'

export default class ComponentDoc extends Component {
  static propTypes = {
    path: PropTypes.string.isRequired,
  }

  state = {
    component: null,
    docs: null,
  }

  componentWillMount() {
    require.ensure([], () => {
      const docsContext = require.context('!!docs!../src', true, /^((?!test(\.js|$)|\.scss$).)*$/)
      const componentContext = require.context('../src', true, /^((?!test(\.js|$)|\.scss$).)*$/)

      this.setState({
        component: componentContext(`./${this.props.path}`),
        docs: docsContext(`./${this.props.path}`),
      })
    })
  }

  render() {
    const { component, docs } = this.state
    const loaded = docs && component

    if (!loaded) {
      return
Loading...

    }

    return (

{component.displayName}

{JSON.stringify(docs, null, 2)}

    )
  }
}

/* eslint-disable global-require, import/no-dynamic-require */

import React, { Component, PropTypes } from 'react'

export default class ComponentDoc extends Component {
  static propTypes = {
    path: PropTypes.string.isRequired,
  }

  state = {
    component: null,
    docs: null,
  }

  componentWillMount() {
    require.ensure([], () => {
      const docsContext = require.context('!!docs!../src', true, /^((?!test(\.js|$)).)*\.js$/)
      const componentContext = require.context('../src', true, /^((?!test(\.js|$)).)*\.js$/)

      this.setState({
        component: componentContext(`./${this.props.path}.js`),
        docs: docsContext(`./${this.props.path}.js`),
      })
    })
  }

  render() {
    const { component, docs } = this.state
    const loaded = docs && component

    if (!loaded) {
      return
Loading...

    }

    return (

{component.displayName}

{JSON.stringify(docs, null, 2)}

    )
  }
}

Load only js files by default


import { resolve } from 'path'
import { node } from 'execa'

const childProcessHelperPath = resolve(__dirname, 'childProcessHelper.js')

export default class ChildProcessRunner {
  #env = null
  #functionKey = null
  #handlerName = null
  #handlerPath = null
  #timeout = null

  constructor(funOptions, env) {
    const { functionKey, handlerName, handlerPath, timeout } = funOptions

    this.#env = env
    this.#functionKey = functionKey
    this.#handlerName = handlerName
    this.#handlerPath = handlerPath
    this.#timeout = timeout
  }

  // no-op
  // () => void
  cleanup() {}

  async run(event, context) {
    const childProcess = node(
      childProcessHelperPath,
      [this.#functionKey, this.#handlerName, this.#handlerPath],
      {
        env: this.#env,
      },
    )

    childProcess.send({
      context,
      event,
      timeout: this.#timeout,
    })

    const message = new Promise((_resolve) => {
      childProcess.on('message', _resolve)
      // TODO
      // on error? on exit? ..
    })

    let result

    try {
      result = await message
    } catch (err) {
      // TODO
      console.log(err)

      throw err
    }

    return result
  }
}

import { resolve } from 'path'
import { node } from 'execa'

const childProcessHelperPath = resolve(__dirname, 'childProcessHelper.js')

export default class ChildProcessRunner {
  #env = null
  #functionKey = null
  #handlerName = null
  #handlerPath = null
  #timeout = null

  constructor(funOptions, env) {
    const { functionKey, handlerName, handlerPath, timeout } = funOptions

    this.#env = env
    this.#functionKey = functionKey
    this.#handlerName = handlerName
    this.#handlerPath = handlerPath
    this.#timeout = timeout
  }

  // no-op
  // () => void
  cleanup() {}

  async run(event, context) {
    const childProcess = node(
      childProcessHelperPath,
      [this.#functionKey, this.#handlerName, this.#handlerPath],
      {
        env: this.#env,
        stdio: 'inherit',
      },
    )

    childProcess.send({
      context,
      event,
      timeout: this.#timeout,
    })

    const message = new Promise((_resolve) => {
      childProcess.on('message', _resolve)
      // TODO
      // on error? on exit? ..
    })

    let result

    try {
      result = await message
    } catch (err) {
      // TODO
      console.log(err)

      throw err
    }

    return result
  }
}

Fix for child process running


var iframeID = 1408059618092;

function hideMenus() {
	var iframe = document.getElementById(iframeID);
	iframe.hidden = true;
}

var iframeID = 1408059618092;

function hideMenus() {
	var iframe = document.getElementById(iframeID);
	iframe.hidden = true;
	console.log("Menu hidden");
}

Add console message that describes behavior


export const defaultOptions = {
    acornOption: {
        ecmaVersion: 6,
        // sourceType: 'script' or 'module'
        // 'module' is used for ES6 modules and
        // 'use strict' assumed for those modules.
        // This option is also used by the analyzer.
        sourceType: 'script'
    },
    // At the start of program and each function,
    // check 'use strict'
    detectUseStrict: true
};

export const defaultOptions = {
    acornOption: {
        ecmaVersion: 6,
        // sourceType: 'script' or 'module'
        // 'module' is used for ES6 modules and
        // 'use strict' assumed for those modules.
        // This option is also used by the analyzer.
        sourceType: 'script'
    },
    // At the start of program and each function,
    // check 'use strict'
    // maybe we do not need this option
    detectUseStrict: true
};

Add comment for probably unnecessary option


'use strict';
var GameDataCreator = require('../gamedata');
var PointView = require('../prefabs/pointview');

function Editor() {}
Editor.prototype = {
  create: function() {
    this.game.data = new GameDataCreator.GameData();
    var background = this.game.add.sprite(0, 0, 'background');
    this.sprites = this.game.add.group();
    background.inputEnabled = true;
    background.events.onInputDown.add(this.clickListener, this);
  },
  update: function() {
  },
  clickListener: function(sprite, pointer) {
    var newPoint = new GameDataCreator.GamePoint(Math.floor(pointer.x),  Math.floor(pointer.y), 'unvisited');
    console.log(newPoint);
    this.game.data.points.push(newPoint);
    var pointSprite = new PointView(this.game, newPoint);
    this.game.add.existing(pointSprite);
    this.sprites.add(pointSprite);
    console.log(Math.floor(pointer.x) + ' ' + Math.floor(pointer.y));
    console.log(this.game.data.points);
    var textArea = document.getElementById('outputJSON');
    textArea.value = JSON.stringify(this.game.data);
  }
};

module.exports = Editor;

'use strict';
var GameDataCreator = require('../gamedata');
var PointView = require('../prefabs/pointview');

function Editor() {}
Editor.prototype = {
  create: function() {
    this.game.data = new GameDataCreator.GameData();
    var background = this.game.add.sprite(0, 0, 'background');
    this.sprites = this.game.add.group();
    background.inputEnabled = true;
    background.events.onInputDown.add(this.clickListener, this);
  },
  update: function() {
  },
  clickListener: function(sprite, pointer) {
    var newPoint = new GameDataCreator.GamePoint(Math.floor(pointer.x),  Math.floor(pointer.y), 'unvisited');
    console.log(newPoint);
    this.game.data.points.push(newPoint);
    var pointSprite = new PointView(this.game, newPoint);
    this.game.add.existing(pointSprite);
    this.sprites.add(pointSprite);
    console.log(Math.floor(pointer.x) + ' ' + Math.floor(pointer.y));
    console.log(this.game.data.points);
    var textArea = window.document.getElementById('outputJSON');
    textArea.value = JSON.stringify(this.game.data, null, 2);
  }
};

module.exports = Editor;

Format displayed JSON more nicely in text area


var mongoose     = require('mongoose');
var Schema       = mongoose.Schema;

var Objectid = mongoose.Schema.Types.ObjectId;

var GuestSchema   = new Schema({
	name: String,
	organisation: String,
	location_id: Objectid,
	email: { type: String, index: true },
	mobile: String,
	invited_by: [ Objectid ],
	created_date: { type: Date, default: Date.now },
	visiting_date: Date,
});

GuestSchema.set("_perms", {
	admin: "crud",
	user: "cr",
});

module.exports = mongoose.model('Guest', GuestSchema);

var mongoose     = require('mongoose');
var Schema       = mongoose.Schema;

var Objectid = mongoose.Schema.Types.ObjectId;

var GuestSchema   = new Schema({
	name: { type: String, index: true },
	organisation: String,
	location_id: Objectid,
	email: { type: String, index: true },
	mobile: String,
	invited_by: [ Objectid ],
	created_date: { type: Date, default: Date.now },
	visiting_date: Date,
	source: String,
	mac: [ mongoose.Schema.Types.Mixed ],
	ip: [ mongoose.Schema.Types.Mixed ]
});

GuestSchema.set("_perms", {
	admin: "crud",
	user: "cr",
});

module.exports = mongoose.model('Guest', GuestSchema);

Add some wifi options to the guest model


import Mn from 'backbone.marionette';

var OverlayView = Mn.ItemView.extend({
  className: 'overlay-view hide',

  // The OverlayView doesn't need a template: it's simply an element
  // that covers up the entirety of the application
  template: false,

  // Convert click DOM events into BB Events
  events: {
    click: '_handleClick'
  },

  // Shows the overlay. Then, when it's clicked, close it.
  display() {
    this.$el.removeClass('hide');
    this.once('click', this.hide);
  },

  hide() {
    // Ensure that the click event has been unregistered
    this.off('click');
    this.$el.addClass('hide');
    this.trigger('hide');
  },

  // Emit the `click` event if the overlay is clicked directly.
  // Otherwise, emit a `click:child` event.
  _handleClick(e) {
    var clickedSelf = e.target === e.currentTarget;
    var eventName = self ? 'click' : 'click:child';
    this.trigger(eventName);
  }
});

Mn.OverlayView = OverlayView;

export default OverlayView;

import Mn from 'backbone.marionette';

var OverlayView = Mn.ItemView.extend({
  className: 'overlay-view hide',

  // The OverlayView doesn't need a template: it's simply an element
  // that covers up the entirety of the application
  template: false,

  // Convert click DOM events into BB Events
  events: {
    click: '_handleClick'
  },

  isDisplaying() {
    return this._isDisplaying;
  }

  // Shows the overlay. Then, when it's clicked, close it.
  display() {
    if (this._isDisplaying) { return; }
    this._isDisplaying = true;
    this.$el.removeClass('hide');
    this.once('click', this.hide);
    this.trigger('display');
  },

  hide() {
    if (!this._isDisplaying) { return; }
    this._isDisplaying = false;
    this.$el.addClass('hide');
    // Ensure that the click event has been unregistered
    this.off('click');
    this.trigger('hide');
  },

  // Whether or not the overlayView is displayed
  _isDisplaying: false,

  // Emit the `click` event if the overlay is clicked directly.
  // Otherwise, emit a `click:child` event.
  _handleClick(e) {
    var clickedSelf = e.target === e.currentTarget;
    var eventName = self ? 'click' : 'click:child';
    this.trigger(eventName);
  }
});

Mn.OverlayView = OverlayView;

export default OverlayView;

Add isDisplaying API; add internal displaying property.


// @flow
export function roundValue(value: number | string | void): number | string {
    if (typeof value !== 'number') {
        return '—';
    }
    const parsedValue = parseFloat(value.toString());
    const sizes = ['', ' K', ' M', ' G', ' T', ' P', ' E', ' Z', ' Y'];
    if (parsedValue === 0) {
        return '0';
    }
    let x = 0;
    while (Math.pow(1000, x + 1) < Math.abs(parsedValue)) {
        x++;
    }
    let prefix = (parsedValue / Math.pow(1000, x)).toFixed(2).toString();
    if (x === 0) {
        prefix = value.toFixed(2).toString();
    }
    let tailToCut = 0;
    while (prefix[prefix.length - (tailToCut + 1)] === '0') {
        tailToCut++;
    }
    if (prefix[prefix.length - (tailToCut + 1)] === '.') {
        tailToCut++;
    }
    return prefix.substring(0, prefix.length - tailToCut) + (sizes[x] || '');
}

export function getJSONContent(data: { [key: string]: any }): string {
    return 'data:text/plain;charset=utf-8,' + encodeURIComponent(JSON.stringify(data, null, 2));
}

// @flow
export function roundValue(value: number | string | void, placeholder: boolean | void): number | string {
    if (typeof value !== 'number') {
        return placeholder ? '—' : '';
    }
    const parsedValue = parseFloat(value.toString());
    const sizes = ['', ' K', ' M', ' G', ' T', ' P', ' E', ' Z', ' Y'];
    if (parsedValue === 0) {
        return '0';
    }
    let x = 0;
    while (Math.pow(1000, x + 1) < Math.abs(parsedValue)) {
        x++;
    }
    let prefix = (parsedValue / Math.pow(1000, x)).toFixed(2).toString();
    if (x === 0) {
        prefix = value.toFixed(2).toString();
    }
    let tailToCut = 0;
    while (prefix[prefix.length - (tailToCut + 1)] === '0') {
        tailToCut++;
    }
    if (prefix[prefix.length - (tailToCut + 1)] === '.') {
        tailToCut++;
    }
    return prefix.substring(0, prefix.length - tailToCut) + (sizes[x] || '');
}

export function getJSONContent(data: { [key: string]: any }): string {
    return 'data:text/plain;charset=utf-8,' + encodeURIComponent(JSON.stringify(data, null, 2));
}

Add custom placeholder for empty value


var _     = require('lodash')
var chalk = require('chalk')

var helpers = {

  // Pick off the attributes from only the current layer of the object.
  skim: function(data) {
    return _.map(data, function(obj) {
      return _.pick(obj, _.keys(obj));
    });
  },

  // Check if the first argument is an array, otherwise
  // uses all arguments as an array.
  normalizeArr: function() {
    var args = new Array(arguments.length);
    for (var i = 0; i < args.length; i++) {
      args[i] = arguments[i];
    }
    if (Array.isArray(args[0])) {
      return args[0];
    }
    return args;
  },

  error: function(msg) {
    console.log(chalk.red('Knex:Error ' + msg))
  },

  // Used to signify deprecated functionality.
  deprecate: function(method, alternate) {
    helpers.warn(method + ' is deprecated, please use ' + alternate);
  },

  // Used to warn about incorrect use, without error'ing
  warn: function(msg) {
    console.log(chalk.yellow("Knex:warning - " + msg))
  },

  exit: function(msg) {
    console.log(chalk.red(msg))
    process.exit()
  }

};

module.exports = helpers;

var _     = require('lodash')
var chalk = require('chalk')

var helpers = {

  // Pick off the attributes from only the current layer of the object.
  skim: function(data) {
    return _.map(data, function(obj) {
      return _.pick(obj, _.keys(obj));
    });
  },

  // Check if the first argument is an array, otherwise
  // uses all arguments as an array.
  normalizeArr: function() {
    var args = new Array(arguments.length);
    for (var i = 0; i < args.length; i++) {
      args[i] = arguments[i];
    }
    if (Array.isArray(args[0])) {
      return args[0];
    }
    return args;
  },

  error: function(msg) {
    console.log(chalk.red('Knex:Error ' + msg))
  },

  // Used to signify deprecated functionality.
  deprecate: function(method, alternate) {
    helpers.warn(method + ' is deprecated, please use ' + alternate);
  },

  // Used to warn about incorrect use, without error'ing
  warn: function(msg) {
    console.log(chalk.yellow("Knex:warning - " + msg))
  },

  exit: function(msg) {
    console.log(chalk.red(msg))
    process.exit(1)
  }

};

module.exports = helpers;

Fix calling process.exit with "ok" exit code


/* global ERRORS */

import * as generatedErrors from '../generated-errors/index';
import ChromiumNetError from './ChromiumNetError';

const errors = ERRORS;

const errorCodeMap = {};
errors.forEach((error) => {
  errorCodeMap[error.code] = generatedErrors[error.name];
});

export function getErrors() {
  return [...errors];
}

export function createByCode(code) {
  const Err = errorCodeMap[code];
  if (!Err) { return new ChromiumNetError(); }

  return new Err();
}

/* global ERRORS */

import * as generatedErrors from '../generated-errors/index';
import ChromiumNetError from './ChromiumNetError';

export class UnknownError extends ChromiumNetError {
  constructor(...args) {
    super(...args);

    this.name = 'UnknownError';
    this.message = 'Unknown error';
  }
}

const errors = ERRORS;

const errorCodeMap = {};
errors.forEach((error) => {
  errorCodeMap[error.code] = generatedErrors[error.name];
});

export function getErrors() {
  return [...errors];
}

export function createByCode(code) {
  const Err = errorCodeMap[code];
  if (!Err) { return new UnknownError(); }

  return new Err();
}

Return UnknownError if unknown error code


'use strict';

var async = require('async');
var qs = require('querystring');

/**
 * Fetch documents and populate their `document_type` and `provider` keys
 * @param {Object} [params] GET parameters to pass to `/documents` (e.g. search)
 * @param {Function} cb(err, document)
 */
module.exports = function getDocumentWithInfo(params, finalCb) {
  if(!finalCb) {
    finalCb = params;
  }

  var pages = {
    '/documents': params,
    '/document_types': {},
    '/providers': {}
  };

  var self = this;
  async.waterfall([
    function makeBatchCall(cb) {
      self.batch(pages, cb);
    },

    function handleResults(res, cb) {
      var docs = res.body['/documents?' + qs.stringify(params)];
      var providers = res.body['/providers'];
      var documentTypes = res.body['/document_types'];

      docs.data.forEach(function(doc) {
        doc.provider = providers[doc.provider];
        doc.document_type = documentTypes[doc.document_type];
      });

      cb(null, docs);
    }
  ], finalCb);
};

'use strict';

var async = require('async');
var qs = require('querystring');

/**
 * Fetch documents and populate their `document_type` and `provider` keys
 * @param {Object} [params] GET parameters to pass to `/documents` (e.g. search)
 * @param {Function} cb(err, document)
 */
module.exports = function getDocumentsWithInfo(params, finalCb) {
  if(!finalCb) {
    finalCb = params;
    params = {};
  }

  var pages = {
    '/documents': params,
    '/document_types': {},
    '/providers': {}
  };

  var self = this;
  async.waterfall([
    function makeBatchCall(cb) {
      self.batch(pages, cb);
    },

    function handleResults(res, cb) {
      var query = '';
      if(Object.keys(params).length > 0) {
        query = '?' + qs.stringify(params);
      }

      var docs = res.body['/documents' + query];
      var providers = res.body['/providers'];
      var documentTypes = res.body['/document_types'];

      docs.data.forEach(function(doc) {
        doc.provider = providers[doc.provider];
        doc.document_type = documentTypes[doc.document_type];
      });

      cb(null, docs);
    }
  ], finalCb);
};

Fix `getDocumentsWithInfo` not allowing to ommit parameters


/* Collapse remember
 * Use a cookie to "remember" the state of bootstrap collapse divs
 * use the 'collapse-remember' class to get this functionality
 */
$(document).ready(function () {
    const state_cookie_c = 'collapse-remember';
    var page = window.location.pathname;
    var state = Cookies.getJSON(state_cookie_c);
    if (state === undefined) {
        state = {};
        Cookies.set(state_cookie_c, state);
    }
    if (state[page] === undefined) {
        state[page] = {};
        Cookies.set(state_cookie_c, state);
    }
    Object.keys(state[page]).forEach(function (collapse_id) {
        if (state[page][collapse_id]) {
            console.log('showing: '+collapse_id);
            $('#'+collapse_id).addClass('show');
        }
    });
    $(".collapse-remember").on('shown.bs.collapse', function () {
        var id = $(this).attr('id');
        console.log('Shown: ' + id);
        state = Cookies.getJSON(state_cookie_c);
        state[page][id] = true;
        Cookies.set(state_cookie_c, state);
    });
    $(".collapse-remember").on('hidden.bs.collapse', function () {
        var id = $(this).attr('id');
        console.log('Hidden: ' + id);
        state = Cookies.getJSON(state_cookie_c);
        state[page][id] = false;
        Cookies.set(state_cookie_c, state);
    });
});

/* Collapse remember
 * Use a cookie to "remember" the state of bootstrap collapse divs
 * use the 'collapse-remember' class to get this functionality
 */
$(document).ready(function () {
    const state_cookie_c = 'collapse-remember';
    var page = window.location.pathname;
    var state = Cookies.getJSON(state_cookie_c);
    if (state === undefined) {
        state = {};
        Cookies.set(state_cookie_c, state);
    }
    if (state.hasOwnProperty(page)) {
        Object.keys(state[page]).forEach(function (collapse_id) {
            if (state[page][collapse_id]) {
                console.log('showing: '+collapse_id);
                $('#'+collapse_id).addClass('show');
            }
        });
    }
    $(".collapse-remember").on('shown.bs.collapse', function () {
        state = Cookies.getJSON(state_cookie_c);
        if (state[page] === undefined) {
            state[page] = {};
        }
        var id = $(this).attr('id');
        console.log('Shown: ' + id);
        state[page][id] = true;
        Cookies.set(state_cookie_c, state);
    });
    $(".collapse-remember").on('hidden.bs.collapse', function () {
        state = Cookies.getJSON(state_cookie_c);
        if (state[page] === undefined) {
            state[page] = {};
        }
        var id = $(this).attr('id');
        console.log('Hidden: ' + id);
        state[page][id] = false;
        Cookies.set(state_cookie_c, state);
    });
});

Make the cookie only add pages lazily


GoogleApi = {
  get: Meteor._wrapAsync(_.bind(GoogleApiAsync.get, GoogleApiAsync)),

  post: Meteor._wrapAsync(_.bind(GoogleApiAsync.post, GoogleApiAsync))
}

var syncUnlessCallbackProvided = function(fn) {
  return function(/* arguments */) {
    var args = _.toArray(arguments);
    if (_.isFunction(args[args.length - 1]))
      return fn.apply(this, args);
    else
      return Meteor._wrapAsync(fn).apply(this, args);
  }
}

GoogleApi = {
  get: syncUnlessCallbackProvided(_.bind(GoogleApiAsync.get, GoogleApiAsync)),

  post: syncUnlessCallbackProvided(_.bind(GoogleApiAsync.post, GoogleApiAsync))
}

Allow async API on server


/**
 * main.js
 */

'use strict';

/* eslint-disable */

// Only run in capable browsers via feature detection
var didCutTheMustard = 'querySelector' in document
  && 'classList' in document.createElement('a')
  && 'addEventListener' in window;

if (didCutTheMustard) {

  // Add class "js" to html element
  document.querySelector('html').classList.add('js');

  // Init requirejs
  require([
    'numberedLines',
		// 'snowyHeader',
    'darkMode'
  ], function(
    numberedLines,
		// snowyHeader,
    darkMode
  ) {});

}

/**
 * main.js
 */

'use strict';

/* eslint-disable */

// Only run in capable browsers via feature detection
var didCutTheMustard = 'querySelector' in document
  && 'classList' in document.createElement('a')
  && 'addEventListener' in window;

if (didCutTheMustard) {

  // Add class "js" to html element
  document.querySelector('html').classList.add('js');

  // Init requirejs
  require([
    'numberedLines'
		// 'snowyHeader',
    // 'darkMode'
  ], function(
    numberedLines
		// snowyHeader,
    // darkMode
  ) {});
}

Add dark mode, but leave it disabled for now


/* global Calendar, vis */

(function() {
    "use strict";

    var calendar = new Calendar();
    calendar.init(document.getElementById('visualization'),
    {
      height: "100vh",
      orientation: "top",
      zoomKey: 'shiftKey',
      zoomMax: 315360000000,
      zoomMin: 600000,
      editable: {
        add: false,
        updateTime: true,
        updateGroup: false,
        remove: true
      }

    });

    $(document).ready(function(){
      $('.vis-center>.vis-content').on('scroll', function () {
          $('.vis-left>.vis-content').scrollTop($(this).scrollTop());
      });
      $('.vis-left>.vis-content').on('scroll', function () {
          $('.vis-center>.vis-content').scrollTop($(this).scrollTop());
      });
    });

})();

/* global console, Calendar, vis */

(function() {
    "use strict";

    var calendar = new Calendar();
    calendar.init(document.getElementById('visualization'),
    {
      height: "100vh",
      orientation: "top",
      zoomKey: 'shiftKey',
      zoomMax: 315360000000,
      zoomMin: 600000,
      editable: {
        add: false,
        updateTime: true,
        updateGroup: false,
        remove: true
      },
      groupOrder: function (a, b) {
          console.log("Ordenar!!");
          console.log(a.id);
          console.log(b.id);
          if (a.id === "demos") {
            return -1;
          }
          if (b.id === "demos") {
            return 1;
          }

          var groups = [a.id, b.id];
          groups.sort();
          if (a.id === groups[0]) {
            return -1;
          } else {
            return 1;
          }
        }

    });

    $(document).ready(function(){
      $('.vis-center>.vis-content').on('scroll', function () {
          $('.vis-left>.vis-content').scrollTop($(this).scrollTop());
      });
      $('.vis-left>.vis-content').on('scroll', function () {
          $('.vis-center>.vis-content').scrollTop($(this).scrollTop());
      });
    });

})();

Add function to sort the groups


import { StatsD } from 'node-dogstatsd'
var dogstatsd = new StatsD()

function registerEvents (bot) {
  bot.on('messageCreate', (msg) => {
    dogstatsd.increment('blair.messages_total')
    if (msg.command) {
      dogstatsd.increment('blair.commands_total', [`cmd:${msg.command.label}`])
    }
  })
  setInterval(() => {
    dogstatsd.gauge('blair.guilds_total', bot.guilds.size)
  }, 10000)
}

export default {
  registerEvents
}

import { StatsD } from 'node-dogstatsd'
var dogstatsd = new StatsD()

function registerEvents (bot) {
  bot.on('messageCreate', (msg) => {
    dogstatsd.increment('blair.messages_total')
    if (msg.command) {
      let label = msg.command.label

      if (msg.command.parentCommand) {
        label = `${msg.command.parentCommand.label} ${label}`
      }

      dogstatsd.increment('blair.commands_total', [`cmd:${label}`])
    }
  })
  setInterval(() => {
    dogstatsd.gauge('blair.guilds_total', bot.guilds.size)
  }, 10000)
}

export default {
  registerEvents
}

Fix cmd metric label not taking into account parentCommand