'use strict';

angular.module('placesOfInterest').
service('fetchPlaceOfInterestData', function (BING_API_KEY) {
    return function(lat, lng, radius) {
        var url = 'http://spatial.virtualearth.net/REST/v1/data/f22876ec257b474b82fe2ffcb8393150/NavteqNA/NavteqPOIs';
        var params = {
            'spatialFilter': 'nearby(' + lat + ',' + lng + ',' + radius + ')',
            '$top': 50,
            '$format': 'json',
            'key': BING_API_KEY
        };

        // Using jQuery here because couldn't get Angular's $http.jsonp to work with bing's API.
        var deferred = $.Deferred();

        $.ajax({
            url: url,
            jsonp: "jsonp",
            dataType: "jsonp",
            data: params,
        }).done(function (response, textStatus, jqXHR) {
            deferred.resolve(response.d.results);
        });

        return deferred;
    };
});

'use strict';

angular.module('placesOfInterest').
service('fetchPlaceOfInterestData', function (BING_API_KEY) {
    return function(lat, lng, radius) {
        var url = 'https://spatial.virtualearth.net/REST/v1/data/f22876ec257b474b82fe2ffcb8393150/NavteqNA/NavteqPOIs';
        var params = {
            'spatialFilter': 'nearby(' + lat + ',' + lng + ',' + radius + ')',
            '$top': 50,
            '$format': 'json',
            'key': BING_API_KEY
        };

        // Using jQuery here because couldn't get Angular's $http.jsonp to work with bing's API.
        var deferred = $.Deferred();

        $.ajax({
            url: url,
            jsonp: "jsonp",
            dataType: "jsonp",
            data: params,
        }).done(function (response, textStatus, jqXHR) {
            deferred.resolve(response.d.results);
        });

        return deferred;
    };
});

Use https for POI fetching to prevent mixed content blocking.


const path = require('path');
const fs = require('fs-extra');

module.exports = {

    /**
     * Database source directory.
     */
    sourceDir: 'sql-database',

    /**
     * Config file name.
     */
    configFile: 'ssc.json',

    /**
     * Default config file values.
     */
    configDefaults: {
        user: '',
        password: '',
        server: '',
        database: '',
        port: 1433
    },

    /**
     * Get and parse config file.
     *
     * @returns Config object.
     */
    getConfig: function () {
        const file = path.join(process.cwd(), this.configFile);

        try {
            fs.readJsonSync(file);
        } catch (e) {
            console.error(`Couldn't find ${this.configFile} file. You can use the init command to create one!`);
            process.exit();
        }
    }

};

const path = require('path');
const fs = require('fs-extra');

module.exports = {

    /**
     * Database source directory.
     */
    sourceDir: 'sql-database',

    /**
     * Config file name.
     */
    configFile: 'ssc.json',

    /**
     * Default config file values.
     */
    configDefaults: {
        user: '',
        password: '',
        server: '',
        database: '',
        port: 1433
    },

    /**
     * Get and parse config file.
     *
     * @returns Config object.
     */
    getConfig: function () {
        const file = path.join(process.cwd(), this.configFile);
        let config;

        try {
            config = fs.readJsonSync(file);
        } catch (e) {
            console.error(`Couldn't find ${this.configFile} file. You can use the init command to create one!`);
            process.exit();
        }

        // validate required fields
        for (let key of ['user', 'password', 'server', 'database']) {
            if (!config[key]) {
                console.log(`Requied property '${key}' is missing!`);
                process.exit();
            }
        }

        return config;
    }

};

Fix getConfig and add validation


// @flow

import { compose } from 'redux';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { dispatched } from 'react-prepare';
import { fetchAll } from 'app/actions/EventActions';
import EventList from './components/EventList';
import { selectSortedEvents } from 'app/reducers/events';
import moment from 'moment';

const mapStateToProps = (state, ownProps) => {
  const user = ownProps.currentUser;
  const icalToken = user ? user.icalToken : null;
  const actionGrant = state => state.events.actionGrant;
  return {
    ...createStructuredSelector({
      events: selectSortedEvents,
      actionGrant
    })(state, ownProps),
    icalToken
  };
};

export default compose(
  dispatched((props, dispatch) =>
    dispatch(fetchAll({ dateAfter: moment().format('YYYY-MM-DD') }))
  ),
  connect(mapStateToProps)
)(EventList);

// @flow

import { compose } from 'redux';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { dispatched } from 'react-prepare';
import { fetchAll } from 'app/actions/EventActions';
import EventList from './components/EventList';
import { selectSortedEvents } from 'app/reducers/events';
import moment from 'moment';

const mapStateToProps = (state, ownProps) => {
  const user = ownProps.currentUser;
  const icalToken = user ? user.icalToken : null;
  const actionGrant = state => state.events.actionGrant;
  return {
    ...createStructuredSelector({
      events: selectSortedEvents,
      actionGrant
    })(state, ownProps),
    icalToken
  };
};

export default compose(
  dispatched((props, dispatch) =>
    dispatch(fetchAll({ dateAfter: moment().format('YYYY-MM-DD') })), {
      componentWillReceiveProps: false
    }
  ),
  connect(mapStateToProps)
)(EventList);

Disable fetching on new props


import React from 'react';
import PetitionListItem from 'components/PetitionListItem';

const Petitions = ({ total, petitions, currentPage, perPage }) => (
  <ul>
    {petitions.map((petition) => <PetitionListItem {...petition} key={petition.id} />)}
  </ul>
);

export default Petitions;

import React from 'react';
import PetitionListItem from 'components/PetitionListItem';

const Petitions = ({ total, petitions, currentPage, perPage }) => (

Petitionen

      {petitions.map((petition) => <PetitionListItem {...petition} key={petition.id} />)}

);

export default Petitions;

Add headline "Petitionen" to petitions page


var app = angular.module('swot', [
    'ui.bootstrap',
    'ui.utils',
    'ui.sortable',
    'focus',
    'confirmExit',
    'ngDebounce',
    'ngAnimate',
    'xeditable',
    'angularBootstrapNavTree'
]);

app.config(['$httpProvider', function ($httpProvider) {
    // Add support for HTTP PATCH verb for sending partial updates.
    $httpProvider.defaults.headers.patch = {
        'Content-Type': 'application/json;charset=utf-8'
    };
}]);

app.run(function (editableOptions, editableThemes) {
    // Set options for angular-xeditable
    editableOptions.theme = 'bs3';
    editableThemes.bs3.inputClass = 'input-sm';
    editableThemes.bs3.buttonsClass = 'btn-sm';
});

var app = angular.module('swot', [
    'ui.bootstrap',
    'ui.utils',
    'ui.sortable',
    'focus',
    'confirmExit',
    'ngDebounce',
    'ngAnimate',
    'xeditable',
    'angularBootstrapNavTree'
]);

app.config(['$httpProvider', function ($httpProvider) {
    // Add support for HTTP PATCH verb for sending partial updates.
    $httpProvider.defaults.headers.patch = {
        'Content-Type': 'application/json;charset=utf-8'
    };
}]);

app.run(function (editableOptions, editableThemes) {
    // Set options for angular-xeditable
    editableOptions.theme = 'bs3';
    editableOptions.activate = 'select';
    editableThemes.bs3.inputClass = 'input-sm';
    editableThemes.bs3.buttonsClass = 'btn-sm';
});

Select all when editing in angular-xeditable by default


/*
 * Copyright (c) 2014-2021 Bjoern Kimminich.
 * SPDX-License-Identifier: MIT
 */

const chai = require('chai')
const expect = chai.expect
const chaiAsPromised = require('chai-as-promised')
chai.use(chaiAsPromised)

describe('webhook', () => {
  const webhook = require('../../lib/webhook')

  const challenge = {
    key: 'key',
    name: 'name'
  }

  describe('notify', () => {
    it('fails when no webhook URL is provided via environment variable', () => {
      expect(webhook.notify(challenge)).to.eventually.throw('options.uri is a required argument')
    })

    it('fails when supplied webhook is not a valid URL', () => {
      expect(webhook.notify(challenge, 'localhorst')).to.eventually.throw('Invalid URI "localhorst"')
    })

    it('submits POST with payload to existing URL', () => {
      expect(webhook.notify(challenge, 'https://enlm7zwniuyah.x.pipedream.net/')).to.eventually.not.throw()
    })
  })
})

/*
 * Copyright (c) 2014-2021 Bjoern Kimminich.
 * SPDX-License-Identifier: MIT
 */

const chai = require('chai')
const expect = chai.expect
const chaiAsPromised = require('chai-as-promised')
chai.use(chaiAsPromised)

describe('webhook', () => {
  const webhook = require('../../lib/webhook')

  const challenge = {
    key: 'key',
    name: 'name',
    difficulty: 1
  }

  describe('notify', () => {
    it('fails when no webhook URL is provided via environment variable', () => {
      expect(webhook.notify(challenge)).to.eventually.throw('options.uri is a required argument')
    })

    it('fails when supplied webhook is not a valid URL', () => {
      expect(webhook.notify(challenge, 'localhorst')).to.eventually.throw('Invalid URI "localhorst"')
    })

    it('submits POST with payload to existing URL', () => {
      expect(webhook.notify(challenge, 'https://enlm7zwniuyah.x.pipedream.net/')).to.eventually.not.throw()
    })
  })
})

Add difficulty to stub challenge to allow cheat calculation to happen


import $ from 'jquery';
import * as Backbone from 'backbone';
import dispatcher from '../helpers/dispatcher.js';

class SearchBoxView extends Backbone.View {

  get el() { return '.search-teaser'; }

  get events() {
    return {
      'submit form': 'getQuery'
    }
  }

  initialize() {
    window.console.log('SearchBoxView initialized!');
  }

  getQuery() {
    let query = $('.search-teaser input').val();
    if( query !== '' ) {
      dispatcher.trigger('router:go', 'search/?q=' + query);
    }
  }

}

export default SearchBoxView;

import $ from 'jquery';
import * as Backbone from 'backbone';
import dispatcher from '../helpers/dispatcher.js';

class SearchBoxView extends Backbone.View {

  get el() { return '#search-box'; }

  get events() {
    return {
      'submit form': 'getQuery'
    }
  }

  initialize() {
    window.console.log('SearchBoxView initialized!');
  }

  getQuery() {
    let query = $('#search-box input').val();
    if( query !== '' ) {
      dispatcher.trigger('router:go', 'search/?q=' + query);
    }
  }

}

export default SearchBoxView;

Update searchBox view to match new WP templates


import React from 'react';
import { dispatch, composeStores } from 'redux';
import CounterApp from './CounterApp';
import TodoApp from './TodoApp';
import * as stores from '../stores/index';

@dispatch(composeStores(stores))
export default class App {
  render() {
    return (
      <div>
        <CounterApp />
        <TodoApp />
      </div>
    );
  }
}

import React from 'react';
import { Dispatcher, composeStores } from 'redux';
import CounterApp from './CounterApp';
import TodoApp from './TodoApp';
import * as stores from '../stores/index';

export default class App {
  render() {
    return (
      <Dispatcher store={composeStores(stores)}>
        {() =>
          <div>
            <CounterApp />
            <TodoApp />
          </div>
        }
      </Dispatcher>
    );
  }
}

Use component in example for consistency


import * as Id from './id';
import * as Fields from './fields';

class Bookmark {
  constructor(id, fields) {
    this.id = id;
    this.fields = fields;
    return Object.freeze(this);
  }
}

function mkBookmark(id, fields) {
  if (!Id.assertType(id)) {
    return new TypeError('Expected:Id');
  }

  if (!Fields.assertType(fields)) {
    return new TypeError('Expected:Fields');
  }

  return new Bookmark(id, fields);
}

export var fromJSON = obj =>
  mkBookmark(
    Id.fromString(obj.id),
    Fields.fromJSON(obj));

export var fromObject = obj =>
  mkBookmark(obj.id, obj.fields);

export function assertType(obj) {
  return obj instanceof Bookmark;
}

import * as Id from './id';
import * as Fields from './fields';

