<commit_before>(function() {
    "use strict";

    var fn = timbre.fn;
    var timevalue  = timbre.timevalue;
    var Compressor = timbre.modules.Compressor;

    function CompressorNode(_args) {
        timbre.Object.call(this, _args);
        fn.fixAR(this);

        var _ = this._;
        _.thresh = timbre(-24);
        _.knee   = timbre(30);
        _.ratio  = timbre(12);
        _.postGain  =   6;
        _.attack    =   3;
        _.release   = 250;
        _.reduction =   0;

        _.comp = new Compressor(timbre.samplerate);
        _.comp.dbPostGain  = _.postGain;
        _.comp.attackTime  = _.attack  * 0.001;
        _.comp.releaseTime = _.release * 0.001;
    }
    fn.extend(CompressorNode);

    var $ = CompressorNode.prototype;

    Object.defineProperties($, {
        thresh: {
            set: function(value) {
                this._.thresh = timbre(value);
            },
            get: function() {
                return this._.thresh;
            }
        },
        knee: {
            set: function(value) {
                this._.kne = timbre(value);
            },
            get: function() {
                return this._.knee;
            }
        },
        ratio: {
            set: function(value) {
                this._.ratio = timbre(value);
            },
            get: function() {
                return this._.ratio;
            }
        },
        postGain: {
            set: function(value) {
                if (typeof value === "number") {
                    this._.postGain = value;
                    this._.comp.dbPostGain = value;
                }
            },
            get: function() {
                return this._.postGain;
            }
        },
        attack: {
            set: function(value) {
                if (typeof value === "string") {
                    value = timevalue(value);
                }
                if (typeof value === "number") {
                    value = (value < 0) ? 0 : (1000 < value) ? 1000 : value;
                    this._.attack = value;
                    this._.comp.attackTime = value * 0.001;
                }
            },
            get: function() {
                return this._.attack;
            }
        },
        release: {
            set: function(value) {
                if (typeof value === "string") {
                    value = timevalue(value);
                    if (value <= 0) {
                        value = 0;
                    }
                }
                if (typeof value === "number") {
                    value = (value < 0) ? 0 : (1000 < value) ? 1000 : value;
                    this._.release = value;
                    this._.comp.releaseTime = value * 0.001;
                }
            },
            get: function() {
                return this._.release;
            }
        },
        reduction: {
            get: function() {
                return this._.reduction;
            }
        }
    });

    $.process = function(tickID) {
        var _ = this._;
        var cell = this.cell;

        if (this.tickID !== tickID) {
            this.tickID = tickID;

            fn.inputSignalAR(this);

            var thresh = _.thresh.process(tickID)[0];
            var knee   = _.knee.process(tickID)[0];
            var ratio  = _.ratio.process(tickID)[0];

            _.comp.process(cell, cell, thresh, knee, ratio);

            _.reduction = _.comp.meteringGain;

            fn.outputSignalAR(this);
        }

        return cell;
    };

    fn.register("comp", CompressorNode);

})();
<commit_msg>add an alias T("compressor")<commit_after>(function() {
    "use strict";

    var fn = timbre.fn;
    var timevalue  = timbre.timevalue;
    var Compressor = timbre.modules.Compressor;

    function CompressorNode(_args) {
        timbre.Object.call(this, _args);
        fn.fixAR(this);

        var _ = this._;
        _.thresh = timbre(-24);
        _.knee   = timbre(30);
        _.ratio  = timbre(12);
        _.postGain  =   6;
        _.attack    =   3;
        _.release   = 250;
        _.reduction =   0;

        _.comp = new Compressor(timbre.samplerate);
        _.comp.dbPostGain  = _.postGain;
        _.comp.attackTime  = _.attack  * 0.001;
        _.comp.releaseTime = _.release * 0.001;
    }
    fn.extend(CompressorNode);

    var $ = CompressorNode.prototype;

    Object.defineProperties($, {
        thresh: {
            set: function(value) {
                this._.thresh = timbre(value);
            },
            get: function() {
                return this._.thresh;
            }
        },
        knee: {
            set: function(value) {
                this._.kne = timbre(value);
            },
            get: function() {
                return this._.knee;
            }
        },
        ratio: {
            set: function(value) {
                this._.ratio = timbre(value);
            },
            get: function() {
                return this._.ratio;
            }
        },
        postGain: {
            set: function(value) {
                if (typeof value === "number") {
                    this._.postGain = value;
                    this._.comp.dbPostGain = value;
                }
            },
            get: function() {
                return this._.postGain;
            }
        },
        attack: {
            set: function(value) {
                if (typeof value === "string") {
                    value = timevalue(value);
                }
                if (typeof value === "number") {
                    value = (value < 0) ? 0 : (1000 < value) ? 1000 : value;
                    this._.attack = value;
                    this._.comp.attackTime = value * 0.001;
                }
            },
            get: function() {
                return this._.attack;
            }
        },
        release: {
            set: function(value) {
                if (typeof value === "string") {
                    value = timevalue(value);
                    if (value <= 0) {
                        value = 0;
                    }
                }
                if (typeof value === "number") {
                    value = (value < 0) ? 0 : (1000 < value) ? 1000 : value;
                    this._.release = value;
                    this._.comp.releaseTime = value * 0.001;
                }
            },
            get: function() {
                return this._.release;
            }
        },
        reduction: {
            get: function() {
                return this._.reduction;
            }
        }
    });

    $.process = function(tickID) {
        var _ = this._;
        var cell = this.cell;

        if (this.tickID !== tickID) {
            this.tickID = tickID;

            fn.inputSignalAR(this);

            var thresh = _.thresh.process(tickID)[0];
            var knee   = _.knee.process(tickID)[0];
            var ratio  = _.ratio.process(tickID)[0];

            _.comp.process(cell, cell, thresh, knee, ratio);

            _.reduction = _.comp.meteringGain;

            fn.outputSignalAR(this);
        }

        return cell;
    };

    fn.register("comp", CompressorNode);
    fn.alias("compressor", "comp");

})();
<|endoftext|>


<commit_before>// @flow
import queryString from 'query-string';

import type { Config } from '../Domain/Config';
import type { EventList } from '../Domain/Event';
import type { Trigger, TriggerList, TriggerState } from '../Domain/Trigger';
import type { Settings } from '../Domain/Settings';
import type { TagStat } from '../Domain/Tag';
import type { PatternList } from '../Domain/Pattern';
import type { NotificationList } from '../Domain/Notification';

export type TagList = {|
    list: Array<string>;
|};

export type TagStatList = {|
    list: Array<TagStat>;
|};

export interface IMoiraApi {
    getPatternList(): Promise<PatternList>;
    getTagList(): Promise<TagList>;
    getTagStats(): Promise<TagStatList>;
    getSettings(): Promise<Settings>;
    getTriggerList(page: number, onlyProblems: boolean, tags: Array<string>): Promise<TriggerList>;
    getTrigger(id: string): Promise<Trigger>;
    setMaintenance(triggerId: string, data: { [metric: string]: number }): Promise<void>;
    getTriggerState(id: string): Promise<TriggerState>;
    getTriggerEvents(id: string, page: number): Promise<EventList>;
    delThrottling(triggerId: string): Promise<void>;
    delMetric(triggerId: string, metric: string): Promise<void>;
    getNotificationList(): Promise<NotificationList>;
}

export default class Api implements IMoiraApi {
    config: Config;

    constructor(config: Config) {
        this.config = config;
    }

    async getPatternList(): Promise<PatternList> {
        const url = this.config.apiUrl + '/pattern';
        const response = await fetch(url, { method: 'GET' });
        if (response.status === 200) {
            return response.json();
        }
        throw new Error(response);
    }

    async getTagList(): Promise<TagList> {
        const url = this.config.apiUrl + '/tag';
        const response = await fetch(url, { method: 'GET' });
        if (response.status === 200) {
            return response.json();
        }
        throw new Error(response);
    }

    async getTagStats(): Promise<TagStatList> {
        const url = this.config.apiUrl + '/tag/stats';
        const response = await fetch(url, { method: 'GET' });
        if (response.status === 200) {
            return response.json();
        }
        throw new Error(response);
    }

    async getSettings(): Promise<Settings> {
        const url = this.config.apiUrl + '/user/settings';
        const response = await fetch(url, {
            method: 'GET',
        });
        if (response.status === 200) {
            return response.json();
        }
        throw new Error(response);
    }

    async getTriggerList(page: number, onlyProblems: boolean, tags: Array<string>): Promise<TriggerList> {
        const url =
            this.config.apiUrl +
            '/trigger/page?' +
            queryString.stringify(
                {
                    /* eslint-disable */
                    p: page,
                    /* eslint-enable */
                    size: this.config.paging.triggerList,
                    tags,
                    onlyProblems,
                },
                { arrayFormat: 'index', encode: true }
            );
        const response = await fetch(url, { method: 'GET' });
        if (response.status === 200) {
            return response.json();
        }
        throw new Error(response);
    }

    async getTrigger(id: string): Promise<Trigger> {
        const url = `${this.config.apiUrl}/trigger/${id}`;
        const response = await fetch(url, { method: 'GET' });
        if (response.status === 200) {
            return response.json();
        }
        throw new Error(response);
    }

    async setMaintenance(triggerId: string, data: { [metric: string]: number }): Promise<void> {
        const url = `${this.config.apiUrl}/trigger/${triggerId}/maintenance`;
        const response = await fetch(url, {
            method: 'PUT',
            body: JSON.stringify(data),
        });
        if (response.status === 200) {
            return;
        }
        throw new Error(response);
    }

    async getTriggerState(id: string): Promise<TriggerState> {
        const url = `${this.config.apiUrl}/trigger/${id}/state`;
        const response = await fetch(url, { method: 'GET' });
        if (response.status === 200) {
            return response.json();
        }
        throw new Error(response);
    }

    async getTriggerEvents(id: string, page: number): Promise<EventList> {
        const url = `${this.config.apiUrl}/event/${id}?p=${page}&size=${this.config.paging.eventHistory}`;
        const response = await fetch(url, { method: 'GET' });
        if (response.status === 200) {
            return response.json();
        }
        throw new Error(response);
    }

    async delThrottling(triggerId: string): Promise<void> {
        const url = `${this.config.apiUrl}/trigger/${triggerId}/throttling`;
        const response = await fetch(url, { method: 'DELETE' });
        if (response.status === 200) {
            return;
        }
        throw new Error(response);
    }

    async delMetric(triggerId: string, metric: string): Promise<void> {
        const url = `${this.config.apiUrl}/trigger/${triggerId}/metrics?name=${metric}`;
        const response = await fetch(url, { method: 'DELETE' });
        if (response.status === 200) {
            return;
        }
        throw new Error(response);
    }

    async getNotificationList(): Promise<NotificationList> {
        const url = this.config.apiUrl + '/notification?start=0&end=-1';
        const response = await fetch(url, { method: 'GET' });
        if (response.status === 200) {
            return response.json();
        }
        throw new Error(response);
    }
}
<commit_msg>Add notification remove method<commit_after>// @flow
import queryString from 'query-string';

