<commit_before>import devboard from '../';
import single from 'webpack-hmr-singleton';
import React from 'react';

import { sourceLink } from './misc';

var definecard = devboard.ns('2. Datatypes');

sourceLink(definecard, __dirname, __filename);

var mdn = 'https://developer.mozilla.org/en-US/docs/Web/Javascript';

definecard(`
  The body of a card can be many different things,
  here are some examples:
`);

definecard('string',
  `Strings are rendered as CommonMark`,
  `
   * [Strings](${mdn}/Reference/Global_Objects/String)
   * [CommonMark](http://commonmark.org/)

  ***

  # ⚡ TADA ⚡ #
  `
);

definecard('number',
  `Numbers are rendered as – unsuprisingly – *numbers*`,
  42
);

definecard('function',
  `Functions get called, and their result rendered

  > TODO: functions can also have state
  `,
  function() {
    return `
~~~js
function() { return 'an example'; }
~~~
    `;
  }
);

definecard('function with state',
  `Functions can also receive a \`card\` argument, which allows
  them to hold some state like a React component.

  * \`card.state\` - the current value of the card's state
  * \`card.setState(fn)\` - update the card's state.

  The initial state can be passed using the \`state\` option.

  To pretty-print the current value, enable the \`inspect\` option.

  ~~~jsx
  function(card) {
    return (

        <button onClick={() => card.setState({ n: 0 })}>
          ZERO
        </button>
        <button onClick={() => card.setState(s => ({ n: s.n + 1 }))}>
          INC
        </button>
        <button onClick={() => card.setState(s => ({ n: s.n - 1 }))}>
          DEC
        </button>

          The current value of n is {card.state.n}

    );
  },
  {
    state: { n: 0 },
    inspect: true
  }
  ~~~

  `,
  function(card) {
    return (

          <button onClick={() => card.setState({ n: 0 })}>
            ZERO
          </button>
          <button onClick={() => card.setState(s => ({ n: s.n + 1 }))}>
            INC
          </button>
          <button onClick={() => card.setState(s => ({ n: s.n - 1 }))}>
            DEC
          </button>

          The current value of n is {card.state.n}

    );
  },
  {
    state: { n: 0 },
    inspect: true
  }
);

definecard('State and Timers',
  `When a card has state, you can also supply options for a function that will
  be called at set intervals which can modify the state.

  This is most useful for automatically showing the transition between two
  states of rendering. A button to disable the tick will get added into the
  footer of the card.

  * \`onTick(card)\` - a function to be called each tick, receives card with:
    * \`card.state\` - the current value of the card's state
    * \`card.setState(fn)\` - update the card's state.
  * \`tickInterval\` - the time between ticks in ms, defaults to 2s.
  * \`tickAutoplay\` - should the timer autoplay, defaults to true

  ~~~jsx
  function(card) {
    return (

    );
  },
  {
    state: 0,
    onTick: function(card) { card.setState(n => (n + 1) % 2); },
    tickInterval: 3000, tickAutoplay: false
  }
  ~~~

  `,
  function(card) {
    var bg = ['#333', '#ccc'][card.state];
    return (

        {bg}

    );
  },
  {
    state: 0,
    onTick: function(card) { card.setState(n => (n + 1) % 2); },
    tickInterval: 3000, tickAutoplay: false
  }
);

definecard('RegExp',
  `Regular expressions also get displayed neatly.

  If you type into the box, you can test what it matches.`,
  /^([de]{2}v)(c|bo|ard){2}s?/
);

definecard('Date',
  `Javascript date objects get converted to a readable form`,
  new Date()
);

definecard(
  `Datatype rendering is powered by [inspect-x]. This handles most built-in
  Javascript datatypes, as well as any custom types which implement their own
  \`inspect()\` method.

  [inspect-x]: https://github.com/Xotic750/inspect-x
  `
);

definecard('Array',
  `Arrays get displayed neatly with some colour`,
  [ 5, 6, 7, 8 ]
);

definecard('object',
  `Ordinary JS objects also get displayed neatly with some colour`,
  { a: 1, b: 2, c: 3 }
);

if (typeof Set === 'function') {
  definecard('ES6 Set',
    `ES6 Sets get displayed neatly with some colour`,
    new Set([5, 7, 11, 13, 17])
  );
}

if (typeof Map === 'function') {
  definecard('ES6 Map',
    `ES6 Maps get displayed neatly with some colour`,
    new Map([['a', 1], ['b', 2], [3, 'c']])
  );
}

definecard('***********');

definecard(
  `Devboard can also work with data which changes, it uses [js-atom]
  as the wrapper to co-ordinate these changes.

  To help make things easier the \`createAtom\` function is exposed
  on the \`devboard\` module:

  ~~~js
  var atom = devboard.atom({ stuff: 123 });
  ~~~

  [js-atom]: https://github.com/cjohansen/js-atom
  `
);

var atom1 = single(module, 'atom1', () => {
  var atom = devboard.atom({ tick: 0 });
  setInterval(
    () => atom.swap(a => ({ tick: a.tick + 1 })),
    1000
  );
  return atom;
});

definecard('atom',
  `[Atoms][js-atom] can be rendered directly, which will attach a
  subscription and re-render whenever the value changes.

  This is done via duck typing on the \`deref()\`, \`addWatch()\` and
  \`removeWatch()\` methods, so anything that looks like an atom
  should work.

  ~~~js
  var single = require('webpack-hmr-singleton');
  var atom1 = single(module, 'atom1', () => {
    var atom = devboard.atom({ tick: 0 });
    setInterval(
      () => atom1.swap(a => ({ tick: a.tick + 1 })),
      1000
    );
    return atom;
  });
  definecard('atom', '... description ...', atom1);
  ~~~

  [js-atom]: https://github.com/cjohansen/js-atom
  `,
  atom1
);

definecard('sharing atoms',
  `Atoms can be shared between cards. This can be very useful to
  create an ad-hoc control panel for another card.

  When the \`state\` option is detected to be an atom, the card
  will subscribe to changes. This can also be combined with \`inspect\`

  ~~~jsx
  var single = require('webpack-hmr-singleton');
  var sharedAtom = single(module, 'sharedAtom', () => devboard.atom(0));

  definecard('shared-atom', sharedAtom);
  definecard('shared-atom-render', ({ state }) => (

Value: {state.deref()}

  ), { state: sharedAtom, inspect: true });
  definecard('shared-atom-inc', ({ state }) => (
    <button onClick={() => state.swap(n => n + 1)}>INC</button>
  ), { state: sharedAtom });
  definecard('shared-atom-dec', ({ state }) => (
    <button onClick={() => state.swap(n => n - 1)}>DEC</button>
  ), { state: sharedAtom });
  ~~~
  `
);

var sharedAtom = single(module, 'sharedAtom', () => devboard.atom(0));

definecard('shared-atom', sharedAtom);
definecard('shared-atom-render', ({ state }) => (

Value: {state.deref()}

), { state: sharedAtom, inspect: true });
definecard('shared-atom-inc', ({ state }) => (
  <button onClick={() => state.swap(n => n + 1)}>INC</button>
), { state: sharedAtom });
definecard('shared-atom-dec', ({ state }) => (
  <button onClick={() => state.swap(n => n - 1)}>DEC</button>
), { state: sharedAtom });

definecard('***********');

var ListToggle = React.createClass({
  getInitialState() { return { current: 'Cuddly Toy' }; },
  render() {
    var current = this.state.current;
    var options = [
      'Food Processor', 'Fondue Set', 'Cuddly Toy', 'Dinner Service'
    ];
    return (

        {options.map(o =>
          <li
            key={o}
            className={`list-group-item ${current == o ? 'active' : ''}`}
            onClick={() => this.setState({ current: o })}
          >
            {o}

        )}

    );
  }
});

definecard('React',
  `Any ReactElement is rendered into the card`,
  <ListToggle />
);
<commit_msg>Tidy up datatype examples a touch<commit_after>import devboard from '../';
import single from 'webpack-hmr-singleton';
import React from 'react';

import { sourceLink } from './misc';

var definecard = devboard.ns('2. Datatypes');

sourceLink(definecard, __dirname, __filename);

var mdn = 'https://developer.mozilla.org/en-US/docs/Web/Javascript';

definecard(`
  The body of a card can be many different things,
  here are some examples:
`);

definecard('string',
  `Strings are rendered as CommonMark`,
  `
   * [Strings](${mdn}/Reference/Global_Objects/String)
   * [CommonMark](http://commonmark.org/)

  ***

  # ⚡ TADA ⚡ #
  `
);

definecard('number',
  `Numbers are rendered as – unsuprisingly – *numbers*`,
  42
);

definecard('function',
  `Functions get called, and their result rendered`,
  function() {
    return `
~~~js
function() { return 'an example'; }
~~~
    `;
  }
);

definecard('function with state',
  `Functions can also receive a \`card\` argument, which allows
  them to hold some state like a React component.

  * \`card.state\` - the current value of the card's state
  * \`card.setState(fn)\` - update the card's state.

  The initial state can be passed using the \`state\` option.

  To pretty-print the current value, enable the \`inspect\` option.

  ~~~jsx
  function(card) {
    return (

        <button onClick={() => card.setState({ n: 0 })}>
          ZERO
        </button>
        <button onClick={() => card.setState(s => ({ n: s.n + 1 }))}>
          INC
        </button>
        <button onClick={() => card.setState(s => ({ n: s.n - 1 }))}>
          DEC
        </button>

          The current value of n is {card.state.n}

    );
  },
  {
    state: { n: 0 },
    inspect: true
  }
  ~~~

  `,
  function(card) {
    return (

          <button onClick={() => card.setState({ n: 0 })}>
            ZERO
          </button>
          <button onClick={() => card.setState(s => ({ n: s.n + 1 }))}>
            INC
          </button>
          <button onClick={() => card.setState(s => ({ n: s.n - 1 }))}>
            DEC
          </button>

          The current value of n is {card.state.n}

    );
  },
  {
    state: { n: 0 },
    inspect: true
  }
);

definecard('State and Timers',
  `When a card has state, you can also supply options for a function that will
  be called at set intervals which can modify the state.

  This is most useful for automatically showing the transition between two
  states of rendering. A button to disable the tick will get added into the
  footer of the card.

  * \`onTick(card)\` - a function to be called each tick, receives card with:
    * \`card.state\` - the current value of the card's state
    * \`card.setState(fn)\` - update the card's state.
  * \`tickInterval\` - the time between ticks in ms, defaults to 2s.
  * \`tickAutoplay\` - should the timer autoplay, defaults to true

  ~~~jsx
  function(card) {
    var bg = ['#333', '#ccc'][card.state];
    return (

        {bg}

    );
  },
  {
    state: 0,
    onTick: function(card) { card.setState(n => (n + 1) % 2); },
    tickInterval: 3000, tickAutoplay: false
  }
  ~~~

  `,
  function(card) {
    var bg = ['#333', '#ccc'][card.state];
    return (

        {bg}

    );
  },
  {
    state: 0,
    onTick: function(card) { card.setState(n => (n + 1) % 2); },
    tickInterval: 3000, tickAutoplay: false
  }
);

definecard('RegExp',
  `Regular expressions also get displayed neatly.

  If you type into the box, you can test what it matches.`,
  /^([de]{2}v)(c|bo|ard){2}s?/
);

definecard('Date',
  `Javascript date objects get converted to a readable form`,
  new Date()
);

definecard(
  `Datatype rendering is powered by [inspect-x]. This handles most built-in
  Javascript datatypes, as well as any custom types which implement their own
  \`inspect()\` method.

  [inspect-x]: https://github.com/Xotic750/inspect-x
  `
);