class Bookmark {
  constructor(id, fields) {
    if (!Id.assertType(id)) {
      return new TypeError('Expected:Id');
    }

    if (!Fields.assertType(fields)) {
      return new TypeError('Expected:Fields');
    }

    this.id = id;
    this.fields = fields;
    return Object.freeze(this);
  }
}

export function fromJSON(obj) {
  return new Bookmark(
    Id.fromString(obj.id),
    Fields.fromJSON(obj));
}

export function fromObject(obj) {
  return new Bookmark(obj.id, obj.fields);
}

export function assertType(obj) {
  return obj instanceof Bookmark;
}

Move validation checks into the constructor of Bookmark Entity


/**
 * Copyright (c) Nicolas Gallagher.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow
 */

import { oneOf, string } from 'prop-types';

const InteractionPropTypes = {
  cursor: string,
  touchAction: oneOf([
    'auto',
    'inherit',
    'manipulation',
    'none',
    'pan-down',
    'pan-left',
    'pan-right',
    'pan-up',
    'pan-x',
    'pan-y',
    'pinch-zoom'
  ]),
  userSelect: string,
  willChange: string
};

export default InteractionPropTypes;

/**
 * Copyright (c) Nicolas Gallagher.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow
 */

import { oneOf, oneOfType, string } from 'prop-types';

const cursorEnum = [
  'auto',
  'default',
  'none',
  'context-menu',
  'help',
  'pointer',
  'progress',
  'wait',
  'cell',
  'crosshair',
  'text',
  'vertical-text',
  'alias',
  'copy',
  'move',
  'no-drop',
  'not-allowed',
  'e-resize',
  'n-resize',
  'ne-resize',
  'nw-resize',
  's-resize',
  'se-resize',
  'sw-resize',
  'w-resize',
  'ew-resize',
  'ns-resize',
  'nesw-resize',
  'nwse-resize',
  'col-resize',
  'row-resize',
  'all-scroll',
  'zoom-in',
  'zoom-out',
  'grab',
  'grabbing '
];
const touchActionEnum = [
  'auto',
  'inherit',
  'manipulation',
  'none',
  'pan-down',
  'pan-left',
  'pan-right',
  'pan-up',
  'pan-x',
  'pan-y',
  'pinch-zoom'
];
const userSelectEnum = ['auto', 'text', 'none', 'contain', 'all'];

const InteractionPropTypes = {
  // https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#Formal_syntax
  cursor: oneOfType([string, oneOf(cursorEnum)]),
  // https://developer.mozilla.org/en-US/docs/Web/CSS/touch-action#Formal_syntax
  touchAction: oneOf(touchActionEnum),
  // https://developer.mozilla.org/en-US/docs/Web/CSS/user-select#Formal_syntax_2
  userSelect: oneOf(userSelectEnum),
  willChange: string
};

export default InteractionPropTypes;

Add enum types to interaction prop types


require(['jquery', 'wdn'], function($, WDN) {
	var $progress = $('<progress>');

	$(function() {
		WDN.log('Something should happen')
	});
});

require(['jquery', 'wdn'], function($, WDN) {
	var $progress = $('<progress>'),
		months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];

	$(function() {
		var $monthWidget = $('.wp-calendar')
		if ($monthWidget.length) {
			var now = new Date();
				month = $.trim($('.monthvalue', $monthWidget).text()),
				year = $.trim($('.yearvalue', $monthWidget).text());

			if (year == now.getFullYear() && month == months[now.getMonth()]) {
				$('td', $monthWidget).not('.prev,.next').each(function() {
					if ($.trim($(this).text()) == now.getDate()) {
						$(this).addClass('today');
						return false;
					}
				});
			}
		}
	});
});

Add script to add "today" class to monthwidget


/**
 * PlayersController
 *
 * @description :: Server-side logic for managing Players
 * @help        :: See http://sailsjs.org/#!/documentation/concepts/Controllers
 */

module.exports = {
  view: function(req, res) {
    Players.findOne(req.params.id)
      .populate('team')
      .exec(function(err, playerRecord) {
        if (typeof playerRecord === 'undefined' || playerRecord === null) {
          res.notFound();
        } else {
          Statistics.findOne({player: playerRecord.id, week: null, season: 1, team: null}).exec(function (err, statsRecord) {
            res.view('players/view.jade', {player: playerRecord, stats: statsRecord});
          });
        }
      });
  }
};

/**
 * PlayersController
 *
 * @description :: Server-side logic for managing Players
 * @help        :: See http://sailsjs.org/#!/documentation/concepts/Controllers
 */

 var Promise = require('bluebird');

module.exports = {
  view: function(req, res) {
    var playerPromise = Players.findOne(req.params.id).populate('team');

    var statsPromise = playerPromise.then(function(playerRecord) {
      if (typeof playerRecord === 'undefined' || playerRecord === null) {
        res.notFound();
      } else {
        return Statistics.findOne({player: playerRecord.id, week: null, season: 1, team: null});
      }
    })
    .catch(function(error) {
      sails.log(error);
      res.serverError();
    });

    Promise.all([playerPromise, statsPromise]).spread(function(playerRecord, statsRecord) {
      res.view('players/view.jade', {player: playerRecord, stats: statsRecord});
    })
    .catch(function(error) {
      sails.log(error);
      res.serverError();
    });
  }
};

Refactor controller to use promises


// Namespace
var mejs = mejs || {};

// version number
mejs.version = '2.10.3';

// player number (for missing, same id attr)
mejs.meIndex = 0;

// media types accepted by plugins
mejs.plugins = {
	silverlight: [
		{version: [3,0], types: ['video/mp4','video/m4v','video/mov','video/wmv','audio/wma','audio/m4a','audio/mp3','audio/wav','audio/mpeg']}
	],
	flash: [
		{version: [9,0,124], types: ['video/mp4','video/m4v','video/mov','video/flv','video/rtmp','video/x-flv','audio/flv','audio/x-flv','audio/mp3','audio/m4a','audio/mpeg', 'video/youtube', 'video/x-youtube']}
		//,{version: [12,0], types: ['video/webm']} // for future reference (hopefully!)
	],
	youtube: [
		{version: null, types: ['video/youtube', 'video/x-youtube']}
	],
	vimeo: [
		{version: null, types: ['video/vimeo', 'video/x-vimeo']}
	]
};

// Namespace
var mejs = mejs || {};

// version number
mejs.version = '2.10.3';

// player number (for missing, same id attr)
mejs.meIndex = 0;

// media types accepted by plugins
mejs.plugins = {
	silverlight: [
		{version: [3,0], types: ['video/mp4','video/m4v','video/mov','video/wmv','audio/wma','audio/m4a','audio/mp3','audio/wav','audio/mpeg']}
	],
	flash: [
		{version: [9,0,124], types: ['video/mp4','video/m4v','video/mov','video/flv','video/rtmp','video/x-flv','audio/flv','audio/x-flv','audio/mp3','audio/m4a','audio/mpeg', 'video/youtube', 'video/x-youtube']}
		//,{version: [12,0], types: ['video/webm']} // for future reference (hopefully!)
	],
	youtube: [
		{version: null, types: ['video/youtube', 'video/x-youtube', 'audio/youtube', 'audio/x-youtube']}
	],
	vimeo: [
		{version: null, types: ['video/vimeo', 'video/x-vimeo']}
	]
};

Make possible to open youtube links as audio only


// ==UserScript==
// @name           Amazon age-verification autoredirect
// @namespace      curipha
// @description    Click the "I'm over 18" link automatically at amazon.co.jp.
// @include        http://www.amazon.co.jp/*
// @include        https://www.amazon.co.jp/*
// @version        0.1.2
// @grant          none
// ==/UserScript==

(function() {
  var pattern = /black\-curtain\-redirect\.html/;
  var t = 0;

  var clickit = function(elem) {
    var hrefs = elem.getElementsByTagName('a');

    for (var a of hrefs) {
      if (pattern.test(a.href)) {
        location.href = a.href;
        return;
      }
    }
  };
  var clickit_wrap = function(mr) {
    if (t) return;
    ｔ = setTimeout(function() {
      for (var mre of mr) {
        if (mre.target.id !== 'nav_flyout_anchor') continue;

        clickit(mre.target);
      }
      t = 0;
    }, 120);
  };

  clickit(document.body);

  var mo = new MutationObserver(clickit_wrap);
  mo.observe(document.getElementById('nav-cross-shop'), { childList: true, subtree: true });
})();

// ==UserScript==
// @name           Amazon age-verification autoredirect
// @namespace      curipha
// @description    Click the "I'm over 18" link automatically at amazon.co.jp.
// @include        http://www.amazon.co.jp/*
// @include        https://www.amazon.co.jp/*
// @version        0.2.0
// @grant          none
// ==/UserScript==

(function() {
  // Adult
  var pattern = /black\-curtain\-redirect\.html/;
  var t = 0;

  var clickit = function(elem) {
    var hrefs = elem.getElementsByTagName('a');

    for (var a of hrefs) {
      if (pattern.test(a.href)) {
        location.href = a.href;
        return;
      }
    }
  };
  var clickit_wrap = function(mr) {
    if (t) return;
    ｔ = setTimeout(function() {
      for (var mre of mr) {
        if (mre.target.id !== 'nav_flyout_anchor') continue;

        clickit(mre.target);
      }
      t = 0;
    }, 120);
  };

  clickit(document.body);

  var mo = new MutationObserver(clickit_wrap);
  mo.observe(document.getElementById('nav-cross-shop'), { childList: true, subtree: true });

  // Alcohol
  var clickit_a = function(elem) {
    var input = elem.querySelector('.dobBody form input[type="image"]');

    if (input) input.click();
  };

  clickit_a(document.body);
})();

Support age verification for alcohol


'use strict';

export Taxonomy from './processor.taxonomy';
export Compromise from './processor.compromise';
export Relations from './processor.relations';
export Sentiment from './processor.sentiment';

'use strict';

import Alchemy from './processor.alchemy';
import Compromise from './processor.compromise';
import Relations from './processor.relations';
import Salient from './processor.salient';

export { Alchemy, Compromise, Relations, Salient };

Fix how we export processors


/*jslint indent: 2, unparam: true*/
/*global $: false, document: false, togglbutton: false*/
'use strict';

togglbutton.render('body.controller-issues.action-show h2:not(.toggl)', {}, function (elem) {
  var link, description,
    numElem = $('h2'),
    titleElem = $('.subject h3'),
    projectElem = $('h1');

  description = titleElem.innerText;
  if (numElem !== null) {
    description = numElem.innerText + " " + description;
  }

  link = togglbutton.createTimerLink({
    className: 'redmine',
    description: description,
    projectName: projectElem && projectElem.textContent.split(" » ").pop()
  });

  $('h2').appendChild(link);
});

/*jslint indent: 2, unparam: true*/
/*global $: false, document: false, togglbutton: false*/
'use strict';

togglbutton.render('body.controller-issues.action-show h2:not(.toggl)', {}, function (elem) {
  var link, description,
    numElem = $('h2'),
    titleElem = $('.subject h3'),
    projectElem = $('h1');

  description = titleElem.innerText;
  if (numElem !== null) {
    description = numElem.innerText + " " + description;
  }

  link = togglbutton.createTimerLink({
    className: 'redmine',
    description: description,
    projectName: projectElem && projectElem.textContent.split(" » ").pop()
  });

  $('h2').appendChild(link);

  var done = createTag('button', '', 'Done');
  $('h2').appendChild(done);
  done.title = 'Tâche faite, assigne le statut À tester.';
  done.addEventListener('click', function() {
    var uid = $('.author .user').href.replace(/^.+?(\d+)$/, '$1');
    $('.icon.icon-edit').click();
    $('#issue_status_id option[value="6"]').selected = 'selected';// "À tester."
    $('#issue_assigned_to_id option[value="' + uid  +'"]').selected = 'selected';
    $('#issue_done_ratio option[value="100"]').selected = 'selected';
    $('#issue_custom_field_values_23 option[value="Doing"]').selected = 'selected';
    $('#issue_notes').focus();
  });
});

Add a Redmine 'Done' button


var webpack = require('webpack');
var path = require('path');
var loaders = require('./webpack.loaders');

