<commit_before>/* global fetch, Headers, Request */
// @flow
import qs from 'qs'
import deepExtend from 'deep-extend'
type OptionsRequest = {
  abort: () => void;
  promise: Promise<*>;
}

type Method = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
type Options = {
  method: Method;
  headers?: ?{ [key: string]: string };
  onProgress?: (num: number) => mixed;
  data?: ?{ [key: string]: mixed };
}

export function ajaxOptions (options: Options): any {
  const headers = new Headers(Object.assign({}, {
    'Content-Type': 'application/json'
  }, options.headers))

  return {
    ...options,
    method: options.method,
    headers,
    body: options.data ? JSON.stringify(options.data) : null
  }
}

export function checkStatus (response: any): any {
  return response.json().then(json => {
    return response.ok ? json : Promise.reject(json)
  })
}

function ajax (url: string, options: Options): OptionsRequest {
  if (options.method === 'GET' && options.data) {
    url = `${url}?${qs.stringify(options.data)}`
    delete options.data
  }
  const request = new Request(url, ajaxOptions(options))
  const xhr = fetch(request)
  const promise = new Promise((resolve, reject) => {
    xhr.then(checkStatus).then(resolve, (error) => {
      const ret = error ? error.errors : {}

      return reject(ret || {})
    })
  })

  const abort = () => {} // noop, fetch is not cancelable

  return { abort, promise }
}

export default {
  apiPath: '',
  commonOptions: {},

  get (path: string, data: ?{}, options?: {} = {}): OptionsRequest {
    return ajax(
      `${this.apiPath}${path}`,
      deepExtend({}, { method: 'GET' }, this.commonOptions, options, { data })
    )
  },

  post (path: string, data: ?{}, options?: {} = {}): OptionsRequest {
    return ajax(
      `${this.apiPath}${path}`,
      deepExtend({}, { method: 'POST' }, this.commonOptions, options, { data })
    )
  },

  put (path: string, data: ?{}, options?: {} = {}): OptionsRequest {
    return ajax(
      `${this.apiPath}${path}`,
      deepExtend({}, { method: 'PUT' }, this.commonOptions, options, { data })
    )
  },

  del (path: string, options?: {} = {}): OptionsRequest {
    return ajax(
      `${this.apiPath}${path}`,
      deepExtend({}, { method: 'DELETE' }, this.commonOptions, options)
    )
  }
}
<commit_msg>Refactor<commit_after>/* global fetch, Headers, Request */
// @flow
import qs from 'qs'
import deepExtend from 'deep-extend'
type OptionsRequest = {
  abort: () => void;
  promise: Promise<*>;
}

type Method = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
type Options = {
  method: Method;
  headers?: ?{ [key: string]: string };
  onProgress?: (num: number) => mixed;
  data?: ?{ [key: string]: mixed };
}

export function ajaxOptions (options: Options): any {
  const { headers, data, ...otherOptions } = options
  const headersObject = new Headers(Object.assign({}, {
    'Content-Type': 'application/json'
  }, headers))

  return {
    ...otherOptions,
    headers: headersObject,
    body: data ? JSON.stringify(data) : null
  }
}

export function checkStatus (response: any): any {
  return response.json().then(json => {
    return response.ok ? json : Promise.reject(json)
  })
}

function ajax (url: string, options: Options): OptionsRequest {
  if (options.method === 'GET' && options.data) {
    url = `${url}?${qs.stringify(options.data)}`
    delete options.data
  }
  const request = new Request(url, ajaxOptions(options))
  const xhr = fetch(request)
  const promise = new Promise((resolve, reject) => {
    xhr.then(checkStatus).then(resolve, (error) => {
      const ret = error ? error.errors : {}

      return reject(ret || {})
    })
  })

  const abort = () => {} // noop, fetch is not cancelable

  return { abort, promise }
}

export default {
  apiPath: '',
  commonOptions: {},

  get (path: string, data: ?{}, options?: {} = {}): OptionsRequest {
    return ajax(
      `${this.apiPath}${path}`,
      deepExtend({}, { method: 'GET' }, this.commonOptions, options, { data })
    )
  },

  post (path: string, data: ?{}, options?: {} = {}): OptionsRequest {
    return ajax(
      `${this.apiPath}${path}`,
      deepExtend({}, { method: 'POST' }, this.commonOptions, options, { data })
    )
  },

  put (path: string, data: ?{}, options?: {} = {}): OptionsRequest {
    return ajax(
      `${this.apiPath}${path}`,
      deepExtend({}, { method: 'PUT' }, this.commonOptions, options, { data })
    )
  },

  del (path: string, options?: {} = {}): OptionsRequest {
    return ajax(
      `${this.apiPath}${path}`,
      deepExtend({}, { method: 'DELETE' }, this.commonOptions, options)
    )
  }
}
<|endoftext|>


<commit_before>const stringify = require('json-stringify-safe');
const isProd = process.env.NODE_ENV === 'production';

module.exports = {
  'application/vnd.api+json': function jsonapiFormatter(req, res, body, next) {
    const response = {
      meta: {
        id: req.id(),
      },
    };

    if (res.meta) {
      Object.assign(response.meta, res.meta);
    }

    if (res.links) {
      response.links = Object.assign({}, res.links);
    }

    if (!isProd) {
      response.meta.timers = req.timers;
    }

    if (body instanceof Error) {
      // check for error message
      res.statusCode = body.statusCode || body.code || body.status_code || 500;
      response.errors = [];

      let err;
      if (typeof body.toJSON === 'function') {
        err = body.toJSON();
        response.errors.push({
          status: body.name,
          code: res.statusCode,
          title: typeof body.generateMessage === 'function' ? body.generateMessage() : (err.message || err.text),
          detail: err.errors,
        });
      } else {
        response.errors.push({
          status: body.name || 'InternalServerError',
          code: res.statusCode,
          title: body.toString(),
          detail: body.reason || body.errors || {},
          stack: isProd ? undefined : (typeof body.stack === 'string' ? body.stack.split('\n') : body.stack),
        });
      }
    } else if (Buffer.isBuffer(body)) {
      response.data = body.toString('base64');
    } else if (body) {
      response.data = body;
    }

    const data = stringify(response);
    res.setHeader('Content-Type', 'application/vnd.api+json');
    res.setHeader('Content-Length', Buffer.byteLength(data));

    return next(null, data);
  },
};
<commit_msg>Add parser to readable<commit_after>'use strict';

const stringify = require('json-stringify-safe');
const isProd = process.env.NODE_ENV === 'production';

const titleToReadable = function(inputString) {
  const regEx = /.*\.([^.]*)/;
  if (!inputString) {
    return undefined;
  }
  return inputString.substring(inputString.indexOf(':') + 2).split(', ').map((eTitle) => {
    return regEx.exec(eTitle)[1];
  }).join(', ');
};

module.exports = {
  'application/vnd.api+json': function jsonapiFormatter(req, res, body, next) {
    const response = {
      meta: {
        id: req.id(),
      },
    };

    if (res.meta) {
      Object.assign(response.meta, res.meta);
    }

    if (res.links) {
      response.links = Object.assign({}, res.links);
    }

    if (!isProd) {
      response.meta.timers = req.timers;
    }

    if (body instanceof Error) {
      // check for error message
      res.statusCode = body.statusCode || body.code || body.status_code || 500;
      response.errors = [];

      let err;
      if (typeof body.toJSON === 'function') {
        err = body.toJSON();
        const title = typeof body.generateMessage === 'function' ? body.generateMessage() : (err.message || err.text);
        response.errors.push({
          status: body.name,
          code: res.statusCode,
          title: titleToReadable(title),
          detail: err.errors,
        });
      } else {
        const title = isProd ? undefined : (typeof body.stack === 'string' ? body.stack.split('\n') : body.stack);
        response.errors.push({
          status: body.name || 'InternalServerError',
          code: res.statusCode,
          title: body.toString(),
          detail: body.reason || body.errors || {},
          stack: titleToReadable(title),
        });
      }

      ///////////////////// readable format of title

      ///////////
    } else if (Buffer.isBuffer(body)) {
      response.data = body.toString('base64');
    } else if (body) {
      response.data = body;
    }

    const data = stringify(response);
    res.setHeader('Content-Type', 'application/vnd.api+json');
    res.setHeader('Content-Length', Buffer.byteLength(data));

    return next(null, data);
  },
};
<|endoftext|>


<commit_before>import * as d3 from 'd3';

const defaults = {
  dateRange: [
    new Date(2017, 1, 1),
    new Date(2018, 1, 1),
  ],
  timeFormat: '%B',
  // ticksIntervals: 'Month',
  data: [
  ],
  intervals: [
  ],
  onClick: function() {
  },
};

/**
 * Timeline class
 *
 * @export
 * @class Timeline
 */
export default class Timeline {

  /**
   * Creates an instance of Timeline
   *
   * @param {object} conf Configuration
   */
  constructor(conf) {
    this.setConf(conf);
    this.init();
  }