definecard('Array',
  `Arrays get displayed neatly with some colour`,
  [ 5, 6, 7, 8 ]
);

definecard('object',
  `Ordinary JS objects also get displayed neatly with some colour`,
  { a: 1, b: 2, c: 3 }
);

if (typeof Set === 'function') {
  definecard('ES6 Set',
    `ES6 Sets get displayed neatly with some colour`,
    new Set([5, 7, 11, 13, 17])
  );
}

if (typeof Map === 'function') {
  definecard('ES6 Map',
    `ES6 Maps get displayed neatly with some colour`,
    new Map([['a', 1], ['b', 2], [3, 'c']])
  );
}

definecard('***********');

definecard(
  `Devboard can also work with data which changes, it uses [js-atom]
  as the wrapper to co-ordinate these changes.

  To help make things easier the \`createAtom\` function is exposed
  on the \`devboard\` module:

  ~~~js
  var atom = devboard.atom({ stuff: 123 });
  ~~~

  [js-atom]: https://github.com/cjohansen/js-atom
  `
);

var atom1 = single(module, 'atom1', () => {
  var atom = devboard.atom({ tick: 0 });
  setInterval(
    () => atom.swap(a => ({ tick: a.tick + 1 })),
    1000
  );
  return atom;
});

definecard('atom',
  `[Atoms][js-atom] can be rendered directly, which will attach a
  subscription and re-render whenever the value changes.

  This is done via duck typing on the \`deref()\`, \`addWatch()\` and
  \`removeWatch()\` methods, so anything that looks like an atom
  should work.

  ~~~js
  var single = require('webpack-hmr-singleton');
  var atom1 = single(module, 'atom1', () => {
    var atom = devboard.atom({ tick: 0 });
    setInterval(
      () => atom1.swap(a => ({ tick: a.tick + 1 })),
      1000
    );
    return atom;
  });
  definecard('atom', '... description ...', atom1);
  ~~~

  [js-atom]: https://github.com/cjohansen/js-atom
  `,
  atom1
);

definecard('sharing atoms',
  `Atoms can be shared between cards. This can be very useful to
  create an ad-hoc control panel for another card.

  When the \`state\` option is detected to be an atom, the card
  will subscribe to changes. This can also be combined with \`inspect\`

  ~~~jsx
  var single = require('webpack-hmr-singleton');
  var sharedAtom = single(module, 'sharedAtom', () => devboard.atom(0));

  definecard('shared-atom', sharedAtom);
  definecard('shared-atom-render', ({ state }) => (

Value: {state.deref()}

  ), { state: sharedAtom, inspect: true });
  definecard('shared-atom-inc', ({ state }) => (
    <button onClick={() => state.swap(n => n + 1)}>INC</button>
  ), { state: sharedAtom });
  definecard('shared-atom-dec', ({ state }) => (
    <button onClick={() => state.swap(n => n - 1)}>DEC</button>
  ), { state: sharedAtom });
  ~~~
  `
);

var sharedAtom = single(module, 'sharedAtom', () => devboard.atom(0));

definecard('shared-atom', sharedAtom);
definecard('shared-atom-render', ({ state }) => (

Value: {state.deref()}

), { state: sharedAtom, inspect: true });
definecard('shared-atom-inc', ({ state }) => (
  <button onClick={() => state.swap(n => n + 1)}>INC</button>
), { state: sharedAtom });
definecard('shared-atom-dec', ({ state }) => (
  <button onClick={() => state.swap(n => n - 1)}>DEC</button>
), { state: sharedAtom });

definecard('***********');

var ListToggle = React.createClass({
  getInitialState() { return { current: 'Cuddly Toy' }; },
  render() {
    var current = this.state.current;
    var options = [
      'Food Processor', 'Fondue Set', 'Cuddly Toy', 'Dinner Service'
    ];
    return (

        {options.map(o =>
          <li
            key={o}
            className={`list-group-item ${current == o ? 'active' : ''}`}
            onClick={() => this.setState({ current: o })}
          >
            {o}

        )}

    );
  }
});

definecard('React',
  `Any ReactElement is rendered into the card`,
  <ListToggle />
);
<|endoftext|>


<commit_before>'use strict';

const queryUtils = require('../../utils/query-utils');
const AggregationMapConfig = require('../../models/aggregation/aggregation-mapconfig');
const aggregationQuery = require('../../models/aggregation/aggregation-query');

function MapnikLayerStats () {
    this._types = {
        mapnik: true,
        cartodb: true
    };
}

MapnikLayerStats.prototype.is = function (type) {
    return this._types[type] ? this._types[type] : false;
};

function columnAggregations(field) {
    if (field.type === 'number') {
        return ['min', 'max', 'avg', 'sum'];
    }
    if (field.type === 'date') { // TODO other types too?
        return ['min', 'max'];
    }
    if (field.type === 'timeDimension') {
        return ['min', 'max'];
    }
    return [];
}

function _getSQL(ctx, query, type='pre', zoom=0) {
    let sql;
    if (type === 'pre') {
        sql = ctx.preQuery;
    }
    else {
        sql = ctx.aggrQuery;
    }
    sql = queryUtils.subsituteTokensForZoom(sql, zoom || 0);
    return query(sql);
}

function _estimatedFeatureCount(ctx) {
    return queryUtils.queryPromise(ctx.dbConnection, _getSQL(ctx, queryUtils.getQueryRowEstimation))
        .then(res => ({ estimatedFeatureCount: res.rows[0].rows }))
        .catch(() => ({ estimatedFeatureCount: -1 }));
}

function _featureCount(ctx) {
    if (ctx.metaOptions.featureCount) {
        // TODO: if ctx.metaOptions.columnStats we can combine this with column stats query
        return queryUtils.queryPromise(ctx.dbConnection, _getSQL(ctx, queryUtils.getQueryActualRowCount))
            .then(res => ({ featureCount: res.rows[0].rows }));
    }
    return Promise.resolve();
}

function _aggrFeatureCount(ctx) {
    if (ctx.metaOptions.hasOwnProperty('aggrFeatureCount')) {
        // We expect as zoom level as the value of aggrFeatureCount
        // TODO: it'd be nice to admit an array of zoom levels to
        // return metadata for multiple levels.
        return queryUtils.queryPromise(
            ctx.dbConnection,
            _getSQL(ctx, queryUtils.getQueryActualRowCount, 'post',  ctx.metaOptions.aggrFeatureCount)
        ).then(res => ({ aggrFeatureCount: res.rows[0].rows }));
    }
    return Promise.resolve();
}

function _geometryType(ctx) {
    if (ctx.metaOptions.geometryType) {
        const geometryColumn = AggregationMapConfig.getAggregationGeometryColumn();
        const sqlQuery = _getSQL(ctx, sql => queryUtils.getQueryGeometryType(sql, geometryColumn));
        return queryUtils.queryPromise(ctx.dbConnection, sqlQuery)
            .then(res => ({ geometryType: (res.rows[0] || {}).geom_type }));
    }
    return Promise.resolve();
}

function _columns(ctx) {
    if (ctx.metaOptions.columns || ctx.metaOptions.columnStats || ctx.metaOptions.dimensions) {
        // note: post-aggregation columns are in layer.options.columns when aggregation is present
        return queryUtils.queryPromise(ctx.dbConnection, _getSQL(ctx, sql => queryUtils.getQueryLimited(sql, 0)))
            .then(res => formatResultFields(ctx.dbConnection, res.fields));
    }
    return Promise.resolve();
}

// combine a list of results merging the properties of all the objects
// undefined results are admitted and ignored
function mergeResults(results) {
    if (results) {
        if (results.length === 0) {
            return {};
        }
        return results.reduce((a, b) => {
            if (a === undefined) {
                return b;
            }
            if (b === undefined) {
                return a;
            }
            return Object.assign({}, a, b);
        });
    }
}

// deeper (1 level) combination of a list of objects:
// mergeColumns([{ col1: { a: 1 }, col2: { a: 2 } }, { col1: { b: 3 } }]) => { col1: { a: 1, b: 3 }, col2: { a: 2 } }
function mergeColumns(results) {
    if (results) {
        if (results.length === 0) {
            return {};
        }
        return results.reduce((a, b) => {
            let c = Object.assign({}, b || {}, a || {});
            Object.keys(c).forEach(key => {
                if (b.hasOwnProperty(key)) {
                    c[key] = Object.assign(c[key], b[key]);
                }
            });
            return c;
        });
    }
}

const DEFAULT_SAMPLE_ROWS = 100;

function _sample(ctx) {
    if (!ctx.metaOptions.sample) {
        return Promise.resolve();
    }

    // We'll use a safety limit just in case numRows is a bad estimate
    const requestedRows = ctx.metaOptions.sample.num_rows || DEFAULT_SAMPLE_ROWS;
    const limit = Math.ceil(requestedRows * 1.5);
    const columns = ctx.metaOptions.sample.include_columns;

    const sqlMaxMin = _getSQL(ctx, sql => queryUtils.getMaxMinSpanColumnQuery(sql));
    return queryUtils.queryPromise(ctx.dbConnection, sqlMaxMin)
        .then(maxMinRes => {
            const { min_id: min, id_span: span } = maxMinRes.rows[0];
            const values = _getSampleValuesFromRange(min, span, limit);
            const sqlSample = _getSQL(ctx, sql => queryUtils.getSampleFromIdsQuery(sql, values, columns));

            return queryUtils.queryPromise(ctx.dbConnection, sqlSample);
        })
        .then(res => ({ sample: res.rows }));
}

function _getSampleValuesFromRange (min, span, limit) {
    const sample = new Set();

    limit = limit < span ? limit : span;

    while (sample.size < limit) {
        sample.add(Math.floor(min + Math.random() * span));
    }

    return Array.from(sample);
}

function _columnsMetadataRequired(options) {
    // We need determine the columns of a query
    // if either column stats or dimension stats are required,
    // since we'll ultimately use the same query to fetch both
    return options.columnStats || options.dimensions;
}

function _columnStats(ctx, columns, dimensions) {
    if (!columns) {
        return Promise.resolve();
    }
    if (_columnsMetadataRequired(ctx.metaOptions)) {
        let queries = [];
        let aggr = [];
        if (ctx.metaOptions.columnStats) {
            queries.push(new Promise(resolve => resolve({ columns }))); // add columns as first result
            Object.keys(columns).forEach(name => {
                aggr = aggr.concat(
                    columnAggregations(columns[name])
                    .map(fn => `${fn}("${name}") AS "${name}_${fn}"`)
                );
                if (columns[name].type === 'string') {
                    const topN = ctx.metaOptions.columnStats.topCategories || 1024;
                    const includeNulls = ctx.metaOptions.columnStats.hasOwnProperty('includeNulls') ?
                        ctx.metaOptions.columnStats.includeNulls :
                        true;

                    // TODO:  ctx.metaOptions.columnStats.maxCategories
                    //        => use PG stats to dismiss columns with more distinct values
                    queries.push(
                        queryUtils.queryPromise(
                            ctx.dbConnection,
                            _getSQL(ctx, sql => queryUtils.getQueryTopCategories(sql, name, topN, includeNulls))
                        ).then(res => ({ columns: { [name]: { categories: res.rows } } }))
                    );
                }
            });
        }
        const dimensionsStats = {};
        let dimensionsInfo = {};
        if (ctx.metaOptions.dimensions && dimensions) {
            dimensionsInfo = aggregationQuery.infoForOptions({ dimensions });
            Object.keys(dimensionsInfo).forEach(dimName => {
                const info = dimensionsInfo[dimName];
                if (info.type === 'timeDimension') {
                    dimensionsStats[dimName] = {
                        params: info.params
                    };
                    aggr = aggr.concat(
                        columnAggregations(info).map(fn => `${fn}(${info.sql}) AS "${dimName}_${fn}"`)
                    );
                }
            });
        }
        queries.push(
            queryUtils.queryPromise(
                ctx.dbConnection,
                _getSQL(ctx, sql => `SELECT ${aggr.join(',')} FROM (${sql}) AS __cdb_query`)
            ).then(res => {
                let stats = { columns: {}, dimensions: {} };
                Object.keys(columns).forEach(name => {
                    stats.columns[name] = {};
                    columnAggregations(columns[name]).forEach(fn => {
                        stats.columns[name][fn] = res.rows[0][`${name}_${fn}`];
                    });
                });
                Object.keys(dimensionsInfo).forEach(name => {
                    stats.dimensions[name] = stats.dimensions[name] || Object.assign({}, dimensionsStats[name]);
                    let type = null;
                    columnAggregations(dimensionsInfo[name]).forEach(fn => {
                        type = type ||
                            fieldTypeSafe(ctx.dbConnection, res.fields.find(f => f.name === `${name}_${fn}`));
                        stats.dimensions[name][fn] = res.rows[0][`${name}_${fn}`];
                    });
                    stats.dimensions[name].type = type;
                });
                return stats;
            })
        );
        return Promise.all(queries).then(results => ({
            columns: mergeColumns(results.map(r => r.columns)),
            dimensions: mergeColumns(results.map( r => r.dimensions))
        }));
    }
    return Promise.resolve({ columns });
}

