var ejs = require('ejs');
var path = require('path');
var merge = require('merge');
var utils = require('loader-utils');
var terser = require('terser');
var htmlmin = require('html-minifier');

module.exports = function (source) {
  this.cacheable && this.cacheable();

  // wepkack3: options
  var options = (this.hasOwnProperty("options") && (typeof this.options['ejs-compiled-loader'] === 'object')) ? this.options['ejs-compiled-loader'] : {};

  // webpack4: query
  var query = (this.hasOwnProperty("query")) ? (typeof this.query === 'object') ? this.query : utils.parseQuery(this.query) : {};

  // merge opts from defaults,opts and query
  var opts = merge({
    client: true,
    compileDebug: !!this.minimize,
    minimize: (typeof this.minimize === 'boolean') ? this.minimize : false,
    beautify: false,
    htmlmin: (typeof this.htmlmin === 'boolean') ? this.htmlmin : false,
    htmlminOptions: {}
  }, options, query);

  // minify html
  if (opts.htmlmin) source = htmlmin.minify(source, opts.htmlminOptions);

  // compile template
  var template = ejs.compile(source, merge(opts, {
    filename: path.relative(process.cwd(), this.resourcePath),
    webpack: this
  })).toString();

  // minify js with terser
  if (opts.minimize) template = terser.minify(template, { output: { beautify: opts.beautify }}).code;

  return 'module.exports = ' + template;

};

var ejs = require('ejs');
var path = require('path');
var merge = require('merge');
var utils = require('loader-utils');
var terser = require('terser');
var htmlmin = require('html-minifier');

module.exports = function (source) {
  this.cacheable && this.cacheable();

  // wepkack3: options
  var options = utils.getOptions(this);

  // merge opts from defaults,opts and query
  var opts = merge({
    client: true,
    compileDebug: !!this.minimize,
    minimize: (typeof this.minimize === 'boolean') ? this.minimize : false,
    beautify: false,
    htmlmin: (typeof this.htmlmin === 'boolean') ? this.htmlmin : false,
    htmlminOptions: {}
  }, options);

  // minify html
  if (opts.htmlmin) source = htmlmin.minify(source, opts.htmlminOptions);

  // compile template
  var template = ejs.compile(source, merge(opts, {
    filename: path.relative(process.cwd(), this.resourcePath),
    webpack: this
  })).toString();

  // minify js with terser
  if (opts.minimize) template = terser.minify(template, { output: { beautify: opts.beautify }}).code;

  return 'module.exports = ' + template;

};

Use utils.getOptions() to get loader options


'use strict';

const electron = require('electron');
const {app, BrowserWindow, ipcMain} = electron;

console.log('Electron: \n' + electron);          // TODO: ???

let win;

function createWindow() {
    win = new BrowserWindow({width: 800, height: 600});
    win.on('closed', () => {
        win = null;
    });
    win.loadURL(`file://${__dirname}/index.html`);
}

app.on('ready', () => {console.log('Starting!'); createWindow();});
app.on('window-all-closed', () => {
    if (process.platform != 'darwin') {
        app.quit();
    }
});
app.on('activate', () => {
    if (window === null) {
        createWindow();
    }
});

'use strict';

const electron = require('electron');
const {app, BrowserWindow, ipcMain} = electron;

let win;

function createWindow() {
    win = new BrowserWindow({width: 1200, height: 800});
    win.on('closed', () => {
        win = null;
    });
    win.loadURL(`file://${__dirname}/index.html`);
}

app.on('ready', () => {console.log('Starting!'); createWindow();});
app.on('window-all-closed', () => {
    if (process.platform != 'darwin') {
        app.quit();
    }
});
app.on('activate', () => {
    if (window === null) {
        createWindow();
    }
});

Create bigger window by default.


var connect = require('connect');
var http = require('http');
var path = require('path');
var serveStatic = require('serve-static');

var app = connect();
app.use('/assets', serveStatic(path.join(path.dirname(require.resolve('classets/package')), 'public')));
app.use(serveStatic(path.join(__dirname, 'public')));

var port = process.env.PORT || 11583;
http.createServer(app).listen(port, function() {
	console.log('Server started http://localhost:' + port);
});

var connect = require('connect');
var http = require('http');
var path = require('path');
var serveStatic = require('serve-static');

var app = connect();
app.use('/assets', serveStatic(path.join(path.dirname(require.resolve('classets/package')), 'public')));
app.use(serveStatic(path.join(__dirname, 'public')));

var port = process.env.PORT || 11583;
var addr = process.env.BINDING_ADDR || 'localhost';
http.createServer(app).listen(port, host, function() {
	console.log('Server started http://' + addr + ':' + port);
});

Allow to customize the address classeur listens to


'use strict'

var express = require('express')
const querystring = require('querystring')

const config = require('../config/environment')

var router = express.Router()

router.get('/github', (req, res) => {
  var params = querystring.stringify({
    client_id: config.github.application.id,
    redirect_uri: config.github.application.redirectURI,
    state: '1234'
  })
  res.redirect('https://github.com/login/oauth/authorize?' + params)
})

router.get('/', (req, res) => {
  res.json({
    github: config.session.cookie.signed ? req.signedCookies.github : req.cookies.github
  })
})

router.delete('/', (req, res) => {
  res.clearCookie('github')
  res.json({
    github: true
  })
})

module.exports = router

'use strict'

var express = require('express')
const querystring = require('querystring')

const config = require('../config/environment')

var router = express.Router()

router.get('/github', (req, res) => {
  var params = querystring.stringify({
    client_id: config.github.application.id,
    redirect_uri: config.github.application.redirectURI,
    state: '1234',
    scopes: 'repo'
  })
  res.redirect('https://github.com/login/oauth/authorize?' + params)
})

router.get('/', (req, res) => {
  res.json({
    github: config.session.cookie.signed ? req.signedCookies.github : req.cookies.github
  })
})

router.delete('/', (req, res) => {
  res.clearCookie('github')
  res.json({
    github: true
  })
})

module.exports = router

Add repo scope in GitHub OAuth request


function (newDoc, oldDoc, userCtx) {
  function forbidden(message) {
    throw({forbidden : message});
  };

  function unauthorized(message) {
    throw({unauthorized : message});
  };

  if (userCtx.roles.indexOf('_admin') == -1) {
    // admin can edit anything, only check when not admin...
    if (newDoc._deleted)
      forbidden("You may not delete a doc.");
  }
};

function (newDoc, oldDoc, userCtx) {
  function forbidden(message) {
    throw({forbidden : message});
  };

  function unauthorized(message) {
    throw({unauthorized : message});
  };

  if ((userCtx.roles.indexOf('_admin') == -1)
      && (userCtx.roles.indexOf('hf_admins') == -1)) {
    // admin can edit anything, only check when not admin...
    if (newDoc._deleted) {
      forbidden("You must be an administrator to delete a document.");
    } else if (userCtx.roles.indexOf('hf_writers') == -1) {
      forbidden("You must have write access to change a document.");
    }
  }
};

Update security to support read-only access.


#!/usr/bin/env node

const {
  FileService,
  ExecService,
  TermApplication,
  ListScriptsCommand,
  RunScriptCommand,
  ConfigService,
  OPTION_PATH_FILE_TOKEN,
  getOptionsFilePath,
  getPackageData,
  MessageService
} = require('../src')

;(() => {
  const cli = TermApplication.createInstance()
  const pkg = getPackageData()

  cli.version = pkg.version
  cli.description = 'Custom scripts manager.'

  cli.register(RunScriptCommand, [ExecService, FileService, MessageService])
  cli.register(ListScriptsCommand, [FileService, MessageService])

  cli.provide({ identity: OPTION_PATH_FILE_TOKEN, useValue: getOptionsFilePath() })
  cli.provide(FileService, [ConfigService, MessageService])
  cli.provide(ExecService, [MessageService])
  cli.provide(ConfigService, [OPTION_PATH_FILE_TOKEN])

  cli.start()
})()

#!/usr/bin/env node

const {
  FileService,
  ExecService,
  TermApplication,
  ListScriptsCommand,
  RunScriptCommand,
  ConfigService,
  OPTION_PATH_FILE_TOKEN,
  getOptionsFilePath,
  getPackageData,
  MessageService
} = require('../src')

;(() => {
  const cli = TermApplication.createInstance()
  const pkg = getPackageData()

  cli.version = pkg.version
  cli.description = 'Custom scripts manager.'

  cli.register(RunScriptCommand, [ExecService, FileService, MessageService])
  cli.register(ListScriptsCommand, [FileService, MessageService])

  cli.provide({ identity: OPTION_PATH_FILE_TOKEN, useValue: getOptionsFilePath() })
  cli.provide(FileService, [ConfigService, MessageService])
  cli.provide(ExecService)
  cli.provide(ConfigService, [OPTION_PATH_FILE_TOKEN])

  cli.start()
})()

Remove MessageScript injection for ExecService


/*!
{
  "name": "Local Storage",
  "property": "localstorage",
  "caniuse": "namevalue-storage",
  "tags": ["storage"],
  "knownBugs": [],
  "notes": [],
  "warnings": [],
  "polyfills": [
    "joshuabell-polyfill",
    "cupcake",
    "storagepolyfill",
    "amplifyjs",
    "yui-cacheoffline"
  ]
}
!*/
define(['Modernizr'], function(Modernizr) {
  // In FF4, if disabled, window.localStorage should === null.

  // Normally, we could not test that directly and need to do a
  //   `('localStorage' in window) && ` test first because otherwise Firefox will
  //   throw bugzil.la/365772 if cookies are disabled

  // Also in iOS5 Private Browsing mode, attempting to use localStorage.setItem
  // will throw the exception:
  //   QUOTA_EXCEEDED_ERRROR DOM Exception 22.
  // Peculiarly, getItem and removeItem calls do not throw.

  // Because we are forced to try/catch this, we'll go aggressive.

  // Just FWIW: IE8 Compat mode supports these features completely:
  //   www.quirksmode.org/dom/html5.html
  // But IE8 doesn't support either with local files

  Modernizr.addTest('localstorage', function() {
    var mod = 'modernizr';
    try {
      localStorage.setItem(mod, mod);
      localStorage.removeItem(mod);
      return true;
    } catch (e) {
      return false;
    }
  });
});

/*!
{
  "name": "Local Storage",
  "property": "localstorage",
  "caniuse": "namevalue-storage",
  "tags": ["storage"],
  "knownBugs": [],
  "notes": [],
  "warnings": [],
  "polyfills": [
    "joshuabell-polyfill",
    "cupcake",
    "storagepolyfill",
    "amplifyjs",
    "yui-cacheoffline"
  ]
}
!*/
define(['Modernizr'], function(Modernizr) {
  // In FF4, if disabled, window.localStorage should === null.

  // Normally, we could not test that directly and need to do a
  //   `('localStorage' in window) && ` test first because otherwise Firefox will
  //   throw bugzil.la/365772 if cookies are disabled

  // Also in iOS5 Private Browsing mode, attempting to use localStorage.setItem
  // will throw the exception:
  //   QUOTA_EXCEEDED_ERROR DOM Exception 22.
  // Peculiarly, getItem and removeItem calls do not throw.

  // Because we are forced to try/catch this, we'll go aggressive.

  // Just FWIW: IE8 Compat mode supports these features completely:
  //   www.quirksmode.org/dom/html5.html
  // But IE8 doesn't support either with local files

  Modernizr.addTest('localstorage', function() {
    var mod = 'modernizr';
    try {
      localStorage.setItem(mod, mod);
      localStorage.removeItem(mod);
      return true;
    } catch (e) {
      return false;
    }
  });
});

Fix tiny typo - will help for google searches!


// ==UserScript==
// @include http://youtube.com/html5
// @include http://www.youtube.com/html5
// @include https://youtube.com/html5
// @include https://www.youtube.com/html5
// ==/UserScript==