  /**
   * Initialize timeline
   */
  init() {
    this.svg = d3.select('svg');
    this.margin = {top: 20, right: 20, bottom: 20, left: 20};
    this.width = +this.svg.attr('width') - this.margin.left - this.margin.right;
    this.height = +this.svg.attr('height') - this.margin.top - this.margin.bottom;
    this.positionY = this.height / 1.4;
    this.timeline = this.svg.append('g')
        .attr('class', 'timeline')
        .attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')');
    this.axis = this.timeline.append('g')
        .attr('class', 'axis axis--x')
        .attr('transform', 'translate(0,' + this.positionY + ')');
  }

  /**
   * Set timeline configuration
   *
   * @param {object} conf
   */
  setConf(conf) {
    // Override the default configuration
    Object.assign(this, defaults, conf);
  }

  /**
   * Render x axis
   */
  renderAxis() {
    const pivots = this.getPivots(this.dateRange, this.intervals);
    const domain = this.intervals.reduce((all, int) => all.concat([int[0], int[1]]), []);

    // Create polylinear scale time (from range A to range B with intervals in between)
    this.x = d3.scaleTime()
      .domain([
        d3.extent(this.dateRange)[0],
        ...domain,
        d3.extent(this.dateRange)[1],
      ])
      .range([0, ...pivots, this.width]); // all pivots

    // Create axis with the given tick interval
    this.xAxis = d3.axisBottom(this.x)
      // .ticks(d3[`time${this.ticksIntervals}`]) // timeDay, timeWeek, timeMonth, timeYear
      .tickFormat(d3.timeFormat(this.timeFormat))
      .tickValues([...this.x.domain(), new Date()])
      .tickPadding(-70)
      .tickSize(20);

    // Draw axis
    this.axis
      .transition()
      .duration(500)
      .call(this.xAxis);

    let lines = this.timeline.selectAll('.reference-line')
      .data(pivots, (d) => +d);

    // Draw intervals separation
    lines
      .enter()
      .append('line')
      .attr('class', 'linear reference-line')
        .attr('x1', (pivot) => pivot + .5)
        .attr('x2', (pivot) => pivot + .5)
        .attr('y1', this.positionY - 30)
        .attr('y2', this.positionY + 30);

    // Add special reference line for today
    let todayLine = this.timeline.selectAll('.reference-line-today.reference-line')
      .data([this.x(new Date())], (d) => d);

    todayLine.enter()
      .append('line')
      .attr('class', 'linear reference-line reference-line-today')
        .attr('x1', (pivot) => pivot + .5)
        .attr('x2', (pivot) => pivot + .5)
        .attr('y1', this.positionY - 30)
        .attr('y2', this.positionY);

    // Remove old intervals separation if needed
    lines
      .exit()
      .remove();
  }

  /**
   * Compute pivots for intervals based on dateRange
   *
   * @param {array} dateRange
   * @param {array} intervals
   * @return {array} List of pivots (in px)
   */
  getPivots(dateRange, intervals) {
    // Sum intervals size in px
    const intervalsSum = intervals.reduce((sum, interval) => sum + interval[2], 0);

    // Sum intervals duration
    const intervalsDateSum = intervals
      .reduce((duration, interval) => duration += interval[1] - interval[0], 0);

    // Create the main scale without intervals
    const xMain = d3.scaleTime()
      .domain(d3.extent([dateRange[0], dateRange[1] - intervalsDateSum]))
      .range([0, this.width - intervalsSum]);

    // Compute each pivot
    return intervals.reduce((res, interval) => {
      const xInterpolate = xMain(interval[0]);
      return {
        pivots: res.pivots.concat([
          xInterpolate + res.eaten,
          xMain(interval[0]) + interval[2] + res.eaten,
        ]),
        // Intervals previously "eaten" (in px)
        eaten: res.eaten + xInterpolate - xMain(interval[1]) + interval[2],
      };
    }, {
      pivots: [],
      eaten: 0,
    }).pivots;
  }

  /**
   * Render data as circles on the timeline
   *
   * @param {array} data
   */
  renderData(data) {
    let circles = this.timeline.selectAll('circle')
      .data(data, (d) => d);

    // Draw circles
    circles
      .enter()
      .append('circle')
      .attr('class', 'dot')
      .attr('cx', (d) => this.x(d.date))
      .attr('cy', this.positionY)
      .attr('r', 0)
      .transition()
      .attr('r', 5);

    this.timeline.selectAll('circle')
      .on('click', (circle) => {
        d3.select(d3.event.target)
          .transition()
          .ease(d3.easeBounceOut)
          .duration(500)
          .attr('r', 15)
          .transition()
          .duration(500)
          .call(() => {
            if (this.onClick) {
              this.onClick.apply(circle);
            }
            // d3.event.stopPropagation();
          })
          .delay(500)
          .attr('r', 5); // reset size
    });

    // Remove out of frame circles
    circles
      .exit()
      .remove();
  }

  /**
   * Update data
   *
   * @param {object} newConf Configuration
   */
  update(newConf) {
    this.setConf(newConf);

    // Range
    if (newConf.dateRange) {
      this.x.domain(d3.extent(newConf.dateRange));

      // TODO
      // if (newConf.intervals) {
      //   const pivots = this.getPivots(newConf.dateRange, newConf.intervals);
      //   const range = [0, ...pivots, this.width];
      //   this.x.range(range);
      // }
    }

    // Intervals
    // if (newConf.ticksIntervals) {
    //   this.xAxis.ticks(d3[`time${newConf.ticksIntervals}`]);
    // }

    if (newConf.timeFormat && newConf.timeFormat !== '') {
      this.xAxis.tickFormat((d) => d3.timeFormat(newConf.timeFormat)(d));
    } else {
      // Reset default format
      this.xAxis.tickFormat(null);
    }

    // if (newConf.ticksIntervals || newConf.dateRange) {
    this.renderAxis();
    // this.axis
    //   .transition()
    //   .duration(500)
    //   .call(this.xAxis);
    // }

    this.renderData(newConf.data);
  }

  /**
   * Render timeline
   */
  render() {
    this.renderAxis();
    this.renderData(this.data);
  }
}

// let t = d3.transition()
//     .duration(500)
//     .ease(d3.easeLinear);

<commit_msg>refactor: Separate axis for ticks and labels<commit_after>import * as d3 from 'd3';

const defaults = {
  dateRange: [
    new Date(2017, 1, 1),
    new Date(2018, 1, 1),
  ],
  timeFormat: '%B',
  // ticksIntervals: 'Month',
  data: [
  ],
  intervals: [
  ],
  onClick: function() {
  },
};

/**
 * Timeline class
 *
 * @export
 * @class Timeline
 */
export default class Timeline {

  /**
   * Creates an instance of Timeline
   *
   * @param {object} conf Configuration
   */
  constructor(conf) {
    this.setConf(conf);
    this.init();
  }

  /**
   * Initialize timeline
   */
  init() {
    this.svg = d3.select('svg');
    this.margin = {top: 20, right: 20, bottom: 20, left: 20};
    this.width = +this.svg.attr('width') - this.margin.left - this.margin.right;
    this.height = +this.svg.attr('height') - this.margin.top - this.margin.bottom;
    this.positionY = this.height / 1.4;
    this.timeline = this.svg.append('g')
        .attr('class', 'timeline')
        .attr('transform', `translate(${this.margin.left}, ${this.margin.top})`);
    this.axisLabels = this.timeline.append('g')
        .attr('class', 'axis axis--x')
        .attr('transform', `translate(0, ${this.positionY})`);
    this.axisTicks = this.timeline.append('g')
        .attr('class', 'axis axis--x')
        .attr('transform', `translate(0, ${this.positionY})`);
  }

  /**
   * Set timeline configuration
   *
   * @param {object} conf
   */
  setConf(conf) {
    // Override the default configuration
    Object.assign(this, defaults, conf);
  }

  /**
   * Render x axis
   */
  renderAxis() {
    const pivots = this.getPivots(this.dateRange, this.intervals);
    const domain = this.intervals.reduce((all, int) => all.concat([int[0], int[1]]), []);

    // Create polylinear scale time (from range A to range B with intervals in between)
    this.x = d3.scaleTime()
      .domain([
        d3.extent(this.dateRange)[0],
        ...domain,
        d3.extent(this.dateRange)[1],
      ])
      .range([0, ...pivots, this.width]); // all pivots

    // Create axis with the given tick interval
    const xAxisLabel = d3.axisBottom(this.x)
      // .ticks(d3[`time${this.ticksIntervals}`]) // timeDay, timeWeek, timeMonth, timeYear
      .tickFormat(d3.timeFormat(this.timeFormat))
      .tickValues([new Date(), ...this.x.domain()])
      .tickPadding(-50)
      .tickSize(0);

    const xAxisTicks = d3.axisBottom(this.x)
      .tickFormat('')
      .tickPadding(-70)
      .tickSize(20);

    // Draw axis
    this.axisLabels
      .transition()
      .duration(500)
      .call(xAxisLabel);

    // Draw axis
    this.axisTicks
      .transition()
      .duration(500)
      .call(xAxisTicks);

    let lines = this.timeline.selectAll('.reference-line')
      .data(pivots, (d) => +d);

    // Draw intervals separation
    lines.enter()
      .append('line')
      .attr('class', 'linear reference-line')
        .attr('x1', (pivot) => pivot + .5)
        .attr('x2', (pivot) => pivot + .5)
        .attr('y1', this.positionY - 30)
        .attr('y2', this.positionY + 30);

    // Add special reference line for today
    let todayLine = this.timeline.selectAll('.reference-line-today.reference-line')
      .data([this.x(new Date())], (d) => d);

    todayLine.enter()
      .append('line')
      .attr('class', 'linear reference-line reference-line-today')
        .attr('x1', (pivot) => pivot + .5)
        .attr('x2', (pivot) => pivot + .5)
        .attr('y1', this.positionY - 30)
        .attr('y2', this.positionY);

    // Remove old intervals separation if needed
    lines.exit()
      .remove();
  }

  /**
   * Compute pivots for intervals based on dateRange
   *
   * @param {array} dateRange
   * @param {array} intervals
   * @return {array} List of pivots (in px)
   */
  getPivots(dateRange, intervals) {
    // Sum intervals size in px
    const intervalsSum = intervals.reduce((sum, interval) => sum + interval[2], 0);

    // Sum intervals duration
    const intervalsDateSum = intervals
      .reduce((duration, interval) => duration += interval[1] - interval[0], 0);

    // Create the main scale without intervals
    const xMain = d3.scaleTime()
      .domain(d3.extent([dateRange[0], dateRange[1] - intervalsDateSum]))
      .range([0, this.width - intervalsSum]);

    // Compute each pivot
    return intervals.reduce((res, interval) => {
      const xInterpolate = xMain(interval[0]);
      return {
        pivots: res.pivots.concat([
          xInterpolate + res.eaten,
          xMain(interval[0]) + interval[2] + res.eaten,
        ]),
        // Intervals previously "eaten" (in px)
        eaten: res.eaten + xInterpolate - xMain(interval[1]) + interval[2],
      };
    }, {
      pivots: [],
      eaten: 0,
    }).pivots;
  }

  /**
   * Render data as circles on the timeline
   *
   * @param {array} data
   */
  renderData(data) {
    let circles = this.timeline.selectAll('circle')
      .data(data, (d) => d);

    // Draw circles
    circles
      .enter()
      .append('circle')
      .attr('class', 'dot')
      .attr('cx', (d) => this.x(d.date))
      .attr('cy', this.positionY)
      .attr('r', 0)
      .transition()
      .attr('r', 5);

    this.timeline.selectAll('circle')
      .on('click', (circle) => {
        d3.select(d3.event.target)
          .transition()
          .ease(d3.easeBounceOut)
          .duration(500)
          .attr('r', 15)
          .transition()
          .duration(500)
          .call(() => {
            if (this.onClick) {
              this.onClick.apply(circle);
            }
            // d3.event.stopPropagation();
          })
          .delay(500)
          .attr('r', 5); // reset size
    });

    // Remove out of frame circles
    circles
      .exit()
      .remove();
  }

  /**
   * Update data
   *
   * @param {object} newConf Configuration
   */
  update(newConf) {
    this.setConf(newConf);

    // Range
    if (newConf.dateRange) {
      this.x.domain(d3.extent(newConf.dateRange));

      // TODO
      // if (newConf.intervals) {
      //   const pivots = this.getPivots(newConf.dateRange, newConf.intervals);
      //   const range = [0, ...pivots, this.width];
      //   this.x.range(range);
      // }
    }

    // Intervals
    // if (newConf.ticksIntervals) {
    //   this.xAxis.ticks(d3[`time${newConf.ticksIntervals}`]);
    // }

    // if (newConf.timeFormat && newConf.timeFormat !== '') {
    //   // this.xAxis.tickFormat((d) => d3.timeFormat(newConf.timeFormat)(d));
    // } else {
    //   // Reset default format
    //   // this.xAxis.tickFormat(null);
    // }

    // if (newConf.ticksIntervals || newConf.dateRange) {
    this.renderAxis();
    // this.axis
    //   .transition()
    //   .duration(500)
    //   .call(this.xAxis);
    // }

    this.renderData(newConf.data);
  }

  /**
   * Render timeline
   */
  render() {
    this.renderAxis();
    this.renderData(this.data);
  }
}

// let t = d3.transition()
//     .duration(500)
//     .ease(d3.easeLinear);

<|endoftext|>


<commit_before>/* @flow */
/* eslint-env browser */
import Err from 'es6-error'

export type StrDict = {[id: string]: string}

/**
 * Bound values to url placeholders or add query string
 */
export type SerializeParams = (url: string, params: StrDict) => string

export type PostProcess<I, O> = (params: I) => O
export type Preprocess<Result, Params>
    = (req: IFetcher<Result, Params>) => Promise<IFetcher<Result, Params>>

/**
 * Input args for Fetcher
 *
 * @example
```js
{
    baseUrl: '/api',
    headers: {
        'Accept-Language': 'ru;q=0.8,en-US;q=0.6,en;q=0.4'
    },
    method: 'GET',
    postProcess,
    serializeParams
}
```
 */