module.exports = {
	entry: [
		'webpack/hot/only-dev-server',
		'./src/index.jsx' // Your appʼs entry point
	],
	devtool: process.env.WEBPACK_DEVTOOL || 'source-map',
	output: {
		path: __dirname + '/build',
		publicPath: '/',
		filename: 'bundle.js'
	},
	resolve: {
		extensions: ['', '.js', '.jsx']
	},
	postcss() {
		return [require('precss'), require('autoprefixer')]
	},
	module: {
		loaders: loaders
	},
	devServer: {
		historyApiFallback: true,
		contentBase: './build',
		hot: true,
		stats: {
				colors: true,
				chunks: false
		},

		proxy: {
			'/api': {
				target: 'http://10.0.0.171:3000',
				pathRewrite: {'^/api' : ''}
			}
		},
	},
	plugins: [
		new webpack.optimize.OccurenceOrderPlugin(),
		new webpack.HotModuleReplacementPlugin(),
		new webpack.NoErrorsPlugin(),
		new webpack.ProvidePlugin({
      'fetch': 'imports?this=>global!exports?global.fetch!whatwg-fetch'
    })
	]
};

var webpack = require('webpack');
var path = require('path');
var loaders = require('./webpack.loaders');

module.exports = {
	entry: [
		'webpack/hot/only-dev-server',
		'./src/index.jsx' // Your appʼs entry point
	],
	devtool: process.env.WEBPACK_DEVTOOL || 'source-map',
	output: {
		path: __dirname + '/build',
		publicPath: '/',
		filename: 'bundle.js'
	},
	resolve: {
		extensions: ['', '.js', '.jsx']
	},
	postcss() {
		return [require('precss'), require('autoprefixer')]
	},
	module: {
		loaders: loaders
	},
	devServer: {
		historyApiFallback: true,
		contentBase: './build',
		hot: true,
		stats: {
				colors: true,
				chunks: false
		},

		proxy: {
			'/api': {
				target: 'http://localhost:3000',
				pathRewrite: {'^/api' : ''}
			}
		},
	},
	plugins: [
		new webpack.optimize.OccurenceOrderPlugin(),
		new webpack.HotModuleReplacementPlugin(),
		new webpack.NoErrorsPlugin(),
		new webpack.ProvidePlugin({
      'fetch': 'imports?this=>global!exports?global.fetch!whatwg-fetch'
    })
	]
};

Set localhost as default proxy


var http = require("http");

function HttpServer(config) {
  this.port = config.port || 8181;
  this.address = config.address || "localhost";
  this.options = {
    data: null,
    resCode: 200
  };
}

HttpServer.prototype.start = function (fn) {
  this.server = http.createServer(function (req, res) {
    res.writeHead(this.options.resCode, {"Content-Type": "application/json"});
    res.end(JSON.stringify(this.options.data));
  }.bind(this)).listen(this.port, this.address, fn);
  return this;
};

HttpServer.prototype.setup = function (data, resCode) {
  this.options.data = data;
  this.options.resCode = resCode || 200;
  return this;
};

HttpServer.prototype.stop = function (fn) {
  this.server.close();
  if (fn) {
    fn();
  }
};

exports.HttpServer = HttpServer;

var http = require("http");

function HttpServer(config) {
  this.port = config.port || 8181;
  this.address = config.address || "localhost";
  this.options = {
    data: null,
    resCode: 200
  };
}

HttpServer.prototype.start = function (fn) {
  this.server = http.createServer(function (req, res) {
    res.writeHead(this.options.resCode, {"Content-Type": "application/json"});
    res.end(JSON.stringify(this.options.data));
  }.bind(this)).listen(this.port, this.address, fn);
  return this;
};

HttpServer.prototype.setup = function (data, resCode) {
  this.options.data = data;
  this.options.resCode = resCode || 200;
  return this;
};

HttpServer.prototype.on = function (event, fn) {
  this.server.on(event, fn);
};

HttpServer.prototype.stop = function (fn) {
  this.server.close();
  if (fn) {
    fn();
  }
};

exports.HttpServer = HttpServer;

Allow adding custom response listeners


/* global describe it */
'use strict'

const chai = require('chai')
const chaiAsPromised = require('chai-as-promised')
const requireInject = require('require-inject')
const sinon = require('sinon')
const sinonChai = require('sinon-chai')
require('sinon-as-promised')

chai.use(chaiAsPromised)
chai.use(sinonChai)

const expect = chai.expect

describe('exec', function () {
  it('rejects if childProcess.exec fails', function () {
    const exec = sinon.stub().yields('some error')
    const stubs = {
      child_process: {
        exec
      }
    }
    const util = requireInject('../lib/util', stubs)
    return expect(util.exec()).to.be.eventually.rejectedWith('some error')
  })
})

/* global describe it */
'use strict'

const chai = require('chai')
const chaiAsPromised = require('chai-as-promised')
const requireInject = require('require-inject')
const sinon = require('sinon')
const sinonChai = require('sinon-chai')
require('sinon-as-promised')

chai.use(chaiAsPromised)
chai.use(sinonChai)

const expect = chai.expect

describe('exec', function () {
  it('rejects if childProcess.exec fails', function () {
    const exec = sinon.stub().yields('some error')
    const stubs = {
      child_process: {
        exec
      }
    }
    const util = requireInject('../lib/util', stubs)
    return expect(util.exec('command'))
      .to.be.eventually.rejectedWith('some error')
  })
  })
})

Add line break to avoid long line


// go.testHelpers.rpc
// ==================

(function(exports) {
  var assertRequest = function(req, url, method, params) {
    assert.equal(req.url, url);
    assert.equal(req.data.method, '/' + method);
    assert.deepEqual(req.data.params, params || []);
  };

  var response = function(id, data) {
    return {id: id, jsonrpc: '2.0', result: data};
  };

  var fakeServer = function(url) {
    var requests = [];

    var stub = sinon.stub($, 'ajax', function(options) {
      options.data = JSON.parse(options.data);
      requests.push(options);
    });

    return {
      requests: requests,

      assertRequest: function(method, params) {
        assertRequest(requests.shift(), url, method, params);
      },

      restore: function() {
        stub.restore();
      },

      respondWith: function(data) {
        var req = requests.shift();

        // deep copy the data to ensure it can't be modified (which may cause
        // obscure test passes/failures)
        data = JSON.parse(JSON.stringify(data));
        req.success(response(req.data.id, data));
      }
    };
  };

  _.extend(exports, {
    assertRequest: assertRequest,
    response: response,
    fakeServer: fakeServer
  });
})(go.testHelpers.rpc = {});

// go.testHelpers.rpc
// ==================

(function(exports) {
  var assertRequest = function(req, url, method, params) {
    assert.equal(req.url, url);
    assert.equal(req.data.method, '/' + method);
    assert.deepEqual(req.data.params, params || []);
  };

  var response = function(id, data) {
    return {id: id, jsonrpc: '2.0', result: data};
  };

  var fakeServer = function(url) {
    var requests = [];

    var stub = sinon.stub($, 'ajax', function(options) {
      options.data = JSON.parse(options.data);
      requests.push(options);
    });

    return {
      requests: requests,

      assertRequest: function(method, params) {
        assertRequest(requests.shift(), url, method, params);
      },

      restore: function() {
        stub.restore();
      },

      respondWith: function(data) {
        var req = requests.shift();

        // deep copy the data to ensure it can't be modified (which may cause
        // obscure test passes/failures)
        data = JSON.parse(JSON.stringify(data));
        req.success(response(req.data.id, data));
      },

      errorWith: function(status, statusText, errorThrown) {
        var req = requests.shift(),
            jqXHR = {status: status, statusText: statusText};

        req.error(req, jqXHR, statusText, errorThrown);
      }
    };
  };

  _.extend(exports, {
    assertRequest: assertRequest,
    response: response,
    fakeServer: fakeServer
  });
})(go.testHelpers.rpc = {});

Add error responses to fakeServer


(function(){

	'use strict';

	/**
	 * Texture loader based on {@link https://github.com/mrdoob/three.js/blob/master/src/loaders/TextureLoader.js}
	 * @memberOf PANOLENS.Utils
	 * @namespace
	 */
	PANOLENS.Utils.TextureLoader = {};

	/**
	 * Load image texture
	 * @param  {string}   url        - An image url
	 * @param  {function} onLoad     - On load callback
	 * @param  {function} onProgress - In progress callback
	 * @param  {function} onError    - On error callback
	 * @return {THREE.Texture}   	 - Image texture
	 */
	PANOLENS.Utils.TextureLoader.load = function ( url, onLoad, onProgress, onError ) {

		var texture = new THREE.Texture();

		PANOLENS.Utils.ImageLoader.load( url, function ( image ) {

			texture.image = image;
			texture.needsUpdate = true;

			onLoad && onLoad( texture );

		}, onProgress, onError );

		return texture;

	};

})();

(function(){

	'use strict';

	/**
	 * Texture loader based on {@link https://github.com/mrdoob/three.js/blob/master/src/loaders/TextureLoader.js}
	 * @memberOf PANOLENS.Utils
	 * @namespace
	 */
	PANOLENS.Utils.TextureLoader = {};

	/**
	 * Load image texture
	 * @param  {string}   url        - An image url
	 * @param  {function} onLoad     - On load callback
	 * @param  {function} onProgress - In progress callback
	 * @param  {function} onError    - On error callback
	 * @return {THREE.Texture}   	 - Image texture
	 */
	PANOLENS.Utils.TextureLoader.load = function ( url, onLoad, onProgress, onError ) {

		var texture = new THREE.Texture();

		PANOLENS.Utils.ImageLoader.load( url, function ( image ) {

			texture.image = image;

			// JPEGs can't have an alpha channel, so memory can be saved by storing them as RGB.
			var isJPEG = url.search( /\.(jpg|jpeg)$/ ) > 0 || url.search( /^data\:image\/jpeg/ ) === 0;

			texture.format = isJPEG ? THREE.RGBFormat : THREE.RGBAFormat;
			texture.needsUpdate = true;

			onLoad && onLoad( texture );

		}, onProgress, onError );

		return texture;

	};

})();

Add texture format to texture loader


var request = require('xhr');
var url = require('./url');

var CORS_PROXIES = ['http://cors.corsproxy.io?url=', 'http://www.corsproxy.com/'];

function getUrl ( text ) {
	var proxy = CORS_PROXIES[Math.floor(Math.random()*CORS_PROXIES.length)] + url(text);
	if ( /corsproxy\.com/.test(proxy) ) {
		proxy = proxy.replace(/http:\/\/(?=hacheck)/,'');
	}
	return proxy;
}

module.exports = function ( text, cb ) {

	request({
		url: getUrl(text),
		method: 'get',
		timeout: 10000,
		useXDR: true,
		response: true
	}, function ( err, resp, body ) {
		cb(body);
	});

};

var request = require('xhr');
var url = require('./url');

var SECURE_CORS_PROXIES = ['//www.corsproxy.com/'];
var CORS_PROXIES        = ['//cors.corsproxy.io?url='].concat(SECURE_CORS_PROXIES);
var proxies             = /^https:\/\//.test(location.href) ? SECURE_CORS_PROXIES : CORS_PROXIES;

function getUrl ( text ) {

	var proxy = proxies[Math.floor(Math.random()*proxies.length)] + url(text);

	if ( /corsproxy\.com/.test(proxy) ) {
		proxy = proxy.replace(/http:\/\/(?=hacheck)/,'');
	}
	return proxy;
}

module.exports = function ( text, cb ) {

	request({
		url: getUrl(text),
		method: 'get',
		timeout: 10000,
		useXDR: true,
		response: true
	}, function ( err, resp, body ) {
		cb(body);
	});

};

Check if HTTPS is active and use proper proxies


angular.module('mewpipe')
       .factory('tokenInterceptor', tokenInterceptor);

tokenInterceptor.$inject = ['Config'];

function tokenInterceptor(Config){
	return {

		'response' : function(response){

			var r = new RegExp('^' + Config.server.url + ':' + Config.server.port);

			if(!r.test(response.config.url)) return response;

			console.log('Back request');

			return response;
		}

	}
}