import type { Config } from '../Domain/Config';
import type { EventList } from '../Domain/Event';
import type { Trigger, TriggerList, TriggerState } from '../Domain/Trigger';
import type { Settings } from '../Domain/Settings';
import type { TagStat } from '../Domain/Tag';
import type { PatternList } from '../Domain/Pattern';
import type { NotificationList } from '../Domain/Notification';

export type TagList = {|
    list: Array<string>;
|};

export type TagStatList = {|
    list: Array<TagStat>;
|};

export interface IMoiraApi {
    getPatternList(): Promise<PatternList>;
    getTagList(): Promise<TagList>;
    getTagStats(): Promise<TagStatList>;
    getSettings(): Promise<Settings>;
    getTriggerList(page: number, onlyProblems: boolean, tags: Array<string>): Promise<TriggerList>;
    getTrigger(id: string): Promise<Trigger>;
    setMaintenance(triggerId: string, data: { [metric: string]: number }): Promise<void>;
    getTriggerState(id: string): Promise<TriggerState>;
    getTriggerEvents(id: string, page: number): Promise<EventList>;
    delThrottling(triggerId: string): Promise<void>;
    delMetric(triggerId: string, metric: string): Promise<void>;
    getNotificationList(): Promise<NotificationList>;
    deltNotification(id: string): Promise<void>;
}

export default class Api implements IMoiraApi {
    config: Config;

    constructor(config: Config) {
        this.config = config;
    }

    async getPatternList(): Promise<PatternList> {
        const url = this.config.apiUrl + '/pattern';
        const response = await fetch(url, { method: 'GET' });
        if (response.status === 200) {
            return response.json();
        }
        throw new Error(response);
    }

    async getTagList(): Promise<TagList> {
        const url = this.config.apiUrl + '/tag';
        const response = await fetch(url, { method: 'GET' });
        if (response.status === 200) {
            return response.json();
        }
        throw new Error(response);
    }

    async getTagStats(): Promise<TagStatList> {
        const url = this.config.apiUrl + '/tag/stats';
        const response = await fetch(url, { method: 'GET' });
        if (response.status === 200) {
            return response.json();
        }
        throw new Error(response);
    }

    async getSettings(): Promise<Settings> {
        const url = this.config.apiUrl + '/user/settings';
        const response = await fetch(url, {
            method: 'GET',
        });
        if (response.status === 200) {
            return response.json();
        }
        throw new Error(response);
    }

    async getTriggerList(page: number, onlyProblems: boolean, tags: Array<string>): Promise<TriggerList> {
        const url =
            this.config.apiUrl +
            '/trigger/page?' +
            queryString.stringify(
                {
                    /* eslint-disable */
                    p: page,
                    /* eslint-enable */
                    size: this.config.paging.triggerList,
                    tags,
                    onlyProblems,
                },
                { arrayFormat: 'index', encode: true }
            );
        const response = await fetch(url, { method: 'GET' });
        if (response.status === 200) {
            return response.json();
        }
        throw new Error(response);
    }

    async getTrigger(id: string): Promise<Trigger> {
        const url = `${this.config.apiUrl}/trigger/${id}`;
        const response = await fetch(url, { method: 'GET' });
        if (response.status === 200) {
            return response.json();
        }
        throw new Error(response);
    }

    async setMaintenance(triggerId: string, data: { [metric: string]: number }): Promise<void> {
        const url = `${this.config.apiUrl}/trigger/${triggerId}/maintenance`;
        const response = await fetch(url, {
            method: 'PUT',
            body: JSON.stringify(data),
        });
        if (response.status === 200) {
            return;
        }
        throw new Error(response);
    }

    async getTriggerState(id: string): Promise<TriggerState> {
        const url = `${this.config.apiUrl}/trigger/${id}/state`;
        const response = await fetch(url, { method: 'GET' });
        if (response.status === 200) {
            return response.json();
        }
        throw new Error(response);
    }

    async getTriggerEvents(id: string, page: number): Promise<EventList> {
        const url = `${this.config.apiUrl}/event/${id}?p=${page}&size=${this.config.paging.eventHistory}`;
        const response = await fetch(url, { method: 'GET' });
        if (response.status === 200) {
            return response.json();
        }
        throw new Error(response);
    }

    async delThrottling(triggerId: string): Promise<void> {
        const url = `${this.config.apiUrl}/trigger/${triggerId}/throttling`;
        const response = await fetch(url, { method: 'DELETE' });
        if (response.status === 200) {
            return;
        }
        throw new Error(response);
    }

    async delMetric(triggerId: string, metric: string): Promise<void> {
        const url = `${this.config.apiUrl}/trigger/${triggerId}/metrics?name=${metric}`;
        const response = await fetch(url, { method: 'DELETE' });
        if (response.status === 200) {
            return;
        }
        throw new Error(response);
    }

    async getNotificationList(): Promise<NotificationList> {
        const url = this.config.apiUrl + '/notification?start=0&end=-1';
        const response = await fetch(url, { method: 'GET' });
        if (response.status === 200) {
            return response.json();
        }
        throw new Error(response);
    }
    async deltNotification(id: string): Promise<void> {
        const url = this.config.apiUrl + '/notification?id=' + id;
        const response = await fetch(url, { method: 'DELETE' });
        if (response.status === 200) {
            return;
        }
        throw new Error(response);
    }
}
<|endoftext|>


<commit_before>// a cache implementation able to produce an ActualizedCache
// ... assuming for now each param is a primitive with meaningful toString()
// (i.e.: strings, numbers and bools)

import t from 'tcomb';
import debug from 'debug';

const log = debug('AvengerCache');

const AllowedParam = t.subtype(t.Any, p => {
  return t.Str.is(p) || t.Num.is(p) || t.Bool.is(p);
}, 'AllowedParam');
const AllowedParams = t.dict(t.Str, t.dict(t.Str, AllowedParam, 'AllowedParams'));

export function hashedParams(params) {
  if (process.env.NODE_ENV !== 'production') {
    AllowedParams(params);
  }
  const keys = Object.keys(params);
  keys.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
  const hashed = keys.map((k1) => {
    const childkeys = Object.keys(params[k1]);
    childkeys.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
    return childkeys.map(k2 => `${k1}.${k2}:${params[k1][k2]}`).join('-');
  }).join('-');
  return hashed ? hashed : '∅';
}

export default class AvengerCache {

  constructor(initialState = {}) {
    this.state = initialState;
  }

  toJSON() {
    return this.state;
  }

  checkParams(params) {
    return AllowedParams.is(params);
  }

  get(id, params) {
    if (process.env.NODE_ENV !== 'production') {
      this.checkParams(params);
    }

    if (!this.state[id]) {
      return null;
    }

    return this.state[id][hashedParams(params)] || null;
  }

  set = (id, params) => value => {
    const hp = hashedParams(params);
    log(`set ${id} %o = %o`, params, value);
    log(`current ${id} %o, ${id}[${hp}] (missing id: ${!this.state[id]})`, this.state[id], this.state[id] ? this.state[id][hp] : undefined);
    if (process.env.NODE_ENV !== 'production') {
      this.checkParams(params);
    }

    if (!this.state.id) {
      this.state[id] = {};
    }

    this.state[id][hp] = value;
  };
}
<commit_msg>fix wrong check in AvengerCache.set()<commit_after>// a cache implementation able to produce an ActualizedCache
// ... assuming for now each param is a primitive with meaningful toString()
// (i.e.: strings, numbers and bools)

import t from 'tcomb';
import debug from 'debug';

const log = debug('AvengerCache');

const AllowedParam = t.subtype(t.Any, p => {
  return t.Str.is(p) || t.Num.is(p) || t.Bool.is(p);
}, 'AllowedParam');
const AllowedParams = t.dict(t.Str, t.dict(t.Str, AllowedParam, 'AllowedParams'));

export function hashedParams(params) {
  if (process.env.NODE_ENV !== 'production') {
    AllowedParams(params);
  }
  const keys = Object.keys(params);
  keys.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
  const hashed = keys.map((k1) => {
    const childkeys = Object.keys(params[k1]);
    childkeys.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
    return childkeys.map(k2 => `${k1}.${k2}:${params[k1][k2]}`).join('-');
  }).join('-');
  return hashed ? hashed : '∅';
}

export default class AvengerCache {

  constructor(initialState = {}) {
    this.state = initialState;
  }

  toJSON() {
    return this.state;
  }

  checkParams(params) {
    return AllowedParams.is(params);
  }

  get(id, params) {
    if (process.env.NODE_ENV !== 'production') {
      this.checkParams(params);
    }

    if (!this.state[id]) {
      return null;
    }

    return this.state[id][hashedParams(params)] || null;
  }

  set = (id, params) => value => {
    const hp = hashedParams(params);
    log(`set ${id} ${hp} = %o`, params, value);
    log(`current ${id} %o, ${id}[${hp}] (missing id: ${!this.state[id]})`, this.state[id], this.state[id] ? this.state[id][hp] : undefined);
    if (process.env.NODE_ENV !== 'production') {
      this.checkParams(params);
    }

    if (!this.state[id]) {
      this.state[id] = {};
    }

    this.state[id][hp] = value;
  };
}
<|endoftext|>


<commit_before>// @flow

/*
Copyright 2016 Aviral Dasgupta
Copyright 2016 OpenMarket Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import dis from 'dispatcher';

/**
 * Base class for classes that provide platform-specific functionality
 * eg. Setting an application badge or displaying notifications
 *
 * Instances of this class are provided by the application.
 */
export default class BasePlatform {
    constructor() {
        this.notificationCount = 0;
        this.errorDidOccur = false;

        dis.register(this._onAction.bind(this));
    }

    _onAction(payload: Object) {
        switch (payload.action) {
            case 'on_logged_out':
                this.setNotificationCount(0);
                break;
        }
    }

    // Used primarily for Analytics
    getHumanReadableName(): string {
        return 'Base Platform';
    }

    setNotificationCount(count: number) {
        this.notificationCount = count;
    }

    setErrorStatus(errorDidOccur: boolean) {
        this.errorDidOccur = errorDidOccur;
    }

    /**
     * Returns true if the platform supports displaying
     * notifications, otherwise false.
     */
    supportsNotifications(): boolean {
        return false;
    }

    /**
     * Returns true if the application currently has permission
     * to display notifications. Otherwise false.
     */
    maySendNotifications(): boolean {
        return false;
    }

    /**
     * Requests permission to send notifications. Returns
     * a promise that is resolved when the user has responded
     * to the request. The promise has a single string argument
     * that is 'granted' if the user allowed the request or
     * 'denied' otherwise.
     */
    requestNotificationPermission(): Promise<string> {
    }

    displayNotification(title: string, msg: string, avatarUrl: string, room: Object) {
    }

