'use strict'

const audio = document.querySelector('audio')
const audioContext = new window.AudioContext
const audioSource = audioContext.createMediaElementSource(audio)
const analyser = audioContext.createAnalyser()
var frequencyData = new Uint8Array(200)

audioSource.connect(analyser)
audioSource.connect(audioContext.destination)

const height = window.screen.height / 3
const width = window.screen.width - 200
const bar = '1'
const visualizer = d3.select('#visualizer')
  .append('svg')
  .attr('height', height)
  .attr('width', width)

visualizer.selectAll('rect')
  .data(frequencyData)
  .enter()
  .append('rect')
  .attr('x', (d, i) => i * (width / frequencyData.length))
  .attr('width', width / frequencyData.length - bar)

function updateChart () {
  window.requestAnimationFrame(updateChart)
  analyser.getByteFrequencyData(frequencyData)

  visualizer.selectAll('rect')
   .data(frequencyData)
   .attr('y', d => height - d)
   .attr('height', d => d)
   .attr('fill', d => `rgb(${d}, ${d - 100}, ${d -50})`)
}

updateChart()

const audio = document.querySelector('audio')
const audioContext = new (window.AudioContext || window.webkitAudioContext)
const audioSource = audioContext.createMediaElementSource(audio)
const analyser = audioContext.createAnalyser()
let frequencyData = new Uint8Array(200)

audioSource.connect(analyser)
audioSource.connect(audioContext.destination)

const height = window.screen.height / 3
const width = window.screen.width - 200
const bar = '1'
const visualizer = d3.select('#visualizer')
  .append('svg')
  .attr('height', height)
  .attr('width', width)

visualizer.selectAll('rect')
  .data(frequencyData)
  .enter()
  .append('rect')
  .attr('x', (d, i) => i * (width / frequencyData.length))
  .attr('width', width / frequencyData.length - bar)

function updateVisualizer () {
  window.requestAnimationFrame(updateVisualizer)
  analyser.getByteFrequencyData(frequencyData)

  visualizer.selectAll('rect')
   .data(frequencyData)
   .attr('y', d => height - d)
   .attr('height', d => d)
   .attr('fill', d => `rgb(${d}, ${d - 100}, ${d -50})`)
}

updateVisualizer()

Add webkit support for AudioContext


'use strict';

const sauceBrowsers = require('sauce-browsers');
const run = require('sauce-test');
const path = require('path');

const pkg = require('../package');

const platforms = sauceBrowsers([
  { name: 'android', version: ['oldest', 'latest'] },
  { name: 'chrome', version: ['oldest', 'latest'] },
  { name: 'firefox', version: ['oldest', 'latest'] },
  { name: 'internet explorer', version: 'oldest..latest' },
  { name: 'iphone', version: ['oldest', 'latest'] },
  { name: 'opera', version: 'oldest..latest' },
  { name: 'safari', version: 'oldest..latest' },
  { name: 'microsoftedge', version: 'oldest..latest' }
]).then((platforms) => {
  return platforms.map((platform) => {
    return {
      browserName: platform.api_name,
      version: platform.short_version,
      platform: platform.os
    };
  });
});

run(path.join(__dirname, 'test.js'), 'saucelabs', {
  html: path.join(__dirname, 'index.html'),
  accessKey: process.env.SAUCE_ACCESS_KEY,
  username: process.env.SAUCE_USERNAME,
  browserify: true,
  disableSSL: true,
  name: pkg.name,
  parallel: 5,
  platforms
}).done((results) => {
  if (!results.passed) process.exit(1);
});

'use strict';

const sauceBrowsers = require('sauce-browsers');
const run = require('sauce-test');
const path = require('path');

const pkg = require('../package');

const platforms = sauceBrowsers([
  { name: 'android', version: ['oldest', 'latest'] },
  { name: 'chrome', version: ['oldest', 'latest'] },
  { name: 'firefox', version: ['oldest', 'latest'] },
  { name: 'internet explorer', version: 'oldest..latest' },
  { name: 'iphone', version: ['oldest', 'latest'] },
  { name: 'opera', version: 'oldest..latest' },
  { name: 'safari', version: 'oldest..latest' },
  { name: 'microsoftedge', version: 'oldest..latest' }
]).then((platforms) => {
  return platforms.map((platform) => {
    const ret = {
      browserName: platform.api_name,
      version: platform.short_version,
      platform: platform.os
    };

    if (ret.browserName === 'android') ret.deviceName = platform.long_name;

    return ret;
  });
});

run(path.join(__dirname, 'test.js'), 'saucelabs', {
  html: path.join(__dirname, 'index.html'),
  accessKey: process.env.SAUCE_ACCESS_KEY,
  username: process.env.SAUCE_USERNAME,
  browserify: true,
  disableSSL: true,
  name: pkg.name,
  parallel: 5,
  platforms
}).done((results) => {
  if (!results.passed) process.exit(1);
});

Add `deviceName` field for Android


function generateData(rows) {
    var data = [];
    for (var i = 0; i < rows; ++i) {
        data.push({
            "id": "#" + (i + 1),
            "created_at": new Date().toDateString(),
            "status": "In progress",
            "title": "Test unicode €∑ĒŽŌ•ōļķņ© " + i + Math.round(Math.random() * 1000),
            "count": Math.round(Math.random() * 100)
        });
    }
    return data;
}

function getColumns(data) {
    var firstRow = data[0];
    return _.map(firstRow, function (val, key) {
        return {name: key,  type: "string"}
    });
}

function generateData(rows) {
    var data = [];
    for (var i = 0; i < rows; ++i) {
        data.push({
            "id": "#" + (i + 1),
            "created_at": new Date().toDateString(),
            "status": "In progress",
            "title": "Test unicode €∑ĒŽŌ•ōļķņ© " + i + Math.round(Math.random() * 1000),
            "count": Math.round(Math.random() * 100)
        });
    }
    return data;
}

function getColumns(data) {
    var firstRow = data[0];
    return _.map(firstRow, function (val, key) {
        var type = _.isNumber(val) ? "int" : "string";
        return {name: key,  type: type}
    });
}

Add correct column types to test data generator.


const MessageTraySource = imports.ui.messageTray.Source;

function init () {
    MessageTraySource.prototype['SOURCE_ICON_SIZE'] = 24;
}

function enable () {
}

function disable () {
}

Add empty enable and disable functions (required)


Add minimized version of updated script.


(function poll() {

	if ( typeof ynabToolKit !== "undefined" && ynabToolKit.actOnChangeInit === true) {

		ynabToolKit.removeZeroCategories = function ()  {
		    var coverOverbudgetingCategories = $( ".modal-budget-overspending .options-shown .ynab-select-options" ).children('li');
		    coverOverbudgetingCategories.each(function(i) {
		      var t = $(this).text(); // Category balance text.
		      var categoryBalance = parseInt(t.substr(t.indexOf(":"), t.length).replace(/[^\d-]/g, ''));
		      if (categoryBalance <= 0) {
		        $(this).remove();
		      }
		    });
		};
		ynabToolKit.removeZeroCategories(); // Run itself once

	} else {
		setTimeout(poll, 250);
	}
})();

(function poll() {

	if ( typeof ynabToolKit !== "undefined" && ynabToolKit.actOnChangeInit === true) {

		ynabToolKit.removeZeroCategories = function ()  {
		    var coverOverbudgetingCategories = $( ".modal-budget-overspending .options-shown .ynab-select-options" ).children('li:not(:first-child)');
		    coverOverbudgetingCategories.each(function(i) {
		      var t = $(this).text(); // Category balance text.
		      var categoryBalance = parseInt(t.substr(t.indexOf(":"), t.length).replace(/[^\d-]/g, ''));
		      if ($(this).hasClass('is-selectable') && categoryBalance <= 0) {
		        $(this).remove();
		      }
		    });
		};
		ynabToolKit.removeZeroCategories(); // Run itself once

	} else {
		setTimeout(poll, 250);
	}
})();

Fix <0 TBB and <0 category headers not showing


console.log('fs attach record');

function loadResource(url) {
  return new Promise(function(resolve, reject){
    var link = document.createElement('link');
    link.setAttribute('rel', 'import');
    link.setAttribute('href', url);
    link.onload = function() {
      resolve(url);
    };
    document.head.appendChild(link);
  });
}

function setup(){
  // Add fs-client
  var fsClient = document.createElement('fs-client');
  fsClient.setAttribute('client-id', 'a02j000000AhNBEAA3');
  fsClient.setAttribute('redirect-uri', '/_fs-auth');
  fsClient.setAttribute('environment', 'sandbox');
  fsClient.setAttribute('save-access-token','');
  document.body.appendChild(fsClient);

  // Add fs-attach-record
  var attach = document.createElement('fs-attach-record');
  attach.setAttribute('person-id', 'KWCF-GK5');
  attach.setAttribute('url', window.location.href);
  attach.setAttribute('title', document.querySelector('h1').textContent);
  document.querySelector('#attach-to-tree').appendChild(attach);
}

loadResource(chrome.extension.getURL('components.html'))
  .then(setup);

function loadResource(url) {
  return new Promise(function(resolve, reject){
    var link = document.createElement('link');
    link.setAttribute('rel', 'import');
    link.setAttribute('href', url);
    link.onload = function() {
      resolve(url);
    };
    document.head.appendChild(link);
  });
}

function setup(){
  // Add fs-client
  var fsClient = document.createElement('fs-client');
  fsClient.setAttribute('client-id', 'a02j000000AhNBEAA3');
  fsClient.setAttribute('redirect-uri', '/_fs-auth');
  fsClient.setAttribute('environment', 'sandbox');
  fsClient.setAttribute('save-access-token','');
  document.body.appendChild(fsClient);

  // Add fs-attach-record
  var attach = document.createElement('fs-attach-record');
  attach.setAttribute('person-id', 'KWCF-GK5');
  attach.setAttribute('url', window.location.href);
  var title = document.querySelector('h1');
  if(title){
    attach.setAttribute('title', title.textContent);
  }
  document.querySelector('#attach-to-tree').appendChild(attach);
}

loadResource(chrome.extension.getURL('components.html'))
  .then(setup);

Fix bug when title didn't exist


const logger = require('winston')
const { Module } = require('../../core')

class Queue extends Module {
  constructor (...args) {
    super(...args, {
      name: 'music:queue'
    })

    this.redis = this.bot.engine.cache.client
  }

  add (guildID, video, prepend = false) {
    if (typeof video === 'object') video = JSON.stringify(video)
    return this.redis[`${prepend ? 'lpush' : 'rpush'}Async`](`music:queues:${guildID}`, video)
  }

  async remove (guildID, index = 0, count = 1) {
    const info = await this.getSongs(guildID, index)
    const res = await this.redis.lremAsync(`music:queues:${guildID}`, count, info)
    if (res) return JSON.parse(info[0])
    return null
  }

  async shift (guildID) {
    return JSON.parse(await this.redis.lpopAsync(`music:queues:${guildID}`))
  }

  clear (guildID) {
    return this.redis.delAsync(`music:queues:${guildID}`)
  }

  getLength (guildID) {
    return this.redis.llenAsync(`music:queues:${guildID}`)
  }

  isRepeat (guildID) {
    return this.redis.sismemberAsync('music:repeats', guildID)
  }

  async getSongs (guildID, start = 0, stop = start) {
    return await this.redis.lrangeAsync(`music:queues:${guildID}`, start, stop)
  }
}

module.exports = Queue

const logger = require('winston')
const { Module } = require('../../core')

class Queue extends Module {
  constructor (...args) {
    super(...args, {
      name: 'music:queue'
    })

    this.redis = this.bot.engine.cache.client
  }

  add (guildID, video, prepend = false) {
    if (typeof video === 'object') video = JSON.stringify(video)
    return this.redis[`${prepend ? 'lpush' : 'rpush'}Async`](`music:queues:${guildID}`, video)
  }

  async remove (guildID, index = 0, count = 1) {
    const info = await this.getSongs(guildID, index)
    const res = await this.redis.lremAsync(`music:queues:${guildID}`, count, info[0])
    if (res) return JSON.parse(info[0])
    return null
  }