angular.module('mewpipe')
       .factory('tokenInterceptor', tokenInterceptor);

tokenInterceptor.$inject = ['satellizer.storage','satellizer.config','Config'];

function tokenInterceptor(satellizerStorage,satellizerConfig,Config){
	return {

		'response' : function(response){

			var r = new RegExp('^' + Config.server.url + ':' + Config.server.port);

			if(!r.test(response.config.url)) return response;

			var tokenKey = satellizerConfig.tokenPrefix ? satellizerConfig.tokenPrefix + '_' + satellizerConfig.tokenName : satellizerConfig.tokenName;
			var newToken = response.headers('authorization');

			if(newToken) satellizerStorage.set(tokenKey,newToken);

			return response;
		}

	}
}

Update token after each request to back


(function() {
  var submitButton = document.querySelector('[data-fx="submit-button"]');
  submitButton.disabled = true;

  var bouncer = new Bouncer('[data-validate]', {
    disableSubmit: true,

    messages: {
      missingValue: {
        text: '💔 Please fill out this required field.',
        textarea: '💔 Please fill out this required field.',
        email: '💔 Please let us know how we can email you.',
      },
    }
  });

  document.addEventListener('bouncerFormValid', function () {
    alert('Form submitted successfully!');
    // window.location.reload();
    submitButton.disabled = false;
  }, false);
})();

(function() {
  var submitButton = document.querySelector('[data-fx="submit-button"]');
  var recaptchaResponse = document.querySelector('g-recaptcha-response');
  submitButton.disabled = 'disabled';

  var bouncer = new Bouncer('[data-validate]', {
    disableSubmit: true,

    messages: {
      missingValue: {
        text: '💔 Please fill out this required field.',
        textarea: '💔 Please fill out this required field.',
        email: '💔 Please let us know how we can email you.',
      },
    }
  });

  var recaptcha_callback = function() {
    if (recaptchaResponse) {
      submitButton.removeAttribute('disabled');
      console.log('recaptcha response');
    }
  };

  recaptcha_callback();

  document.addEventListener('bouncerFormValid', function () {
    // alert('Form submitted successfully!');
    recaptcha_callback();
    // window.location.reload();
  }, false);
})();

Add a callback for recaptcha


import Ember from 'ember';
import layout from '../templates/components/lt-cell';

const {
  get,
  computed
} = Ember;

export default Ember.Component.extend({
  layout,
  tagName: 'td',
  classNames: ['lt-cell'],
  classNameBindings: ['align', 'isSorted'],

  column: null,
  row: null,
  tableActions: null,

  align: computed('column.align', function() {
    return `align-${this.get('column.align')}`;
  }),

  isSorted: computed.oneWay('column.sorted'),

  value: computed('row', 'column', function() {
    let valuePath = this.get('column.valuePath');
    if(valuePath) {
      return get(this.get('row'), valuePath);
    }
  })
});

import Ember from 'ember';
import layout from '../templates/components/lt-cell';

const {
  computed,
  defineProperty
} = Ember;

export default Ember.Component.extend({
  layout,
  tagName: 'td',
  classNames: ['lt-cell'],
  classNameBindings: ['align', 'isSorted'],

  column: null,
  row: null,
  tableActions: null,

  align: computed('column.align', function() {
    return `align-${this.get('column.align')}`;
  }),

  isSorted: computed.oneWay('column.sorted'),

  init() {
    this._super(...arguments);
    let valuePath = this.get('column.valuePath');
    if(valuePath) {
      defineProperty(this, 'value', computed.readOnly(`row.${valuePath}`));
    }
  }
});

Define cell value as a read only cp


(function () {
    'use strict';

    var mds = angular.module('mds', [
        'motech-dashboard', 'entityService','instanceService', 'mdsSettingsService', 'ngCookies', 'ui.directives',
        'ngRoute', 'ui.directives'
    ]);

    $.get('../mds/available/mdsTabs').done(function(data) {
         mds.constant('AVAILABLE_TABS', data);
    });

    mds.run(function ($rootScope, AVAILABLE_TABS) {
        $rootScope.AVAILABLE_TABS = AVAILABLE_TABS;
    });

    mds.config(function ($routeProvider, AVAILABLE_TABS) {
        angular.forEach(AVAILABLE_TABS, function (tab) {
            $routeProvider.when(
                '/{0}'.format(tab),
                {
                    templateUrl: '../mds/resources/partials/{0}.html'.format(tab),
                    controller: '{0}Ctrl'.format(tab.capitalize())
                }
            );
        });

        $routeProvider.otherwise({
            redirectTo: '/{0}'.format(AVAILABLE_TABS[0])
        });
    });
}());

(function () {
    'use strict';

    var mds = angular.module('mds', [
        'motech-dashboard', 'entityService','instanceService', 'mdsSettingsService', 'ngCookies', 'ui.directives',
        'ngRoute', 'ui.directives'
    ]);

    $.ajax({
        url:      '../mds/available/mdsTabs',
        success:  function(data) {
            mds.constant('AVAILABLE_TABS', data);
        },
        async:    false
    });

    mds.run(function ($rootScope, AVAILABLE_TABS) {
        $rootScope.AVAILABLE_TABS = AVAILABLE_TABS;
    });

    mds.config(function ($routeProvider, AVAILABLE_TABS) {
        angular.forEach(AVAILABLE_TABS, function (tab) {
            $routeProvider.when(
                '/{0}'.format(tab),
                {
                    templateUrl: '../mds/resources/partials/{0}.html'.format(tab),
                    controller: '{0}Ctrl'.format(tab.capitalize())
                }
            );
        });

        $routeProvider.otherwise({
            redirectTo: '/{0}'.format(AVAILABLE_TABS[0])
        });
    });
}());

Fix ajax error while checking available tabs


"use strict";

var adp = adp || {};

adp.determination = {
  init : function(container_id) {
    this.addChangeEvent(container_id);
  },
  calculateAmount: function(fee, expenses) {

    var f = fee || 0,
      e = expenses || 0;
    f = f < 0 ? 0 : f;
    e = e < 0 ? 0 : e;
    var t = (f + e).toFixed(2);
    t = t < 0 ? 0 : t;
    return t;

  },
  addChangeEvent: function(container_id) {
    $('#' + container_id).on('change', '#claim_assessment_attributes_fees, #claim_assessment_attributes_expenses', function(e) {
      var fees = parseFloat($('#claim_assessment_attributes_fees').val());
      var expenses = parseFloat($('#claim_assessment_attributes_expenses').val());
      var total = adp.determination.calculateAmount(fees,expenses);
      if (isNaN(total) ){
        $('.total-determination').text('£0.00');
      }
      else{
        $('.total-determination').text('£ '+ total);
      }
    });
  }
};

"use strict";

var adp = adp || {};

adp.determination = {
  init : function(container_id) {
    this.addChangeEvent(container_id);
  },
  calculateAmount: function(fee, expenses) {

    var f = fee || 0,
      e = expenses || 0;
    f = f < 0 ? 0 : f;
    e = e < 0 ? 0 : e;
    var t = (f + e).toFixed(2);
    t = t < 0 ? 0 : t;
    return t;

  },
  addChangeEvent: function(container_id) {
    $('#' + container_id).on('change', '#claim_assessment_attributes_fees, #claim_assessment_attributes_expenses', function(e) {

      var fees = parseFloat($('#claim_assessment_attributes_fees').val().replace(/,/g, ""));
      var expenses = parseFloat($('#claim_assessment_attributes_expenses').val().replace(/,/g, ""));
      var total = adp.determination.calculateAmount(fees,expenses);
      if (isNaN(total) ){
        $('.total-determination').text('£0.00');
      }
      else{
        $('.total-determination').text('£ '+ total);
      }
    });
  }
};

Allow commas in assessment fees and expenses values


define([
  'require',
  'extensions/views/timeseries-graph/timeseries-graph',
  'extensions/views/graph/linelabel',
  './callout'
],
function (require, TimeseriesGraph, LineLabel, Callout) {
  var LPATimeseriesGraph = TimeseriesGraph.extend({

    components: function () {
      return [
        { view: this.sharedComponents.xaxis },
        { view: this.sharedComponents.yaxis },
        { view: this.sharedComponents.stack },
        { view: LineLabel },
        { view: Callout },
        { view: this.sharedComponents.hover }
      ];
    },

    getConfigNames: function () {
      return ['stack', 'week'];
    }

  });

  return LPATimeseriesGraph;
});

define([
  'require',
  'extensions/views/timeseries-graph/timeseries-graph',
  'extensions/views/graph/linelabel',
  './callout'
],
function (require, TimeseriesGraph, LineLabel, Callout) {
  var LPATimeseriesGraph = TimeseriesGraph.extend({

    components: function () {
      return [
        { view: this.sharedComponents.xaxis },
        { view: this.sharedComponents.yaxis },
        {
          view: this.sharedComponents.stack,
          options: {
            selectGroup: false,
            drawCursorLine: true
          }
        },
        {
          view: this.sharedComponents.linelabel,
          options: {
            showValues: true,
            showValuesPercentage: true
          }
        },
        { view: Callout },
        { view: this.sharedComponents.hover }
      ];
    },

    getConfigNames: function () {
      return ['stack', 'week'];
    }

  });

  return LPATimeseriesGraph;
});

Apply stacked graph improvements to LPA graph as example


import axios from 'axios';

export default itemIdentifier => axios.get(`items`, {
  baseURL: `http://track.bpost.be/btr/api/`,
  responseType: `json`,
  params: { itemIdentifier },
}).then(res => res.data[0]);

import axios from 'axios';

export default itemIdentifier => axios.get(`items`, {
  baseURL: `http://track.bpost.be/btr/api/`,
  responseType: `json`,
  params: { itemIdentifier },
}).then(res => res.data.items[0]);

Fix return of fetch response API response now uses a top level key items followed by the array


/**
 * Update the price on the product details page in real time when the variant or the quantity are changed.
 **/

$(document).ready(function() {
  // Product page with variant choice
  $("#product-variants input[type='radio']").change(products_update_price_with_variant);
  $("#quantity").change(products_update_price_with_variant);
  $("#quantity").change();

  // Product page with master price only
  $(".add-to-cart input.title:not(#quantity)").change(products_update_price_without_variant).change();
});

function products_update_price_with_variant() {
  var variant_price = $("#product-variants input[type='radio']:checked").parent().find("span.price").html();
  variant_price = variant_price.substr(2, variant_price.length-3);

  var quantity = $("#quantity").val();

  $("#product-price span.price").html("$"+(parseFloat(variant_price) * parseInt(quantity)).toFixed(2));
}

function products_update_price_without_variant() {
  var master_price = $("#product-price span.price").data('master-price');
  if(master_price == null) {
    // Store off the master price
    master_price = $("#product-price span.price").html();
    master_price = master_price.substr(1, master_price.length-2);
    $("#product-price span.price").data('master-price', master_price);
  }

  var quantity = $(this).val();

  $("#product-price span.price").html("$"+(parseFloat(master_price)*parseInt(quantity)).toFixed(2));
}

/**
 * Update the price on the product details page in real time when the variant or the quantity are changed.
 **/

$(document).ready(function() {
  // Product page with variant choice
  $("#product-variants input[type='radio']").change(products_update_price_with_variant);
  $("#quantity").change(products_update_price_with_variant);
  $("#quantity").change();

  // Product page with master price only
  $(".add-to-cart input.title:not(#quantity):not(#max_quantity)").change(products_update_price_without_variant).change();
});

function products_update_price_with_variant() {
  var variant_price = $("#product-variants input[type='radio']:checked").parent().find("span.price").html();
  variant_price = variant_price.substr(2, variant_price.length-3);

  var quantity = $("#quantity").val();

  $("#product-price span.price").html("$"+(parseFloat(variant_price) * parseInt(quantity)).toFixed(2));
}

function products_update_price_without_variant() {
  var master_price = $("#product-price span.price").data('master-price');
  if(master_price == null) {
    // Store off the master price
    master_price = $("#product-price span.price").html();
    master_price = master_price.substr(1, master_price.length-2);
    $("#product-price span.price").data('master-price', master_price);
  }

  var quantity = $(this).val();

  $("#product-price span.price").html("$"+(parseFloat(master_price)*parseInt(quantity)).toFixed(2));
}