// This is adapted from SQL API:
function fieldType(cname) {
    let tname;
    switch (true) {
        case /bool/.test(cname):
            tname = 'boolean';
            break;
        case /int|float|numeric/.test(cname):
            tname = 'number';
            break;
        case /text|char|unknown/.test(cname):
            tname = 'string';
            break;
        case /date|time/.test(cname):
            tname = 'date';
            break;
        default:
            tname = cname;
    }
    if ( tname && cname.match(/^_/) ) {
        tname += '[]';
    }
    return tname;
}

function fieldTypeSafe(dbConnection, field) {
    const cname = dbConnection.typeName(field.dataTypeID);
    return cname ? fieldType(cname) : `unknown(${field.dataTypeID})`;
}

// columns are returned as an object { columnName1: { type1: ...}, ..}
// for consistency with SQL API
function formatResultFields(dbConnection, fields = []) {
    let nfields = {};
    for (let field of fields) {
      nfields[field.name] = { type: fieldTypeSafe(dbConnection, field) };
    }
    return nfields;
}

MapnikLayerStats.prototype.getStats =
function (layer, dbConnection, callback) {
    let aggrQuery = layer.options.sql;
    let preQuery =  layer.options.sql_raw || aggrQuery;

    let ctx = {
        dbConnection,
        preQuery,
        aggrQuery,
        metaOptions: layer.options.metadata || {},
    };

    // TODO: could save some queries if queryUtils.getAggregationMetadata() has been used and kept somewhere
    // we would set queries.results.estimatedFeatureCount and queries.results.geometryType
    // (if metaOptions.geometryType) from it.

    // TODO: compute _sample with _featureCount when available
    // TODO: add support for sample.exclude option by, in that case, forcing the columns query and
    // passing the results to the sample query function.

    const dimensions = (layer.options.aggregation || {}).dimensions;

    Promise.all([
        _estimatedFeatureCount(ctx).then(
            ({ estimatedFeatureCount }) => _sample(ctx)
                .then(sampleResults => mergeResults([ sampleResults, { estimatedFeatureCount }] ))
        ),
        _featureCount(ctx),
        _aggrFeatureCount(ctx),
        _geometryType(ctx),
        _columns(ctx).then(columns => _columnStats(ctx, columns, dimensions))
    ]).then(results => {
        results = mergeResults(results);
        callback(null, results);
    }).catch(error => {
        callback(error);
    });
};

module.exports = MapnikLayerStats;
<commit_msg>Remove safe limit<commit_after>'use strict';

const queryUtils = require('../../utils/query-utils');
const AggregationMapConfig = require('../../models/aggregation/aggregation-mapconfig');
const aggregationQuery = require('../../models/aggregation/aggregation-query');

function MapnikLayerStats () {
    this._types = {
        mapnik: true,
        cartodb: true
    };
}

MapnikLayerStats.prototype.is = function (type) {
    return this._types[type] ? this._types[type] : false;
};

function columnAggregations(field) {
    if (field.type === 'number') {
        return ['min', 'max', 'avg', 'sum'];
    }
    if (field.type === 'date') { // TODO other types too?
        return ['min', 'max'];
    }
    if (field.type === 'timeDimension') {
        return ['min', 'max'];
    }
    return [];
}

function _getSQL(ctx, query, type='pre', zoom=0) {
    let sql;
    if (type === 'pre') {
        sql = ctx.preQuery;
    }
    else {
        sql = ctx.aggrQuery;
    }
    sql = queryUtils.subsituteTokensForZoom(sql, zoom || 0);
    return query(sql);
}

function _estimatedFeatureCount(ctx) {
    return queryUtils.queryPromise(ctx.dbConnection, _getSQL(ctx, queryUtils.getQueryRowEstimation))
        .then(res => ({ estimatedFeatureCount: res.rows[0].rows }))
        .catch(() => ({ estimatedFeatureCount: -1 }));
}

function _featureCount(ctx) {
    if (ctx.metaOptions.featureCount) {
        // TODO: if ctx.metaOptions.columnStats we can combine this with column stats query
        return queryUtils.queryPromise(ctx.dbConnection, _getSQL(ctx, queryUtils.getQueryActualRowCount))
            .then(res => ({ featureCount: res.rows[0].rows }));
    }
    return Promise.resolve();
}

function _aggrFeatureCount(ctx) {
    if (ctx.metaOptions.hasOwnProperty('aggrFeatureCount')) {
        // We expect as zoom level as the value of aggrFeatureCount
        // TODO: it'd be nice to admit an array of zoom levels to
        // return metadata for multiple levels.
        return queryUtils.queryPromise(
            ctx.dbConnection,
            _getSQL(ctx, queryUtils.getQueryActualRowCount, 'post',  ctx.metaOptions.aggrFeatureCount)
        ).then(res => ({ aggrFeatureCount: res.rows[0].rows }));
    }
    return Promise.resolve();
}

function _geometryType(ctx) {
    if (ctx.metaOptions.geometryType) {
        const geometryColumn = AggregationMapConfig.getAggregationGeometryColumn();
        const sqlQuery = _getSQL(ctx, sql => queryUtils.getQueryGeometryType(sql, geometryColumn));
        return queryUtils.queryPromise(ctx.dbConnection, sqlQuery)
            .then(res => ({ geometryType: (res.rows[0] || {}).geom_type }));
    }
    return Promise.resolve();
}

function _columns(ctx) {
    if (ctx.metaOptions.columns || ctx.metaOptions.columnStats || ctx.metaOptions.dimensions) {
        // note: post-aggregation columns are in layer.options.columns when aggregation is present
        return queryUtils.queryPromise(ctx.dbConnection, _getSQL(ctx, sql => queryUtils.getQueryLimited(sql, 0)))
            .then(res => formatResultFields(ctx.dbConnection, res.fields));
    }
    return Promise.resolve();
}

// combine a list of results merging the properties of all the objects
// undefined results are admitted and ignored
function mergeResults(results) {
    if (results) {
        if (results.length === 0) {
            return {};
        }
        return results.reduce((a, b) => {
            if (a === undefined) {
                return b;
            }
            if (b === undefined) {
                return a;
            }
            return Object.assign({}, a, b);
        });
    }
}

// deeper (1 level) combination of a list of objects:
// mergeColumns([{ col1: { a: 1 }, col2: { a: 2 } }, { col1: { b: 3 } }]) => { col1: { a: 1, b: 3 }, col2: { a: 2 } }
function mergeColumns(results) {
    if (results) {
        if (results.length === 0) {
            return {};
        }
        return results.reduce((a, b) => {
            let c = Object.assign({}, b || {}, a || {});
            Object.keys(c).forEach(key => {
                if (b.hasOwnProperty(key)) {
                    c[key] = Object.assign(c[key], b[key]);
                }
            });
            return c;
        });
    }
}

const DEFAULT_SAMPLE_ROWS = 100;

function _sample(ctx) {
    if (!ctx.metaOptions.sample) {
        return Promise.resolve();
    }

    const limit = ctx.metaOptions.sample.num_rows || DEFAULT_SAMPLE_ROWS;
    const columns = ctx.metaOptions.sample.include_columns;

    const sqlMaxMin = _getSQL(ctx, sql => queryUtils.getMaxMinSpanColumnQuery(sql));
    return queryUtils.queryPromise(ctx.dbConnection, sqlMaxMin)
        .then(maxMinRes => {
            const { min_id: min, id_span: span } = maxMinRes.rows[0];
            const values = _getSampleValuesFromRange(min, span, limit);
            const sqlSample = _getSQL(ctx, sql => queryUtils.getSampleFromIdsQuery(sql, values, columns));

            return queryUtils.queryPromise(ctx.dbConnection, sqlSample);
        })
        .then(res => ({ sample: res.rows }));
}

function _getSampleValuesFromRange (min, span, limit) {
    const sample = new Set();

    limit = limit < span ? limit : span;

    while (sample.size < limit) {
        sample.add(Math.floor(min + Math.random() * span));
    }

    return Array.from(sample);
}

function _columnsMetadataRequired(options) {
    // We need determine the columns of a query
    // if either column stats or dimension stats are required,
    // since we'll ultimately use the same query to fetch both
    return options.columnStats || options.dimensions;
}

function _columnStats(ctx, columns, dimensions) {
    if (!columns) {
        return Promise.resolve();
    }
    if (_columnsMetadataRequired(ctx.metaOptions)) {
        let queries = [];
        let aggr = [];
        if (ctx.metaOptions.columnStats) {
            queries.push(new Promise(resolve => resolve({ columns }))); // add columns as first result
            Object.keys(columns).forEach(name => {
                aggr = aggr.concat(
                    columnAggregations(columns[name])
                    .map(fn => `${fn}("${name}") AS "${name}_${fn}"`)
                );
                if (columns[name].type === 'string') {
                    const topN = ctx.metaOptions.columnStats.topCategories || 1024;
                    const includeNulls = ctx.metaOptions.columnStats.hasOwnProperty('includeNulls') ?
                        ctx.metaOptions.columnStats.includeNulls :
                        true;

                    // TODO:  ctx.metaOptions.columnStats.maxCategories
                    //        => use PG stats to dismiss columns with more distinct values
                    queries.push(
                        queryUtils.queryPromise(
                            ctx.dbConnection,
                            _getSQL(ctx, sql => queryUtils.getQueryTopCategories(sql, name, topN, includeNulls))
                        ).then(res => ({ columns: { [name]: { categories: res.rows } } }))
                    );
                }
            });
        }
        const dimensionsStats = {};
        let dimensionsInfo = {};
        if (ctx.metaOptions.dimensions && dimensions) {
            dimensionsInfo = aggregationQuery.infoForOptions({ dimensions });
            Object.keys(dimensionsInfo).forEach(dimName => {
                const info = dimensionsInfo[dimName];
                if (info.type === 'timeDimension') {
                    dimensionsStats[dimName] = {
                        params: info.params
                    };
                    aggr = aggr.concat(
                        columnAggregations(info).map(fn => `${fn}(${info.sql}) AS "${dimName}_${fn}"`)
                    );
                }
            });
        }
        queries.push(
            queryUtils.queryPromise(
                ctx.dbConnection,
                _getSQL(ctx, sql => `SELECT ${aggr.join(',')} FROM (${sql}) AS __cdb_query`)
            ).then(res => {
                let stats = { columns: {}, dimensions: {} };
                Object.keys(columns).forEach(name => {
                    stats.columns[name] = {};
                    columnAggregations(columns[name]).forEach(fn => {
                        stats.columns[name][fn] = res.rows[0][`${name}_${fn}`];
                    });
                });
                Object.keys(dimensionsInfo).forEach(name => {
                    stats.dimensions[name] = stats.dimensions[name] || Object.assign({}, dimensionsStats[name]);
                    let type = null;
                    columnAggregations(dimensionsInfo[name]).forEach(fn => {
                        type = type ||
                            fieldTypeSafe(ctx.dbConnection, res.fields.find(f => f.name === `${name}_${fn}`));
                        stats.dimensions[name][fn] = res.rows[0][`${name}_${fn}`];
                    });
                    stats.dimensions[name].type = type;
                });
                return stats;
            })
        );
        return Promise.all(queries).then(results => ({
            columns: mergeColumns(results.map(r => r.columns)),
            dimensions: mergeColumns(results.map( r => r.dimensions))
        }));
    }
    return Promise.resolve({ columns });
}