  async shift (guildID) {
    return JSON.parse(await this.redis.lpopAsync(`music:queues:${guildID}`))
  }

  clear (guildID) {
    return this.redis.delAsync(`music:queues:${guildID}`)
  }

  getLength (guildID) {
    return this.redis.llenAsync(`music:queues:${guildID}`)
  }

  isRepeat (guildID) {
    return this.redis.sismemberAsync('music:repeats', guildID)
  }

  async getSongs (guildID, start = 0, stop = start) {
    return await this.redis.lrangeAsync(`music:queues:${guildID}`, start, stop)
  }
}

module.exports = Queue

Fix logic in queue removal


(function(){
  'use strict';

  // Prepare the 'taxretApp' module for subsequent registration of configuration, controllers and delegates
angular
  .module('taxretApp', ['ngMessages', 'ngMaterial', 'ui.router', 'app', 'users', 'hc.marked', 'kutomer.ng-lovefield']);

})();

(function(){
  'use strict';

  // Prepare the 'taxretApp' module for subsequent registration of configuration, controllers and delegates
angular
  .module('taxretApp', [

    // Global dependencies
    // -- Angular & Angular Material helpers
    'ngMessages',
    'ngMaterial',
    // -- Routing
    'ui.router',
    // -- Markup
    'hc.marked',
    // -- Database
    'kutomer.ng-lovefield',

    // App Features
    'app',
    'users',

  ]);

})();

Bring structure to app dependecies list