Fix add to cart JS for compatibility with group buy


import * as types from '../actions/action-types';
import initialState from './initial-state';

export default (state = initialState.courses, action) => {
    switch (action.type) {
    case types.LOAD_COURSES_SUCCESS:
        return action.courses;

    default:
        return state;
    }
};

import * as types from '../actions/action-types';
import initialState from './initial-state';

export default (state = initialState.courses, action) => {
    switch (action.type) {
    case types.LOAD_COURSES_SUCCESS:
        return action.courses;

    case types.CREATE_COURSE_SUCCESS:
        return [
            ...state,
            Object.assign({}, action.course),
        ];

    case types.UPDATE_COURSE_SUCCESS:
        return [
            ...state.filter(course => course.id !== action.course.id),
            Object.assign({}, action.course),
        ];

    default:
        return state;
    }
};

Handle creates and updates in the reducer


var configuration = require('../../../config/configuration.json')
var utility = require('../../../public/method/utility')

module.exports = {
  setsubCampaignSettingModel: function (redisClient, campaignHashID, subcampaignHashID, payload, callback) {

  }
}

var configuration = require('../../../config/configuration.json')
var utility = require('../../../public/method/utility')

module.exports = {
  setsubCampaignSettingModel: function (redisClient, campaignHashID, subcampaignHashID, payload, callback) {
    var table
    var score = utility.getUnixTimeStamp()
    var multi = redisClient.multi()
    var settingKeys = Object.keys(configuration.settingEnum)

    var tableName = configuration.TableMASubcampaignModelSettingModel + subcampaignHashID
    multi.hset(tableName, configuration.ConstantSMPriority, payload[configuration.ConstantSMPriority])

    var finalCounter = 0
    for (var i = 0; i < settingKeys.length; i++) {
      if (settingKeys[i] === configuration.settingEnum.Priority)
        continue

      var key = settingKeys[i]
      var scoreMemberArray = []
      var keyValueArray = payload[key]

      for (var j = 0; j < keyValueArray.length; j++) {
        scoreMemberArray.push(score)
        scoreMemberArray.push(keyValueArray[j])
      }

      var counter = 0
      for (var j = 0; j < keyValueArray.length; j++) {
        table = configuration.TableModel.general.SubcampaignModel + campaignHashID
        utility.stringReplace(table, '@', key)

        redisClient.zrange(table, '0', '-1', function (err, replies) {
          if (err) {
            callback(err, null)
            return
          }

          table = configuration.TableModel.general.SubcampaignModel + campaignHashID
          for (var k = 0; k < replies.length; k++) {
            utility.stringReplace(table, '@', replies[k])
            multi.zrem(table, subcampaignHashID)
          }

          /* Add to Model List */
          table = configuration.TableModel.general.SubcampaignModel + campaignHashID
          utility.stringReplace(table, '@', keyValueArray[j])
          multi.zadd(table, score, subcampaignHashID)
          /* Add to Model Set */
          table = configuration.TableModel.general.SubcampaignModel
          utility.stringReplace(table, '@', keyValueArray[j])
          multi.zadd(table, score, subcampaignHashID)

          counter++
        })

        if (counter == keyValueArray.length) {
          /* Model Set */
          table = configuration.TableModel.general.subcampaignHashID + subcampaignHashID
          utility.stringReplace(table, '@', key)
          /* Remove from Model Set */
          multi.zremrangebyrank(table, '0', '-1')
          /* Add to Model Set */
          scoreMemberArray.unshift(table)
          multi.zadd(scoreMemberArray)
        }
      }

      if (finalCounter == settingKeys.length) {
        multi.exec(function (err, replies) {
          if (err) {
            callback(err, null)
            return
          }
          callback(null, configuration.message.setting.subcampaign.set.successful)
        })
      }
    }
  }
}

Implement Create a Subcampaign Model Setting Dynamically


import React from 'react';

export default ({contributors}) => {
  return

    {contributors.map(c =>
      <Contributor
        key={c.id}
        avatar={c.avatar_url}
        href={c.html_url}
        dimensions="100"/>)}

};

const Contributor = ({avatar, href, dimensions}) => {
  return (

  );
};

import React from 'react';

export default ({contributors}) => {
  return

    {contributors.map(c =>
      <Contributor
        key={c.id}
        avatar={c.avatar_url}
        href={c.html_url}
        dimensions="100"
        name={c.login}/>)}

};

const Contributor = ({avatar, href, dimensions, name}) => {
  return (

  );
};

Set contributor alt gh username


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

var userSchema = new Schema({
  githubId: Number,
  name: String,
  avatar_url: String,
  isDeveloper: Boolean,
  projects: Array
});

module.exports = mongoose.model('User', userSchema);

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

var userSchema = new Schema({
  githubId: Number,
  name: String,
  avatar_url: String,
  projects: Array,
  userId: String,
  isUser: Boolean,
  isDeveloper: Boolean
});

module.exports = mongoose.model('User', userSchema);

Add new properties to user schema


/*
 * EksiOkuyucu - unofficial eksisozluk client
 * http://eksiokuyucu.com/
 *
 * Copyright (C) 2014  Onur Aslan  <onuraslan@gmail.com>
 * Licensed under MIT
 * https://github.com/onuraslan/EksiOkuyucu/blob/master/COPYING
 */

require.config ({
  paths: {
    requireLib: 'libs/require',
    jquery: 'libs/jquery-1.11.0.min',
    underscore: 'libs/underscore-min',
    backbone: 'libs/backbone-min',
    text: 'libs/text',
    bootstrap: 'libs/bootstrap.min',
    select2: 'libs/select2.min',
    templates: '../templates'
  },

  shim: {
    'bootstrap': {
       deps: ["jquery"]
    },
    'select2': {
       deps: ["jquery"]
    }
  }
});

require([
  'app',
], function (App) {
  App.initialize ();
});

/*
 * EksiOkuyucu - unofficial eksisozluk client
 * http://eksiokuyucu.com/
 *
 * Copyright (C) 2014  Onur Aslan  <onuraslan@gmail.com>
 * Licensed under MIT
 * https://github.com/onuraslan/EksiOkuyucu/blob/master/COPYING
 */

require.config ({
  paths: {
    requireLib: 'libs/require',
    jquery: 'libs/jquery-1.11.0.min',
    underscore: 'libs/underscore-min',
    backbone: 'libs/backbone-min',
    text: 'libs/text',
    bootstrap: 'libs/bootstrap.min',
    templates: '../templates'
  },

  shim: {
    'bootstrap': {
       deps: ["jquery"]
    }
  }
});

require([
  'app',
], function (App) {
  App.initialize ();
});

Remove select2 library from config


if (Meteor.isServer) {
  // Check if there are any nodes at all in the database
  var query = 'MATCH (n) RETURN n'
  var options = null
  Meteor.N4JDB.query(query, options, callback) // output is undefined

  // The database sends its response to a callback
  function callback(error, nodeArray) {
    console.log(error, nodeArray) // JSON.stringify(nodeArray))
    // null
    // [{ n: { db: [Object], _request: [Object], _data: [Object] } }]
    if (error) {
      return console.log(error)
    }

    if (!nodeArray.length) {
      var node = Meteor.N4JDB.createNode({name: 'hello world'})

      // node is not saved to the database: you must manually save it
      node.save(function (error, savedNode) {
        if (error) {
          return console.error('New node not saved:', error)
        }

        // The node is ready to use. See it at...
        // http://localhost:7474/browser/
        // ... using the query:
        // MATCH (n) RETURN n
       })
    }
  }
}

if (Meteor.isServer) {
  // Check if there are any nodes with the Meteo4J label in the
  // database, using a standard cypher query
  var label = "Meteo4j"
  var cypher = "MATCH (n:" + label + ") RETURN n"
  var options = null
  Meteor.N4JDB.query(cypher, options, matchCallback)

  function matchCallback(error, nodeArray) {
    console.log(error, nodeArray)
    if (error) {
      return console.log(error)
    }

    // There are no nodes, create one
    if (!nodeArray.length) {
      cypher = "CREATE (n:" + label + " {name: 'hello world'})"
      Meteor.N4JDB.query(cypher, options, createCallback)

      function createCallback(error, resultArray) {
        if (error) {
          return console.error('New node not created:', error)
          // { [Error: Unexpected end of input: expected whitespace, ')' or a relationship pattern...
        }

        //console.log(result) // []

        // The node is ready to use. See it at...
        // http://localhost:7474/browser/
        // ... using the query:
        // MATCH (n:Meteo4j) RETURN n
       }
    }
  }
}

Use N4JDB.query to create a (:Meteo4j {name: 'hello world'}) node


;(function (w, doc) {
  // Enable strict mode
  "use strict";
  var accordion = document.querySelectorAll('.accordion');

  var forEach = function (array, callback, scope) {
    for (var i = 0; i < array.length; i++) {
      callback.call(scope, i, array[i]); // passes back stuff we need
    }
  };

  forEach(accordion, function(index, value) {
    var accordionContent = accordion[index].querySelectorAll('.accordion-content'),
        accordionHeader  = accordion[index].querySelectorAll('.accordion-header'),
        topIndex         = index + 1;

    forEach(accordionHeader, function(index, value) {
      var head  = value,
          index = index + 1;

      // Set ARIA and ID attributes
      head.setAttribute('id', 'tab' + topIndex + '-' + index);
      head.setAttribute('aria-selected', 'false');
      head.setAttribute('aria-expanded', 'false');
      head.setAttribute('aria-controls', 'panel' + topIndex + '-' + index);
      head.setAttribute('role', 'tab');
      head.setAttribute('tabindex', '0');

      head.onclick = accordionHandle;

      function accordionHandle() {
        var nextPanel = value.nextElementSibling;

        value.classList.toggle('is-active');
        nextPanel.classList.toggle('is-active');
        nextPanel.querySelector("h2").setAttribute("tabindex", -1);
        nextPanel.querySelector("h2").focus();

        if(!nextPanel.classList.contains('visually-hidden')) {
          head.setAttribute('aria-selected', 'true');
          head.setAttribute('aria-expanded', 'true');
          nextPanel.setAttribute('aria-hidden', 'false');
        } else {
          head.setAttribute('aria-selected', 'false');
          head.setAttribute('aria-expanded', 'false');
          nextPanel.setAttribute('aria-hidden', 'true');
        }
      }
    });

    forEach(accordionContent, function(index, value) {
      var content = value,
          index   = index + 1;

      // Set ARIA and ID attributes
      content.setAttribute('id', 'panel' + topIndex + '-' + index);
      content.setAttribute('aria-hidden', 'true');
      content.setAttribute('aria-labelledby', 'tab' + topIndex + '-' + index);
      content.setAttribute('role', 'tabpanel');
      content.setAttribute('tabindex', '-1');
    });

  });
} )( this, this.document );

Add aria state setting, class toggles and ID setting.


'use strict';

var d      = require('es5-ext/lib/Object/descriptor')
  , proto  = require('../_proto')
  , simple = require('./simple')

  , validate;

Object.defineProperties(simple(proto._defineRel_('toString')), {
	validateValue: d(validate = function (value) {
		if (!value || (typeof value !== 'function') || value._type_) {
			return new TypeError(value + ' is not valid function');
		}
		return null;
	}),
	validateCreateValue: d(validate)
}).$$setValue(function () { return '[DBJS:' + this._id_ + ']'; });

'use strict';

var d      = require('es5-ext/lib/Object/descriptor')
  , proto  = require('../_proto')
  , simple = require('./simple')

  , validate;

Object.defineProperties(simple(proto._defineRel_('toString')), {
	validateValue: d(validate = function (value) {
		if (!value || (typeof value !== 'function') || value._type_) {
			return new TypeError(value + ' is not valid function');
		}
		return null;
	}),
	validateCreateValue: d(validate)
}).$$setValue(function () { return '[dbjs ' + this._id_ + ']'; });

Improve low level string representation