(function() {
  window.addEventListener('DOMContentLoaded', function()
  {
    submitFormWithSessionToken();
  }, false);

  var form = document.getElementById('html5form');

  function submitFormWithSessionToken()
  {
    if (widget.preferences.continueTesting == 'true')
    {
      if (form)
      {
        if (form.querySelector("input[name='enable_html5']"))
        {
          if (!form.querySelector("input[name='session_token']"))
          {
           form.addEventListener('DOMNodeInserted', formSubmitter, false);
          }
          else formSubmitter();
        }
        else if (form.querySelector("input[name='disable_html5']"))
        {
          continueTesting();
          opera.extension.postMessage('testCandidate');
      }}
      else opera.postError('HTML5 Trial Applicant form not found.');
  }}

  function formSubmitter()
  {
    form.submit();
  }

  function continueTesting()
  { // keep the test subjects in the HTML5 Trial
    var enrichmentCentre = document.getElementById('html5-join-link'),
    escapeElevator = enrichmentCentre.getElementsByTagName('h3')[0];
    enrichmentCentre.removeChild(escapeElevator);
  } // ... there is no escape ...
}());

// ==UserScript==
// @include http://youtube.com/html5
// @include http://www.youtube.com/html5
// @include https://youtube.com/html5
// @include https://www.youtube.com/html5
// ==/UserScript==

(function() {
  window.addEventListener('DOMContentLoaded', function()
  {
    submitFormWithSessionToken();
  }, false);

  var form = document.getElementById('html5form');

  function submitFormWithSessionToken()
  {
    if (widget.preferences.continueTesting == 'true')
    {
      if (form)
      {
        if (form.querySelector("input[name='enable_html5']"))
        {
          if (!form.querySelector("input[name='session_token']"))
          {
           form.addEventListener('DOMNodeInserted', formSubmitter, false);
          }
          else formSubmitter();
        }
        else if (form.querySelector("input[name='disable_html5']"))
        {
          continueTesting();
          opera.extension.postMessage('testCandidate');
      }}
      else opera.extension.postMessage('discontinuedTesting');
  }}

  function formSubmitter()
  {
    form.submit();
  }

  function continueTesting()
  { // keep the test subjects in the HTML5 Trial
    var enrichmentCentre = document.getElementById('html5-join-link'),
    escapeElevator = enrichmentCentre.getElementsByTagName('h3')[0];
    enrichmentCentre.removeChild(escapeElevator);
  } // ... there is no escape ...
}());

Send a message instead of posting error. Let us free up more document memory.


// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

'use strict';

var transportSpecificTests = require('./_client_test.js');

describe('Over real AMQP', function () {
  this.timeout(60000);
  var opts = {
    transport: null,
    connectionString: process.env.IOTHUB_CONNECTION_STRING,
    id: process.env.IOTHUB_DEVICE_ID
  };
  transportSpecificTests(opts);
});

// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

'use strict';

var transportSpecificTests = require('./_client_test.js');

describe('Over real AMQP', function () {
  this.timeout(30000);
  var opts = {
    transport: null,
    connectionString: process.env.IOTHUB_CONNECTION_STRING,
    id: process.env.IOTHUB_DEVICE_ID
  };
  transportSpecificTests(opts);
});

Increase timeout for node integration tests to 30seconds


'use strict';

import "es5-shim";
import "es6-shim";
import * as ready from './ready';
import * as language from './language';
import * as expander from './expander';
import * as clipboard from './clipboard';
import * as highlight from './highlight';

if (window.innerHeight < 600) {
    document.getElementById('content').scrollIntoView(true)
}

Adjust viewport for Office webviewer and mobile devices


define(['backbone', 'core/views/MapView', 'hbs!core/templates/map-browse'], function(Backbone, MapView, mapBrowseTemplate) {

    var MapBrowseLayout = Backbone.Layout.extend({
        manage: true,
        template: mapBrowseTemplate,
        className: 'map-browse-layout',
        name: 'MapBrowseLayout',
        beforeRender: function() {
            this.setView(".content-map", new MapView());
        }
    });

    return MapBrowseLayout;
});

define(['backbone', 'core/views/MapView', 'hbs!core/templates/map-browse'], function(Backbone, MapView, mapBrowseTemplate) {

    var MapBrowseLayout = Backbone.Layout.extend({
        manage: true,
        template: mapBrowseTemplate,
        className: 'map-browse-layout',
        name: 'MapBrowseLayout',

        // Previously we set this view in 'views' this is WRONG
        //
        // That way the view is a class attribute rather than being
        // created when it is required, as it is here (beforeRender)
        //
        // See commit #6511cae
        beforeRender: function() {
            this.setView(".content-map", new MapView());
        }
    });

    return MapBrowseLayout;
});

Comment on creating MapView as it is required


!function()
{
	var images = document.querySelectorAll('#post-body p > img');
	for (var i = 0; i < images.length; i++) {
		// Only apply to wide images
		if ( images[i].clientWidth < 520 ) {
			return;
		}
		var wrap = images[i].parentNode;
		if ( wrap.nodeName === 'P' ) {
			wrap.classList += ' img-wrap';
		}
	}
}()

!function()
{
	var images = document.querySelectorAll('#post-body p > img');
	for (var i = 0; i < images.length; i++) {
		// Only apply to wide images
		if ( images[i].clientWidth < 518 ) {
			return;
		}
		var wrap = images[i].parentNode;
		if ( wrap.nodeName === 'P' ) {
			wrap.classList += ' img-wrap';
		}
	}
}()

Tweak image widening JS to compensate for 1px border


var AVM2 = (function () {

  function AVM2(builtinABC, mode) {
    if (!builtinABC) {
      throw new Error("Cannot initialize AVM2 without builtin.abc");
    }

    var sysDomain = new Domain(null, mode, true);
    sysDomain.executeAbc(new AbcFile(builtinABC), "builtin.abc");

    // TODO: this will change when we implement security domains.
    this.systemDomain = sysDomain;
    this.applicationDomain = new Domain(sysDomain, mode, false);
  }

  AVM2.prototype = {
    loadPlayerGlobal: function (playerGlobalSWF) {
      var sysDomain = this.systemDomain;
      // Load, but don't execute, the default player globals.
      Timer.start("Load Player Globals");
      SWF.parse(playerGlobalSWF, {
        oncomplete: function(result) {
          var tags = result.tags;
          for (var i = 0, n = tags.length; i < n; i++) {
            var tag = tags[i];
            if (tag.type === "abc") {
              sysDomain.loadAbc(new AbcFile(tag.data, "playerGlobal/library" + i + ".abc"));
            }
          }
        }
      });
      Timer.stop();
    }
  };

  return AVM2;

})();

var AVM2 = (function () {

  function AVM2(builtinABC, sysMode, appMode) {
    if (!builtinABC) {
      throw new Error("Cannot initialize AVM2 without builtin.abc");
    }

    var sysDomain = new Domain(null, sysMode, true);
    sysDomain.executeAbc(new AbcFile(builtinABC), "builtin.abc");

    // TODO: this will change when we implement security domains.
    this.systemDomain = sysDomain;
    this.applicationDomain = new Domain(sysDomain, appMode, false);
  }

  AVM2.prototype = {
    loadPlayerGlobal: function (playerGlobalSWF) {
      var sysDomain = this.systemDomain;
      // Load, but don't execute, the default player globals.
      Timer.start("Load Player Globals");
      SWF.parse(playerGlobalSWF, {
        oncomplete: function(result) {
          var tags = result.tags;
          for (var i = 0, n = tags.length; i < n; i++) {
            var tag = tags[i];
            if (tag.type === "abc") {
              sysDomain.loadAbc(new AbcFile(tag.data, "playerGlobal/library" + i + ".abc"));
            }
          }
        }
      });
      Timer.stop();
    }
  };

  return AVM2;

})();

Allow for different execution modes between the sys and app domains.


import Entity from '../Entity.js';
import printMessage from '../printMessage.js';
import action from '../action.js';
import time from '../time.js';
import state from '../state.js';
import { addItem } from '../inventory.js';

export class CellPhone extends Entity {
  constructor() {
    super();
  }

  name() {
    return "cell phone";
  }

  actions() {
    return [
      action("Put it in your pocket.", () => {
        addItem(self)
      })
    ];
  }
}

export default new CellPhone();

import Entity from '../Entity.js';
import printMessage from '../printMessage.js';
import action from '../action.js';
import time from '../time.js';
import state from '../state.js';
import { addItem } from '../inventory.js';

export class CellPhone extends Entity {
  constructor() {
    super();
  }

  name() {
    return "cell phone";
  }

  actions() {
    return [
      action("Put it in your pocket.", () => {
        addItem(self)
      }),
      action("Check time.", () => { printMessage(`It is now ${time()}.`) })
    ];
  }
}

export default new CellPhone();

Allow time checking on phone


import Component from 'flarum/component';
import avatar from 'flarum/helpers/avatar';
import username from 'flarum/helpers/username';
import humanTime from 'flarum/helpers/human-time';
import highlight from 'flarum/helpers/highlight';

export default class PostPreview extends Component {
  view() {
    var post = this.props.post;
    var user = post.user();

    var excerpt = post.contentPlain();
    var start = 0;

    if (highlight) {
      var regexp = new RegExp(this.props.highlight, 'gi');
      start = Math.max(0, excerpt.search(regexp) - 100);
    }

    excerpt = (start > 0 ? '...' : '')+excerpt.substring(start, start + 200)+(excerpt.length > start + 200 ? '...' : '');

    if (highlight) {
      excerpt = highlight(excerpt, regexp);
    }

    return m('a.post-preview', {
      href: app.route.post(post),
      config: m.route,
      onclick: this.props.onclick
    }, m('div.post-preview-content', [
      avatar(user), ' ',
      username(user), ' ',
      humanTime(post.time()), ' ',
      excerpt
    ]));
  }
}

import Component from 'flarum/component';
import avatar from 'flarum/helpers/avatar';
import username from 'flarum/helpers/username';
import humanTime from 'flarum/helpers/human-time';
import highlight from 'flarum/helpers/highlight';

export default class PostPreview extends Component {
  view() {
    var post = this.props.post;
    var user = post.user();

    var excerpt = post.contentPlain();
    var start = 0;

    if (this.props.highlight) {
      var regexp = new RegExp(this.props.highlight, 'gi');
      start = Math.max(0, excerpt.search(regexp) - 100);
    }

    excerpt = (start > 0 ? '...' : '')+excerpt.substring(start, start + 200)+(excerpt.length > start + 200 ? '...' : '');

    if (this.props.highlight) {
      excerpt = highlight(excerpt, regexp);
    }

    return m('a.post-preview', {
      href: app.route.post(post),
      config: m.route,
      onclick: this.props.onclick
    }, m('div.post-preview-content', [
      avatar(user), ' ',
      username(user), ' ',
      humanTime(post.time()), ' ',
      excerpt
    ]));
  }
}

Fix incorrect highlighting of post excerpts


/////////////////////////
// Helpers and globals //
/////////////////////////

const expect = chai.expect;

export function run(lang, description, code, result) {
    it(description, (done) => {
        Rainbow.color(code, lang, (html) => {
            expect(result).to.equal(html);
            done();
        });
    });
}

export function skip(lang, description, code, result) {
    it.skip(description);
}

/////////////////////////
// Helpers and globals //
/////////////////////////

const expect = chai.expect;

export function run(lang, description, code, result, only = false) {
    let toCall = it;
    if (only) {
        toCall = it.only;
    }

    toCall(description, (done) => {
        Rainbow.color(code, lang, (html) => {
            expect(result).to.equal(html);
            done();
        });
    });
}

export function skip(lang, description, code, result) {
    it.skip(description);
}

Add ability to run only a single test for debugging


import {createRunner} from 'atom-mocha-test-runner';
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';

import until from 'test-until';

chai.use(chaiAsPromised);
global.assert = chai.assert;