(function (){
  var GOVUKDATEFIELDS = function (){
    var $allDateGroupInstances = $(".form-date"),

      init = function (){

        $allDateGroupInstances.each(function (){
          var $currentGroup = $(this);
          var $inputs = $currentGroup.find('input');
          var $todayButton = $currentGroup.find('a');

          //Show Today Button if js is enabled
          $todayButton.addClass('js-enabled');

          $todayButton.on('click', function(e) {
            //Set date and remove inputs from natural tab order
            event.preventDefault();
            setTodayDate($currentGroup, $inputs, $todayButton);
            $inputs.attr('tabIndex', -1);
          }).on('focus', function(){
            $inputs.attr('tabIndex', 0);
          })
        })
      },
      setTodayDate = function($currentGroup, $inputs, $todayButton){
        //Cache the input fields
        var $dateField = $inputs.eq(0);
        var $monthField = $inputs.eq(1);
        var $yearField = $inputs.eq(2);

        //Create and return todays date
        var d = new Date();
        var day = d.getUTCDate();
        var month = d.getUTCMonth() + 1;
        var year = d.getUTCFullYear();

        if(month < 10 ){
          month = '0' + month;
        }

        //Set the input fields
        $dateField.val(day);
        $monthField.val(month);
        $yearField.val(year);
      };

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

  GOVUKDATEFIELDS.init();

})();

(function (){
  var GOVUKDATEFIELDS = function (){
    var $allDateGroupInstances = $(".form-date"),

      init = function (){

        $allDateGroupInstances.each(function (){
          var $currentGroup = $(this);
          var $inputs = $currentGroup.find('input');
          var $todayButton = $currentGroup.find('a');

          //Show Today Button if js is enabled
          $todayButton.addClass('js-enabled');

          $todayButton.on('click', function(event) {
            //Set date and remove inputs from natural tab order
            event.preventDefault();
            setTodayDate($currentGroup, $inputs, $todayButton);
            $inputs.attr('tabIndex', -1);
          }).on('focus', function(){
            $inputs.attr('tabIndex', 0);
          })
        })
      },
      setTodayDate = function($currentGroup, $inputs, $todayButton){
        //Cache the input fields
        var $dateField = $inputs.eq(0);
        var $monthField = $inputs.eq(1);
        var $yearField = $inputs.eq(2);

        //Create and return todays date
        var d = new Date();
        var day = d.getUTCDate();
        var month = d.getUTCMonth() + 1;
        var year = d.getUTCFullYear();

        if(month < 10 ){
          month = '0' + month;
        }

        //Set the input fields
        $dateField.val(day);
        $monthField.val(month);
        $yearField.val(year);
      };

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

  GOVUKDATEFIELDS.init();

})();

Fix undeclared variable error and prevent event default


/* eslint-env jest */

var mockStorage = {}

export const __mockClear = () => {
  mockStorage = {}
}

export default {
  getItem: jest.fn((key) => {
    return mockStorage[key]
  }),
  setItem: jest.fn((key, val) => {
    if (val) {
      mockStorage[key] = String(val)
    }
  }),
  removeItem: jest.fn((key, val) => {
    delete (mockStorage[key])
  })
}

/* eslint-env jest */
import { isNil } from 'lodash/lang'

var mockStorage = {}

export const __mockClear = () => {
  mockStorage = {}
}

export default {
  getItem: jest.fn((key) => {
    return mockStorage[key]
  }),
  setItem: jest.fn((key, val) => {
    if (!isNil(val)) {
      mockStorage[key] = String(val)
    }
  }),
  removeItem: jest.fn((key, val) => {
    delete (mockStorage[key])
  })
}

Make localStorage mock a little more precise


'use strict';

const fs = require('fs');
const path = require('path');
const { template, packageJson } = require('mrm-core');

module.exports = function(config) {
	const filename = config('license', 'License.md');

	const pkg = packageJson();
	const license = pkg.get('license');
	const templateFile = path.join(__dirname, `templates/${license}.md`);

	if (!fs.existsSync(templateFile)) {
		console.log(`No template for the "${license}" found, skipping`);
		return;
	}

	template(filename, templateFile)
		.apply(config(), {
			year: new Date().getFullYear(),
		})
		.save();
};
module.exports.description = 'Adds license file';

// @ts-check
'use strict';

const fs = require('fs');
const path = require('path');
const { template, packageJson } = require('mrm-core');

const defaultLicense = 'mit';

function task(config) {
	const filename = config('license', 'License.md');

	const pkg = packageJson();
	const license = pkg.get('license', defaultLicense);
	const templateFile = path.join(__dirname, `templates/${license}.md`);

	if (!fs.existsSync(templateFile)) {
		console.log(`No template for the "${license}" found, skipping`);
		return;
	}

	template(filename, templateFile)
		.apply(config(), {
			year: new Date().getFullYear(),
		})
		.save();
}
task.description = 'Adds license file';

module.exports = task;

Create MIT license by default


function WinClassicTheme() {
  Theme.call(this);

  var pickers = document.getElementsByClassName("color-item");
  var exportDestination = document.getElementById("export");

  for (var i = 0; i < pickers.length; i++) {
    var picker = pickers[i];
    var itemName = picker.dataset.item;
    this.updateFromStylesheet(itemName);
    picker.value = this.getItemColor(itemName);
    picker.oninput = this.onColorChange.bind(this);
    picker.onchange = function() {
      exportDestination.value = this.exportToIni();
    }.bind(this);
  }

  exportDestination.value = this.exportToIni();

  return this;
}

Object.setPrototypeOf(WinClassicTheme.prototype, Theme.prototype);

WinClassicTheme.prototype.onColorChange = function(e) {
  var name = e.target.dataset.item;
  var color = e.target.value;
  this.setItemColor(name, color);
  this.updateStylesheet(name);
}

WinClassicTheme.prototype.exportToIni = function() {
  var ini = "";
  for (var item in this.items) {
    var rgb = window.normalizeColor.rgba(window.normalizeColor(this.items[item].color));
    ini += item + "=" + rgb.r + " " + rgb.g + " " + rgb.b + "\n";
  }

  return ini.trim();
}

function WinClassicTheme() {
  Theme.call(this);

  this.pickers = document.getElementsByClassName("color-item");
  var exportDestination = document.getElementById("export");

  for (var i = 0; i < this.pickers.length; i++) {
    var picker = this.pickers[i];
    var itemName = picker.dataset.item;
    this.updateFromStylesheet(itemName);
    picker.value = this.getItemColor(itemName);
    picker.oninput = this.onColorChange.bind(this);
    picker.onchange = function() {
      exportDestination.value = this.exportToIni();
    }.bind(this);
  }

  exportDestination.value = this.exportToIni();

  return this;
}

Object.setPrototypeOf(WinClassicTheme.prototype, Theme.prototype);

WinClassicTheme.prototype.onColorChange = function(e) {
  var name = e.target.dataset.item;
  var color = e.target.value;
  this.setItemColor(name, color);
  this.updateStylesheet(name);
}

WinClassicTheme.prototype.exportToIni = function() {
  var ini = "";
  for (var item in this.items) {
    var rgb = window.normalizeColor.rgba(window.normalizeColor(this.items[item].color));
    ini += item + "=" + rgb.r + " " + rgb.g + " " + rgb.b + "\n";
  }

  return ini.trim();
}

WinClassicTheme.prototype.resetPickers = function() {
  for (var i = 0; i < this.pickers.length; i++) {
    var picker = this.pickers[i];
    picker.value = this.getItemColor(picker.dataset.item);
  }
}

Add function to reset pickers from stored data


export default `
20-10-2017 - Changed completion status to "Not actively maintained" as @wopr resigned. (by Zerotorescue)
24-08-2017 - Added blackout combo statistic (by wopr)
21-08-2017 - Fixed bug with stagger if a tick of the dot is absorbed it will calculate correctly (by wopr)
21-08-2017 - Added T20 2pc and 4pc stats (by wopr)
20-08-2017 - Added more information about what occured while ISB was up or not (by wopr)
20-08-2017 - This is an initial implementation, will be updated soon (by wopr)
`;

import React from 'react';

import Wrapper from 'common/Wrapper';
import ZerotorescueAvatar from 'Parser/Paladin/Holy/Images/zerotorescue-avatar.png';

const Zerotorescue = ['Zerotorescue', ZerotorescueAvatar];

export default [
  {
    date: new Date('3000-01-01'),
    changes: Changed completion status to Not actively maintained as @wopr resigned. Any help is welcome to continue support for this spec, see GitHub for more information.,
    contributors: [Zerotorescue],
  },
  {
    date: new Date('2017-08-24'),
    changes: 'Added blackout combo statistic.',
    contributors: ['wopr'],
  },
  {
    date: new Date('2017-08-21'),
    changes: 'Fixed bug with stagger if a tick of the dot is absorbed it will calculate correctly.',
    contributors: ['wopr'],
  },
  {
    date: new Date('2017-08-21'),
    changes: 'Added T20 2pc and 4pc stats.',
    contributors: ['wopr'],
  },
  {
    date: new Date('2017-08-20'),
    changes: 'Added more information about what occured while ISB was up or not',
    contributors: ['wopr'],
  },
  {
    date: new Date('2017-08-20'),
    changes: 'This is an initial implementation, will be updated soon',
    contributors: ['wopr'],
  },
];

Convert Brewmaster changelog to new format & update the *not actively maintained* entry


/**
 *
 * @flow
 */
'use strict';

import type {Action} from '../actions/types';

export type User = {
    id: number;
    name: string;
    handle: string;
    score: number;
    teamId?: number;
    email?: string;
    address?: string;
}

type State = {
    isLoggedIn: boolean;
}

const initialState: State = {
    isLoggedIn: false
};

export default function user(state: State = initialState, action: Action): State {
    switch (action.type) {
        // TODO

        default:
            return state;
    }
}

/**
 *
 * @flow
 */
'use strict';

import type {Action} from '../actions/types';

export type User = {
    id: string;
    name: string;
    username: string;
    score: string;
    teamId?: string;
    email?: string;
    address?: string;
}

type State = {
    isLoggedIn: boolean;
}

const initialState: State = {
    isLoggedIn: false
};

export default function user(state: State = initialState, action: Action): State {
    switch (action.type) {
        // TODO

        default:
            return state;
    }
}

Fix types across the stack; User contract expects bytes32 vals


;(function() {
  var templates = {
    form: '\
      <form class="form-horizontal">{{fieldsets}}</form>\
    ',

    fieldset: '\
      <fieldset>\
        {{legend}}\
        {{fields}}\
      </fieldset>\
    ',

    field: '\

\
        {{title}}\

\

{{editor}}
\

{{help}}
\

\

\
    ',

    simpleList: '\

{{items}}
\
      <button class="btn" data-action="add">Add
\
    ',

    simpleListItem: '\

\

{{editor}}
\
        <button class="btn" style="margin-left: 4px" data-action="remove">x</button>\

\
    '
  };

  var classNames = {
    error: 'error'
  };

  Backbone.Form.helpers.setTemplates(templates, classNames);
})();

;(function() {
  var templates = {
    form: '\
      <form class="form-horizontal">{{fieldsets}}</form>\
    ',

    fieldset: '\
      <fieldset>\
        {{legend}}\
        {{fields}}\
      </fieldset>\
    ',

    field: '\

\
        {{title}}\

\

{{editor}}
\

{{help}}
\

\

\
    ',

    simpleList: '\

{{items}}
\
      <button class="btn" style="margin-top: -10px" data-action="add">Add
\
    ',

    simpleListItem: '\

\

{{editor}}
\
        <button class="btn" style="margin-left: 4px" data-action="remove">x</button>\

\
    '
  };

  var classNames = {
    error: 'error'
  };

  Backbone.Form.helpers.setTemplates(templates, classNames);
})();

Use clearfix instead of overflow


/**
 * A simple directive that will add or remove a class to the body of the document if the document has been scrolled.
 */
export function ScrollClass() {
  return {
    restrict: 'A',
    scope: {},
    link: function($scope, elem, attrs) {
      const threshold = attrs.scrollClassThreshold || 0;

      function stickyHeader() {
        const scrolled = document.documentElement.scrollTop > threshold;
        elem[0].classList.toggle(attrs.scrollClass, scrolled);
      }

      let rafTimer;
      function scrollHandler() {
        cancelAnimationFrame(rafTimer);
        rafTimer = requestAnimationFrame(stickyHeader);
      }

      document.addEventListener('scroll', scrollHandler, false);

      $scope.$on('$destroy', () => {
        document.removeEventListener('scroll', scrollHandler);
        cancelAnimationFrame(rafTimer);
      });
    }
  };
}

/**
 * A simple directive that will add or remove a class to the body of the document if the document has been scrolled.
 */
export function ScrollClass() {
  return {
    restrict: 'A',
    scope: {},
    link: function($scope, elem, attrs) {
      const threshold = attrs.scrollClassThreshold || 0;

      function stickyHeader() {
        const scrolled = document.body.scrollTop > threshold || document.documentElement.scrollTop > threshold;
        elem[0].classList.toggle(attrs.scrollClass, scrolled);
      }

      let rafTimer;
      function scrollHandler() {
        cancelAnimationFrame(rafTimer);
        rafTimer = requestAnimationFrame(stickyHeader);
      }

      document.addEventListener('scroll', scrollHandler, false);

      $scope.$on('$destroy', () => {
        document.removeEventListener('scroll', scrollHandler);
        cancelAnimationFrame(rafTimer);
      });
    }
  };
}

Fix shadow on iOS too


import moment from 'moment';

export default function init(ngModule) {
  ngModule.filter('toMilliseconds', () => value => value * 1000.0);

  ngModule.filter('dateTime', clientConfig =>
    function dateTime(value) {
      if (!value) {
        return '-';
      }

      return moment(value).format(clientConfig.dateTimeFormat);
    });
}

import moment from 'moment';

export default function init(ngModule) {
  ngModule.filter('toMilliseconds', () => value => value * 1000.0);

  ngModule.filter('dateTime', clientConfig =>
    function dateTime(value) {
      if (!value || !value.isValid()) {
        return '-';
      }

      return moment(value).format(clientConfig.dateTimeFormat);
    });
}

Replace invalid date time strings


'use strict';

/**
 * @constructor
 */
function EventEmitter() {
  this.eventHandlers = {};
}

EventEmitter.prototype.emit = function(event) {
  if (!this.eventHandlers[event]) {
    return;
  }
  var argsOut = [];
  for (var i = 1; i < arguments.length; ++i) {
    argsOut.push(arguments[i]);
  }
  for (var j = 0; j < this.eventHandlers[event].length; ++j) {
    this.eventHandlers[event][j].apply(this, argsOut);
  }
};

EventEmitter.prototype.addEventListener = function(event, handler) {
  if (!this.eventHandlers[event]) {
    this.eventHandlers[event] = [];
  }
  this.eventHandlers[event].push(handler);
};
EventEmitter.prototype.on = EventEmitter.prototype.addEventListener;

EventEmitter.prototype.removeEventListener = function(event, handler) {
  if (!this.eventHandlers[event]) {
    return false;
  }
  var handlerIdx = this.eventHandlers[event].indexOf(handler);
  if (handlerIdx !== -1) {
    this.eventHandlers[event].splice(handlerIdx, 1);
    return true;
  }
  return false;
};

EventEmitter.prototype.once = function(event, handler) {
  var onceHandler = function() {
    this.removeEventListener(event, onceHandler);
    handler();
  }.bind(this);
  this.addEventListener(event, onceHandler);
};

'use strict';

/**
 * @constructor
 */
function EventEmitter() {
  this.eventHandlers = {};
}

EventEmitter.prototype.emit = function(event) {
  if (!this.eventHandlers[event]) {
    return;
  }
  var argsOut = [];
  for (var i = 1; i < arguments.length; ++i) {
    argsOut.push(arguments[i]);
  }
  for (var j = 0; j < this.eventHandlers[event].length; ++j) {
    this.eventHandlers[event][j].apply(this, argsOut);
  }
};

EventEmitter.prototype.addEventListener = function(event, handler) {
  if (!this.eventHandlers[event]) {
    this.eventHandlers[event] = [];
  }
  this.eventHandlers[event].push(handler);
};
EventEmitter.prototype.on = EventEmitter.prototype.addEventListener;

EventEmitter.prototype.removeEventListener = function(event, handler) {
  if (!this.eventHandlers[event]) {
    return false;
  }
  var handlerIdx = this.eventHandlers[event].indexOf(handler);
  if (handlerIdx !== -1) {
    this.eventHandlers[event].splice(handlerIdx, 1);
    return true;
  }
  return false;
};

EventEmitter.prototype.once = function(event, handler) {
  var onceHandler = function() {
    this.removeEventListener(event, onceHandler);
    handler.apply(this, arguments);
  }.bind(this);
  this.addEventListener(event, onceHandler);
};

Fix bug in EventEmitter once method causing arguments to be lost.


({
  name: 'support-consistent-playback-delay',
  description: 'Consistent time between play() and actual playback (< 20ms)',
  assert: function(finish) {
    var audio = this.audio = document.createElement('audio'),
        playTime;

    audio.addEventListener('playing', function() {
      finish(Date.now() - playTime < 20);
    }, false);

    audio.addEventListener('canplay', function() {
      audio.volume = 0;
      playTime = Date.now();
      audio.play();
    }, false);

    audio.src = AWPY.sound.short.stream_url();
  }
})

({
  name: 'support-consistent-playback-delay',
  description: 'Consistent time between play() and actual playback (< 20ms)',
  assert: function(finish) {
    var audio = this.audio = new Audio(),
        playTime;

    audio.addEventListener('playing', function() {
      finish(Date.now() - playTime < 20);
    }, false);

    audio.addEventListener('canplay', function() {
      audio.volume = 0;
      playTime = Date.now();
      audio.play();
    }, false);

    audio.src = AWPY.sound.short.stream_url();
  }
})

Use new Audio() instead of createElement


$(document).ready(function() {
	OC.Router.registerLoadedCallback(function() {
		var url;
		if (OC.Router) {
			url = OC.Router.generate('roundcube_refresh');
		} else {
			url = OC.generateUrl('roundcube_refresh');
		}
		setInterval(function() {
			$.post(url);
		}, 30000);
	});
});

$(document).ready(function() {
	if (OC.Router) {
		OC.Router.registerLoadedCallback(function() {
			var url = OC.Router.generate('roundcube_refresh');
			setInterval(function() {
				$.post(url);
			}, 30000);
		});
		// starting with OC7 OC.Router was removed
	} else {
		var url = OC.generateUrl('roundcube_refresh');
		setInterval(function() {
			$.post(url);
		}, 30000);
	}
});

Fix JS errors in OC7


#!/usr/bin/env node

'use strict';

const dnode = require('dnode');
const host = process.env.COLLECTD_HOSTNAME || 'storj.node';
const interval = 120; //override default collectd interval to avoid storj-daemon performance issue
const storjDaemonPort = 45015;

const outputCollectdData = (host, share) => {
    console.log(`PUTVAL ${host} /storj/peers-${share.id} interval=${interval} N:${share.meta.farmerState.totalPeers}`);
    console.log(`PUTVAL ${host} /storj/restarts-${share.id} interval=${interval} N:${share.meta.numRestarts}`);
    console.log(`PUTVAL ${host} /storj/shared-${share.id} interval=${interval} N:${share.meta.farmerState.spaceUsedBytes}`);
};

const status = () => {

    const sock = dnode.connect(storjDaemonPort);

    sock.on('error', function() {
        console.error('Daemon is not running');
        process.exit();
    });

    sock.on('remote', (rpc) => {

        rpc.status((err, shares) => {

            shares.forEach((share) => {
                outputCollectdData(host, share);
            });

            sock.end();
        });
    });
};

setInterval(status, interval * 1000);

#!/usr/bin/env node

'use strict';

const dnode = require('dnode');
const host = process.env.COLLECTD_HOSTNAME || 'storj.node';
const interval = 120; //override default collectd interval to avoid storj-daemon performance issue
const storjDaemonPort = 45015;

const outputCollectdData = (share) => {
    console.log(`PUTVAL ${host}/storj/peers-${share.id} interval=${interval} N:${share.meta.farmerState.totalPeers}`);
    console.log(`PUTVAL ${host}/storj/restarts-${share.id} interval=${interval} N:${share.meta.numRestarts}`);
    console.log(`PUTVAL ${host}/storj/shared-${share.id} interval=${interval} N:${share.meta.farmerState.spaceUsedBytes}`);
};

const status = () => {

    const sock = dnode.connect(storjDaemonPort);

    sock.on('error', function() {
        console.error('Daemon is not running');
        process.exit();
    });

    sock.on('remote', (rpc) => {

        rpc.status((err, shares) => {

            shares.forEach((share) => outputCollectdData);

            sock.end();
        });
    });
};

setInterval(status, interval * 1000);

Fix bug in collectd plain text protocol output


require('dotenv').config();
const config = require('./source/config');
const info = require('./source/info');
const notaries = require('./source/notaries');
const smartContract = require('./source/smart_contract');

exports.setKeys = config.setKeys;
exports.useSandbox = config.useSandbox;

exports.getInfoNodeBitcoin = info.getInfoNodeBitcoin;
exports.getInfoNodeEthereum = info.getInfoNodeEthereum;

exports.postNotary = notaries.postNotary;
exports.getNotary = notaries.getNotary;
exports.getNotaries = notaries.getNotaries;

exports.postContractEthereumSolidity = smartContract.postContractEthereumSolidity;
exports.getContract = smartContract.getContract;
exports.postContractEthereumSolidityFunction = smartContract.postContractEthereumSolidityFunction;

const fs = require('fs');
const config = require('./source/config');
const info = require('./source/info');
const notaries = require('./source/notaries');
const smartContract = require('./source/smart_contract');

const s = fs.statSync('./.env');
if (s.isFile()) {
  require('dotenv').config();
}

exports.setKeys = config.setKeys;
exports.useSandbox = config.useSandbox;

exports.getInfoNodeBitcoin = info.getInfoNodeBitcoin;
exports.getInfoNodeEthereum = info.getInfoNodeEthereum;

exports.postNotary = notaries.postNotary;
exports.getNotary = notaries.getNotary;
exports.getNotaries = notaries.getNotaries;

exports.postContractEthereumSolidity = smartContract.postContractEthereumSolidity;
exports.getContract = smartContract.getContract;
exports.postContractEthereumSolidityFunction = smartContract.postContractEthereumSolidityFunction;

Fix to avoid crash at online build


/*!
 * customize-engine-less <https://github.com/nknapp/customize-engine-less>
 *
 * Copyright (c) 2015 Nils Knappmeier.
 * Released under the MIT license.
 */
'use strict'

var _ = require('lodash')
var less = require('less')
var Q = require('q')

module.exports = {
  defaultConfig: {
    main: [],
    paths: []
  },

  preprocessConfig: function (config) {
    return {
      main: coerceToArray(config.main),
      paths: coerceToArray(config.paths)
    }
  },

  run: function (config) {
    var lessSource = config.main.map(function (file) {
      return '@import "' + file + '";'
    }).join('\n')
    return less.render(lessSource, {
      paths: config.paths,
      sourceMap: {},
      filename: 'main.less',
      compress: true
    })
  }
}

/**
 * If `objOrArray` exists and is a non-array, it is replaced by
 * an array with the property as single object.
 * @param {object} objOrArray the object or an array
 * @return objOrArray, if it is an array or an array containing `objOrArray` (if it is no array)
 */
function coerceToArray (objOrArray) {
  if (!_.isUndefined(objOrArray) && !_.isArray(objOrArray)) {
    return [ objOrArray ];
  }
  return objOrArray;

}

/*!
 * customize-engine-less <https://github.com/nknapp/customize-engine-less>
 *
 * Copyright (c) 2015 Nils Knappmeier.
 * Released under the MIT license.
 */
'use strict'

var _ = require('lodash')
var less = require('less')
var Q = require('q')

module.exports = {
  defaultConfig: {
    main: [],
    paths: []
  },

  preprocessConfig: function (config) {
    return {
      main: coerceToArray(config.main),
      paths: coerceToArray(config.paths)
    }
  },

  run: function (config) {
    var lessSource = config.main.map(function (file) {
      return '@import "' + file + '";'
    }).join('\n')
    return less.render(lessSource, {
      paths: config.paths,
      sourceMap: {},
      filename: 'customize-bundle.less',
      compress: true
    })
  }
}

/**
 * If `objOrArray` exists and is a non-array, it is replaced by
 * an array with the property as single object.
 * @param {object} objOrArray the object or an array
 * @return objOrArray, if it is an array or an array containing `objOrArray` (if it is no array)
 */
function coerceToArray (objOrArray) {
  if (!_.isUndefined(objOrArray) && !_.isArray(objOrArray)) {
    return [ objOrArray ];
  }
  return objOrArray;

}

Change less main-source name to something more recognizable


#!/usr/bin/env node

'use strict';

var fs   = require('fs');
var exec = require('../util/exec');

var repoDir = process.env.REPO_DIR;
var file = repoDir + '/package.json';
var pkg;

if (!fs.existsSync(file)) {
    return;
}

// Read package json
pkg = require(file);

// Is it ok already?
if (pkg.license === 'MIT') {
    process.stdout.write('Nothing to be changed.\n');
    process.exit();
}

// Change to MIT and save file
pkg.license = 'MIT';
fs.writeFileSync(file + '/package.json', JSON.stringify(pkg, null, 2) + '\n');

// Commit & push
exec('git', ['commit', '-a', '-m', 'Change license to MIT (made with screpto)'], { cwd: repoDir });
exec('git', ['push'], { cwd: repoDir, stdio: 'inherit' });

#!/usr/bin/env node

'use strict';

var fs   = require('fs');
var exec = require('../util/exec');

var repoDir = process.env.REPO_DIR;
var file = repoDir + '/package.json';
var pkg;

if (!fs.existsSync(file)) {
    return;
}

// Read package json
pkg = require(file);

// Is it ok already?
if (pkg.license === 'MIT') {
    process.stdout.write('Nothing to be changed.\n');
    process.exit();
}

// Change to MIT and save file
pkg.license = 'MIT';
fs.writeFileSync(file, JSON.stringify(pkg, null, 2) + '\n');

// Commit & push
exec('git', ['commit', '-a', '-m', 'Change license to MIT (made with screpto)'], { cwd: repoDir });
exec('git', ['push'], { cwd: repoDir, stdio: 'inherit' });

Fix example mit license script


import React from 'react';
import styles from './petition-sidebar.scss';
import Countdown from 'components/Countdown';
import PetitionResponseStatus from 'containers/PetitionResponseStatus';
import ButtonIcon from 'components/ButtonIcon';
import FakeButton from 'components/FakeButton';
import SupportButton from 'containers/SupportButton';
import SharePetition from 'containers/SharePetition';
import settings from 'settings';

const PetitionSidebar = ({
  processing,
  timeMetric,
  isSupportable,
  userHasSupported,
  startDate,
  runningTime
}) => (

    {processing &&
      <PetitionResponseStatus />
    }

    {isSupportable &&

          <Countdown timeMetric={timeMetric} />

{startDate}

{runningTime}

    }

      {isSupportable && !userHasSupported &&
        <SupportButton />
      }

      {isSupportable && userHasSupported &&
        <FakeButton disabled block>
          <ButtonIcon id={'Checkmark'} modifier={'dimmed'}>
            {settings.petitionPage.supportButton.supportedText}
          </ButtonIcon>
        </FakeButton>
      }

      {!isSupportable &&
        <FakeButton disabled block>
          {settings.petitionPage.supportButton.unsupportableText}
        </FakeButton>
      }

      <SharePetition />

);

export default PetitionSidebar;

import React from 'react';
import styles from './petition-sidebar.scss';
import Countdown from 'components/Countdown';
import PetitionResponseStatus from 'containers/PetitionResponseStatus';
import ButtonIcon from 'components/ButtonIcon';
import FakeButton from 'components/FakeButton';
import SupportButton from 'containers/SupportButton';
import SharePetition from 'containers/SharePetition';
import settings from 'settings';

const PetitionSidebar = ({
  processing,
  timeMetric,
  isSupportable,
  userHasSupported,
  startDate,
  runningTime
}) => (

    {processing &&
      <PetitionResponseStatus />
    }

    {isSupportable &&

        <Countdown timeMetric={timeMetric} />

    }

{startDate}

{runningTime}

      {isSupportable && !userHasSupported &&
        <SupportButton />
      }

      {isSupportable && userHasSupported &&
        <FakeButton disabled block>
          <ButtonIcon id={'Checkmark'} modifier={'dimmed'}>
            {settings.petitionPage.supportButton.supportedText}
          </ButtonIcon>
        </FakeButton>
      }

      {!isSupportable &&
        <FakeButton disabled block>
          {settings.petitionPage.supportButton.unsupportableText}
        </FakeButton>
      }

      <SharePetition />

);

export default PetitionSidebar;

Add petition start date and running time back in for ended petitions


var doc = window.document;

function setup(container) {
  var inputs = container.getElementsByTagName('input');
  var arr = Array.prototype.slice.call(inputs);

  for (var i=0; i<arr.length; i++) {
    var element = arr[i];
    if (element.id.indexOf('weeks[') !== -1) {
      element.addEventListener("blur", function (event) {
        var value = event.target.value;
        var parts = value.split(':');

        if (parts.length === 2) {
          var element = event.target;
          element.value = parseInt(parts[0]) + parseFloat(parseInt(parts[1])/60);
          if ("createEvent" in doc) {
            var evt = doc.createEvent("HTMLEvents");
            evt.initEvent("change", false, true);
            element.dispatchEvent(evt);
          }
          else {
            element.fireEvent("onchange");
          }
        }
      }, true);
    }
  }
}

var interval = setInterval(function () {
  var container = doc.getElementById('ajaxContentDataForm');
  if (container) {
    clearInterval(interval);
    setup(container);
  }
}, 50);

var doc = window.document;

function setup(container) {
  var inputs = container.getElementsByClassName('hours');
  var arr = Array.prototype.slice.call(inputs);
  for (var i=0; i<arr.length; i++) {
    var element = arr[i];
    if ( (" " + element.className + " ").replace(/[\n\t]/g, " ").indexOf(" smart-processed ") > -1 ) {
      //Skip already processed fields
      continue;
    }
    element.addEventListener("blur", function (event) {
      var value = event.target.value;
      var parts = value.split(':');

      if (parts.length === 2) {
        var element = event.target;
        element.value = parseInt(parts[0]) + parseFloat(parseInt(parts[1])/60);
        if ("createEvent" in doc) {
          var evt = doc.createEvent("HTMLEvents");
          evt.initEvent("change", false, true);
          element.dispatchEvent(evt);
        }
        else {
          element.fireEvent("onchange");
        }
      }
      var oldClasses = element.getAttribute('class');
      element.setAttribute('class', oldClasses + ' smart-processed');
    }, true);

  }
}

var interval = setInterval(function () {
  var container = doc.getElementById('ajaxContenthourListTable');
  if (container) {
    setup(container);
  }
}, 3000);

Update to always run, and tune some of the selectors


const router = require('../router')
const urls = require('../../../lib/urls')

describe('Company router', () => {
  it('should define all routes', () => {
    const paths = router.stack.filter(r => r.route).map(r => r.route.path)
    expect(paths).to.deep.equal(
      [
        '/',
        '/export',
        '/:companyId/archive',
        '/:companyId/unarchive',
        '/:companyId',
        '/:companyId/details',
        urls.companies.exports.index.route,
        urls.companies.exports.edit.route,
        '/:companyId/business-details',
        '/:companyId/hierarchies/ghq/search',
        '/:companyId/hierarchies/ghq/:globalHqId/add',
        '/:companyId/hierarchies/ghq/remove',
        '/:companyId/hierarchies/subsidiaries/search',
        '/:companyId/hierarchies/subsidiaries/:subsidiaryCompanyId/add',
        '/:companyId/contacts',
        '/:companyId/orders',
        '/:companyId/audit',
        '/:companyId/documents',
        '/:companyId/manage-company-list',
        '/:companyId/subsidiaries',
        '/:companyId/subsidiaries/link',
      ])
  })
})

const router = require('../router')
const urls = require('../../../lib/urls')

describe('Company router', () => {
  it('should define all routes', () => {
    const paths = router.stack.filter(r => r.route).map(r => r.route.path)
    expect(paths).to.deep.equal([
      '/',
      '/export',
      '/:companyId/archive',
      '/:companyId/unarchive',
      '/:companyId',
      '/:companyId/details',
      urls.companies.exports.index.route,
      urls.companies.exports.edit.route,
      '/:companyId/business-details',
      '/:companyId/hierarchies/ghq/search',
      '/:companyId/hierarchies/ghq/:globalHqId/add',
      '/:companyId/hierarchies/ghq/remove',
      '/:companyId/hierarchies/subsidiaries/search',
      '/:companyId/hierarchies/subsidiaries/:subsidiaryCompanyId/add',
      '/:companyId/contacts',
      '/:companyId/orders',
      '/:companyId/audit',
      '/:companyId/documents',
      '/:companyId/manage-company-list',
      '/:companyId/subsidiaries',
      '/:companyId/subsidiaries/link',
    ])
  })
})

Reset file as no changes were made. Just formatting


(function (env) {
    "use strict";
    env.ddg_spice_rfc = function(api_result) {

        if (!api_result || api_result.error || api_result.message == "no results.") {
            return Spice.failed('request_for_comments');
        }

        Spice.add({
            id: "rfc",
            name: "Answer",
            data: api_result,
            meta: {
                sourceName: "RFC Search"
            },
            normalize: function(item) {
                return {
                    title: item.title,
                    altSubtitle: item.authors,
                    description: item.moreinfo,
                    url: item.link,
                    number: item.number,
                    date: item.date
                };
            },
            templates: {
                group: 'text',
                detail: false,
                item_detail: false,
                options: {
                    moreAt: false,
                    footer: Spice.rfc.footer
                },
                variants: {
                    tile: 'wide',
                    tileTitle: '3line-small',
                    tileSnippet: 'small',
                    tileFooter: '2line'
                }
            }
        });
    };
}(this));

(function (env) {
    "use strict";
    env.ddg_spice_rfc = function(api_result) {

        if (!api_result || api_result.error || api_result.message == "no results.") {
            return Spice.failed('request_for_comments');
        }

        Spice.add({
            id: "request_for_comments",
            name: "Answer",
            data: api_result,
            meta: {
                sourceName: "RFC Search"
            },
            normalize: function(item) {
                return {
                    title: item.title,
                    altSubtitle: item.authors,
                    description: item.moreinfo,
                    url: item.link,
                    number: item.number,
                    date: item.date
                };
            },
            templates: {
                group: 'text',
                detail: false,
                item_detail: false,
                options: {
                    moreAt: false,
                    footer: Spice.rfc.footer
                },
                variants: {
                    tile: 'wide',
                    tileTitle: '3line-small',
                    tileSnippet: 'small',
                    tileFooter: '2line'
                }
            }
        });
    };
}(this));

Update ID to match IA Page


/**
 * Common stack, plugins and helpers
 */
import config from 'config';
import $ from 'jquery';
import 'bootstrap';
import 'widgets/site-search';
import 'utils/ellipsis';
import 'vendor/jquery.microdata';
import 'i18n';

$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type)) {
            xhr.setRequestHeader('X-CSRFToken', config.csrftoken);
        }
    }
});