    loudNotification(ev: Event, room: Object) {
    }

    /**
     * Returns a promise that resolves to a string representing
     * the current version of the application.
     */
    getAppVersion(): Promise<string> {
        throw new Error("getAppVersion not implemented!");
    }

    /*
     * If it's not expected that capturing the screen will work
     * with getUserMedia, return a string explaining why not.
     * Otherwise, return null.
     */
    screenCaptureErrorString(): string {
        return "Not implemented";
    }

    isElectron(): boolean { return false; }

    /**
     * Restarts the application, without neccessarily reloading
     * any application code
     */
    reload() {
        throw new Error("reload not implemented!");
    }
}
<commit_msg>fix ambiguity<commit_after>// @flow

/*
Copyright 2016 Aviral Dasgupta
Copyright 2016 OpenMarket Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import dis from './dispatcher';

/**
 * Base class for classes that provide platform-specific functionality
 * eg. Setting an application badge or displaying notifications
 *
 * Instances of this class are provided by the application.
 */
export default class BasePlatform {
    constructor() {
        this.notificationCount = 0;
        this.errorDidOccur = false;

        dis.register(this._onAction.bind(this));
    }

    _onAction(payload: Object) {
        switch (payload.action) {
            case 'on_logged_out':
                this.setNotificationCount(0);
                break;
        }
    }

    // Used primarily for Analytics
    getHumanReadableName(): string {
        return 'Base Platform';
    }

    setNotificationCount(count: number) {
        this.notificationCount = count;
    }

    setErrorStatus(errorDidOccur: boolean) {
        this.errorDidOccur = errorDidOccur;
    }

    /**
     * Returns true if the platform supports displaying
     * notifications, otherwise false.
     */
    supportsNotifications(): boolean {
        return false;
    }

    /**
     * Returns true if the application currently has permission
     * to display notifications. Otherwise false.
     */
    maySendNotifications(): boolean {
        return false;
    }

    /**
     * Requests permission to send notifications. Returns
     * a promise that is resolved when the user has responded
     * to the request. The promise has a single string argument
     * that is 'granted' if the user allowed the request or
     * 'denied' otherwise.
     */
    requestNotificationPermission(): Promise<string> {
    }

    displayNotification(title: string, msg: string, avatarUrl: string, room: Object) {
    }

    loudNotification(ev: Event, room: Object) {
    }

    /**
     * Returns a promise that resolves to a string representing
     * the current version of the application.
     */
    getAppVersion(): Promise<string> {
        throw new Error("getAppVersion not implemented!");
    }

    /*
     * If it's not expected that capturing the screen will work
     * with getUserMedia, return a string explaining why not.
     * Otherwise, return null.
     */
    screenCaptureErrorString(): string {
        return "Not implemented";
    }

    isElectron(): boolean { return false; }

    /**
     * Restarts the application, without neccessarily reloading
     * any application code
     */
    reload() {
        throw new Error("reload not implemented!");
    }
}
<|endoftext|>


<commit_before>goog.provide('og.math');

og.math.GLArray = typeof Float32Array != "undefined" ? Float32Array : typeof WebGLFloatArray != "undefined" ? WebGLFloatArray : Array;

og.math.X = 0;
og.math.Y = 1;
og.math.Z = 2;
og.math.W = 3;

og.math.LOG2 = Math.log(2);

og.math.MAX = 100000000;
og.math.MIN = -100000000;

og.math.RADIANS = Math.PI / 180;
og.math.DEGREES = 180 / Math.PI;

//Float round-off PI
Math.PI = 3.1415927410125732;

og.math.clamp = function (number, min, max) {
    return Math.max(min, Math.min(number, max));
};

og.math.DEG2RAD = function (degrees) {
    return degrees * og.math.RADIANS;
};

og.math.RAD2DEG = function (angle) {
    return angle * og.math.DEGREES;
};

og.math.isPowerOfTwo = function (x) {
    return (x & (x - 1)) == 0;
};

og.math.nextHighestPowerOfTwo = function (x) {
    --x;
    for (var i = 1; i < 32; i <<= 1) {
        x = x | x >> i;
    }
    return x + 1;
};

og.math.mod = function(x, y) {
	return x - y * Math.floor(x / y);
};

og.math.step = function(edge, x) {
	return x < edge ? 0.0 : 1.0;
};

og.math.frac = function(v) {
	return v - floor(v);
};

og.math.log2 = function(x) {
	return Math.log(x) / og.math.LOG2;
};

og.math.exp2 = function(x) {
	return Math.pow(2, x);
};<commit_msg>Max double precision.<commit_after>goog.provide('og.math');

og.math.GLArray = typeof Float32Array != "undefined" ? Float32Array : typeof WebGLFloatArray != "undefined" ? WebGLFloatArray : Array;

og.math.X = 0;
og.math.Y = 1;
og.math.Z = 2;
og.math.W = 3;

og.math.LOG2 = Math.log(2);

og.math.MAX = 549755748352;
og.math.MIN = -og.math.MAX;

og.math.RADIANS = Math.PI / 180;
og.math.DEGREES = 180 / Math.PI;

//Float round-off PI
Math.PI = 3.1415927410125732;

og.math.clamp = function (number, min, max) {
    return Math.max(min, Math.min(number, max));
};

og.math.DEG2RAD = function (degrees) {
    return degrees * og.math.RADIANS;
};

og.math.RAD2DEG = function (angle) {
    return angle * og.math.DEGREES;
};

og.math.isPowerOfTwo = function (x) {
    return (x & (x - 1)) == 0;
};

og.math.nextHighestPowerOfTwo = function (x) {
    --x;
    for (var i = 1; i < 32; i <<= 1) {
        x = x | x >> i;
    }
    return x + 1;
};

og.math.mod = function(x, y) {
	return x - y * Math.floor(x / y);
};

og.math.step = function(edge, x) {
	return x < edge ? 0.0 : 1.0;
};

og.math.frac = function(v) {
	return v - floor(v);
};

og.math.log2 = function(x) {
	return Math.log(x) / og.math.LOG2;
};

og.math.exp2 = function(x) {
	return Math.pow(2, x);
};<|endoftext|>


<commit_before>/**
 * @module ol/transform
 */
import {assert} from './asserts.js';

/**
 * An array representing an affine 2d transformation for use with
 * {@link module:ol/transform} functions. The array has 6 elements.
 * @typedef {!Array<number>} Transform
 */

/**
 * Collection of affine 2d transformation functions. The functions work on an
 * array of 6 elements. The element order is compatible with the [SVGMatrix
 * interface](https://developer.mozilla.org/en-US/docs/Web/API/SVGMatrix) and is
 * a subset (elements a to f) of a 3×3 matrix:
 * ```
 * [ a c e ]
 * [ b d f ]
 * [ 0 0 1 ]
 * ```
 */

/**
 * @private
 * @type {Transform}
 */
const tmp_ = new Array(6);

/**
 * Create an identity transform.
 * @return {!Transform} Identity transform.
 */
export function create() {
  return [1, 0, 0, 1, 0, 0];
}

/**
 * Resets the given transform to an identity transform.
 * @param {!Transform} transform Transform.
 * @return {!Transform} Transform.
 */
export function reset(transform) {
  return set(transform, 1, 0, 0, 1, 0, 0);
}

/**
 * Multiply the underlying matrices of two transforms and return the result in
 * the first transform.
 * @param {!Transform} transform1 Transform parameters of matrix 1.
 * @param {!Transform} transform2 Transform parameters of matrix 2.
 * @return {!Transform} transform1 multiplied with transform2.
 */
export function multiply(transform1, transform2) {
  const a1 = transform1[0];
  const b1 = transform1[1];
  const c1 = transform1[2];
  const d1 = transform1[3];
  const e1 = transform1[4];
  const f1 = transform1[5];
  const a2 = transform2[0];
  const b2 = transform2[1];
  const c2 = transform2[2];
  const d2 = transform2[3];
  const e2 = transform2[4];
  const f2 = transform2[5];

  transform1[0] = a1 * a2 + c1 * b2;
  transform1[1] = b1 * a2 + d1 * b2;
  transform1[2] = a1 * c2 + c1 * d2;
  transform1[3] = b1 * c2 + d1 * d2;
  transform1[4] = a1 * e2 + c1 * f2 + e1;
  transform1[5] = b1 * e2 + d1 * f2 + f1;

  return transform1;
}

/**
 * Set the transform components a-f on a given transform.
 * @param {!Transform} transform Transform.
 * @param {number} a The a component of the transform.
 * @param {number} b The b component of the transform.
 * @param {number} c The c component of the transform.
 * @param {number} d The d component of the transform.
 * @param {number} e The e component of the transform.
 * @param {number} f The f component of the transform.
 * @return {!Transform} Matrix with transform applied.
 */
export function set(transform, a, b, c, d, e, f) {
  transform[0] = a;
  transform[1] = b;
  transform[2] = c;
  transform[3] = d;
  transform[4] = e;
  transform[5] = f;
  return transform;
}

/**
 * Set transform on one matrix from another matrix.
 * @param {!Transform} transform1 Matrix to set transform to.
 * @param {!Transform} transform2 Matrix to set transform from.
 * @return {!Transform} transform1 with transform from transform2 applied.
 */
export function setFromArray(transform1, transform2) {
  transform1[0] = transform2[0];
  transform1[1] = transform2[1];
  transform1[2] = transform2[2];
  transform1[3] = transform2[3];
  transform1[4] = transform2[4];
  transform1[5] = transform2[5];
  return transform1;
}

/**
 * Transforms the given coordinate with the given transform returning the
 * resulting, transformed coordinate. The coordinate will be modified in-place.
 *
 * @param {Transform} transform The transformation.
 * @param {import("./coordinate.js").Coordinate|import("./pixel.js").Pixel} coordinate The coordinate to transform.
 * @return {import("./coordinate.js").Coordinate|import("./pixel.js").Pixel} return coordinate so that operations can be
 *     chained together.
 */
export function apply(transform, coordinate) {
  const x = coordinate[0];
  const y = coordinate[1];
  coordinate[0] = transform[0] * x + transform[2] * y + transform[4];
  coordinate[1] = transform[1] * x + transform[3] * y + transform[5];
  return coordinate;
}

/**
 * Applies rotation to the given transform.
 * @param {!Transform} transform Transform.
 * @param {number} angle Angle in radians.
 * @return {!Transform} The rotated transform.
 */
export function rotate(transform, angle) {
  const cos = Math.cos(angle);
  const sin = Math.sin(angle);
  return multiply(transform, set(tmp_, cos, sin, -sin, cos, 0, 0));
}

/**
 * Applies scale to a given transform.
 * @param {!Transform} transform Transform.
 * @param {number} x Scale factor x.
 * @param {number} y Scale factor y.
 * @return {!Transform} The scaled transform.
 */
export function scale(transform, x, y) {
  return multiply(transform, set(tmp_, x, 0, 0, y, 0, 0));
}

