// Auto-expand the accordion and jump to the target if url contains a valid hash
(function(doc){
    "use strict";

    $(function(){
        var $section, hash = window.location.hash;
        if(hash){
            $section = $(hash);
            if($section){
                $(doc).one('hidden.bs.collapse', hash, function(){
                    $section.parent().find('h3').click();
                });
                $('html, body').animate({
                        scrollTop: $(hash).offset().top
                }, 500);
            }
        }
    })
})(document);

// Auto-expand the accordion and jump to the target if url contains a valid hash
(function(doc){
    "use strict";

    // On DOM Load...
    $(function(){
        var hash = window.location.hash;
        var headerHeight = $('.navbar').height();
        var methods = $('.accordion-group .accordion-heading');
        var methodCount = methods.length;

        // Sets-up an event handler that will expand and scroll to the desired
        // (hash) method.
        if(hash){
            // We need to know that all the panes have (at least initially)
            // collapsed as a result of loading the page. Unfortunately, we
            // only get events for each collapsed item, so we count...
            $(doc).on('hidden.bs.collapse', function(evt) {
                var methodTop, $hdr = $(hash).parent('.accordion-group');
                methodCount--;
                if(methodCount === 0){
                    // OK, they've /all/ collapsed, now we expand the one we're
                    // interested in the scroll to it.

                    // First, remove this very event handler
                    $(doc).off('hidden.bs.collapse');
                    // Now wait just a beat more to allow the last collapse
                    // animation to complete...
                    setTimeout(function(){
                        // Open the desired method
                        $hdr.find('h3').click();
                        // Take into account the fixed header and the margin
                        methodTop = $hdr.offset().top - headerHeight - 8;
                        // Scroll it into view
                        $('html, body').animate({scrollTop: methodTop}, 250);
                    }, 250);
                }
            });
        }

        // Delegated event handler to prevent the collapse/expand function of
        // a method's header when clicking the Pilcrow (permalink)
        $('.accordion-group').on('click', '.permalink', function(evt){
            evt.preventDefault();
            evt.stopImmediatePropagation();
            window.location = $(this).attr('href');
        });
    })
})(document);

Work around for lack of signal for collapsing of all methods


/* jshint devel: true */
'use strict';

xss.debug.NS = 'DEBUG';

// Debug URL: debug.html?debug=level:0
xss.debug.levelIndex = location.search.match(/debug=level:([0-9]+)$/)[1];
if (xss.debug.levelIndex) {
    document.addEventListener('DOMContentLoaded', function() {
        window.setTimeout(function() {
            xss.levels.onload = xss.debug.level;
        }, 0);
    });
}

xss.debug.level = function() {
    var game;

    xss.socket = {emit: xss.util.noop};
    xss.menuSnake.destruct();

    game = new xss.Game(0, xss.debug.levelIndex, ['']);
    game.start();

    console.info(game.level.levelData);

    xss.event.on(xss.PUB_GAME_TICK, xss.debug.NS, function() {
        if (game.snakes[0].limbo) {
            console.log(game.snakes[0]);
            game.snakes[0].crash();

            xss.event.off(xss.PUB_GAME_TICK, xss.debug.NS);
        }
    });
};

/* jshint devel: true */
'use strict';

xss.debug.NS = 'DEBUG';

// Debug URL: debug.html?debug=level:0
xss.debug.debugLevelMatch = location.search.match(/debug=level:([0-9]+)$/);
if (xss.debug.debugLevelMatch) {
    document.addEventListener('DOMContentLoaded', function() {
        window.setTimeout(function() {
            xss.levels.onload = xss.debug.level;
        }, 0);
    });
}

xss.debug.level = function() {
    var game;

    xss.socket = {emit: xss.util.noop};
    xss.menuSnake.destruct();

    game = new xss.Game(0, xss.debug.debugLevelMatch, ['']);
    game.start();

    console.info(game.level.levelData);

    xss.event.on(xss.PUB_GAME_TICK, xss.debug.NS, function() {
        if (game.snakes[0].limbo) {
            console.log(game.snakes[0]);
            game.snakes[0].crash();

            xss.event.off(xss.PUB_GAME_TICK, xss.debug.NS);
        }
    });
};

Fix indexerr caused by debug page in non-debug mode


var util = require('util');
var _ = require('underscore');
var EventEmitter = require('events').EventEmitter;

/**
 * @param {String} app
 * @param {String} env
 * @param {String} task
 * @param {Object.<String, String>} [taskVariables]
 * @constructor
 */
function Job(app, env, task, taskVariables) {
  this.app = app;
  this.env = env;
  this.task = task;
  this.taskVariables = taskVariables;
  this.data = {};

  EventEmitter.call(this);
}

util.inherits(Job, EventEmitter);

/**
 * @param {Object} jobData
 * @returns {Object} new merged Job's data
 */
Job.prototype.setData = function(jobData) {
  return _.extend(this.data, jobData);
};

Job.prototype.toString = function() {
  var result = util.format('%s "%s" to "%s"', this.task, this.app, this.env);
  if (this.data.id) {
    result += ' id: ' + this.data.id;
  }
  return result;
};

module.exports = Job;

var util = require('util');
var _ = require('underscore');
var EventEmitter = require('events').EventEmitter;
var PulsarJob = require('../node_modules/pulsar-rest-api/lib/pulsar/job');

/**
 * @param {String} app
 * @param {String} env
 * @param {String} task
 * @param {Object.<String, String>} [taskVariables]
 * @constructor
 */
function Job(app, env, task, taskVariables) {
  this.app = app;
  this.env = env;
  this.task = task;
  this.taskVariables = taskVariables;
  this.data = {};

  EventEmitter.call(this);
}

util.inherits(Job, EventEmitter);

/**
 * @param {Object} jobData
 * @returns {Object} new merged Job's data
 */
Job.prototype.setData = function(jobData) {
  return _.extend(this.data, jobData);
};

Job.prototype.isRunning = function() {
  return this.data.status == PulsarJob.STATUS.RUNNING;
};

Job.prototype.toString = function() {
  var result = util.format('%s "%s" to "%s"', this.task, this.app, this.env);
  if (this.data.id) {
    result += ' id: ' + this.data.id;
  }
  return result;
};

module.exports = Job;

Add isRunning method to Job


// Copyright (c) 2017 The Regents of the University of Michigan.
// All Rights Reserved. Licensed according to the terms of the Revised
// BSD License. See LICENSE.txt for details.

module.exports = function(landscape) {
  let actions = new Map();
  let tickCount = 0;

  let tickOnce = () => new Promise(function(resolve, reject) {
    tickCount += 1;

    let runAllActions = Promise.resolve();

    for (let action of actions.get(tickCount) || [])
      runAllActions = new Promise(function(done) {
        runAllActions.then(function() {
          try {
            Promise.resolve(
              landscape[action.method].apply(landscape, action.args)
            ).then(done, reject);

          } catch(error) {
            reject(error);
          }
        }, reject);
      });

    runAllActions.then(resolve, reject);
  });

  return {
    "tick": n => new Promise(function(resolve, reject) {
      let allTicks = Promise.resolve();
      if (typeof n === "undefined") n = 1;

      for (; n > 0; n -= 1)
        allTicks = allTicks.then(tickOnce, reject);

      allTicks.then(resolve, reject);
    }),

    "at": function(when, method) {
      if (!actions.has(when))
        actions.set(when, []);

      actions.get(when).push({
        "when": when,
        "method": method,
        "args": [...arguments].slice(2)
      });
    }
  };
};

// Copyright (c) 2017 The Regents of the University of Michigan.
// All Rights Reserved. Licensed according to the terms of the Revised
// BSD License. See LICENSE.txt for details.

module.exports = function(landscape) {
  let actions = new Map();
  let tickCount = 0;

  let tickOnce = () => new Promise(function(resolve, reject) {
    tickCount += 1;

    let runAllActions = Promise.resolve();

    for (let action of actions.get(tickCount) || [])
      runAllActions = new Promise(function(done) {
        runAllActions.then(function() {
          try {
            Promise.resolve(
              landscape[action.method].apply(landscape, action.args)
            ).then(done, reject);

          } catch(error) {
            reject(error);
          }
        }, reject);
      });

    runAllActions.then(resolve, reject);
  });

  return {
    "tick": n => new Promise(function(resolve, reject) {
      let allTicks = Promise.resolve();
      if (typeof n === "undefined") n = 1;

      for (; n > 0; n -= 1)
        allTicks = allTicks.then(tickOnce, reject);

      allTicks.then(resolve, reject);
    }),

    "at": function(when, method, ...args) {
      if (!actions.has(when))
        actions.set(when, []);

      actions.get(when).push({
        "when": when,
        "method": method,
        "args": args
      });
    }
  };
};

Use rest parameters instead of arguments


import {formatSize} from '../../../core/src/common/utils';
import log from '../../../core/src/common/log';

const BLOB_SIZE_LIMIT = 10 * 1024 * 1024; // 10 MB

export function fetchURL(url) {
  log.info(`Fetching data from "${url}"`);
  return new Promise((resolve, reject) => {
    const request = new XMLHttpRequest;
    request.open('GET', url, true);
    request.responseType = 'arraybuffer';
    request.onload = () => {
      if (request.status === 200) {
        resolve(request.response);
      } else if (request.status === 0) {
        reject(new Error('Unable to connect to the server'));
      } else {
        reject(new Error(`Unable to download file (${request.status} ${request.statusText})`));
      }
    };
    request.onerror = () => {
      reject(new Error('Unable to connect to the server'));
    };
    request.send();
  });
}

export function readBlob(blob) {
  log.info(`Reading contents of ${formatSize(blob.size)} blob`);
  return new Promise((resolve, reject) => {
    if (blob.size > BLOB_SIZE_LIMIT) {
      reject(new Error(`Input is too large (${formatSize(blob.size)})`));
      return;
    }
    const reader = new FileReader;
    reader.onload = event => {
      resolve(event.target.result);
    };
    reader.onerror = event => {
      reject(new Error(event.target.error || 'Unknown error'));
    };
    reader.readAsArrayBuffer(blob);
  });
}

import {formatSize} from '../../../core/src/common/utils';
import log from '../../../core/src/common/log';

const BLOB_SIZE_LIMIT = 10 * 1024 * 1024; // 10 MB

export function fetchURL(url) {
  log.info(`Downloading data from "${url}"`);
  return new Promise((resolve, reject) => {
    const request = new XMLHttpRequest;
    request.open('GET', url, true);
    request.responseType = 'arraybuffer';
    request.onload = () => {
      if (request.status === 200) {
        resolve(request.response);
      } else if (request.status === 0) {
        reject(new Error('Failed to connect to the server'));
      } else {
        reject(new Error(`Failed to download data (${request.status} ${request.statusText})`));
      }
    };
    request.onerror = () => {
      reject(new Error('Failed to connect to the server'));
    };
    request.send();
  });
}

export function readBlob(blob) {
  log.info(`Reading contents of ${formatSize(blob.size)} blob`);
  return new Promise((resolve, reject) => {
    if (blob.size > BLOB_SIZE_LIMIT) {
      reject(new Error(`Input is too large (${formatSize(blob.size)})`));
      return;
    }
    const reader = new FileReader;
    reader.onload = event => {
      resolve(event.target.result);
    };
    reader.onerror = event => {
      reject(new Error(event.target.error || 'Unknown error'));
    };
    reader.readAsArrayBuffer(blob);
  });
}

Unify error messages between lib and app


const updateComment = (body, commentArr = []) => {
  body = body.map((obj, index, array) => {
    if (obj.type === 'Line' || obj.type === 'Block') {
      if (array[index + 1].type === 'Line' || array[index + 1].type === 'Block') {
        commentArr.push(obj)
      } else {
        commentArr.push(obj)
        array[index + 1].leadingComments = commentArr
        commentArr = []
      }
      return undefined
    }
    return obj
  }).filter(stmt => stmt !== undefined)
  return body
}