$(function() {
    // Display tooltips and popovers with markup
    $('[rel=tooltip]').tooltip();
    $('[rel=popover]').popover().on('click', function(e) {
        if ($(this).data('trigger').match(/(click|focus)/)) {
            e.preventDefault();
            return true;
        }
    });
});

/**
 * Common stack, plugins and helpers
 */
import config from 'config';
import $ from 'jquery';
import Notify from 'notify';
import 'bootstrap';
import 'widgets/site-search';
import 'utils/ellipsis';
import 'vendor/jquery.microdata';
import 'i18n';

$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type)) {
            xhr.setRequestHeader('X-CSRFToken', config.csrftoken);
        }
    }
});

$(document).ajaxError(function(event, jqxhr, settings, thrownError) {
    const sentry_id = jqxhr.getResponseHeader('X-Sentry-ID');
    if (sentry_id) {
        Notify.error([
            i18n._('An error occured'),
            i18n._('The error identifier is {id}', {id: sentry_id}),
        ].join('. '))
    }
});

$(function() {
    // Display tooltips and popovers with markup
    $('[rel=tooltip]').tooltip();
    $('[rel=popover]').popover().on('click', function(e) {
        if ($(this).data('trigger').match(/(click|focus)/)) {
            e.preventDefault();
            return true;
        }
    });
});