/**
 * Creates a scale transform.
 * @param {!Transform} target Transform to overwrite.
 * @param {number} x Scale factor x.
 * @param {number} y Scale factor y.
 * @return {!Transform} The scale transform.
 */
export function makeScale(target, x, y) {
  return set(target, x, 0, 0, y, 0, 0);
}

/**
 * Applies translation to the given transform.
 * @param {!Transform} transform Transform.
 * @param {number} dx Translation x.
 * @param {number} dy Translation y.
 * @return {!Transform} The translated transform.
 */
export function translate(transform, dx, dy) {
  return multiply(transform, set(tmp_, 1, 0, 0, 1, dx, dy));
}

/**
 * Creates a composite transform given an initial translation, scale, rotation, and
 * final translation (in that order only, not commutative).
 * @param {!Transform} transform The transform (will be modified in place).
 * @param {number} dx1 Initial translation x.
 * @param {number} dy1 Initial translation y.
 * @param {number} sx Scale factor x.
 * @param {number} sy Scale factor y.
 * @param {number} angle Rotation (in counter-clockwise radians).
 * @param {number} dx2 Final translation x.
 * @param {number} dy2 Final translation y.
 * @return {!Transform} The composite transform.
 */
export function compose(transform, dx1, dy1, sx, sy, angle, dx2, dy2) {
  const sin = Math.sin(angle);
  const cos = Math.cos(angle);
  transform[0] = sx * cos;
  transform[1] = sy * sin;
  transform[2] = -sx * sin;
  transform[3] = sy * cos;
  transform[4] = dx2 * sx * cos - dy2 * sx * sin + dx1;
  transform[5] = dx2 * sy * sin + dy2 * sy * cos + dy1;
  return transform;
}

/**
 * Creates a composite transform given an initial translation, scale, rotation, and
 * final translation (in that order only, not commutative). The resulting transform
 * string can be applied as `transform` porperty of an HTMLElement's style.
 * @param {number} dx1 Initial translation x.
 * @param {number} dy1 Initial translation y.
 * @param {number} sx Scale factor x.
 * @param {number} sy Scale factor y.
 * @param {number} angle Rotation (in counter-clockwise radians).
 * @param {number} dx2 Final translation x.
 * @param {number} dy2 Final translation y.
 * @return {string} The composite css transform.
 * @api
 */
export function composeCssTransform(dx1, dy1, sx, sy, angle, dx2, dy2) {
  return toString(compose(create(), dx1, dy1, sx, sy, angle, dx2, dy2));
}

/**
 * Invert the given transform.
 * @param {!Transform} source The source transform to invert.
 * @return {!Transform} The inverted (source) transform.
 */
export function invert(source) {
  return makeInverse(source, source);
}

/**
 * Invert the given transform.
 * @param {!Transform} target Transform to be set as the inverse of
 *     the source transform.
 * @param {!Transform} source The source transform to invert.
 * @return {!Transform} The inverted (target) transform.
 */
export function makeInverse(target, source) {
  const det = determinant(source);
  assert(det !== 0, 32); // Transformation matrix cannot be inverted

  const a = source[0];
  const b = source[1];
  const c = source[2];
  const d = source[3];
  const e = source[4];
  const f = source[5];

  target[0] = d / det;
  target[1] = -b / det;
  target[2] = -c / det;
  target[3] = a / det;
  target[4] = (c * f - d * e) / det;
  target[5] = -(a * f - b * e) / det;

  return target;
}

/**
 * Returns the determinant of the given matrix.
 * @param {!Transform} mat Matrix.
 * @return {number} Determinant.
 */
export function determinant(mat) {
  return mat[0] * mat[3] - mat[1] * mat[2];
}

/**
 * A string version of the transform.  This can be used
 * for CSS transforms.
 * @param {!Transform} mat Matrix.
 * @return {string} The transform as a string.
 */
export function toString(mat) {
  return 'matrix(' + mat.join(', ') + ')';
}
<commit_msg>Include Transform typedef in API<commit_after>/**
 * @module ol/transform
 */
import {assert} from './asserts.js';

/**
 * An array representing an affine 2d transformation for use with
 * {@link module:ol/transform} functions. The array has 6 elements.
 * @typedef {!Array<number>} Transform
 * @api
 */

/**
 * Collection of affine 2d transformation functions. The functions work on an
 * array of 6 elements. The element order is compatible with the [SVGMatrix
 * interface](https://developer.mozilla.org/en-US/docs/Web/API/SVGMatrix) and is
 * a subset (elements a to f) of a 3×3 matrix:
 * ```
 * [ a c e ]
 * [ b d f ]
 * [ 0 0 1 ]
 * ```
 */

/**
 * @private
 * @type {Transform}
 */
const tmp_ = new Array(6);

/**
 * Create an identity transform.
 * @return {!Transform} Identity transform.
 */
export function create() {
  return [1, 0, 0, 1, 0, 0];
}

/**
 * Resets the given transform to an identity transform.
 * @param {!Transform} transform Transform.
 * @return {!Transform} Transform.
 */
export function reset(transform) {
  return set(transform, 1, 0, 0, 1, 0, 0);
}

/**
 * Multiply the underlying matrices of two transforms and return the result in
 * the first transform.
 * @param {!Transform} transform1 Transform parameters of matrix 1.
 * @param {!Transform} transform2 Transform parameters of matrix 2.
 * @return {!Transform} transform1 multiplied with transform2.
 */
export function multiply(transform1, transform2) {
  const a1 = transform1[0];
  const b1 = transform1[1];
  const c1 = transform1[2];
  const d1 = transform1[3];
  const e1 = transform1[4];
  const f1 = transform1[5];
  const a2 = transform2[0];
  const b2 = transform2[1];
  const c2 = transform2[2];
  const d2 = transform2[3];
  const e2 = transform2[4];
  const f2 = transform2[5];

  transform1[0] = a1 * a2 + c1 * b2;
  transform1[1] = b1 * a2 + d1 * b2;
  transform1[2] = a1 * c2 + c1 * d2;
  transform1[3] = b1 * c2 + d1 * d2;
  transform1[4] = a1 * e2 + c1 * f2 + e1;
  transform1[5] = b1 * e2 + d1 * f2 + f1;

  return transform1;
}

/**
 * Set the transform components a-f on a given transform.
 * @param {!Transform} transform Transform.
 * @param {number} a The a component of the transform.
 * @param {number} b The b component of the transform.
 * @param {number} c The c component of the transform.
 * @param {number} d The d component of the transform.
 * @param {number} e The e component of the transform.
 * @param {number} f The f component of the transform.
 * @return {!Transform} Matrix with transform applied.
 */
export function set(transform, a, b, c, d, e, f) {
  transform[0] = a;
  transform[1] = b;
  transform[2] = c;
  transform[3] = d;
  transform[4] = e;
  transform[5] = f;
  return transform;
}

/**
 * Set transform on one matrix from another matrix.
 * @param {!Transform} transform1 Matrix to set transform to.
 * @param {!Transform} transform2 Matrix to set transform from.
 * @return {!Transform} transform1 with transform from transform2 applied.
 */
export function setFromArray(transform1, transform2) {
  transform1[0] = transform2[0];
  transform1[1] = transform2[1];
  transform1[2] = transform2[2];
  transform1[3] = transform2[3];
  transform1[4] = transform2[4];
  transform1[5] = transform2[5];
  return transform1;
}

/**
 * Transforms the given coordinate with the given transform returning the
 * resulting, transformed coordinate. The coordinate will be modified in-place.
 *
 * @param {Transform} transform The transformation.
 * @param {import("./coordinate.js").Coordinate|import("./pixel.js").Pixel} coordinate The coordinate to transform.
 * @return {import("./coordinate.js").Coordinate|import("./pixel.js").Pixel} return coordinate so that operations can be
 *     chained together.
 */
export function apply(transform, coordinate) {
  const x = coordinate[0];
  const y = coordinate[1];
  coordinate[0] = transform[0] * x + transform[2] * y + transform[4];
  coordinate[1] = transform[1] * x + transform[3] * y + transform[5];
  return coordinate;
}

/**
 * Applies rotation to the given transform.
 * @param {!Transform} transform Transform.
 * @param {number} angle Angle in radians.
 * @return {!Transform} The rotated transform.
 */
export function rotate(transform, angle) {
  const cos = Math.cos(angle);
  const sin = Math.sin(angle);
  return multiply(transform, set(tmp_, cos, sin, -sin, cos, 0, 0));
}

/**
 * Applies scale to a given transform.
 * @param {!Transform} transform Transform.
 * @param {number} x Scale factor x.
 * @param {number} y Scale factor y.
 * @return {!Transform} The scaled transform.
 */
export function scale(transform, x, y) {
  return multiply(transform, set(tmp_, x, 0, 0, y, 0, 0));
}

/**
 * Creates a scale transform.
 * @param {!Transform} target Transform to overwrite.
 * @param {number} x Scale factor x.
 * @param {number} y Scale factor y.
 * @return {!Transform} The scale transform.
 */
export function makeScale(target, x, y) {
  return set(target, x, 0, 0, y, 0, 0);
}

/**
 * Applies translation to the given transform.
 * @param {!Transform} transform Transform.
 * @param {number} dx Translation x.
 * @param {number} dy Translation y.
 * @return {!Transform} The translated transform.
 */
export function translate(transform, dx, dy) {
  return multiply(transform, set(tmp_, 1, 0, 0, 1, dx, dy));
}

/**
 * Creates a composite transform given an initial translation, scale, rotation, and
 * final translation (in that order only, not commutative).
 * @param {!Transform} transform The transform (will be modified in place).
 * @param {number} dx1 Initial translation x.
 * @param {number} dy1 Initial translation y.
 * @param {number} sx Scale factor x.
 * @param {number} sy Scale factor y.
 * @param {number} angle Rotation (in counter-clockwise radians).
 * @param {number} dx2 Final translation x.
 * @param {number} dy2 Final translation y.
 * @return {!Transform} The composite transform.
 */
export function compose(transform, dx1, dy1, sx, sy, angle, dx2, dy2) {
  const sin = Math.sin(angle);
  const cos = Math.cos(angle);
  transform[0] = sx * cos;
  transform[1] = sy * sin;
  transform[2] = -sx * sin;
  transform[3] = sy * cos;
  transform[4] = dx2 * sx * cos - dy2 * sx * sin + dx1;
  transform[5] = dx2 * sy * sin + dy2 * sy * cos + dy1;
  return transform;
}

/**
 * Creates a composite transform given an initial translation, scale, rotation, and
 * final translation (in that order only, not commutative). The resulting transform
 * string can be applied as `transform` porperty of an HTMLElement's style.
 * @param {number} dx1 Initial translation x.
 * @param {number} dy1 Initial translation y.
 * @param {number} sx Scale factor x.
 * @param {number} sy Scale factor y.
 * @param {number} angle Rotation (in counter-clockwise radians).
 * @param {number} dx2 Final translation x.
 * @param {number} dy2 Final translation y.
 * @return {string} The composite css transform.
 * @api
 */