// Give tests that rely on filesystem event delivery lots of breathing room.
until.setDefaultTimeout(5000);

module.exports = createRunner({
  htmlTitle: `GitHub Package Tests - pid ${process.pid}`,
  reporter: 'spec',
  overrideTestPaths: [/spec$/, /test/],
}, mocha => {
  mocha.timeout(5000);
  if (process.env.APPVEYOR_API_URL) {
    mocha.reporter(require('mocha-appveyor-reporter'));
  }
});

import {createRunner} from 'atom-mocha-test-runner';
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';

import until from 'test-until';

chai.use(chaiAsPromised);
global.assert = chai.assert;

// Give tests that rely on filesystem event delivery lots of breathing room.
until.setDefaultTimeout(5000);

module.exports = createRunner({
  htmlTitle: `GitHub Package Tests - pid ${process.pid}`,
  reporter: process.env.MOCHA_REPORTER || 'spec',
  overrideTestPaths: [/spec$/, /test/],
}, mocha => {
  mocha.timeout(5000);
  if (process.env.APPVEYOR_API_URL) {
    mocha.reporter(require('mocha-appveyor-reporter'));
  }
});

Allow test reporter to be specified with MOCHA_REPORTER


// @flow strict

import * as React from 'react';
import { HeaderTitle } from '@kiwicom/mobile-navigation';
import { Translation } from '@kiwicom/mobile-localization';

import FillTravelDocumentContainer from '../scenes/travelDocument/FillTravelDocumentContainer';

export default function FillTravelDocumentScreen() {
  return <FillTravelDocumentContainer />;
}

// TODO: All text is not showing on mobile. Find out how to enlarge surrounding view
FillTravelDocumentScreen.navigationOptions = () => ({
  title: (
    <HeaderTitle>
      <Translation id="mmb.fill_travel_document_screen.title" />
    </HeaderTitle>
  ),
});

// @flow strict

import * as React from 'react';
import { HeaderTitle } from '@kiwicom/mobile-navigation';
import { Translation } from '@kiwicom/mobile-localization';

import FillTravelDocumentContainer from '../scenes/travelDocument/FillTravelDocumentContainer';

export default function FillTravelDocumentScreen() {
  return <FillTravelDocumentContainer />;
}

FillTravelDocumentScreen.navigationOptions = () => ({
  headerTitle: (
    <HeaderTitle>
      <Translation id="mmb.fill_travel_document_screen.title" />
    </HeaderTitle>
  ),
});

Make fill travel document header fit on mobile


function adminHandleFieldType(e) {
    if (ADMIN_FIELD_TIMEOUT)
        clearTimeout(ADMIN_FIELD_TIMEOUT);

    ADMIN_FIELD_TIMEOUT = setTimeout(function () {
        $(e.target).trigger({
            type: 'change',
            _typing: true
        });
    }, 200);
}

function adminHandlePaneStep(e, name) {
    name = $(e.target).closest('[data-pane]').attr('data-pane');

    if (e.target.name == 'step-ok')
        paneGoto(name, ADMIN_PANES[name].last);
    else if (e.target.name == 'step-prev' && ADMIN_PANES[name].active > 1)
        paneGoto(name, ADMIN_PANES[name].active - 1);
    else if (e.target.name == 'step-next' && ADMIN_PANES[name].active < ADMIN_PANES[name].count)
        paneGoto(name, ADMIN_PANES[name].active + 1);
}

function adminReloadServer() {
    ADMIN_RELOAD_TIMEOUT = setTimeout(function () {
        overlayCreate('loader', {
            message: $.t('main.mesg_server_loading')
        });
    }, 500);

    return $.ajax({
        url: urlPrefix + '/reload',
        type: 'GET'
    }).always(function ($deferred) {
        if (ADMIN_RELOAD_TIMEOUT)
            clearTimeout(ADMIN_RELOAD_TIMEOUT);

        overlayDestroy('loader');

        if ($deferred.state() == "resolved")
            window.location.reload(true);
    });
}

function adminHandleFieldType(e) {
    if (ADMIN_FIELD_TIMEOUT)
        clearTimeout(ADMIN_FIELD_TIMEOUT);

    ADMIN_FIELD_TIMEOUT = setTimeout(function () {
        $(e.target).trigger({
            type: 'change',
            _typing: true
        });
    }, 200);
}

function adminHandlePaneStep(e, name) {
    name = $(e.target).closest('[data-pane]').attr('data-pane');

    if (e.target.name == 'step-ok')
        paneGoto(name, ADMIN_PANES[name].last);
    else if (e.target.name == 'step-prev' && ADMIN_PANES[name].active > 1)
        paneGoto(name, ADMIN_PANES[name].active - 1);
    else if (e.target.name == 'step-next' && ADMIN_PANES[name].active < ADMIN_PANES[name].count)
        paneGoto(name, ADMIN_PANES[name].active + 1);
}

function adminReloadServer() {
    ADMIN_RELOAD_TIMEOUT = setTimeout(function () {
        overlayCreate('loader', {
            message: $.t('main.mesg_server_loading')
        });
    }, 500);

    return $.ajax({
        url: urlPrefix + '/reload',
        type: 'GET'
    }).always(function (xhr) {
        if (ADMIN_RELOAD_TIMEOUT)
            clearTimeout(ADMIN_RELOAD_TIMEOUT);

        overlayDestroy('loader');

        if (!xhr)
            window.location.reload(true);
    });
}

Fix undefined XHR state on reload success


/* eslint-env node */

module.exports = function (config) {
  config.set({
    basePath: 'public',
    browsers: ['ChromeHeadless'],
    files: ['scripts/modules.js', 'scripts/test.js'],
    reporters: ['dots'].concat(process.env.COVERAGE ? ['coverage'] : []),
    frameworks: ['mocha'],
    middleware: ['static'],
    preprocessors: {
      '**/*.js': ['sourcemap'],
      'scripts/modules.js': process.env.COVERAGE ? ['coverage'] : []
    },
    coverageReporter: {
      type: 'json',
      dir: '../coverage/',
      subdir: '.',
      file: 'coverage-unmapped.json'
    },
    static: {
      path: 'public'
    }
  });
};

/* eslint-env node */

module.exports = function (config) {
  config.set({
    basePath: 'public',
    browsers: ['ChromeHeadlessCustom'],
    files: ['scripts/modules.js', 'scripts/test.js'],
    reporters: ['dots'].concat(process.env.COVERAGE ? ['coverage'] : []),
    frameworks: ['mocha'],
    middleware: ['static'],
    preprocessors: {
      '**/*.js': ['sourcemap'],
      'scripts/modules.js': process.env.COVERAGE ? ['coverage'] : []
    },
    coverageReporter: {
      type: 'json',
      dir: '../coverage/',
      subdir: '.',
      file: 'coverage-unmapped.json'
    },
    static: {
      path: 'public'
    },
    customLaunchers: {
      ChromeHeadlessCustom: {
        base: 'ChromeHeadless',
        flags: ['--no-sandbox']
      }
    }
  });
};

Apply Chrome Headless fix for Travis builds


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

    basePath : './',

    files : [
      'app/bower_components/angular/angular.min.js',
      'app/bower_components/angular-ui-router/release/angular-ui-router.min.js',
      'app/bower_components/angular-resource/angular-resource.min.js',
      'app/bower_components/angular-mocks/angular-mocks.js',
      'app/bower_components/ng-table/dist/ng-table.min.js',
      'app/bower_components/spin.js/spin.js',
      'app/bower_components/angular-loading/angular-loading.min.js',
      'app/search/**/*.js',
      'app/services/**/*.js'
    ],

    preprocessors: {
      'app/search/*.js': ['coverage', 'coveralls'],
      'app/services/*.js': ['coverage', 'coveralls']
    },

    reporters: ['progress', 'coverage'],

    coverageReporter: {
      type: 'lcov',
      dir: 'coverage/'
    },

    autoWatch : true,

    frameworks: ['jasmine'],

    browsers : ['Chrome'],

    plugins : [
            'karma-chrome-launcher',
            'karma-firefox-launcher',
            'karma-jasmine',
            'karma-junit-reporter',
            'karma-coverage'
            ],

    junitReporter : {
      outputFile: 'test_out/unit.xml',
      suite: 'unit'
    }

  });
};

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

    basePath : './',

    files : [
      'app/bower_components/angular/angular.min.js',
      'app/bower_components/angular-ui-router/release/angular-ui-router.min.js',
      'app/bower_components/angular-resource/angular-resource.min.js',
      'app/bower_components/angular-mocks/angular-mocks.js',
      'app/bower_components/ng-table/dist/ng-table.min.js',
      'app/bower_components/spin.js/spin.js',
      'app/bower_components/angular-loading/angular-loading.min.js',
      'app/search/**/*.js',
      'app/services/**/*.js'
    ],

    preprocessors: {
      'app/search/*.js': ['coverage', 'coveralls'],
      'app/services/*.js': ['coverage', 'coveralls']
    },

    reporters: ['progress', 'coverage'],

    coverageReporter: {
      type: 'lcov',
      dir: 'coverage/'
    },

    autoWatch : true,

    frameworks: ['jasmine'],

    browsers : ['Chrome'],

    plugins : [
            'karma-chrome-launcher',
            'karma-firefox-launcher',
            'karma-jasmine',
            'karma-junit-reporter',
            'coveralls',
            'karma-coverage'
            ],

    junitReporter : {
      outputFile: 'test_out/unit.xml',
      suite: 'unit'
    }

  });
};

Add coveralls to karma plugins list.


// DecentCMS (c) 2015 Bertrand Le Roy, under MIT. See LICENSE.txt for licensing details.
'use strict';

/**
 * Builds indexes from content items and map functions.
 */
function Index(scope) {
  this.scope = scope;
}
Index.service = 'index';
Index.feature = 'query';
Index.scope = 'shell';

/**
 * Creates or gets an index.
 * @param {RegExp|string} [context.idFilter] A regular expression that validates
 *   content item ids before they are read and indexed.
 * @param {Function} context.map The map function takes a content item and returns
 *   an array of index entries, a single index entry, or null.
 *   An index entry should have an id property.
 *   It is recommended to name the map function.
 * @param {Function} [context.orderBy] The function that defines the order
 *   on which the index entries should be sorted.
 *   It takes two index entries A and B, and returns -1 if A comes before B,
 *   and +1 if A comes after B.
 *   It is recommended to name the order function.
 * @returns {object} The index object.
 */
Index.prototype.getIndex = function getIndex(context) {
  var indexStore = this.scope.require('index-store');
  return indexStore.getIndex(context.idFilter, context.map, context.orderBy);
};

module.exports = Index;

// DecentCMS (c) 2015 Bertrand Le Roy, under MIT. See LICENSE.txt for licensing details.
'use strict';

/**
 * Builds indexes from content items and map functions.
 */
function Index(scope) {
  this.scope = scope;
}
Index.service = 'index';
Index.feature = 'query';
Index.scope = 'shell';

/**
 * Creates or gets an index.
 * @param {RegExp|string} [context.idFilter] A regular expression that validates
 *   content item ids before they are read and indexed.
 * @param {Function} context.map The map function takes a content item and returns
 *   an array of index entries, a single index entry, or null.
 *   An index entry should have an id property.
 *   It is recommended to name the map function.
 * @param {Function} [context.orderBy] The function that defines the order
 *   on which the index entries should be sorted.
 *   It takes an index entry, and returns the sort order.
 *   The sort order can be a simple string, date, number, or it can be an array,
 *   in which case the items in the array will be used one after the other.
 *   It is recommended to name the order function.
 * @returns {object} The index object.
 */
Index.prototype.getIndex = function getIndex(context) {
  var indexStore = this.scope.require('index-store');
  return indexStore.getIndex(context.idFilter, context.map, context.orderBy);
};

module.exports = Index;

Fix order by doc comment.


'use strict';

var Backbone = require('backbone');
var $ = require('jquery');
Backbone.$ = $;