Handle Sentry error ID in front Ajax


const defaultState = {
  comment: { id: null, content: '' }
};

export default function(state = defaultState, action) {
  switch (action.type) {
    case 'COMMENT_SELECT':
      return {
        ...state,
        comment: action.value
      };

    default:
      return state;
  }
}

const defaultState = {
  endCursor: '0',
  comment: { id: null, content: '' }
};

export default function(state = defaultState, action) {
  switch (action.type) {
    case 'COMMENT_SELECT':
      return {
        ...state,
        comment: action.value
      };

    case 'POST_ENDCURSOR':
      return {
        ...state,
        endCursor: action.value
      };

    default:
      return state;
  }
}

Rollback prematurely modified by mistake post reducers


"use strict";

module.exports = function(chat) {
	const selection = window.getSelection();

	// If selection does not span multiple elements, do nothing
	if (selection.anchorNode === selection.focusNode) {
		return;
	}

	const range = selection.getRangeAt(0);
	const documentFragment = range.cloneContents();
	const div = document.createElement("div");

	div.id = "js-copy-hack";
	div.appendChild(documentFragment);
	chat.appendChild(div);

	selection.selectAllChildren(div);

	window.setTimeout(() => {
		chat.removeChild(div);
		selection.removeAllRanges();
		selection.addRange(range);
	}, 0);
};

"use strict";

module.exports = function(chat) {
	// Disable in Firefox as it already copies flex text correctly
	if (typeof window.InstallTrigger !== "undefined") {
		return;
	}

	const selection = window.getSelection();

	// If selection does not span multiple elements, do nothing
	if (selection.anchorNode === selection.focusNode) {
		return;
	}

	const range = selection.getRangeAt(0);
	const documentFragment = range.cloneContents();
	const div = document.createElement("div");

	div.id = "js-copy-hack";
	div.appendChild(documentFragment);
	chat.appendChild(div);

	selection.selectAllChildren(div);

	window.setTimeout(() => {
		chat.removeChild(div);
		selection.removeAllRanges();
		selection.addRange(range);
	}, 0);
};

Disable copy hack in Firefox


import { filters2cql } from '@folio/stripes-components/lib/FilterGroups';

function makePathFunction(basePath, findAll, queryTemplate, sortMap, filterConfig) {
  return (queryParams, _pathComponents, _resourceValues, logger) => {
    const { query, filters, sort } = queryParams || {};

    let cql = !query ? undefined : queryTemplate.replace(/\${query}/g, query);
    const filterCql = filters2cql(filterConfig, filters);
    if (filterCql) {
      if (cql) {
        cql = `(${cql}) and ${filterCql}`;
      } else {
        cql = filterCql;
      }
    }

    if (sort) {
      const sortIndex = sortMap[sort] || sort;
      if (cql === undefined) cql = findAll;
      cql += ` sortby ${sortIndex}`;
    }

    let path = basePath;
    if (cql) path += `?query=${encodeURIComponent(cql)}`;

    logger.log('mpath', `query='${query}' filters='${filters}' sort='${sort}' -> ${path}`);
    return path;
  };
}

export default makePathFunction;

import { filters2cql } from '@folio/stripes-components/lib/FilterGroups';

function makePathFunction(basePath, findAll, queryTemplate, sortMap, filterConfig) {
  return (queryParams, _pathComponents, _resourceValues, logger) => {
    const { query, filters, sort } = queryParams || {};

    let cql = !query ? undefined : queryTemplate.replace(/\$QUERY/g, query);
    const filterCql = filters2cql(filterConfig, filters);
    if (filterCql) {
      if (cql) {
        cql = `(${cql}) and ${filterCql}`;
      } else {
        cql = filterCql;
      }
    }

    if (sort) {
      const sortIndex = sortMap[sort] || sort;
      if (cql === undefined) cql = findAll;
      cql += ` sortby ${sortIndex}`;
    }

    let path = basePath;
    if (cql) path += `?query=${encodeURIComponent(cql)}`;

    logger.log('mpath', `query='${query}' filters='${filters}' sort='${sort}' -> ${path}`);
    return path;
  };
}

export default makePathFunction;

Change substitution syntax in queryTemplate from ${query} to $QUERY. The former misleading resembled ES6 string-template notations, but that's not really what was happening here -- as was rightly pointed out by an ESLint no-template-curly-in-string error in Users.js.


Kwf.onJElementReady('.cssClass .outerYoutubeContainer', function youtubePlayer(el) {
    var youtubeApiLoaded = false;
    var animationFinished = false;
    var imageEl = el.children('.image');
    var youtubeContainerEl = el.children('.youtubeContainer');
    var youtubePlayerEl = youtubeContainerEl.children('.youtubePlayer');
    var loadingEl = youtubeContainerEl.children('.outerLoading');

    imageEl.click(function(ev) {
        Kwf.Utils.YoutubePlayer.load(function() {});
        imageEl.fadeOut(300, function() {
            youtubeContainerEl.css('position', 'relative');
            youtubePlayerEl.show();
            el.addClass('youtubeActive');
            Kwf.callOnContentReady(el.parent());
            loadingEl.css('display', 'block');
        });
    });
});

Kwf.onJElementReady('.cssClass .outerYoutubeContainer', function youtubePlayer(el) {
    var youtubeApiLoaded = false;
    var animationFinished = false;
    var imageEl = el.children('.image');
    var youtubeContainerEl = el.children('.youtubeContainer');
    var youtubePlayerEl = youtubeContainerEl.children('.youtubePlayer');
    var loadingEl = youtubeContainerEl.children('.outerLoading');

    imageEl.click(function(ev) {
        Kwf.Utils.YoutubePlayer.load(function() {});
        imageEl.fadeOut(300, function() {
            youtubeContainerEl.css('position', 'relative');
            youtubePlayerEl.show();
            el.addClass('youtubeActive');
            Kwf.callOnContentReady(el.parent(), { action: 'show' });
            loadingEl.css('display', 'block');
        });
    });
});

Add missing action parameter for callOnContentRedady


var express = require('express');
var cors = require('cors');
var bodyParser = require('body-parser');
var nano = require('nano')('http://localhost:8984');
var todo = nano.db.use('todo');

var app = express();

app.use(cors());
app.use(bodyParser.json());

app.get('/', function(req, res, next) {
  console.log('get triggered');
  res.json([]);
});

app.post('/', function(req, res, next) {
  console.log('post triggered');
  res.json([]);
});

app.listen(3000, function() {
  console.log('Cors enabled server listening on port 3000');
});

var express = require('express');
var cors = require('cors');
var bodyParser = require('body-parser');
var nano = require('nano')('http://localhost:8984');
var todo = nano.db.use('todo');

var app = express();

app.use(cors());
app.use(bodyParser.json());

app.get('/', function(req, res, next) {
  console.log('get triggered');
  res.json([]);
});

app.post('/', function(req, res, next) {
  console.log('post triggered');
  var task = {title: req.body.title};
  res.json(task);
});

app.listen(3000, function() {
  console.log('Cors enabled server listening on port 3000');
});

Return task passed in from post - passing 2/16.


// call to required packages
var express = require('express');
var d3 = require('d3');
var fs = require('fs');

var app = express(); // create the application module

var port = process.env.PORT || 5000;

var router = express.Router();  // Router associate URL requests to specific handlers
var dsv = d3.dsv(";","text/html")

// define a test route to check if the server is working correctly
router.get('/', function(req, res){
	res.json({message: 'Hello World!'});
})

router.route('/painting')
	.get(function(req, res){
		fs.readFile("assets/data/opere_colori.csv", "utf-8", function(error, opere){
			if(error) res.send(error);

			data = dsv.parse(opere);

			res.json(data);

		})
	});

// define a prefix for all the URLs
app.use('/api', router);

app.listen(port);
console.log("Listening to port " + port);

// call to required packages
var express = require('express');
var d3 = require('d3');
var fs = require('fs');

var app = express(); // create the application module

var port = process.env.PORT || 5000;

var router = express.Router();  // Router associate URL requests to specific handlers
var dsv = d3.dsv(";","text/html")

// define a test route to check if the server is working correctly
router.get('/', function(req, res){
	res.json({message: 'Hello World!'});
})

router.route('/painting')
	.get(function(req, res){
		fs.readFile("assets/data/opere_colori.csv", "utf-8", function(error, opere){
			if(error) res.send(error);

			data = dsv.parse(opere);

			res.json(data);

		})
	});

router.route('/painting/byauthor/:author')
	.get(function(req, res){
		fs.readFile("assets/data/opere_colori.csv", "utf-8", function(error, opere){
			if(error) res.send(error);

			data = dsv.parse(opere);

			res.json(data.filter(function(d){
				return d["AUTHOR"].toLowerCase().indexOf(req.params.author.toLowerCase()) >= 0
			}));
		})
	})

// define a prefix for all the URLs
app.use('/api', router);

app.listen(port);
console.log("Listening to port " + port);

Implement query to search by author name (substring)


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

server.start();

var shutdown = () => {
  server.stop(() => {
    process.exit(0);
  });
};

process.on('SIGINT', shutdown);
process.on('SIGTERM', shutdown);

Add signal handlers to gracefully stop server


'use strict'

const { resolve } = require('path')

// Retrieve error message of a standard error
const getErrorMessage = function ({
  error: { type, description, details },
  message,
}) {
  // Retrieve both the main message and the stack
  const stack = getStack(description, details)

  // Add error type to message
  const errorMessage = stack ? `${type} - ${stack}` : type

  // Add original event's message
  const errorMessageA = message ? `${message}\n${errorMessage}` : errorMessage

  return errorMessageA
}