export function composeCssTransform(dx1, dy1, sx, sy, angle, dx2, dy2) {
  return toString(compose(create(), dx1, dy1, sx, sy, angle, dx2, dy2));
}

/**
 * Invert the given transform.
 * @param {!Transform} source The source transform to invert.
 * @return {!Transform} The inverted (source) transform.
 */
export function invert(source) {
  return makeInverse(source, source);
}

/**
 * Invert the given transform.
 * @param {!Transform} target Transform to be set as the inverse of
 *     the source transform.
 * @param {!Transform} source The source transform to invert.
 * @return {!Transform} The inverted (target) transform.
 */
export function makeInverse(target, source) {
  const det = determinant(source);
  assert(det !== 0, 32); // Transformation matrix cannot be inverted

  const a = source[0];
  const b = source[1];
  const c = source[2];
  const d = source[3];
  const e = source[4];
  const f = source[5];

  target[0] = d / det;
  target[1] = -b / det;
  target[2] = -c / det;
  target[3] = a / det;
  target[4] = (c * f - d * e) / det;
  target[5] = -(a * f - b * e) / det;

  return target;
}

/**
 * Returns the determinant of the given matrix.
 * @param {!Transform} mat Matrix.
 * @return {number} Determinant.
 */
export function determinant(mat) {
  return mat[0] * mat[3] - mat[1] * mat[2];
}

/**
 * A string version of the transform.  This can be used
 * for CSS transforms.
 * @param {!Transform} mat Matrix.
 * @return {string} The transform as a string.
 */
export function toString(mat) {
  return 'matrix(' + mat.join(', ') + ')';
}
<|endoftext|>


<commit_before>var format = require("util").format;
var types = ["ham", "pb&b"];

module.exports = function (tennu) {
    return {
        dependencies: [],
        exports: {
            help: {
                sandwhich: "Makes a sandwhich for you."
            };
        },
        handlers: {
            "!sandwhich" : function (command) {
                var requester = command.nickname;
                var type = types[Math.floor(Math.random() * types.length)];

                return format("%s: Have a %s sandwhich.", requester, type);
            }
        }
    };
};<commit_msg>Remove semicolon from javascript object definition<commit_after>var format = require("util").format;
var types = ["ham", "pb&b"];

module.exports = function (tennu) {
    return {
        dependencies: [],
        exports: {
            help: {
                sandwhich: "Makes a sandwhich for you."
            }
        },
        handlers: {
            "!sandwhich" : function (command) {
                var requester = command.nickname;
                var type = types[Math.floor(Math.random() * types.length)];

                return format("%s: Have a %s sandwhich.", requester, type);
            }
        }
    };
};
<|endoftext|>


<commit_before>import Loki from 'lokijs';
import logger from './logging/logger';
import Module from './module';
import jsonpath from 'jsonpath';
import dockerNames from 'docker-names';
import * as mq from './mq/connection';
import * as symbols from './utils/symbols';
import Promise from 'bluebird';
import _ from 'lodash/core';
import uuid from 'node-uuid';
import * as temp from 'temp';
import * as JSON from 'json3';

/**
 * The Orchestrator is the class that manages all modules
 * - manages connections and queue
 * - manages elasticsearch as persistence
 * - it manages things
 * - it is smart
 * @class
 */
class Orchestrator {

  /**
   * Initialise the Orchestrator
   * @param {Object} [options]
   */
  constructor(options) {
    const defaults = {
      dbPath: temp.path(),
      name: dockerNames.getRandomName(false),
      modulesCollectionName: 'modules',
      registerQueue: 'o_register',
      messagesQueue: 'o_messages',
      amqpURL: 'amqp://localhost:5672',
      prefetch: 100
    };

    options = _.defaults(options || {}, defaults);
    logger.info('Initializing Orchestrator with options', options);

    /**
     * The name/path of the lokijs in memory database
     * @type {any}
     */
    this.dbPath = options.dbPath;

    /**
     * The name of this Orchestrator
     * @type {String}
     */
    this.name = options.name;

    /**
     * Whether this orchestrator is currently _running or not;
     * @type {boolean}
     * @private
     */
    this._running = false;

    /**
     * Last module order, this is used to order modules when querying them
     * @type {number}
     * @private
     */
    this._order = 0;

    /**
     * The name of the queue in which new messages will arrive
     * @type {string}
     */
    this.messagesQueue = options.messagesQueue;

    /**
     * Wether the database was fully initialized or not.
     * @type {boolean}
     * @private
     */
    this._dbInitialized = false;

    /**
     * The lokijs database;
     * @type {Loki}
     * @private
     */
    this._db = new Loki(this.dbPath, {
      autoload: true, autosave: true, autoloadCallback: () => {
        this._onRestoreDB();
      }
    });

    this.modulesCollectionName = options.modulesCollectionName;

    /**
     * The modules collection
     * @type {Collection}
     */
    this.modulesCollection = null;

    /**
     * The name of the queue in which registrations will be received
     * @type {string}
     */
    this.registerQueue = options.registerQueue;

    /**
     * The AMQP connection url, eg: amqp://localhost:5672
     * @type {string}
     */
    this.amqpURL = options.amqpURL;

    /**
     * The amqp context (connection) that this Orchestrator is running
     * @see listen
     * @type {Context}
     */
    this.amqpContext = null;

    /**
     * The amount of messages to be prefetched
     * @type {Number}
     */
    this.prefetch = options.prefetch;

    this.modulesSockets = [];
  }

  /**
   * Start listening to a queue for new modules to be registered
   * @return {Promise}
   */
  listen() {
    if (this._running) {
      return Promise.resolve(this);
    }
    if (!this._dbInitialized) {
      return new Promise((resolve) => {
        logger.debug('Database not initialized, waiting 100ms');
        setTimeout(() => {
          resolve(this.listen());
        }, 100);
      });
    }
    return mq.connect(this.amqpURL)
      .bind(this)
      .then((context) => {
        this.amqpContext = context;
        return context.socket('REPLY');
      })
      .then(this._connectToRegistrationQueue)
      .then(this._waitForPersistenceModules)
      .then(() => {
        this._running = true;
        return this.amqpContext.socket('WORKER', { prefetch: this.prefetch });
      }).then(this._connectToMessagesQueue);
  }

  /**
   * Wait for persistence modules to register
   * @private
   * @return {Promise} that only resolves when a persistence module is connected
   */
  _waitForPersistenceModules() {

    return new Promise((resolve) => {
      if (!this._hasPersistenceModule()) {
        logger.debug('No persistence module registered, will wait for 200ms');
        setTimeout(() => {
          this._waitForPersistenceModules().then(resolve);
        }, 200);
      } else {
        resolve();
      }
    });
  }

  /**
   * Check if a persistence module is already registered
   * @return {boolean}
   * @private
   */
  _hasPersistenceModule() {
    return this.modulesCollection.find({ type: 'persistence' }).length > 0;
  }

  /**
   * Connects to the message queue and wait for new messages to arrive
   * @param {WorkerSocket} workerSocket
   * @private
   * @return {WorkerSocket}
   */
  _connectToMessagesQueue(workerSocket) {
    logger.debug('Connecting to messages queue', this.messagesQueue);
    workerSocket.connect(this.messagesQueue);
    const _this = this;

    workerSocket.on('data', (message) => {
      _this._onMessage(JSON.parse(message.toString()));
      workerSocket.ack();
    });
    return workerSocket;
  }

  /**
   * Connects to the registration queue and wait for new modules
   *
   * @param {RepSocket} replySocket
   * @return {RepSocket}
   * @private
   */
  _connectToRegistrationQueue(replySocket) {
    replySocket.connect(this.registerQueue);
    replySocket.on('data', (message) => {
      logger.debug('Received new module registration');
      return this.onNewModule(message).then((m) => {
        logger.debug('Module registered, writing back response', m.toJSON());
        replySocket.write(m.toJSON());
        logger.debug('Response written');
        return m;
      });
    });
    logger.info(`[${symbols.check}] AMQP connected, waiting for new modules`);
    return replySocket;
  }

  /**
   * Shutdown this Orchestrator, disconnection from MQ and ES and freeing resources
   * @return {Promise}
   */
  shutdown() {
    return new Promise((resolve) => {
      let waitAmqp = false;

      if (this._running) {
        if (this.amqpContext) {
          this.amqpContext.on('close', () => {
            resolve();
          });
          this.amqpContext.close();
          waitAmqp = true;
        }
        logger.info(`[${symbols.check}] AMQP disconnected`);
        if (this.esClient) {
          this.esClient.close();
        }
        logger.info(`[${symbols.check}] Elasticsearch disconnected`);
        this._running = false;
      }
      if (this._db) {
        this._db.saveDatabase(() => {
          this._db.close(!waitAmqp ? resolve : null);
        });
      }
      if (!waitAmqp) {
        resolve();
      }
    });

  }

  /**
   * Handles new modules registrations from the amqp queue
   * @param  {Object} message
   * @return {Promise}
   */
  onNewModule(message) {
    return new Promise((resolve, reject) => {
      try {
        message = typeof message === 'string' || Buffer.isBuffer(message) ? JSON.parse(message.toString()) : message;
        logger.debug(`Received new message on the module register queue ${message}`, message);
        const module = new Module(message);

        return this.register(module).then((m) => {
          resolve(m);
        });
      } catch (e) {
        logger.warn('Error parsing new module', e);
        reject(e);
      }
    });
  }

  /**
   * Handles new messages arrived, dispatching to first matching worker it can find
   * @param {any} originalMessage
   * @private
   * @return {Promise} that resolves if handling of the message is OK
   */
  _onMessage(originalMessage) {

    const meta = originalMessage.__meta;

    delete originalMessage.__meta; // Removing meta to make sure it's not send to other modules
    if (meta && meta.type === 'persistence') { // If it's coming from the persistence module
      return this.findMatchingModules(originalMessage)
        .then((modules) => {
          if (_.isEmpty(modules)) {
            logger.info('Finished pipeline for message, storing and not redirecting to any module');
          } else {
            return this._sendMessageToModule(originalMessage, modules[0]);
          }
        });
    }
    return this._storeMessage(originalMessage);
  }

  /**
   * Send the given message to the given module.
   * @param {*} message
   * @param {Module} module
   * @private
   */
  _sendMessageToModule(message, module) {
    module = Orchestrator.checkModule(module);

    logger.info('Redirecting message to module', module.service, module.name);
    logger.debug('Sending message to queue', message, module.workerQueueName);
    if (!this.modulesSockets[module.service]) {
      const pushSocket = this.amqpContext.socket('PUSH');

      this.modulesSockets[module.service] = pushSocket;
      pushSocket.connect(module.workerQueueName, () => {
        this._sendMessageToModule(message, module);
      });

    } else {
      this.modulesSockets[module.service].write(JSON.stringify(message));
    }
  }