// This is adapted from SQL API:
function fieldType(cname) {
    let tname;
    switch (true) {
        case /bool/.test(cname):
            tname = 'boolean';
            break;
        case /int|float|numeric/.test(cname):
            tname = 'number';
            break;
        case /text|char|unknown/.test(cname):
            tname = 'string';
            break;
        case /date|time/.test(cname):
            tname = 'date';
            break;
        default:
            tname = cname;
    }
    if ( tname && cname.match(/^_/) ) {
        tname += '[]';
    }
    return tname;
}

function fieldTypeSafe(dbConnection, field) {
    const cname = dbConnection.typeName(field.dataTypeID);
    return cname ? fieldType(cname) : `unknown(${field.dataTypeID})`;
}

// columns are returned as an object { columnName1: { type1: ...}, ..}
// for consistency with SQL API
function formatResultFields(dbConnection, fields = []) {
    let nfields = {};
    for (let field of fields) {
      nfields[field.name] = { type: fieldTypeSafe(dbConnection, field) };
    }
    return nfields;
}

MapnikLayerStats.prototype.getStats =
function (layer, dbConnection, callback) {
    let aggrQuery = layer.options.sql;
    let preQuery =  layer.options.sql_raw || aggrQuery;

    let ctx = {
        dbConnection,
        preQuery,
        aggrQuery,
        metaOptions: layer.options.metadata || {},
    };

    // TODO: could save some queries if queryUtils.getAggregationMetadata() has been used and kept somewhere
    // we would set queries.results.estimatedFeatureCount and queries.results.geometryType
    // (if metaOptions.geometryType) from it.

    // TODO: compute _sample with _featureCount when available
    // TODO: add support for sample.exclude option by, in that case, forcing the columns query and
    // passing the results to the sample query function.

    const dimensions = (layer.options.aggregation || {}).dimensions;

    Promise.all([
        _estimatedFeatureCount(ctx).then(
            ({ estimatedFeatureCount }) => _sample(ctx)
                .then(sampleResults => mergeResults([ sampleResults, { estimatedFeatureCount }] ))
        ),
        _featureCount(ctx),
        _aggrFeatureCount(ctx),
        _geometryType(ctx),
        _columns(ctx).then(columns => _columnStats(ctx, columns, dimensions))
    ]).then(results => {
        results = mergeResults(results);
        callback(null, results);
    }).catch(error => {
        callback(error);
    });
};

module.exports = MapnikLayerStats;
<|endoftext|>


<commit_before>'use strict'

function size(width) {
  return function (driver) {
    return driver.setWindowSize(width, 600 /* any height*/);
  };
}

module.exports = {
  seleniumHost: 'http://localhost:4444/wd/hub',
  browsers: ['chrome'],
  envHosts: {
    build: 'http://localhost:8000/example/build',
    prod: 'http://localhost:8000/example/prod'
  },
  paths: [
    { 'Tiny CSS Difference': ['/tiny_css_difference.html', size(800)] },
    {
      'Chart Difference': ['/chart_difference.html', function (browser) {
        return size(800)(browser).sleep(2000);
      }]
    },
    { 'Content Difference': ['/content_difference.html', size(1000)] },
    {
      'Number Difference': ['/number_difference.html', size(800)]
    },
    {
      'Event Handling': ['/event_handling.html', function (browser) {
        return browser.elementByCss('#showContent').click();
      }]
    },
    {
      'Partial Difference': ['/content_difference.html', 'tr:nth-of-type(5)', size(800)]
    },
    {
      'Responsive': ['/responsive.html', size(480)]
    }
  ],
  reportFormat: 'file'
};
<commit_msg>update viff example<commit_after>'use strict'

function size(width) {
  return function (driver) {
    return driver.setWindowSize(width, 600 /* any height*/);
  };
}

module.exports = {
  seleniumHost: 'http://localhost:4444/wd/hub',
  browsers: ['firefox'],
  envHosts: {
    build: 'http://localhost:8000/example/build',
    prod: 'http://localhost:8000/example/prod'
  },
  paths: [
    { 'Tiny CSS Difference': ['/tiny_css_difference.html', size(800)] },
    {
      'Chart Difference': ['/chart_difference.html', function (browser) {
        return size(800)(browser).sleep(2000);
      }]
    },
    { 'Content Difference': ['/content_difference.html', size(1000)] },
    {
      'Number Difference': ['/number_difference.html', size(800)]
    },
    {
      'Event Handling': ['/event_handling.html', function (browser) {
        return browser.elementByCss('#showContent').click();
      }]
    },
    {
      'Partial Difference': ['/content_difference.html', 'tr:nth-of-type(5)', size(800)]
    },
    {
      'Responsive': ['/responsive.html', size(480)]
    }
  ],
  reportFormat: 'file'
};
<|endoftext|>


<commit_before>// need to add 'is_other' to repos so we can make this work

var get_slug_from_repo_data = function (repo_root_url, repo_branch_url) {
	var slug = null;
	for (i=0;i<repo_data.length;i++) {
		if (repo_data[i].url == repo_root_url) {
			var regexp_title = new RegExp(repo_data[i].repo_regex);
			var match_obj = regexp_title.exec(repo_branch_url);
			if (match_obj !== null) {
				slug = match_obj[1]
			}
			break;
		}
	}
	return slug;
}