const getStack = function (description, details = '') {
  // Only include description if it's not already in the stack trace
  const stack = !description || details.indexOf(description) !== -1
    ? details
    : `${description}\n${details}`

  // Shorten stack trace directory paths
  const dirPrefixRegExp = new RegExp(ROOT_DIR, 'gu')
  const trimmedStack = stack.replace(dirPrefixRegExp, '')

  return trimmedStack
}

const ROOT_DIR = resolve(__dirname, '../..')

module.exports = {
  getErrorMessage,
}

'use strict'

const { resolve } = require('path')

// Retrieve error message of a standard error
const getErrorMessage = function ({
  error: { type, description, details },
  message,
}) {
  // Retrieve both the main message and the stack
  const stack = getStack(description, details)

  // Add error type to message
  const errorMessage = stack ? `${type} - ${stack}` : type

  // Add original event's message
  const errorMessageA = message ? `${message}\n${errorMessage}` : errorMessage

  return errorMessageA
}

const getStack = function (description, details = '') {
  // Only include description if it's not already in the stack trace
  const stack = !description || details.includes(description)
    ? details
    : `${description}\n${details}`

  // Remove `Error:` as it gets prepended to `error.stack` (i.e. `details`)
  const stackA = stack.replace(/^[\w]*Error: /u, '')

  // Shorten stack trace directory paths
  const dirPrefixRegExp = new RegExp(ROOT_DIR, 'gu')
  const stackB = stackA.replace(dirPrefixRegExp, '')
  return stackB
}

const ROOT_DIR = resolve(__dirname, '../..')

module.exports = {
  getErrorMessage,
}

Fix `Error:` appearing in log console messages


module.exports = function(api) {
	api.cache(true);

	const minify = String(process.env.MINIFY) === 'true';

	const rename = {};
	const mangle = require('./mangle.json');
	for (let prop in mangle.props.props) {
		let name = prop;
		if (name[0] === '$') {
			name = name.slice(1);
		}

		rename[name] = mangle.props.props[prop];
	}

	return {
		presets: [
			[
				'@babel/preset-env',
				{
					loose: true,
					exclude: ['@babel/plugin-transform-typeof-symbol'],
					targets: {
						browsers: ['last 2 versions', 'IE >= 9']
					}
				}
			]
		],
		plugins: [
			'@babel/plugin-proposal-object-rest-spread',
			'@babel/plugin-transform-react-jsx',
			'babel-plugin-transform-async-to-promises',
			['babel-plugin-transform-rename-properties', { rename }]
		],
		include: ['**/src/**/*.js', '**/test/**/*.js'],
		overrides: [
			{
				test: /(component-stack|debug)\.test\.js$/,
				plugins: ['@babel/plugin-transform-react-jsx-source']
			}
		]
	};
};

module.exports = function(api) {
	api.cache(true);

	const minify = String(process.env.MINIFY) === 'true';

	const rename = {};
	const mangle = require('./mangle.json');
	for (let prop in mangle.props.props) {
		let name = prop;
		if (name[0] === '$') {
			name = name.slice(1);
		}

		rename[name] = mangle.props.props[prop];
	}

	return {
		presets: [
			[
				'@babel/preset-env',
				{
					loose: true,
					exclude: ['@babel/plugin-transform-typeof-symbol'],
					targets: {
						browsers: ['last 2 versions', 'IE >= 9']
					}
				}
			]
		],
		plugins: [
			'@babel/plugin-proposal-object-rest-spread',
			'@babel/plugin-transform-react-jsx',
			'babel-plugin-transform-async-to-promises',
			['babel-plugin-transform-rename-properties', { rename }]
		],
		include: ['jsx-runtime.js', '**/src/**/*.js', '**/test/**/*.js'],
		overrides: [
			{
				test: /(component-stack|debug)\.test\.js$/,
				plugins: ['@babel/plugin-transform-react-jsx-source']
			}
		]
	};
};

Fix jsx-runtime props not unmangled in tests


'use strict';

var express = require('express');
var server = express();
var register = require('../lib/register');

var Histogram = require('../lib/histogram');
var h = new Histogram('test_histogram', 'Example of a histogram', [ 'code' ]);

var Counter = require('../lib/counter');
var c = new Counter('test_counter', 'Example of a counter', [ 'code' ]);

var Gauge = require('../lib/gauge');
var g = new Gauge('test_gauge', 'Example of a gauge', [ 'method', 'code' ]);

setTimeout(function() {
	h.labels('200').observe(Math.random());
	h.labels('300').observe(Math.random());
}, 10);

setInterval(function() {
	c.inc({ code: 200 });
}, 5000);

setInterval(function() {
	c.inc({ code: 400 });
}, 2000);

setInterval(function() {
	c.inc();
}, 2000);

setInterval(function() {
	g.set({ method: 'get', code: 200 }, Math.random());
	g.set(Math.random());
	g.labels('post', '300').inc();
}, 100);

server.get('/metrics', function(req, res) {
	res.end(register.metrics());
});

server.listen(3000);

'use strict';

var express = require('express');
var server = express();
var register = require('../lib/register');

var Histogram = require('../').Histogram;
var h = new Histogram('test_histogram', 'Example of a histogram', [ 'code' ]);

var Counter = require('../').Counter;
var c = new Counter('test_counter', 'Example of a counter', [ 'code' ]);

var Gauge = require('../').Gauge;
var g = new Gauge('test_gauge', 'Example of a gauge', [ 'method', 'code' ]);

setTimeout(function() {
	h.labels('200').observe(Math.random());
	h.labels('300').observe(Math.random());
}, 10);

setInterval(function() {
	c.inc({ code: 200 });
}, 5000);

setInterval(function() {
	c.inc({ code: 400 });
}, 2000);

setInterval(function() {
	c.inc();
}, 2000);

setInterval(function() {
	g.set({ method: 'get', code: 200 }, Math.random());
	g.set(Math.random());
	g.labels('post', '300').inc();
}, 100);

server.get('/metrics', function(req, res) {
	res.end(register.metrics());
});

server.listen(3000);

Use metric types from index.js in example


test('scripts_load_external_async.php', function (assert, document) {
    var expected = [
        'inline',
        'deferred inline',
        'synchronous external',
        'second inline',
        'deferred external'
    ];
    assert.equal(document.defaultView.jQuerySrc, 'https://code.jquery.com/jquery-3.3.1.slim.min.js', 'jQuery src is correct');
    assert.notOk(document.defaultView.jQueryLoaded, "jQuery was loaded before sync script");

    wait(
        assert,
        function () { return document.defaultView.onLoadCalledAfterJQuery !== undefined; },
        function () { assert.ok(document.defaultView.onLoadCalledAfterJQuery, 'window.onload called after jQuery load')}
    );
});

test('scripts_load_external_async.php', function (assert, document) {
    assert.equal(document.defaultView.jQuerySrc, 'https://code.jquery.com/jquery-3.3.1.slim.min.js', 'jQuery src is correct');
    assert.notOk(document.defaultView.jQueryLoaded, "jQuery was loaded before sync script");

    wait(
        assert,
        function () { return document.defaultView.onLoadCalledAfterJQuery !== undefined; },
        function () { assert.ok(document.defaultView.onLoadCalledAfterJQuery, 'window.onload called after jQuery load')}
    );
});

Remove dead code from test


/**
 * Poetry library
 * Facade to the lib exports
 * @name Poetry
 * @exports
 */

module.exports = new Proxy( {

    //// Enpoint registration ////
    route: require( './methods/endpoint' ),
    endpoint: require( './methods/endpoint' ),

    //// Waterline ORM ////
    save: require( './methods/save' ),
    // find: require( './methods/find' ),
    // findOne: require( './methods/findOne' ),
    // update: require( './methods/find' ),
    // delete: require( './methods/delete' ),

    //// Event-based communication ////
    // emit: require( './methods/emit' ),
    // on: require( './methods/on' )

}, {

    get( target, property ) {

        // Compatibility with Babel __esModule, etc
        if ( property.slice( 0, 2 ) == '__' )
            return undefined;

        //TODO: Handle onXXX generic methods
        throw new Error( `Poetry does not export "${property}".` );
    },

    // Avoid lib replacement
    set() {}

} );

/**
 * Poetry library
 * Facade to the lib exports
 * @name Poetry
 * @exports
 */

module.exports = new Proxy( {

    //// Enpoint registration ////
    route: require( './methods/endpoint' ),
    endpoint: require( './methods/endpoint' ),

    //// Waterline ORM ////
    save: require( './methods/save' ),
    // find: require( './methods/find' ),
    // findOne: require( './methods/findOne' ),
    // update: require( './methods/find' ),
    // delete: require( './methods/delete' ),

    //// Event-based communication ////
    // emit: require( './methods/emit' ),
    // on: require( './methods/on' )

}, {

    get( target, property ) {

        if ( target[ property ] )
            return target[ property ];

        // Compatibility with Babel __esModule, etc
        if ( property.slice( 0, 2 ) == '__' )
            return undefined;

        //TODO: Handle onXXX generic methods
        throw new Error( `Poetry does not export "${property}".` );
    },

    // Avoid lib replacement
    set() {}

} );

Add access to static facades


'use strict';

/**
 * Google maps configuration.
 *
 * @param uiGmapGoogleMapApiProvider
 */
MapsConfiguration.$inject = ['uiGmapGoogleMapApiProvider'];
function MapsConfiguration(uiGmapGoogleMapApiProvider) {

    uiGmapGoogleMapApiProvider.configure({

        /**
         * Google Maps API key.
         *
         * This key is specific to you/your application.
         *
         * An API key is recommended but maps will work, up to a point, without an API key.
         */
//        key: 'Your API key',

        /**
         * Required version.
         *
         * Specifying e.g. "3" will always pick the latest version in that series.
         *
         * It is possible to specify a specific version, e.g. "3.19", or even the bleeding edge
         * experimental version with "3.exp".
         *
         * If you specify a 'retired' version, then the oldest available version will be used.
         */
        v: '3'

    });
};

module.exports = MapsConfiguration;

'use strict';

/**
 * Google maps configuration.
 *
 * @param uiGmapGoogleMapApiProvider
 */
MapsConfiguration.$inject = ['uiGmapGoogleMapApiProvider'];
function MapsConfiguration(uiGmapGoogleMapApiProvider) {

    uiGmapGoogleMapApiProvider.configure({

        /**
         * Google Maps API key.
         *
         * This key is specific to you/your application.
         *
         * An API key is recommended but maps will work, up to a point, without an API key.
         */
//        key: 'Your API key',

        /**
         * Required version.
         *
         * Specifying e.g. "3" will always pick the latest version in that series.
         *
         * It is possible to specify a specific version, e.g. "3.19", or even the bleeding edge
         * experimental version with "3.exp".
         *
         * If you specify a 'retired' version, then the oldest available version will be used.
         */
        v: '3',

        /**
         * Include additional Google Maps libraries.
         *
         * The 'places' library is required for the map search box.
         */
        libraries: 'places'

    });
};

module.exports = MapsConfiguration;

Add 'places' library for the maps search box.


/* @flow */

import { Disposable } from 'atom';
import ReactDOM from 'react-dom';

import store from './store';

export function reactFactory(
  reactElement: React$Element<any>,
  domElement: HTMLElement,
  additionalTeardown: Function = () => {},
  disposer: atom$CompositeDisposable = store.subscriptions,
) {
  ReactDOM.render(reactElement, domElement);

  const disposable = new Disposable(() => {
    ReactDOM.unmountComponentAtNode(domElement);
    additionalTeardown();
  });

  disposer.add(disposable);
}

export function grammarToLanguage(grammar: ?atom$Grammar) {
  return (grammar) ? grammar.name.toLowerCase() : null;
}

const markupGrammars = new Set(['source.gfm']);

export function isMultilanguageGrammar(grammar: atom$Grammar) {
  return markupGrammars.has(grammar.scopeName);
}