/*  Module Exports updateComment  */
module.exports = updateComment

const updateComment = (body, commentArr = []) => {
  body = body.map((obj, index, array) => {
    if (obj.type === 'Line' || obj.type === 'Block') {
      commentArr.push(obj)
      if (array[index + 1] === undefined) {
        array[index - commentArr.length].trailingComments = commentArr
        return undefined
      }
      if (!(array[index + 1].type === 'Line' || array[index + 1].type === 'Block')) {
        array[index + 1].leadingComments = commentArr
        commentArr = []
      }
      return undefined
    }
    return obj
  }).filter(stmt => stmt !== undefined)
  return body
}

/*  Module Exports updateComment  */
module.exports = updateComment

Add check for comments at the end of the file


(function () {
    'use strict';
    window.scrollTo(0, document.body.scrollHeight);
    var likeElements = document.querySelectorAll(".coreSpriteHeartOpen");
    var likeCount = 0;
    var nextTime = 1000;

    function doLike(i) {
        likeElements[i].click();
    }

    for (var i = 0; i < likeElements.length; i++) {
        nextTime = Math.random() * (14000 - 4000) + 4000;
        console.log(nextTime);
        console.log("Like: " + likeCount);
        likeCount++;
        if (likeCount > 10) {
            console.log("too much liking !");
            break;
        } else {
            setTimeout(doLike(i), nextTime);
        }
    }

})();

(function () {
    'use strict';
    window.scrollTo(0, document.body.scrollHeight);
    var likeElements = document.querySelectorAll(".coreSpriteLikeHeartOpen");
    var likeCount = 0;
    var nextTime = 1000;

    function doLike(photo) {
        photo.click();
    }

    likeElements.forEach(photo => {
        nextTime = Math.random() * (14000 - 4000) + 4000;
        console.log(nextTime);
        console.log("Like: " + likeCount);
        likeCount++;
        if (likeCount > 5) {
            console.log("too much liking !");
            return
        } else {
            setTimeout(doLike(photo), nextTime);
        }
    });

})();

Fix instagram script on feed


var express = require('express');

exports.PROXY_ERROR_CODE_TO_HTTP_CODE_MAP = {
  'NR-1000': 400,
  'NR-1001': 400,
  'NR-1002': 401,

  'NR-2000': 400
};

exports.getTestHttpServer = function(port, ip, callback) {
  ip = ip || '127.0.0.1';

  var server = express.createServer();
  server.listen(port, ip, function(err) {
    callback(err, server);
  });
};

exports.setupErrorEchoHandlers = function(server) {
  function echoError(req, res) {
    var code;

    if (!req.headers.hasOwnProperty('x-rp-error-code')) {
      res.writeHead(404, {});
      res.end();
      return;
    }

    code = exports.PROXY_ERROR_CODE_TO_HTTP_CODE_MAP[req.headers['x-rp-error-code']];

    res.writeHead(code, {});
    res.end(req.headers['x-rp-error-message']);
  }

  server.get('*', echoError);
  server.post('*', echoError);
  server.put('*', echoError);
};

var express = require('express');

exports.PROXY_ERROR_CODE_TO_HTTP_CODE_MAP = {
  'NR-1000': 400,
  'NR-1001': 400,
  'NR-1002': 401,

  'NR-2000': 400,

  'NR-5000': 500
};

exports.getTestHttpServer = function(port, ip, callback) {
  ip = ip || '127.0.0.1';

  var server = express.createServer();
  server.listen(port, ip, function(err) {
    callback(err, server);
  });
};

exports.setupErrorEchoHandlers = function(server) {
  function echoError(req, res) {
    var code;

    if (!req.headers.hasOwnProperty('x-rp-error-code')) {
      res.writeHead(404, {});
      res.end();
      return;
    }

    code = exports.PROXY_ERROR_CODE_TO_HTTP_CODE_MAP[req.headers['x-rp-error-code']];

    res.writeHead(code, {});
    res.end(req.headers['x-rp-error-message']);
  }

  server.get('*', echoError);
  server.post('*', echoError);
  server.put('*', echoError);
};

Add a status code mapping for NR-5000.


navigate = function (hash) {
  window.location.replace(Meteor.absoluteUrl(null, { secure: true }) + hash);
};

navigate = function (hash) {
  window.location.replace(Meteor.absoluteUrl() + hash);
};

Remove extra option to absoluteUrl


{
  angular.module('meganote.notesForm')
    .controller('NotesFormController', NotesFormController);

  NotesFormController.$inject = ['$state', 'Flash', 'NotesService'];
  function NotesFormController($state, Flash, NotesService) {
    const vm = this;
    vm.note = NotesService.find($state.params.noteId);
    vm.clearForm = clearForm;
    vm.save = save;
    vm.destroy = destroy;

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

    function clearForm() {
      vm.note = { title: '', body_html: '' };
    }

    function save() {
      if (vm.note._id) {
        NotesService.update(vm.note)
          .then(
            function(res) {
              vm.note = angular.copy(res.data.note);
              Flash.create('success', res.data.message);
            },
            function() {
              Flash.create('danger', 'Oops! Something went wrong.');
            });
      }
      else {
        NotesService.create(vm.note)
          .then(
            function(res) {
              vm.note = res.data.note;
              Flash.create('success', res.data.message);
              $state.go('notes.form', { noteId: vm.note._id });
            },
            function() {
              Flash.create('danger', 'Oops! Something went wrong.');
            });
      }
    }

    function destroy() {
      NotesService.destroy(vm.note)
        .then(function() {
          vm.clearForm();
        });
    }
  }
}

{
  angular.module('meganote.notesForm')
    .controller('NotesFormController', NotesFormController);

  NotesFormController.$inject = ['$state', 'Flash', 'NotesService'];
  function NotesFormController($state, Flash, NotesService) {
    const vm = this;
    vm.note = NotesService.find($state.params.noteId);
    vm.clearForm = clearForm;
    vm.save = save;
    vm.destroy = destroy;

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

    function clearForm() {
      vm.note = { title: '', body_html: '' };
    }

    function save() {
      if (vm.note._id) {
        NotesService.update(vm.note)
          .then(
            res => {
              vm.note = angular.copy(res.data.note);
              Flash.create('success', res.data.message);
            },
            () => Flash.create('danger', 'Oops! Something went wrong.')
          );
      }
      else {
        NotesService.create(vm.note)
          .then(
            res => {
              vm.note = res.data.note;
              Flash.create('success', res.data.message);
              $state.go('notes.form', { noteId: vm.note._id });
            },
            () => Flash.create('danger', 'Oops! Something went wrong.')
          );
      }
    }

    function destroy() {
      NotesService.destroy(vm.note)
        .then(
          () => vm.clearForm()
        );
    }
  }
}

Use fat arrow syntax for callbacks.


'use strict';

describe('<%= classedName %>', function () {
  var <%= scriptAppName %>, component;

  beforeEach(function () {
    var container = document.createElement('div');
    container.id = 'content';
    document.body.appendChild(container);

    <%= scriptAppName %> = require('../../../src/scripts/components/<%= scriptAppName %>.jsx');
    component = <%= scriptAppName %>();
  });

  it('should create a new instance of <%= scriptAppName %>', function () {
    expect(component).toBeDefined();
  });
});

'use strict';

describe('<%= classedName %>', function () {
  var React = require('react/addons');
  var <%= scriptAppName %>, component;

  beforeEach(function () {
    var container = document.createElement('div');
    container.id = 'content';
    document.body.appendChild(container);

    <%= scriptAppName %> = require('../../../src/scripts/components/<%= scriptAppName %>.js');
    component = React.createElement(<%= scriptAppName %>);
  });

  it('should create a new instance of <%= scriptAppName %>', function () {
    expect(component).toBeDefined();
  });
});

Fix error and warning in test


export default function shellDirective() {
  return {
    templateUrl: 'app/layout/shell.html',
    restrict: 'EA',
    transclude: true,
    scope: true,
    controller: 'ShellController',
    controllerAs: 'vm'
  };
}

export default function shellDirective() {
  return {
    templateUrl: 'app/layout/shell.html',
    restrict: 'EA',
    transclude: true,
    scope: {},
    controller: 'ShellController',
    controllerAs: 'vm'
  };
}

Change to correct isolate scope in shell


(function (module) {
    module.controller('projectViewProcess', projectViewProcess);
    projectViewProcess.$inject = ["$state", "process"];

    function projectViewProcess($state, process) {
        var ctrl = this;
        ctrl.editProvenance = editProvenance;
        ctrl.process = process;

        function editProvenance() {
            $state.go('projects.project.processes.edit', {process_id: ctrl.process.id});
        }
    }
}(angular.module('materialscommons')));

(function (module) {
    module.controller('projectViewProcess', projectViewProcess);
    projectViewProcess.$inject = ["process"];

    function projectViewProcess(process) {
        var ctrl = this;
        ctrl.process = process;
    }
}(angular.module('materialscommons')));

Remove editProvenance and replace with ui-sref.


import WebSocketClient from 'websocket.js';
import config from '../config';
import createQueryString from './createQueryString';
import { User } from 'app/actions/ActionTypes';

export default function createWebSocketMiddleware() {
  let socket = null;

  return store => {
    const makeSocket = jwt => {
      if (socket || !jwt) return;

      const qs = createQueryString({ jwt });
      socket = new WebSocketClient(`${config.wsServerUrl}/${qs}`);

      socket.onmessage = event => {
        const { type, payload, meta } = JSON.parse(event.data);
        store.dispatch({ type, payload, meta });
      };

      socket.onopen = () => {
        store.dispatch({ type: 'WS_CONNECTED' });
      };

      socket.onclose = () => {
        store.dispatch({ type: 'WS_CLOSED' });
      };

      socket.onerror = () => {
        store.dispatch({ type: 'WS_ERROR' });
      };
    };

    return next =>
      action => {
        if (action.type === 'REHYDRATED') {
          makeSocket(store.getState().auth.token);
          return next(action);
        }

        if (action.type === User.LOGIN.SUCCESS) {
          makeSocket(action.payload.token);
          return next(action);
        }

        if (action.type === User.LOGOUT) {
          if (socket) {
            socket.close();
          }

          socket = null;

          return next(action);
        }

        return next(action);
      };
  };
}

import WebSocketClient from 'websocket.js';
import config from '../config';
import createQueryString from './createQueryString';
import { addNotification } from 'app/actions/NotificationActions';
import { User } from 'app/actions/ActionTypes';

export default function createWebSocketMiddleware() {
  let socket = null;

  return store => {
    const makeSocket = jwt => {
      if (socket || !jwt) return;

      const qs = createQueryString({ jwt });
      socket = new WebSocketClient(`${config.wsServerUrl}/${qs}`);

      socket.onmessage = event => {
        const { type, payload, meta } = JSON.parse(event.data);
        store.dispatch({ type, payload, meta });
        if (meta.errorMessage) {
          store.dispatch(addNotification({ message: meta.errorMessage }));
        }
      };

      socket.onopen = () => {
        store.dispatch({ type: 'WS_CONNECTED' });
      };

      socket.onclose = () => {
        store.dispatch({ type: 'WS_CLOSED' });
      };

      socket.onerror = () => {
        store.dispatch({ type: 'WS_ERROR' });
      };
    };

    return next =>
      action => {
        if (action.type === 'REHYDRATED') {
          makeSocket(store.getState().auth.token);
          return next(action);
        }

        if (action.type === User.LOGIN.SUCCESS) {
          makeSocket(action.payload.token);
          return next(action);
        }

        if (action.type === User.LOGOUT) {
          if (socket) {
            socket.close();
          }

          socket = null;

          return next(action);
        }

        return next(action);
      };
  };
}

Add errorMessage notifcation from backend