require(
    [
        "domReady",
        "rexster/history",
        "rexster/template/template",
        "rexster/ui/main-menu",
        "order!has",
        "order!has-detect-features"
    ],
    function (domReady, history, template, mainMenu) {
        domReady(function () {

            // only make this feature available to browsers that support it
            if (has("native-history-state")) {
                window.onpopstate = function(event) {
                    restoreApplication();
                };
            }

            function restoreApplication() {
                // compile the templates
                template.initTemplates();

                // build the main menu.  this action will initialize the
                // first enabled panel
                mainMenu.initMainMenu();
            }

            // determine if the state is already established
            var state = history.getApplicationState();
            if (!state.hasOwnProperty("menu")) {
                // since there is no menu selected initialized the graph page first.
                history.historyPush("/doghouse/main/graph");

                if (!has("native-history-state")) {
                    restoreApplication()
                }

            } else {
                if (!has("native-history-state")) {
                    restoreApplication()
                }
            }
        });
    });

require(
    [
        "domReady",
        "rexster/history",
        "rexster/template/template",
        "rexster/ui/main-menu",
        "order!has",
        "order!has-detect-features"
    ],
    function (domReady, history, template, mainMenu) {
        domReady(function () {

            // only make this feature available to browsers that support it
            if (has("native-history-state")) {
                window.onpopstate = function(event) {
                    var popped = ('state' in window.history), initialURL = location.href
                    $(window).bind('popstate', function(event) {
                      // Ignore inital popstate that some browsers fire on page load
                      var initialPop = !popped && location.href == initialURL
                      popped = true
                      if ( initialPop ) return

                        restoreApplication();
                    });
                };
            }

            function restoreApplication() {
                // compile the templates
                template.initTemplates();

                // build the main menu.  this action will initialize the
                // first enabled panel
                mainMenu.initMainMenu();
            }

            // determine if the state is already established
            var state = history.getApplicationState();
            if (!state.hasOwnProperty("menu")) {
                // since there is no menu selected initialized the graph page first.
                history.historyPush("/doghouse/main/graph");
            }

            restoreApplication();
        });
    });

Fix FF issues with doghouse.


import React from 'react'

export default ({ event }) => (
  <h4>
    {
      `${event.code}
      - ${new Date(event.date * 1000).toLocaleString()}
      - ${event.home} X ${event.away}
      - ${event.sport}
      - ${event.country}
      - ${event.competition}`
    }
  </h4>
)

import React from 'react'

export default class EventHeading extends React.Component {
  constructor (props) {
    super(props)

    const date = new Date(props.event.date * 1000)

    this.state = {
      dateDate: formatDate(date),
      dateTime: formatTime(date),
      eventName: `${props.event.home} X ${props.event.away}`
    }
  }

  render () {
    const event = this.props.event

    return (
      <h4>
        {
          `${event.code}
          - ${this.state.dateDate}, ${this.state.dateTime}
          - ${this.state.eventName}
          - ${event.sport}
          - ${event.country}
          - ${event.competition}`
        }
      </h4>
    )
  }
}

function formatDate (date) {
  const now = new Date()

  return date.getDate() === now.getDate() ? 'Hoje'
    : date.getDate() === (now.getDate() + 1) ? 'Amanhã'
    : date.toLocaleDateString()
}

function formatTime (date) {
  const time = date.toLocaleTimeString()
  // Remove seconds from return string
  return time.slice(0, time.lastIndexOf(':'))
}

Format date and time display.


'use strict';

//Start by defining the main module and adding the module dependencies
angular.module(ApplicationConfiguration.applicationModuleName,
               ApplicationConfiguration.applicationModuleVendorDependencies);

// Setting HTML5 Location Mode
angular.module(ApplicationConfiguration.applicationModuleName).config(['$locationProvider',
  function($locationProvider) {
    $locationProvider.hashPrefix('!');
  }
]);

//Then define the init function for starting up the application
angular.element(document).ready(function() {
  //Fixing facebook bug with redirect
  if (window.location.hash === '#_=_') {
    window.location.hash = '#!';
  }

  //Then init the app
  angular.bootstrap(document, [ApplicationConfiguration.applicationModuleName]);
});

'use strict';

// Start by defining the main module and adding the module dependencies.
angular.module(ApplicationConfiguration.applicationModuleName,
               ApplicationConfiguration.applicationModuleVendorDependencies);

// Setting HTML5 Location Mode.
angular.module(ApplicationConfiguration.applicationModuleName).config(['$locationProvider',
  function($locationProvider) {
    // Enable app to use HTML5 History API.
    $locationProvider.html5Mode(true);//.hashPrefix('!');
  }
]);

//Then define the init function for starting up the application
angular.element(document).ready(function() {
  //Fixing facebook bug with redirect
  if (window.location.hash === '#_=_') {
    window.location.hash = '#!';
  }

  //Then init the app
  angular.bootstrap(document, [ApplicationConfiguration.applicationModuleName]);
});

Change to use HTML5mode for URL. Remove hashbang and hash prefix from app URL.


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

module.exports = {
  afterInstall: function() {
    this.addPackagesToProject([
      { name: 'ember-cli-paint' },
      { name: 'ember-cli-lodash' },
      { name: 'ember-moment' }
    ]);

    this.addBowerPackagesToProject([
      { name: 'moment-timezone' },
      { name: 'interact' },
      { name: 'lodash' }
    ]);
  }
};

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

module.exports = {
  normalizeEntityName: function() {},

  beforeInstall: function() {
    return this.addBowerPackagesToProject([
      { name: 'moment-timezone' },
      { name: 'jquery-simulate' },
      { name: 'interact' },
      { name: 'lodash' }
    ]);
  },

  afterInstall: function() {
    return this.addPackagesToProject([
      { name: 'ember-cli-paint' },
      { name: 'ember-cli-lodash' },
      { name: 'ember-moment' }
    ]);
  }
};

Normalize entity name and return promises


"use strict";

angular.module('arethusa.comments').factory('CommentsRetriever', [
  'configurator',
  'idHandler',
  function(configurator, idHandler) {
    var comments = {};

    function splitIdAndComment(comment) {
      var regexp = new RegExp('^##(.*?)##\n\n(.*)$');
      var match = regexp.exec(comment);
      return match ? match.slice(1, 3) : ['noId', comment];
    }

    function addComments(id, comment) {
      var arr = arethusaUtil.getProperty(comments, id);
      if (!arr) {
        arr = [];
        arethusaUtil.setProperty(comments, id, arr);
      }
      arr.push(comment);
    }

    function parseComments(res) {
      angular.forEach(res, function(commentObj, i) {
        var comment = commentObj.comment;
        var extracted = splitIdAndComment(comment);
        commentObj.comment = extracted[1];
        addComments(extracted[0], commentObj);
      });
    }

    return function(conf) {
      var self = this;
      var resource = configurator.provideResource(conf.resource);

      var alreadyLoaded;

      this.getData = function(chunkId, callback) {
        if (alreadyLoaded) {
          callback(comments[chunkId]);
        } else {
          resource.get().then(function(res) {
            parseComments(res.data);
            alreadyLoaded = true;
            callback(comments[chunkId]);
          });
        }
      };
    };
  }
]);

"use strict";

angular.module('arethusa.comments').factory('CommentsRetriever', [
  'configurator',
  'idHandler',
  function(configurator, idHandler) {
    var comments = {};
    var alreadyLoaded;

    function splitIdAndComment(comment) {
      var regexp = new RegExp('^##(.*?)##\n\n(.*)$');
      var match = regexp.exec(comment);
      return match ? match.slice(1, 3) : ['noId', comment];
    }

    function addComments(id, comment) {
      var arr = arethusaUtil.getProperty(comments, id);
      if (!arr) {
        arr = [];
        arethusaUtil.setProperty(comments, id, arr);
      }
      arr.push(comment);
    }

    function parseComments(res) {
      angular.forEach(res, function(commentObj, i) {
        var comment = commentObj.comment;
        var extracted = splitIdAndComment(comment);
        commentObj.comment = extracted[1];
        addComments(extracted[0], commentObj);
      });
    }

    return function(conf) {
      var self = this;
      var resource = configurator.provideResource(conf.resource);

      this.getData = function(chunkId, callback) {
        if (alreadyLoaded) {
          callback(comments[chunkId]);
        } else {
          resource.get().then(function(res) {
            parseComments(res.data);
            callback(comments[chunkId]);
          });
          alreadyLoaded = true;
        }
      };
    };
  }
]);

Fix scope issues in CommentsRetriever


'use strict';

function killApiUmbrellaServer(callback) {
  global.apiUmbrellaStopping = true;

  if(global.apiUmbrellaServer) {
    if(callback) {
      global.apiUmbrellaServer.on('close', function() {
        callback();
      });
    }

    global.apiUmbrellaServer.kill();
  } else {
    if(callback) {
      callback();
    }
  }
}

after(function stopProcesses(done) {
  this.timeout(30000);
  killApiUmbrellaServer(done);
});

process.on('exit', function() {
  killApiUmbrellaServer();
});

'use strict';

function killApiUmbrellaServer(callback) {
  global.apiUmbrellaStopping = true;

  if(global.apiUmbrellaServer) {
    if(callback) {
      global.apiUmbrellaServer.on('close', function() {
        callback();
      });
    }

    global.apiUmbrellaServer.kill();
  } else {
    if(callback) {
      callback();
    }
  }
}

after(function stopProcesses(done) {
  this.timeout(60000);
  killApiUmbrellaServer(done);
});

process.on('exit', function() {
  killApiUmbrellaServer();
});

Increase timeout for stopping processes during tests.


module.exports = {
  server: {},

  options: {
    hostname: '0.0.0.0',
    port: (process.env.PORT || 8000),
    base: '.',
    middleware: function(connect, options) {
      return [
        require('connect-redirection')(),
        function(req, res, next) {
          if (req.url === '/') {
            res.redirect('/test');
          } else {
            next();
          }
        },
        connect.static(options.base)
      ];
    }

  }
};

module.exports = {
  server: {},

  options: {
    hostname: '0.0.0.0',
    port: (process.env.PORT || 8000),
    base: '.',
    middleware: function(connect, options) {
      return [
        require('connect-redirection')(),
        function(req, res, next) {
          if (req.url === '/') {
            res.redirect('/test');
          } else {
            next();
          }
        },
        connect.static(String(options.base))
      ];
    }

  }
};

Repair tests with new grunt


const selectors = require('../../selectors')

describe('Company interaction', () => {
  beforeEach(() => {
    cy.visit('/companies/4cd4128b-1bad-4f1e-9146-5d4678c6a018/interactions')
  })

  it('Hide add interaction for archived companies', () => {
    cy.get(selectors.companyInteraction.add).should('not.be.visible')
    cy.get(selectors.companyInteraction.header).should('be.visible')

    cy.get(selectors.companyInteraction.header).should('contain', 'Company interactions')
    cy.get(selectors.companyInteraction.archivedMsg).should(
      'contain', 'Why can I not add an interaction?')
  })
})

const selectors = require('../../selectors')

describe('Company interaction', () => {
  beforeEach(() => {
    cy.visit('/companies/346f78a5-1d23-4213-b4c2-bf48246a13c3/interactions')
  })

  it('Hide add interaction for archived companies', () => {
    cy.get(selectors.companyInteraction.add).should('not.be.visible')
    cy.get(selectors.companyInteraction.header).should('be.visible')

    cy.get(selectors.companyInteraction.header).should('contain', 'Company interactions')
    cy.get(selectors.companyInteraction.archivedMsg).should(
      'contain', 'Why can I not add an interaction?')
  })
})

Update ID to be archived company ID


'use strict';

// MODULES //

var merge = require( '@stdlib/utils/merge' );
var NS = require( '@stdlib/datasets' );

// ASSIGN //

/**
* Assigns modules to a namespace.
*
* @private
* @param {Object} ns - namespace
* @returns {Object} input namespace
*
* @example
* var ns = {};
* assign( ns );
* // ns => {...}
*/
function assign( ns ) {
	return merge( ns, NS );
} // end FUNCTION assign()

// EXPORTS //

module.exports = assign;

'use strict';

// MODULES //

var datasets = require( '@stdlib/datasets' );