var Router = require('./routes/router');
var router = new Router();

Backbone.history.start(); // Allows for fwd and back
Backbone.history.navigate('', {
	trigger: true
}); // This starts the history?

Remove that question mark :)


pref("extensions.torbirdy.protected", false);
pref("extensions.torbirdy.proxy", 0);
pref("extensions.torbirdy.first_run", true);
pref("extensions.torbirdy.warn", true);
pref("extensions.torbirdy.startup_folder", false);
pref("extensions.torbirdy.enigmail.throwkeyid", false);
pref("extensions.torbirdy.enigmail.confirmemail", false);
pref("extensions.torbirdy.gpg_already_torified", false);
pref("extensions.torbirdy.timezone", true);
pref("extensions.torbirdy.whonix_run", true);
pref("extensions.torbirdy.info_run", false);
pref("extensions.torbirdy.emailwizard", false);
pref("extensions.torbirdy.fetchall", false);
pref("extensions.torbirdy.defaultprotocol", 0); // 0 - POP3, 1 - IMAP

pref("extensions.torbirdy.protected", false);
pref("extensions.torbirdy.proxy", 0);
pref("extensions.torbirdy.first_run", true);
pref("extensions.torbirdy.warn", true);
pref("extensions.torbirdy.startup_folder", false);
pref("extensions.torbirdy.enigmail.throwkeyid", false);
pref("extensions.torbirdy.enigmail.confirmemail", false);
pref("extensions.torbirdy.enigmail.protected", true);
pref("extensions.torbirdy.gpg_already_torified", false);
pref("extensions.torbirdy.timezone", true);
pref("extensions.torbirdy.whonix_run", true);
pref("extensions.torbirdy.info_run", false);
pref("extensions.torbirdy.emailwizard", false);
pref("extensions.torbirdy.fetchall", false);
pref("extensions.torbirdy.defaultprotocol", 0); // 0 - POP3, 1 - IMAP

Update default preference for encrypted mail headers


import { generalStrings } from '../localization';
import { UIDatabase } from '../database';

const routeList = {
  customerRequisitions: 'ResponseRequisition',
  supplierRequisitions: 'RequestRequisition',
  supplierInvoices: 'SupplierInvoice',
  stocktakes: 'Stocktake',
  customerInvoices: 'CustomerInvoice',
  vaccines: 'TemperatureBreach',
};

const getBadgeData = routeName => {
  const dataType = routeName in routeList ? routeList[routeName] : '';

  switch (dataType) {
    case 'TemperatureBreach':
      return [
        {
          count: UIDatabase.objects(dataType).filtered('acknowledged == false').length,
          title: `${generalStrings.unacknowledged} ${generalStrings[routeName]}`,
        },
      ];
    default:
      return [
        {
          count: dataType
            ? UIDatabase.objects(dataType).filtered('status != "finalised"').length
            : 0,
          title: `${generalStrings.unfinalised} ${generalStrings[routeName]}`,
        },
      ];
  }
};

export default getBadgeData;
export { getBadgeData };

import { generalStrings } from '../localization';
import { UIDatabase } from '../database';

const routeList = {
  customerRequisitions: 'ResponseRequisition',
  supplierRequisitions: 'RequestRequisition',
  supplierInvoices: 'SupplierInvoice',
  stocktakes: 'Stocktake',
  customerInvoices: 'CustomerInvoice',
  vaccines: 'Sensor',
};

const getBadgeData = routeName => {
  const dataType = routeName in routeList ? routeList[routeName] : '';

  switch (dataType) {
    case 'Sensor': {
      return [
        {
          count: UIDatabase.objects(dataType).filter(
            sensor => sensor.isInHotBreach || sensor.isInColdBreach
          ).length,
          title: `${generalStrings.unacknowledged} ${generalStrings[routeName]}`,
        },
      ];
    }
    default:
      return [
        {
          count: dataType
            ? UIDatabase.objects(dataType).filtered('status != "finalised"').length
            : 0,
          title: `${generalStrings.unfinalised} ${generalStrings[routeName]}`,
        },
      ];
  }
};

export default getBadgeData;
export { getBadgeData };

Update badge to show number of sensors currently in breach


import React from 'react';
import NavbarContainer from './Navigation/NavbarContainer';

const App = ({ children }) =>
  <div>
    <NavbarContainer />
    { children }
  </div>;

App.propTypes = {
  children: React.PropTypes.element.isRequired,
};

export default App;

import React from 'react';
import NavbarContainer from './Navigation/NavbarContainer';

const App = ({ children }) =>
  <div>
    <NavbarContainer />
    { children }
  </div>;

App.propTypes = {
  children: React.PropTypes.element,
};

export default App;

Remove required children to silence warning


/*!
 * Chrome Extension that forces all Google products to use English.
 *
 * @license MIT
 * @author Tomas Varaneckas <tomas.varaneckas@gmail.com>
 */
chrome.webRequest.onBeforeRequest.addListener(
  function (details) {
    var url = new Url(details.url);
    if (url.query.hl != 'en') {
      url.query.hl = 'en'
return { redirectUrl: url.toString() };
    }
  },
  { urls: ["*://*.google.com/*"] },
  ["blocking"]
);

/*!
 * Chrome Extension that forces all Google products to use English.
 *
 * @license MIT
 * @author Tomas Varaneckas <tomas.varaneckas@gmail.com>
 */
chrome.webRequest.onBeforeRequest.addListener(
  function (details) {
    var url = new Url(details.url);
    if (url.query.hl != 'en') {
      url.query.hl = 'en';
      return { redirectUrl: url.toString() };
    }
  },
  { urls: ["*://*.google.com/*"] },
  ["blocking"]
);

Update formatting and add missing semicolon


Meteor.startup(function () {
    var stack;

    if (process.env.RESTRICT_ENV) {
        // Ensure credentials are provided
        if (!Meteor.settings.basicAuth) {
            throw new Error("[talos:basic-auth] No credentials were found in your meteor settings");
        }

        // Add handler
        WebApp.connectHandlers.use(function (req, res, next) {
            var auth = Npm.require("basic-auth"),
                valid = Meteor.settings.basicAuth,
                user = auth(req);

            if (!user || user.name !== valid.name || user.pass !== valid.pass) {
                res.statusCode = 401;
                res.end('Access denied');
            } else {
                next();
            }
        });

        // Move handler to the top of the stack
        stack = WebApp.connectHandlers.stack;
        stack.unshift(stack.pop());
    }
});

Meteor.startup(function () {
    var stack;

    if (process.env.RESTRICT_ENV) {
        // Ensure credentials are provided
        if (!Meteor.settings.basicAuth) {
            throw new Error("[talos:basic-auth] No credentials were found in your meteor settings");
        }

        // Add handler
        WebApp.connectHandlers.use(function (req, res, next) {
            var auth = Npm.require("basic-auth"),
                valid = Meteor.settings.basicAuth,
                user = auth(req);

            if (!user || user.name !== valid.name || user.pass !== valid.pass) {
                res.statusCode = 401;
                res.setHeader('WWW-Authenticate', 'Basic realm="Promised Land"');
                res.end('Access denied');
            } else {
                next();
            }
        });

        // Move handler to the top of the stack
        stack = WebApp.connectHandlers.stack;
        stack.unshift(stack.pop());
    }
});

Add auth header to challenge browsers


var isOpen = false;

function checkStatus(text) {
  let num = parseInt(text, 10);
  if (num > 0) {
    chrome.browserAction.setTitle({ title: 'λspace is open' });
    chrome.browserAction.setBadgeText({ text: num.toString() });
    chrome.browserAction.setBadgeBackgroundColor({ color: '#808080' });
    chrome.browserAction.setIcon({
      path: {
        48: 'icons/o_48x48.png',
        96: 'icons/o_96x96.png'
      }
    });
  } else {
    chrome.browserAction.setTitle({ title: 'λspace is closed' });
    chrome.browserAction.setBadgeText({ text: '' });
    chrome.browserAction.setIcon({
      path: {
        48: 'icons/c_48x48.png',
        96: 'icons/c_96x96.png'
      }
    });
  }
}

function timer() {
  fetch('https://www.lambdaspace.gr/hackers.txt')
    .then(response => response.text()) // Get the text of the response
    .then(data => checkStatus(data))
    .catch(e => console.error(e)); // Handle exeptions
}

timer();
setInterval(timer, 300000);

var isOpen = false;

function checkStatus(text) {
  let num = parseInt(text, 10);
  if (num > 0) {
    chrome.browserAction.setTitle({ title: 'λspace is open' });
    chrome.browserAction.setBadgeText({ text: num.toString() });
    chrome.browserAction.setBadgeBackgroundColor({ color: '#808080' });
    chrome.browserAction.setIcon({
      path: {
        48: 'icons/o_48x48.png',
        96: 'icons/o_96x96.png'
      }
    });
  } else {
    chrome.browserAction.setTitle({ title: 'λspace is closed' });
    chrome.browserAction.setBadgeText({ text: '' });
    chrome.browserAction.setIcon({
      path: {
        48: 'icons/c_48x48.png',
        96: 'icons/c_96x96.png'
      }
    });
  }
}

function timer() {
  fetch('https://api.lambdaspace.gr/api/v2.0/status')
    .then(response => response.json())
    .then(data => checkStatus(data.people_now_present))
    .catch(e => console.error(e));
}

timer();
setInterval(timer, 300000);

Change the live users endpoint


'use strict';

// Extend the process global with some more functionality
// https://nodejs.org/api/process.html
Object.assign(process.env, {
  TERM: 'xterm-256color',
  SHELL: '/bin/sh',
  USER: 'user',
  LOGNAME: 'user',
  PATH: '~/.bin/:/usr/bin/:/bin:/usr/sbin:/sbin:/usr/local/bin',
  PWD: '/',
  HOME: '/',
});
process.exit = (code) => {
  console.log(`process.exit(${code})`);
};
Object.assign(process.versions, {
  node: '4.2.4', // simulated node.js compatibility level version (optimistic)
  app: navigator.appVersion,
  webkit: navigator.appVersion.match(/WebKit\/([\d.]+)/)[1],
  chrome: navigator.appVersion.match(/Chrome\/([\d.]+)/)[1],
  // TODO: how can we get v8 version? if at all, from Chrome version? node process.versions has it
});
process.version = process.versions.node;
process.cwd = () => {
  return process.env.PWD;
};
process.execPath = '/bin/node';
const BrowserStdout = require('browser-stdout');
process.stdout = BrowserStdout();
process.stderr = BrowserStdout({label: 'stderr'}); // TODO: console.error instead of console.log?
// TODO: stdin

'use strict';

// Extend the process global with some more functionality
// https://nodejs.org/api/process.html
Object.assign(process.env, {
  TERM: 'xterm-256color',
  SHELL: '/bin/sh',
  USER: 'user',
  LOGNAME: 'user',
  PATH: '~/.bin/:/usr/bin/:/bin:/usr/sbin:/sbin:/usr/local/bin',
  PWD: '/',
  HOME: '/home',
});
process.exit = (code) => {
  console.log(`process.exit(${code})`);
};
Object.assign(process.versions, {
  node: '4.2.4', // simulated node.js compatibility level version (optimistic)
  app: navigator.appVersion,
  webkit: navigator.appVersion.match(/WebKit\/([\d.]+)/)[1],
  chrome: navigator.appVersion.match(/Chrome\/([\d.]+)/)[1],
  // TODO: how can we get v8 version? if at all, from Chrome version? node process.versions has it
});
process.version = process.versions.node;
process.cwd = () => {
  return process.env.PWD;
};
process.execPath = '/bin/node';
const BrowserStdout = require('browser-stdout');
process.stdout = BrowserStdout();
process.stderr = BrowserStdout({label: 'stderr'}); // TODO: console.error instead of console.log?
// TODO: stdin

Set $HOME to /home to avoid colliding .npmrc


var test = require('tape')
var fs = require('fs')