import Publication from 'common/modules/Publication/pages/Publication/Publication'
import Organization from 'common/modules/Publication/pages/Organization/Organization'
import PublishingDatasets from 'common/modules/Publication/pages/PublishingDatasets/PublishingDatasets'
import OrganizationProducers from 'common/modules/Publication/pages/OrganizationProducers/OrganizationProducers'

export default () => ({
  path: 'publication',

  indexRoute: {
    component: Publication
  },

  childRoutes: [
    {
      path: ':organizationId',
      component: Organization
    },
    {
      path: ':organizationId/datasets',
      component: PublishingDatasets
    },
    {
      path: ':organizationId/producers',
      component: OrganizationProducers
    }
  ]
})

export default () => ({
  path: 'publication',

  indexRoute: {
    async getComponent(nextState, cb) {
      const Publication = await import(
        /* webpackChunkName: 'publication' */
        'common/modules/Publication/pages/Publication/Publication'
      )

      cb(null, Publication.default)
    }
  },

  childRoutes: [
    {
      path: ':organizationId',
      async getComponent(nextState, cb) {
        const Organization = await import(
          /* webpackChunkName: 'publication' */
          'common/modules/Publication/pages/Organization/Organization'
        )

        cb(null, Organization.default)
      }
    },
    {
      path: ':organizationId/datasets',
      async getComponent(nextState, cb) {
        const PublishingDatasets = await import(
          /* webpackChunkName: 'publication' */
          'common/modules/Publication/pages/PublishingDatasets/PublishingDatasets'
        )

        cb(null, PublishingDatasets.default)
      }
    },
    {
      path: ':organizationId/producers',
      async getComponent(nextState, cb) {
        const OrganizationProducers = await import(
          /* webpackChunkName: 'publication' */
          'common/modules/Publication/pages/OrganizationProducers/OrganizationProducers'
        )

        cb(null, OrganizationProducers.default)
      }
    }
  ]
})

Split publication route out of the main bundle


TAPi18next.init({resStore: {}, fallbackLng: globals.fallback_language});

TAPi18next.init({resStore: {}, fallbackLng: globals.fallback_language, useCookie: false});

Disable TAPi18next cookie feature that caused anomalies


var assert = require('chai').assert;
var convert = require('../../browser');

var data = require('../data');
var formats = data.formats;
var tests = data.tests;

describe('client-side', function() {
  tests.forEach(function(test) {
    it(test.desc, function(done) {
      test.opts = {};
      var result = convert(test.delta, formats, test.opts);
      assert.equal(result, test.expected);
      done();
    });
  });

  it('throws an error for a delta with non-inserts', function() {
    assert.throws(function() {
      convert({ ops: [{ insert: 'abc' }, { retain: 3 }] }, formats);
    }, 'Cannot convert delta with non-insert operations');
  });
});

var assert = require('chai').assert;
var convert = require('../../browser');

var data = require('../data');
var formats = data.formats;
var tests = data.tests;

describe('client-side', function() {
  tests.forEach(function(test) {
    it(test.desc, function(done) {
      if (!test.hasOwnProperty('opts')) {
        test.opts = {};
      }
      var result = convert(test.delta, formats, test.opts);
      assert.equal(result, test.expected);
      done();
    });
  });

  it('throws an error for a delta with non-inserts', function() {
    assert.throws(function() {
      convert({ ops: [{ insert: 'abc' }, { retain: 3 }] }, formats);
    }, 'Cannot convert delta with non-insert operations');
  });
});

Check for test opts in data blob


import React, { Component, PropTypes } from 'react'
import JoyRide from 'react-joyride'

import Library from '../containers/Library'
import Workspace from '../containers/Workspace'

const styles = {
  display: 'flex'
}

export default class LambdaBricksApp extends Component {
  constructor() {
    super()

    this.state = {
      joyrideOverlay: true,
      joyrideType: 'continuous',
      ready: false,
      showStepsProgress: true,
      steps: [
        {
          title: 'Library',
          text: 'Library description',
          selector: '#library',
          position: 'right'
        },
        {
          title: 'Constants',
          text: 'Constants description',
          selector: '#constants',
          position: 'right'
        }
      ]
    }
  }

  componentDidMount() {
    this.setState({
      ready: true
    })
  }

  componentDidUpdate(prevProps, prevState) {
    if(!prevState.ready && this.state.ready) {
      this.refs.joyride.start(true)
    }
  }

  render() {
    const state = this.state

    return (
      <div style={ styles }>
        <Library />
        <Workspace />
        <JoyRide
          ref="joyride"
          debug={ false }
          steps={ state.steps }
          type={ state.joyrideType }
          showSkipButton={ false }
          showStepsProgress={ state.showStepsProgress }
          showOverlay={ state.showOverlay }
        />
      </div>
    )
  }

  getChildContext() {
    return {
      locale: 'en'
    }
  }
}

LambdaBricksApp.childContextTypes = {
  locale: PropTypes.string.isRequired
}

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

import Library from '../containers/Library'
import Workspace from '../containers/Workspace'

const styles = {
  display: 'flex'
}

export default class LambdaBricksApp extends Component {
  render() {
    return (
      <div style={ styles }>
        <Library />
        <Workspace />
      </div>
    )
  }

  getChildContext() {
    return {
      locale: 'en'
    }
  }
}

LambdaBricksApp.childContextTypes = {
  locale: PropTypes.string.isRequired
}

Remove site tour from main app


const { User } = require('./../models');

const getUser = (req, res) => {
  User.fetch({ facebook_id: req.query.facebook_id })
    .then(fetchedUser => {
      res.status(200).json(fetchedUser);
    })
    .catch(err => {
      console.err(err);
    });
};

const postUser = (req, res) => {
  const newUser = {
    facebook_id: req.body.facebook_id,
    first_name: req.body.first_name,
    last_name: req.body.last_name,
    display_name: req.body.display_name,
    gender: req.body.gender,
    photo_url: req.body.photo_url,
  };

  User.save(newUser)
    .then(savedUser => {
      res.status(200).send(savedUser);
    })
    .catch(err => {
      console.err(err);
    });
};

module.exports = {
  getUser,
  postUser,
};

const { User } = require('./../models');

const getUser = (req, res) => {
  User.fetch({ facebook_id: req.query.facebook_id })
    .then(results => results[0])
    .then(fetchedUser => {
      if (fetchedUser) {
        res.status(200).json(fetchedUser);
      } else {
        res.sendStatus(404);
      }
    })
    .catch(err => {
      console.error(err);
      res.status(500).json({
        description: 'Gobble DB - User.fetch',
        error: err,
      });
    });
};

const postUser = (req, res) => {
  const newUser = {
    facebook_id: req.body.facebook_id,
    first_name: req.body.first_name,
    last_name: req.body.last_name,
    display_name: req.body.display_name,
    gender: req.body.gender,
    photo_url: req.body.photo_url,
  };

  User.save(newUser)
    .then(() => {
      // Not using mySQL response as of now
      console.log('New user success.');
      User.fetch({ facebook_id: newUser.facebook_id })
        .then(results => results[0])
        .then(fetchedUser => {
          res.status(200).json(fetchedUser);
        });
    })
    .catch(err => {
      console.error(err);
      res.status(500).json({
        description: 'Gobble DB - User.save',
        error: err,
      });
    });
};

module.exports = {
  getUser,
  postUser,
};

Add database interaction error handling


var fs  = require('fs');

function createDir(directory) {
   if (!fs.existsSync(directory)){
     fs.mkdirSync(directory, 0766, function(err){
       if(err) console.log(err);
     });
   }
}

function createFile(filename, content) {
  if (!fs.exists(filename)) {
    fs.writeFile(filename, content, function(err){
      if(err) console.log(err);
    });
  }
}

function createDirs() {
  createDir('./saved-env');
  createDir('./public/assets');
  createDir('./public/assets/images');
  createDir('./public/assets/images/screenshots');
  createDir('./public/assets/images/screenshots/new');
  createDir('./public/assets/images/screenshots/old');
  createDir('./public/assets/images/screenshots/results');
}

function createFiles() {
  createFile('executions.json', JSON.stringify({"log" : []}));
}

module.exports.directory    = createDir;
module.exports.file         = createFile;

module.exports.directories  = createDirs;
module.exports.files        = createFiles;

var fs  = require('fs');

function createDir(directory) {
   if (!fs.existsSync(directory)){
     fs.mkdirSync(directory, 0766, function(err){
       if(err) console.log(err);
     });
   }
}

function createFile(filename, content) {
  if (!fs.existsSync(filename)) {
    fs.writeFile(filename, content, function(err){
      if(err) console.log(err);
    });
  }
}

function createDirs() {
  createDir('./saved-env');
  createDir('./public/assets');
  createDir('./public/assets/images');
  createDir('./public/assets/images/screenshots');
  createDir('./public/assets/images/screenshots/new');
  createDir('./public/assets/images/screenshots/old');
  createDir('./public/assets/images/screenshots/results');
  if (fs.existsSync('./browser-list.json')) {
    var browsers = JSON.parse(fs.readFileSync('./browser-list.json'));
    var browser_list = browsers.browsers;
    for(i=0; i<browser_list.length; i++){
      screensDir(browser_list[i]);
    }
  }
}

function createFiles() {
  createFile('./executions.json', JSON.stringify({"log" : []}));
  createFile('./browser-list.json', JSON.stringify({"browsers" : []}));
}

function screensDir(browser){
  createDir('./public/assets/images/screenshots/new/' + browser);
  createDir('./public/assets/images/screenshots/old/' + browser);
  createDir('./public/assets/images/screenshots/results/' + browser);
}

module.exports.directory    = createDir;
module.exports.file         = createFile;

module.exports.directories  = createDirs;
module.exports.files        = createFiles;

module.exports.screensDir   = screensDir;

Create per browser screenshot folder


let Promise = require('bluebird');
let fnArgs = require('fn-args');

let dict = {};

module.exports = () => {
  let di = {
    value: (name, val) => {
      dict[name] = {
        type: 'value',
        x: val
      };
      return di;
    },
    factory: (name, fx) => {
      dict[name] = {
        type: 'factory',
        x: fx
      };
      return di;
    },
    invoke: (fx, context, hardcodedArgs) => {
      let args = fnArgs(fx);
      args = args.map((name) => {
        for (a in hardcodedArgs) {
          if (a === name)
            return hardcodedArgs[a];
        }

        let d = dict[name];
        if (!d)
          throw new Error(`Unknown dependency: '${name}'`);
        if (d.type === 'value')
          return d.x;
        else if (d.type === 'factory')
          return di.invoke(d.x, context, hardcodedArgs);
        else
          return undefined;
      });

      return fx.apply(context, args);
    }
  };

  return di;
};

let Promise = require('bluebird');
let fnArgs = require('fn-args');

let dict = {};

module.exports = () => {
  let di = {
    value: (name, val) => {
      dict[name] = {
        type: 'value',
        x: val
      };
      return di;
    },
    factory: (name, fx) => {
      dict[name] = {
        type: 'factory',
        x: fx
      };
      return di;
    },
    invoke: (fx, context, hardcodedArgs) => {
      let args = fnArgs(fx);
      args = args.map((name) => {
        for (a in hardcodedArgs) {
          if (a === name)
            return hardcodedArgs[a];
        }

        let d = dict[name];
        if (!d)
          log.debug(`Unknown dependency: '${name}'`);
        if (d.type === 'value')
          return d.x;
        else if (d.type === 'factory')
          return di.invoke(d.x, context, hardcodedArgs);
        else
          return undefined;
      });

      return fx.apply(context, args);
    }
  };

  return di;
};

Print debug message when there are unknown dependencies rather than throw


#! /usr/bin/env node

//     __    _____ _____ _____ _____ _____
//    |  |  |  |  |     | __  |   __| __  |
//    |  |__|  |  | | | | __ -|   __|    -|
//    |_____|_____|_|_|_|_____|_____|__|__|