// ASSIGN //

/**
* Assigns modules to a namespace.
*
* @private
* @param {Object} ns - namespace
* @returns {Object} input namespace
*
* @example
* var ns = {};
* assign( ns );
* // ns => {...}
*/
function assign( ns ) {
	ns.datasets = datasets;
	return ns;
} // end FUNCTION assign()

// EXPORTS //

module.exports = assign;

Update datasets assignment in namespace


/* eslint-env node */
module.exports = {
  "test_page": "tests/index.html?hidepassed",
  "disable_watching": true,
  "launch_in_ci": [
    "PhantomJS"
  ],
  "launch_in_dev": [
    "PhantomJS",
    "Chrome"
  ]
};

/* eslint-env node */
module.exports = {
  "test_page": "tests/index.html?hidepassed",
  "disable_watching": true,
  "launch_in_ci": [
    "PhantomJS",
    "Firefox"
  ],
  "launch_in_dev": [
    "PhantomJS",
    "Chrome"
  ]
};

Add firefox launching for CI.


define(['mac/roman'], function(macintoshRoman) {

  'use strict';

  return function(resource) {
    var dv = new DataView(resource.data.buffer, resource.data.byteOffset, resource.data.byteLength);
    resource.dataObject = {
      rectangle: {
        top: dv.getInt16(0, false),
        left: dv.getInt16(2, false),
        bottom: dv.getInt16(4, false),
        right: dv.getInt16(6, false),
      },
      type: dv.getUint16(8, false),
      visible: !!resource.data[10],
      closeBox: !!resource.data[12],
      referenceConstant: dv.getInt32(14, false),
      itemListResourceID: dv.getInt16(18, false),
    };
    switch(resource.dataObject.type) {
      case 0: resource.dataObject.type = 'modal'; break;
      case 4: resource.dataObject.type = 'modeless'; break;
      case 5: resource.dataObject.type = 'movableModal'; break;
    }
    resource.dataObject.text = macintoshRoman(resource.data, 21, resource.data[20]);
    var pos = 20 + 1 + resource.dataObject.text.length + (1 + resource.dataObject.text.length) % 2;
    resource.dataObject.positionCode = dv.getUint16(pos, false);
  };

});

define(['mac/roman'], function(macintoshRoman) {

  'use strict';

  return function(resource) {
    var dv = new DataView(resource.data.buffer, resource.data.byteOffset, resource.data.byteLength);
    resource.dataObject = {
      rectangle: {
        top: dv.getInt16(0, false),
        left: dv.getInt16(2, false),
        bottom: dv.getInt16(4, false),
        right: dv.getInt16(6, false),
      },
      type: dv.getUint16(8, false),
      visible: !!resource.data[10],
      closeBox: !!resource.data[12],
      referenceConstant: dv.getInt32(14, false),
      itemListResourceID: dv.getInt16(18, false),
    };
    switch(resource.dataObject.type) {
      case 0: resource.dataObject.type = 'modal'; break;
      case 4: resource.dataObject.type = 'modeless'; break;
      case 5: resource.dataObject.type = 'movableModal'; break;
    }
    resource.dataObject.text = macintoshRoman(resource.data, 21, resource.data[20]);
    var pos = 20 + 1 + resource.dataObject.text.length + (1 + resource.dataObject.text.length) % 2;
    if (pos + 2 <= resource.data.length) {
      resource.dataObject.positionCode = dv.getUint16(pos, false);
    }
  };

});

Check that position code is present before reading


define(function() {

  return function(resource) {
    var dv = new DataView(resource.data.buffer, resource.data.byteOffset, resource.data.byteLength);
    var entryCount = dv.getInt16(6, false) + 1;
    if (entryCount < 0) {
      console.error('color table resource: invalid number of entries');
    }
    var palCanvas = document.createElement('CANVAS');
    palCanvas.width = entryCount;
    if (entryCount === 0) {
      palCanvas.height = 0;
    }
    else {
      palCanvas.height = 1;
      var palCtx = palCanvas.getContext('2d');
      var palData = palCtx.createImageData(entryCount, 1);
      for (var icolor = 0; icolor < entryCount; icolor++) {
        var offset = dv.getInt16(8 + icolor*8, false) * 4;
        if (offset >= 0) {
          palData.data[offset] = resource.data[8 + icolor*8 + 2];
          palData.data[offset + 1] = resource.data[8 + icolor*8 + 4];
          palData.data[offset + 2] = resource.data[8 + icolor*8 + 6];
          palData.data[offset + 3] = 255;
        }
      }
      palCtx.putImageData(palData, 0, 0);
    }
    resource.image = {
      width: palCanvas.width,
      height: palCanvas.height,
      url: palCanvas.toDataURL(),
    };

  };

});

define(function() {

  return function(resource) {
    var dv = new DataView(resource.data.buffer, resource.data.byteOffset, resource.data.byteLength);
    var entryCount = dv.getInt16(6, false) + 1;
    if (entryCount < 0) {
      console.error('color table resource: invalid number of entries');
    }
    if (entryCount === 0) {
      resource.dataObject = null;
      return;
    }
    var palCanvas = document.createElement('CANVAS');
    palCanvas.width = entryCount;
    palCanvas.height = 1;
    var palCtx = palCanvas.getContext('2d');
    var palData = palCtx.createImageData(entryCount, 1);
    for (var icolor = 0; icolor < entryCount; icolor++) {
      var offset = dv.getInt16(8 + icolor*8, false) * 4;
      if (offset >= 0) {
        palData.data[offset] = resource.data[8 + icolor*8 + 2];
        palData.data[offset + 1] = resource.data[8 + icolor*8 + 4];
        palData.data[offset + 2] = resource.data[8 + icolor*8 + 6];
        palData.data[offset + 3] = 255;
      }
    }
    palCtx.putImageData(palData, 0, 0);
    resource.image = {
      width: entryCount,
      height: 1,
      url: palCanvas.toDataURL(),
    };

  };

});

Use null dataObject if color table is empty


module.exports = {
  constant: {
    LOOPBACK_CLIENT_ID: 'LOOPBACK_CLIENT_ID',
    TURN_BASE_URL: 'https://node-apprtc.herokuapp.com',
    TURN_URL_TEMPLATE: '%s/turn?username=%s&key=%s',
    WSS_HOST_PORT_PAIRS: ['node-apprtc-ws.herokuapp.com'],
    RESPONSE_UNKNOWN_ROOM: 'UNKNOWN_ROOM',
    RESPONSE_UNKNOWN_CLIENT: 'UNKNOWN_CLIENT',
    RESPONSE_ROOM_FULL: 'FULL',
    RESPONSE_DUPLICATE_CLIENT: 'DUPLICATE_CLIENT',
  },
  server: {
    host: '127.0.0.1',
    port: process.env.PORT || 4567
  }
};

module.exports = {
  constant: {
    LOOPBACK_CLIENT_ID: 'LOOPBACK_CLIENT_ID',
    TURN_BASE_URL: 'https://node-apprtc.herokuapp.com',
    TURN_URL_TEMPLATE: '%s/turn',
    WSS_HOST_PORT_PAIRS: ['node-apprtc-ws.herokuapp.com'],
    RESPONSE_UNKNOWN_ROOM: 'UNKNOWN_ROOM',
    RESPONSE_UNKNOWN_CLIENT: 'UNKNOWN_CLIENT',
    RESPONSE_ROOM_FULL: 'FULL',
    RESPONSE_DUPLICATE_CLIENT: 'DUPLICATE_CLIENT',
  },
  server: {
    host: '127.0.0.1',
    port: process.env.PORT || 4567
  }
};

Remove credentials path in TURN request URL.


'use strict';

const assert = require('assert');
const encoders = require('../lib/encoders');
const fixtures = require('./fixture/server');

describe('encoders', function () {
  describe('thumbprint', function () {
    it('should return the thumbprint in all caps', function () {
      const certThumbprint = encoders.thumbprint(fixtures.credentials.cert);
      assert.equal(certThumbprint, '499FDF1C2218A99C8595AAC2FD95CE36F0A6D59D');
    });
  });
});

var assert = require('assert');
var encoders = require('../lib/encoders');
var fixtures = require('./fixture/server');

describe('encoders', function () {
  describe('thumbprint', function () {
    it('should return the thumbprint in all caps', function () {
      var certThumbprint = encoders.thumbprint(fixtures.credentials.cert);
      assert.equal(certThumbprint, '499FDF1C2218A99C8595AAC2FD95CE36F0A6D59D');
    });
  });
});

Make compatible with Node 0.10 🤢


var test = require('tape');
var expect = require('./helpers/expect-constraints');

test('camera max:15fps', expect({
  audio: true,
  video: {
    mandatory: {
      maxFrameRate: 15
    },

    optional: []
  }
}));

test('camera min:25fps', expect({
  audio: true,
  video: {
    mandatory: {
      minFrameRate: 25
    },

    optional: []
  }
}));

test('camera min:15fps max:25fps', expect({
  audio: true,
  video: {
    mandatory: {
      minFrameRate: 15,
      maxFrameRate: 25
    },

    optional: []
  }
}));

var test = require('tape');
var expect = require('./helpers/expect-constraints');

test('camera 15fps', expect({
  audio: true,
  video: {
    mandatory: {},

    optional: [
      { minFrameRate: 15 },
      { maxFrameRate: 15 },
      { frameRate: 15 }
    ]
  }
}));

test('camera max:15fps', expect({
  audio: true,
  video: {
    mandatory: {},
    optional: [
      { maxFrameRate: 15 },
      { frameRate: { max: 15 } }
    ]
  }
}));

test('camera min:25fps', expect({
  audio: true,
  video: {
    mandatory: {},
    optional: [
      { minFrameRate: 25 },
      { frameRate: { min: 25 } }
    ]
  }
}));

test('camera min:15fps max:25fps', expect({
  audio: true,
  video: {
    mandatory: {},
    optional: [
      { minFrameRate: 15 },
      { maxFrameRate: 25 },
      { frameRate: { min: 15, max: 25 } }
    ]
  }
}));

Update test to check for required new format


import "./App.css"
import { BrowserRouter, Route } from "react-router-dom"

import Login from "./containers/Login/Login"
import SGonksPlatfrom from "./containers/SGonksPlatform/SGonksPlatform"

function App() {
  return (
    <BrowserRouter>
      <div className="App">
        <Route path="/" exact component={Login}></Route>
        <Route path="/sgonks-platform" component={SGonksPlatfrom}></Route>
      </div>
    </BrowserRouter>
  )
}

export default App

import './App.css'
import { BrowserRouter, Route } from 'react-router-dom'

import Login from './containers/Login/Login'
import SGonksPlatfrom from './containers/SGonksPlatform/SGonksPlatform'

function App() {
  return (
    <BrowserRouter>
      <div className='App'>
        <Route path='/' exact component={Login}></Route>
        <Route path='/sgonks-platform' component={SGonksPlatfrom}></Route>
      </div>
    </BrowserRouter>
  )
}

export default App

Change double quotes to single quotes to satisfy linter


import { ModelComponent } from '../../kit'

export default class MetaSectionTOCEntry extends ModelComponent {
  render ($$) {
    const id = this.props.id
    const name = this.props.name
    const model = this.props.model
    let el = $$('a').addClass('sc-meta-section-toc-entry')
      .attr({ href: '#' + id })
      .on('click', this.handleClick)

    let label = this.getLabel(name)
    if (model.isCollection) {
      const items = model.getItems()
      if (items.length > 0) {
        label = label + ' (' + items.length + ')'
        el.append(label)
      }
    } else {
      el.append(label)
    }

    return el
  }

  handleClick (e) {
    // NOTE: currently we are using link anchors for metadata panel navigation
    // this way we are preventing Electron from opening links in a new window
    // later we want to introduce different routing mechanism
    e.stopPropagation()
  }
}

import { ModelComponent } from '../../kit'