  /**
   * Stores the arrived message into the elasticsearch
   * and return a Promise that resolves to the stored message
   * @param {Object} message
   * @private
   * @return {Promise}
   */
  _storeMessage(message) {
    const _this = this;

    if (!message.uuid) {
      message.uuid = uuid.v4();
    }
    return new Promise((resolve, reject) => {
      const modules = _this.modulesCollection.find({ type: 'persistence' });

      if (modules.length === 1) { // only allow 1 persistence module for now
        this._sendMessageToModule(message, modules[0]);
        // } else if (modules.length > 1) { // This is being limited by registration now
        //   reject(new Error('More than 1 persistence module is currently not supported'));
      } else {
        reject(new Error('No persistence module registered, orchestrator should not have been started'));
      }
    });
  }

  /**
   * Unregister a module in the Orchestrator
   * @param {Module} module
   * @return {Promise}
   */
  unregister(module) {

    return new Promise((resolve) => {
      module = Orchestrator.checkModule(module);
      const previousModule = this.modulesCollection.find({ uuid: module.uuid });

      if (previousModule) {
        logger.info(`Unregistering ${module.name} with uuid ${module.uuid}`);
        this.modulesCollection.remove(previousModule);
      }
      resolve();
    });
  }

  /**
   * Check if a module is registered
   * @param {Module} module
   * @return {boolean}
   */
  isRegistered(module) {
    module = Orchestrator.checkModule(module);
    return this.modulesCollection.find({ uuid: module.uuid }).length > 0;
  }

  /**
   * Register a module in the Orchestrator
   * @param {Module} module
   * @return {*|Promise}
   */
  register(module) {

    return Promise.resolve().then(() => {
      module = Orchestrator.checkModule(module);
      logger.info('Registering new module', module.toJSON());

      if (this.isRegistered(module)) {
        logger.info(`Module ${module.name} already registered for uuid ${module.uuid}`);
        this.modulesCollection.removeWhere({ uuid: module.uuid });
        return this.modulesCollection.insert(module);
      }

      // Only 1 persistence module allowed, the new one always replace the old one
      if (module.type === 'persistence' && this._hasPersistenceModule()) {
        this.modulesCollection.removeWhere((m) => {
          return m.type === 'persistence';
        });
      }
      module.order = ++this._order;
      module.messagesQueue = this.messagesQueue;
      module.workerQueueName = this.generateModuleQueueName(module);
      return this.modulesCollection.insert(module);
    });
  }

  /**
   * Find modules that matches for the give message
   * ordered by their registration _order
   * @param {Object} message
   * @param {Object} [meta] the message metadata
   * @return {Promise}
   */
  findMatchingModules(message, meta) {
    return new Promise((resolve) => {
      logger.debug('Finding modules that matches', message);
      const modules = this.modulesCollection.where((module) => {
        if (module.type === 'persistence') {
          return false;
        }
        if (meta && meta.service === module.service && !module.resend) {
          return false;
        }

        let matchesPositive = true;
        let matchesNegative = true;

        if (module.positivePath) {
          matchesPositive = Orchestrator.matchesPath(message, module.positivePath);
        }

        if (module.negativePath) {
          matchesNegative = !Orchestrator.matchesPath(message, module.negativePath);
        }
        return matchesPositive && matchesNegative;
      });

      logger.debug('Found modules:', modules.length);
      resolve(modules);

    });

  }

  /**
   * Generates a random (with pattern) queue name to be used by modules
   * NOTE: if a module is already registered for the same service, the previously generated queue is returned.
   * This way all modules for the same service will be listening to the same queue
   * @param {Module} module
   * @return {string}
   */
  generateModuleQueueName(module) {
    const serviceModules = this.modulesCollection.find({ service: module.service });

    if (!_.isEmpty(serviceModules)) {
      return serviceModules[0].workerQueueName;
    }
    return `${module.service}-${module.order}-${uuid.v4()}`;
  }

  /**
   * Checks if the given object matches a jsonpath
   * @param {*} obj
   * @param {Path} path
   * @return {boolean}
   */
  static
  matchesPath(obj, path) {
    return jsonpath.query(obj, path).length > 0;
  }

  /**
   * Check if the module is of type {Module} if not convert to it
   *
   * @param {*|Module} module
   * @return {Module}
   */
  static
  checkModule(module) {
    return module instanceof Module ? module : new Module(module);
  }

  /**
   * Called when the database is ready
   * @private
   */
  _onRestoreDB() {
    this.modulesCollection = this._db.getCollection(this.modulesCollectionName);
    if (!this.modulesCollection) {
      this.modulesCollection = this._db.addCollection(this.modulesCollectionName);
      this._dbInitialized = true;
    } else {

      const parseAndRegister = (m) => {
        if (typeof m === 'string') {
          m = JSON.parse(m);
        }
        return this.register(m);
      };

      Promise.all(this.modulesCollection.find().map(parseAndRegister))
        .then(() => {
          logger.debug(`Database loaded with, ${this.modulesCollection.count()} modules`);
          this._dbInitialized = true;
        });
    }
  }
}

export default Orchestrator;

<commit_msg>esClient is now a module, nothing to do here.<commit_after>import Loki from 'lokijs';
import logger from './logging/logger';
import Module from './module';
import jsonpath from 'jsonpath';
import dockerNames from 'docker-names';
import * as mq from './mq/connection';
import * as symbols from './utils/symbols';
import Promise from 'bluebird';
import _ from 'lodash/core';
import uuid from 'node-uuid';
import * as temp from 'temp';
import * as JSON from 'json3';

/**
 * The Orchestrator is the class that manages all modules
 * - manages connections and queue
 * - manages elasticsearch as persistence
 * - it manages things
 * - it is smart
 * @class
 */
class Orchestrator {

  /**
   * Initialise the Orchestrator
   * @param {Object} [options]
   */
  constructor(options) {
    const defaults = {
      dbPath: temp.path(),
      name: dockerNames.getRandomName(false),
      modulesCollectionName: 'modules',
      registerQueue: 'o_register',
      messagesQueue: 'o_messages',
      amqpURL: 'amqp://localhost:5672',
      prefetch: 100
    };

    options = _.defaults(options || {}, defaults);
    logger.info('Initializing Orchestrator with options', options);

    /**
     * The name/path of the lokijs in memory database
     * @type {any}
     */
    this.dbPath = options.dbPath;

    /**
     * The name of this Orchestrator
     * @type {String}
     */
    this.name = options.name;

    /**
     * Whether this orchestrator is currently _running or not;
     * @type {boolean}
     * @private
     */
    this._running = false;

    /**
     * Last module order, this is used to order modules when querying them
     * @type {number}
     * @private
     */
    this._order = 0;

    /**
     * The name of the queue in which new messages will arrive
     * @type {string}
     */
    this.messagesQueue = options.messagesQueue;

    /**
     * Wether the database was fully initialized or not.
     * @type {boolean}
     * @private
     */
    this._dbInitialized = false;

    /**
     * The lokijs database;
     * @type {Loki}
     * @private
     */
    this._db = new Loki(this.dbPath, {
      autoload: true, autosave: true, autoloadCallback: () => {
        this._onRestoreDB();
      }
    });

    this.modulesCollectionName = options.modulesCollectionName;

    /**
     * The modules collection
     * @type {Collection}
     */
    this.modulesCollection = null;

    /**
     * The name of the queue in which registrations will be received
     * @type {string}
     */
    this.registerQueue = options.registerQueue;

    /**
     * The AMQP connection url, eg: amqp://localhost:5672
     * @type {string}
     */
    this.amqpURL = options.amqpURL;

    /**
     * The amqp context (connection) that this Orchestrator is running
     * @see listen
     * @type {Context}
     */
    this.amqpContext = null;

    /**
     * The amount of messages to be prefetched
     * @type {Number}
     */
    this.prefetch = options.prefetch;

    this.modulesSockets = [];
  }

  /**
   * Start listening to a queue for new modules to be registered
   * @return {Promise}
   */
  listen() {
    if (this._running) {
      return Promise.resolve(this);
    }
    if (!this._dbInitialized) {
      return new Promise((resolve) => {
        logger.debug('Database not initialized, waiting 100ms');
        setTimeout(() => {
          resolve(this.listen());
        }, 100);
      });
    }
    return mq.connect(this.amqpURL)
      .bind(this)
      .then((context) => {
        this.amqpContext = context;
        return context.socket('REPLY');
      })
      .then(this._connectToRegistrationQueue)
      .then(this._waitForPersistenceModules)
      .then(() => {
        this._running = true;
        return this.amqpContext.socket('WORKER', { prefetch: this.prefetch });
      }).then(this._connectToMessagesQueue);
  }

  /**
   * Wait for persistence modules to register
   * @private
   * @return {Promise} that only resolves when a persistence module is connected
   */
  _waitForPersistenceModules() {

    return new Promise((resolve) => {
      if (!this._hasPersistenceModule()) {
        logger.debug('No persistence module registered, will wait for 200ms');
        setTimeout(() => {
          this._waitForPersistenceModules().then(resolve);
        }, 200);
      } else {
        resolve();
      }
    });
  }

  /**
   * Check if a persistence module is already registered
   * @return {boolean}
   * @private
   */
  _hasPersistenceModule() {
    return this.modulesCollection.find({ type: 'persistence' }).length > 0;
  }

  /**
   * Connects to the message queue and wait for new messages to arrive
   * @param {WorkerSocket} workerSocket
   * @private
   * @return {WorkerSocket}
   */
  _connectToMessagesQueue(workerSocket) {
    logger.debug('Connecting to messages queue', this.messagesQueue);
    workerSocket.connect(this.messagesQueue);
    const _this = this;

    workerSocket.on('data', (message) => {
      _this._onMessage(JSON.parse(message.toString()));
      workerSocket.ack();
    });
    return workerSocket;
  }

  /**
   * Connects to the registration queue and wait for new modules
   *
   * @param {RepSocket} replySocket
   * @return {RepSocket}
   * @private
   */
  _connectToRegistrationQueue(replySocket) {
    replySocket.connect(this.registerQueue);
    replySocket.on('data', (message) => {
      logger.debug('Received new module registration');
      return this.onNewModule(message).then((m) => {
        logger.debug('Module registered, writing back response', m.toJSON());
        replySocket.write(m.toJSON());
        logger.debug('Response written');
        return m;
      });
    });
    logger.info(`[${symbols.check}] AMQP connected, waiting for new modules`);
    return replySocket;
  }

  /**
   * Shutdown this Orchestrator, disconnection from MQ and ES and freeing resources
   * @return {Promise}
   */
  shutdown() {
    return new Promise((resolve) => {
      let waitAmqp = false;

      if (this._running) {
        if (this.amqpContext) {
          this.amqpContext.on('close', () => {
            resolve();
          });
          this.amqpContext.close();
          waitAmqp = true;
        }
        logger.info(`[${symbols.check}] AMQP disconnected`);
        this._running = false;
      }
      if (this._db) {
        this._db.saveDatabase(() => {
          this._db.close(!waitAmqp ? resolve : null);
        });
      }
      if (!waitAmqp) {
        resolve();
      }
    });

  }