export type FetcherRec<Params: Object> = {
    /**
     * `baseUrl` will be prepended to `url`.
     *
     * Supported placeholders like `:id`.
     *
     * @example https://some-serckkver.org/
     */
    baseUrl?: ?string;

    /**
     * Relative server url.
     *
     * Supported placeholders like `:id`.
     *
     * @example /user/:id
     */
    url?: ?string;

    /**
     * Url parameters key-value dict.
     *
     * If exists url placeholder with key, this value replaces url placeholder.
     * If no placeholder found - parameters adds as querystring.
     *
     * @example /user/:id/:some + {id: 1, some: 'test'} = /user/1?some=test
     */
    params?: ?Params;

    /**
     * Params serializer function.
     *
     * @example
     ```js
     //@flow
     function serializeParams(url: string, params: ?Object): string {
         const qStr: ?string = params ? querystring.stringify(params) : null
         return url + (qStr ? ('?' + qStr) : '')
     }
     ```
     */
    serializeParams?: ?SerializeParams;

    /**
     * Composable postProcess function.
     *
     * @example
     * ```js
     * // @flow
     *
     * function postProcess<Result>(response: Promise<Response>): Promise<Result> {
     *     return response.then(r => r.json)
     * }
     *
     * fetch(fullUrl, options).then(postProcess)
     * ```
     */
    postProcess?: ?PostProcess<*, *>;

    /**
     * Preprocess Request options before fetch
     *
     * @example
     * ```js
     * // @flow
     *
     * function preProcess<R, P>(opts: IFetcher<R, P>): Promise<IFetcher<R, P>> {
     *     return Promise.resolve(opts)
     * }
     *
     * preprocess(fetcher).then((f) => fetch(f.fullUrl, f.options).then(f.postProcess))
     * ```
     */
    preProcess?: ?Preprocess<*, *>;

    /**
     * Whatwg fetch function, default to global fetch
     */
    fetchFn?: ?FetchFn;

    /**
     * Request body.
     *
     * Plain objects will be searilzed to json string.
     */
    body?: ?(Blob | FormData | URLSearchParams | string | Object);

    /**
     * Below parameters from RequestOptions
     *
     * @see RequestOptions in https://github.com/facebook/flow/blob/master/lib/bom.js
     */
    cache?: ?CacheType;
    credentials?: ?CredentialsType;
    headers?: ?HeadersInit;
    integrity?: ?string;
    method?: ?MethodType;
    mode?: ?ModeType;
    redirect?: ?RedirectType;
    referrer?: ?string;
    referrerPolicy?: ?ReferrerPolicyType;
}

export interface IFetcher<Result, Params: Object> {
    /**
     * Request options.
     */
    options: RequestOptions;

    /**
     * Generated full url from baseUrl, url and params.
     */
    fullUrl: string;

    /**
     * Composable fetch.then postProcess function.
     */
    postProcess: (response: Promise<Response>) => Promise<Result>;

    /**
     * Create new copy of Fetcher with some options redefined.
     *
     * Headers will be merged with existing headers.
     * postProcess will be composed with existing postProcess.
     */
    copy<R, P: Object>(rec: FetcherRec<P>): IFetcher<R, P>;

    /**
     * Fetch data
     *
     */
    fetch(rec?: FetcherRec<*>): Promise<Result>;
}

function isFormData(val: Object): boolean {
    return (typeof FormData !== 'undefined') && (val instanceof FormData)
}

const toString = Object.prototype.toString
function isBlob(val: Object): boolean {
    return toString.call(val) === '[object Blob]'
}

function isURLSearchParams(val: Object): boolean {
    return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams
}

/**
 * Merge headers objects.
 *
 * Uses global Headers class if exists.
 *
 * @example
 * ```js
 * // @flow
 * mergeHeaders({a: 1}, {a: 2, b: 3})
 * // If Headers not supported, result is object: {a: 2, b: 3}
 * // Else result is new Headers({a: 2, b: 3})
 * ```
 */
export function mergeHeaders(...headerSets: (?HeadersInit)[]): HeadersInit {
    const isSupported: boolean = typeof Headers !== 'undefined'
    let result: HeadersInit = isSupported ? new Headers() : {}

    for (let i = 0, l = headerSets.length; i < l; i++) {
        const headers: ?HeadersInit = headerSets[i]
        if (!headers || typeof headers !== 'object') {
            continue // eslint-disable-line
        }

        if (isSupported && result instanceof Headers) {
            if (headers instanceof Headers && Array.from) {
                const entries: [string, string][] = Array.from(headers.entries())
                for (let j = 0, k = entries.length; j < k; j++) {
                    const [name, v] = entries[j]
                    result.append(name, v)
                }
            } else {
                const entries: string[] = Object.keys(headers)
                for (let j = 0, k = entries.length; j < k; j++) {
                    result.append(entries[j], headers[entries[j]])
                }
            }
        } else {
            result = {...result, ...(headers: Object)}
        }
    }

    return result
}

export class HttpError extends Err {
    response: Response;

    constructor(response: Response) {
        super(response.statusText)
        this.response = response
    }
}

/**
 * Check response status value and throw error if it not in 200-300 range.
 */
export function checkStatus(response: Response): Response {
    if (response.status >= 200 && response.status < 300) {
        return response
    }
    throw new HttpError(response)
}

function regExpMapString(replaceRegExp: RegExp, template: string, params: StrDict): {
    str: string,
    newParams: StrDict
} {
    const newParams: StrDict = {...params}
    const str: string = template.replace(replaceRegExp, (v, k: string) => {
        if (!params[k]) {
            throw new Error(`No parameter provided to params: ${k}`)
        }
        delete newParams[k]
        return params[k]
    })

    return {
        str,
        newParams
    }
}

const DEFAULT_MAP_REGEXP = new RegExp(':([\\w]+)', 'g')

/**
 * Create params serializer.
 */
export function createSerializeParams(
    stringify: (params: StrDict) => string,
    placeholderRegExp: RegExp = DEFAULT_MAP_REGEXP
): SerializeParams {
    function serializeParams(url: string, params: StrDict): string {
        const {str, newParams} = regExpMapString(placeholderRegExp, url, params)
        const qStr: string = stringify(newParams)

        return str + (qStr ? ('?' + qStr) : '')
    }

    return serializeParams
}

function compose<In1, Out1, Out2>(
    f1: (arg: In1) => Out1,
    f2: (arg: Out1) => Out2
): (arg: In1) => Out2 {
    return (arg: In1) => f2(f1(arg))
}

function pass<V>(arg: V): any {
    return arg
}

export type FetchFn = (url: string, options: RequestOptions) => Promise<Response>;

/**
 * Cacheable data loader
 */
export class Loader<Result> {
    _result: ?Promise<Result> = null
    _fetcher: ?IFetcher<Result, *>

    constructor(fetcher?: IFetcher<Result, *>) {
        this._fetcher = fetcher || null
    }

    _onError: (err: Error) => void = (err: Error) => {
        this.reset()
        throw err
    }

    fetch(): Promise<Result> {
        let result: ?Promise<Result> = this._result
        if (result) {
            return result
        }

        result = this._fetch(this._fetcher)
            .catch(this._onError)

        this._result = result

        return result
    }

    _fetch(fetcher?: ?IFetcher<Result, *>): Promise<Result> { // eslint-disable-line
        if (!fetcher) {
            throw new Error('Fetcher is not initialized')
        }

        return fetcher.fetch()
    }

    reset(): Loader<Result> {
        this._result = null
        return this
    }
}

function defaultGetKey(rec: FetcherRec<*>): string {
    const params: {[id: string]: string} = rec.params || {}
    return Object.keys(params).sort().map((key: string) => `${key}:${params[key]}`).join('.')
}

function callFetch<Result, Params: Object>(f: IFetcher<Result, Params>): Promise<Result> {
    return f.fetch()
}

/**
 * Fetch options builder
 */
export class Fetcher<Result, Params: Object> {
    _baseUrl: string
    _serializeParams: ?SerializeParams
    _url: string
    _params: ?Params
    _fetchFn: FetchFn

    /**
     * Request options.
     */
    options: RequestOptions

    /**
     * Generated full url from baseUrl, url and params.
     */
    fullUrl: string

    postProcess: (req: Promise<Response>) => Promise<Result>

    preProcess: ?Preprocess<Result, Params>;

    constructor(rec?: FetcherRec<Params> = {}) {
        this._baseUrl = rec.baseUrl || '/'
        this._serializeParams = rec.serializeParams
        this._params = rec.params || null
        this._url = rec.url || ''
        this.postProcess = rec.postProcess || pass
        this._fetchFn = rec.fetchFn || (typeof fetch === 'undefined' ? pass : fetch)
        this.preProcess = rec.preProcess || null
        let headers: ?HeadersInit = rec.headers || {}

        let isPlainObject: boolean = false
        const body = rec.body
        if (body && typeof body === 'object') {
            const isUrlSearchParams: boolean = isURLSearchParams(body)
            isPlainObject =
                !isFormData(body)
                && !isBlob(body)
                && !isUrlSearchParams

            if (isUrlSearchParams) {
                if (!headers) {
                    headers = {}
                }
                if (typeof Headers !== 'undefined' && headers instanceof Headers) {
                    if (!headers.has('Content-type')) {
                        headers.set(
                            'Content-Type',
                            'application/x-www-form-urlencoded;charset=utf-8'
                        )
                    }
                } else if (!(headers: Object)['Content-Type']) {
                    (headers: Object)['Content-Type']
                        = 'application/x-www-form-urlencoded;charset=utf-8'
                }
            }
        }

        const options: RequestOptions = {
            body: isPlainObject && body ? JSON.stringify(body) : body,
            headers
        }
        if (rec.cache) options.cache = rec.cache
        if (rec.credentials) options.credentials = rec.credentials
        if (rec.integrity) options.integrity = rec.integrity
        if (rec.method) options.method = rec.method
        if (rec.mode) options.mode = rec.mode
        if (rec.redirect) options.redirect = rec.redirect
        if (rec.referrer) options.referrer = rec.referrer
        if (rec.referrerPolicy) options.referrerPolicy = rec.referrerPolicy

        this.options = options

        if (this._params) {
            if (!this._serializeParams) {
                throw new TypeError('params exists, but no serializeParams method provided')
            }
            this.fullUrl = this._baseUrl + this._serializeParams(this._url, this._params)
        } else {
            this.fullUrl = this._baseUrl + this._url
        }
    }

    /**
     * Create new copy of Fetcher with some options redefined.
     *
     * Headers will be merged with existing headers.
     * postProcessors will be composed with existing postProcessors.
     */
    copy<R, P: Object>(rec: FetcherRec<any>): IFetcher<R, P> {
        const headers: ?HeadersInit = this.options.headers
        return (new this.constructor({
            baseUrl: rec.baseUrl || this._baseUrl,
            serializeParams: this._serializeParams,
            url: this._url,
            ...this.options,
            ...rec,
            postProcess: rec.postProcess
                ? compose(this.postProcess, rec.postProcess)
                : this.postProcess,
            params: this._params
                ? {...this._params, ...rec.params || {}}
                : rec.params,
            headers: rec.headers
                ? mergeHeaders(headers, rec.headers)
                : headers
        }): any)
    }

    _fetch(f: IFetcher<Result, Params>): Promise<Result> {
        const result = this._fetchFn.call(null, f.fullUrl, f.options)
        return f.postProcess(result)
    }

    fetch(rec?: FetcherRec<*>): Promise<Result> {
        const opts: IFetcher<Result, Params> = rec
            ? this.copy(rec)
            : this

        return this.preProcess
            ? this.preProcess(opts).then(callFetch)
            : this._fetch(opts)
    }
}