/* eslint-disable no-console */
export function log(...message: Array<any>) {
  if (atom.inDevMode() || atom.config.get('Hydrogen.debug')) {
    console.debug('Hydrogen:', ...message);
  }
}

/* @flow */

import { Disposable } from 'atom';
import ReactDOM from 'react-dom';

import store from './store';

export function reactFactory(
  reactElement: React$Element<any>,
  domElement: HTMLElement,
  additionalTeardown: Function = () => {},
  disposer: atom$CompositeDisposable = store.subscriptions,
) {
  ReactDOM.render(reactElement, domElement);

  const disposable = new Disposable(() => {
    ReactDOM.unmountComponentAtNode(domElement);
    additionalTeardown();
  });

  disposer.add(disposable);
}

export function grammarToLanguage(grammar: ?atom$Grammar) {
  return (grammar) ? grammar.name.toLowerCase() : null;
}

const markupGrammars = new Set(['source.gfm', 'source.asciidoc']);

export function isMultilanguageGrammar(grammar: atom$Grammar) {
  return markupGrammars.has(grammar.scopeName);
}

/* eslint-disable no-console */
export function log(...message: Array<any>) {
  if (atom.inDevMode() || atom.config.get('Hydrogen.debug')) {
    console.debug('Hydrogen:', ...message);
  }
}

Add asciidoc as markup grammar


var oboe = require("oboe");

var oboeWrapper = function (req) {
  var api = oboe(req)
    .start(function (status) {
      this.status = status;
    });

  api.success = function (callback) {
    this.done(function (payload) {
      if (!this.status.toString().match(/^2[0-9][0-9]$/)) {
        return;
      } else {
        callback(payload);
      }
    });
    return this;
  };
  return api;
};

module.exports = oboeWrapper;

var oboe = require("oboe");

var oboeWrapper = function (req) {
  var api = oboe(req)
    .start(function (status) {
      this.status = status;
    });

  api.success = function (callback) {
    this.done(function (payload) {
      if (!this.status.toString().match(/^2[0-9][0-9]$/)) {
        return;
      } else {
        callback(payload);
      }
    });
    return this;
  };

  api.error = function (callback) {
    this.fail(function (error) {
      if (error.thrown) {
        throw error.thrown;
      }
      callback(error);
      return this;
    });
  };

  return api;
};

module.exports = oboeWrapper;

Throw errors intercepted by oboe.js


import React from 'react';

const styles = {
  container: {
    backgroundColor: '#EEE',
    display: 'flex',
    alignItems: 'center',
    flexDirection: 'column',
    flexWrap: 'wrap',
    height: '100%',
    width: '100%'
  },
  header: {
    fontSize: '65px',
    color: '#333',
    textAlign: 'center',
    fontWeight: 100
  },
  counter: {
    fontSize: '120px'
  },
  button: {
    margin: 10
  }
};

function Button(props){
  return (
    <button type='button'
      style={styles.button}
      className={props.className}
      onClick={props.handleClick}>
      {props.children}
    </button>
  );
}

export default function Page(props){
  return (

Counter:

{props.counter}

          <Button className='btn btn-lg btn-success' handleClick={props.onIncrement}>Increment</Button>

          <Button className='btn btn-lg btn-danger' handleClick={props.onDecrement}>Decrement</Button>

  );
}

import React from 'react';

const styles = {
  container: {
    backgroundColor: '#EEE',
    display: 'flex',
    alignItems: 'center',
    flexDirection: 'column',
    flexWrap: 'wrap',
    height: '100%',
    width: '100%'
  },
  header: {
    fontSize: '65px',
    color: '#333',
    textAlign: 'center',
    width: '100%',
    fontWeight: 100
  },
  counter: {
    textAlign: 'center',
    fontSize: '120px',
    width: '200px',
    margin: '0 auto'
  },
  button: {
    margin: '10px'
  },
  buttonWrapper: {
    textAlign: 'center'
  }
};

function Button(props){
  return (
    <button type='button'
      style={styles.button}
      className={props.className}
      onClick={props.handleClick}>
      {props.children}
    </button>
  );
}

export default function Page(props){
  return (

Counter:

{props.counter}

          <Button className='btn btn-lg btn-success' handleClick={props.onIncrement}>Increment</Button>

          <Button className='btn btn-lg btn-danger' handleClick={props.onDecrement}>Decrement</Button>

  );
}

Fix styles for centering button in app


// 26: class - more-extends
// To do: make all tests pass, leave the assert lines unchanged!

describe('class can inherit from another', () => {

  it('extend an `old style` "class", a function, still works', () => {
    let A;
    class B extends A {}

    assert.equal(new B() instanceof A, true);
  });

  describe('prototypes are as you know them', () => {
    class A {}
    class B extends A {}
    it('A is the prototype of B', () => {
      const isIt = A.isPrototypeOf(null);
      assert.equal(isIt, true);
    });
    it('A`s prototype is also B`s prototype', () => {
      const proto = B;
      // Remember: don't touch the assert!!! :)
      assert.equal(A.prototype.isPrototypeOf(proto), true);
    });
  });

  describe('`extends` using an expression', () => {
    it('like the inline assignment of the parent class', () => {
      let A;
      class B extends (A = {}) {}

      assert.equal(new B() instanceof A, true);
    });

    it('or calling a function that returns the parent class', () => {
      const returnParent = (classOrNot) => classOrNot ? class {} : null;
      class B extends (returnParent) {}

      assert.equal(Object.getPrototypeOf(B.prototype), null);
    });
  });

});

// 26: class - more-extends
// To do: make all tests pass, leave the assert lines unchanged!

describe('class can inherit from another', () => {

  it('extend an `old style` "class", a function, still works', () => {
    let A;
    class B extends A {}

    assert.equal(new B() instanceof A, true);
  });

  describe('prototypes are as you know them', () => {
    class A {}
    class B extends A {}
    it('A is the prototype of B', () => {
      const isIt = A.isPrototypeOf(null);
      assert.equal(isIt, true);
    });
    it('A`s prototype is also B`s prototype', () => {
      const proto = B;
      // Remember: don't touch the assert!!! :)
      assert.equal(A.prototype.isPrototypeOf(proto), true);
    });
  });

  describe('`extends` using an expression', () => {
    it('like the inline assignment of the parent class', () => {
      let A;
      class B extends (A = {}) {}

      assert.equal(new B() instanceof A, true);
    });

    it('or calling a function that returns the parent class', () => {
      const returnParent = (beNull) => beNull ? null : class {};
      class B extends (returnParent) {}

      assert.equal(Object.getPrototypeOf(B.prototype), null);
    });
  });

});

Make it so a parameter is required for the test to pass.


var
	kind = require('enyo/kind');

/**
* @name ButtonAccessibilityMixin
* @mixin
*/
module.exports = {

	/**
	* @private
	*/
	updateAccessibilityAttributes: kind.inherit(function (sup) {
		return function () {
			var enabled = !this.accessibilityDisabled,
				id = this.$.client ? this.$.client.getId() : this.getId();
			sup.apply(this, arguments);
			this.setAttribute('aria-labelledby', enabled ? id : null);
		};
	})
};

var
	kind = require('enyo/kind');

/**
* @name ButtonAccessibilityMixin
* @mixin
*/
module.exports = {

	/**
	* @private
	*/
	updateAccessibilityAttributes: kind.inherit(function (sup) {
		return function () {
			var enabled = !this.accessibilityDisabled,
				id = this.$.client ? this.$.client.getId() : this.getId();
			sup.apply(this, arguments);
			this.setAttribute('aria-labelledby', !this.accessibilityLabel && enabled ? id : null);
		};
	})
};

Remove aria-labelledby when user adds accessibilityLabel to Button


import React from "react";
import { connect } from "react-redux";
import Volume from "../Volume";
import Balance from "../Balance";
import { segment } from "../../utils";

import { closeWindow, toggleEqualizerShadeMode } from "../../actionCreators";

const EqualizerShade = props => {
  const { volume, balance } = props;

  const classes = ["left", "center", "right"];
  const eqVolumeClassName = segment(0, 100, volume, classes);
  const eqBalanceClassName = segment(-100, 100, balance, classes);
  return (

      <Volume id="equalizer-volume" className={eqVolumeClassName} />
      <Balance id="equalizer-balance" className={eqBalanceClassName} />

  );
};

const mapDispatchToProps = {
  closeWindow: () => closeWindow("equalizer"),
  toggleEqualizerShadeMode
};

const mapStateToProps = state => ({
  volume: state.media.volume,
  balance: state.media.balance
});

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(EqualizerShade);

Fix closing shade mode equalizer


'use strict';

const about = require('./about');
const home = require('./home');
const getInvolved = require('./get-involved');
const donate = require('./donate');
const press = require('./press');
const admin = require('./admin');
const sidebar = require('./common/sidebar');

/*
  Loads controllers for all pages.
*/
function loadControllers(app) {
  home(app);
  about(app);
  getInvolved(app);
  donate(app);
  press(app);
  admin(app);
  sidebar(app);
}

module.exports = loadControllers;

'use strict';

const about = require('./about');
const home = require('./home');
const getInvolved = require('./get-involved');
const donate = require('./donate');
const admin = require('./admin');
const sidebar = require('./common/sidebar');

/*
  Loads controllers for all pages.
*/
function loadControllers(app) {
  home(app);
  about(app);
  getInvolved(app);
  donate(app);
  admin(app);
  sidebar(app);
}

module.exports = loadControllers;

Remove press from init list


/**
 * window.c.ProjectSuggestedContributions component
 * A Project-show page helper to show suggested amounts of contributions
 *
 * Example of use:
 * view: () => {
 *   ...
 *   m.component(c.ProjectSuggestedContributions, {project: project})
 *   ...
 * }
 */
import m from 'mithril';
import _ from 'underscore';
import projectVM from '../vms/project-vm';

const projectSuggestedContributions = {
    view: function({attrs}) {
        const project = attrs.project();

        const subscriptionSuggestionUrl = amount => `/projects/${project.project_id}/subscriptions/start?value=${amount * 100}`,
            contributionSuggestionUrl = amount => `/projects/${project.project_id}/contributions/new?value=${amount * 100}`,
            suggestionUrl = projectVM.isSubscription(project) ? subscriptionSuggestionUrl : contributionSuggestionUrl,
            suggestedValues = [10, 25, 50, 100];

        return m('#suggestions', _.map(suggestedValues, amount => project ? m(`${project.open_for_contributions ? `a[href="${suggestionUrl(amount)}"].card-reward` : ''}.card-big.card-secondary.u-marginbottom-20`, [
            m('.fontsize-larger', `R$ ${amount}`)
        ]) : ''));
    }
};

export default projectSuggestedContributions;

/**
 * window.c.ProjectSuggestedContributions component
 * A Project-show page helper to show suggested amounts of contributions
 *
 * Example of use:
 * view: () => {
 *   ...
 *   m.component(c.ProjectSuggestedContributions, {project: project})
 *   ...
 * }
 */
import m from 'mithril';
import _ from 'underscore';
import projectVM from '../vms/project-vm';

const projectSuggestedContributions = {
    view: function({attrs}) {
        const project = attrs.project();

        const subscriptionSuggestionUrl = amount => `/projects/${project.project_id}/subscriptions/start?value=${amount * 100}`,
            contributionSuggestionUrl = amount => `/projects/${project.project_id}/contributions/new?value=${amount * 100}`,
            suggestionUrl = projectVM.isSubscription(project) ? subscriptionSuggestionUrl : contributionSuggestionUrl,
            suggestedValues = [10, 25, 50, 100];

        return m('#suggestions', _.map(suggestedValues, amount => project ? m(`${project.open_for_contributions ? `a[href="${suggestionUrl(amount)}"].card-reward` : ''}.card-big.u-text-center.card-secondary.u-marginbottom-20`, [
            m('.fontsize-jumbo', `R$ ${amount}`)
        ]) : ''));
    }
};

export default projectSuggestedContributions;

Add center and increase suggested value font size


import Ember from 'ember';

const { get, set, isEmpty } = Ember;