const program = require('commander');
const packagejson = require('./package.json');

program
  .version(packagejson.version)
  .command('generate', 'generate your admin panel application based on your database schema')
  .command('update', 'update your models\'s definition according to your database schema')
  .command('deploy', 'deploy your admin panel application to production')
  .command('user', 'show your current logged user')
  .command('login', 'sign in to your Forest account')
  .command('logout', 'sign out of your Forest account')
  .parse(process.argv);

#! /usr/bin/env node

//     __    _____ _____ _____ _____ _____
//    |  |  |  |  |     | __  |   __| __  |
//    |  |__|  |  | | | | __ -|   __|    -|
//    |_____|_____|_|_|_|_____|_____|__|__|

const program = require('commander');
const packagejson = require('./package.json');

program
  .version(packagejson.version)
  .command('generate <projectName>', 'generate your admin panel application based on your database schema')
  .command('update', 'update your models\'s definition according to your database schema')
  .command('deploy', 'deploy your admin panel application to production')
  .command('user', 'show your current logged user')
  .command('login', 'sign in to your Forest account')
  .command('logout', 'sign out of your Forest account')
  .parse(process.argv);

Add the <projectName> to the generate command's help


var moment = require('moment');

// note that month indices are zero-based

module.exports.DATA_START_YEAR = 2012;
module.exports.DATA_START_MONTH = 0;

module.exports.DATA_END_YEAR = 2015;
module.exports.DATA_END_MONTH = 10;

module.exports.DATA_END_MOMENT = moment([
	module.exports.DATA_END_YEAR,
	module.exports.DATA_END_MONTH]).endOf('month');

module.exports.ASYLUM_APPLICANTS_DATA_UPDATED_MOMENT = moment([2016, 0, 6]);
module.exports.SYRIA_REFUGEES_DATA_UPDATED_MOMENT = moment([2016, 0, 6]);

module.exports.disableLabels = ['BIH', 'MKD', 'ALB', 'LUX', 'MNE', 'ARM', 'AZE', 'LBN'];

module.exports.labelShowBreakPoint = 992;

var moment = require('moment');

// NOTE: month indices are zero-based

module.exports.DATA_START_YEAR = 2012;
module.exports.DATA_START_MONTH = 0;
module.exports.DATA_START_MOMENT = moment([
  module.exports.DATA_START_YEAR,
  module.exports.DATA_START_MONTH]).startOf('month');

module.exports.DATA_END_YEAR = 2015;
module.exports.DATA_END_MONTH = 10;
module.exports.DATA_END_MOMENT = moment([
	module.exports.DATA_END_YEAR,
	module.exports.DATA_END_MONTH]).endOf('month');

module.exports.ASYLUM_APPLICANTS_DATA_UPDATED_MOMENT = moment([2016, 0, 6]);
module.exports.SYRIA_REFUGEES_DATA_UPDATED_MOMENT = moment([2016, 0, 6]);

module.exports.disableLabels = ['BIH', 'MKD', 'ALB', 'LUX', 'MNE', 'ARM', 'AZE', 'LBN'];

module.exports.labelShowBreakPoint = 992;

Add start data moment to RefugeeConstants


// @flow
module.exports = {
  env: {
    jest: true,
  },
  plugins: ['jest', 'testing-library', 'jest-formatting', 'jest-dom'],
  extends: [
    'plugin:jest/recommended',
    'plugin:testing-library/react',
    'plugin:jest-dom/recommended',
  ],
  rules: {
    'react/jsx-no-bind': 0,
    // This rule isn't useful because use Flow.
    'jest/valid-title': 0,

    // Adding more errors now
    'testing-library/no-manual-cleanup': 'error',
    'testing-library/no-wait-for-empty-callback': 'error',
    'testing-library/no-wait-for-snapshot': 'error',
    'testing-library/prefer-explicit-assert': 'error',
    'testing-library/prefer-presence-queries': 'error',
    'testing-library/prefer-wait-for': 'error',

    // Individual jest-formatting rules so that we format only test and describe blocks
    'jest-formatting/padding-around-describe-blocks': 2,
    'jest-formatting/padding-around-test-blocks': 2,
  },
};

// @flow
module.exports = {
  env: {
    jest: true,
  },
  plugins: ['jest', 'testing-library', 'jest-formatting', 'jest-dom'],
  extends: [
    'plugin:jest/recommended',
    'plugin:testing-library/react',
    'plugin:jest-dom/recommended',
  ],
  rules: {
    'react/jsx-no-bind': 0,
    // This rule isn't useful because use Flow.
    'jest/valid-title': 0,

    // Adding more errors now
    'testing-library/no-manual-cleanup': 'error',
    'testing-library/no-wait-for-snapshot': 'error',
    'testing-library/prefer-explicit-assert': 'error',
    'testing-library/prefer-presence-queries': 'error',
    'testing-library/prefer-wait-for': 'error',

    // Disable some rules that are in the "recommended" part.
    // This is a purely stylistic rule
    'testing-library/render-result-naming-convention': 'off',
    // This disallows using `container`, but this is still useful for us sometimes
    'testing-library/no-container': 'off',
    // This disallows using direct Node properties (eg: firstChild), but we have
    // legitimate uses:
    'testing-library/no-node-access': 'off',
    // Disable until https://github.com/testing-library/eslint-plugin-testing-library/issues/359
    // is fixed.
    'testing-library/await-async-query': 'off',

    // Individual jest-formatting rules so that we format only test and describe blocks
    'jest-formatting/padding-around-describe-blocks': 2,
    'jest-formatting/padding-around-test-blocks': 2,
  },
};

Disable some new rules that are too strict for us


import Firebase from 'firebase';
import config from 'config';

export function firebaseInit() {
  // Authenticate with Firebase
  const ref = new Firebase(config.get('firebase.path'));
  ref.authWithCustomToken(config.get('firebase.apiKey'), (error) => {
    if (error) {
      console.error('Terminating startup due to bad Firebase ref');
      process.exit(1);
    }

    // Conveniently access the root ref
    Firebase.root = ref;
  });
}

export function once(type, child) {
  return new Promise((resolve, reject) => {
    Firebase.root.child(child).once(type, (snapshot) => {
      resolve(snapshot.val());
    }, (err) => {
      reject(err);
    });
  });
}

Add Firebase once promise wrapper to facilitate async/await


export default class WaterfallError extends Error {
    get results() {
        return this._results;
    }

    set results(results) {
        if (this._results == null) {
            this._results = results;
        }

        return this;
    }

    constructor(...args) {
        super(...args);

        this.name = 'WaterfallError';
        this._results = null;
    }
};

module.exports = function WaterfallError(message, results) {
    Error.captureStackTrace(this, this.constructor);
    this.name = this.constructor.name;
    this.message = message;
    this.results = results;
};

require('util').inherits(module.exports, Error);

Fix WaterfallError to properly extend Error


/*
* Application root
* */

import React from 'react';

const AppRoot = props => {
	return (
		<div className="application">

		</div>
	);
};

export default AppRoot;

/*
* Application root
* */

import React from 'react';
import imgSrc from './images/redux.png';

const AppRoot = props => {
	return (

			Example component

	);
};

export default AppRoot;

Add image to example component


num=0;
function createDiv(fil) {
  obj=fil.form;
  tab = document.createElement('div');
  tab.id = 'calendario';
  tab.style.padding = "1px";
  tab.style.border = "1px solid red";
  tab.style.background = "blue";
  obj.appendChild(tab);
}

$(document).ready(function(){
	function exampleJQuery()
	{
	    $("p").click(function(){
	    $(".classTest").hide();
	    });
	}
});

	function createDiv(contentDiv,className) {
		var div = document.createElement('div');
		div.className  = className;
		var content = document.getElementById('content');
		div.innerHTML = "<p>"+contentDiv+"</p>";
		content.appendChild(div);
	}

Add function to create dynamic divs


/**
 * Simple pluralizer
 */
Handlebars.registerHelper('pluralize', function(n, thing) {
  if (n === 1) {
    return '1 ' + thing;
  } else {
    return n + ' ' + thing + 's';
  }
});

/**
 * Turn titles into CSS classes
 *
 * Remove puncuation
 * Spaces to undescores
 * Lowercase text
 */