if (0) ((new Fetcher(...(0: any))): IFetcher<*, *>) // eslint-disable-line

export class Repository<Result> {
    _loaders: Map<string, Loader<Result>>;
    _getKey: (params: FetcherRec<*>) => string;

    constructor(
        getKey?: ?(params: FetcherRec<*>) => string
    ) {
        this._loaders = new Map()
        this._getKey = getKey || defaultGetKey
    }

    fetch(params: FetcherRec<*>): Promise<Result> {
        const key: string = this._getKey(params)
        let loader: ?Loader<Result> = this._loaders.get(key)
        if (!loader) {
            loader = new Loader(new Fetcher(params))
            this._loaders.set(key, loader)
        }

        return loader.fetch()
    }

    _clearAll: (loader: Loader<Result>) => void = (loader: Loader<Result>) => {
        loader.reset()
    };

    reset(params?: FetcherRec<*>): Repository<Result> {
        if (!params) {
            this._loaders.forEach(this._clearAll)
            return this
        }

        const loader: ?Loader<Result> = this._loaders.get(this._getKey(params))
        if (loader) {
            loader.reset()
        }

        return this
    }
}
<commit_msg>add normalize context-type for object body<commit_after>/* @flow */
/* eslint-env browser */
import Err from 'es6-error'

export type StrDict = {[id: string]: string}

/**
 * Bound values to url placeholders or add query string
 */
export type SerializeParams = (url: string, params: StrDict) => string

export type PostProcess<I, O> = (params: I) => O
export type Preprocess<Result, Params>
    = (req: IFetcher<Result, Params>) => Promise<IFetcher<Result, Params>>

/**
 * Input args for Fetcher
 *
 * @example
```js
{
    baseUrl: '/api',
    headers: {
        'Accept-Language': 'ru;q=0.8,en-US;q=0.6,en;q=0.4'
    },
    method: 'GET',
    postProcess,
    serializeParams
}
```
 */
export type FetcherRec<Params: Object> = {
    /**
     * `baseUrl` will be prepended to `url`.
     *
     * Supported placeholders like `:id`.
     *
     * @example https://some-serckkver.org/
     */
    baseUrl?: ?string;

    /**
     * Relative server url.
     *
     * Supported placeholders like `:id`.
     *
     * @example /user/:id
     */
    url?: ?string;

    /**
     * Url parameters key-value dict.
     *
     * If exists url placeholder with key, this value replaces url placeholder.
     * If no placeholder found - parameters adds as querystring.
     *
     * @example /user/:id/:some + {id: 1, some: 'test'} = /user/1?some=test
     */
    params?: ?Params;

    /**
     * Params serializer function.
     *
     * @example
     ```js
     //@flow
     function serializeParams(url: string, params: ?Object): string {
         const qStr: ?string = params ? querystring.stringify(params) : null
         return url + (qStr ? ('?' + qStr) : '')
     }
     ```
     */
    serializeParams?: ?SerializeParams;

    /**
     * Composable postProcess function.
     *
     * @example
     * ```js
     * // @flow
     *
     * function postProcess<Result>(response: Promise<Response>): Promise<Result> {
     *     return response.then(r => r.json)
     * }
     *
     * fetch(fullUrl, options).then(postProcess)
     * ```
     */
    postProcess?: ?PostProcess<*, *>;

    /**
     * Preprocess Request options before fetch
     *
     * @example
     * ```js
     * // @flow
     *
     * function preProcess<R, P>(opts: IFetcher<R, P>): Promise<IFetcher<R, P>> {
     *     return Promise.resolve(opts)
     * }
     *
     * preprocess(fetcher).then((f) => fetch(f.fullUrl, f.options).then(f.postProcess))
     * ```
     */
    preProcess?: ?Preprocess<*, *>;

    /**
     * Whatwg fetch function, default to global fetch
     */
    fetchFn?: ?FetchFn;

    /**
     * Request body.
     *
     * Plain objects will be searilzed to json string.
     */
    body?: ?(Blob | FormData | URLSearchParams | string | Object);

    /**
     * Below parameters from RequestOptions
     *
     * @see RequestOptions in https://github.com/facebook/flow/blob/master/lib/bom.js
     */
    cache?: ?CacheType;
    credentials?: ?CredentialsType;
    headers?: ?HeadersInit;
    integrity?: ?string;
    method?: ?MethodType;
    mode?: ?ModeType;
    redirect?: ?RedirectType;
    referrer?: ?string;
    referrerPolicy?: ?ReferrerPolicyType;
}

export interface IFetcher<Result, Params: Object> {
    /**
     * Request options.
     */
    options: RequestOptions;

    /**
     * Generated full url from baseUrl, url and params.
     */
    fullUrl: string;

    /**
     * Composable fetch.then postProcess function.
     */
    postProcess: (response: Promise<Response>) => Promise<Result>;

    /**
     * Create new copy of Fetcher with some options redefined.
     *
     * Headers will be merged with existing headers.
     * postProcess will be composed with existing postProcess.
     */
    copy<R, P: Object>(rec: FetcherRec<P>): IFetcher<R, P>;

    /**
     * Fetch data
     *
     */
    fetch(rec?: FetcherRec<*>): Promise<Result>;
}

function isFormData(val: Object): boolean {
    return (typeof FormData !== 'undefined') && (val instanceof FormData)
}

const toString = Object.prototype.toString
function isBlob(val: Object): boolean {
    return toString.call(val) === '[object Blob]'
}

function isURLSearchParams(val: Object): boolean {
    return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams
}

/**
 * Merge headers objects.
 *
 * Uses global Headers class if exists.
 *
 * @example
 * ```js
 * // @flow
 * mergeHeaders({a: 1}, {a: 2, b: 3})
 * // If Headers not supported, result is object: {a: 2, b: 3}
 * // Else result is new Headers({a: 2, b: 3})
 * ```
 */
export function mergeHeaders(...headerSets: (?HeadersInit)[]): HeadersInit {
    const isSupported: boolean = typeof Headers !== 'undefined'
    let result: HeadersInit = isSupported ? new Headers() : {}

    for (let i = 0, l = headerSets.length; i < l; i++) {
        const headers: ?HeadersInit = headerSets[i]
        if (!headers || typeof headers !== 'object') {
            continue // eslint-disable-line
        }

        if (isSupported && result instanceof Headers) {
            if (headers instanceof Headers && Array.from) {
                const entries: [string, string][] = Array.from(headers.entries())
                for (let j = 0, k = entries.length; j < k; j++) {
                    const [name, v] = entries[j]
                    result.append(name, v)
                }
            } else {
                const entries: string[] = Object.keys(headers)
                for (let j = 0, k = entries.length; j < k; j++) {
                    result.append(entries[j], headers[entries[j]])
                }
            }
        } else {
            result = {...result, ...(headers: Object)}
        }
    }

    return result
}

export class HttpError extends Err {
    response: Response;

    constructor(response: Response) {
        super(response.statusText)
        this.response = response
    }
}

/**
 * Check response status value and throw error if it not in 200-300 range.
 */
export function checkStatus(response: Response): Response {
    if (response.status >= 200 && response.status < 300) {
        return response
    }
    throw new HttpError(response)
}

function regExpMapString(replaceRegExp: RegExp, template: string, params: StrDict): {
    str: string,
    newParams: StrDict
} {
    const newParams: StrDict = {...params}
    const str: string = template.replace(replaceRegExp, (v, k: string) => {
        if (!params[k]) {
            throw new Error(`No parameter provided to params: ${k}`)
        }
        delete newParams[k]
        return params[k]
    })

    return {
        str,
        newParams
    }
}

const DEFAULT_MAP_REGEXP = new RegExp(':([\\w]+)', 'g')

/**
 * Create params serializer.
 */
export function createSerializeParams(
    stringify: (params: StrDict) => string,
    placeholderRegExp: RegExp = DEFAULT_MAP_REGEXP
): SerializeParams {
    function serializeParams(url: string, params: StrDict): string {
        const {str, newParams} = regExpMapString(placeholderRegExp, url, params)
        const qStr: string = stringify(newParams)

        return str + (qStr ? ('?' + qStr) : '')
    }

    return serializeParams
}

function compose<In1, Out1, Out2>(
    f1: (arg: In1) => Out1,
    f2: (arg: Out1) => Out2
): (arg: In1) => Out2 {
    return (arg: In1) => f2(f1(arg))
}

function pass<V>(arg: V): any {
    return arg
}

export type FetchFn = (url: string, options: RequestOptions) => Promise<Response>;

/**
 * Cacheable data loader
 */
export class Loader<Result> {
    _result: ?Promise<Result> = null
    _fetcher: ?IFetcher<Result, *>

    constructor(fetcher?: IFetcher<Result, *>) {
        this._fetcher = fetcher || null
    }

    _onError: (err: Error) => void = (err: Error) => {
        this.reset()
        throw err
    }

    fetch(): Promise<Result> {
        let result: ?Promise<Result> = this._result
        if (result) {
            return result
        }

        result = this._fetch(this._fetcher)
            .catch(this._onError)

        this._result = result

        return result
    }

    _fetch(fetcher?: ?IFetcher<Result, *>): Promise<Result> { // eslint-disable-line
        if (!fetcher) {
            throw new Error('Fetcher is not initialized')
        }

        return fetcher.fetch()
    }

    reset(): Loader<Result> {
        this._result = null
        return this
    }
}

function defaultGetKey(rec: FetcherRec<*>): string {
    const params: {[id: string]: string} = rec.params || {}
    return Object.keys(params).sort().map((key: string) => `${key}:${params[key]}`).join('.')
}

function callFetch<Result, Params: Object>(f: IFetcher<Result, Params>): Promise<Result> {
    return f.fetch()
}

/**
 * Fetch options builder
 */
export class Fetcher<Result, Params: Object> {
    _baseUrl: string
    _serializeParams: ?SerializeParams
    _url: string
    _params: ?Params
    _fetchFn: FetchFn

    /**
     * Request options.
     */
    options: RequestOptions

    /**
     * Generated full url from baseUrl, url and params.
     */
    fullUrl: string

    postProcess: (req: Promise<Response>) => Promise<Result>

    preProcess: ?Preprocess<Result, Params>;

    constructor(rec?: FetcherRec<Params> = {}) {
        this._baseUrl = rec.baseUrl || '/'
        this._serializeParams = rec.serializeParams
        this._params = rec.params || null
        this._url = rec.url || ''
        this.postProcess = rec.postProcess || pass
        this._fetchFn = rec.fetchFn || (typeof fetch === 'undefined' ? pass : fetch)
        this.preProcess = rec.preProcess || null
        let headers: ?HeadersInit = rec.headers || {}

        let isPlainObject: boolean = false
        const body = rec.body
        if (body && typeof body === 'object') {
            const isUrlSearchParams: boolean = isURLSearchParams(body)
            isPlainObject =
                !isFormData(body)
                && !isBlob(body)
                && !isUrlSearchParams
            if (isUrlSearchParams || isPlainObject) {
                if (!headers) {
                    headers = {}
                }
                const ctxType: string = isUrlSearchParams
                    ? 'application/x-www-form-urlencoded;charset=utf-8'
                    : 'application/json'

                if (typeof Headers !== 'undefined' && headers instanceof Headers) {
                    if (!headers.has('Content-Type')) {
                        headers.set('Content-Type', ctxType)
                    }
                } else if (!(headers: Object)['Content-Type']) {
                    (headers: Object)['Content-Type'] = ctxType
                }
            }
        }

        const options: RequestOptions = {
            body: isPlainObject && body ? JSON.stringify(body) : body,
            headers
        }
        if (rec.cache) options.cache = rec.cache
        if (rec.credentials) options.credentials = rec.credentials
        if (rec.integrity) options.integrity = rec.integrity
        if (rec.method) options.method = rec.method
        if (rec.mode) options.mode = rec.mode
        if (rec.redirect) options.redirect = rec.redirect
        if (rec.referrer) options.referrer = rec.referrer
        if (rec.referrerPolicy) options.referrerPolicy = rec.referrerPolicy

        this.options = options

        if (this._params) {
            if (!this._serializeParams) {
                throw new TypeError('params exists, but no serializeParams method provided')
            }
            this.fullUrl = this._baseUrl + this._serializeParams(this._url, this._params)
        } else {
            this.fullUrl = this._baseUrl + this._url
        }
    }