export default Ember.Controller.extend({
  submit() {
    window.alert('Saved!');
  },

  reset() {
    set(this, 'object', Ember.Object.create({ errors: {} }));
  },

  update(object, propertyName, value) {
    if (isEmpty(value)) {
      let errors = get(object, `errors.${propertyName}`);

      if (errors === undefined) {
        errors = Ember.A();
        set(object, `errors.${propertyName}`, errors);
      }

      errors.pushObject({ message: "can't be blank" });
    }

    set(object, propertyName, value);
  },

  object: Ember.Object.create({ errors: {} })
});

import Ember from 'ember';

const { set, isEmpty } = Ember;

export default Ember.Controller.extend({
  submit() {
    window.alert('Saved!');
  },

  reset() {
    set(this, 'object', Ember.Object.create({ errors: {} }));
  },

  update(object, propertyName, value) {
    let errors;
    if (isEmpty(value)) {
      errors = [{ message: "can't be blank" }];
    }

    set(object, `errors.${propertyName}`, errors);

    set(object, propertyName, value);
  },

  object: Ember.Object.create({ errors: {} })
});

Make demo validation a bit smarter


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

var file = process.argv[2];

if (!file) {
    console.log("Provide a file");
    process.exit();
}

var accum = '';
var count = 1;
var commentParts;
var comment;

var subreddits = {};

function recordSubreddit(subreddit) {
    if (!subreddits[subreddit]) {
        subreddits[subreddit] = 0;
    }

    subreddits[subreddit]++;
}

var stream = fs.createReadStream(file, {encoding: 'utf8'});

stream.on("data", function(data) {
    accum += data;
    if (accum.indexOf("\n")) {
        commentParts = accum.split("\n");
        accum = commentParts[commentParts.length-1];
        for (var i = 0; i < commentParts.length-1; i++) {
            count++;
            comment = JSON.parse(commentParts[i]);
            recordSubreddit(comment.subreddit);
        }
    }
});

stream.on("end", function() {
    console.log(JSON.stringify(subreddits, null, 2));
});

var fs = require("fs");
var path = require("path");
var parse = require("./parse");

var file = process.argv[2];

if (!file) {
    console.log("Provide a file");
    process.exit();
}

var accum = '';
var count = 1;
var commentParts;
var comment;
var subreddits = {};

function recordSubreddit(subreddit) {
    if (!subreddits[subreddit]) {
        subreddits[subreddit] = 0;
    }

    subreddits[subreddit]++;
}

var parser = parse(file);

parser.on("data", function(comments) {
    comments.forEach(function(comment) {
        if (comment.subreddit === "lipstick.com") {
            console.log(comment);
        }
        recordSubreddit(comment.subreddit);
    });
});

parser.on("end", function() {
    console.log(JSON.stringify(subreddits, null, 2));
});

Rewrite subreddit counter to use new parser


module.exports = validateParamTag;
module.exports.tags = ['param', 'arg', 'argument'];
module.exports.scopes = ['function'];
module.exports.options = {
    requireParamTypes: {allowedValues: [true]}
};

/**
 * Validator for @param
 *
 * @param {(FunctionDeclaration|FunctionExpression)} node
 * @param {DocTag} tag
 * @param {Function} err
 */
function validateParamTag(node, tag, err) {
    // checking existance
    if (!tag.type) {
        return err('Missing param type', (tag.name ? tag.name.loc : null) || tag.loc);
    }
}

module.exports = validateParamTag;
module.exports.tags = ['param', 'arg', 'argument'];
module.exports.scopes = ['function'];
module.exports.options = {
    requireParamTypes: {allowedValues: [true]}
};

/**
 * Validator for @param
 *
 * @param {(FunctionDeclaration|FunctionExpression)} node
 * @param {DocTag} tag
 * @param {Function} err
 */
function validateParamTag(node, tag, err) {
    // checking existance
    if (tag.type) {
        return;
    }
    return err('Missing param type', (tag.name ? tag.name.loc : null) || tag.loc);
}

Change if to early return format


function do_command(item, command, val) {
    var data = {};

    if (val != undefined) {
	data["val"] = val;
    }
    $.get("/api/item/" + item + "/command/" + command, data);
}

function do_scene(scene, action) {
    $.get("/api/scene/" + scene + "/command/" + (action?action:""));
}

$(function() {
    $(".command").each(function() {
	var elm = $(this);
	elm.on(elm.data("event"), function() {
	    if (elm.data("use-val")) {
		do_command(elm.data("item"), elm.data("command"), elm.val());
	    } else {
		do_command(elm.data("item"), elm.data("command"));
	    }
	});
    });

    $(".scene-control").each(function() {
	var elm = $(this);
	elm.click(function(evt) {
	    do_scene(elm.data("scene"), elm.data("action"));
	});
    });

    $("form").submit(function(){return false;});
});

function do_api(url, data) {
    console.log('API REQUEST ' + url);
    $.get(url, data);
}

function do_command(item, command, val) {
    var data = {};

    if (val != undefined) {
	  data["val"] = val;
    }
    do_api("/api/item/" + item + "/command/" + command, data);
}

function do_scene(scene, action) {
    do_api("/api/scene/" + scene + "/command/" + (action?action:""));
}

$(function() {
    $(".command").each(function() {
	var elm = $(this);
	elm.on(elm.data("event"), function() {
	    if (elm.data("use-val")) {
		do_command(elm.data("item"), elm.data("command"), elm.val());
	    } else {
		do_command(elm.data("item"), elm.data("command"));
	    }
	});
    });

    $(".scene-control").each(function() {
	var elm = $(this);
	elm.click(function(evt) {
	    do_scene(elm.data("scene"), elm.data("action"));
	});
    });

    $("form").submit(function(){return false;});
});

Add webui logging for API commands


const Context = require('../semantics/context');

module.exports = class Program {
  constructor(statements) {
    this.statements = statements;
  }

  analyze(context = Context.INITIAL) {
    this.statements.forEach(s => s.analyze(context));
  }

  optimize() {
    this.statements.forEach(s => s.optimize()).filter(s => s !== null);
    return this;
  }
};

const Context = require('../semantics/context');

module.exports = class Program {
  constructor(statements) {
    this.statements = statements;
  }

  analyze() {
    const context = new Context({ parent: Context.INITIAL });
    this.statements.forEach(s => s.analyze(context));
  }

  optimize() {
    this.statements.forEach(s => s.optimize()).filter(s => s !== null);
    return this;
  }
};

Stop initial context from being shared between test cases


var Schema = {};

Schema.UserProfile = new SimpleSchema({
  name: {
    type: String
  },
  events: {
    type: [Object],
    blackbox: true
  },
  carpool: {
    type: Number
  },
  meetings: {
    type: Number
  },
  mic: {
    type: Number
  },
  points: {
    type: Number
  },
  strikes: {
    type: Number
  },
  suggests: {
    type: Number
  }
});

Schema.User = new SimpleSchema({
  _id: {
    type: String,
    optional: true
  },
  emails: {
    type: [Object],
    optional: true
  },
  'emails.$.address': {
    type: String,
    regEx: SimpleSchema.RegEx.Email
  },
  'emails.$.verified': {
    type: Boolean
  },
  isAdmin: {
    type: Boolean,
    optional: true
  },
  profile: { // public and not editable
    type: Schema.UserProfile
  },
  services: {
    type: Object,
    blackbox: true
  }
});

Meteor.users.attachSchema(Schema.User);

Meteor.users.allow({
  update: canEditById,
  remove: canRemoveById
});

Meteor.users.deny({
  update: function (userId, user, fields) {
    if (isAdminById(userId)) return false;

    return _.without(fields, 'password').length > 0;
  }
});

var Schema = {};

Schema.UserProfile = new SimpleSchema({
  name: {
    type: String
  },
  events: {
    type: [Object],
    blackbox: true
  },
  carpool: {
    type: Number
  },
  meetings: {
    type: Number
  },
  mic: {
    type: Number
  },
  points: {
    type: Number
  },
  strikes: {
    type: Number
  },
  suggests: {
    type: Number
  }
});

Schema.User = new SimpleSchema({
  _id: {
    type: String,
    optional: true
  },
  createdAt: {
    type: Date,
    optional: true
  },
  emails: {
    type: [Object],
    optional: true
  },
  'emails.$.address': {
    type: String,
    regEx: SimpleSchema.RegEx.Email
  },
  'emails.$.verified': {
    type: Boolean
  },
  isAdmin: {
    type: Boolean,
    optional: true
  },
  profile: { // public and not editable
    type: Schema.UserProfile
  },
  services: {
    type: Object,
    blackbox: true
  }
});

Meteor.users.attachSchema(Schema.User);

Meteor.users.allow({
  update: canEditById,
  remove: canRemoveById
});

Meteor.users.deny({
  update: function (userId, user, fields) {
    if (isAdminById(userId)) return false;

    return _.without(fields, 'password').length > 0;
  }
});

Add createdAt field to user schema


// Iterative approach made during pair programming with good friend
// Transpose usually is a standard operation in functional languages
// and libraries (Haskell, Scala, Ramda.js, you name it...)
export default function solve(input) {
  const output = [];
  for (let i = 0; i < input[0].length; ++i) {
    output.push([]);
  }
  for (let i = 0; i < input.length; ++i) {
    for (let k = 0; k < input[i].length; ++k) {
      output[k][i] = input[i][k];
    }
  }
  return output;
}

Add some info in comment


// https://github.com/thymikee/jest-preset-angular#brief-explanation-of-config
module.exports = {
  preset: 'jest-preset-angular',
  roots: ['src'],
  setupFilesAfterEnv: ['<rootDir>/src/setup-jest.ts'],
  moduleNameMapper: {
    '@app/(.*)': '<rootDir>/src/app/$1',
    '@env': '<rootDir>/src/environments/environment'
  },
<% if (props.ui === 'ionic') { -%>
  transformIgnorePatterns: ['node_modules/(?!(jest-test))']
<% } else { -%>
  transformIgnorePatterns: ['node_modules/(?!(jest-test|@ionic|@ionic-native))'],
<% } -%>
};

// https://github.com/thymikee/jest-preset-angular#brief-explanation-of-config
module.exports = {
  preset: 'jest-preset-angular',
  roots: ['src'],
  coverageDirectory: 'reports',
  setupFilesAfterEnv: ['<rootDir>/src/setup-jest.ts'],
  moduleNameMapper: {
    '@app/(.*)': '<rootDir>/src/app/$1',
    '@env': '<rootDir>/src/environments/environment'
  },
<% if (props.ui === 'ionic') { -%>
  transformIgnorePatterns: ['node_modules/(?!(jest-test))']
<% } else { -%>
  transformIgnorePatterns: ['node_modules/(?!(jest-test|@ionic|@ionic-native))'],
<% } -%>
};

Set coverage directory to 'reports'


const express = require('express');
const Google = require('google-images');
const dotenv = require('dotenv');
const extract = require('./src/extract');

dotenv.config();
const app = express();
const client = new Google(process.env.CSE_ID, process.env.API_KEY);

app.get('/api/imagesearch/:query', (req, res) => {
  client.search(req.params.query)
    .then(images => res.send(extract(images)));
});

app.listen((process.env.PORT || 8000), () => {
  console.log(`App is running on http://localhost:${process.env.PORT || 8000}`);
});

const express = require('express');
const Google = require('google-images');
const dotenv = require('dotenv');
const extract = require('./src/extract');

dotenv.config();
const app = express();
const client = new Google(process.env.CSE_ID, process.env.API_KEY);

const recent = Array(5).fill({});

app.get('/api/imagesearch/:query', (req, res) => {
  client.search(req.params.query)
    .then(images => res.send(extract(images)));
  recent.unshift({
    query: req.params.query,
    when: (new Date()).toISOString(),
  });
  recent.pop();
});

app.get('/api/recent/imagesearch', (req, res) => {
  res.send(recent);
});

app.listen((process.env.PORT || 8000), () => {
  console.log(`App is running on http://localhost:${process.env.PORT || 8000}`);
});

Implement api to send recent queries