Handlebars.registerHelper('lowerSpacesToDashes', function(input) {
    if (input) {
        return input.replace(/[\.,-\/#!$%\^&\*;:{}=\-_`~()@\+\?><\[\]\+]/g, '').replace(/\s+/g, '-').toLowerCase();
    }
});

/**
 * Strip HTML from a string
 *
 * This is obviously imperfect, but I trust the data source...me.
 */
Handlebars.registerHelper('brToSpace', function(input) {
    if (input) {
        return input.replace(/<br ?\/?>/g, " ")
    }
});

/**
 * Simple pluralizer
 */
Handlebars.registerHelper('pluralize', function(n, thing) {
  if (n === 1) {
    return '1 ' + thing;
  } else {
    return n + ' ' + thing + 's';
  }
});

/**
 * Turn titles into CSS classes
 *
 * Remove puncuation
 * Spaces to undescores
 * Lowercase text
 */
Handlebars.registerHelper('lowerSpacesToDashes', function(input) {
    if (input) {
        return input.replace(/[\.,'-\/#!$%\^&\*;:{}=\-_`~()@\+\?><\[\]\+]/g, '').replace(/\s+/g, '-').toLowerCase();
    }
});

/**
 * Strip HTML from a string
 *
 * This is obviously imperfect, but I trust the data source...me.
 */
Handlebars.registerHelper('brToSpace', function(input) {
    if (input) {
        return input.replace(/<br ?\/?>/g, " ")
    }
});

Remove curly quotes as well


module.exports = (router) => {
  // router is props.history from a component served by react router
  let cookies = {};
  for (let cookie of document.cookie.split(';')) {
    let c = cookie.trim().split('=');
    cookies[c[0]] = c[1];
  }

  if (!(cookies.fridgrSesh && cookies.fridgrSesh.userId && cookies.fridgrSesh.houseId)) {
    router.push('/login');
  }

};

import {parse} from 'cookie';
module.exports = (router) => {
  // router is props.history from a component served by react router
  let cookies = parse(document.cookie);

  if (!(cookies.fridgrSesh && cookies.fridgrSesh.userId && cookies.fridgrSesh.houseId)) {
    router.push('/login');
  }

};

Refactor auth to use npm cookie parse


/*!
 * Generator-Gnar Gruntfile
 * http://gnarmedia.com
 * @author Adam Murphy
 */

// Generated on <%= (new Date).toISOString().split('T')[0] %> using <%= pkg.name %> <%= pkg.version %>

'use strict';

/**
 * Grunt module
 */
module.exports = function (grunt) {

  /**
   * Generator-Gnar Grunt config
   */
  grunt.initConfig({
  ]);

};

/*!
 * Generator-Gnar Gruntfile
 * http://gnarmedia.com
 * @author Adam Murphy
 */

// Generated on <%= (new Date).toISOString().split('T')[0] %> using <%= pkg.name %> <%= pkg.version %>

'use strict';

/**
 * Grunt module
 */
module.exports = function (grunt) {

  /**
   * Generator-Gnar Grunt config
   */
  grunt.initConfig({

    pkg: grunt.file.readJSON('package.json'),

    /**
     * Set project info
     */
    project: {
      src: 'src',
      dist: 'dist',
      assets: '<%= project.dist %>/assets',
      css: [
        '<%= project.src %>/scss/style.scss'
      ],
      js: [
        '<%= project.src %>/js/*.js'
      ]
    }
  ]);

};

Add pkg json and project variables


// Copyright © 2015-2017 Esko Luontola
// This software is released under the Apache License 2.0.
// The license text is at http://www.apache.org/licenses/LICENSE-2.0

/* @flow */

import combineReducers from "redux/es/combineReducers";
import type {ApiState} from "./apiReducer";
import api from "./apiReducer";
import type {ConfigState} from "./configReducer";
import config from "./configReducer";
import {reducer as form} from "redux-form";

export type State = {
  api: ApiState,
  config: ConfigState,
  form: any
}

export default combineReducers({
  api,
  config,
  form,
});

// Copyright © 2015-2017 Esko Luontola
// This software is released under the Apache License 2.0.
// The license text is at http://www.apache.org/licenses/LICENSE-2.0

/* @flow */

import combineReducers from "redux/lib/combineReducers";
import type {ApiState} from "./apiReducer";
import api from "./apiReducer";
import type {ConfigState} from "./configReducer";
import config from "./configReducer";
import {reducer as form} from "redux-form";

export type State = {
  api: ApiState,
  config: ConfigState,
  form: any
}

export default combineReducers({
  api,
  config,
  form,
});

Fix "SyntaxError: Unexpected token import" when running mocha tests


module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    jshint: {
      build: ['Gruntfile.js', 'src/**/*.js']
    },
    watch: {
      scripts: {
        files: 'src/**/*.js',
        tasks: ['jshint']
      }
    },
    copy: {
      build: {
        files: [
          {
            expand: true,
            src: 'node_modules/knockout/build/output/knockout-latest.js',
            dest: 'dist/js/',
            flatten: true,
            rename: function (dest, src) {
              return dest + 'knockout.js';
            }
          }
        ]
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-copy');

  // Default task(s).
  grunt.registerTask('default', ['jshint']);

};

module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    jshint: {
      build: ['Gruntfile.js', 'src/**/*.js']
    },
    watch: {
      scripts: {
        files: 'src/**/*.js',
        tasks: ['jshint']
      }
    },
    copy: {
      build: {
        files: [
          {
            expand: true,
            src: 'node_modules/knockout/build/output/knockout-latest.js',
            dest: 'dist/js/',
            flatten: true,
            rename: function (dest, src) {
              return dest + 'knockout.js';
            }
          }
        ]
      }
    },
    htmlmin: {
      build: {
        options: {
          removeComments: true,
          collapseWhitespace: true
        },
        files: {
          'dist/index.html': 'src/index.html'
        }
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-copy');
  grunt.loadNpmTasks('grunt-contrib-htmlmin');

  // Default task(s).
  grunt.registerTask('default', ['jshint']);

};

Add htmlmin task to gruntfile


module.exports = function (grunt) {
  var standaloneFiles = [
    'bower_components/sockjs-client/dist/sockjs.min.js',
    'bower_components/stomp-websocket/lib/stomp.min.js',
    'src/ng-stomp.js'
  ]

  grunt.initConfig({
    fileExists: {
      scripts: standaloneFiles
    },
    uglify: {
      main: {
        options: {
          preserveComments: 'some'
        },
        files: {
          'dist/ng-stomp.min.js': ['src/ng-stomp.js'],
          'dist/ng-stomp.standalone.min.js': standaloneFiles
        }
      }
    },
    standard: {
      options: {
        format: true
      },
      app: {
        src: ['ng-stomp.js']
      }
    }
  })

  grunt.loadNpmTasks('grunt-file-exists')
  grunt.loadNpmTasks('grunt-contrib-uglify')
  grunt.loadNpmTasks('grunt-standard')
  grunt.registerTask('default', ['standard', 'fileExists', 'uglify'])
}

module.exports = function (grunt) {
  var standaloneFiles = [
    'bower_components/sockjs-client/dist/sockjs.min.js',
    'bower_components/stomp-websocket/lib/stomp.min.js',
    'src/ng-stomp.js'
  ]

  grunt.initConfig({
    fileExists: {
      scripts: standaloneFiles
    },
    uglify: {
      main: {
        options: {
          preserveComments: 'some'
        },
        files: {
          'dist/ng-stomp.min.js': ['src/ng-stomp.js'],
          'dist/ng-stomp.standalone.min.js': standaloneFiles
        }
      }
    }
  })

  grunt.loadNpmTasks('grunt-file-exists')
  grunt.loadNpmTasks('grunt-contrib-uglify')
  grunt.registerTask('default', ['fileExists', 'uglify'])
}

Disable StandardJS in Grunt to fix builds


/*
 * grunt-contrib-watch
 * http://gruntjs.com/
 *
 * Copyright (c) 2013 "Cowboy" Ben Alman, contributors
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {
  grunt.initConfig({
    jshint: {
      all: [
        'Gruntfile.js',
        'tasks/**/*.js',
        '<%= nodeunit.tests %>'
      ],
      options: {
        jshintrc: '.jshintrc'
      }
    },
    watch: {
      all: {
        files: ['<%= jshint.all %>'],
        tasks: ['jshint', 'nodeunit'],
      },
    },
    nodeunit: {
      tests: ['test/tasks/*_test.js']
    }
  });

  // Dynamic alias task to nodeunit. Run individual tests with: grunt test:events
  grunt.registerTask('test', function(file) {
    grunt.config('nodeunit.tests', String(grunt.config('nodeunit.tests')).replace('*', file || '*'));
    grunt.task.run('nodeunit');
  });

  grunt.loadTasks('tasks');

  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-nodeunit');
  grunt.loadNpmTasks('grunt-contrib-internal');

  grunt.registerTask('default', ['jshint', 'nodeunit', 'build-contrib']);
};

/*
 * grunt-contrib-watch
 * http://gruntjs.com/
 *
 * Copyright (c) 2013 "Cowboy" Ben Alman, contributors
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {
  grunt.initConfig({
    jshint: {
      all: [
        'Gruntfile.js',
        'tasks/**/*.js',
        '<%= nodeunit.tests %>',
      ],
      options: {
        jshintrc: '.jshintrc',
      },
    },
    watch: {
      all: {
        files: ['<%= jshint.all %>'],
        tasks: ['jshint', 'nodeunit'],
      },
    },
    nodeunit: {
      tests: ['test/tasks/*_test.js'],
    },
  });

  // Dynamic alias task to nodeunit. Run individual tests with: grunt test:events
  grunt.registerTask('test', function(file) {
    grunt.config('nodeunit.tests', String(grunt.config('nodeunit.tests')).replace('*', file || '*'));
    grunt.task.run('nodeunit');
  });

  grunt.loadTasks('tasks');

  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-nodeunit');
  grunt.loadNpmTasks('grunt-contrib-internal');

  grunt.registerTask('default', ['jshint', 'nodeunit', 'build-contrib']);
};

Add trailing commas (to secretly nudge travis-ci)


var assert, desktopCapturer;

assert = require('assert');

desktopCapturer = require('electron').desktopCapturer;

describe('desktopCapturer', function() {
  return it('should return a non-empty array of sources', function(done) {
    return desktopCapturer.getSources({
      types: ['window', 'screen']
    }, function(error, sources) {
      assert.equal(error, null);
      assert.notEqual(sources.length, 0);
      return done();
    });
  });
});

const assert = require('assert');
const desktopCapturer = require('electron').desktopCapturer;

describe('desktopCapturer', function() {
  it('should return a non-empty array of sources', function(done) {
    desktopCapturer.getSources({
      types: ['window', 'screen']
    }, function(error, sources) {
      assert.equal(error, null);
      assert.notEqual(sources.length, 0);
      done();
    });
  });
});

Use const and remove extra returns


const ColorPicker = require("./components/ColorPicker");

module.exports = ColorPicker;

const ColorPicker = require("./components/ColorPicker");

/* ColorPicker is modified version of https://github.com/adhbh/react-color-selector */
module.exports = ColorPicker;

Add link to color picker original project


// modules
var assert      = require('assert')
var _           = require('underscore')
var fs          = require('fs');
var S           = require('string');
var passmarked  = require('passmarked');
var pluginFunc  = require('../lib/rules/lint/prettify');

describe('lint', function(){

    describe('#prettify', function(){

      // handle the settings
      it('Should return a error if the message passed was undefined', function(done){

        // handle the stream
        pluginFunc(undefined, function(err, content){

          // check for a error
          if(!err) assert.fail('Was expecting a error for the undefined content');

          // done
          done();

        });

      });

      // handle the settings
      it('Should return a error if the message passed was null', function(done){

        // handle the stream
        pluginFunc(null, function(err, content){

          // check for a error
          if(!err) assert.fail('Was expecting a error for the null content');

          // done
          done();

        });

      });

      // handle the settings
      it('Should return a pretified version from our passed in CSS', function(done){

        // handle the stream
        pluginFunc('.text{color:black;}', function(err, content){

          // check for a error
          if(err) assert.fail(err);

          // check if not empty
          if(S(content).isEmpty() == true)
            assert.fail('Prettified content was blank')

          // should match a cleaned version
          if(content != `.text
{
  color: black;
}`)
            assert.fail('Did not clean the content correctly.');

          // done
          done();

        });

      });

  });

});

// modules
var assert      = require('assert')
var _           = require('underscore')
var fs          = require('fs');
var S           = require('string');
var passmarked  = require('passmarked');
var pluginFunc  = require('../lib/rules/lint/prettify');

describe('lint', function(){

    describe('#prettify', function(){

      // handle the settings
      it('Should return a error if the message passed was undefined', function(done){

        // handle the stream
        pluginFunc(undefined, function(err, content){

          // check for a error
          if(!err) assert.fail('Was expecting a error for the undefined content');

          // done
          done();

        });

      });

      // handle the settings
      it('Should return a error if the message passed was null', function(done){

        // handle the stream
        pluginFunc(null, function(err, content){

          // check for a error
          if(!err) assert.fail('Was expecting a error for the null content');

          // done
          done();

        });

      });

      // handle the settings
      it('Should return a pretified version from our passed in CSS', function(done){

        // handle the stream
        pluginFunc('.text{color:black;}', function(err, content){

          // check for a error
          if(err) assert.fail(err);

          // check if not empty
          if(S(content).isEmpty() == true)
            assert.fail('Prettified content was blank')

          // should match a cleaned version
          if(content.replace(/\s+/gi, '') != '.text{color: black;}'.replace(/\s+/gi, ''))
            assert.fail('Did not clean the content correctly.');

          // done
          done();

        });

      });

  });

});

Update to not use newer ES6 function so we can support Node 0.12


/**
 * <%= name %>
 *
 * Makes bla bla.
 *
 * @version 0.0.0
 * @requires jQuery
 * @author <%= authorName %>
 * @copyright <%= (new Date).getFullYear() %> <%= authorName %>, <%= authorUrl %>
 * @license MIT
 */

/*jshint browser:true, jquery:true, white:false, smarttabs:true, eqeqeq:true,
         immed:true, latedef:false, newcap:true, undef:true */
/*global define:false*/
;(function(factory) {  // Try to register as an anonymous AMD module
	if (typeof define === 'function' && define.amd) {
		define(['jquery'], factory);
	} else {
		factory(jQuery);
	}
}(function($) {
	'use strict';

	$.fn.<%= method %> = function(options) {
		options = $.extend({}, $.fn.<%= method %>.defaults, options);

		return this.each(function() {
			// var elem = $(this);
			new <%= cls %>($(this), options);
		});
	};

	$.fn.<%= method %>.defaults = {
	};

	function <%= cls %>(container, options) {
		this.container = container;
		this.options = options;

		this.init();
	}

	<%= cls %>.prototype = {
		init: function() {
			/* Magic begins here */
		}
	};

}));

/**
 * <%= name %>
 *
 * Makes bla bla.
 *
 * @version 0.0.0
 * @requires jQuery
 * @author <%= authorName %>
 * @copyright <%= (new Date).getFullYear() %> <%= authorName %>, <%= authorUrl %>
 * @license MIT
 */