function package_form(data){
	var REPLACEMENTS = {
		'https://github.com': [
			'http://github.com',
			'git@github.com:',
			'git://github.com'
		],
		'https://bitbucket.org': [
			'http://bitbucket.org'
		],
		'https://code.launchpad.net/': [
			'lp:'
		]
	};

	var repo_url = $("#id_repo_url");
	var pypi_url = $("#id_pypi_url");

	String.prototype.starts_with = function(str){
		return (this.indexOf(str) === 0);
	}

	String.prototype.ends_with = function(str){
		return (this.lastIndexOf(str) === this.length-str.length);
	}

	repo_url.focus();

	pypi_url_g = "http://pypi.python.org/pypi/";

	pypi_url.val(pypi_url.val().replace(pypi_url_g,""));

	pypi_url.change(function(e){
		pypi_url.val(pypi_url.val().replace(pypi_url_g,""));
	});

	repo_url.keyup(function(e) {
		var url = repo_url.val();
		return url
	});

	repo_url.change(function(e) {

		$("#target").text(repo_url.val());

		var url = repo_url.val();

		// this fixes the problem with trailing slashes
		while (1==1){
			if (url.ends_with('/')){
				url = url.slice(0, url.length-1);
				repo_url.val(url);
				}
			else {
				break;
			};
		};

		$.each(REPLACEMENTS, function(key, value){
			$.each(value, function(index, prefix){
				if (url.starts_with(prefix)){
					url = key + url.slice(prefix.length);s
				}
			}
		});
		if (url.ends_with('.git')){
			url = url.slice(0, url.length-4);
			repo_url.val(url);
		};
		repo_url.val(url);

		var url_array = url.split('/');
		$.each(repo_data, function(index, item) {
			if (url.starts_with(item.url)){
				if (url !== item.url){
					var slug = null;
					if ($("#id_title").val().length === 0) {
						// determine title/slug automagically
						slug = get_slug_from_repo_data(item.url, url);
						$("#id_title").val(slug);
					};
					if (slug === null) {
						// fallback slug detector
						slug = DPSlugify(url_array[url_array.length-1]);
					}
					$("#id_slug").val(slug);
					pypi_url.val(slug);
					$("#package-form-message").text("Your package is hosted at " + item.title)
				};
			};
		});
	});

	$("#package-form").submit(function(e) {
		// hack to get around some database vs front end weirdness
		var pypi_url = pypi_url.val();
		if (pypi_url.length > 0){
		  pypi_url.attr("name", "nuke");
		  $("#temp").val(pypi_url_g + pypi_url);
		  $("#temp").attr("name", "pypi_url");
		};

		return true
	});
}<commit_msg>Forgot that I renamed a var in JS<commit_after>function package_form(data){
	var get_slug_from_repo_data = function (repo_root_url, repo_branch_url) {
		var slug = null;
		for (i=0;i<data.length;i++) {
			if (data[i].url == repo_root_url) {
				var regexp_title = new RegExp(data[i].repo_regex);
				var match_obj = regexp_title.exec(repo_branch_url);
				if (match_obj !== null) {
					slug = match_obj[1]
				}
				break;
			}
		}
		return slug;
	}
	var REPLACEMENTS = {
		'https://github.com': [
			'http://github.com',
			'git@github.com:',
			'git://github.com'
		],
		'https://bitbucket.org': [
			'http://bitbucket.org'
		],
		'https://code.launchpad.net/': [
			'lp:'
		]
	};

	var repo_url = $("#id_repo_url");
	var pypi_url = $("#id_pypi_url");

	String.prototype.starts_with = function(str){
		return (this.indexOf(str) === 0);
	}

	String.prototype.ends_with = function(str){
		return (this.lastIndexOf(str) === this.length-str.length);
	}

	repo_url.focus();

	pypi_url_g = "http://pypi.python.org/pypi/";

	pypi_url.val(pypi_url.val().replace(pypi_url_g,""));

	pypi_url.change(function(e){
		pypi_url.val(pypi_url.val().replace(pypi_url_g,""));
	});

	repo_url.keyup(function(e) {
		var url = repo_url.val();
		return url
	});

	repo_url.change(function(e) {

		$("#target").text(repo_url.val());

		var url = repo_url.val();

		// this fixes the problem with trailing slashes
		while (1==1){
			if (url.ends_with('/')){
				url = url.slice(0, url.length-1);
				repo_url.val(url);
				}
			else {
				break;
			};
		};

		$.each(REPLACEMENTS, function(key, value){
			$.each(value, function(index, prefix){
				if (url.starts_with(prefix)){
					url = key + url.slice(prefix.length);s
				}
			}
		});
		if (url.ends_with('.git')){
			url = url.slice(0, url.length-4);
			repo_url.val(url);
		};
		repo_url.val(url);

		var url_array = url.split('/');
		$.each(data, function(index, item) {
			if (url.starts_with(item.url)){
				if (url !== item.url){
					var slug = null;
					if ($("#id_title").val().length === 0) {
						// determine title/slug automagically
						slug = get_slug_from_repo_data(item.url, url);
						$("#id_title").val(slug);
					};
					if (slug === null) {
						// fallback slug detector
						slug = DPSlugify(url_array[url_array.length-1]);
					}
					$("#id_slug").val(slug);
					pypi_url.val(slug);
					$("#package-form-message").text("Your package is hosted at " + item.title)
				};
			};
		});
	});

	$("#package-form").submit(function(e) {
		// hack to get around some database vs front end weirdness
		var pypi_url = pypi_url.val();
		if (pypi_url.length > 0){
		  pypi_url.attr("name", "nuke");
		  $("#temp").val(pypi_url_g + pypi_url);
		  $("#temp").attr("name", "pypi_url");
		};

		return true
	});
}<|endoftext|>


<commit_before>// BusinessProcess costs resolution

'use strict';

var memoize               = require('memoizee/plain')
  , definePercentage      = require('dbjs-ext/number/percentage')
  , defineMultipleProcess = require('../lib/multiple-process')
  , defineCost            = require('../cost')
  , defineRegistrations   = require('./registrations');

module.exports = memoize(function (db/* options */) {
	var BusinessProcess = defineRegistrations(db, arguments[1])
	  , Percentage = definePercentage(db)
	  , MultipleProcess = defineMultipleProcess(db)
	  , Cost = defineCost(db);

	BusinessProcess.prototype.defineProperties({
		costs: { type: MultipleProcess, nested: true }
	});
	BusinessProcess.prototype.costs.map._descriptorPrototype_.type = Cost;
	BusinessProcess.prototype.costs.defineProperties({
		// Applicable costs resolved out of requested registrations
		applicable: { type: Cost, value: function (_observe) {
			var result = [];
			_observe(this.master.registrations.requested).forEach(function (registration) {
				_observe(registration.costs).forEach(function (cost) { result.push(cost); });
			});
			return result;
		} },
		// Paid costs
		paid: { type: Cost, mutiple: true, value: function (_observe) {
			var result = [];
			this.applicable.forEach(function (cost) {
				if (_observe(cost._isPaid)) result.push(cost);
			});
			return result;
		} },
		// Payment progress
		paymentProgress: { type: Percentage, value: function () {
			if (!this.applicable.size) return 1;
			return this.paid.size / this.applicable.size;
		} },
		// Payment progress for online payments
		onlinePaymentProgress: { type: Percentage, value: function (_observe) {
			var valid = 0, total = 0;
			this.applicable.forEach(function (cost) {
				if (!cost.isElectronic) return;
				++total;
				if (_observe(cost._isPaid)) ++valid;
			});
			if (!total) return 1;
			return valid / total;
		} }
	});
	return BusinessProcess;
}, { normalizer: require('memoizee/normalizers/get-1')() });
<commit_msg>Better documentation for costs<commit_after>// BusinessProcess costs resolution

'use strict';

var memoize               = require('memoizee/plain')
  , definePercentage      = require('dbjs-ext/number/percentage')
  , defineMultipleProcess = require('../lib/multiple-process')
  , defineCost            = require('../cost')
  , defineRegistrations   = require('./registrations');

module.exports = memoize(function (db/* options */) {
	var BusinessProcess = defineRegistrations(db, arguments[1])
	  , Percentage = definePercentage(db)
	  , MultipleProcess = defineMultipleProcess(db)
	  , Cost = defineCost(db);

	BusinessProcess.prototype.defineProperties({
		costs: { type: MultipleProcess, nested: true }
	});
	BusinessProcess.prototype.costs.map._descriptorPrototype_.type = Cost;
	BusinessProcess.prototype.costs.defineProperties({
		// Applicable costs resolved out of requested registrations
		applicable: { type: Cost, value: function (_observe) {
			var result = [];
			_observe(this.master.registrations.requested).forEach(function (registration) {
				_observe(registration.costs).forEach(function (cost) { result.push(cost); });
			});
			return result;
		} },
		// Paid costs
		paid: { type: Cost, mutiple: true, value: function (_observe) {
			var result = [];
			this.applicable.forEach(function (cost) {
				if (_observe(cost._isPaid)) result.push(cost);
			});
			return result;
		} },
		// Payment progress
		paymentProgress: { type: Percentage, value: function () {
			if (!this.applicable.size) return 1;
			return this.paid.size / this.applicable.size;
		} },
		// Payment progress for online payments
		// We require all online payments to be done in Part A stage.
		// So having this below 1, doesn't allow submit of application
		onlinePaymentProgress: { type: Percentage, value: function (_observe) {
			var valid = 0, total = 0;
			this.applicable.forEach(function (cost) {
				if (!cost.isElectronic) return;
				++total;
				if (_observe(cost._isPaid)) ++valid;
			});
			if (!total) return 1;
			return valid / total;
		} }
	});
	return BusinessProcess;
}, { normalizer: require('memoizee/normalizers/get-1')() });
<|endoftext|>


<commit_before>exports.name = 'pluginExample';
exports.dependencies = ['mwc_plugin_foo','mwc_plugin_bar']; //we throw error it this plugins are not loaded in application

exports.core = {
  'parameterOne': 1,
  'parameterTwo': [1, 2, 3, 4, 5],
  'parameterThree': {},
  'getSum': function (config) {
    return function (a, b) {
      return (a + b) * (config.multipyKoefficient);
    }
  }
};

exports.model = {
  'Cats': function (mongoose, config) {
    var CatsSchema = new mongoose.Schema({
      'nickname': String
    });

    CatsSchema.index({
      nickname: 1
    });
    return mongoose.model('cats', CatsSchema);
  },

  'Dogs': function (mongoose, config) {
    var DogsSchema = new mongoose.Schema({
      'nickname': String
    });

    DogsSchema.index({
      nickname: 1
    });

    return mongoose.model('Dogs', DogsSchema);
  }
};

exports.app = function (core) {
  core.app.set('someValue',42);
};

var LinkedInStrategy = require('passport-linkedin').Strategy;

//sorry, only one(!) passportJS strategy per plugin!
exports.strategy = {
  'strategy': function (core) {
    return new LinkedInStrategy({
      consumerKey: core.config.passport.LINKEDIN_API_KEY,
      consumerSecret: core.config.passport.LINKEDIN_SECRET_KEY,
      callbackURL: core.config.hostUrl + 'auth/linkedin/callback'
    }, function (token, tokenSecret, profile, done) {
      console.log('==============');
      console.log(profile);
      console.log('==============');
      var email = profile.emails[0].value;
      if (email) {
        core.model.Users.processOAuthProfile(email, done);
      } else {
        return done(new Error('There is something strange instead of user profile'));
      }
    });
  },
  'routes': function (passport, core) {
    core.app.get('/auth/linkedin', passport.authenticate('linkedin'), function (req, res) {
    });
    core.app.get('/auth/linkedin/callback', passport.authenticate('linkedin', { failureRedirect: '/' }),
      function (req, res) {
        res.redirect('/');
      });
  }
};

//the most hard to understand function
//because middleware conception is tricky and it do need the core object, for example,
// in this way or maybe in some other way. But it do need the core!!!
//for simplicity of code of plugin all middlewares are setted to all enviroments and are mounted to path /
exports.middleware = [
  function (core) {
    return function (request, response, next) {
      request.model.Cats.count({name: 'Grumpy'}, function (err, numberOfCats) {
        if (numberOfCats > core.parameterOne) {
          request.getSum = core.getSum; //DI of core methods or values
          next();
        } else {
          response.send(500, 'There is not enough cats called "Grumpy" to run this application!');
        }
      });
    };
  },
  function (core) {
    return function (request, response, next) {
      request.model.Dogs.count({name: 'Strelka'}, function (err, numberOfDogs) {
        if (numberOfDogs > core.parameterOne) {
          request.getSum = core.getSum; //DI of core methods or values
          next();
        } else {
          response.send(500, 'There is not enough Dogs called "Strelka" to run this application!');
        }
      });
    };
  }
];

//extending routes
//maybe we have injected all we need in routes parameters in extendMiddleware
//and we can bind routes to application instead of core...

exports.routes = function(core){
  core.app.get('/kittens',function(request,response){
    request.model.Cats.find({},function(err,cats){
      if(err) throw err;
      response.json(cats);
    });
  });

  core.app.get('/dogs',function(request,response){
    request.model.Dogs.find({},function(err,dogs){
      if(err) throw err;
      response.json(dogs);
    });
  });
};

//custom listeners to core events
exports.listeners('alert',function(alert){
  console.log(alert);
});

<commit_msg>[documentation] - for plugin<commit_after>/**
 * @ngdoc function
 * @name Plugin
 * @description
 * Plugin object, that can be loaded by mwc.loadPlugin
 */

exports.name = 'pluginExample';
exports.dependencies = ['mwc_plugin_foo','mwc_plugin_bar']; //we throw error it this plugins are not loaded in application

exports.core = {
  'parameterOne': 1,
  'parameterTwo': [1, 2, 3, 4, 5],
  'parameterThree': {},
  'getSum': function (config) {
    return function (a, b) {
      return (a + b) * (config.multipyKoefficient);
    }
  }
};

exports.model = {
  'Cats': function (mongoose, config) {
    var CatsSchema = new mongoose.Schema({
      'nickname': String
    });

    CatsSchema.index({
      nickname: 1
    });
    return mongoose.model('cats', CatsSchema);
  },

  'Dogs': function (mongoose, config) {
    var DogsSchema = new mongoose.Schema({
      'nickname': String
    });

    DogsSchema.index({
      nickname: 1
    });

    return mongoose.model('Dogs', DogsSchema);
  }
};

exports.app = function (core) {
  core.app.set('someValue',42);
};

var LinkedInStrategy = require('passport-linkedin').Strategy;

//sorry, only one(!) passportJS strategy per plugin!
exports.strategy = {
  'strategy': function (core) {
    return new LinkedInStrategy({
      consumerKey: core.config.passport.LINKEDIN_API_KEY,
      consumerSecret: core.config.passport.LINKEDIN_SECRET_KEY,
      callbackURL: core.config.hostUrl + 'auth/linkedin/callback'
    }, function (token, tokenSecret, profile, done) {
      console.log('==============');
      console.log(profile);
      console.log('==============');
      var email = profile.emails[0].value;
      if (email) {
        core.model.Users.processOAuthProfile(email, done);
      } else {
        return done(new Error('There is something strange instead of user profile'));
      }
    });
  },
  'routes': function (passport, core) {
    core.app.get('/auth/linkedin', passport.authenticate('linkedin'), function (req, res) {
    });
    core.app.get('/auth/linkedin/callback', passport.authenticate('linkedin', { failureRedirect: '/' }),
      function (req, res) {
        res.redirect('/');
      });
  }
};

//the most hard to understand function
//because middleware conception is tricky and it do need the core object, for example,
// in this way or maybe in some other way. But it do need the core!!!
//for simplicity of code of plugin all middlewares are setted to all enviroments and are mounted to path /
exports.middleware = [
  function (core) {
    return function (request, response, next) {
      request.model.Cats.count({name: 'Grumpy'}, function (err, numberOfCats) {
        if (numberOfCats > core.parameterOne) {
          request.getSum = core.getSum; //DI of core methods or values
          next();
        } else {
          response.send(500, 'There is not enough cats called "Grumpy" to run this application!');
        }
      });
    };
  },
  function (core) {
    return function (request, response, next) {
      request.model.Dogs.count({name: 'Strelka'}, function (err, numberOfDogs) {
        if (numberOfDogs > core.parameterOne) {
          request.getSum = core.getSum; //DI of core methods or values
          next();
        } else {
          response.send(500, 'There is not enough Dogs called "Strelka" to run this application!');
        }
      });
    };
  }
];

//extending routes
//maybe we have injected all we need in routes parameters in extendMiddleware
//and we can bind routes to application instead of core...

exports.routes = function(core){
  core.app.get('/kittens',function(request,response){
    request.model.Cats.find({},function(err,cats){
      if(err) throw err;
      response.json(cats);
    });
  });

  core.app.get('/dogs',function(request,response){
    request.model.Dogs.find({},function(err,dogs){
      if(err) throw err;
      response.json(dogs);
    });
  });
};

//custom listeners to core events
exports.listeners('alert',function(alert){
  console.log(alert);
});

<|endoftext|>


<commit_before>/*
 * MODAL VIEWS
 */

'use strict';

+function(window, $, _) {

    window.LCB = window.LCB || {};

    window.LCB.ModalView = Backbone.View.extend({
        events: {
        	'submit form': 'submit'
        },
        initialize: function(options) {
            this.render();
        },
        render: function() {
            this.$('form.validate').validate();
            this.$el.on('shown.bs.modal hidden.bs.modal',
                        _.bind(this.refresh, this));
        },
        refresh: function() {
            var that = this;
            this.$('[data-model]').each(function() {
                $(this).val && $(this).val(that.model.get($(this).data('model')));
            });
        },
        success: function() {
            swal('Updated!', '', 'success');
            this.$el.modal('hide');
        },
        error: function() {
            swal('Woops!', '', 'error');
        },
        submit: function(e) {
        	e && e.preventDefault();

            var $form = this.$('form[action]');
            var opts = {
                type: $form.attr('method') || 'POST',
                url: $form.attr('action'),
                data: $form.serialize(),
                dataType: 'json'
            };

            if (this.success) {
                opts.success = _.bind(this.success, this);
            }
            if (this.error) {
                opts.error = _.bind(this.error, this);
            }
            if (this.complete) {
                opts.complete = _.bind(this.complete, this);
            }

            $.ajax(opts);
        }
    });

    window.LCB.ProfileModalView = window.LCB.ModalView.extend({
        success: function() {
            swal('Profile Updated!', 'Your profile has been updated.',
                 'success');
            this.$el.modal('hide');
        },
        error: function() {
            swal('Woops!', 'Your profile was not updated.', 'error');
        }
    });

    window.LCB.AccountModalView = window.LCB.ModalView.extend({
        success: function() {
            swal('Account Updated!', 'Your account has been updated.', 'success');
            this.$el.modal('hide');
            this.$('[type="password"]').val('');
        },
        error: function(req) {
            var message = req.responseJSON && req.responseJSON.reason ||
                          'Your account was not updated.';

            swal('Woops!', message, 'error');
        },
        complete: function() {
            this.$('[name="current-password"]').val('');
        }
    });

    window.LCB.AuthTokensModalView = Backbone.View.extend({
        events: {
            'click .generate-token': 'generateToken',
            'click .revoke-token': 'revokeToken'
        },
        initialize: function(options) {
            this.render();
        },
        render: function() {
            this.$el.on('shown.bs.modal hidden.bs.modal',
                        _.bind(this.refresh, this));
        },
        refresh: function() {
            this.$('.token').val('');
            this.$('.generated-token').hide();
        },
        getToken: function() {
            var that = this;
            $.post('./account/token/generate', function(data) {
                if (data.token) {
                    that.$('.token').val(data.token);
                    that.$('.generated-token').show();
                }
            });
        },
        removeToken: function() {
            var that = this;
            $.post('./account/token/revoke', function(data) {
                that.refresh();
                swal('Success', 'Authentication token revoked!', 'success');
            });
        },
        generateToken: function() {
            swal({
                title: 'Are you sure?',
                text: 'This will overwrite any existing authentication token you may have.',   type: 'warning',
                showCancelButton: true,
                confirmButtonText: 'Yes',
                closeOnConfirm: true },
                _.bind(this.getToken, this)
            );
        },
        revokeToken: function() {
            swal({
                title: 'Are you sure?',
                text: 'This will revoke access from any process using your current authentication token.',   type: 'warning',
                showCancelButton: true,
                confirmButtonText: 'Yes',
                closeOnConfirm: false },
                _.bind(this.removeToken, this)
            );
        }
    });

    window.LCB.NotificationsModalView = Backbone.View.extend({
        events: {
            'click [name=desktop-notifications]': 'toggleDesktopNotifications'
        },
        initialize: function() {
            this.render();
        },
        render: function() {
            var $input = this.$('[name=desktop-notifications]');
            $input.find('.disabled').show()
              .siblings().hide();
            if (!notify.isSupported) {
                $input.attr('disabled', true);
                // Welp we're done here
                return;
            }
            if (notify.permissionLevel() === notify.PERMISSION_GRANTED) {
                $input.find('.enabled').show()
                  .siblings().hide();
            }
            if (notify.permissionLevel() === notify.PERMISSION_DENIED) {
                $input.find('.blocked').show()
                  .siblings().hide();
            }
        },
        toggleDesktopNotifications: function() {
            var that = this;
            if (!notify.isSupported) {
                return;
            }
            notify.requestPermission(function() {
                that.render();
            });
        }
    });

    window.LCB.FindPatientsView = window.LCB.ModalView.extend({
        events: {
            'keyup #lcb-rooms-browser-filter-input': 'filter',
            'click .lcb-rooms-list-item-row': 'activateRoom'
        },
        initialize: function(options) {
            this.client = options.client;
            this.template = Handlebars.compile($('#template-room-browser-row').html());
            this.rooms = options.rooms;
            this.rooms.on('add', this.add, this);
            this.rooms.on('remove', this.removeRoom, this);
            this.rooms.on('change:name', this.update, this);
            this.rooms.on('change:lastActive', _.debounce(this.updateLastActive, 200), this);
            // this.rooms.on('change:joined', this.updateToggles, this);
            this.rooms.current.on('change:id', function(current, id) {
                // We only care about the list pane
                if (id !== 'list') return;
                this.sort();
            }, this);
        },
        // updateToggles: function(room, joined) {
        //     this.$('.lcb-rooms-switch[data-id=' + room.id + ']').prop('checked', joined);
        // },
        activateRoom: function(e) {
            e.preventDefault();
            var $roomRow = $(e.currentTarget),
                id = $roomRow.data('id'),
                room = this.rooms.get(id);
            if (!room) {
                return;
            }
            if(!this.rooms.get(room.id).get('joined')){
                this.client.joinRoom(room.id);
            }
        },
        add: function(room) {
            var room = room.toJSON ? room.toJSON() : room,
                context = _.extend(room, {
                    lastActive: moment(room.lastActive).format('M/D/YYYY')
                });
            this.$('.lcb-rooms-list-modal tbody').append(this.template(context));
        },
        removeRoom: function(room) {
            this.$('.lcb-rooms-list-item-row[data-id=' + room.id + ']').remove();
        },
        update: function(room) {
            this.$('.lcb-rooms-list-item-row[data-id=' + room.id + '] .lcb-rooms-list-item-name-modal').text(room.get('name'));
        },
        updateLastActive: function(room) {
            this.$('.lcb-rooms-list-item-row[data-id=' + room.id + '] .lcb-rooms-list-item-last-active .value').text(moment(room.get('lastActive')).format('M/D/YYYY'));
        },
        sort: function(model) {//Eric.TODO, verify sort logic
            var that = this,
                $items = this.$('.lcb-rooms-list-item-row');
            // We only care about other users
            if (this.$el.hasClass('hide') && model && model.id === this.client.user.id)
                return;
            $items.sort(function(a, b){
                var ar = that.rooms.get($(a).data('id')),
                    br = that.rooms.get($(b).data('id')),
                    au = ar.users.length,
                    bu = br.users.length,
                    aj = ar.get('joined'),
                    bj = br.get('joined')
                if ((aj && bj) || (!aj && !bj)) {
                    if (au > bu) return -1;
                    if (au < bu) return 1;
                }
                if (aj) return -1;
                if (bj) return 1;
                return 0;
            });
            $items.detach().appendTo(this.$('.lcb-rooms-list-modal'));
        },
        filter: function(e) {
            e.preventDefault();
            var $input = $(e.currentTarget),
                needle = $input.val().toLowerCase();
            this.$('.lcb-rooms-list-item-row').each(function () {
                var name = $(this).find('.lcb-rooms-list-item-name-modal').text().toLowerCase();
                var mrn = $(this).find('.lcb-rooms-list-item-name-mrn').text().toLowerCase();
                $(this).toggle(name.indexOf(needle) >= 0 || mrn.indexOf(needle) >= 0 );
            });
        },
        hide: function(){
            this.$el.modal('hide');
        },
        show: function(){
            this.$el.modal('show');
        },
    });

    window.LCB.AddPatientView = window.LCB.ModalView.extend({
        initialize: function(options) {
            var self = this;
            self.client = options.client;
            self.parentModal = options.parentModal;
            self.render();

            self.$el.on('show.bs.modal', function(){
                self.parentModal.hide();
            });
            self.$el.on('hide.bs.modal', function(){
                self.parentModal.show();
            });
        },
        submit: function(e) {
            e.preventDefault();
            var $modal = $('#lcb-add-room'),
                $form = this.$(e.target),
                $name = this.$('.lcb-room-name'),
                $slug = this.$('.lcb-room-slug'),
                data = {
                    name: $name.val().trim(),
                    slug: $slug.val().trim(),
                    description: this.$('.lcb-room-description').val(),
                    callback: function success() {
                        $modal.modal('hide');
                        $('#lcb-find-patients').modal('hide');
                        $form.trigger('reset');

                        swal('Patient Added!', 'The patient has been added.', 'success');
                    }
                };

            this.$('.form-group').removeClass('has-error');
            // we require name is non-empty
            if (!data.name) {
                $name.parent().addClass('has-error');
                return;
            }
            // we require slug is non-empty
            if (!data.slug) {
                $slug.parent().addClass('has-error');
                return;
            }
            this.client.events.trigger('rooms:create', data);
        }
    });

    window.LCB.FindProvidersView = Backbone.View.extend({
        events: {
            'keyup #lcb-providers-browser-filter-input': 'filter',
        },
        initialize: function(options) {
            var self = this;
            this.client = options.client;
            this.listenTo(this.collection, 'add', this.addProvider);
            this.$el.on('show.bs.modal', function(){
                self.client.getUsersSync();
            });
        },
        render: function () {
            this.collection.each(this.add);
            return this;
        },
        addProvider: function (item) {
            var providerView = new ProviderView ({ model: item , client : this.client, hideModal: _.bind(this.hide, this)}),
                rendered = providerView.render().el;

                //Item will be added to the list in order of name
                var afterAll = true;
                this.$el.find('.lcb-providers-list-item').each(function(){
                    var name = $(this).find('.lcb-providers-list-item-name').text();
                    if(afterAll && item.get('displayName') < name){
                        $(this).before(rendered);
                        afterAll = false;
                    }
                })
                if(afterAll){
                     this.$el.find('table>tbody').append(rendered);
                }
        },
        hide: function(){
            this.$el.modal('hide');
        },
        show: function(){
            this.$el.modal('show');
        },
        sort: function(model) {
            var that = this,
                $items = this.$('.lcb-providers-list-item');
            // We only care about other users
            if (this.$el.hasClass('hide') && model && model.id === this.client.user.id)
                return;
            $items.sort(function(a, b){
                var ar = that.providers.get($(a).data('id')),
                    br = that.providers.get($(b).data('id')),
                    au = ar.users.length,
                    bu = br.users.length,
                    aj = ar.get('joined'),
                    bj = br.get('joined')
                if ((aj && bj) || (!aj && !bj)) {
                    if (au > bu) return -1;
                    if (au < bu) return 1;
                }
                if (aj) return -1;
                if (bj) return 1;
                return 0;
            });
            $items.detach().appendTo(this.$('.lcb-providers-list'));
        },
        filter: function(e) {
            e.preventDefault();
            var $input = $(e.currentTarget),
                needle = $input.val().toLowerCase();
            //TODO:We might want to display message while no matched records
            //but i think low priorty
            this.$('.lcb-providers-list-item').each(function () {
                var haystack = $(this).find('.lcb-providers-list-item-name').text().toLowerCase();
                $(this).toggle(haystack.indexOf(needle) >= 0);
            });
        },
    });

    var ProviderView = Backbone.View.extend({
        tagName:"tr",
        className : 'lcb-providers-list-item',
        initialize: function(options){
            this.client = options.client;
            this._tmplRender = Handlebars.compile($('#template-provider-browser-item').html());
            this.hideModal = options.hideModal;
        },
        events: {
            "click td": "sendInvite",
        },
        render: function(){
            var innerHTML = this._tmplRender({user:this.model.toJSON()});
            this.el.innerHTML = innerHTML;
            return this;
        },
        sendInvite: function () {
            var username = this.model.get('username')
            var roomId = this.client.rooms.current.get('id');

            //There might be multiple entry box, one message box per room
            var $activedMessageBox = $('.lcb-room:not(.hide) .lcb-entry-input');

            var currentContent = ($activedMessageBox.val() === '' ? '' : $activedMessageBox.val() + ' ');
            $activedMessageBox.val(currentContent +'@' + username + ' ');
            // TODO double check whether we need foucs
            // at least in chrome, focus works,and you can type text diretly even the modal
            // window is on the top, but a little bit wired
            // $activedMessageBox.focus();
            this.client.inviteToRoom(username, roomId);
        }

    });
    window.LCB.InviteNewProviderView = Backbone.View.extend({
        initialize: function(options){
            var self = this;
            this.client = options.client;
            this.parentModal = options.parentModal;
            this.$el.on('show.bs.modal', function(){
                self.parentModal.hide();
            });
            this.$el.on('hide.bs.modal', function(){
                self.parentModal.show();
            });
        },
        events: {
            "click #lcb-invite-new-provider-confirm": "inviteNewProvider",
        },
        inviteNewProvider: function () {
            $.ajax({
                type: "POST",
                url: "/mail/inviteProvider",
                data: {
                    'receiverEmail': $('#lcb-invite-new-provider-email').val(),
                    'message': $('#lcb-invite-new-provider-message').val(),
                    'inviterName': this.client.user.get('displayName'),
                    'inviterOrg': this.client.user.get('organizationName')
                },
                success: function(res){
                    //console.log('invitation mail sent');
                }
            });
        }
    });

}(window, $, _);
<commit_msg>Display error message while fail to create a room with duplicated MRN number<commit_after>/*
 * MODAL VIEWS
 */

'use strict';

+function(window, $, _) {

    window.LCB = window.LCB || {};

    window.LCB.ModalView = Backbone.View.extend({
        events: {
        	'submit form': 'submit'
        },
        initialize: function(options) {
            this.render();
        },
        render: function() {
            this.$('form.validate').validate();
            this.$el.on('shown.bs.modal hidden.bs.modal',
                        _.bind(this.refresh, this));
        },
        refresh: function() {
            var that = this;
            this.$('[data-model]').each(function() {
                $(this).val && $(this).val(that.model.get($(this).data('model')));
            });
        },
        success: function() {
            swal('Updated!', '', 'success');
            this.$el.modal('hide');
        },
        error: function() {
            swal('Woops!', '', 'error');
        },
        submit: function(e) {
        	e && e.preventDefault();

            var $form = this.$('form[action]');
            var opts = {
                type: $form.attr('method') || 'POST',
                url: $form.attr('action'),
                data: $form.serialize(),
                dataType: 'json'
            };

            if (this.success) {
                opts.success = _.bind(this.success, this);
            }
            if (this.error) {
                opts.error = _.bind(this.error, this);
            }
            if (this.complete) {
                opts.complete = _.bind(this.complete, this);
            }

            $.ajax(opts);
        }
    });

    window.LCB.ProfileModalView = window.LCB.ModalView.extend({
        success: function() {
            swal('Profile Updated!', 'Your profile has been updated.',
                 'success');
            this.$el.modal('hide');
        },
        error: function() {
            swal('Woops!', 'Your profile was not updated.', 'error');
        }
    });

    window.LCB.AccountModalView = window.LCB.ModalView.extend({
        success: function() {
            swal('Account Updated!', 'Your account has been updated.', 'success');
            this.$el.modal('hide');
            this.$('[type="password"]').val('');
        },
        error: function(req) {
            var message = req.responseJSON && req.responseJSON.reason ||
                          'Your account was not updated.';

            swal('Woops!', message, 'error');
        },
        complete: function() {
            this.$('[name="current-password"]').val('');
        }
    });

    window.LCB.AuthTokensModalView = Backbone.View.extend({
        events: {
            'click .generate-token': 'generateToken',
            'click .revoke-token': 'revokeToken'
        },
        initialize: function(options) {
            this.render();
        },
        render: function() {
            this.$el.on('shown.bs.modal hidden.bs.modal',
                        _.bind(this.refresh, this));
        },
        refresh: function() {
            this.$('.token').val('');
            this.$('.generated-token').hide();
        },
        getToken: function() {
            var that = this;
            $.post('./account/token/generate', function(data) {
                if (data.token) {
                    that.$('.token').val(data.token);
                    that.$('.generated-token').show();
                }
            });
        },
        removeToken: function() {
            var that = this;
            $.post('./account/token/revoke', function(data) {
                that.refresh();
                swal('Success', 'Authentication token revoked!', 'success');
            });
        },
        generateToken: function() {
            swal({
                title: 'Are you sure?',
                text: 'This will overwrite any existing authentication token you may have.',   type: 'warning',
                showCancelButton: true,
                confirmButtonText: 'Yes',
                closeOnConfirm: true },
                _.bind(this.getToken, this)
            );
        },
        revokeToken: function() {
            swal({
                title: 'Are you sure?',
                text: 'This will revoke access from any process using your current authentication token.',   type: 'warning',
                showCancelButton: true,
                confirmButtonText: 'Yes',
                closeOnConfirm: false },
                _.bind(this.removeToken, this)
            );
        }
    });

    window.LCB.NotificationsModalView = Backbone.View.extend({
        events: {
            'click [name=desktop-notifications]': 'toggleDesktopNotifications'
        },
        initialize: function() {
            this.render();
        },
        render: function() {
            var $input = this.$('[name=desktop-notifications]');
            $input.find('.disabled').show()
              .siblings().hide();
            if (!notify.isSupported) {
                $input.attr('disabled', true);
                // Welp we're done here
                return;
            }
            if (notify.permissionLevel() === notify.PERMISSION_GRANTED) {
                $input.find('.enabled').show()
                  .siblings().hide();
            }
            if (notify.permissionLevel() === notify.PERMISSION_DENIED) {
                $input.find('.blocked').show()
                  .siblings().hide();
            }
        },
        toggleDesktopNotifications: function() {
            var that = this;
            if (!notify.isSupported) {
                return;
            }
            notify.requestPermission(function() {
                that.render();
            });
        }
    });

    window.LCB.FindPatientsView = window.LCB.ModalView.extend({
        events: {
            'keyup #lcb-rooms-browser-filter-input': 'filter',
            'click .lcb-rooms-list-item-row': 'activateRoom'
        },
        initialize: function(options) {
            this.client = options.client;
            this.template = Handlebars.compile($('#template-room-browser-row').html());
            this.rooms = options.rooms;
            this.rooms.on('add', this.add, this);
            this.rooms.on('remove', this.removeRoom, this);
            this.rooms.on('change:name', this.update, this);
            this.rooms.on('change:lastActive', _.debounce(this.updateLastActive, 200), this);
            // this.rooms.on('change:joined', this.updateToggles, this);
            this.rooms.current.on('change:id', function(current, id) {
                // We only care about the list pane
                if (id !== 'list') return;
                this.sort();
            }, this);
        },
        // updateToggles: function(room, joined) {
        //     this.$('.lcb-rooms-switch[data-id=' + room.id + ']').prop('checked', joined);
        // },
        activateRoom: function(e) {
            e.preventDefault();
            var $roomRow = $(e.currentTarget),
                id = $roomRow.data('id'),
                room = this.rooms.get(id);
            if (!room) {
                return;
            }
            if(!this.rooms.get(room.id).get('joined')){
                this.client.joinRoom(room.id);
            }
        },
        add: function(room) {
            var room = room.toJSON ? room.toJSON() : room,
                context = _.extend(room, {
                    lastActive: moment(room.lastActive).format('M/D/YYYY')
                });
            this.$('.lcb-rooms-list-modal tbody').append(this.template(context));
        },
        removeRoom: function(room) {
            this.$('.lcb-rooms-list-item-row[data-id=' + room.id + ']').remove();
        },
        update: function(room) {
            this.$('.lcb-rooms-list-item-row[data-id=' + room.id + '] .lcb-rooms-list-item-name-modal').text(room.get('name'));
        },
        updateLastActive: function(room) {
            this.$('.lcb-rooms-list-item-row[data-id=' + room.id + '] .lcb-rooms-list-item-last-active .value').text(moment(room.get('lastActive')).format('M/D/YYYY'));
        },
        sort: function(model) {//Eric.TODO, verify sort logic
            var that = this,
                $items = this.$('.lcb-rooms-list-item-row');
            // We only care about other users
            if (this.$el.hasClass('hide') && model && model.id === this.client.user.id)
                return;
            $items.sort(function(a, b){
                var ar = that.rooms.get($(a).data('id')),
                    br = that.rooms.get($(b).data('id')),
                    au = ar.users.length,
                    bu = br.users.length,
                    aj = ar.get('joined'),
                    bj = br.get('joined')
                if ((aj && bj) || (!aj && !bj)) {
                    if (au > bu) return -1;
                    if (au < bu) return 1;
                }
                if (aj) return -1;
                if (bj) return 1;
                return 0;
            });
            $items.detach().appendTo(this.$('.lcb-rooms-list-modal'));
        },
        filter: function(e) {
            e.preventDefault();
            var $input = $(e.currentTarget),
                needle = $input.val().toLowerCase();
            this.$('.lcb-rooms-list-item-row').each(function () {
                var name = $(this).find('.lcb-rooms-list-item-name-modal').text().toLowerCase();
                var mrn = $(this).find('.lcb-rooms-list-item-name-mrn').text().toLowerCase();
                $(this).toggle(name.indexOf(needle) >= 0 || mrn.indexOf(needle) >= 0 );
            });
        },
        hide: function(){
            this.$el.modal('hide');
        },
        show: function(){
            this.$el.modal('show');
        },
    });

    window.LCB.AddPatientView = window.LCB.ModalView.extend({
        initialize: function(options) {
            var self = this;
            self.client = options.client;
            self.parentModal = options.parentModal;
            self.render();

            self.$el.on('show.bs.modal', function(){
                self.parentModal.hide();
            });
            self.$el.on('hide.bs.modal', function(){
                self.parentModal.show();
            });
        },
        submit: function(e) {
            var self = this;
            e.preventDefault();
            var $modal = this.$el,
                $form = self.$(e.target),
                $name = self.$('.lcb-room-name'),
                $slug = self.$('.lcb-room-slug'),
                data = {
                    name: $name.val().trim(),
                    slug: $slug.val().trim(),
                    description: self.$('.lcb-room-description').val(),
                    callback: function success(resp) {
                        if(!resp.errors){
                            $modal.off('hide.bs.modal'); //Avoid the parentModal display again
                            $modal.modal('hide');
                            $form.trigger('reset');

                            swal('Patient Added!', 'The patient has been added.', 'success');
                        }
                    }
                };

            this.$('.form-group').removeClass('has-error');
            // we require name is non-empty
            if (!data.name) {
                $name.parent().addClass('has-error');
                return;
            }
            // we require slug is non-empty
            if (!data.slug) {
                $slug.parent().addClass('has-error');
                return;
            }
            this.client.events.trigger('rooms:create', data);
        }
    });

    window.LCB.FindProvidersView = Backbone.View.extend({
        events: {
            'keyup #lcb-providers-browser-filter-input': 'filter',
        },
        initialize: function(options) {
            var self = this;
            this.client = options.client;
            this.listenTo(this.collection, 'add', this.addProvider);
            this.$el.on('show.bs.modal', function(){
                self.client.getUsersSync();
            });
        },
        render: function () {
            this.collection.each(this.add);
            return this;
        },
        addProvider: function (item) {
            var providerView = new ProviderView ({ model: item , client : this.client, hideModal: _.bind(this.hide, this)}),
                rendered = providerView.render().el;

                //Item will be added to the list in order of name
                var afterAll = true;
                this.$el.find('.lcb-providers-list-item').each(function(){
                    var name = $(this).find('.lcb-providers-list-item-name').text();
                    if(afterAll && item.get('displayName') < name){
                        $(this).before(rendered);
                        afterAll = false;
                    }
                })
                if(afterAll){
                     this.$el.find('table>tbody').append(rendered);
                }
        },
        hide: function(){
            this.$el.modal('hide');
        },
        show: function(){
            this.$el.modal('show');
        },
        sort: function(model) {
            var that = this,
                $items = this.$('.lcb-providers-list-item');
            // We only care about other users
            if (this.$el.hasClass('hide') && model && model.id === this.client.user.id)
                return;
            $items.sort(function(a, b){
                var ar = that.providers.get($(a).data('id')),
                    br = that.providers.get($(b).data('id')),
                    au = ar.users.length,
                    bu = br.users.length,
                    aj = ar.get('joined'),
                    bj = br.get('joined')
                if ((aj && bj) || (!aj && !bj)) {
                    if (au > bu) return -1;
                    if (au < bu) return 1;
                }
                if (aj) return -1;
                if (bj) return 1;
                return 0;
            });
            $items.detach().appendTo(this.$('.lcb-providers-list'));
        },
        filter: function(e) {
            e.preventDefault();
            var $input = $(e.currentTarget),
                needle = $input.val().toLowerCase();
            //TODO:We might want to display message while no matched records
            //but i think low priorty
            this.$('.lcb-providers-list-item').each(function () {
                var haystack = $(this).find('.lcb-providers-list-item-name').text().toLowerCase();
                $(this).toggle(haystack.indexOf(needle) >= 0);
            });
        },
    });

    var ProviderView = Backbone.View.extend({
        tagName:"tr",
        className : 'lcb-providers-list-item',
        initialize: function(options){
            this.client = options.client;
            this._tmplRender = Handlebars.compile($('#template-provider-browser-item').html());
            this.hideModal = options.hideModal;
        },
        events: {
            "click td": "sendInvite",
        },
        render: function(){
            var innerHTML = this._tmplRender({user:this.model.toJSON()});
            this.el.innerHTML = innerHTML;
            return this;
        },
        sendInvite: function () {
            var username = this.model.get('username')
            var roomId = this.client.rooms.current.get('id');

            //There might be multiple entry box, one message box per room
            var $activedMessageBox = $('.lcb-room:not(.hide) .lcb-entry-input');

            var currentContent = ($activedMessageBox.val() === '' ? '' : $activedMessageBox.val() + ' ');
            $activedMessageBox.val(currentContent +'@' + username + ' ');
            // TODO double check whether we need foucs
            // at least in chrome, focus works,and you can type text diretly even the modal
            // window is on the top, but a little bit wired
            // $activedMessageBox.focus();
            this.client.inviteToRoom(username, roomId);
        }

    });
    window.LCB.InviteNewProviderView = Backbone.View.extend({
        initialize: function(options){
            var self = this;
            this.client = options.client;
            this.parentModal = options.parentModal;
            this.$el.on('show.bs.modal', function(){
                self.parentModal.hide();
            });
            this.$el.on('hide.bs.modal', function(){
                self.parentModal.show();
            });
        },
        events: {
            "click #lcb-invite-new-provider-confirm": "inviteNewProvider",
        },
        inviteNewProvider: function () {
            $.ajax({
                type: "POST",
                url: "/mail/inviteProvider",
                data: {
                    'receiverEmail': $('#lcb-invite-new-provider-email').val(),
                    'message': $('#lcb-invite-new-provider-message').val(),
                    'inviterName': this.client.user.get('displayName'),
                    'inviterOrg': this.client.user.get('organizationName')
                },
                success: function(res){
                    //console.log('invitation mail sent');
                }
            });
        }
    });

}(window, $, _);
<|endoftext|>


<commit_before>/**
 * Twaddlr - a simple chat server. Inspired by Campfire.
 */

var util = require('util'),
    connect = require('connect'),
    http = require('http'),
    redis = require('redis'),
    PORT = 3000;

// Connect to Redis
var redisClient = redis.createClient();
redisClient.on('error', function(err) {
    console.log("Couldn't connect to redis: " + err);
    process.exit(1);
});

// Create our server
var app = connect();
var publicDir = __dirname + '/public';

// Add mime-type for LESS files
//express.static.mime.define({ 'text/css': ['css', 'less'] });
//console.log(express.static.mime.lookup('less'));

// Configure express
app.use(connect.cookieParser('malabar'));
app.use(connect.session());
//app.use(express.logger());
//app.use(app.router);
//app.use(express.compiler({ src: publicDir, enable: ['less'] }));
app.use(connect.static(publicDir));

//app.use(express.basicAuth)

//app.set('views', viewsDir);
//app.set('view engine', 'jade');

//app.configure('development', function() {
//  //app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
//  app.use(express.static(publicDir));
//});

// Setup routes
//require('./routes/site')(app);
//require('./routes/rest_api')(app, null);

// Ok, let's listen on port PORT
console.log("Starting server on port " + PORT);
//var server = app.listen(PORT);
var server = http.createServer(app).listen(PORT);

// Initialize socket.io subsystem
require('./lib/com_hub')(server, redisClient);

//});

<commit_msg>Removed deprecated code<commit_after>/**
 * Twaddlr - a simple chat server. Inspired by Campfire.
 */

var util = require('util'),
    connect = require('connect'),
    http = require('http'),
    redis = require('redis'),
    PORT = 3000;

// Connect to Redis
var redisClient = redis.createClient();
redisClient.on('error', function(err) {
    console.log("Couldn't connect to redis: " + err);
    process.exit(1);
});

// Create our server
var app = connect();
var publicDir = __dirname + '/public';

// Configure express
app.use(connect.cookieParser('malabar'));
app.use(connect.session());
app.use(connect.static(publicDir));

// Ok, let's listen on port PORT
console.log("Starting server on port " + PORT);
var server = http.createServer(app).listen(PORT);

// Initialize socket.io subsystem
require('./lib/com_hub')(server, redisClient);
<|endoftext|>


<commit_before>exports.BattleFormatsData = {
	gengarmega: {
		viableMoves: {"shadowball":1,"sludgebomb":1,"focusblast":1,"thunderbolt":1,"substitute":1,"disable":1,"painsplit":1,"hypnosis":1,"gigadrain":1},
		viableDoublesMoves: {"shadowball":1,"sludgebomb":1,"focusblast":1,"thunderbolt":1,"substitute":1,"disable":1,"taunt":1,"hypnosis":1,"gigadrain":1,"trick":1,"dazzlinggleam":1,"protect":1},
		tier: "Uber"
	},
	lucariomega: {
		viableMoves: {"swordsdance":1,"closecombat":1,"crunch":1,"extremespeed":1,"icepunch":1,"bulletpunch":1,"nastyplot":1,"aurasphere":1,"darkpulse":1,"vacuumwave":1,"flashcannon":1},
		tier: "Uber"
	}
};
<commit_msg>Update formats-data.js<commit_after>exports.BattleFormatsData = {
	gengarmega: {
		randomBattleMoves: {"shadowball":1,"sludgebomb":1,"focusblast":1,"thunderbolt":1,"substitute":1,"disable":1,"painsplit":1,"hypnosis":1,"gigadrain":1},
		tier: "Uber"
	},
	lucariomega: {
		randomBattleMoves: {"swordsdance":1,"closecombat":1,"crunch":1,"extremespeed":1,"icepunch":1,"bulletpunch":1,"nastyplot":1,"aurasphere":1,"darkpulse":1,"vacuumwave":1,"flashcannon":1},
		tier: "Uber"
	}
};
<|endoftext|>


<commit_before><commit_msg>added server file<commit_after>'use strict';

const express = require('express');
const cors = require('cors');
const dotenv = require('dotenv');
const morgan = require('morgan');
const mongoose = require('mongoose');
const Promise = require('bluebird');
const debug = require('debug')('ccb:server');

const PORT = process.env.PORT;
const app = express();

app.use(cors());
app.use(morgan('dev'));

app.listen(PORT, () => {
  debug(`server up at port: ${PORT}`);
});
<|endoftext|>


<commit_before>import constants from '../constants';
import ApiFetch from '../../../utils/fetch/ApiFetch';

/**
 * Retrieve genres of interest show
 *
 * **Dispatch**: `FETCH_SHOW_INTEREST_GENRES`
 *
 * @alias module:Shows.doFetchShowInterestGenres
 * @category actions
 *
 * @example
 * BetaSeries.getAction('shows', 'doFetchShowInterestGenres')({
 *   showId: 118,
 *   country: 'fr',
 *   platforms: 1,34,
 * });
 *
 * @param {Object}  [obj]           Accept the following:
 * @param {Number}  [obj.showId]    Show ID
 * @param {String}  [obj.country]   Country code
 * @param {Number}  [obj.platforms] Platforms ids
 *
 * @returns {Promise}
 */
const doFetchShowInterestGenres = ({ showId, country, platforms, ...props }) =>
  dispatch =>
    ApiFetch.get('shows/interests_genres', {
      id: showId,
      country,
      platforms,
      ...props
    }).then(response =>
      dispatch({
        type: constants.FETCH_SHOW_INTEREST_GENRES,
        payload: {
          showId,
          country,
          platforms,
          ...props,
          genres: response.genres || {}
        }
      }));

export default doFetchShowInterestGenres;
<commit_msg>add total found in response for interest_genres API<commit_after>import constants from '../constants';
import ApiFetch from '../../../utils/fetch/ApiFetch';

/**
 * Retrieve genres of interest show
 *
 * **Dispatch**: `FETCH_SHOW_INTEREST_GENRES`
 *
 * @alias module:Shows.doFetchShowInterestGenres
 * @category actions
 *
 * @example
 * BetaSeries.getAction('shows', 'doFetchShowInterestGenres')({
 *   showId: 118,
 *   country: 'fr',
 *   platforms: 1,34,
 * });
 *
 * @param {Object}  [obj]           Accept the following:
 * @param {Number}  [obj.showId]    Show ID
 * @param {String}  [obj.country]   Country code
 * @param {Number}  [obj.platforms] Platforms ids
 *
 * @returns {Promise}
 */
const doFetchShowInterestGenres = ({ showId, country, platforms, ...props }) =>
  dispatch =>
    ApiFetch.get('shows/interests_genres', {
      id: showId,
      country,
      platforms,
      ...props
    }).then(response =>
      dispatch({
        type: constants.FETCH_SHOW_INTEREST_GENRES,
        payload: {
          showId,
          country,
          platforms,
          ...props,
          totalFound: response.totalFound || 0,
          genres: response.genres || {}
        }
      }));

export default doFetchShowInterestGenres;
<|endoftext|>