var columnify =  require('../')

var data = require('./large.json')

test('handling large data', function(t) {
  t.plan(1)
  console.time('large data')
  t.ok(columnify(data, {
    config: {
      description: {
        maxWidth: 30,
        minWidth: 10
      }
    }
  }))
  console.timeEnd('large data')
})

var test = require('tape')
var fs = require('fs')

var columnify =  require('../')

var data = require('./large.json')

test('handling large data', function(t) {
  t.plan(3)
  // have to reduce dataset, otherwise bench
  // blows memory limit
  data = data.slice(0, data.length / 20)
  console.time('large data 1')
  t.ok(columnify(data, {
    config: {
      description: {
        maxWidth: 30,
        minWidth: 10
      }
    }
  }))
  console.timeEnd('large data 1')
  console.time('large data 2')
  t.ok(columnify(data, {
    config: {
      description: {
        maxWidth: 30,
        minWidth: 10
      }
    }
  }))
  console.timeEnd('large data 2')
  console.time('large data 3')
  t.ok(columnify(data, {
    config: {
      description: {
        maxWidth: 30,
        minWidth: 10
      }
    }
  }))
  console.timeEnd('large data 3')
})

Make benchmark actually useful by reducing problem set.


import { combineReducers } from 'redux'
import { default as cart, getQuantity, getAddedIds } from './cart'
import { default as products, getProduct } from './products'

export function getTotal(state) {
  return getAddedIds(state.cart).reduce((total, id) =>
    total + getProduct(state.products, id).price * getQuantity(state.cart, id),
    0
  ).toFixed(2)
}

export function getCartProducts(state) {
  return getAddedIds(state.cart).map(id => Object.assign(
    {},
    getProduct(state.products, id),
    {
      quantity: getQuantity(state.cart, id)
    }
  ))
}

export default combineReducers({
  cart,
  products
})

import { combineReducers } from 'redux'
import cart, * as fromCart from './cart'
import products, * as fromProducts from './products'

export function getTotal(state) {
  return fromCart.getAddedIds(state.cart).reduce((total, id) =>
    total + (
      fromProducts.getProduct(state.products, id).price *
      fromCart.getQuantity(state.cart, id)
    ),
    0
  ).toFixed(2)
}

export function getCartProducts(state) {
  return fromCart.getAddedIds(state.cart).map(id => Object.assign(
    {},
    fromProducts.getProduct(state.products, id),
    {
      quantity: fromCart.getQuantity(state.cart, id)
    }
  ))
}

export default combineReducers({
  cart,
  products
})

Use a different convention for colocating reducers with selectors


"use strict";
var Main = module.parent.exports;

Main.ActionSet = function(Child, Request){
  Main.ValidateArguments(Main.ARGS_EVEN, Request.length);
  for(let Number = 0; Number < Request.length; Number += 2){
    let Value = Main.NormalizeType(Request[Number + 1]);
    this.Database.set(Request[Number], Value);
  }
  return {Type: 'OK'};
};

Main.ActionGET = function(Child, Request){
  if(Request.length === 1){
    return this.Database.get(Request[0]) || null;
  }
  return Request.map(function(Name){
    return this.Database.get(Name) || null;
  }.bind(this));
};

Main.ActionINCR = function(Child, Request){
  if(Request.length === 1){
    let Value = this.Database.get(Request[0]) || 0;
    Main.Validate(Main.VAL_NUMERIC, 'INCR', Value);
    this.Database.set(Request[0], ++Value);
    return Value;
  }
  return Request.map(function(Name){
    let Value = this.Database.get(Name) || 0;
    Main.Validate(Main.VAL_NUMERIC, 'INCR', Value);
    this.Database.set(Request[0], ++Value);
    return Value;
  }.bind(this));
};

"use strict";
var Main = module.parent.exports;

Main.ActionSET = function(Child, Request){
  Main.ValidateArguments(Main.ARGS_EVEN, Request.length);
  for(let Number = 0; Number < Request.length; Number += 2){
    let Value = Main.NormalizeType(Request[Number + 1]);
    this.Database.set(Request[Number], Value);
  }
  return {Type: 'OK'};
};

Main.ActionGET = function(Child, Request){
  if(Request.length === 1){
    return this.Database.get(Request[0]) || null;
  }
  return Request.map(function(Name){
    return this.Database.get(Name) || null;
  }.bind(this));
};

Main.ActionINCR = function(Child, Request){
  if(Request.length === 1){
    let Value = this.Database.get(Request[0]) || 0;
    Main.Validate(Main.VAL_NUMERIC, 'INCR', Value);
    this.Database.set(Request[0], ++Value);
    return Value;
  }
  return Request.map(function(Name){
    let Value = this.Database.get(Name) || 0;
    Main.Validate(Main.VAL_NUMERIC, 'INCR', Value);
    this.Database.set(Name, ++Value);
    return Value;
  }.bind(this));
};

Fix some bugs in ActionSET and ActionINCR


$('#loginModal').on('shown.bs.modal', function () {
  $('#loginInput').focus()
  $(this).find('input[type="submit"]').on('submit', function(event) {
    event.preventDefault();
    /**
     * 1. Write a test for this
     * then code:
     * 2. Make login AJAX request
     * 3. Added logic for better error message
     * 4. ...
     * */
  });
})

$('#loginModal').on('shown.bs.modal', function () {
  $('#loginInput').focus()
  var loginForm = document.querySelector('#loginModal form');
  console.log(loginForm);
  loginForm.addEventListener('submit', function(event) {
    event.preventDefault();
    // account.signIn(event, this);
    /**
     * 1. Write a test for this
     * then code:
     * 2. Make login AJAX request
     * 3. Added logic for better error message
     * 4. ...
     * */
  });
})

Add preventDefault to login form to prevent new page on submission


const path = require('path');

const electronIsDev = require('electron-is-dev');

let environment;

// In production mode, NODE_ENV cannot be customized by the user
if (electronIsDev) {
  environment = process.env.NODE_ENV || 'development';
} else {
  environment = 'production';
}

// Set environment vars to configure node-config before requiring it
process.env.NODE_ENV = environment;
process.env.NODE_CONFIG_DIR = path.join(__dirname, '..', 'config');

if (environment === 'production') {
  // harden production config against the local env
  process.env.NODE_CONFIG = '';
  process.env.NODE_CONFIG_STRICT_MODE = true;
  process.env.HOSTNAME = '';
  process.env.NODE_APP_INSTANCE = '';
  process.env.ALLOW_CONFIG_MUTATIONS = '';
  process.env.SUPPRESS_NO_CONFIG_WARNING = '';
}

// We load config after we've made our modifications to NODE_ENV
const config = require('config');

config.environment = environment;

// Log resulting env vars in use by config
[
  'NODE_ENV',
  'NODE_CONFIG_DIR',
  'NODE_CONFIG',
  'ALLOW_CONFIG_MUTATIONS',
  'HOSTNAME',
  'NODE_APP_INSTANCE',
  'SUPPRESS_NO_CONFIG_WARNING',
].forEach(s => {
  console.log(`${s} ${config.util.getEnv(s)}`);
});

module.exports = config;

const path = require('path');

const electronIsDev = require('electron-is-dev');

let environment;

// In production mode, NODE_ENV cannot be customized by the user
if (electronIsDev) {
  environment = process.env.NODE_ENV || 'development';
} else {
  environment = 'production';
}

// Set environment vars to configure node-config before requiring it
process.env.NODE_ENV = environment;
process.env.NODE_CONFIG_DIR = path.join(__dirname, '..', 'config');

if (environment === 'production') {
  // harden production config against the local env
  process.env.NODE_CONFIG = '';
  process.env.NODE_CONFIG_STRICT_MODE = true;
  process.env.HOSTNAME = '';
  process.env.NODE_APP_INSTANCE = '';
  process.env.ALLOW_CONFIG_MUTATIONS = '';
  process.env.SUPPRESS_NO_CONFIG_WARNING = '';
  process.env.NODE_TLS_REJECT_UNAUTHORIZED = '';
}

// We load config after we've made our modifications to NODE_ENV
const config = require('config');

config.environment = environment;

// Log resulting env vars in use by config
[
  'NODE_ENV',
  'NODE_CONFIG_DIR',
  'NODE_CONFIG',
  'ALLOW_CONFIG_MUTATIONS',
  'HOSTNAME',
  'NODE_APP_INSTANCE',
  'SUPPRESS_NO_CONFIG_WARNING',
].forEach(s => {
  console.log(`${s} ${config.util.getEnv(s)}`);
});

module.exports = config;

Add a new env variable to the hardening list


angular.module('voteApp').controller('createUserController',
['$scope', 'userService', 'alertService', 'cardKeyService', function($scope, userService, alertService, cardKeyService) {

    $scope.user = {};

    $scope.createUser = function(user) {
        userService.createUser(user)
            .success(function(data) {
                alertService.addSuccess('Bruker registrert!');
            })
            .error(function(data) {
                alertService.addError();
            });
    };

    cardKeyService.listen(function(cardKey) {
        $scope.user.cardKey = cardKey;
        $scope.$apply();
    });

}]);

angular.module('voteApp').controller('createUserController',
['$scope', 'userService', 'alertService', 'cardKeyService', function($scope, userService, alertService, cardKeyService) {

    $scope.user = {};

    $scope.createUser = function(user) {
        userService.createUser(user)
            .success(function(data) {
                alertService.addSuccess('Bruker registrert!');
                $scope.user = {};
            })
            .error(function(data) {
                alertService.addError();
            });
    };

    cardKeyService.listen(function(cardKey) {
        $scope.user.cardKey = cardKey;
        $scope.$apply();
    });

}]);

Set user to empty object after submiting user


var request = require('superagent');
var Q = require('q');

// Marvel public API key
require('./api-key');

var baseUrl = 'http://gateway.marvel.com'
var charactersUrl = baseUrl+'/v1/public/characters?'+key

exports.getCharacters = function(offset) {
  var deferred = Q.defer();

  request
    .get(charactersUrl)
    .query({ offset: offset })
    .end(function(res) {
      deferred.resolve(res.body.data);
    });

  return deferred.promise;
};

var request = require('superagent');
var Q = require('q');

// Marvel public API key
require('./api-key');

var charactersUrl = 'http://gateway.marvel.com/v1/public/characters'

exports.getCharacters = function(offset) {
  var deferred = Q.defer();

  request
    .get(charactersUrl)
    .query({ apikey: key })
    .query({ offset: offset })
    .end(function(res) {
      deferred.resolve(res.body.data);
    });

  return deferred.promise;
};

Simplify characters URL in the API module


'use strict';

module.exports = function yo(controller) {
  controller.hears('yo', ['direct_mention'], function(bot, message) {
    bot.reply(message, 'Yo!');
  });
}

'use strict';

module.exports = function yo(controller) {
  controller.hears('yo', ['direct_mention'], function(bot, message) {
    console.log(message);
    bot.reply(message, 'Yo!');
  });
}

Test on the Yo COmmand


import Ember from 'ember';

export default Ember.Mixin.create({

    /**
     * Enables the tooltip functionality, based on component's `title` attribute
     * @method enableTooltip
     */
    enableTooltip: function () {
        var popoverContent = this.get( 'popover' );
        var title = this.get( 'title' );

        if ( !popoverContent && !title ) {
            return;
        }

        if ( this.get( 'isPopover' )) {
            this.$().popover( 'destroy' );
            this.set( 'isPopover', false );
        }

        if ( this.get( 'isTooltip' )) {
            this.$().tooltip( 'destroy' );
            this.set( 'isTooltip', false );
        }

        if ( popoverContent ) {
            this.set( 'data-toggle', 'popover' );
            this.$().popover({
                content: popoverContent,
                placement: 'top'
            });
            this.set( 'isPopover', false );
        } else if ( title ) {
            this.set( 'data-toggle', 'tooltip' );
            this.$().tooltip({ title: title });
            this.set( 'isTooltip', true );
        }
    }.observes( 'popover', 'title' ).on( 'didInsertElement' ),

    /**
     * Whether the component has been set up as a popover
     * @property {boolean} isPopover
     * @default false
     */
    isPopover: false,

    /**
     * Whether the component has been set up as a tooltip
     * @property {boolean} isTooltip
     * @default false
     */
    isTooltip: false
});