/*global define:false*/
;(function(factory) {  // Try to register as an anonymous AMD module
	if (typeof define === 'function' && define.amd) {
		define(['jquery'], factory);
	}
	else {
		factory(jQuery);
	}
}(function($) {
	'use strict';

	$.fn.<%= method %> = function(options) {
		options = $.extend({}, $.fn.<%= method %>.defaults, options);

		return this.each(function() {
			// var elem = $(this);
			new <%= cls %>($(this), options);
		});
	};

	$.fn.<%= method %>.defaults = {
	};

	function <%= cls %>(container, options) {
		this.container = container;
		this.options = options;

		this.init();
	}

	<%= cls %>.prototype = {
		init: function() {
			/* Magic begins here */
		}
	};

}));

Fix code style, remove JSHint comment.


define(['backbone', 'handlebars', 'views/row', 'text!../../../src/templates/datagrid.hbs'], function(Backbone, Handlebars, Row, datagridTemplate) {

  var Datagrid = Backbone.View.extend({
    initialize: function() {
      this.collection.on('reset', this.render, this);
      this._prepareColumns();
    },

    render: function() {
      var template = Handlebars.compile(datagridTemplate);
      var html     = template({
        columns: this.columns
      });
      this.$el.html(html);

      this.collection.forEach(this.renderRow, this);

      return this;
    },

    renderRow: function(model) {
      var row = new Row({model: model, columns: this.columns});
      this.$('tbody').append(row.render(this.columns).el);
    },

    _prepareColumns: function() {
      this.columns = [];
      var model = this.collection.first();
      for (var p in model.toJSON()) {
        this.columns.push({
          name:     p.charAt(0).toUpperCase() + p.substr(1),
          property: p
        });
      }
    }
  });

  return Datagrid;

});

define(['backbone', 'handlebars', 'views/row', 'text!../../../src/templates/datagrid.hbs'], function(Backbone, Handlebars, Row, datagridTemplate) {

  var Datagrid = Backbone.View.extend({
    initialize: function() {
      this.collection.on('reset', this.render, this);
      this._prepareColumns();
    },

    render: function() {
      var template = Handlebars.compile(datagridTemplate);
      var html     = template({
        columns: this.columns
      });
      this.$el.html(html);

      this.collection.forEach(this.renderRow, this);

      return this;
    },

    renderRow: function(model) {
      var row = new Row({model: model, columns: this.columns});
      this.$('tbody').append(row.render(this.columns).el);
    },

    sort: function(column) {
      this.collection.comparator = function(model) {
        return model.get(column.property);
      };
      this.collection.sort();
    },

    _prepareColumns: function() {
      this.columns = [];
      var model = this.collection.first();
      for (var p in model.toJSON()) {
        this.columns.push({
          name:     p.charAt(0).toUpperCase() + p.substr(1),
          property: p
        });
      }
    }
  });

  return Datagrid;

});

Add a sort function which is not bind on any event for now.


BustinBash.Directory.View = function() {}

BustinBash.Directory.View.prototype = {

	hideAndShowDOM: function(data) {
		$(data.Hide).hide();
		$(data.Show).show();
		this.clearDom();
	},

	clearDom: function() {
		$('#directory-template li').hide();
	},

	appendCurrentFolder: function(data) {
		$('.current-folder').text(data)
	}
}

BustinBash.Directory.Controller = function(view) {
	this.view = view;
}

BustinBash.Directory.Controller.prototype = {
	init: function() {
		this.bindListeners();
	},
	bindListeners: function() {
		$(document).on('changeLevel', function(event, data) {
			this.data = data;
			this.clickLevel(this.data)
		}.bind(this));

		$(document).on('success', function() {
			if(this.data.ID <= 12 ){
				this.checkLevel(this.data)
			}
		}.bind(this));
	},

	checkLevel: function(data) {
		this.view.hideAndShowDOM(data);
	},

	clickLevel: function(data){
		$(document).on('clickLevel', function(event, data){
			this.view.hideAndShowDOM(data);
		}.bind(this))
	}

}

BustinBash.Directory.View = function() {}

BustinBash.Directory.View.prototype = {

	hideAndShowDOM: function(data) {
		if(data != undefined){
			$(data.Hide).hide();
			$(data.Show).show();
			this.clearDom();
		}
	},

	clearDom: function() {
		$('#directory-template li').hide();
	},

	appendCurrentFolder: function(data) {
		$('.current-folder').text(data)
	}
}

BustinBash.Directory.Controller = function(view) {
	this.view = view;
}

BustinBash.Directory.Controller.prototype = {
	init: function() {
		this.bindListeners();
	},
	bindListeners: function() {
		$(document).on('changeLevel', function(event, data) {
			this.data = data;
			this.clickLevel(this.data)
		}.bind(this));

		$(document).on('success', function() {
			if(this.data.ID <= 12 ){
				this.checkLevel(this.data)
			}
		}.bind(this));
	},

	checkLevel: function(data) {
		this.view.hideAndShowDOM(data);
	},

	clickLevel: function(data){
		$(document).on('clickLevel', function(event, data){
			this.view.hideAndShowDOM(data);
		}.bind(this))
	}

}

Fix a bug while clicking the first button to show the lesson


/**
 * @fileOverview Level 01 controller.
 */

var Vector = require('../vector/main.vector');

/**
 * Level 01 controller.
 *
 * @constructor
 */
var Level01 = module.exports = function () {
  this.vector = new Vector();
};

/**
 * Initialize Level 01.
 *
 */
Level01.prototype.init = function() {
  this.vector.makeLine(50,200, 500, 200);

  this.vector.update();
};

/**
 * @fileOverview Level 01 controller.
 */

var Vector = require('../vector/main.vector');
var Level = require('../level/level.base');

/**
 * Level 01 controller.
 *
 * @constructor
 */
var Level01 = module.exports = function () {
};

/**
 * Initialize Level 01.
 *
 */
Level01.prototype.init = function() {

  this.vector = new Vector();

  this.level = new Level(this.vector);

  this.level.makeLine(50,200, 500, 200);

  this.vector.update();
};

Adjust new API on controller


const rules = require('./index').rules;
const RuleTester = require('eslint').RuleTester;
const ruleTester = new RuleTester();

ruleTester.run('no-only-tests', rules['no-only-tests'], {
  valid: [
    {code: 'describe("Some describe block", function() {});'},
    {code: 'it("Some assertion", function() {});'},
    {code: 'xit.only("Some assertion", function() {});'},
    {code: 'xdescribe.only("Some describe block", function() {});'},
    {code: 'xcontext.only("A context block", function() {});'},
    {code: 'xtape.only("A tape block", function() {});'},
    {code: 'xtest.only("A test block", function() {});'},
    {code: 'other.only("An other block", function() {});'}
  ],

  invalid: [{
    code: 'describe.only("Some describe block", function() {});',
    errors: [{message: 'describe.only not permitted'}]
  }, {
    code: 'it.only("Some assertion", function() {});',
    errors: [{message: 'it.only not permitted'}]
  }, {
    code: 'context.only("Some context", function() {});',
    errors: [{message: 'context.only not permitted'}]
  }, {
    code: 'test.only("Some test", function() {});',
    errors: [{message: 'test.only not permitted'}]
  }, {
    code: 'tape.only("A tape", function() {});',
    errors: [{message: 'tape.only not permitted'}]
  }]
});

console.log('Tests completed successfully');

const rules = require('./index').rules;
const RuleTester = require('eslint').RuleTester;
const ruleTester = new RuleTester();

ruleTester.run('no-only-tests', rules['no-only-tests'], {
  valid: [
    'describe("Some describe block", function() {});',
    'it("Some assertion", function() {});',
    'xit.only("Some assertion", function() {});',
    'xdescribe.only("Some describe block", function() {});',
    'xcontext.only("A context block", function() {});',
    'xtape.only("A tape block", function() {});',
    'xtest.only("A test block", function() {});',
    'other.only("An other block", function() {});',
    'var args = {only: "test"}',
  ],

  invalid: [{
    code: 'describe.only("Some describe block", function() {});',
    errors: [{message: 'describe.only not permitted'}]
  }, {
    code: 'it.only("Some assertion", function() {});',
    errors: [{message: 'it.only not permitted'}]
  }, {
    code: 'context.only("Some context", function() {});',
    errors: [{message: 'context.only not permitted'}]
  }, {
    code: 'test.only("Some test", function() {});',
    errors: [{message: 'test.only not permitted'}]
  }, {
    code: 'tape.only("A tape", function() {});',
    errors: [{message: 'tape.only not permitted'}]
  }]
});

console.log('Tests completed successfully');

Add failing test for 'only' object keys


'use strict';

const http = require('http');

const { host, port } = require('../../../config');
const { addStopFunctions } = require('./stop');

// Start HTTP server
const startServer = function ({
  serverState: { handleRequest, handleListening, processLog },
}) {
  const server = http.createServer(function requestHandler(req, res) {
    handleRequest({ req, res });
  });

  addStopFunctions(server);
  server.protocolName = 'HTTP';

  server.listen(port, host, function listeningHandler() {
    handleListening({ protocol: 'HTTP', host, port });
  });

  handleClientError({ server, log: processLog });

  const promise = new Promise((resolve, reject) => {
    server.on('listening', () => resolve(server));
    server.on('error', () => reject());
  });

  return promise;
};

// Report TCP client errors
const handleClientError = function ({ server, log }) {
  server.on('clientError', async (error, socket) => {
    const message = 'Client TCP socket error';
    await log.process({ value: error, message });

    socket.end('');
  });
};

module.exports = {
  HTTP: {
    startServer,
  },
};

'use strict';

const http = require('http');

const { host, port } = require('../../../config');
const { addStopFunctions } = require('./stop');

// Start HTTP server
const startServer = function ({
  serverState: { handleRequest, handleListening, processLog },
}) {
  const server = http.createServer(function requestHandler(req, res) {
    handleRequest({ req, res });
  });

  addStopFunctions(server);
  server.protocolName = 'HTTP';

  server.listen(port, host, function listeningHandler() {
    const { address: usedHost, port: usedPort } = server.address();
    handleListening({ protocol: 'HTTP', host: usedHost, port: usedPort });
  });

  handleClientError({ server, log: processLog });

  const promise = new Promise((resolve, reject) => {
    server.on('listening', () => resolve(server));
    server.on('error', error => reject(error));
  });

  return promise;
};

// Report TCP client errors
const handleClientError = function ({ server, log }) {
  server.on('clientError', async (error, socket) => {
    const message = 'Client TCP socket error';
    await log.process({ value: error, message });

    socket.end('');
  });
};

module.exports = {
  HTTP: {
    startServer,
  },
};

Fix missing server error information


import {GroupMark} from '../marks/marktypes';

export default function(role, style, name, dataRef, interactive, encode, marks) {
  return {
    type: GroupMark,
    name: name,
    role: role,
    style: style,
    from: dataRef,
    interactive: interactive || false,
    encode: encode,
    marks: marks
  };
}

import {GroupMark} from '../marks/marktypes';

export default function(role, style, name, dataRef, interactive, encode, marks, layout) {
  return {
    type: GroupMark,
    name: name,
    role: role,
    style: style,
    from: dataRef,
    interactive: interactive || false,
    encode: encode,
    marks: marks,
    layout: layout
  };
}

Update guide group util to include layout option.


var http = require('http');
var phantom=require('node-phantom-simple');

http.createServer(function (req, res) {
  phantom.create(function(err,ph) {
    return ph.createPage(function(err, page) {
      return page.open(req.url.slice(1), function(err, status) {
        //Wait for a bit for AJAX content to load on the page. Better solution?
        setTimeout(function() {
          page.renderBase64("PNG", function(error, result){
            var imageBuffer = new Buffer(result, 'base64');
            res.writeHead(200, {
              'Content-Type': 'image/png',
              'Content-Length': imageBuffer.length});
            res.end(imageBuffer);
          });
        }, 2000);
      });
    });
  });
}).listen(1337, '127.0.0.1');

console.log('Server running at port 1337');