    /**
     * Create new copy of Fetcher with some options redefined.
     *
     * Headers will be merged with existing headers.
     * postProcessors will be composed with existing postProcessors.
     */
    copy<R, P: Object>(rec: FetcherRec<any>): IFetcher<R, P> {
        const headers: ?HeadersInit = this.options.headers
        return (new this.constructor({
            baseUrl: rec.baseUrl || this._baseUrl,
            serializeParams: this._serializeParams,
            url: this._url,
            ...this.options,
            ...rec,
            postProcess: rec.postProcess
                ? compose(this.postProcess, rec.postProcess)
                : this.postProcess,
            params: this._params
                ? {...this._params, ...rec.params || {}}
                : rec.params,
            headers: rec.headers
                ? mergeHeaders(headers, rec.headers)
                : headers
        }): any)
    }

    _fetch(f: IFetcher<Result, Params>): Promise<Result> {
        const result = this._fetchFn.call(null, f.fullUrl, f.options)
        return f.postProcess(result)
    }

    fetch(rec?: FetcherRec<*>): Promise<Result> {
        const opts: IFetcher<Result, Params> = rec
            ? this.copy(rec)
            : this

        return this.preProcess
            ? this.preProcess(opts).then(callFetch)
            : this._fetch(opts)
    }
}

if (0) ((new Fetcher(...(0: any))): IFetcher<*, *>) // eslint-disable-line

export class Repository<Result> {
    _loaders: Map<string, Loader<Result>>;
    _getKey: (params: FetcherRec<*>) => string;

    constructor(
        getKey?: ?(params: FetcherRec<*>) => string
    ) {
        this._loaders = new Map()
        this._getKey = getKey || defaultGetKey
    }

    fetch(params: FetcherRec<*>): Promise<Result> {
        const key: string = this._getKey(params)
        let loader: ?Loader<Result> = this._loaders.get(key)
        if (!loader) {
            loader = new Loader(new Fetcher(params))
            this._loaders.set(key, loader)
        }

        return loader.fetch()
    }

    _clearAll: (loader: Loader<Result>) => void = (loader: Loader<Result>) => {
        loader.reset()
    };

    reset(params?: FetcherRec<*>): Repository<Result> {
        if (!params) {
            this._loaders.forEach(this._clearAll)
            return this
        }

        const loader: ?Loader<Result> = this._loaders.get(this._getKey(params))
        if (loader) {
            loader.reset()
        }

        return this
    }
}
<|endoftext|>


<commit_before>const Electron = require('electron')
const AutoLaunch = require('auto-launch')
const path = require('path')

const app = Electron.app
const BrowserWindow = Electron.BrowserWindow
const Menu = Electron.Menu
const Tray = Electron.Tray

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

// My own global variables

// To know when to close it or just move to the tray bar
let realClose = false

const InboxDesktopAutoLauncher = new AutoLaunch({
  name: 'Desktop Inbox',
  isHidden: true
})

const createWindow = () => {
    // Create the browser window.
  mainWindow = new BrowserWindow({
    width: 1280,
    height: 720,
    title: 'Desktop Inbox',
    icon: path.join(__dirname, '/resources/images/icon.png')
  })

  //  Activate in production
   mainWindow.setMenuBarVisibility(false)

  mainWindow.loadURL(`file://${__dirname}/index.html`)

  const tray = new Tray(path.join(__dirname, '/resources/images/icon.png'))
  tray.on('click', () => {
    mainWindow.isVisible() ? mainWindow.hide() : mainWindow.show()
  })

  const contextMenu = Menu.buildFromTemplate([{
    label: 'Abrir Inbox',
    click: function () {
      mainWindow.show()
    }
  },
  {
    visible: (InboxDesktopAutoLauncher.isEnabled() !== true),
    label: 'Ejecutar al arrancar',
    click: function () {
      InboxDesktopAutoLauncher.enable()
      contextMenu.items[1].visible = false
      contextMenu.items[2].visible = true
      tray.setContextMenu(contextMenu)
    }
  },
  {
    visible: (InboxDesktopAutoLauncher.isEnabled() === true),
    label: 'Ejecutar al arrancar ✓',
    click: function () {
      InboxDesktopAutoLauncher.disable()
      contextMenu.items[1].visible = true
      contextMenu.items[2].visible = false
      tray.setContextMenu(contextMenu)
    }
  },
  {
    label: 'Cerrar inbox',
    click: function () {
      realClose = true
      mainWindow.close()
    }
  }
  ])

  tray.setToolTip('Inbox by Google')
  tray.setContextMenu(contextMenu)

  mainWindow.on('minimize', function (event) {})

  mainWindow.on('close', (event) => {
    if (!realClose) {
      event.preventDefault()
      mainWindow.hide()
    }
  })

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

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)

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

app.on('activate', () => {
    // On OS X it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
  if (mainWindow === null) {
    createWindow()
  }
})
<commit_msg>Fix code style guides<commit_after>const Electron = require('electron')
const AutoLaunch = require('auto-launch')
const path = require('path')

const app = Electron.app
const BrowserWindow = Electron.BrowserWindow
const Menu = Electron.Menu
const Tray = Electron.Tray

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

// My own global variables

// To know when to close it or just move to the tray bar
let realClose = false

const InboxDesktopAutoLauncher = new AutoLaunch({
  name: 'Desktop Inbox',
  isHidden: true
})

const createWindow = () => {
    // Create the browser window.
  mainWindow = new BrowserWindow({
    width: 1280,
    height: 720,
    title: 'Desktop Inbox',
    icon: path.join(__dirname, '/resources/images/icon.png')
  })

  //  Activate in production
  mainWindow.setMenuBarVisibility(false)

  mainWindow.loadURL(`file://${__dirname}/index.html`)

  const tray = new Tray(path.join(__dirname, '/resources/images/icon.png'))
  tray.on('click', () => {
    mainWindow.isVisible() ? mainWindow.hide() : mainWindow.show()
  })

  const contextMenu = Menu.buildFromTemplate([{
    label: 'Abrir Inbox',
    click: function () {
      mainWindow.show()
    }
  },
  {
    visible: (InboxDesktopAutoLauncher.isEnabled() !== true),
    label: 'Ejecutar al arrancar',
    click: function () {
      InboxDesktopAutoLauncher.enable()
      contextMenu.items[1].visible = false
      contextMenu.items[2].visible = true
      tray.setContextMenu(contextMenu)
    }
  },
  {
    visible: (InboxDesktopAutoLauncher.isEnabled() === true),
    label: 'Ejecutar al arrancar ✓',
    click: function () {
      InboxDesktopAutoLauncher.disable()
      contextMenu.items[1].visible = true
      contextMenu.items[2].visible = false
      tray.setContextMenu(contextMenu)
    }
  },
  {
    label: 'Cerrar inbox',
    click: function () {
      realClose = true
      mainWindow.close()
    }
  }
  ])

  tray.setToolTip('Inbox by Google')
  tray.setContextMenu(contextMenu)

  mainWindow.on('minimize', function (event) {})

  mainWindow.on('close', (event) => {
    if (!realClose) {
      event.preventDefault()
      mainWindow.hide()
    }
  })

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

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)

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

app.on('activate', () => {
    // On OS X it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
  if (mainWindow === null) {
    createWindow()
  }
})
<|endoftext|>


<commit_before>'use strict';

//  ▄▄ •             ▄▄▄ . ▄· ▄▌
// ▐█ ▀ ▪▪     ▪     ▀▄.▀·▐█▪██▌
// ▄█ ▀█▄ ▄█▀▄  ▄█▀▄ ▐▀▀▪▄▐█▌▐█▪
// ▐█▄▪▐█▐█▌.▐▌▐█▌.▐▌▐█▄▄▌ ▐█▀·.
// ·▀▀▀▀  ▀█▄▀▪ ▀█▄▀▪ ▀▀▀   ▀ •

import jsonPath from 'jsonpath'
import _ from 'lodash'

var _services = {}

// default configuration
const _config = {
  strict: true,
  data: {
    lazy: false,

    matching: {
      queries : true
    },

    publish: {
      ignore: {
        falsy: true
      }
    }
  }
}

// supported types of tree traversals
const traversals = ['depth', 'breadth']

// a canonical, heiarchical source of data that can delegate updates bi-directionally
// TODO - make `data` a Proxy object and integrate event callbacks with publish.
//      - if the data is a proxy, concurrent Service subscription handlers will be able to more easily synchronize data mutation events
//        this is easier on the developer because we can trigger native events when data is mutated, and therefore we can trigger
//        a recursive publication of that data change. can provide a "weight" heuristic to automatically resolve any remaining conflicts
export class Service {

  constructor(name: String, model?: Function, parent?: Service, children?: Array = [], config?: Object = _config) {
    if (_.isUndefined(name) || Service.isRegistered(name)) {
      throw `Services must have unique names: ${name}`
    }

    this.name          = name
    this.model         = model
    this.parent        = parent ? parent.relateTo(this) : null
    this.children      = this.relateToAll(children)
    this.data          = {}
    this.subscriptions = []
    this.config        = config

    _services[name] = this

    if (this.model) {
      this.model(this.data)
    }
  }

  publish(data, success: Function = _.noop, error: Function = _.noop, traversal: String = 'breadth'): Promise {
    if (!traversals.find(t => t === traversal)) {
      throw `Failed to publish, invalid traversal: ${traversal}`
    }

    // subscribers who match the current publish
    const matches = this.subscriptions.filter(scrip => !!this.matches(data, scrip).size)

    // current service node. proxy data if this service's data update matches any subscriptions
    const result = matches.length ? matches.map(scrip => scrip.on(data)) : data

    // direction: down
    if (this.children.length) {
      if (traversal === 'breadth') {
        return Promise
          .all(this.children.map(child =>
            child.publish(result, success, error, traversal)
          ))
      }

      if (traversal === 'depth') {
        return this.children.map(child =>
          child.publish(result, success, error, traversal)
        )
      }
    }

    // leaf node (TODO - reject)
    return new Promise((resolve, reject) => {
      resolve(success(result))
    })
  }

  // creates and registers a publish subscription against a jsonpath pattern
  subscribe(path: String = '$', on: Function): Subscription {
    const scrip = new Subscription(this.name, path, on)

    this.subscriptions.push(scrip)

    return scrip
  }

  // ends a publish subscription
  unsubscribe(scrip: Subscription) {
    this.subscriptions.splice(this.subscriptions.indexOf(scrip), 1)
  }

  // updates the Service's canonical data source with new data and publishs the change
  update(data, success?: Function, error?: Function): Promise {
    this.data = data

    return this.publish(data, success, error)
  }

  // merges and updates the Service's canonical date source with a new data object and publishs the change
  merge(data: Object, success?: Function, error?: Function): Promise {
    if (_.isObject(data)) {
      _.merge(this.data, data)
    }

    return this.update(this.data, success, error)
  }