import Ember from 'ember';

export default Ember.Mixin.create({

    /**
     * Enables the tooltip functionality, based on component's `title` attribute
     * @method enableTooltip
     */
    enableTooltip: function () {
        var popoverContent = this.get( 'popover' );
        var title = this.get( 'title' );

        if ( !popoverContent && !title ) {
            return;
        }

        if ( this.get( 'isPopover' )) {
            this.$().popover( 'destroy' );
            this.set( 'isPopover', false );
        }

        if ( this.get( 'isTooltip' )) {
            this.$().tooltip( 'destroy' );
            this.set( 'isTooltip', false );
        }

        if ( popoverContent ) {
            this.set( 'data-toggle', 'popover' );
            this.$().popover({
                content: popoverContent,
                placement: 'top'
            });
            this.set( 'isPopover', false );
        } else if ( title ) {
            this.set( 'data-toggle', 'tooltip' );
            this.$().tooltip({
                container: 'body',
                title: title
            });
            this.set( 'isTooltip', true );
        }
    }.observes( 'popover', 'title' ).on( 'didInsertElement' ),

    /**
     * Whether the component has been set up as a popover
     * @property {boolean} isPopover
     * @default false
     */
    isPopover: false,

    /**
     * Whether the component has been set up as a tooltip
     * @property {boolean} isTooltip
     * @default false
     */
    isTooltip: false
});

Make tooltip's container the page's body


#!/usr/bin/env node

var Chance = require('../chance.js');
var argv = require('minimist')(process.argv.slice(2),{string:"pool"});

// Check which generator the user wants to invoke.
var generator = process.argv[2];
var options = argv;
// Use .toString() until #121 is merged.
var chance = new Chance(new Date().getTime().toString());
// Does the given generator exist in Chance?
if(generator && chance[generator]) {
    // Invoke the generator on our Chance instance and print the result.
    process.stdout.write(chance[generator](options) + '\n');
} else {
    process.stderr.write('Unknown generator "' + generator + '"\n');
}

#!/usr/bin/env node

var Chance = require('../chance.js');
var argv = require('minimist')(process.argv.slice(2),{string:"pool"});

// Check which generator the user wants to invoke.
var generator = process.argv[2];
var options = argv;
// Use .toString() until #121 is merged.
var chance = new Chance(new Date().getTime().toString());
// Does the given generator exist in Chance?
if(generator && chance[generator]) {
    // Invoke the generator on our Chance instance and print the result.
    process.stdout.write(chance[generator](options));
} else {
    process.stderr.write('Unknown generator "' + generator + '"');
}

Remove needless newline from cli so Chance output can be properly piped


var daysPerPage = 5;

var coreSubscriptions = new SubsManager({
  // cache recent 50 subscriptions
  cacheLimit: 50,
  // expire any subscription after 30 minutes
  expireIn: 30
});

// note: FastRender not defined here?

PostsDailyController = RouteController.extend({
  template: getTemplate('posts_daily'),
  onBeforeAction: function() {
    this.days = this.params.days ? this.params.days : daysPerPage;
    // this.days = Session.get('postsDays') ? Session.get('postsDays') : 3;

    var terms = {
      view: 'daily',
      days: this.days,
      after: moment().subtract(this.days, 'days').startOf('day').toDate()
    };

    this.postsSubscription = coreSubscriptions.subscribe('postsList', terms, function() {
      Session.set('postsLoaded', true);
    });

    this.postsUsersSubscription = coreSubscriptions.subscribe('postsListUsers', terms);

    return [this.postsSubscription, this.postsUsersSubscription];

  },
  data: function() {
    Session.set('postsDays', this.days);
    return {
      days: this.days
    };
  }
});

Meteor.startup(function () {

  Router.map(function() {

    this.route('postsDaily', {
      path: '/daily/:days?',
      controller: PostsDailyController
    });

  });

});

var daysPerPage = 5;

var coreSubscriptions = new SubsManager({
  // cache recent 50 subscriptions
  cacheLimit: 50,
  // expire any subscription after 30 minutes
  expireIn: 30
});

// note: FastRender not defined here?

Meteor.startup(function () {

  PostsDailyController = RouteController.extend({
    template: getTemplate('posts_daily'),
    onBeforeAction: function() {
      this.days = this.params.days ? this.params.days : daysPerPage;
      // this.days = Session.get('postsDays') ? Session.get('postsDays') : 3;

      var terms = {
        view: 'daily',
        days: this.days,
        after: moment().subtract(this.days, 'days').startOf('day').toDate()
      };

      this.postsSubscription = coreSubscriptions.subscribe('postsList', terms, function() {
        Session.set('postsLoaded', true);
      });

      this.postsUsersSubscription = coreSubscriptions.subscribe('postsListUsers', terms);

      return [this.postsSubscription, this.postsUsersSubscription];

    },
    data: function() {
      Session.set('postsDays', this.days);
      return {
        days: this.days
      };
    }
  });

  Router.map(function() {

    this.route('postsDaily', {
      path: '/daily/:days?',
      controller: PostsDailyController
    });

  });

});

Put controller block in Meteor.startup()


import init from './init';
import createFromHtml from '../util/create-from-html';

const DocumentFragmentPrototype = DocumentFragment.prototype;
const slice = Array.prototype.slice;

function decorateFragmentMethods (frag) {
  frag.appendChild = function (el) {
    return DocumentFragmentPrototype.appendChild.call(this, init(el));
  };

  frag.insertBefore = function (el, beforeEl) {
    return DocumentFragmentPrototype.insertBefore.call(this, init(el), beforeEl);
  };

  frag.replaceChild = function (el, replacedEl) {
    return DocumentFragmentPrototype.replaceChild.call(this, init(el), replacedEl);
  };

  frag.cloneNode = function () {
    var clone = DocumentFragmentPrototype.cloneNode.apply(this, arguments);
    decorateFragmentMethods(clone);
    var children = slice.call(clone.childNodes);
    for (var i = 0; i < children.length; i++) {
      init(children[i]);
    }
    return clone;
  };
}

export default function (html) {
  var frag = document.createDocumentFragment();
  decorateFragmentMethods(frag);
  if (html) {
    if (typeof html === 'string') {
      var par = createFromHtml(html);
      while (par.firstElementChild) {
        frag.appendChild(par.firstElementChild);
      }
    } else if (html.nodeType) {
      frag.appendChild(html);
    }
  }
  return frag;
}

import init from './init';
import createFromHtml from '../util/create-from-html';

const DocumentFragmentPrototype = DocumentFragment.prototype;
const slice = Array.prototype.slice;

function decorateFragmentMethods (frag) {
  frag.appendChild = function (el) {
    return DocumentFragmentPrototype.appendChild.call(this, init(el));
  };

  frag.insertBefore = function (el, beforeEl) {
    return DocumentFragmentPrototype.insertBefore.call(this, init(el), beforeEl);
  };

  frag.replaceChild = function (el, replacedEl) {
    return DocumentFragmentPrototype.replaceChild.call(this, init(el), replacedEl);
  };

  frag.cloneNode = function () {
    var clone = DocumentFragmentPrototype.cloneNode.apply(this, arguments);
    decorateFragmentMethods(clone);
    var children = slice.call(clone.childNodes);
    for (var i = 0; i < children.length; i++) {
      init(children[i]);
    }
    return clone;
  };
}

export default function (html) {
  var frag = document.createDocumentFragment();
  decorateFragmentMethods(frag);
  if (typeof html === 'string') {
    let parent = createFromHtml(html);
    while (parent.firstChild) {
      frag.appendChild(parent.firstChild);
    }
  } else if (html) {
    frag.appendChild(html);
  }
  return frag;
}

Work with nodes instead of elements. Simplify checks.


'use strict';
'format es6';

import './module/index';
import './directives/index';

import moduleName from './name';

export function bootstrap() {

  //Bootstrap the ng application
  angular.element(document).ready(function() {

    try {

      angular.bootstrap(document, [moduleName], {strictDi: true});

    } catch (e) {
      DeepFramework.Kernel.container.get('log').log(e);
    }
  });
}

Build without env vars in repo settings


const path=require("path");
const fs=require("fs");

var siteconfig = {}

function siteconfig_file() {
  if(process.env.LEXONOMY_SITECONFIG !== undefined) {
    var path = process.env.LEXONOMY_SITECONFIG;
    if(fs.existsSync(path)) {
      return path;
    }
    else {
      throw Error(`Cannot locate $LEXONOMY_SITECONFIG file: ${ path }`);
    }
  }
  return path.join(__dirname, "siteconfig.json");
}

function load() {
  var new_siteconfig = JSON.parse(fs.readFileSync(siteconfig_file(), "utf8"));
  for (var attrname in new_siteconfig) {
    siteconfig[attrname] = new_siteconfig[attrname];
  }
  return siteconfig;
}

siteconfig.load = load;
siteconfig.reload = load;
module.exports = siteconfig

const path=require("path");
const fs=require("fs");

var siteconfig = {}

function siteconfig_file() {
  if(process.env.LEXONOMY_SITECONFIG !== undefined) {
    var path = process.env.LEXONOMY_SITECONFIG;
    if(fs.existsSync(path)) {
      return path;
    } else {
      throw Error("Cannot locate $LEXONOMY_SITECONFIG file: ${ path }");
    }
  }
  return path.join(__dirname, "siteconfig.json");
}

function load() {
  var new_siteconfig = JSON.parse(fs.readFileSync(siteconfig_file(), "utf8"));
  for (var attrname in new_siteconfig) {
    siteconfig[attrname] = new_siteconfig[attrname];
  }
  return siteconfig;
}

siteconfig.load = load;
siteconfig.reload = load;
module.exports = siteconfig

Fix inverted comma (and opinionated newline)


const waitFor = require('./waitFor')

describe('Utils > waitFor', () => {
  it('waits for the condition', async () => {
    let conditionValid = false

    setTimeout(() => {
      conditionValid = true
    }, 6)

    await expect(waitFor(() => conditionValid, { delay: 5 }))
  })

  it('rejects the promise if the callback fail', async () => {
    await expect(
      waitFor(
        () => {
          throw new Error('callback failed!')
        },
        { delay: 1 }
      )
    ).rejects.toHaveProperty('message', 'callback failed!')
  })

  it('rejects the promise if the callback never succeeds', async () => {
    await expect(waitFor(() => false, { delay: 1, maxWait: 2 })).rejects.toHaveProperty(
      'message',
      'Timeout'
    )
  })

  it('rejects the promise with a custom timeout message', async () => {
    await expect(
      waitFor(() => false, { delay: 1, maxWait: 2, timeoutMessage: 'foo bar' })
    ).rejects.toHaveProperty('message', 'foo bar')
  })
})

const sleep = require('./sleep')
const waitFor = require('./waitFor')

describe('Utils > waitFor', () => {
  it('waits for the condition', async () => {
    let conditionValid = false

    setTimeout(() => {
      conditionValid = true
    }, 6)

    await expect(waitFor(() => conditionValid, { delay: 5 }))
  })

  it('rejects the promise if the callback fail', async () => {
    await expect(
      waitFor(
        () => {
          throw new Error('callback failed!')
        },
        { delay: 1 }
      )
    ).rejects.toHaveProperty('message', 'callback failed!')
  })

  it('rejects the promise if the callback never succeeds', async () => {
    const condition = jest.fn().mockReturnValue(false)
    await expect(waitFor(condition, { delay: 1, maxWait: 2 })).rejects.toHaveProperty(
      'message',
      'Timeout'
    )

    // Verify that the check is not rescheduled after a timeout
    await sleep(10)
    expect(condition).toHaveBeenCalledTimes(2)
  })

  it('rejects the promise with a custom timeout message', async () => {
    await expect(
      waitFor(() => false, { delay: 1, maxWait: 2, timeoutMessage: 'foo bar' })
    ).rejects.toHaveProperty('message', 'foo bar')
  })
})