var http = require('http');
var phantom=require('node-phantom-simple');

http.createServer(function (req, res) {
  phantom.create(function(err,ph) {
    return ph.createPage(function(err, page) {
      return page.open(req.url.slice(1), function(err, status) {
        //Wait for a bit for AJAX content to load on the page. Better solution?
        setTimeout(function() {
          page.renderBase64("PNG", function(error, result){
            var imageBuffer = new Buffer(result, 'base64');
            res.writeHead(200, {
              'Content-Type': 'image/png',
              'Content-Length': imageBuffer.length});
            res.end(imageBuffer);
            ph.exit()
          });
        }, 2000);
      });
    });
  });
}).listen(1337, '127.0.0.1');

console.log('Server running at port 1337');

Exit phantomJS after every screenshot to kill phantom process


const fs = require('fs')
const Twit = require('twit')
const config = require('./config')

const quotes = JSON.parse(fs.readFileSync('./data/backup-2016-11-03.json', 'utf8'))
const T = new Twit(config.oauth_creds)

function randomQuote () {
  return quotes[Math.floor(Math.random() * quotes.length)]
}

function tweetMessage (quote) {
  let msg = quote.msg + '\n' + quote.src
  T.post('statuses/update', {status: msg}, (err, res) => {
    if (err) {
      console.error('--->>>')
      console.error(msg)
      console.error(quote)
      console.dir(err)
    } else {
      console.log('tweet succeed.')
      console.log(res.text)
    }
  })
}

tweetMessage(randomQuote())

const quotes = JSON.parse(require('fs').readFileSync('./data/backup-2016-11-03.json', 'utf8'))
const config = require('./config')
const Twit = require('twit')
const T = new Twit(config.oauth_creds)

function randomQuote () {
  return quotes[Math.floor(Math.random() * quotes.length)]
}

function tweetMessage (quote) {
  let msg = quote.msg + '\n' + quote.src
  T.post('statuses/update', {status: msg}, (err, res) => {
    if (err) {
      console.error('--->>>')
      console.error(msg)
      console.error(quote)
      console.dir(err)
    } else {
      console.log('tweet succeed at ', new Date())
      console.log(res.text)
    }
  })
}

tweetMessage(randomQuote())

Clean up code and add timestamp


var util = require('util')
  , twitter = require('twitter')
  , Stream = require('stream')
  , repl = require('repl')
  , keys = require('./keys')
  , local = repl.start()
  , first = true

local.context.repl = local
local.defineCommand('tweet', function(tweet){
  twish
    .verifyCredentials(function(data) {
      console.log(util.inspect(data))
    })
    .updateStatus(tweet,
      function(data) {
        console.log(util.inspect(data))
    })
})

var twish = new twitter(keys)
//twish.get('/statuses/show/183682338646011904.json', function(data){
  //console.log(data)
//})
//twish.stream('statuses/sample', function(stream){
twish.stream('user', {track:'gkatsev', delimited:20}, function(stream){
  stream.on('data', function(data){
    if(first){
      first = false
      return
    }
    setTimeout(function(){
      process.stdout.write(JSON.stringify(data, null, '  '))
    }, 500)
  })
})

module.exports = twish

var util = require('util')
  , twitter = require('twitter')
  , Stream = require('stream')
  , repl = require('repl')
  , keys = require('./keys')
  , local = repl.start()
  , first = true

local.context.repl = local
local.defineCommand('tweet', function(tweet){
  twish
    .verifyCredentials(function(data) {
      console.log(util.inspect(data))
    })
    .updateStatus(tweet,
      function(data) {
        console.log(util.inspect(data))
    })
})
local.commands['.tweet'].help = 'Tweet as currently signed in user'

var twish = new twitter(keys)
//twish.get('/statuses/show/183682338646011904.json', function(data){
  //console.log(data)
//})
//twish.stream('statuses/sample', function(stream){
twish.stream('user', {track:'gkatsev', delimited:20}, function(stream){
  stream.on('data', function(data){
    if(first){
      first = false
      return
    }
    setTimeout(function(){
      process.stdout.write(JSON.stringify(data, null, '  '))
    }, 500)
  })
})

module.exports = twish

Add help text for the tweet command


const chokidar = require('chokidar')
const compile = require('./compiler').run

module.exports = function () {
  const watcher = chokidar.watch(process.cwd(), {
    ignored: /dist|layouts|.DS_Store/
  })

  console.log('Watching for changes...')

  process.on('SIGINT', function () {
    watcher.close()
    process.exit(0)
  })

  watcher.on('change', (which) => {
    const paths = new function () {
      this.full = which
      this.relative = this.full.replace(process.cwd(), '').replace('scss', 'css')
    }

    compile(paths, () => {
      console.log('File ' + paths.relative.gray + ' changed, rebuilt finished')
    })
  })
}

const chokidar = require('chokidar')
const path = require('path')
const walk = require('walk')
const compile = require('./compiler').run

function finished (paths) {
  console.log('File ' + paths.relative.gray + ' changed, rebuilt finished')
}

module.exports = function () {
  const watcher = chokidar.watch(process.cwd(), {
    ignored: /dist|.DS_Store/
  })

  console.log('Watching for changes...')

  process.on('SIGINT', function () {
    watcher.close()
    process.exit(0)
  })

  watcher.on('change', (which) => {
    var dir = path.dirname(which)
    var lastSlash = dir.lastIndexOf('/')

    dir = dir.substring(lastSlash + 1)

    const Paths = function () {
      this.full = which
      this.relative = this.full.replace(process.cwd(), '').replace('scss', 'css')
    }

    if (dir === 'layouts') {
      const walker = walk.walk(process.cwd() + '/pages')

      walker.on('file', function (root, fileStat, next) {
        var newPaths = new Paths

        newPaths.full = path.resolve(root, fileStat.name)
        newPaths.relative = newPaths.full.replace(process.cwd(), '')

        compile(newPaths, next)
      })

      walker.on('end', function() {
        finished(new Paths)
      })

      return
    }

    const originals = new Paths
    compile(originals, finished(originals))
  })
}

Rebuild pages if their layout changes


export default function({
  renderedXAxis,
  renderedYAxis,
  xLabel,
  yLabel,
  width
}) {
  let _y = this.axis == 'y'
  let axis = _y ? renderedYAxis : renderedXAxis

  if (axis) {
    let label = _y ? yLabel : xLabel
    let _label = axis.append('text')
      .attr('class', 'label')
      .attr('y', 15)
      .style('text-anchor', 'end')
      .text(label)

    if (_y) {
      _label
        .attr('transform', 'rotate(-90)')
    } else {
      _label
        .attr('x', width)
    }
  }
}

export default function({
  renderedXAxis,
  renderedYAxis,
  xLabel,
  yLabel,
  width
}) {
  let _y = this.axis == 'y'
  let axis = _y ? renderedYAxis : renderedXAxis

  if (axis) {
    let label = _y ? yLabel : xLabel
    let _label = axis.append('text')
      .attr('class', 'label')
      .style('text-anchor', 'end')
      .text(label)

    if (_y) {
      _label
        .attr('y', 15)
        .attr('transform', 'rotate(-90)')
    } else {
      _label
        .attr('y', -6)
        .attr('x', width)
    }
  }
}

Fix rendering of x-Axis labels


var five = require("../lib/johnny-five.js"),
    board, photoresistor;

board = new five.Board();

board.on("ready", function() {

  // Create a new `photoresistor` hardware instance.
  photoresistor = new five.Sensor({
    pin: "A2",
    freq: 250
  });

  // Inject the `sensor` hardware into
  // the Repl instance's context;
  // allows direct command line access
  board.repl.inject({
    pot: photoresistor
  });

  // "read" get the current reading from the photoresistor
  photoresistor.on("read", function( err, value ) {
    console.log( this.value );
  });
});

// References
//
// http://nakkaya.com/2009/10/29/connecting-a-photoresistor-to-an-arduino/

var five = require("../lib/johnny-five.js"),
    board, photoresistor;

board = new five.Board();

board.on("ready", function() {

  // Create a new `photoresistor` hardware instance.
  photoresistor = new five.Sensor({
    pin: "A2",
    freq: 250
  });

  // Inject the `sensor` hardware into
  // the Repl instance's context;
  // allows direct command line access
  board.repl.inject({
    pot: photoresistor
  });

  // "read" get the current reading from the photoresistor
  photoresistor.on("read", function() {
    console.log( this.value );
  });
});

// References
//
// http://nakkaya.com/2009/10/29/connecting-a-photoresistor-to-an-arduino/

Remove err, value in favor of this.value


var
	extend        = req('/lib/utilities/extend'),
	BaseError     = req('/lib/errors/base'),
	ErrorCodes    = req('/lib/constants/error-codes'),
	ErrorReasons  = req('/lib/constants/error-reasons'),
	createMessage = req('/lib/utilities/create-message'),
	ReplyNumerics = req('/lib/constants/reply-numerics'),
	Commands      = req('/lib/constants/commands');

class InvalidChannelNameError extends BaseError {

	getBody() {
		switch (this.reason) {
			case ErrorReasons.OMITTED:
				return 'You must specify a channel name';
			case ErrorReasons.INVALID_CHARACTERS:
				return 'Invalid channel name: ' + this.getChannelName();
			case ErrorReasons.NOT_FOUND:
				return 'Channel did not exist: ' + this.getChannelName();
			default:
				return 'Invalid channel name: ' + this.reason;
		}
	}

	getChannelName() {
		return this.value;
	}

	toMessage() {
		var command = this.getCommand();

		switch (command) {
			case Commands.MODE:
				let message = createMessage(ReplyNumerics.ERR_NOSUCHCHANNEL);

				return message.setChannelName(this.getValue());

			default:
				throw new Error('implement');
		}
	}

}

extend(InvalidChannelNameError.prototype, {
	code: ErrorCodes.INVALID_CHANNEL_NAME
});

module.exports = InvalidChannelNameError;

var
	extend        = req('/lib/utilities/extend'),
	BaseError     = req('/lib/errors/base'),
	ErrorCodes    = req('/lib/constants/error-codes'),
	ErrorReasons  = req('/lib/constants/error-reasons'),
	createMessage = req('/lib/utilities/create-message'),
	ReplyNumerics = req('/lib/constants/reply-numerics'),
	Commands      = req('/lib/constants/commands');

class InvalidChannelNameError extends BaseError {

	getBody() {
		switch (this.reason) {
			case ErrorReasons.OMITTED:
				return 'You must specify a channel name';
			case ErrorReasons.INVALID_CHARACTERS:
				return 'Invalid channel name: ' + this.getChannelName();
			case ErrorReasons.NOT_FOUND:
				return 'Channel did not exist: ' + this.getChannelName();
			default:
				return 'Invalid channel name: ' + this.reason;
		}
	}

	getChannelName() {
		return this.value;
	}

	toMessage() {
		var command = this.getCommand();

		switch (command) {
			case Commands.JOIN:
			case Commands.MODE:
			case Commands.PART:
				let message = createMessage(ReplyNumerics.ERR_NOSUCHCHANNEL);

				return message.setChannelName(this.getValue());

			default:
				throw new Error('implement');
		}
	}

}

extend(InvalidChannelNameError.prototype, {
	code: ErrorCodes.INVALID_CHANNEL_NAME
});

module.exports = InvalidChannelNameError;

Add handling to InvalidChannelNameError for generating reply messages for several other commands


const nearley = require('nearley');
const grammar = require('./grammar/grammar');
const parser = new nearley.Parser(nearley.Grammar.fromCompiled(grammar));

module.exports = function parse(input) {
  return parser.feed(input);
}

const nearley = require('nearley');
const grammar = require('./grammar/grammar');

module.exports = function parse(input) {
  const parser = new nearley.Parser(nearley.Grammar.fromCompiled(grammar));
  return parser.feed(input).finish().results[0];
};

Fix issue with sharing states between different parsings