  // alias for subscribe
  on(path: String, on: Function): Subscription {
    return this.subscribe(path, on)
  }

  // alias for update
  use(data, success?: Function, error?: Function): Promise {
    return this.update(data, success, error)
  }

  // alias for upsert
  up(data: Object, success?: Function, error?: Function): Promise {
    return upsert(data, success, error)
  }

  // determines if a subscription path matches data
  matches(data, scrip: Subscription): Set {
    const matchSet = new Set()

    if (this.config.data.matching.queries) {
      const jpMatches = jsonPath.query(data, scrip.path)

      if (jpMatches && !!jpMatches.length) {
        matchSet.add(...jpMatches)
      }
    }

    if (data === scrip.pattern && !matchSet.contains(data)) {
      matchSet.add(data)
    }

    return matchSet
  }

  //  TODO - validate for cyclic dependencies
  // establishes service as a parent to the provided child service
  relateTo(child: Service): Service {
    this.children.push(child)

    return this
  }

  // establishes service as a parent to each provided child service
  relateToAll(children: Array): Array {
    return children.map(c => {
      c.parent = this

      return c
    })
  }

  // determines a service's depth in the service tree
  depth(node = this): Int {
    let nodeDepth = 0

    while (node.parent) {
      node = node.parent
      nodeDepth += 1
    }

    return nodeDepth
  }

  // determines if the service is a root node in the service tree
  isRoot(): Boolean {
    return !this.parent
  }

  // determines if the service is a leaf node in the service tree
  isLeaf(): Boolean {
    return !this.children
  }

  // determines all root node Services in the tree
  static findRoots(services: Array = _services): Array {
    return _(services).values().filter(svc =>
      (svc instanceof Service) ? svc.isRoot() : false
    ).value()
  }

  // determines all leaf node Services in the tree
  static findLeafs(services: Array = _services): Array {
    return _(services).values().filter(svc =>
      (svc instanceof Service) ? svc.isLeaf() : false
    ).value()
  }

  // determines if a service name is already registered in the tree
  static isRegistered(name: String): Boolean {
    return _.contains(Array.from(_services).map(s => s.name), name)
  }

  // determines if a cyclic relationship exists anywhere in the service tree
  static cycleExists(services = _services): Boolean {
    const roots = Service.findRoots(services)
    const found = !_.isEmpty(roots) ? roots.map(r => r.name) : []

    let curNode = null
    let cyclic  = false

    _.forEach(roots, (root) => {
      curNode = root

      while (!cyclic && !_.isEmpty(curNode.children)) {
        curNode.children.forEach(child => {
          if (!_.contains(found, child.name)) {
            found.push(child.name)

            curNode = child
          } else {
            cyclic = true
          }
        })
      }
    })

    return cyclic
  }
}

export var service  = ({name, model, parent, children, config}) => new Service(name, model, parent, children, config)
export var services = _services
export var clear    = () => { _services = new Set() }

export class Subscription {

  constructor(service: Service, path: String, on: Function) {
    this.service = service
    this.path    = path
    this.on      = on
  }

  end() {
    service.unsubscribe(this)

    Object.freeze(this)
  }

}

// subscribe to all services and react to any data changes within them matching the provided hash
// export function subscribe(hash: String, callback: Function)

// publish a data change across all services (searches for roots, leafs, and orphans)
// export function publish(path: String, data)
<commit_msg>:scroll: just comments<commit_after>'use strict';

//  ▄▄ •             ▄▄▄ . ▄· ▄▌
// ▐█ ▀ ▪▪     ▪     ▀▄.▀·▐█▪██▌
// ▄█ ▀█▄ ▄█▀▄  ▄█▀▄ ▐▀▀▪▄▐█▌▐█▪
// ▐█▄▪▐█▐█▌.▐▌▐█▌.▐▌▐█▄▄▌ ▐█▀·.
// ·▀▀▀▀  ▀█▄▀▪ ▀█▄▀▪ ▀▀▀   ▀ •

import jsonPath from 'jsonpath'
import _ from 'lodash'

// flat object of all registered services by name
var _services = {}

// default service configuration
const _config = {
  strict: true,
  data: {
    lazy: false,

    matching: {
      queries : true
    },

    publish: {
      ignore: {
        falsy: true
      }
    }
  }
}

// supported flavors of service tree traversals
const traversals = ['depth', 'breadth']

// a canonical, heiarchical source of data that can delegate updates bi-directionally
export class Service {

  constructor(name: String, model?: Function, parent?: Service, children?: Array = [], config?: Object = _config) {
    if (_.isUndefined(name) || Service.isRegistered(name)) {
      throw `Services must have unique names: ${name}`
    }

    this.name          = name
    this.model         = model
    this.parent        = parent ? parent.relateTo(this) : null
    this.children      = this.relateToAll(children)
    this.data          = {}
    this.subscriptions = []
    this.config        = config

    _services[name] = this

    if (this.model) {
      this.model(this.data)
    }
  }

  publish(data, success: Function = _.noop, error: Function = _.noop, traversal: String = 'breadth'): Promise {
    if (!traversals.find(t => t === traversal)) {
      throw `Failed to publish, invalid traversal: ${traversal}`
    }

    // subscribers who match the current publish
    const matches = this.subscriptions.filter(scrip => !!this.matches(data, scrip).size)

    // current service node. proxy data if this service's data update matches any subscriptions
    const result = matches.length ? matches.map(scrip => scrip.on(data)) : data

    // direction: down
    if (this.children.length) {
      if (traversal === 'breadth') {
        return Promise
          .all(this.children.map(child =>
            child.publish(result, success, error, traversal)
          ))
      }

      if (traversal === 'depth') {
        return this.children.map(child =>
          child.publish(result, success, error, traversal)
        )
      }
    }

    // leaf node (TODO - reject)
    return new Promise((resolve, reject) => {
      resolve(success(result))
    })
  }

  // creates and registers a publish subscription against a jsonpath pattern
  subscribe(path: String = '$', on: Function): Subscription {
    const scrip = new Subscription(this.name, path, on)

    this.subscriptions.push(scrip)

    return scrip
  }

  // ends a publish subscription
  unsubscribe(scrip: Subscription) {
    this.subscriptions.splice(this.subscriptions.indexOf(scrip), 1)
  }

  // updates the Service's canonical data source with new data and publishs the change
  update(data, success?: Function, error?: Function): Promise {
    this.data = data

    return this.publish(data, success, error)
  }

  // merges and updates the Service's canonical date source with a new data object and publishs the change
  merge(data: Object, success?: Function, error?: Function): Promise {
    if (_.isObject(data)) {
      _.merge(this.data, data)
    }

    return this.update(this.data, success, error)
  }

  // alias for subscribe
  on(path: String, on: Function): Subscription {
    return this.subscribe(path, on)
  }

  // alias for update
  use(data, success?: Function, error?: Function): Promise {
    return this.update(data, success, error)
  }

  // alias for upsert
  up(data: Object, success?: Function, error?: Function): Promise {
    return upsert(data, success, error)
  }

  // determines if a subscription path matches data
  matches(data, scrip: Subscription): Set {
    const matchSet = new Set()

    if (this.config.data.matching.queries) {
      const jpMatches = jsonPath.query(data, scrip.path)

      if (jpMatches && !!jpMatches.length) {
        matchSet.add(...jpMatches)
      }
    }

    if (data === scrip.pattern && !matchSet.contains(data)) {
      matchSet.add(data)
    }

    return matchSet
  }

  //  TODO - validate for cyclic dependencies
  // establishes service as a parent to the provided child service
  relateTo(child: Service): Service {
    this.children.push(child)

    return this
  }

  // establishes service as a parent to each provided child service
  relateToAll(children: Array): Array {
    return children.map(c => {
      c.parent = this

      return c
    })
  }

  // determines a service's depth in the service tree
  depth(node = this): Int {
    let nodeDepth = 0

    while (node.parent) {
      node = node.parent
      nodeDepth += 1
    }

    return nodeDepth
  }

  // determines if the service is a root node in the service tree
  isRoot(): Boolean {
    return !this.parent
  }

  // determines if the service is a leaf node in the service tree
  isLeaf(): Boolean {
    return !this.children
  }

  // determines all root node Services in the tree
  static findRoots(services: Array = _services): Array {
    return _(services).values().filter(svc =>
      (svc instanceof Service) ? svc.isRoot() : false
    ).value()
  }

  // determines all leaf node Services in the tree
  static findLeafs(services: Array = _services): Array {
    return _(services).values().filter(svc =>
      (svc instanceof Service) ? svc.isLeaf() : false
    ).value()
  }

  // determines if a service name is already registered in the tree
  static isRegistered(name: String): Boolean {
    return _.contains(Array.from(_services).map(s => s.name), name)
  }

  // determines if a cyclic relationship exists anywhere in the service tree
  static cycleExists(services = _services): Boolean {
    const roots = Service.findRoots(services)
    const found = !_.isEmpty(roots) ? roots.map(r => r.name) : []

    let curNode = null
    let cyclic  = false

    _.forEach(roots, (root) => {
      curNode = root

      while (!cyclic && !_.isEmpty(curNode.children)) {
        curNode.children.forEach(child => {
          if (!_.contains(found, child.name)) {
            found.push(child.name)

            curNode = child
          } else {
            cyclic = true
          }
        })
      }
    })

    return cyclic
  }
}

export var service  = ({name, model, parent, children, config}) => new Service(name, model, parent, children, config)
export var services = _services
export var clear    = () => { _services = new Set() }

export class Subscription {

  constructor(service: Service, path: String, on: Function) {
    this.service = service
    this.path    = path
    this.on      = on
  }

  end() {
    service.unsubscribe(this)

    Object.freeze(this)
  }

}

// subscribe to all services and react to any data changes within them matching the provided hash
// export function subscribe(hash: String, callback: Function)

// publish a data change across all services (searches for roots, leafs, and orphans)
// export function publish(path: String, data)
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import { Dimensions, Modal } from 'react-native';
import { View } from 'react-native-animatable';

import styles from './index.style.js';

export class AnimatedModal extends Component {
  static propTypes = {
    animationIn: PropTypes.string,
    animationInTiming: PropTypes.number,
    animationOut: PropTypes.string,
    animationOutTiming: PropTypes.number,
    backdropColor: PropTypes.string,
    backdropOpacity: PropTypes.number,
    backdropTransitionInTiming: PropTypes.number,
    backdropTransitionOutTiming: PropTypes.number,
    children: PropTypes.node.isRequired,
    isVisible: PropTypes.bool.isRequired,
    onModalShow: PropTypes.func,
    onModalHide: PropTypes.func,
    hideOnBack: PropTypes.bool,
    style: PropTypes.any,
  };

  static defaultProps = {
    animationIn: 'slideInUp',
    animationInTiming: 300,
    animationOut: 'slideOutDown',
    animationOutTiming: 300,
    backdropColor: 'black',
    backdropOpacity: 0.70,
    backdropTransitionInTiming: 300,
    backdropTransitionOutTiming: 300,
    onModalShow: () => null,
    onModalHide: () => null,
    isVisible: false,
    hideOnBack: true,
  };

  state = {
    isVisible: false,
    deviceWidth: Dimensions.get('window').width,
    deviceHeight: Dimensions.get('window').height,
  };

  componentWillReceiveProps(nextProps) {
    if (!this.state.isVisible && nextProps.isVisible) {
      this.setState({ isVisible: true });
    }
  }