Verify waitFor does not reschedule the check after timing out


module.exports = {
    all: {
        files: [
            { expand: true, src: ['config.json'], dest: 'dist' },
            { expand: true, src: ['javascript.json'], dest: 'dist' },
            { expand: true, cwd: 'src/config/locales/', src: ['**'], dest: 'dist/config/locales/' },
            { expand: true, cwd: 'src/images/', src: ['**'], dest: 'dist/images/', },
            { expand: true, cwd: 'src/downloads/', src: ['**'], dest: 'dist/downloads/' },
            { expand: true, cwd: 'src/flash/', src: ['**'], dest: 'dist/flash/' },
            { expand: true, cwd: 'src/templates/', src: ['**'], dest: 'dist/templates/' },
            { expand: true, cwd: 'src/css/external/jquery-ui-custom-theme/images/', src: ['**'], dest: 'dist/css/images' },
            { expand: true, cwd: 'src/css/external/jquery-ui-custom-theme/', src: ['*.css'], dest: 'dist/css/' },
            { expand: true, cwd: 'src/css/fonts/', src: ['**'], dest: 'dist/css/fonts' },
            { expand: true, cwd: 'src/javascript/', src: ['**'], dest: 'dist/dev/javascript/' }
        ]
    }
};

module.exports = {
    all: {
        files: [
            { expand: true, src: ['config.json'], dest: 'dist' },
            { expand: true, src: ['javascript.json'], dest: 'dist' },
            { expand: true, cwd: 'src/config/locales/', src: ['**'], dest: 'dist/config/locales/' },
            { expand: true, cwd: 'src/images/', src: ['**'], dest: 'dist/images/', },
            { expand: true, cwd: 'src/downloads/', src: ['**'], dest: 'dist/downloads/' },
            { expand: true, cwd: 'src/flash/', src: ['**'], dest: 'dist/flash/' },
            { expand: true, cwd: 'src/templates/', src: ['**'], dest: 'dist/templates/' },
            { expand: true, cwd: 'src/css/external/jquery-ui-custom-theme/images/', src: ['**'], dest: 'dist/css/images' },
            { expand: true, cwd: 'src/css/external/jquery-ui-custom-theme/', src: ['*.css'], dest: 'dist/css/' },
            { expand: true, cwd: 'src/css/fonts/', src: ['**'], dest: 'dist/css/fonts' },
            { expand: true, cwd: 'src/javascript/', src: ['**'], dest: 'dist/dev/src/javascript/' }
        ]
    }
};

Add src in path for dev javascript


'use strict';

var gulp = require('gulp');

gulp.task('default', [
    'lint',
    'jscs',
    'test',
    'doc-build'
]);

'use strict';

var gulp = require('gulp');

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

Remove call to removed JSCS task


import React from 'react';
import _ from 'lodash';
import PaymentIcon from '../PaymentIcon';

const PaymentIcons = ({ number, cardNiceType }) => {
  const styles = {
    paymentIcon: {
      width: 40,
      height: 'auto',
      display: 'block',
      marginRight: 10
    },
    paymentIconHidden: {
      opacity: '.1'
    }
  };
  const paymentIcons = [
    'Visa',
    'MasterCard',
    'American Express',
    'Discover',
    'Diners Club',
    'JCB'
  ];
  const renderPaymentIcons = () => _.map(paymentIcons, (icon) => {
    let iconStyles = styles.paymentIcon;

    if (number && cardNiceType && icon !== cardNiceType) {
      iconStyles = { ...styles.paymentIcon, ...styles.paymentIconHidden };
    }

    return (
      <PaymentIcon
        niceType={icon}
        style={iconStyles}
      />
    );
  });

  return (
    <div
      className="col-flex-1"
      style={{ display: 'flex', alignItems: 'center' }}
    >
      {renderPaymentIcons()}
      <img
        src={'/img/stripe-badge@3x.png'}
        alt="Powered by Stripe"
        style={{ display: 'block', height: 78 / 3, marginLeft: 'auto' }}
      />
    </div>
  );
};

export default PaymentIcons;

import React from 'react';
import _ from 'lodash';
import PaymentIcon from '../PaymentIcon';

const PaymentIcons = ({ number, cardNiceType }) => {
  const styles = {
    paymentIcon: {
      width: 40,
      height: 'auto',
      display: 'block',
      marginRight: 10
    },
    paymentIconHidden: {
      opacity: '.1'
    }
  };
  const paymentIcons = [
    'Visa',
    'MasterCard',
    'American Express',
    'Discover',
    'Diners Club',
    'JCB'
  ];
  const renderPaymentIcons = () => _.map(paymentIcons, (icon) => {
    let iconStyles = styles.paymentIcon;

    if (number && cardNiceType && icon !== cardNiceType) {
      iconStyles = { ...styles.paymentIcon, ...styles.paymentIconHidden };
    }

    return (
      <PaymentIcon
        niceType={icon}
        style={iconStyles}
      />
    );
  });

  return (
    <div
      className="col-flex-1"
      style={{ display: 'flex', alignItems: 'center' }}
    >
      {renderPaymentIcons()}
      <img
        src={require('../../assets/img/stripe-badge@3x.png')}
        alt="Powered by Stripe"
        style={{ display: 'block', height: 78 / 3, marginLeft: 'auto' }}
      />
    </div>
  );
};

export default PaymentIcons;

Fix stripe icon in billing section


'use strict';

module.exports = function(data) {
    return {
        getUserAccountsDetails(req, res) {
            let currentUserId = req.user.id;

            return data.getAccountsByUserId(currentUserId)
                .then((accountsData) => {
                    if (accountsData.accounts.length == 0) {
                       res.status(400).json({message: 'No accounts'});
                    }

                    res.status(200).json(accounts);
                })
                .catch((err) => {
                    res.status(400).json({ message: err });
                });
        },
        addNewAccount(req, res) {
            let currentUserId = req.user.id;
            let accountToAdd = req.body;
            let username = req.user.username;

            let response = {
                sucsess: true,
                username: username
            };

            return data.getAccountsByUserId(currentUserId)
                .then((resObject) => {
                    data.addNewAccountToCurrentUser(accountToAdd, currentUserId)
                        .then((userWithAccounts) => {
                           res.status(200).json(response);
                        });
                });
        }
    }
};

'use strict';

module.exports = function(data) {
    return {
        getUserAccountsDetails(req, res) {
            let currentUserId = req.user.id;

            return data.getAccountsByUserId(currentUserId)
                .then((accountsData) => {
                    if (accountsData.accounts.length == 0) {
                       res.status(400).json({message: 'No accounts'});
                    }

                    res.status(200).json(accounts);
                })
                .catch((err) => {
                    res.status(400).json({ message: err.message });
                });
        },
        addNewAccount(req, res) {
            let currentUserId = req.user.id;
            let accountToAdd = req.body;
            let username = req.user.username;

            let response = {
                sucsess: true,
                username: username
            };

            return data.getAccountsByUserId(currentUserId)
                .then((resObject) => {
                    data.addNewAccountToCurrentUser(accountToAdd, currentUserId)
                        .then((userWithAccounts) => {
                           res.status(200).json(response);
                        });
                });
        }
    }
};

Add update user income to server


'use strict';

const events = require('events');

const utils = require('./utils');
const Listener = require('./listener');

class Server extends events.EventEmitter {
  constructor(user_options) {
    super();
    this.options = {
      prefix: '',
      response_limit: 128*1024,
      websocket: true,
      faye_server_options: null,
      jsessionid: false,
      heartbeat_delay: 25000,
      disconnect_delay: 5000,
      log() {},
      sockjs_url: 'https://cdn.jsdelivr.net/sockjs/1.0.1/sockjs.min.js'
    };
    Object.assign(this.options, user_options);
  }

  listener(handler_options) {
    const options = Object.assign({}, this.options, handler_options);
    return new Listener(options, function() {
      this.emit.apply(this, arguments);
    }.bind(this));
  }

  installHandlers(http_server, handler_options) {
    let handler = this.listener(handler_options).getHandler();
    utils.overshadowListeners(http_server, 'request', handler);
    utils.overshadowListeners(http_server, 'upgrade', handler);
    return true;
  }

  middleware(handler_options) {
    let handler = this.listener(handler_options).getHandler();
    handler.upgrade = handler;
    return handler;
  }
}

module.exports = Server;

'use strict';

const events = require('events');

const utils = require('./utils');
const Listener = require('./listener');

class Server extends events.EventEmitter {
  constructor(user_options) {
    super();
    this.options = {
      prefix: '',
      response_limit: 128*1024,
      websocket: true,
      faye_server_options: null,
      jsessionid: false,
      heartbeat_delay: 25000,
      disconnect_delay: 5000,
      log() {},
      sockjs_url: 'https://cdn.jsdelivr.net/sockjs/1/sockjs.min.js'
    };
    Object.assign(this.options, user_options);
  }

  listener(handler_options) {
    const options = Object.assign({}, this.options, handler_options);
    return new Listener(options, function() {
      this.emit.apply(this, arguments);
    }.bind(this));
  }

  installHandlers(http_server, handler_options) {
    let handler = this.listener(handler_options).getHandler();
    utils.overshadowListeners(http_server, 'request', handler);
    utils.overshadowListeners(http_server, 'upgrade', handler);
    return true;
  }

  middleware(handler_options) {
    let handler = this.listener(handler_options).getHandler();
    handler.upgrade = handler;
    return handler;
  }
}

module.exports = Server;

Use latest v1 client by default


'use strict';

angular.module('ncsaas')
  .constant('MODE', {
    modeName: 'modeCostTracking',
    toBeFeatures: [
      'resources',
      'payment',
      'eventlog',
      'localSignup',
      'users',
      'people',
      'backups',
      'templates',
      'monitoring',
      'projectGroups'
    ],
    featuresVisible: false,
    homeTemplate: 'views/home/costtracking/home.html',
    initialDataTemplate: 'views/initial-data/initial-data.html'
  });

'use strict';

angular.module('ncsaas')
  .constant('MODE', {
    modeName: 'modeCostTracking',
    toBeFeatures: [
      'resources',
      'payment',
      'eventlog',
      'localSignup',
      'users',
      'people',
      'backups',
      'templates',
      'monitoring',
      'projectGroups',
      'premiumSupport'
    ],
    featuresVisible: false,
    homeTemplate: 'views/home/costtracking/home.html',
    initialDataTemplate: 'views/initial-data/initial-data.html'
  });

Disable premium support for cost mode (SAAS-751)


import React from 'react';
import Route from 'react-router-dom/Route';
import { connectFor } from '@folio/stripes-connect';
import { modules } from 'stripes-loader'; // eslint-disable-line
import AddContext from './AddContext';

if (!Array.isArray(modules.app) && modules.length < 0) {
  throw new Error('At least one module of type "app" must be enabled.');
}