'use strict';

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

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

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

      output.push(branchName);

      var branchSHA  = fs.readFileSync(branchPath);
      output.push(branchSHA.slice(0,10));
    }
  } catch (err) {
    console.error(err.stack);
  }

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

'use strict';

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

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

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

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

      output.push(branchName);

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

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

Trim error were thrown when linked ember-cli had deatched HEAD.


;(function($) {

  // initalize Editable
  Editable.init({
    log: false
  });

  $(document).ready(function() {
    var tooltip = $('
How may I help?
')
    $(document.body).append(tooltip);

    $("article>div>p, article>div li").editable();
    Editable.focus(function(el) {
      console.log('Focus event handler was triggered on', el);

    }).blur(function(el) {
      console.log('Blur event handler was triggered on', el);

      // todo: this should not be necessary here
      tooltip.hide();

    }).selection(function(el, selection) {
      if (selection) {
        coords = selection.getCoordinates()

        // position tooltip
        var top = coords.top - tooltip.outerHeight() - 15;
        var left = coords.left + (coords.width / 2) - (tooltip.outerWidth() / 2);
        tooltip.show().css('top', top).css('left', left);
      } else {
        tooltip.hide();
      }
    });
  });
})(jQuery);

;(function($) {

  // initalize Editable
  Editable.init({
    log: false
  });

  var setupTooltip = function() {
    var tooltip = $('
How may I help?
')
    $(document.body).append(tooltip);

    Editable.selection(function(el, selection) {
      if (selection) {
        coords = selection.getCoordinates()

        // position tooltip
        var top = coords.top - tooltip.outerHeight() - 15;
        var left = coords.left + (coords.width / 2) - (tooltip.outerWidth() / 2);
        tooltip.show().css('top', top).css('left', left);
      } else {
        tooltip.hide();
      }
    }).blur(function(el) {
      // todo: this should not be necessary here
      tooltip.hide();
    });
  };

  $(document).ready(function() {
    $("article>div>p, article>div li").editable();
    setupTooltip();
  });

})(jQuery);

Clean up js for index.html a bit


var path = require('path');
var rootPath = path.join(__dirname, '..')

module.exports = {
    '~': path.join(rootPath, 'src', 'client'),
    '~server': path.join(rootPath, 'src', 'server'),
    '~root': rootPath,
    'react': 'preact-compat',
	'react-dom': 'preact-compat'
}

var path = require('path');
var rootPath = path.join(__dirname, '..')

module.exports = {
    '~': path.join(rootPath, 'src', 'client'),
    '~server': path.join(rootPath, 'src', 'server'),
    '~root': rootPath
}

Remove preact for now. Problems with event bubbling.


'use strict'

module.exports = function readBoundingBox (buffer, offset) {
  const Xmin = buffer.readDoubleLE(offset, true)
  const Ymin = buffer.readDoubleLE(offset + 8, true)
  const Xmax = buffer.readDoubleLE(offset + 16, true)
  const Ymax = buffer.readDoubleLE(offset + 24, true)
  return {Xmin, Ymin, Xmax, Ymax}
}

'use strict'

module.exports = function readBoundingBox (buffer, offset) {
  const xMin = buffer.readDoubleLE(offset, true)
  const yMin = buffer.readDoubleLE(offset + 8, true)
  const xMax = buffer.readDoubleLE(offset + 16, true)
  const yMax = buffer.readDoubleLE(offset + 24, true)
  return {xMin, yMin, xMax, yMax}
}

Change caseing to conform with shp header


/* jslint browser: true */
/* global $ */
(function(){
  var makeCloneable = function(selector) {
    var scope = $(selector);

    var update_remove_one_visibility = function() {
      var hidden = scope.find('.cloneable').length < 2;
      scope.find('.remove-one').toggleClass('hidden', hidden);
    };

    update_remove_one_visibility();

    scope.find('.add-one').click(function(e) {
      e.preventDefault();

      var newContent = scope.find('.cloneable:first').clone();
      scope.find('.cloneable:last').after(newContent);

      scope.find('.cloneable:last input[type="text"]').val('');
      update_remove_one_visibility();
    });

    scope.find('.remove-one').click(function(e) {
      e.preventDefault();

      scope.find('.cloneable:last').remove();
      update_remove_one_visibility();
    });
  };

  $(document).ready(function() {
    makeCloneable('#budgetary-responsibilities');
  });
})();

/* jslint browser: true */
/* global $ */
(function(){
  var makeCloneable = function(selector) {
    var scope = $(selector);

    var update_remove_one_visibility = function() {
      var hidden = scope.find('.cloneable').length < 2;
      scope.find('.remove-one').toggleClass('hidden', hidden);
    };

    update_remove_one_visibility();

    scope.find('.add-one').click(function(e) {
      e.preventDefault();

      var newContent = scope.find('.cloneable:first').clone();
      scope.find('.cloneable:last').after(newContent);

      scope.find('.cloneable:last input[type="text"]').val('');
      scope.find('.cloneable:last textarea').text('');
      update_remove_one_visibility();
    });

    scope.find('.remove-one').click(function(e) {
      e.preventDefault();

      scope.find('.cloneable:last').remove();
      update_remove_one_visibility();
    });
  };

  $(document).ready(function() {
    makeCloneable('#budgetary-responsibilities');
  });
})();

Clear textarea in cloned row


'use strict';

export class Timer {
	constructor() {
		this.reset();
	}

	/**
	 * Start the timer
	*/
	start() {
		if(this._ran) throw new Error('timer already ran. use reset() first');
		if(this._running) throw new Error('timer currently running');
		this._hrtime = process.hrtime();
		this._running = true;
	}

	/**
	 * Stop the timer
	*/
	stop() {
		if(!this._running) throw new Error('timer not running');
		this._hrtime = process.hrtime(this._hrtime);
		this._runtime = (this._hrtime[0] * 1000) + (this._hrtime[1] / 1000000);
		this._running = false;
		this._ran = true;
	}

	/**
	 * Get the timer's time
	*/
	time() {
		if(!this._ran) throw new Error('timer not ran yet');
		if(this._running) throw new Error('timmer currently running');
		return this._runtime;
	}

	/**
	 * Return true if the timer has ran and finished
	*/
	isFinished() {
		return this._ran;
	}

	/**
	 * Rest timer variables
	*/
	reset() {
		this._hrtime = null;
		this._runtime = 0;
		this._running = false;
		this._ran = false;
	}
}

'use strict';

export class Timer {
	constructor() {
		this.reset();
	}

	/**
	 * Start the timer
	*/
	start() {
		if(this._ran) throw new Error('timer already ran. use reset() first');
		if(this._running) throw new Error('timer currently running');
		this._running = true;
		this._hrtime = process.hrtime();
	}

	/**
	 * Stop the timer
	*/
	stop() {
		if(!this._running) throw new Error('timer not running');
		this._hrtime = process.hrtime(this._hrtime);
		this._runtime = (this._hrtime[0] * 1000) + (this._hrtime[1] / 1000000);
		this._running = false;
		this._ran = true;
	}

	/**
	 * Get the timer's time
	*/
	time() {
		if(!this._ran) throw new Error('timer not ran yet');
		if(this._running) throw new Error('timmer currently running');
		return this._runtime;
	}

	/**
	 * Return true if the timer has ran and finished
	*/
	isFinished() {
		return this._ran;
	}

	/**
	 * Rest timer variables
	*/
	reset() {
		this._hrtime = null;
		this._runtime = 0;
		this._running = false;
		this._ran = false;
	}
}

Move running flag to before htrtime start


/*jshint node:true*/
/* global require, module */
var EmberApp = require('ember-cli/lib/broccoli/ember-app');

module.exports = function(defaults) {
  var app = new EmberApp(defaults, {
    // Add options here
  });

  // Use `app.import` to add additional libraries to the generated
  // output files.
  //
  // If you need to use different assets in different
  // environments, specify an object as the first parameter. That
  // object's keys should be the environment name and the values
  // should be the asset to use in that environment.
  //
  // If the library that you are including contains AMD or ES6
  // modules that you would like to import into your application
  // please specify an object with the list of modules as keys
  // along with the exports of each module as its value.

  return app.toTree();
};

/*jshint node:true*/
/* global require, module */
var EmberApp = require('ember-cli/lib/broccoli/ember-app');

module.exports = function(defaults) {
  var app = new EmberApp(defaults, {
    storeConfigInMeta: false,
  });

  // Use `app.import` to add additional libraries to the generated
  // output files.
  //
  // If you need to use different assets in different
  // environments, specify an object as the first parameter. That
  // object's keys should be the environment name and the values
  // should be the asset to use in that environment.
  //
  // If the library that you are including contains AMD or ES6
  // modules that you would like to import into your application
  // please specify an object with the list of modules as keys
  // along with the exports of each module as its value.

  return app.toTree();
};

Store config in JS file


angular.module('createPageController.controller', [])
  .controller('createPageController', function($scope, $http, $routeParams, $location, pageService) {
    $scope.message = "first";
    $scope.name = $routeParams.name;

    $scope.createPage = function(pass) {
      pageService.createPage($routeParams.name, pass).success(function(res) {
        $location.path('/' + $routeParams.name);
      });
    };
  });

angular.module('createPageController.controller', [])
  .controller('createPageController', function($scope, $http, $routeParams, $location, pageService) {
    $scope.message = "first";
    $scope.name = $routeParams.name;

    pageService.getPage($routeParams.name).success(function() {
      $location.path('/' + $routeParams.name);
    });

    $scope.createPage = function(pass) {
      pageService.createPage($routeParams.name, pass).success(function(res) {
        $location.path('/' + $routeParams.name);
      });
    };
  });

Create page will redirect to full page if already created


$(function(){

	readFileIntoBuffer(batotoJSONFile, function(buffer){

		if (buffer){
			batotoJSON = JSON.parse(buffer);
			if ('chapters' in batotoJSON){
				$.each(batotoJSON.chapters, function(index, val) {
					addRowToTable(val);
				});
			}
		}

		$("#urlEntry").keypress(function (e) {
		 if (e.which == 13){
		 	var val = $(this).val();
		    parseUrl(val);
			$(this).val('');
		    return false;
		  }
		});

		if (isReadClipboard()){
			readClipboard();
		}

	});

});

function readClipboard(){

	var data = clipboard.readText();

	console.log('Clipboard: '+data);

	if (data.length > 0 && batotoRegex.test(data)){
		console.log('test succeeded');
		clipboard.writeText(''); //Clear clipboard, so we don't parse the same url twice
		var matches = data.match(batotoRegex);
		console.log(matches);
		var len = matches.length;

		for (var index = 0; i < len; index++){
			parseUrl(matches[index]);
		}
	}

	setTimeout(readClipboard,1000);

}

$(function(){

	readFileIntoBuffer(batotoJSONFile, function(buffer){

		if (buffer){
			batotoJSON = JSON.parse(buffer);
			if ('chapters' in batotoJSON){
				$.each(batotoJSON.chapters, function(index, val) {
					addRowToTable(val);
				});
			}
		}

		$("#urlEntry").keypress(function (e) {
		 if (e.which == 13){
		 	var val = $(this).val();
		    parseUrl(val);
			$(this).val('');
		    return false;
		  }
		});

		if (isReadClipboard()){
			readClipboard();
		}

		checkRssFeed(true);

	});

});

function readClipboard(){

	var data = clipboard.readText();

	console.log('Clipboard: '+data);

	if (data.length > 0 && batotoRegex.test(data)){
		console.log('test succeeded');
		clipboard.writeText(''); //Clear clipboard, so we don't parse the same url twice
		var matches = data.match(batotoRegex);
		console.log(matches);
		var len = matches.length;

		for (var index = 0; i < len; index++){
			parseUrl(matches[index]);
		}
	}

	setTimeout(readClipboard,1000);

}

Check RSS feed on startup