export default class MetaSectionTOCEntry extends ModelComponent {
  render ($$) {
    const id = this.props.id
    const name = this.props.name
    const model = this.props.model
    let el = $$('a').addClass('sc-meta-section-toc-entry')
      .attr({ href: '#' + id })
      .on('click', this.handleClick)

    let label = this.getLabel(name)
    if (model.isCollection) {
      const items = model.getItems()
      if (items.length > 0) {
        label = label + ' (' + items.length + ')'
        el.setStyle('display', 'block').append(label)
      } else {
        el.setStyle('display', 'none')
      }
    } else {
      el.append(label)
    }

    return el
  }

  handleClick (e) {
    // NOTE: currently we are using link anchors for metadata panel navigation
    // this way we are preventing Electron from opening links in a new window
    // later we want to introduce different routing mechanism
    e.stopPropagation()
  }
}

Hide empty TOC items in metadata editor.


import React from 'react';
import PropTypes from 'prop-types';

import { FlexColumn } from '../FlexColumn';
import { PageInfoRow } from './PageInfoRow';

export const PageInfoColumn = ({
  columnData,
  isEditingDisabled,
  titleColor,
  infoColor,
  numberOfLines,
  titleTextAlign,
}) => (
  <FlexColumn flex={1}>
    {columnData.map(({ onPress, editableType, title, info }) => (
      <PageInfoRow
        titleTextAlign={titleTextAlign}
        numberOfLines={numberOfLines}
        key={`${title}${info}`}
        isEditingDisabled={isEditingDisabled}
        titleColor={titleColor}
        infoColor={infoColor}
        onPress={onPress}
        editableType={editableType}
        info={info}
        title={title}
      />
    ))}
  </FlexColumn>
);

PageInfoColumn.defaultProps = {
  isEditingDisabled: false,
  numberOfLines: 1,
  titleTextAlign: 'left',
};

PageInfoColumn.propTypes = {
  // eslint-disable-next-line react/forbid-prop-types
  columnData: PropTypes.array.isRequired,
  isEditingDisabled: PropTypes.bool,
  titleColor: PropTypes.string.isRequired,
  infoColor: PropTypes.string.isRequired,
  numberOfLines: PropTypes.number,
  titleTextAlign: PropTypes.string,
};

import React from 'react';
import PropTypes from 'prop-types';

import { FlexColumn } from '../FlexColumn';
import { PageInfoRow } from './PageInfoRow';

export const PageInfoColumn = ({
  columnData,
  isEditingDisabled,
  titleColor,
  infoColor,
  numberOfLines,
  titleTextAlign,
}) => (
  <FlexColumn flex={1}>
    {columnData.map(({ onPress, editableType, title, info }, idx) => (
      <PageInfoRow
        titleTextAlign={titleTextAlign}
        numberOfLines={numberOfLines}
        // eslint-disable-next-line react/no-array-index-key
        key={`${title}${info}${idx}`}
        isEditingDisabled={isEditingDisabled}
        titleColor={titleColor}
        infoColor={infoColor}
        onPress={onPress}
        editableType={editableType}
        info={info}
        title={title}
      />
    ))}
  </FlexColumn>
);

PageInfoColumn.defaultProps = {
  isEditingDisabled: false,
  numberOfLines: 1,
  titleTextAlign: 'left',
};

PageInfoColumn.propTypes = {
  // eslint-disable-next-line react/forbid-prop-types
  columnData: PropTypes.array.isRequired,
  isEditingDisabled: PropTypes.bool,
  titleColor: PropTypes.string.isRequired,
  infoColor: PropTypes.string.isRequired,
  numberOfLines: PropTypes.number,
  titleTextAlign: PropTypes.string,
};

Add corrected key for pageinfocolumns


import Service from '@ember/service';
import { computed } from '@ember/object';
import ObjectProxy from '@ember/object/proxy';
import PromiseProxyMixin from '@ember/object/promise-proxy-mixin';
import { Promise, resolve } from 'rsvp';
import { getOwner } from '@ember/application';
import { bind } from '@ember/runloop';
import runloopifyGoogleMaps from '../utils/runloopify-google-maps';

let ObjectPromiseProxy = ObjectProxy.extend(PromiseProxyMixin);

export default Service.extend({
  google: computed(function() {
    return ObjectPromiseProxy.create({
      promise: this._loadMapsAPI()
    });
  }).readOnly(),

  /**
   * Return or load the Google Maps API.
   */
  _loadMapsAPI() {
    const google = window.google;
    if (google) { return resolve(google); }

    // Pre-built url set to environment variable.
    const ENV = getOwner(this).resolveRegistration('config:environment');
    const src = ENV['ember-google-maps']['src'];

    return new Promise((resolve, reject) => {
      window.initGoogleMap = bind(() => {
        runloopifyGoogleMaps();
        resolve(window.google);
      });

      let s = document.createElement('script');
      s.type = 'text/javascript';
      s.async = true;
      s.onerror = (error) => reject(error);
      // Insert into dom (to avoid cors problems)
      document.body.appendChild(s);

      // Load map
      s.src = `${src}&callback=initGoogleMap`;
    });
  }
});

import Service from '@ember/service';
import { computed } from '@ember/object';
import ObjectProxy from '@ember/object/proxy';
import PromiseProxyMixin from '@ember/object/promise-proxy-mixin';
import { Promise, reject, resolve } from 'rsvp';
import { getOwner } from '@ember/application';
import { bind } from '@ember/runloop';
import runloopifyGoogleMaps from '../utils/runloopify-google-maps';

let ObjectPromiseProxy = ObjectProxy.extend(PromiseProxyMixin);

export default Service.extend({
  google: computed(function() {
    return ObjectPromiseProxy.create({
      promise: this._loadMapsAPI()
    });
  }).readOnly(),

  /**
   * Return or load the Google Maps API.
   */
  _loadMapsAPI() {
    if (typeof document === 'undefined') { return reject(); }

    const google = window.google;
    if (google) { return resolve(google); }

    // Pre-built url set to environment variable.
    const ENV = getOwner(this).resolveRegistration('config:environment');
    const src = ENV['ember-google-maps']['src'];

    return new Promise((resolve, reject) => {
      window.initGoogleMap = bind(() => {
        runloopifyGoogleMaps();
        resolve(window.google);
      });

      let s = document.createElement('script');
      s.type = 'text/javascript';
      s.async = true;
      s.onerror = (error) => reject(error);
      // Insert into dom (to avoid cors problems)
      document.body.appendChild(s);

      // Load map
      s.src = `${src}&callback=initGoogleMap`;
    });
  }
});

Add extra FastBoot protection when loading the sdk


export function jfVScrollElement($compile) {
    return {
        restrict: 'E',
        scope: {
            data: '=',
            template: '=',
            variable: '=',
            index: '=',
            vscroll: '='
        },
        template: '

',
        controller: jfVScrollElementController,
        controllerAs: 'jfVScrollElement',
        bindToController: true,
        link: function(scope, element, attrs, directiveCtrl) {

            let elementScope = scope.$parent.$parent.$parent.$new();
            let target = $(element).find('.compile-placeholder');

            elementScope[directiveCtrl.variable] = directiveCtrl.data;
            elementScope['$index'] = directiveCtrl.index;

            directiveCtrl.elementScope = elementScope;
            let compiled = $compile($(`<div>${directiveCtrl.template}</div>`))(elementScope);
            target.replaceWith(compiled);

        }
    }
}

class jfVScrollElementController {
    constructor($scope, $element, $timeout) {

        $timeout(() => {
            let height = $($element).children().height();
            if (height) this.vscroll.setItemHeight(height);
        });

        $scope.$watch('jfVScrollElement.data', () => {
            this.elementScope[this.variable] = this.data;
        })
    }
}

export function jfVScrollElement($compile) {
    return {
        restrict: 'E',
        scope: {
            data: '=',
            template: '=',
            variable: '=',
            index: '=',
            vscroll: '='
        },
        template: '

',
        controller: jfVScrollElementController,
        controllerAs: 'jfVScrollElement',
        bindToController: true,
        link: function(scope, element, attrs, directiveCtrl) {

            let elementScope = scope.$parent.$parent.$parent.$new();
            let target = $(element).find('.compile-placeholder');

            elementScope[directiveCtrl.variable] = directiveCtrl.data;
            elementScope['$index'] = directiveCtrl.index;

            directiveCtrl.elementScope = elementScope;
            let compiled = $compile($(`<div>${directiveCtrl.template}</div>`))(elementScope);
            target.replaceWith(compiled);

        }
    }
}

class jfVScrollElementController {
    constructor($scope, $element, $timeout) {

        this.$element = $element;

        let unwatchHeight = $scope.$watch('jfVScrollElement.childrenHeight', () => {
            if (this.childrenHeight) {
                this.vscroll.setItemHeight(this.childrenHeight);
                unwatchHeight();
            }
        })

        $scope.$watch('jfVScrollElement.data', () => {
            this.elementScope[this.variable] = this.data;
        })
    }

    get childrenHeight() {
        return $(this.$element).children().height();
    }
}

Use one time watch, instead of timeout to init element height init


import React from "react"
import {TradingView} from "../Market"

export default class LimitOrderChart extends React.Component {
  render() {
    return (
      <TradingView />
    )
  }
}

import React from "react"
import {TradingView} from "../Market"

export default class LimitOrderChart extends React.Component {
  render() {
    return (
      <div style={{width: '100%', height: '650px'}}>
        {/*<TradingView />*/}
      </div>
    )
  }
}

Add a place holder for chart


import { Sparkline } from 'lib/visualizations'

window.GobiertoCharters.ChartersController = (function() {

  function ChartersController() {}

  ChartersController.prototype.show = function(opts){

    // DEBUG: Esta funcion DEBE ser eliminada cuando se obtengan datos verdaderos
    function mock(length = 2) {
      let json = [];
      for (var i = 0; i < length; i++) {
        json.push({
          date: new Date().getFullYear() - i,
          value: Math.floor(Math.random() * Math.floor(100))
        })
      }
      return json
    }

    const $sparklines = $('.sparkline')

    $sparklines.each((i, container) => {
      let options = {
        axes: true,
        aspectRatio: 3,
        margins: {
          top: 0,
          bottom: 0,
          left: 0,
          right: 0
        },
        freq: opts.freq
      }

      if (opts.sparklinesData[container.id] && opts.sparklinesData[container.id].length > 1 && $(`#${container.id} svg`).length === 0) {
        let chart = new Sparkline(`#${container.id}`, opts.sparklinesData[container.id], options)
        chart.render()
      }
    })
  };

  return ChartersController;
})();

window.GobiertoCharters.charters_controller = new GobiertoCharters.ChartersController;

import { Sparkline } from 'lib/visualizations'

window.GobiertoCharters.ChartersController = (function() {

  function ChartersController() {}

  ChartersController.prototype.show = function(opts){

    // DEBUG: Esta funcion DEBE ser eliminada cuando se obtengan datos verdaderos
    function mock(length = 2) {
      let json = [];
      for (var i = 0; i < length; i++) {
        json.push({
          date: new Date().getFullYear() - i,
          value: Math.floor(Math.random() * Math.floor(100))
        })
      }
      return json
    }

    function _parseNumericStrings(data) {
      for (let i in data) {
        data[i].value = parseFloat(data[i].value)
      }
      return data
    }

    const $sparklines = $('.sparkline')

    $sparklines.each((i, container) => {
      let options = {
        axes: true,
        aspectRatio: 3,
        margins: {
          top: 0,
          bottom: 0,
          left: 0,
          right: 0
        },
        freq: opts.freq
      }

      if (opts.sparklinesData[container.id] && opts.sparklinesData[container.id].length > 1 && $(`#${container.id} svg`).length === 0) {
        let chart = new Sparkline(`#${container.id}`, _parseNumericStrings(opts.sparklinesData[container.id]), options)
        chart.render()
      }
    })
  };

  return ChartersController;
})();

window.GobiertoCharters.charters_controller = new GobiertoCharters.ChartersController;

Convert data to float in sparklines


module.exports = function(server) {
	//This route is added for the server health monitoring
	server.use('/ping', function(req, res, next) {
	  res.send('pong');
	});
};

module.exports = function(server) {
	//This route is added for the server health monitoring
	server.use('/ping', function(req, res, next) {
	  res.send('Hello from MedCheck!!!');
	});
};

Change the ping service message