  componentDidUpdate(prevProps, prevState) {
    // On modal open request slide the view up and fade in the backdrop
    if (this.state.isVisible && !prevState.isVisible) {
      this._open();
      // On modal close request slide the view down and fade out the backdrop
    } else if (!this.props.isVisible && prevProps.isVisible) {
      this._close();
    }
  }

  _open = () => {
    this.backdropRef.transitionTo(
      { opacity: this.props.backdropOpacity },
      this.props.backdropTransitionInTiming,
    );
    this.contentRef[this.props.animationIn](this.props.animationInTiming).then(() => {
      this.props.onModalShow();
    });
  };

  _close = async () => {
    this.backdropRef.transitionTo({ opacity: 0 }, this.props.backdropTransitionOutTiming);
    this.contentRef[this.props.animationOut](this.props.animationOutTiming).then(() => {
      this.setState({ isVisible: false });
      this.props.onModalHide();
    });
  };

  _closeOnBack = () => {
    if (this.props.hideOnBack) {
      this._close();
    }
  };

  _handleLayout = event => {
    const deviceWidth = Dimensions.get('window').width;
    const deviceHeight = Dimensions.get('window').height;
    if (deviceWidth !== this.state.deviceWidth || deviceHeight !== this.state.deviceHeight) {
      this.setState({ deviceWidth, deviceHeight });
    }
  };

  render() {
    const {
      animationIn,
      animationInTiming,
      animationOut,
      animationOutTiming,
      backdropColor,
      backdropOpacity,
      backdropTransitionInTiming,
      backdropTransitionOutTiming,
      children,
      isVisible,
      onModalShow,
      onModalHide,
      style,
      ...otherProps
    } = this.props;
    const { deviceWidth, deviceHeight } = this.state;
    return (
      <Modal
        transparent={true}
        animationType={'none'}
        visible={this.state.isVisible}
        onRequestClose={this._closeOnBack}
        {...otherProps}
      >
        <View
          onLayout={this._handleLayout}
          ref={ref => this.backdropRef = ref}
          style={[
            styles.backdrop,
            { backgroundColor: backdropColor, width: deviceWidth, height: deviceHeight },
          ]}
        />
        <View
          ref={ref => this.contentRef = ref}
          style={[{ margin: deviceWidth * 0.05 }, styles.content, style]}
          {...otherProps}
        >
          {children}
        </View>
      </Modal>
    );
  }
}

export default AnimatedModal;
<commit_msg>Show the modal on componentWillMount if isVisible is true<commit_after>import React, { Component, PropTypes } from 'react';
import { Dimensions, Modal } from 'react-native';
import { View } from 'react-native-animatable';

import styles from './index.style.js';

export class AnimatedModal extends Component {
  static propTypes = {
    animationIn: PropTypes.string,
    animationInTiming: PropTypes.number,
    animationOut: PropTypes.string,
    animationOutTiming: PropTypes.number,
    backdropColor: PropTypes.string,
    backdropOpacity: PropTypes.number,
    backdropTransitionInTiming: PropTypes.number,
    backdropTransitionOutTiming: PropTypes.number,
    children: PropTypes.node.isRequired,
    isVisible: PropTypes.bool.isRequired,
    onModalShow: PropTypes.func,
    onModalHide: PropTypes.func,
    hideOnBack: PropTypes.bool,
    style: PropTypes.any,
  };

  static defaultProps = {
    animationIn: 'slideInUp',
    animationInTiming: 300,
    animationOut: 'slideOutDown',
    animationOutTiming: 300,
    backdropColor: 'black',
    backdropOpacity: 0.70,
    backdropTransitionInTiming: 300,
    backdropTransitionOutTiming: 300,
    onModalShow: () => null,
    onModalHide: () => null,
    isVisible: false,
    hideOnBack: true,
  };

  state = {
    isVisible: false,
    deviceWidth: Dimensions.get('window').width,
    deviceHeight: Dimensions.get('window').height,
  };

  componentWillReceiveProps(nextProps) {
    if (!this.state.isVisible && nextProps.isVisible) {
      this.setState({ isVisible: true });
    }
  }

  componentWillMount() {
    if (this.props.isVisible) {
      this.setState({ isVisible: true });
      this._open();
    }
  }

  componentDidUpdate(prevProps, prevState) {
    // On modal open request slide the view up and fade in the backdrop
    if (this.state.isVisible && !prevState.isVisible) {
      this._open();
      // On modal close request slide the view down and fade out the backdrop
    } else if (!this.props.isVisible && prevProps.isVisible) {
      this._close();
    }
  }

  _open = () => {
    this.backdropRef.transitionTo(
      { opacity: this.props.backdropOpacity },
      this.props.backdropTransitionInTiming,
    );
    this.contentRef[this.props.animationIn](this.props.animationInTiming).then(() => {
      this.props.onModalShow();
    });
  };

  _close = async () => {
    this.backdropRef.transitionTo({ opacity: 0 }, this.props.backdropTransitionOutTiming);
    this.contentRef[this.props.animationOut](this.props.animationOutTiming).then(() => {
      this.setState({ isVisible: false });
      this.props.onModalHide();
    });
  };

  _closeOnBack = () => {
    if (this.props.hideOnBack) {
      this._close();
    }
  };

  _handleLayout = event => {
    const deviceWidth = Dimensions.get('window').width;
    const deviceHeight = Dimensions.get('window').height;
    if (deviceWidth !== this.state.deviceWidth || deviceHeight !== this.state.deviceHeight) {
      this.setState({ deviceWidth, deviceHeight });
    }
  };

  render() {
    const {
      animationIn,
      animationInTiming,
      animationOut,
      animationOutTiming,
      backdropColor,
      backdropOpacity,
      backdropTransitionInTiming,
      backdropTransitionOutTiming,
      children,
      isVisible,
      onModalShow,
      onModalHide,
      style,
      ...otherProps
    } = this.props;
    const { deviceWidth, deviceHeight } = this.state;
    return (
      <Modal
        transparent={true}
        animationType={'none'}
        visible={this.state.isVisible}
        onRequestClose={this._closeOnBack}
        {...otherProps}
      >
        <View
          onLayout={this._handleLayout}
          ref={ref => this.backdropRef = ref}
          style={[
            styles.backdrop,
            { backgroundColor: backdropColor, width: deviceWidth, height: deviceHeight },
          ]}
        />
        <View
          ref={ref => this.contentRef = ref}
          style={[{ margin: deviceWidth * 0.05 }, styles.content, style]}
          {...otherProps}
        >
          {children}
        </View>
      </Modal>
    );
  }
}

export default AnimatedModal;
<|endoftext|>


<commit_before>import * as utils from './utils';
import LD from './LanguageDetector';

export var LanguageDetector = LD;

export function handle(i18next, options = {}) {
  return function(req, res, next) {
    let ignores = options.ignoreRoutes instanceof Array&&options.ignoreRoutes || [];
    for (var i=0;i< ignores.length;i++){
      if (req.path.indexOf(ignores[i]) > -1) return next();
    }

    let i18n = i18next.cloneInstance();
    i18n.on('languageChanged', (lng) => { // Keep language in sync
        req.language = req.locale = req.lng = lng;
        req.languages = i18next.services.languageUtils.toResolveHierarchy(lng);
    });

    let lng = req.lng;
    if (!req.lng && i18next.services.languageDetector) lng = i18next.services.languageDetector.detect(req, res);

    // set locale
    i18n.changeLanguage(lng || i18next.options.fallbackLng[0]);

    if(req.i18nextLookupName === 'path' && options.removeLngFromUrl) {
      req.url = utils.removeLngFromUrl(req.url, i18next.services.languageDetector.options.lookupFromPathIndex);
    }

    // assert for req
    req.i18n = i18n;
    req.t = i18n.t;

    // assert for res -> template
    if (res.locals) {
      res.locals.t = t;
      res.locals.exists = exists;
      res.locals.i18n = i18n;
      res.locals.language = lng;
      res.locals.languageDir = i18next.dir(lng);
    }

    if (i18next.services.languageDetector) i18next.services.languageDetector.cacheUserLanguage(req, res, lng);

    // load resources
    i18next.loadLanguages(req.lng, function() {
      next();
    });
  };
};

export function getResourcesHandler(i18next, options) {
  options = options || {};
  let maxAge = options.maxAge || 60 * 60 * 24 * 30;

  return function(req, res) {
    if (!i18next.services.backendConnector) return res.status(404).send('i18next-express-middleware:: no backend configured');

    let resources = {};

    res.contentType('json');
    if (options.cache !== undefined ? options.cache : process.env.NODE_ENV === 'production') {
      res.header('Cache-Control', 'public, max-age=' + maxAge);
      res.header('Expires', (new Date(new Date().getTime() + maxAge * 1000)).toUTCString());
    } else {
      res.header('Pragma', 'no-cache');
      res.header('Cache-Control', 'no-cache');
    }

    let languages = req.query[options.lngParam || 'lng'] ? req.query[options.lngParam || 'lng'].split(' ') : [];
    let namespaces = req.query[options.nsParam || 'ns'] ? req.query[options.nsParam || 'ns'].split(' ') : [];

    // extend ns
    namespaces.forEach(ns => {
      if (i18next.options.ns && i18next.options.ns.indexOf(ns) < 0) i18next.options.ns.push(ns);
    });

    i18next.services.backendConnector.load(languages, namespaces, function() {
      languages.forEach(lng => {
        namespaces.forEach(ns => {
          utils.setPath(resources, [lng, ns], i18next.getResourceBundle(lng, ns));
        });
      });

      res.send(resources);
    });
  };
};

export function missingKeyHandler(i18next, options) {
  options = options || {};

  return function(req, res) {
    let lng = req.params[options.lngParam || 'lng'];
    let ns = req.params[options.nsParam || 'ns'];

    if (!i18next.services.backendConnector) return res.status(404).send('i18next-express-middleware:: no backend configured');

    for (var m in req.body) {
      i18next.services.backendConnector.saveMissing([lng], ns, m, req.body[m]);
    }
    res.send('ok');
  };
};

export function addRoute(i18next, route, lngs, app, verb, fc) {
  if (typeof verb === 'function') {
    fc = verb;
    verb = 'get';
  }

  // Combine `fc` and possible more callbacks to one array
  var callbacks = [fc].concat(Array.prototype.slice.call(arguments, 6));

  for (var i = 0, li = lngs.length; i < li; i++) {
    var parts = String(route).split('/');
    var locRoute = [];
    for (var y = 0, ly = parts.length; y < ly; y++) {
      var part = parts[y];
      // if the route includes the parameter :lng
      // this is replaced with the value of the language
      if (part === ':lng') {
        locRoute.push(lngs[i]);
      } else if (part.indexOf(':') === 0 || part === '') {
        locRoute.push(part);
      } else {
        locRoute.push(i18next.t(part, { lng: lngs[i] }));
      }
    }

    var routes = [locRoute.join('/')];
    app[verb || 'get'].apply(app, routes.concat(callbacks));
  }
};

export default {
  handle,
  getResourcesHandler,
  missingKeyHandler,
  addRoute
}
<commit_msg>Fix t and exists<commit_after>import * as utils from './utils';
import LD from './LanguageDetector';

export var LanguageDetector = LD;