  /**
   * Handles new modules registrations from the amqp queue
   * @param  {Object} message
   * @return {Promise}
   */
  onNewModule(message) {
    return new Promise((resolve, reject) => {
      try {
        message = typeof message === 'string' || Buffer.isBuffer(message) ? JSON.parse(message.toString()) : message;
        logger.debug(`Received new message on the module register queue ${message}`, message);
        const module = new Module(message);

        return this.register(module).then((m) => {
          resolve(m);
        });
      } catch (e) {
        logger.warn('Error parsing new module', e);
        reject(e);
      }
    });
  }

  /**
   * Handles new messages arrived, dispatching to first matching worker it can find
   * @param {any} originalMessage
   * @private
   * @return {Promise} that resolves if handling of the message is OK
   */
  _onMessage(originalMessage) {

    const meta = originalMessage.__meta;

    delete originalMessage.__meta; // Removing meta to make sure it's not send to other modules
    if (meta && meta.type === 'persistence') { // If it's coming from the persistence module
      return this.findMatchingModules(originalMessage)
        .then((modules) => {
          if (_.isEmpty(modules)) {
            logger.info('Finished pipeline for message, storing and not redirecting to any module');
          } else {
            return this._sendMessageToModule(originalMessage, modules[0]);
          }
        });
    }
    return this._storeMessage(originalMessage);
  }

  /**
   * Send the given message to the given module.
   * @param {*} message
   * @param {Module} module
   * @private
   */
  _sendMessageToModule(message, module) {
    module = Orchestrator.checkModule(module);

    logger.info('Redirecting message to module', module.service, module.name);
    logger.debug('Sending message to queue', message, module.workerQueueName);
    if (!this.modulesSockets[module.service]) {
      const pushSocket = this.amqpContext.socket('PUSH');

      this.modulesSockets[module.service] = pushSocket;
      pushSocket.connect(module.workerQueueName, () => {
        this._sendMessageToModule(message, module);
      });

    } else {
      this.modulesSockets[module.service].write(JSON.stringify(message));
    }
  }

  /**
   * Stores the arrived message into the elasticsearch
   * and return a Promise that resolves to the stored message
   * @param {Object} message
   * @private
   * @return {Promise}
   */
  _storeMessage(message) {
    const _this = this;

    if (!message.uuid) {
      message.uuid = uuid.v4();
    }
    return new Promise((resolve, reject) => {
      const modules = _this.modulesCollection.find({ type: 'persistence' });

      if (modules.length === 1) { // only allow 1 persistence module for now
        this._sendMessageToModule(message, modules[0]);
        // } else if (modules.length > 1) { // This is being limited by registration now
        //   reject(new Error('More than 1 persistence module is currently not supported'));
      } else {
        reject(new Error('No persistence module registered, orchestrator should not have been started'));
      }
    });
  }

  /**
   * Unregister a module in the Orchestrator
   * @param {Module} module
   * @return {Promise}
   */
  unregister(module) {

    return new Promise((resolve) => {
      module = Orchestrator.checkModule(module);
      const previousModule = this.modulesCollection.find({ uuid: module.uuid });

      if (previousModule) {
        logger.info(`Unregistering ${module.name} with uuid ${module.uuid}`);
        this.modulesCollection.remove(previousModule);
      }
      resolve();
    });
  }

  /**
   * Check if a module is registered
   * @param {Module} module
   * @return {boolean}
   */
  isRegistered(module) {
    module = Orchestrator.checkModule(module);
    return this.modulesCollection.find({ uuid: module.uuid }).length > 0;
  }

  /**
   * Register a module in the Orchestrator
   * @param {Module} module
   * @return {*|Promise}
   */
  register(module) {

    return Promise.resolve().then(() => {
      module = Orchestrator.checkModule(module);
      logger.info('Registering new module', module.toJSON());

      if (this.isRegistered(module)) {
        logger.info(`Module ${module.name} already registered for uuid ${module.uuid}`);
        this.modulesCollection.removeWhere({ uuid: module.uuid });
        return this.modulesCollection.insert(module);
      }

      // Only 1 persistence module allowed, the new one always replace the old one
      if (module.type === 'persistence' && this._hasPersistenceModule()) {
        this.modulesCollection.removeWhere((m) => {
          return m.type === 'persistence';
        });
      }
      module.order = ++this._order;
      module.messagesQueue = this.messagesQueue;
      module.workerQueueName = this.generateModuleQueueName(module);
      return this.modulesCollection.insert(module);
    });
  }

  /**
   * Find modules that matches for the give message
   * ordered by their registration _order
   * @param {Object} message
   * @param {Object} [meta] the message metadata
   * @return {Promise}
   */
  findMatchingModules(message, meta) {
    return new Promise((resolve) => {
      logger.debug('Finding modules that matches', message);
      const modules = this.modulesCollection.where((module) => {
        if (module.type === 'persistence') {
          return false;
        }
        if (meta && meta.service === module.service && !module.resend) {
          return false;
        }

        let matchesPositive = true;
        let matchesNegative = true;

        if (module.positivePath) {
          matchesPositive = Orchestrator.matchesPath(message, module.positivePath);
        }

        if (module.negativePath) {
          matchesNegative = !Orchestrator.matchesPath(message, module.negativePath);
        }
        return matchesPositive && matchesNegative;
      });

      logger.debug('Found modules:', modules.length);
      resolve(modules);

    });

  }

  /**
   * Generates a random (with pattern) queue name to be used by modules
   * NOTE: if a module is already registered for the same service, the previously generated queue is returned.
   * This way all modules for the same service will be listening to the same queue
   * @param {Module} module
   * @return {string}
   */
  generateModuleQueueName(module) {
    const serviceModules = this.modulesCollection.find({ service: module.service });

    if (!_.isEmpty(serviceModules)) {
      return serviceModules[0].workerQueueName;
    }
    return `${module.service}-${module.order}-${uuid.v4()}`;
  }

  /**
   * Checks if the given object matches a jsonpath
   * @param {*} obj
   * @param {Path} path
   * @return {boolean}
   */
  static
  matchesPath(obj, path) {
    return jsonpath.query(obj, path).length > 0;
  }

  /**
   * Check if the module is of type {Module} if not convert to it
   *
   * @param {*|Module} module
   * @return {Module}
   */
  static
  checkModule(module) {
    return module instanceof Module ? module : new Module(module);
  }

  /**
   * Called when the database is ready
   * @private
   */
  _onRestoreDB() {
    this.modulesCollection = this._db.getCollection(this.modulesCollectionName);
    if (!this.modulesCollection) {
      this.modulesCollection = this._db.addCollection(this.modulesCollectionName);
      this._dbInitialized = true;
    } else {

      const parseAndRegister = (m) => {
        if (typeof m === 'string') {
          m = JSON.parse(m);
        }
        return this.register(m);
      };

      Promise.all(this.modulesCollection.find().map(parseAndRegister))
        .then(() => {
          logger.debug(`Database loaded with, ${this.modulesCollection.count()} modules`);
          this._dbInitialized = true;
        });
    }
  }
}

export default Orchestrator;

<|endoftext|>


<commit_before>import React, { Fragment } from 'react'
import {
  Heading,
  Container,
  Card,
  Flex,
  Box,
  Text,
  LargeButton,
  Icon,
  Section,
  Link as A
} from '@hackclub/design-system'
import Helmet from 'react-helmet'
import Link from 'gatsby-link'
import Nav from 'components/Nav'
import Stat from 'components/Stat'
import {
  Triangle,
  Hexagon,
  Pentagon,
  Square,
  Circle,
  Line
} from 'components/Shapes'
import DonateForm from 'components/donate/DonateForm'
import Spent from 'components/donate/Spent'
import Footer from 'components/Footer'
import commaNumber from 'comma-number'

const Header = Section.withComponent('header').extend`
  background: url('/pattern.svg');
  > div {
    display: grid;
    grid-gap: ${props => props.theme.space[4]}px;
    ${props => props.theme.mediaQueries.md} {
      grid-template-columns: 3fr 2fr;
    }
  }
  + div {
    position: relative;
  }
`

const Row = Box.extend`
  display: grid;
  grid-gap: ${props => props.theme.space[3]}px;
  text-align: left;
  ${props => props.theme.mediaQueries.md} {
    grid-template-columns: 2fr 3fr;
  }
`

const Financials = Box.extend`
  display: grid;
  border-radius: ${props => props.theme.radius};
  overflow: hidden;

  ${props => props.theme.mediaQueries.md} {
    grid-template-columns: 2fr 3fr;
  }
`

const Stats = Box.extend`
  div {
    width: 100%;
    display: block;
    text-align: left !important;
  }
  span:before {
    content: '$';
    font-size: ${props => props.theme.fontSizes[4]}px;
    margin-left: -12px;
    vertical-align: super;
  }
  p {
    color: ${props => props.theme.colors.red[1]};
  }
`

const Shapes = Box.extend`
  display: none;
  ${props => props.theme.mediaQueries.md} {
    display: block;
    float: right;
    position: relative;
    z-index: -1;
    svg {
      position: absolute;
    }
  }
`

const WishShapes = Shapes.extend`
  svg {
    &:first-child {
      top: 0;
      right: 0;
      color: rgba(115, 45, 228, 0.75);
      z-index: 1;
    }
    &:last-child {
      top: 0;
      right: 4rem;
      color: rgba(45, 228, 207, 0.75);
    }
  }
`

const ContributionShapes = Shapes.extend`
  svg {
    right: 2rem;
    top: 6rem;
    color: ${props => props.theme.colors.pink[4]};
  }
`

const headline = { f: [5, 6], color: 'black', style: { lineHeight: '1.125' } }
const subhline = { f: [3, 4], color: 'black', style: { lineHeight: '1.375' } }
const subtext = { f: [3, 4], color: 'black', style: { lineHeight: '1.5' } }

const contentContainer = {
  maxWidth: 64,
  w: 1,
  p: 3,
  style: { position: 'relative' }
}
const content = { maxWidth: 48, mx: 0 }

A.link = A.withComponent(Link)

const title = 'Donate to Hack Club'
const description =
  'Contribute today to empower the next generation and help start a coding club at every high school.'

const stats = {
  monthly: 6742.71,
  student: 5,
  club: 100
}