function getModuleRoutes(stripes) {
  return modules.app.map((module) => {
    const name = module.module.replace(/^@folio\//, '');
    const perm = `module.${name}.enabled`;
    if (!stripes.hasPerm(perm)) return null;

    const connect = connectFor(module.module, stripes.epics, stripes.logger);
    const Current = connect(module.getModule());
    const moduleStripes = stripes.clone({ connect });

    return (
      <Route
        path={module.route}
        key={module.route}
        render={props =>
          <AddContext context={{ stripes: moduleStripes }}>
            <span id={name+'-module-display'} data-module={module.module} data-version={module.version} >
              <Current {...props} connect={connect} stripes={moduleStripes} />
            </span>
          </AddContext>
        }
      />
    );
  }).filter(x => x);
}

export default getModuleRoutes;

import React from 'react';
import Route from 'react-router-dom/Route';
import { connectFor } from '@folio/stripes-connect';
import { modules } from 'stripes-loader'; // eslint-disable-line
import AddContext from './AddContext';

if (!Array.isArray(modules.app) && modules.length < 0) {
  throw new Error('At least one module of type "app" must be enabled.');
}

function getModuleRoutes(stripes) {
  return modules.app.map((module) => {
    const name = module.module.replace(/^@folio\//, '');
    const perm = `module.${name}.enabled`;
    if (!stripes.hasPerm(perm)) return null;

    const connect = connectFor(module.module, stripes.epics, stripes.logger);
    const Current = connect(module.getModule());
    const moduleStripes = stripes.clone({ connect });

    return (
      <Route
        path={module.route}
        key={module.route}
        render={props =>
          <AddContext context={{ stripes: moduleStripes }}>
            <span id={`${name}-module-display`} data-module={module.module} data-version={module.version} >
              <Current {...props} connect={connect} stripes={moduleStripes} />
            </span>
          </AddContext>
        }
      />
    );
  }).filter(x => x);
}

export default getModuleRoutes;

Use string template in place of concatenation (STRIPES-100)


define([
	"Ember",
	"text!templates/components/infinitescroll.html.hbs"
], function( Ember, template ) {

	var get = Ember.get;
	var or = Ember.computed.or;

	return Ember.Component.extend({
		layout: Ember.HTMLBars.compile( template ),
		tagName: "button",
		classNameBindings: [ ":btn", ":btn-with-icon", ":infinitescroll", "hasFetchedAll:hidden" ],
		attributeBindings: [ "type", "disabled" ],

		type: "button",
		disabled: or( "isFetching", "hasFetchedAll" ),
		errorBinding: "targetObject.fetchError",

		isFetchingBinding: "targetObject.isFetching",
		hasFetchedAllBinding: "targetObject.hasFetchedAll",

		click: function() {
			var targetObject = get( this, "targetObject" );
			targetObject.send( "willFetchContent", true );
		}
	});

});

define([
	"Ember",
	"text!templates/components/infinitescroll.html.hbs"
], function( Ember, template ) {

	var get = Ember.get;
	var alias = Ember.computed.alias;
	var or = Ember.computed.or;

	return Ember.Component.extend({
		layout: Ember.HTMLBars.compile( template ),
		tagName: "button",
		classNameBindings: [ ":btn", ":btn-with-icon", ":infinitescroll", "hasFetchedAll:hidden" ],
		attributeBindings: [ "type", "disabled" ],

		type: "button",
		disabled: or( "isFetching", "hasFetchedAll" ),
		error: alias( "targetObject.fetchError" ),

		isFetching: alias( "targetObject.isFetching" ),
		hasFetchedAll: alias( "targetObject.hasFetchedAll" ),

		click: function() {
			var targetObject = get( this, "targetObject" );
			targetObject.send( "willFetchContent", true );
		}
	});

});

Replace Ember bindings with aliases


/* justcord-3
 * A JC3MP-Discord bridge.
 *
 * Author: MD678685
 * Licensed under the MIT license.
 *
 * Uses Eris, a Discord library licensed under the MIT license.
 * Requires the chat package.
 */

// Modules
import { Client as ErisClient } from "eris";
import erisEventInit from "./erisevents";
import jcmpEventInit from "./jcevents";

// Config
const config = require("./config.json");

// Initialise Eris
const eris = new ErisClient(config.eris.token);

// Globally-Exposed Variables
global.justcord = {
    chat: jcmp.events.Call("get_chat")[0],
    config,
    eris
};

// Event Handlers
jcmpEventInit();
erisEventInit();

eris.connect();

/* justcord-3
 * A JC3MP-Discord bridge.
 *
 * Author: MD678685
 * Licensed under the MIT license.
 *
 * Uses Eris, a Discord library licensed under the MIT license.
 * Requires the chat package.
 */

// Modules
import { Client as ErisClient } from "eris";
import getConfig from "./configHelper";
import erisEventInit from "./erisevents";
import jcmpEventInit from "./jcevents";

// Config
const config = getConfig();

// Initialise Eris
const eris = new ErisClient(config.eris.token);

// Globally-Exposed Variables
global.justcord = {
    chat: jcmp.events.Call("get_chat")[0],
    config,
    eris
};

// Event Handlers
jcmpEventInit();
erisEventInit();

eris.connect();

Use config helper for loading config


var app = require('app');  // Module to control application life.
var BrowserWindow = require('browser-window');  // Module to create native browser window.

// Report crashes to our server.
require('crash-reporter').start();

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
var mainWindow = null;

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

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
app.on('ready', function() {
  // Create the browser window.
  mainWindow = new BrowserWindow({width: 800, height: 600, 'web-preferences': {'web-security': false}});
  // and load the index.html of the app.
  mainWindow.loadUrl('file://' + __dirname + '/index.html');

  // Open the DevTools.
  mainWindow.webContents.openDevTools();

  // Emitted when the window is closed.
  mainWindow.on('closed', function() {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null;
  });
});

var app = require('app');  // Module to control application life.
var BrowserWindow = require('browser-window');  // Module to create native browser window.

// Report crashes to our server.
require('crash-reporter').start();

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
var mainWindow = null;

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

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
app.on('ready', function() {
  var pythonServer = require('child_process').spawn('python', ['-m', 'SimpleHTTPServer', '3000']);

  // Create the browser window.
  mainWindow = new BrowserWindow({width: 800, height: 600, 'web-preferences': {'web-security': false}});
  // and load the index.html of the app.
  mainWindow.loadUrl('file://' + __dirname + '/index.html');

  // Open the DevTools.
  mainWindow.webContents.openDevTools();

  // Emitted when the window is closed.
  mainWindow.on('closed', function() {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null;
    pythonServer.kill('SIGINT');
  });
});

Allow app to run standalone with internal python webserver


module.exports = function (creep) {
    if(creep.memory.currentTarget) {
        creep.memory.currentTarget = Game.getObjectById(creep.memory.currentTarget.id);
    }

    // Aquire target
    if(!creep.memory.currentTarget || !creep.memory.currentTarget.structureType || creep.memory.currentTarget.energy === creep.memory.currentTarget.energyCapacity) {
        creep.memory.currentTarget = creep.pos.findClosest(FIND_MY_SPAWNS, {
            filter: function(spawn) {
                    return (spawn.energy < spawn.energyCapacity);
                },
            algorithm: "astar"
        });

        if(!creep.memory.currentTarget) {
            creep.memory.currentTarget = creep.pos.findClosest(FIND_MY_STRUCTURES, {
                filter: function(structure) {
                     return (structure.structureType === STRUCTURE_EXTENSION) && (structure.energy < structure.energyCapacity);
                },
                algorithm: "astar"
            });
        }
    }

    // Execute on target
    if(creep.memory.currentTarget) {
        if(creep.pos.isNearTo(creep.memory.currentTarget.pos.x, creep.memory.currentTarget.pos.y)) {
            creep.transferEnergy(Game.getObjectById(creep.memory.currentTarget.id));
        } else {
            creep.advMove(creep.memory.currentTarget);
        }
    }
};

module.exports = function (creep) {
    if(creep.memory.currentTarget) {
        creep.memory.currentTarget = Game.getObjectById(creep.memory.currentTarget.id);
    }

    // Aquire target
    if(!creep.memory.currentTarget || !creep.memory.currentTarget.structureType || creep.memory.currentTarget.energy === creep.memory.currentTarget.energyCapacity) {
        creep.memory.currentTarget = creep.pos.findClosest(FIND_MY_SPAWNS, {
            filter: function(spawn) {
                    return (spawn.energy < spawn.energyCapacity);
                },
            algorithm: "astar"
        });

        if(!creep.memory.currentTarget) {
            creep.memory.currentTarget = creep.pos.findClosest(FIND_MY_STRUCTURES, {
                filter: function(structure) {
                     return (structure.structureType === STRUCTURE_EXTENSION || structure.structureType === STRUCTURE_LINK) && (structure.energy < structure.energyCapacity);
                },
                algorithm: "astar"
            });
        }
    }

    // Execute on target
    if(creep.memory.currentTarget) {
        if(creep.pos.isNearTo(creep.memory.currentTarget.pos.x, creep.memory.currentTarget.pos.y)) {
            creep.transferEnergy(Game.getObjectById(creep.memory.currentTarget.id));
        } else {
            creep.advMove(creep.memory.currentTarget);
        }
    }
};

Add links to harvester dump sites


var gulp = require('gulp'),
    vulcanize = require('gulp-vulcanize');

gulp.task('default', function () {
    console.log('it works!');
});

gulp.task('bundle', function () {
    return gulp.src('./public/index.html')
        .pipe(vulcanize({
            dest: './dist',
            strip: true,
            abspath: './public'
        }))
        .pipe(gulp.dest('./dist'));
});

var gulp = require('gulp'),
    vulcanize = require('gulp-vulcanize');

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

gulp.task('bundle', function () {
    return gulp.src('./public/index.html')
        .pipe(vulcanize({
            dest: './dist',
            strip: true,
            abspath: './public'
        }))
        .pipe(gulp.dest('./dist'));
});

Make bundle the default gulp task.


const FluentDOM = require('@fluent/dom');
const Fluent = require('@fluent/bundle');

const availableLanguages = {
  'en-US': ['/fluent/en-US/main.ftl'],
  'es-MX': ['/fluent/es-MX/main.ftl'],
};

let bundle;

async function load() {
  // const links = document.querySelectorAll('link[rel="localization"]');
  let language = 'en-US'; // navigator.language;
  let links = availableLanguages[language];
  if (!links) {
    language = 'en-US';
    links = availableLanguages[language];
  }
  bundle = new Fluent.FluentBundle(language);
  for (const link of links) {
    const res = await fetch(link);
    const text = await res.text();
    bundle.addResource(new Fluent.FluentResource(text));
  }
}

async function *generateBundles(_resourceIds) {
  if (!bundle) {
    await load();
  }
  yield bundle;
}

const l10n = new FluentDOM.DOMLocalization([], generateBundles);

function init() {
  l10n.connectRoot(document.documentElement);
  l10n.translateRoots();
}

function getMessage(id) {
  const obj = bundle.getMessage(id);
  if (!obj) {
    console.warn('Missing id', id);
    return `<${id}>`;
  }
  return obj.value;
}

module.exports = {
  load,
  l10n,
  init,
  getMessage,
};

const FluentDOM = require('@fluent/dom');
const Fluent = require('@fluent/bundle');

const availableLanguages = {
  'en-US': ['/fluent/en-US/main.ftl'],
  'es-MX': ['/fluent/es-MX/main.ftl'],
};

let bundle;

async function load() {
  // const links = document.querySelectorAll('link[rel="localization"]');
  let language = 'en-US'; // navigator.language;
  let links = availableLanguages[language];
  if (!links) {
    language = 'en-US';
    links = availableLanguages[language];
  }
  bundle = new Fluent.FluentBundle(language);
  for (const link of links) {
    try {
      const res = await fetch(link);
      const text = await res.text();
      bundle.addResource(new Fluent.FluentResource(text));
    } catch (e) {
      console.warn('Unable to download language pack', e);
    }
  }
}

async function *generateBundles(_resourceIds) {
  if (!bundle) {
    await load();
  }
  yield bundle;
}

const l10n = new FluentDOM.DOMLocalization([], generateBundles);

function init() {
  l10n.connectRoot(document.documentElement);
  l10n.translateRoots();
}

function getMessage(id) {
  const obj = bundle.getMessage(id);
  if (!obj) {
    console.warn('Missing id', id);
    return `<${id}>`;
  }
  return obj.value;
}

module.exports = {
  load,
  l10n,
  init,
  getMessage,
};

Handle download errors since testing environment doesn't have fetch