export function handle(i18next, options = {}) {
  return function(req, res, next) {
    let ignores = options.ignoreRoutes instanceof Array&&options.ignoreRoutes || [];
    for (var i=0;i< ignores.length;i++){
      if (req.path.indexOf(ignores[i]) > -1) return next();
    }

    let i18n = i18next.cloneInstance();
    i18n.on('languageChanged', (lng) => { // Keep language in sync
        req.language = req.locale = req.lng = lng;
        req.languages = i18next.services.languageUtils.toResolveHierarchy(lng);
    });

    let lng = req.lng;
    if (!req.lng && i18next.services.languageDetector) lng = i18next.services.languageDetector.detect(req, res);

    // set locale
    i18n.changeLanguage(lng || i18next.options.fallbackLng[0]);

    if(req.i18nextLookupName === 'path' && options.removeLngFromUrl) {
      req.url = utils.removeLngFromUrl(req.url, i18next.services.languageDetector.options.lookupFromPathIndex);
    }

    let t = i18n.t.bind(i18n);
    let exists = i18n.exists.bind(i18n);

    // assert for req
    req.i18n = i18n;
    req.t = t;

    // assert for res -> template
    if (res.locals) {
      res.locals.t = t;
      res.locals.exists = exists;
      res.locals.i18n = i18n;
      res.locals.language = lng;
      res.locals.languageDir = i18next.dir(lng);
    }

    if (i18next.services.languageDetector) i18next.services.languageDetector.cacheUserLanguage(req, res, lng);

    // load resources
    i18next.loadLanguages(req.lng, function() {
      next();
    });
  };
};

export function getResourcesHandler(i18next, options) {
  options = options || {};
  let maxAge = options.maxAge || 60 * 60 * 24 * 30;

  return function(req, res) {
    if (!i18next.services.backendConnector) return res.status(404).send('i18next-express-middleware:: no backend configured');

    let resources = {};

    res.contentType('json');
    if (options.cache !== undefined ? options.cache : process.env.NODE_ENV === 'production') {
      res.header('Cache-Control', 'public, max-age=' + maxAge);
      res.header('Expires', (new Date(new Date().getTime() + maxAge * 1000)).toUTCString());
    } else {
      res.header('Pragma', 'no-cache');
      res.header('Cache-Control', 'no-cache');
    }

    let languages = req.query[options.lngParam || 'lng'] ? req.query[options.lngParam || 'lng'].split(' ') : [];
    let namespaces = req.query[options.nsParam || 'ns'] ? req.query[options.nsParam || 'ns'].split(' ') : [];

    // extend ns
    namespaces.forEach(ns => {
      if (i18next.options.ns && i18next.options.ns.indexOf(ns) < 0) i18next.options.ns.push(ns);
    });

    i18next.services.backendConnector.load(languages, namespaces, function() {
      languages.forEach(lng => {
        namespaces.forEach(ns => {
          utils.setPath(resources, [lng, ns], i18next.getResourceBundle(lng, ns));
        });
      });

      res.send(resources);
    });
  };
};

export function missingKeyHandler(i18next, options) {
  options = options || {};

  return function(req, res) {
    let lng = req.params[options.lngParam || 'lng'];
    let ns = req.params[options.nsParam || 'ns'];

    if (!i18next.services.backendConnector) return res.status(404).send('i18next-express-middleware:: no backend configured');

    for (var m in req.body) {
      i18next.services.backendConnector.saveMissing([lng], ns, m, req.body[m]);
    }
    res.send('ok');
  };
};

export function addRoute(i18next, route, lngs, app, verb, fc) {
  if (typeof verb === 'function') {
    fc = verb;
    verb = 'get';
  }

  // Combine `fc` and possible more callbacks to one array
  var callbacks = [fc].concat(Array.prototype.slice.call(arguments, 6));

  for (var i = 0, li = lngs.length; i < li; i++) {
    var parts = String(route).split('/');
    var locRoute = [];
    for (var y = 0, ly = parts.length; y < ly; y++) {
      var part = parts[y];
      // if the route includes the parameter :lng
      // this is replaced with the value of the language
      if (part === ':lng') {
        locRoute.push(lngs[i]);
      } else if (part.indexOf(':') === 0 || part === '') {
        locRoute.push(part);
      } else {
        locRoute.push(i18next.t(part, { lng: lngs[i] }));
      }
    }

    var routes = [locRoute.join('/')];
    app[verb || 'get'].apply(app, routes.concat(callbacks));
  }
};

export default {
  handle,
  getResourcesHandler,
  missingKeyHandler,
  addRoute
}
<|endoftext|>


<commit_before>import {clone, pipeP, map} from 'ramda'
import {printRequest} from 'apollo-client/transport/networkInterface'
import {Socket as PhoenixSocket} from 'phoenix';

function applyWares (ctx, wares) {
  return new Promise(function (resolve, reject) {
    const queue = function (funcs, scope) {
      const next = function () {
        if (funcs.length > 0) {
          const f = funcs.shift()
          f && (f.applyMiddleware || f.applyAfterware).apply(scope, [ctx, next])
        }
        else {
          resolve(ctx)
        }
      }
      next()
    }
    queue(wares.slice())
  })
}

function executeQuery(sockets, context) {
  const {request, options} = context
  const {uri, channel} = options
  const Socket = options.Socket || PhoenixSocket

  if (! uri) throw "Missing options.uri"
  if (! channel) throw "Missing options.channel"
  if (! channel.topic) throw "Missing options.channel.topic"

  if (options.logger === true) {
    options.logger = (kind, msg, data) =>
      console.log(`Apollo websocket ${kind}: ${msg}`, data)
  }

  if(! sockets[uri]) {
    sockets[uri] = {
      conn: new Socket(uri, options),
      channels: {}
    }
  }

  const socket = sockets[uri]
  let chan = socket.channels[channel.topic]

  function joinChannel(resolve) {
    if (! socket.conn.isConnected()) {
      return socket.conn.connect()
    }
    if (! chan.conn) {
      chan.conn = socket.conn.channel(channel.topic, channel.params || {})
    }
    if (!chan.conn.isJoined() && !chan.conn.isJoining()) {
      chan.conn.join().receive("ok", _ => {
        const queue = chan.queue
        chan.queue = []
        map(performQuery, queue)
      }).receive('error', err => {
        chan.conn.leave()
        chan.conn = null
        chan.queue.pop()
        resolve(err)
      })
    }
  }

  function performQuery ({context, resolve, reject}) {
    const msg = context.options.channel.in_msg || "gql"
    const payload = printRequest(context.request)
    chan.conn.push(msg, payload)
      .receive("ok", resolve)
      .receive("error", resolve)
      .receive("timeout", reject.bind(null, 'timeout'))
  }

  return new Promise(function (resolve, reject) {
    if(! chan) {
      chan = socket.channels[channel.topic] = {
        conn: null,
        queue: []
      }
      socket.conn.onOpen(joinChannel.bind(null, resolve))
      socket.conn.onError(err => {
        if (chan.conn && (chan.conn.isJoined() || chan.conn.isJoining())) {
          chan.conn.leave()
        }
        chan.queue = []
        resolve(err)
      })
    }
    if (chan.conn && chan.conn.isJoined()) {
      performQuery({context, resolve, reject})
    } else {
      chan.queue.push({context, resolve, reject})
      joinChannel(resolve)
    }
  })
}

const responseData = ({response}) => {
  return new Promise(function (resolve, reject) {
    if (response.error){
      reject(response)
    } else if (response.data) {
      resolve(response)
    } else {
      reject('No response')
    }
  })
}

export function createNetworkInterface(ifaceOpts) {
  const sockets = {}
  const middlewares = []
  const afterwares = []

  const use = map(item => middlewares.push(item))
  const useAfter = map(item => afterwares.push(item))

  const requestMiddleware = (request) => {
    const options = clone(ifaceOpts)
    return applyWares({request, options}, middlewares)
  }

  const responseMiddleware = (response) => {
    const options = clone(ifaceOpts)
    return applyWares({response, options}, afterwares)
  }

  const query = pipeP(requestMiddleware,
                      executeQuery.bind(null, sockets),
                      responseMiddleware,
                      responseData)

  return {query, use, useAfter}
}
<commit_msg>Enable function to be passed for getting socket instance.<commit_after>import {clone, pipeP, map} from 'ramda'
import {printRequest} from 'apollo-client/transport/networkInterface'
import {Socket as PhoenixSocket} from 'phoenix';

function applyWares (ctx, wares) {
  return new Promise(function (resolve, reject) {
    const queue = function (funcs, scope) {
      const next = function () {
        if (funcs.length > 0) {
          const f = funcs.shift()
          f && (f.applyMiddleware || f.applyAfterware).apply(scope, [ctx, next])
        }
        else {
          resolve(ctx)
        }
      }
      next()
    }
    queue(wares.slice())
  })
}

function executeQuery(sockets, context) {
  const {request, options} = context
  const {uri, channel} = options
  const Socket = options.Socket || PhoenixSocket

  if (! uri) throw "Missing options.uri"
  if (! channel) throw "Missing options.channel"
  if (! channel.topic) throw "Missing options.channel.topic"

  if (options.logger === true) {
    options.logger = (kind, msg, data) =>
      console.log(`Apollo websocket ${kind}: ${msg}`, data)
  }

  if(! sockets[uri]) {
    if (typeof Socket === 'function') {
      sockets[uri] = {
        conn: Socket(),
        channels: {}
      }
    }
    else {
      sockets[uri] = {
        conn: new Socket(uri, options),
        channels: {}
      }
    }
  }

  const socket = sockets[uri]
  let chan = socket.channels[channel.topic]

  function joinChannel(resolve) {
    if (! socket.conn.isConnected()) {
      return socket.conn.connect()
    }
    if (! chan.conn) {
      chan.conn = socket.conn.channel(channel.topic, channel.params || {})
    }
    if (!chan.conn.isJoined() && !chan.conn.isJoining()) {
      chan.conn.join().receive("ok", _ => {
        const queue = chan.queue
        chan.queue = []
        map(performQuery, queue)
      }).receive('error', err => {
        chan.conn.leave()
        chan.conn = null
        chan.queue.pop()
        resolve(err)
      })
    }
  }

  function performQuery ({context, resolve, reject}) {
    const msg = context.options.channel.in_msg || "gql"
    const payload = printRequest(context.request)
    chan.conn.push(msg, payload)
      .receive("ok", resolve)
      .receive("error", resolve)
      .receive("timeout", reject.bind(null, 'timeout'))
  }

  return new Promise(function (resolve, reject) {
    if(! chan) {
      chan = socket.channels[channel.topic] = {
        conn: null,
        queue: []
      }
      socket.conn.onOpen(joinChannel.bind(null, resolve))
      socket.conn.onError(err => {
        if (chan.conn && (chan.conn.isJoined() || chan.conn.isJoining())) {
          chan.conn.leave()
        }
        chan.queue = []
        resolve(err)
      })
    }
    if (chan.conn && chan.conn.isJoined()) {
      performQuery({context, resolve, reject})
    } else {
      chan.queue.push({context, resolve, reject})
      joinChannel(resolve)
    }
  })
}

const responseData = ({response}) => {
  return new Promise(function (resolve, reject) {
    if (response.error){
      reject(response)
    } else if (response.data) {
      resolve(response)
    } else {
      reject('No response')
    }
  })
}

export function createNetworkInterface(ifaceOpts) {
  const sockets = {}
  const middlewares = []
  const afterwares = []

  const use = map(item => middlewares.push(item))
  const useAfter = map(item => afterwares.push(item))

  const requestMiddleware = (request) => {
    const options = clone(ifaceOpts)
    return applyWares({request, options}, middlewares)
  }

  const responseMiddleware = (response) => {
    const options = clone(ifaceOpts)
    return applyWares({response, options}, afterwares)
  }

  const query = pipeP(requestMiddleware,
                      executeQuery.bind(null, sockets),
                      responseMiddleware,
                      responseData)

  return {query, use, useAfter}
}
<|endoftext|>