export default () => (
  <Fragment>
    <Helmet
      title={title}
      meta={[
        { name: 'twitter:title', content: title },
        { name: 'description', content: description },
        { name: 'twitter:description', content: description },
        { property: 'og:title', content: title },
        { property: 'og:description', content: description },
        { property: 'og:url', content: 'https://hackclub.com/donate' }
      ]}
    />

      <Container {...contentContainer} align="left" py={[4]}>
        <Container maxWidth={36} mx={0}>
          <Heading.h1 color="primary" f={[3, 4]} caps>
            Donate to Hack Club
          </Heading.h1>
          <Heading.h2 my={3} {...headline}>
            We rely on people like you to bring coding to the world.
          </Heading.h2>
          <Text {...subhline}>
            Contribute today to empower the next generation. Help start a
            Hack Club at every high school.
          </Text>
          <Text mt={3} f={2} color="muted">
            Your contribution is tax-deductible.

            Hack Club is a 501(c)(3) non-profit with the EIN 81-2908499.
          </Text>
        </Container>
        <Card bg="snow" p={[3, 4]} style={{ overflow: 'hidden' }}>
          <DonateForm />
        </Card>
      </Container>

    <Container {...contentContainer}>
      <WishShapes mt={3}>
        <Triangle size={128} rotate={64} />
        <Circle size={128} />
      </WishShapes>
      <Container {...content}>
        <Heading.h2 {...headline}>
          Transparent, Free, and Open
        </Heading.h2>
        <Text my={3} {...subtext}>
          Hack Club is a new kind of non-profit with total
          transparency. We open source all of our {' '}
          content
          {' and '}
          code. Many of our
          leaders and members are also available on{' '}
          our Slack.
        </Text>
        <Text my={3} {...subtext}>
           You deserve to know exactly where your contribution will go—so{' '}

            all of our financials are public
          .
        </Text>
      </Container>
      <Row my={5} {...content}>
        <Box>
          <Heading.h2 {...headline}>
            Every last contribution is for our clubs.
          </Heading.h2>
          <ContributionShapes>
            <Pentagon size={128} rotate={16} />
          </ContributionShapes>
        </Box>
        <Box>
          <Text {...subtext}>
            We strive to build a maximally-efficient organization, spending
            98% of funds given directly on our clubs.
          </Text>
          <Text mt={3} {...subtext}>
            When you give to Hack Club, your money goes where students need
            it most.
          </Text>
          <Financials mt={4}>
            <Box bg="primary" color="white" p={[3, 4]} pr={2}>
              <Heading.h3 f={4} mb={2} caps>
                Financials
              </Heading.h3>
              <Stats>
                <Stat
                  f={6}
                  value={commaNumber(stats.monthly)}
                  label="monthly spend"
                  mb={3}
                  w={1}
                />
                <Stat f={6} mb={2} value={stats.club} label="per club" />
                <Stat f={6} value={stats.student} label="per student" />
              </Stats>
            </Box>
            <Box bg="snow" p={[3, 4]}>
              <Heading.h3 f={4} my={0} caps>
                Spending breakdown
              </Heading.h3>
              <Spent />
            </Box>
          </Financials>
        </Box>
      </Row>
      <Container {...content}>
        <Heading.h2 {...headline}>
          Contribute More Than Just Dollars
        </Heading.h2>
        <Text my={3} {...subtext}>
          We accept donations of time, technical or hard science fiction
          books, stocks / other securities, and cryptocurrency.
        </Text>
        <Text my={3} {...subtext}>
          Please get in touch at{' '}
          donate@hackclub.com
          {' '}if you're interested in making a contribution or have any
          questions.
        </Text>
      </Container>
    </Container>

  </Fragment>
)
<commit_msg>Add WIP donor list<commit_after>import React, { Fragment } from 'react'
import {
  Heading,
  Container,
  Card,
  Flex,
  Box,
  Text,
  LargeButton,
  Icon,
  Section,
  Link as A,
  radius
} from '@hackclub/design-system'
import Helmet from 'react-helmet'
import Link from 'gatsby-link'
import Nav from 'components/Nav'
import Stat from 'components/Stat'
import {
  Triangle,
  Hexagon,
  Pentagon,
  Square,
  Circle,
  Line
} from 'components/Shapes'
import DonateForm from 'components/donate/DonateForm'
import Spent from 'components/donate/Spent'
import Footer from 'components/Footer'
import commaNumber from 'comma-number'
import donors from 'components/donate/donors.json'

const Header = Section.withComponent('header').extend`
  background: url('/pattern.svg');
  > div {
    display: grid;
    grid-gap: ${props => props.theme.space[4]}px;
    ${props => props.theme.mediaQueries.md} {
      grid-template-columns: 3fr 2fr;
    }
  }
  + div {
    position: relative;
  }
`

const Row = Box.extend`
  display: grid;
  grid-gap: ${props => props.theme.space[3]}px;
  text-align: left;
  ${props => props.theme.mediaQueries.md} {
    grid-template-columns: 2fr 3fr;
  }
`

const Financials = Box.extend`
  display: grid;
  border-radius: ${props => props.theme.radius};
  overflow: hidden;

  ${props => props.theme.mediaQueries.md} {
    grid-template-columns: 2fr 3fr;
  }
`

const Stats = Box.extend`
  div {
    width: 100%;
    display: block;
    text-align: left !important;
  }
  span:before {
    content: '$';
    font-size: ${props => props.theme.fontSizes[4]}px;
    margin-left: -12px;
    vertical-align: super;
  }
  p {
    color: ${props => props.theme.colors.red[1]};
  }
`

const Shapes = Box.extend`
  display: none;
  ${props => props.theme.mediaQueries.md} {
    display: block;
    float: right;
    position: relative;
    z-index: -1;
    svg {
      position: absolute;
    }
  }
`

const WishShapes = Shapes.extend`
  svg {
    &:first-child {
      top: 0;
      right: 0;
      color: rgba(115, 45, 228, 0.75);
      z-index: 1;
    }
    &:last-child {
      top: 0;
      right: 4rem;
      color: rgba(45, 228, 207, 0.75);
    }
  }
`

const ContributionShapes = Shapes.extend`
  svg {
    right: 2rem;
    top: 6rem;
    color: ${props => props.theme.colors.pink[4]};
  }
`

const headline = { f: [5, 6], color: 'black', style: { lineHeight: '1.125' } }
const subhline = { f: [3, 4], color: 'black', style: { lineHeight: '1.375' } }
const subtext = { f: [3, 4], color: 'black', style: { lineHeight: '1.5' } }

const contentContainer = {
  maxWidth: 64,
  w: 1,
  p: 3,
  style: { position: 'relative' }
}
const content = { maxWidth: 48, mx: 0 }

A.link = A.withComponent(Link)

const title = 'Donate to Hack Club'
const description =
  'Contribute today to empower the next generation and help start a coding club at every high school.'

const stats = {
  monthly: 6742.71,
  student: 5,
  club: 100
}

const DonorCard = ({ name }) => (
  <Card boxShadowSize="sm" radius={radius} p={[3, 4]} m={[2, 3]}>
    <Text {...subtext} color="inherit">
      {name}
    </Text>
  </Card>
)

const DonorListing = ({ name, url }) =>
  url ? (

      <DonorCard name={name} />

  ) : (
    <DonorCard name={name} />
  )

export default () => (
  <Fragment>
    <Helmet
      title={title}
      meta={[
        { name: 'twitter:title', content: title },
        { name: 'description', content: description },
        { name: 'twitter:description', content: description },
        { property: 'og:title', content: title },
        { property: 'og:description', content: description },
        { property: 'og:url', content: 'https://hackclub.com/donate' }
      ]}
    />

      <Container {...contentContainer} align="left" py={[4]}>
        <Container maxWidth={36} mx={0}>
          <Heading.h1 color="primary" f={[3, 4]} caps>
            Donate to Hack Club
          </Heading.h1>
          <Heading.h2 my={3} {...headline}>
            We rely on people like you to bring coding to the world.
          </Heading.h2>
          <Text {...subhline}>
            Contribute today to empower the next generation. Help start a
            Hack Club at every high school.
          </Text>
          <Text mt={3} f={2} color="muted">
            Your contribution is tax-deductible.

            Hack Club is a 501(c)(3) non-profit with the EIN 81-2908499.
          </Text>
        </Container>
        <Card bg="snow" p={[3, 4]} style={{ overflow: 'hidden' }}>
          <DonateForm />
        </Card>
      </Container>

    <Container {...contentContainer}>
      <WishShapes mt={3}>
        <Triangle size={128} rotate={64} />
        <Circle size={128} />
      </WishShapes>
      <Container {...content}>
        <Heading.h2 {...headline}>
          Transparent, Free, and Open
        </Heading.h2>
        <Text my={3} {...subtext}>
          Hack Club is a new kind of non-profit with total
          transparency. We open source all of our {' '}
          content
          {' and '}
          code. Many of our
          leaders and members are also available on{' '}
          our Slack.
        </Text>
        <Text my={3} {...subtext}>
           You deserve to know exactly where your contribution will go—so{' '}

            all of our financials are public
          .
        </Text>
      </Container>
      <Row my={5} {...content}>
        <Box>
          <Heading.h2 {...headline}>
            Every last contribution is for our clubs.
          </Heading.h2>
          <ContributionShapes>
            <Pentagon size={128} rotate={16} />
          </ContributionShapes>
        </Box>
        <Box>
          <Text {...subtext}>
            We strive to build a maximally-efficient organization, spending
            98% of funds given directly on our clubs.
          </Text>
          <Text mt={3} {...subtext}>
            When you give to Hack Club, your money goes where students need
            it most.
          </Text>
          <Financials mt={4}>
            <Box bg="primary" color="white" p={[3, 4]} pr={2}>
              <Heading.h3 f={4} mb={2} caps>
                Financials
              </Heading.h3>
              <Stats>
                <Stat
                  f={6}
                  value={commaNumber(stats.monthly)}
                  label="monthly spend"
                  mb={3}
                  w={1}
                />
                <Stat f={6} mb={2} value={stats.club} label="per club" />
                <Stat f={6} value={stats.student} label="per student" />
              </Stats>
            </Box>
            <Box bg="snow" p={[3, 4]}>
              <Heading.h3 f={4} my={0} caps>
                Spending breakdown
              </Heading.h3>
              <Spent />
            </Box>
          </Financials>
        </Box>
      </Row>
      <Container {...content}>
        <Heading.h2 {...headline}>
          Contribute More Than Just Dollars
        </Heading.h2>
        <Text my={3} {...subtext}>
          We accept donations of time, technical or hard science fiction
          books, stocks / other securities, and cryptocurrency.
        </Text>
        <Text my={3} {...subtext}>
          Please get in touch at{' '}
          donate@hackclub.com
          {' '}if you're interested in making a contribution or have any
          questions.
        </Text>
      </Container>
      <Container {...content} mx="auto" align="center" my={5}>
        <Heading.h2 {...headline} my={3} w={1}>
          Our Donors
        </Heading.h2>
        <Flex wrap="wrap" justify="center">
          {Object.keys(donors).map((name, index) => (
            <DonorListing key={index} name={name} url={donors[name]} />
          ))}
        </Flex>
      </Container>
    </Container>

  </Fragment>
)
<|endoftext|>