<commit_before>import React from 'react';
import { ButtonBase, Card, Typography, withStyles } from '@material-ui/core';

import { Source, Time, SubheadingItem } from 'components/common';
import Tags from './Tags';

const styles = theme => ({
  root: {
    display: 'flex',
    flexFlow: 'column nowrap',
    height: '150px',
    maxWidth: '300px',
  },
  button: {
    display: 'block',
    flex: 1,
    overflow: 'hidden',
    textAlign: 'initial',
    verticalAlign: 'top',
    position: 'relative',
    marginBottom: '16px',
  },
  wrapper: {
    top: 0,
    left: 0,
    position: 'absolute',
    width: '100%',
    height: '100%',
    padding: '16px 24px 0 24px',
  },
  title: {
    marginBottom: '16px',
  },
  oneLine: {
    overflow: 'hidden',
    textOverflow: 'ellipsis',
    whiteSpace: 'nowrap',
    width: '100%',
  },
});

ListItem.defaultProps = {
  recipe: {},
};

function ListItem({ classes, recipe, onClick }) {
  const { cook_time, notes, prep_time, preparation, tags, title } = recipe;

  if (!recipe) {
    return null;
  }

  return (
    <Card className={classes.root}>
      <ButtonBase className={classes.button} onClick={onClick}>

            <Typography variant={'body2'} className={classes.oneLine}>
              {title}
            </Typography>
            <Typography variant={'caption'} className={classes.oneLine}>
              <Source recipe={recipe} />
            </Typography>
            <Typography variant={'caption'} className={classes.oneLine}>
              {prep_time && (
                <SubheadingItem halfMargin label={'Prep'}>
                  <Time time={prep_time} />
                </SubheadingItem>
              )}
              {cook_time && (
                <SubheadingItem halfMargin label={'Cook'}>
                  <Time time={cook_time} />
                </SubheadingItem>
              )}
              {prep_time && (
                <SubheadingItem halfMargin label={'Prep'}>
                  <Time time={prep_time} />
                </SubheadingItem>
              )}
              {cook_time && (
                <SubheadingItem halfMargin label={'Cook'}>
                  <Time time={cook_time} />
                </SubheadingItem>
              )}
            </Typography>
            <Typography variant={'caption'} className={classes.oneLine}>
              <Tags {...{ tags }} />
            </Typography>

          <Typography variant={'body1'}>
            {notes || (preparation && preparation.join('\n'))}
          </Typography>

      </ButtonBase>
    </Card>
  );
}

export default withStyles(styles)(ListItem);
<commit_msg>Remove duplice cook and prep times in List<commit_after>import React from 'react';
import { ButtonBase, Card, Typography, withStyles } from '@material-ui/core';

import { Source, Time, SubheadingItem } from 'components/common';
import Tags from './Tags';

const styles = theme => ({
  root: {
    display: 'flex',
    flexFlow: 'column nowrap',
    height: '150px',
    maxWidth: '300px',
  },
  button: {
    display: 'block',
    flex: 1,
    overflow: 'hidden',
    textAlign: 'initial',
    verticalAlign: 'top',
    position: 'relative',
    marginBottom: '16px',
  },
  wrapper: {
    top: 0,
    left: 0,
    position: 'absolute',
    width: '100%',
    height: '100%',
    padding: '16px 24px 0 24px',
  },
  title: {
    marginBottom: '16px',
  },
  oneLine: {
    overflow: 'hidden',
    textOverflow: 'ellipsis',
    whiteSpace: 'nowrap',
    width: '100%',
  },
});

ListItem.defaultProps = {
  recipe: {},
};

function ListItem({ classes, recipe, onClick }) {
  const { cook_time, notes, prep_time, preparation, tags, title } = recipe;

  if (!recipe) {
    return null;
  }

  return (
    <Card className={classes.root}>
      <ButtonBase className={classes.button} onClick={onClick}>

            <Typography variant={'body2'} className={classes.oneLine}>
              {title}
            </Typography>
            <Typography variant={'caption'} className={classes.oneLine}>
              <Source recipe={recipe} />
            </Typography>
            <Typography variant={'caption'} className={classes.oneLine}>
              {prep_time && (
                <SubheadingItem halfMargin label={'Prep'}>
                  <Time time={prep_time} />
                </SubheadingItem>
              )}
              {cook_time && (
                <SubheadingItem halfMargin label={'Cook'}>
                  <Time time={cook_time} />
                </SubheadingItem>
              )}
            </Typography>
            <Typography variant={'caption'} className={classes.oneLine}>
              <Tags {...{ tags }} />
            </Typography>

          <Typography variant={'body1'}>
            {notes || (preparation && preparation.join('\n'))}
          </Typography>

      </ButtonBase>
    </Card>
  );
}

export default withStyles(styles)(ListItem);
<|endoftext|>


<commit_before>'use strict';

//  ---------------------------------------------------------------------------

const ccxt = require ('ccxt');
const { ExchangeError } = require ('ccxt/js/base/errors');

//  ---------------------------------------------------------------------------

module.exports = class binance extends ccxt.binance {
    describe () {
        return this.deepExtend (super.describe (), {
            'has': {
                'watchOrderBook': true,
            },
            'urls': {
                'api': {
                    'ws': 'wss://stream.binance.com:9443/ws',
                },
            },
            'options': {
                'watchOrderBookRate': 100, // get updates every 100ms or 1000ms
            },
        });
    }

    async loadMarkets (reload = false, params = {}) {
        const markets = await super.loadMarkets (reload, params);
        let marketsByLowercaseId = this.safeValue (this.options, 'marketsByLowercaseId');
        if ((marketsByLowercaseId === undefined) || reload) {
            marketsByLowercaseId = {};
            for (let i = 0; i < this.symbols.length; i++) {
                const symbol = this.symbols[i];
                const market = this.markets[symbol];
                const lowercaseId = this.safeStringLower (market, 'id');
                market['lowercaseId'] = lowercaseId;
                this.markets_by_id[market['id']] = market;
                this.markets[symbol] = market;
                marketsByLowercaseId[lowercaseId] = this.markets[symbol];
            }
            this.options['marketsByLowercaseId'] = marketsByLowercaseId;
        }
        return markets;
    }

    async watchOrderBook (symbol, limit = undefined, params = {}) {
        //
        // https://github.com/binance-exchange/binance-official-api-docs/blob/master/web-socket-streams.md#partial-book-depth-streams
        //
        // <symbol>@depth<levels>@100ms or <symbol>@depth<levels> (1000ms)
        // valid <levels> are 5, 10, or 20
        //
        if (limit !== undefined) {
            if ((limit !== 5) && (limit !== 10) && (limit !== 20)) {
                throw new ExchangeError (this.id + ' watchOrderBook limit argument must be undefined, 5, 10 or 20');
            }
        }
        await this.loadMarkets ();
        const market = this.market (symbol);
        //
        // https://github.com/binance-exchange/binance-official-api-docs/blob/master/web-socket-streams.md#how-to-manage-a-local-order-book-correctly
        //
        // 1. Open a stream to wss://stream.binance.com:9443/ws/bnbbtc@depth.
        // 2. Buffer the events you receive from the stream.
        // 3. Get a depth snapshot from https://www.binance.com/api/v1/depth?symbol=BNBBTC&limit=1000 .
        // 4. Drop any event where u is <= lastUpdateId in the snapshot.
        // 5. The first processed event should have U <= lastUpdateId+1 AND u >= lastUpdateId+1.
        // 6. While listening to the stream, each new event's U should be equal to the previous event's u+1.
        // 7. The data in each event is the absolute quantity for a price level.
        // 8. If the quantity is 0, remove the price level.
        // 9. Receiving an event that removes a price level that is not in your local order book can happen and is normal.
        //
        const name = 'depth';
        const messageHash = market['lowercaseId'] + '@' + name;
        const url = this.urls['api']['ws']; // + '/' + messageHash;
        const requestId = this.nonce ();
        const watchOrderBookRate = this.safeString (this.options, 'watchOrderBookRate', '100');
        const request = {
            'method': 'SUBSCRIBE',
            'params': [
                messageHash + '@' + watchOrderBookRate + 'ms',
            ],
            'id': requestId,
        };
        const subscription = {
            'id': requestId.toString (),
            'messageHash': messageHash,
            'name': name,
            'symbol': symbol,
            'method': this.handleOrderBookSubscription,
        };
        const message = this.extend (request, params);
        // 1. Open a stream to wss://stream.binance.com:9443/ws/bnbbtc@depth.
        const future = this.watch (url, messageHash, message, messageHash, subscription);
        return await this.after (future, this.limitOrderBook, symbol, limit, params);
    }

    limitOrderBook (orderbook, symbol, limit = undefined, params = {}) {
        return orderbook.limit (limit);
    }

    async fetchOrderBookSnapshot (client, message, subscription) {
        const symbol = this.safeString (subscription, 'symbol');
        const messageHash = this.safeString (subscription, 'messageHash');
        // 3. Get a depth snapshot from https://www.binance.com/api/v1/depth?symbol=BNBBTC&limit=1000 .
        // todo: this is a synch blocking call in ccxt.php - make it async
        const snapshot = await this.fetchOrderBook (symbol);
        const orderbook = this.orderbooks[symbol];
        orderbook.reset (snapshot);
        // unroll the accumulated deltas
        const messages = orderbook.cache;
        for (let i = 0; i < messages.length; i++) {
            const message = messages[i];
            this.handleOrderBookMessage (client, message, orderbook);
        }
        this.orderbooks[symbol] = orderbook;
        client.resolve (orderbook, messageHash);
    }

    handleDelta (bookside, delta) {
        const price = this.safeFloat (delta, 0);
        const amount = this.safeFloat (delta, 1);
        bookside.store (price, amount);
    }

    handleDeltas (bookside, deltas) {
        for (let i = 0; i < deltas.length; i++) {
            this.handleDelta (bookside, deltas[i]);
        }
    }

    handleOrderBookMessage (client, message, orderbook) {
        const u = this.safeInteger2 (message, 'u', 'lastUpdateId');
        // merge accumulated deltas
        // 4. Drop any event where u is <= lastUpdateId in the snapshot.
        if (u > orderbook['nonce']) {
            const U = this.safeInteger (message, 'U');
            // 5. The first processed event should have U <= lastUpdateId+1 AND u >= lastUpdateId+1.
            if ((U !== undefined) && ((U - 1) > orderbook['nonce'])) {
                throw new ExchangeError (this.id + ' handleOrderBook received an out-of-order nonce');
            }
            this.handleDeltas (orderbook['asks'], this.safeValue (message, 'a', []));
            this.handleDeltas (orderbook['bids'], this.safeValue (message, 'b', []));
            orderbook['nonce'] = u;
            const timestamp = this.safeInteger (message, 'E');
            orderbook['timestamp'] = timestamp;
            orderbook['datetime'] = this.iso8601 (timestamp);
        }
        return orderbook;
    }

    handleOrderBook (client, message) {
        //
        // initial snapshot is fetched with ccxt's fetchOrderBook
        // the feed does not include a snapshot, just the deltas
        //
        //     {
        //         "e": "depthUpdate", // Event type
        //         "E": 1577554482280, // Event time
        //         "s": "BNBBTC", // Symbol
        //         "U": 157, // First update ID in event
        //         "u": 160, // Final update ID in event
        //         "b": [ // bids
        //             [ "0.0024", "10" ], // price, size
        //         ],
        //         "a": [ // asks
        //             [ "0.0026", "100" ], // price, size
        //         ]
        //     }
        //
        const marketId = this.safeString (message, 's');
        let market = undefined;
        let symbol = undefined;
        if (marketId !== undefined) {
            if (marketId in this.markets_by_id) {
                market = this.markets_by_id[marketId];
                symbol = market['symbol'];
            }
        }
        const name = 'depth';
        const messageHash = market['lowercaseId'] + '@' + name;
        const orderbook = this.orderbooks[symbol];
        if (orderbook['nonce'] !== undefined) {
            // 5. The first processed event should have U <= lastUpdateId+1 AND u >= lastUpdateId+1.
            // 6. While listening to the stream, each new event's U should be equal to the previous event's u+1.
            this.handleOrderBookMessage (client, message, orderbook);
            client.resolve (orderbook, messageHash);
        } else {
            // 2. Buffer the events you receive from the stream.
            orderbook.cache.push (message);
        }
    }

    signMessage (client, messageHash, message, params = {}) {
        // todo: binance signMessage not implemented yet
        return message;
    }

    handleOrderBookSubscription (client, message, subscription) {
        const symbol = this.safeString (subscription, 'symbol');
        if (symbol in this.orderbooks) {
            delete this.orderbooks[symbol];
        }
        this.orderbooks[symbol] = this.limitedOrderBook ();
        // fetch the snapshot in a separate async call
        this.spawn (this.fetchOrderBookSnapshot, client, message, subscription);
    }

    handleSubscriptionStatus (client, message) {
        //
        //     {
        //         "result": null,
        //         "id": 1574649734450
        //     }
        //
        const requestId = this.safeString (message, 'id');
        const subscriptionsByRequestId = this.indexBy (client.subscriptions, 'id');
        const subscription = this.safeValue (subscriptionsByRequestId, requestId, {});
        const method = this.safeValue (subscription, 'method');
        if (method !== undefined) {
            this.call (method, client, message, subscription);
        }
        return message;
    }

    handleMessage (client, message) {
        const methods = {
            'depthUpdate': this.handleOrderBook,
        };
        const event = this.safeString (message, 'e');
        const method = this.safeValue (methods, event);
        if (method === undefined) {
            const requestId = this.safeString (message, 'id');
            if (requestId !== undefined) {
                return this.handleSubscriptionStatus (client, message);
            }
            return message;
        } else {
            return this.call (method, client, message);
        }
    }
};

<commit_msg>binance minor simplifications<commit_after>'use strict';

//  ---------------------------------------------------------------------------

const ccxt = require ('ccxt');
const { ExchangeError } = require ('ccxt/js/base/errors');

//  ---------------------------------------------------------------------------

module.exports = class binance extends ccxt.binance {
    describe () {
        return this.deepExtend (super.describe (), {
            'has': {
                'watchOrderBook': true,
            },
            'urls': {
                'api': {
                    'ws': 'wss://stream.binance.com:9443/ws',
                },
            },
            'options': {
                'watchOrderBookRate': 100, // get updates every 100ms or 1000ms
            },
        });
    }

    async loadMarkets (reload = false, params = {}) {
        const markets = await super.loadMarkets (reload, params);
        let marketsByLowercaseId = this.safeValue (this.options, 'marketsByLowercaseId');
        if ((marketsByLowercaseId === undefined) || reload) {
            marketsByLowercaseId = {};
            for (let i = 0; i < this.symbols.length; i++) {
                const symbol = this.symbols[i];
                const market = this.markets[symbol];
                const lowercaseId = this.safeStringLower (market, 'id');
                market['lowercaseId'] = lowercaseId;
                this.markets_by_id[market['id']] = market;
                this.markets[symbol] = market;
                marketsByLowercaseId[lowercaseId] = this.markets[symbol];
            }
            this.options['marketsByLowercaseId'] = marketsByLowercaseId;
        }
        return markets;
    }

    async watchOrderBook (symbol, limit = undefined, params = {}) {
        //
        // https://github.com/binance-exchange/binance-official-api-docs/blob/master/web-socket-streams.md#partial-book-depth-streams
        //
        // <symbol>@depth<levels>@100ms or <symbol>@depth<levels> (1000ms)
        // valid <levels> are 5, 10, or 20
        //
        if (limit !== undefined) {
            if ((limit !== 5) && (limit !== 10) && (limit !== 20)) {
                throw new ExchangeError (this.id + ' watchOrderBook limit argument must be undefined, 5, 10 or 20');
            }
        }
        await this.loadMarkets ();
        const market = this.market (symbol);
        //
        // https://github.com/binance-exchange/binance-official-api-docs/blob/master/web-socket-streams.md#how-to-manage-a-local-order-book-correctly
        //
        // 1. Open a stream to wss://stream.binance.com:9443/ws/bnbbtc@depth.
        // 2. Buffer the events you receive from the stream.
        // 3. Get a depth snapshot from https://www.binance.com/api/v1/depth?symbol=BNBBTC&limit=1000 .
        // 4. Drop any event where u is <= lastUpdateId in the snapshot.
        // 5. The first processed event should have U <= lastUpdateId+1 AND u >= lastUpdateId+1.
        // 6. While listening to the stream, each new event's U should be equal to the previous event's u+1.
        // 7. The data in each event is the absolute quantity for a price level.
        // 8. If the quantity is 0, remove the price level.
        // 9. Receiving an event that removes a price level that is not in your local order book can happen and is normal.
        //
        const name = 'depth';
        const messageHash = market['lowercaseId'] + '@' + name;
        const url = this.urls['api']['ws']; // + '/' + messageHash;
        const requestId = this.nonce ();
        const watchOrderBookRate = this.safeString (this.options, 'watchOrderBookRate', '100');
        const request = {
            'method': 'SUBSCRIBE',
            'params': [
                messageHash + '@' + watchOrderBookRate + 'ms',
            ],
            'id': requestId,
        };
        const subscription = {
            'id': requestId.toString (),
            'messageHash': messageHash,
            'name': name,
            'symbol': symbol,
            'method': this.handleOrderBookSubscription,
        };
        const message = this.extend (request, params);
        // 1. Open a stream to wss://stream.binance.com:9443/ws/bnbbtc@depth.
        const future = this.watch (url, messageHash, message, messageHash, subscription);
        return await this.after (future, this.limitOrderBook, symbol, limit, params);
    }

    limitOrderBook (orderbook, symbol, limit = undefined, params = {}) {
        return orderbook.limit (limit);
    }

    async fetchOrderBookSnapshot (client, message, subscription) {
        const symbol = this.safeString (subscription, 'symbol');
        const messageHash = this.safeString (subscription, 'messageHash');
        // 3. Get a depth snapshot from https://www.binance.com/api/v1/depth?symbol=BNBBTC&limit=1000 .
        // todo: this is a synch blocking call in ccxt.php - make it async
        const snapshot = await this.fetchOrderBook (symbol);
        const orderbook = this.orderbooks[symbol];
        orderbook.reset (snapshot);
        // unroll the accumulated deltas
        const messages = orderbook.cache;
        for (let i = 0; i < messages.length; i++) {
            const message = messages[i];
            this.handleOrderBookMessage (client, message, orderbook);
        }
        this.orderbooks[symbol] = orderbook;
        client.resolve (orderbook, messageHash);
    }

    handleDelta (bookside, delta) {
        const price = this.safeFloat (delta, 0);
        const amount = this.safeFloat (delta, 1);
        bookside.store (price, amount);
    }

    handleDeltas (bookside, deltas) {
        for (let i = 0; i < deltas.length; i++) {
            this.handleDelta (bookside, deltas[i]);
        }
    }

    handleOrderBookMessage (client, message, orderbook) {
        const u = this.safeInteger2 (message, 'u', 'lastUpdateId');
        // merge accumulated deltas
        // 4. Drop any event where u is <= lastUpdateId in the snapshot.
        if (u > orderbook['nonce']) {
            const U = this.safeInteger (message, 'U');
            // 5. The first processed event should have U <= lastUpdateId+1 AND u >= lastUpdateId+1.
            if ((U !== undefined) && ((U - 1) > orderbook['nonce'])) {
                throw new ExchangeError (this.id + ' handleOrderBook received an out-of-order nonce');
            }
            this.handleDeltas (orderbook['asks'], this.safeValue (message, 'a', []));
            this.handleDeltas (orderbook['bids'], this.safeValue (message, 'b', []));
            orderbook['nonce'] = u;
            const timestamp = this.safeInteger (message, 'E');
            orderbook['timestamp'] = timestamp;
            orderbook['datetime'] = this.iso8601 (timestamp);
        }
        return orderbook;
    }

    handleOrderBook (client, message) {
        //
        // initial snapshot is fetched with ccxt's fetchOrderBook
        // the feed does not include a snapshot, just the deltas
        //
        //     {
        //         "e": "depthUpdate", // Event type
        //         "E": 1577554482280, // Event time
        //         "s": "BNBBTC", // Symbol
        //         "U": 157, // First update ID in event
        //         "u": 160, // Final update ID in event
        //         "b": [ // bids
        //             [ "0.0024", "10" ], // price, size
        //         ],
        //         "a": [ // asks
        //             [ "0.0026", "100" ], // price, size
        //         ]
        //     }
        //
        const marketId = this.safeString (message, 's');
        let market = undefined;
        let symbol = undefined;
        if (marketId !== undefined) {
            if (marketId in this.markets_by_id) {
                market = this.markets_by_id[marketId];
                symbol = market['symbol'];
            }
        }
        const name = 'depth';
        const messageHash = market['lowercaseId'] + '@' + name;
        const orderbook = this.orderbooks[symbol];
        if (orderbook['nonce'] !== undefined) {
            // 5. The first processed event should have U <= lastUpdateId+1 AND u >= lastUpdateId+1.
            // 6. While listening to the stream, each new event's U should be equal to the previous event's u+1.
            this.handleOrderBookMessage (client, message, orderbook);
            client.resolve (orderbook, messageHash);
        } else {
            // 2. Buffer the events you receive from the stream.
            orderbook.cache.push (message);
        }
    }

    signMessage (client, messageHash, message, params = {}) {
        // todo: binance signMessage not implemented yet
        return message;
    }

    handleOrderBookSubscription (client, message, subscription) {
        const symbol = this.safeString (subscription, 'symbol');
        if (symbol in this.orderbooks) {
            delete this.orderbooks[symbol];
        }
        this.orderbooks[symbol] = this.limitedOrderBook ();
        // fetch the snapshot in a separate async call
        this.spawn (this.fetchOrderBookSnapshot, client, message, subscription);
    }

    handleSubscriptionStatus (client, message) {
        //
        //     {
        //         "result": null,
        //         "id": 1574649734450
        //     }
        //
        const id = this.safeString (message, 'id');
        const subscriptionsById = this.indexBy (client.subscriptions, 'id');
        const subscription = this.safeValue (subscriptionsById, id, {});
        const method = this.safeValue (subscription, 'method');
        if (method !== undefined) {
            this.call (method, client, message, subscription);
        }
        return message;
    }

    handleMessage (client, message) {
        const methods = {
            'depthUpdate': this.handleOrderBook,
        };
        const event = this.safeString (message, 'e');
        const method = this.safeValue (methods, event);
        if (method === undefined) {
            const requestId = this.safeString (message, 'id');
            if (requestId !== undefined) {
                return this.handleSubscriptionStatus (client, message);
            }
            return message;
        } else {
            return this.call (method, client, message);
        }
    }
};

<|endoftext|>


<commit_before>// Definition of the links collection

import { Mongo } from 'meteor/mongo';

const Links = new Mongo.Collection('links');

export { Links };
<commit_msg>more concise export<commit_after>// Definition of the links collection

import { Mongo } from 'meteor/mongo';

export const Links = new Mongo.Collection('links');
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright Google Inc. All Rights Reserved.
 *
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://angular.io/license
 */
const Package = require('dgeni').Package;

const basePackage = require('../angular-base-package');
const typeScriptPackage = require('dgeni-packages/typescript');
const { API_SOURCE_PATH, API_TEMPLATES_PATH, requireFolder } = require('../config');

module.exports = new Package('angular-api', [basePackage, typeScriptPackage])

  // Register the processors
  .processor(require('./processors/migrateLegacyJSDocTags'))
  .processor(require('./processors/splitDescription'))
  .processor(require('./processors/convertPrivateClassesToInterfaces'))
  .processor(require('./processors/generateApiListDoc'))
  .processor(require('./processors/addNotYetDocumentedProperty'))
  .processor(require('./processors/mergeDecoratorDocs'))
  .processor(require('./processors/extractDecoratedClasses'))
  .processor(require('./processors/extractPipeParams'))
  .processor(require('./processors/matchUpDirectiveDecorators'))
  .processor(require('./processors/addMetadataAliases'))
  .processor(require('./processors/computeApiBreadCrumbs'))
  .processor(require('./processors/filterContainedDocs'))
  .processor(require('./processors/processClassLikeMembers'))
  .processor(require('./processors/markBarredODocsAsPrivate'))
  .processor(require('./processors/filterPrivateDocs'))
  .processor(require('./processors/computeSearchTitle'))
  .processor(require('./processors/simplifyMemberAnchors'))
  .processor(require('./processors/computeStability'))

  /**
   * These are the API doc types that will be rendered to actual files.
   * This is a super set of the exported docs, since we convert some classes to
   * more Angular specific API types, such as decorators and directives.
   */
  .factory(function API_DOC_TYPES_TO_RENDER(EXPORT_DOC_TYPES) {
    return EXPORT_DOC_TYPES.concat(['decorator', 'directive', 'pipe', 'module']);
  })

  /**
   * These are the doc types that are API docs, including ones that will be merged into container docs,
   * such as members and overloads.
   */
  .factory(function API_DOC_TYPES(API_DOC_TYPES_TO_RENDER) {
    return API_DOC_TYPES_TO_RENDER.concat(['member', 'function-overload']);
  })

  // Where do we get the source files?
  .config(function(readTypeScriptModules, readFilesProcessor, collectExamples, tsParser) {

    // Tell TypeScript how to load modules that start with with `@angular`
    tsParser.options.paths = { '@angular/*': [API_SOURCE_PATH + '/*'] };
    tsParser.options.baseUrl = '.';

    // API files are typescript
    readTypeScriptModules.basePath = API_SOURCE_PATH;
    readTypeScriptModules.ignoreExportsMatching = [/^[_ɵ]|^VERSION$/];
    readTypeScriptModules.hidePrivateMembers = true;

    // NOTE: This list shold be in sync with tools/public_api_guard/BUILD.bazel
    readTypeScriptModules.sourceFiles = [
      'animations/index.ts',
      'animations/browser/index.ts',
      'animations/browser/testing/index.ts',
      'common/http/index.ts',
      'common/http/testing/index.ts',
      'common/index.ts',
      'common/testing/index.ts',
      'core/index.ts',
      'core/testing/index.ts',
      'elements/index.ts',
      'forms/index.ts',
      'http/index.ts',
      'http/testing/index.ts',
      'platform-browser/index.ts',
      'platform-browser/animations/index.ts',
      'platform-browser/testing/index.ts',
      'platform-browser-dynamic/index.ts',
      'platform-browser-dynamic/testing/index.ts',
      'platform-server/index.ts',
      'platform-server/testing/index.ts',
      'platform-webworker/index.ts',
      'platform-webworker-dynamic/index.ts',
      'router/index.ts',
      'router/testing/index.ts',
      'router/upgrade/index.ts',
      'service-worker/index.ts',
      'upgrade/index.ts',
      'upgrade/static/index.ts',
    ];

    // API Examples
    readFilesProcessor.sourceFiles = [
      {
        basePath: API_SOURCE_PATH,
        include: API_SOURCE_PATH + '/examples/**/*',
        fileReader: 'exampleFileReader'
      }
    ];
    collectExamples.exampleFolders.push('examples');
  })

  // Configure jsdoc-style tag parsing
  .config(function(parseTagsProcessor, getInjectables) {
    // Load up all the tag definitions in the tag-defs folder
    parseTagsProcessor.tagDefinitions =
        parseTagsProcessor.tagDefinitions.concat(getInjectables(requireFolder(__dirname, './tag-defs')));
  })

  .config(function(computeStability, splitDescription, addNotYetDocumentedProperty, EXPORT_DOC_TYPES, API_DOC_TYPES) {
    computeStability.docTypes = EXPORT_DOC_TYPES;
    // Only split the description on the API docs
    splitDescription.docTypes = API_DOC_TYPES;
    addNotYetDocumentedProperty.docTypes = API_DOC_TYPES;
  })

  .config(function(computePathsProcessor, EXPORT_DOC_TYPES, generateApiListDoc) {

    const API_SEGMENT = 'api';

    generateApiListDoc.outputFolder = API_SEGMENT;

    computePathsProcessor.pathTemplates.push({
      docTypes: ['module'],
      getPath: function computeModulePath(doc) {
        doc.moduleFolder = `${API_SEGMENT}/${doc.id.replace(/\/index$/, '')}`;
        return doc.moduleFolder;
      },
      outputPathTemplate: '${moduleFolder}.json'
    });
    computePathsProcessor.pathTemplates.push({
      docTypes: EXPORT_DOC_TYPES.concat(['decorator', 'directive', 'pipe']),
      pathTemplate: '${moduleDoc.moduleFolder}/${name}',
      outputPathTemplate: '${moduleDoc.moduleFolder}/${name}.json',
    });
  })

  .config(function(templateFinder) {
    // Where to find the templates for the API doc rendering
    templateFinder.templateFolders.unshift(API_TEMPLATES_PATH);
  })

  .config(function(convertToJsonProcessor, postProcessHtml, API_DOC_TYPES_TO_RENDER, API_DOC_TYPES, autoLinkCode) {
    convertToJsonProcessor.docTypes = convertToJsonProcessor.docTypes.concat(API_DOC_TYPES_TO_RENDER);
    postProcessHtml.docTypes = convertToJsonProcessor.docTypes.concat(API_DOC_TYPES_TO_RENDER);
    autoLinkCode.docTypes = API_DOC_TYPES;
    autoLinkCode.codeElements = ['code', 'code-example', 'code-pane'];
  });
<commit_msg>build(aio): implement rules to prevent headings in content (#22759)<commit_after>/**
 * @license
 * Copyright Google Inc. All Rights Reserved.
 *
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://angular.io/license
 */
const Package = require('dgeni').Package;

const basePackage = require('../angular-base-package');
const typeScriptPackage = require('dgeni-packages/typescript');
const { API_SOURCE_PATH, API_TEMPLATES_PATH, requireFolder } = require('../config');

module.exports = new Package('angular-api', [basePackage, typeScriptPackage])

  // Register the processors
  .processor(require('./processors/migrateLegacyJSDocTags'))
  .processor(require('./processors/splitDescription'))
  .processor(require('./processors/convertPrivateClassesToInterfaces'))
  .processor(require('./processors/generateApiListDoc'))
  .processor(require('./processors/addNotYetDocumentedProperty'))
  .processor(require('./processors/mergeDecoratorDocs'))
  .processor(require('./processors/extractDecoratedClasses'))
  .processor(require('./processors/extractPipeParams'))
  .processor(require('./processors/matchUpDirectiveDecorators'))
  .processor(require('./processors/addMetadataAliases'))
  .processor(require('./processors/computeApiBreadCrumbs'))
  .processor(require('./processors/filterContainedDocs'))
  .processor(require('./processors/processClassLikeMembers'))
  .processor(require('./processors/markBarredODocsAsPrivate'))
  .processor(require('./processors/filterPrivateDocs'))
  .processor(require('./processors/computeSearchTitle'))
  .processor(require('./processors/simplifyMemberAnchors'))
  .processor(require('./processors/computeStability'))

  /**
   * These are the API doc types that will be rendered to actual files.
   * This is a super set of the exported docs, since we convert some classes to
   * more Angular specific API types, such as decorators and directives.
   */
  .factory(function API_DOC_TYPES_TO_RENDER(EXPORT_DOC_TYPES) {
    return EXPORT_DOC_TYPES.concat(['decorator', 'directive', 'pipe', 'module']);
  })

  /**
   * These are the doc types that are API docs, including ones that will be merged into container docs,
   * such as members and overloads.
   */
  .factory(function API_DOC_TYPES(API_DOC_TYPES_TO_RENDER) {
    return API_DOC_TYPES_TO_RENDER.concat(['member', 'function-overload']);
  })

  // Where do we get the source files?
  .config(function(readTypeScriptModules, readFilesProcessor, collectExamples, tsParser) {

    // Tell TypeScript how to load modules that start with with `@angular`
    tsParser.options.paths = { '@angular/*': [API_SOURCE_PATH + '/*'] };
    tsParser.options.baseUrl = '.';

    // API files are typescript
    readTypeScriptModules.basePath = API_SOURCE_PATH;
    readTypeScriptModules.ignoreExportsMatching = [/^[_ɵ]|^VERSION$/];
    readTypeScriptModules.hidePrivateMembers = true;

    // NOTE: This list shold be in sync with tools/public_api_guard/BUILD.bazel
    readTypeScriptModules.sourceFiles = [
      'animations/index.ts',
      'animations/browser/index.ts',
      'animations/browser/testing/index.ts',
      'common/http/index.ts',
      'common/http/testing/index.ts',
      'common/index.ts',
      'common/testing/index.ts',
      'core/index.ts',
      'core/testing/index.ts',
      'elements/index.ts',
      'forms/index.ts',
      'http/index.ts',
      'http/testing/index.ts',
      'platform-browser/index.ts',
      'platform-browser/animations/index.ts',
      'platform-browser/testing/index.ts',
      'platform-browser-dynamic/index.ts',
      'platform-browser-dynamic/testing/index.ts',
      'platform-server/index.ts',
      'platform-server/testing/index.ts',
      'platform-webworker/index.ts',
      'platform-webworker-dynamic/index.ts',
      'router/index.ts',
      'router/testing/index.ts',
      'router/upgrade/index.ts',
      'service-worker/index.ts',
      'upgrade/index.ts',
      'upgrade/static/index.ts',
    ];

    // API Examples
    readFilesProcessor.sourceFiles = [
      {
        basePath: API_SOURCE_PATH,
        include: API_SOURCE_PATH + '/examples/**/*',
        fileReader: 'exampleFileReader'
      }
    ];
    collectExamples.exampleFolders.push('examples');
  })

  // Configure jsdoc-style tag parsing
  .config(function(parseTagsProcessor, getInjectables) {
    // Load up all the tag definitions in the tag-defs folder
    parseTagsProcessor.tagDefinitions =
        parseTagsProcessor.tagDefinitions.concat(getInjectables(requireFolder(__dirname, './tag-defs')));
  })

  .config(function(computeStability, splitDescription, addNotYetDocumentedProperty, EXPORT_DOC_TYPES, API_DOC_TYPES) {
    computeStability.docTypes = EXPORT_DOC_TYPES;
    // Only split the description on the API docs
    splitDescription.docTypes = API_DOC_TYPES;
    addNotYetDocumentedProperty.docTypes = API_DOC_TYPES;
  })

  .config(function(checkContentRules, EXPORT_DOC_TYPES) {
    const createNoMarkdownHeadings = require('./content-rules/noMarkdownHeadings');
    const noMarkdownHeadings = createNoMarkdownHeadings();
    const allowOnlyLevel3Headings = createNoMarkdownHeadings(1, 2, '4,');
    const DOC_TYPES_TO_CHECK = EXPORT_DOC_TYPES.concat(['member', 'overload-info']);
    const PROPS_TO_CHECK = ['description', 'shortDescription'];

    DOC_TYPES_TO_CHECK.forEach(docType => {
      const ruleSet = checkContentRules.docTypeRules[docType] = checkContentRules.docTypeRules[docType] || {};
      PROPS_TO_CHECK.forEach(prop => {
        const rules = ruleSet[prop] = ruleSet[prop] || [];
        rules.push(noMarkdownHeadings);
      });
      const rules = ruleSet['usageNotes'] = ruleSet['usageNotes'] || [];
      rules.push(allowOnlyLevel3Headings);
    });
  })

  .config(function(computePathsProcessor, EXPORT_DOC_TYPES, generateApiListDoc) {

    const API_SEGMENT = 'api';

    generateApiListDoc.outputFolder = API_SEGMENT;

    computePathsProcessor.pathTemplates.push({
      docTypes: ['module'],
      getPath: function computeModulePath(doc) {
        doc.moduleFolder = `${API_SEGMENT}/${doc.id.replace(/\/index$/, '')}`;
        return doc.moduleFolder;
      },
      outputPathTemplate: '${moduleFolder}.json'
    });
    computePathsProcessor.pathTemplates.push({
      docTypes: EXPORT_DOC_TYPES.concat(['decorator', 'directive', 'pipe']),
      pathTemplate: '${moduleDoc.moduleFolder}/${name}',
      outputPathTemplate: '${moduleDoc.moduleFolder}/${name}.json',
    });
  })

  .config(function(templateFinder) {
    // Where to find the templates for the API doc rendering
    templateFinder.templateFolders.unshift(API_TEMPLATES_PATH);
  })

  .config(function(convertToJsonProcessor, postProcessHtml, API_DOC_TYPES_TO_RENDER, API_DOC_TYPES, autoLinkCode) {
    convertToJsonProcessor.docTypes = convertToJsonProcessor.docTypes.concat(API_DOC_TYPES_TO_RENDER);
    postProcessHtml.docTypes = convertToJsonProcessor.docTypes.concat(API_DOC_TYPES_TO_RENDER);
    autoLinkCode.docTypes = API_DOC_TYPES;
    autoLinkCode.codeElements = ['code', 'code-example', 'code-pane'];
  });
<|endoftext|>


<commit_before>var express = require('express')
var app = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);

var webpack = require('webpack');
var WebpackDevMiddleware = require('webpack-dev-middleware');
var config = require('./webpack.config');

var people = [];

server.listen(process.env.PORT)
console.log('Listening on port', process.env.PORT)

app.set('view engine', 'ejs');

if(process.env.NODE_ENV === 'development') {
  config.path = __dirname + '/scripts/';
  config.publicPath = '/scripts/';
  config.plugins = [
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin()
  ];
  config.devtool = 'eval';
  app.use(WebpackDevMiddleware(webpack(config), {
    publicPath: config.output.publicPath,
    hot: true,
    historyApiFallback: true
  }));
}

app.get('/', function(req, res) {
  res.render('index', {env: process.env.NODE_ENV})
});

app.use(express.static(__dirname + '/static'));

io.on('connection', function (socket) {
  socket.on('client:message', function(data) {
    console.log(data)
    socket.broadcast.emit('send:message', data)
  });
  socket.on('set:join', function(data) {
    people.push(data)
    socket.emit('client:join', people)
    socket.broadcast.emit('client:join', people)
  });
  socket.on('set:leave', function(data) {
    var index = people.indexOf(data);
    if(index > -1) people.splice(index, 1);
    socket.broadcast.emit('client:leave', people)
  });
});
<commit_msg>Fix heroku require<commit_after>var express = require('express')
var app = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);

var webpack = require('webpack');
var config = require('./webpack.config');

var people = [];

server.listen(process.env.PORT)
console.log('Listening on port', process.env.PORT)

app.set('view engine', 'ejs');

if(process.env.NODE_ENV === 'development') {
  var WebpackDevMiddleware = require('webpack-dev-middleware');
  config.path = __dirname + '/scripts/';
  config.publicPath = '/scripts/';
  config.plugins = [
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin()
  ];
  config.devtool = 'eval';
  app.use(WebpackDevMiddleware(webpack(config), {
    publicPath: config.output.publicPath,
    hot: true,
    historyApiFallback: true
  }));
}

app.get('/', function(req, res) {
  res.render('index', {env: process.env.NODE_ENV})
});

app.use(express.static(__dirname + '/static'));

io.on('connection', function (socket) {
  socket.on('client:message', function(data) {
    console.log(data)
    socket.broadcast.emit('send:message', data)
  });
  socket.on('set:join', function(data) {
    people.push(data)
    socket.emit('client:join', people)
    socket.broadcast.emit('client:join', people)
  });
  socket.on('set:leave', function(data) {
    var index = people.indexOf(data);
    if(index > -1) people.splice(index, 1);
    socket.broadcast.emit('client:leave', people)
  });
});
<|endoftext|>


<commit_before>define(
  'tinymce.themes.mobile.ui.ImagePicker',

  [
    'ephox.alloy.api.component.Memento',
    'ephox.alloy.api.ui.Button',
    'ephox.alloy.construct.EventHandler',
    'ephox.imagetools.api.BlobConversions',
    'ephox.katamari.api.Id',
    'ephox.katamari.api.Option',
    'tinymce.themes.mobile.style.Styles'
  ],

  function (Memento, Button, EventHandler, BlobConversions, Id, Option, Styles) {
    var addImage = function (editor, blob) {
      BlobConversions.blobToBase64(blob).then(function (base64) {
        editor.undoManager.transact(function () {
          var cache = editor.editorUpload.blobCache;
          var info = cache.create(
            Id.generate('mceu'), blob, base64
          );
          cache.add(info);
          var img = editor.dom.createHTML('img', {
            src: info.blobUri()
          });
          editor.insertContent(img);
        });
      });
    };

    var extractBlob = function (simulatedEvent) {
      var event = simulatedEvent.event();
      var files = event.raw().target.files || event.raw().dataTransfer.files;
      return Option.from(files[0]);
    };

    var sketch = function (editor) {
      var pickerDom = {
        tag: 'input',
        attributes: { accept: 'image/*', type: 'file', title: '' },
         // Visibility hidden so that it cannot be seen, and position absolute so that it doesn't
        // disrupt the layout
        styles: { visibility: 'hidden', position: 'absolute' }
      };

      var memPicker = Memento.record({
        dom: pickerDom,
        events: {
          click: EventHandler.nu({
            run: function (picker, simulatedEvent) {
              // Stop the event firing again at the button level
              simulatedEvent.cut();
            }
          }),
          change: EventHandler.nu({
            run: function (picker, simulatedEvent) {
              extractBlob(simulatedEvent).each(function (blob) {
                addImage(editor, blob);
              });
            }
          })
        }
      });

      return Button.sketch({
        dom: {
          tag: 'span',
          classes: [ Styles.resolve('toolbar-button'), Styles.resolve('icon-image'), Styles.resolve('icon') ]
        },
        components: [
          memPicker.asSpec()
        ],
        action: function (button) {
          var picker = memPicker.get(button);
          // Trigger a dom click for the file input
          picker.element().dom().click();
        }
      });
    };

    return {
      sketch: sketch
    };
  }
);
<commit_msg>TM-60: removing EventHandler<commit_after>define(
  'tinymce.themes.mobile.ui.ImagePicker',

  [
    'ephox.alloy.api.component.Memento',
    'ephox.alloy.api.events.AlloyEvents',
    'ephox.alloy.api.events.NativeEvents',
    'ephox.alloy.api.ui.Button',
    'ephox.imagetools.api.BlobConversions',
    'ephox.katamari.api.Id',
    'ephox.katamari.api.Option',
    'tinymce.themes.mobile.style.Styles'
  ],

  function (Memento, AlloyEvents, NativeEvents, Button, BlobConversions, Id, Option, Styles) {
    var addImage = function (editor, blob) {
      BlobConversions.blobToBase64(blob).then(function (base64) {
        editor.undoManager.transact(function () {
          var cache = editor.editorUpload.blobCache;
          var info = cache.create(
            Id.generate('mceu'), blob, base64
          );
          cache.add(info);
          var img = editor.dom.createHTML('img', {
            src: info.blobUri()
          });
          editor.insertContent(img);
        });
      });
    };

    var extractBlob = function (simulatedEvent) {
      var event = simulatedEvent.event();
      var files = event.raw().target.files || event.raw().dataTransfer.files;
      return Option.from(files[0]);
    };

    var sketch = function (editor) {
      var pickerDom = {
        tag: 'input',
        attributes: { accept: 'image/*', type: 'file', title: '' },
         // Visibility hidden so that it cannot be seen, and position absolute so that it doesn't
        // disrupt the layout
        styles: { visibility: 'hidden', position: 'absolute' }
      };

      var memPicker = Memento.record({
        dom: pickerDom,
        events: AlloyEvents.derive([
          // Stop the event firing again at the button level
          AlloyEvents.cutter(NativeEvents.click()),

          AlloyEvents.run(NativeEvents.change(), function (picker, simulatedEvent) {
            extractBlob(simulatedEvent).each(function (blob) {
              addImage(editor, blob);
            });
          })
        ])
      });

      return Button.sketch({
        dom: {
          tag: 'span',
          classes: [ Styles.resolve('toolbar-button'), Styles.resolve('icon-image'), Styles.resolve('icon') ]
        },
        components: [
          memPicker.asSpec()
        ],
        action: function (button) {
          var picker = memPicker.get(button);
          // Trigger a dom click for the file input
          picker.element().dom().click();
        }
      });
    };

    return {
      sketch: sketch
    };
  }
);
<|endoftext|>


<commit_before>const debug = require('debug')('dao')
const Observable = require("./Observable.js")

class ObservableProxy extends Observable {
  constructor(observable) {
    super()
    this.disposed = true
    this.observer = (signal, ...args) => {
      //console.log("PROXY OBSERVER SIGNAL", signal, ...args)
      this.fireObservers(signal, ...args)
    }
    this.properties = []
    this.errorProperties = []
    this.setTarget(observable)
  }

  setTarget(observable) {
    if(this === observable) throw new Error('infinite loop')
    if(!this.disposed && this.observable) {
      this.observable.unobserve(this.observer)
      for(let [object, property] of this.properties) {
        this.observable.unbindProperty(object, property)
      }
      for(let [object, property] of this.errorProperties) {
        this.observable.unbindErrorProperty(object,property)
      }
    }
    this.observable = observable
    if(!this.disposed && this.observable) {
      this.observable.observe(this.observer)
      for(let [object, property] of this.properties) {
        this.observable.bindProperty(object, property)
      }
      for(let [object, property] of this.errorProperties) {
        this.observable.bindErrorProperty(object,property)
      }
    }
  }

  dispose() {
    if(this.disposed) return
    this.disposed = true
    if(this.observable) this.observable.unobserve(this.observer)
  }

  respawn() {
    if(!this.disposed) return
    this.disposed = false
    if(this.observable) this.observable.observe(this.observer)
  }

  reobserveTarget() {
    if(!this.disposed && this.observable) {
      this.observable.unobserve(this.observer)
      this.observable.observe(this.observer)
    }
  }

  isUseless() {
    return (this.observers.length == 0) && (this.properties.length == 0)
        && (this.errorProperties.length == 0)
  }

  catch(...args) {
    let beenDisposed = this.disposed
    Observable.prototype.catch.apply(this, args)
    if(!beenDisposed) this.reobserveTarget()
  }

  observe(...args) {
    let beenDisposed = this.disposed
    Observable.prototype.observe.apply(this, args)
    if(!beenDisposed) this.reobserveTarget()
  }

  bindProperty(object, property) {
    if(this.isDisposed()) this.respawn()
    this.properties.push([object, property])
    if(this.observable) this.observable.bindProperty(object, property)
  }
  unbindProperty(object, property) {
    for(var i = 0; i < this.properties.length; i++) {
      var prop = this.properties[i]
      if(prop[0] === object && prop[1] === property) {
        this.properties.splice(i, 1)
        if(this.observable) this.observable.unbindProperty(object, property)
        if(this.isUseless()) this.dispose()
        return
      }
    }
    throw new Error("cannot unbind not bound property "+property)
  }

  bindErrorProperty(object, property) {
    if(this.isDisposed()) this.respawn()
    this.errorProperties.push([object, property])
    if(this.observable) this.observable.bindErrorProperty(object, property)
  }
  unbindErrorProperty(object, property) {
    for(var i = 0; i < this.errorProperties.length; i++) {
      var prop = this.errorProperties[i]
      if(prop[0] == object && prop[1] == property) {
        this.errorProperties.splice(i,1)
        if(this.observable) this.observable.unbindErrorProperty(object, property)
        if(this.isUseless()) this.dispose()
        return
      }
    }
    throw new Error("cannot unbind not bound property "+property)
  }
  getValue() {
    if(!this.observable) return undefined
    return this.observable.getValue()
  }

}

module.exports = ObservableProxy
<commit_msg>observable proxy observe optimization<commit_after>const debug = require('debug')('dao')
const Observable = require("./Observable.js")

class ObservableProxy extends Observable {
  constructor(observable) {
    super()
    this.disposed = true
    this.observer = (signal, ...args) => {
      //console.log("PROXY OBSERVER SIGNAL", signal, ...args)
      this.fireObservers(signal, ...args)
    }
    this.properties = []
    this.errorProperties = []
    this.setTarget(observable)
  }

  setTarget(observable) {
    if(this === observable) throw new Error('infinite loop')
    if(!this.disposed && this.observable) {
      //console.log("SET TARGET UNOBSERVE TARGET")
      this.observable.unobserve(this.observer)
      for(let [object, property] of this.properties) {
        this.observable.unbindProperty(object, property)
      }
      for(let [object, property] of this.errorProperties) {
        this.observable.unbindErrorProperty(object,property)
      }
    }
    this.observable = observable
    if(!this.disposed && this.observable) {
      //console.log("SET TARGET OBSERVE TARGET")
      this.observable.observe(this.observer)
      for(let [object, property] of this.properties) {
        this.observable.bindProperty(object, property)
      }
      for(let [object, property] of this.errorProperties) {
        this.observable.bindErrorProperty(object,property)
      }
    }
  }

  dispose() {
    if(this.disposed) return
    this.disposed = true
    if(this.observable) this.observable.unobserve(this.observer)
  }

  respawn() {
    if(!this.disposed) return
    this.disposed = false
    if(this.observable) this.observable.observe(this.observer)
  }

  reobserveTarget() {
    if(!this.disposed && this.observable) {
      //console.log("REOBSERVE TARGET!", this.observable)
      this.observable.unobserve(this.observer)
      this.observable.observe(this.observer)
    }
  }

  isUseless() {
    return (this.observers.length == 0) && (this.properties.length == 0)
        && (this.errorProperties.length == 0)
  }

  catch(...args) {
    let beenDisposed = this.disposed
    Observable.prototype.catch.apply(this, args)
    if(!beenDisposed) {
      if(this.observable.getError) {
        const error = this.observable.getError()
        if(error) this.fireObserver(args[0], 'error', error)
      } else {
        this.reobserveTarget()
      }
    }
  }

  observe(...args) {
    let beenDisposed = this.disposed
    Observable.prototype.observe.apply(this, args)
    if(!beenDisposed) {
      if(this.observable.getValue) {
        const value = this.observable.getValue()
        if(value !== undefined) this.fireObserver(args[0], 'set', value)
      } else {
        this.reobserveTarget()
      }
    }
  }

  bindProperty(object, property) {
    if(this.isDisposed()) this.respawn()
    this.properties.push([object, property])
    if(this.observable) this.observable.bindProperty(object, property)
  }
  unbindProperty(object, property) {
    for(var i = 0; i < this.properties.length; i++) {
      var prop = this.properties[i]
      if(prop[0] === object && prop[1] === property) {
        this.properties.splice(i, 1)
        if(this.observable) this.observable.unbindProperty(object, property)
        if(this.isUseless()) this.dispose()
        return
      }
    }
    throw new Error("cannot unbind not bound property "+property)
  }

  bindErrorProperty(object, property) {
    if(this.isDisposed()) this.respawn()
    this.errorProperties.push([object, property])
    if(this.observable) this.observable.bindErrorProperty(object, property)
  }
  unbindErrorProperty(object, property) {
    for(var i = 0; i < this.errorProperties.length; i++) {
      var prop = this.errorProperties[i]
      if(prop[0] == object && prop[1] == property) {
        this.errorProperties.splice(i,1)
        if(this.observable) this.observable.unbindErrorProperty(object, property)
        if(this.isUseless()) this.dispose()
        return
      }
    }
    throw new Error("cannot unbind not bound property "+property)
  }
  getValue() {
    if(!this.observable) return undefined
    return this.observable.getValue()
  }

}

module.exports = ObservableProxy
<|endoftext|>


<commit_before>'use strict'

const got = require('got')
const stream = require('stream')
const AWS = require('aws-sdk')
const zlib = require('zlib')
const local = require('kes/src/local')

const stepfunctions = new AWS.StepFunctions()
const s3 = new AWS.S3()

function invokeLambda(satellite, firstFileNum, lastFileNum, arn, cb) {
  const prefix = process.env.prefix || 'sat-api-dev'
  if (firstFileNum > lastFileNum) return
  const inputs = {
    bucket: process.env.bucket || 'sat-api',
    key: `${prefix}/csv/${satellite}`,
    satellite,
    currentFileNum: firstFileNum,
    lastFileNum,
    arn
  }
  const params = {
    stateMachineArn: arn,
    input: JSON.stringify(inputs),
    name: `csv_${satellite}_${firstFileNum}_${Date.now()}`
  }
  stepfunctions.startExecution(params, function(err, data) {
    console.log(`${params.name} step function launched: ${JSON.stringify(inputs)}`);
    if (err) console.log(`step function launch error: ${err}`)
  })
}

function split(satellite, arn, maxFiles, linesPerFile, maxLambdas, cb) {

  let fileCounter = 0
  let lineCounter = 0
  linesPerFile = linesPerFile || 500
  maxLambdas = maxLambdas || 20
  arn = arn || ''

  const bucket = process.env.bucket || 'sat-api'
  const prefix = process.env.prefix || 'sat-api-dev'

  const lineBuffer = new Buffer(4096)
  const gunzip = zlib.createGunzip()
  let remoteCsv
  let newStream
  let currentFile
  let lineLength = 0
  let stopSplitting = false
  let header
  let reverse = false

  switch (satellite) {
    case 'landsat':
      remoteCsv = 'https://landsat.usgs.gov/landsat/metadata_service/bulk_metadata_files/LANDSAT_8_C1.csv'
      newStream = got.stream(remoteCsv)
      break
    case 'sentinel':
      remoteCsv = 'https://storage.googleapis.com/gcp-public-data-sentinel-2/index.csv.gz'
      reverse = true
      newStream = got.stream(remoteCsv).pipe(gunzip)
      break
  }

  const build = function buildFile(line) {
    const fileName = `${prefix}/csv/${satellite}/${satellite}_${fileCounter}.csv`;

    // get the csv header
    if (fileCounter === 0 && lineCounter === 0) header = line.toString()

    // create a new file or add to existing
    if (lineCounter === 0) {
      currentFile = new stream.PassThrough()
      currentFile.push(header)
    } else {
      currentFile.push(line.toString())
    }
    lineCounter += 1 // increment the filename
    lineLength = 0 // reset the buffer

    if (lineCounter > linesPerFile) {
      const params = {
        Body: currentFile,
        Bucket: bucket,
        Key: fileName
      };
      currentFile.end();
      s3.upload(params, (e, d) => { if (e) console.log(e) })
      lineCounter = 0 // start counting the lines again
      if ((fileCounter) % 250 === 0 && fileCounter != 0) console.log(`uploaded ${fileCounter + 1} files`)
      fileCounter += 1

      // sentinel csv is ordered from old to new so always have to go all the way back
      if ((fileCounter >= maxFiles) && maxFiles != 0 && satellite != 'sentinel') {
        stopSplitting = true
      }
    }
  }

  newStream.on('data', (data) => {
    if (!stopSplitting) {
      const dataLen = data.length;
      for (let i = 0; i < dataLen; i++) {
        lineBuffer[lineLength] = data[i] // Buffer new line data.
        lineLength++;
        if (data[i] === 10) { // Newline char was found.
          build(lineBuffer.slice(0, lineLength))
        }
      }
    }
  })

  newStream.on('end', () => {
    // write the last records
    if (lineCounter > 0) {
        const params = {
          Body: currentFile,
          Bucket: bucket,
          Key: `${prefix}/csv/${satellite}/${satellite}_${fileCounter}.csv`
        };
        s3.upload(params, (e, d) => { if (e) console.log(e) })
        currentFile.end();
    }
    console.log(`${fileCounter} total files`)
    // determine batches and run lambdas
    if (arn != '') {
      maxFiles = (maxFiles === 0) ? fileCounter : maxFiles
      var numLambdas = Math.min(maxFiles, maxLambdas)
      var batchSize = Math.floor(maxFiles / maxLambdas)
      var extra = maxFiles % numLambdas
      var maxEndFile = reverse ? fileCounter - 1 : maxFiles - 1

      var startFile = reverse ? fileCounter - maxFiles : 0
      var endFile
      console.log(`Invoking ${numLambdas} batches of Lambdas up to ${batchSize} each (Files ${startFile}-${maxEndFile})`)
      for (var i = 0; i < numLambdas; i++) {
        endFile = (i < extra) ? startFile + batchSize: startFile + batchSize - 1
        invokeLambda(satellite, startFile, Math.min(endFile, maxEndFile), arn)
        startFile = endFile + 1
      }
    }
    cb()
  })
  newStream.on('error', e => cb(e))
}

module.exports.handler = function (event, context, cb) {
  split(event.satellite, event.arn, event.maxFiles, event.linesPerFile, event.maxLambdas, cb)
}

local.localRun(() => {
  const payload = {
    satellite: 'landsat',
    arn: '',
    maxFiles: 1,
  }

  module.exports.handler(payload, null, (e, r) => {
    console.log(e, r)
  })
})
<commit_msg>fix batch size calculation<commit_after>'use strict'

const got = require('got')
const stream = require('stream')
const AWS = require('aws-sdk')
const zlib = require('zlib')
const local = require('kes/src/local')

const stepfunctions = new AWS.StepFunctions()
const s3 = new AWS.S3()

function invokeLambda(satellite, firstFileNum, lastFileNum, arn, cb) {
  const prefix = process.env.prefix || 'sat-api-dev'
  if (firstFileNum > lastFileNum) return
  const inputs = {
    bucket: process.env.bucket || 'sat-api',
    key: `${prefix}/csv/${satellite}`,
    satellite,
    currentFileNum: firstFileNum,
    lastFileNum,
    arn
  }
  const params = {
    stateMachineArn: arn,
    input: JSON.stringify(inputs),
    name: `csv_${satellite}_${firstFileNum}_${Date.now()}`
  }
  stepfunctions.startExecution(params, function(err, data) {
    console.log(`${params.name} step function launched: ${JSON.stringify(inputs)}`);
    if (err) console.log(`step function launch error: ${err}`)
  })
}

function split(satellite, arn, maxFiles, linesPerFile, maxLambdas, cb) {

  let fileCounter = 0
  let lineCounter = 0
  linesPerFile = linesPerFile || 500
  maxLambdas = maxLambdas || 20
  arn = arn || ''

  const bucket = process.env.bucket || 'sat-api'
  const prefix = process.env.prefix || 'sat-api-dev'

  const lineBuffer = new Buffer(4096)
  const gunzip = zlib.createGunzip()
  let remoteCsv
  let newStream
  let currentFile
  let lineLength = 0
  let stopSplitting = false
  let header
  let reverse = false

  switch (satellite) {
    case 'landsat':
      remoteCsv = 'https://landsat.usgs.gov/landsat/metadata_service/bulk_metadata_files/LANDSAT_8_C1.csv'
      newStream = got.stream(remoteCsv)
      break
    case 'sentinel':
      remoteCsv = 'https://storage.googleapis.com/gcp-public-data-sentinel-2/index.csv.gz'
      reverse = true
      newStream = got.stream(remoteCsv).pipe(gunzip)
      break
  }

  const build = function buildFile(line) {
    const fileName = `${prefix}/csv/${satellite}/${satellite}_${fileCounter}.csv`;

    // get the csv header
    if (fileCounter === 0 && lineCounter === 0) header = line.toString()

    // create a new file or add to existing
    if (lineCounter === 0) {
      currentFile = new stream.PassThrough()
      currentFile.push(header)
    } else {
      currentFile.push(line.toString())
    }
    lineCounter += 1 // increment the filename
    lineLength = 0 // reset the buffer

    if (lineCounter > linesPerFile) {
      const params = {
        Body: currentFile,
        Bucket: bucket,
        Key: fileName
      };
      currentFile.end();
      s3.upload(params, (e, d) => { if (e) console.log(e) })
      lineCounter = 0 // start counting the lines again
      if ((fileCounter) % 250 === 0 && fileCounter != 0) console.log(`uploaded ${fileCounter + 1} files`)
      fileCounter += 1

      // sentinel csv is ordered from old to new so always have to go all the way back
      if ((fileCounter >= maxFiles) && maxFiles != 0 && satellite != 'sentinel') {
        stopSplitting = true
      }
    }
  }

  newStream.on('data', (data) => {
    if (!stopSplitting) {
      const dataLen = data.length;
      for (let i = 0; i < dataLen; i++) {
        lineBuffer[lineLength] = data[i] // Buffer new line data.
        lineLength++;
        if (data[i] === 10) { // Newline char was found.
          build(lineBuffer.slice(0, lineLength))
        }
      }
    }
  })

  newStream.on('end', () => {
    // write the last records
    if (lineCounter > 0) {
        const params = {
          Body: currentFile,
          Bucket: bucket,
          Key: `${prefix}/csv/${satellite}/${satellite}_${fileCounter}.csv`
        };
        s3.upload(params, (e, d) => { if (e) console.log(e) })
        currentFile.end();
    }
    console.log(`${fileCounter} total files`)
    // determine batches and run lambdas
    if (arn != '') {
      maxFiles = (maxFiles === 0) ? fileCounter : maxFiles
      var numLambdas = Math.min(maxFiles, maxLambdas)
      var batchSize = Math.floor(maxFiles / numLambdas)
      var extra = maxFiles % numLambdas
      var maxEndFile = reverse ? fileCounter - 1 : maxFiles - 1

      var startFile = reverse ? fileCounter - maxFiles : 0
      var endFile
      console.log(`Invoking ${numLambdas} batches of Lambdas up to ${batchSize} each (Files ${startFile}-${maxEndFile})`)
      for (var i = 0; i < numLambdas; i++) {
        endFile = (i < extra) ? startFile + batchSize: startFile + batchSize - 1
        invokeLambda(satellite, startFile, Math.min(endFile, maxEndFile), arn)
        startFile = endFile + 1
      }
    }
    cb()
  })
  newStream.on('error', e => cb(e))
}

module.exports.handler = function (event, context, cb) {
  split(event.satellite, event.arn, event.maxFiles, event.linesPerFile, event.maxLambdas, cb)
}

local.localRun(() => {
  const payload = {
    satellite: 'landsat',
    arn: '',
    maxFiles: 1,
  }

  module.exports.handler(payload, null, (e, r) => {
    console.log(e, r)
  })
})
<|endoftext|>


<commit_before>// TODO: handle that the websocket goes down
//       * Notify the user?
// TODO: Do not track data in incognito-mode

(function () {
    var ws, connected = false, backoff = 1000;

    if (!localStorage.queue) {
        localStorage.queue = JSON.stringify([]);
    }

    if (!localStorage.client_id) {
        localStorage.client_id = (new Date()).getTime().toString();
    }

    function addToQueue(event) {
        var queue = JSON.parse(localStorage.queue);
        queue.push(event);
        localStorage.queue = JSON.stringify(queue);
    }

    // TODO: now it is possible to lose events, fixit
    function emptyQueue(callback) {
        var queue = JSON.parse(localStorage.queue);
        queue.forEach(callback);
        localStorage.queue = JSON.stringify([]);
    }

    function setupWebsocket() {
        ws = new WebSocket("ws://localhost:8765/tabs");
        ws.onopen = function () {
            console.log("Connected!");
            backoff = 1000;
            connected = true;
            emptyQueue(function (event) {
                ws.send(JSON.stringify(event));
            });
        };
        ws.onclose = function () {
            connected = false;
            console.log("connection to WebSocket closed! " +
                        "trying again in " + Math.floor(backoff / 1000) +
                        " seconds.");
            setTimeout(setupWebsocket, backoff);
            if (backoff < 180000) {
                backoff *= 2;
            }
        };
        ws.onmessage = function (evt) {
            console.log(evt);
        };
    }

    function addEvent(event) {
        if (connected) {
            ws.send(JSON.stringify(event));
        } else {
            addToQueue(event);
        }
    }

    function addEventForTab(eventType, tab) {
        var event = {"event": eventType,
                     "client_id": localStorage.client_id,
                     "tabId": tab.id,
                     "windowId": tab.windowId,
                     "title": tab.title,
                     "url": tab.url,
                     "active": tab.active,
                     "time": new Date()};
        addEvent(event);
    }

    function addEventByTabAndWindowId(eventType, tabId, windowId) {
        var event = {"event": eventType,
                     "client_id": localStorage.client_id,
                     "tabId": tabId,
                     "time": new Date()
                    };
        if (windowId !== chrome.windows.WINDOW_ID_NONE) {
            event.windowId = windowId;
        }
        addEvent(event);
    }

    function addEventByTabId(eventType, tabId) {
        addEventByTabAndWindowId(eventType, tabId,
                                 chrome.windows.WINDOW_ID_NONE);
    }

    setupWebsocket();

    chrome.windows.getAll(null, function (windows) {
        windows.forEach(function (window) {
            chrome.tabs.getAllInWindow(window.id, function (tabs) {
                tabs.forEach(function (tab) {
                    addEventForTab("startup", tab);
                });
            });
        });
    });

    chrome.tabs.onCreated.addListener(function (tab) {
        addEventForTab("created", tab);
    });

    chrome.tabs.onRemoved.addListener(function (tabId) {
        addEventByTabId("removed", tabId);
    });

    chrome.tabs.onActiveChanged.addListener(function (tabId, selectInfo) {
        addEventByTabAndWindowId("activeChanged", tabId, selectInfo.windowId);
    });

    chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
        if (changeInfo.url) {
            addEventForTab("updated", tab);
        }
    });

    chrome.windows.onFocusChanged.addListener(function (windowId) {
        var query, event;
        if (windowId !== -1) {
            query = {"active": true, "windowId": windowId};
            chrome.tabs.query(query, function (tabs) {
                tabs.forEach(function (tab) {
                    addEventForTab("activeChanged", tab);
                });
            });
        } else {
            event = {"event": "unfocused",
                     "client_id": localStorage.client_id,
                     "windowId": windowId,
                     "time": new Date()};
            addEvent(event);
        }
    });

    chrome.browserAction.onClicked.addListener(function (tab) {
        addEventForTab("watched", tab);
    });
}());
<commit_msg>Write the number of queued events on the icon to notify the user.<commit_after>(function () {
    var ws, connected = false, backoff = 1000;

    if (!localStorage.queue) {
        localStorage.queue = JSON.stringify([]);
    }

    if (!localStorage.client_id) {
        localStorage.client_id = (new Date()).getTime().toString();
    }

    function addToQueue(event) {
        var queue = JSON.parse(localStorage.queue);
        queue.push(event);
        chrome.browserAction.setBadgeText(
            {"text":
             queue.length < 10000 ? queue.length.toString() : "9999"});
        localStorage.queue = JSON.stringify(queue);
    }

    // TODO: now it is possible to lose events, fixit
    function emptyQueue(callback) {
        var queue = JSON.parse(localStorage.queue);
        queue.forEach(callback);
        chrome.browserAction.setBadgeText({"text": ""});
        localStorage.queue = JSON.stringify([]);
    }

    function setupWebsocket() {
        ws = new WebSocket("ws://localhost:8765/tabs");
        ws.onopen = function () {
            console.log("Connected!");
            backoff = 1000;
            connected = true;
            emptyQueue(function (event) {
                ws.send(JSON.stringify(event));
            });
        };
        ws.onclose = function () {
            connected = false;
            console.log("connection to WebSocket closed! " +
                        "trying again in " + Math.floor(backoff / 1000) +
                        " seconds.");
            setTimeout(setupWebsocket, backoff);
            if (backoff < 180000) {
                backoff *= 2;
            }
        };
        ws.onmessage = function (evt) {
            console.log(evt);
        };
    }

    function addEvent(event) {
        if (connected) {
            ws.send(JSON.stringify(event));
        } else {
            addToQueue(event);
        }
    }

    function addEventForTab(eventType, tab) {
        var event = {"event": eventType,
                     "client_id": localStorage.client_id,
                     "tabId": tab.id,
                     "windowId": tab.windowId,
                     "title": tab.title,
                     "url": tab.url,
                     "active": tab.active,
                     "time": new Date()};
        addEvent(event);
    }

    function addEventByTabAndWindowId(eventType, tabId, windowId) {
        var event = {"event": eventType,
                     "client_id": localStorage.client_id,
                     "tabId": tabId,
                     "time": new Date()
                    };
        if (windowId !== chrome.windows.WINDOW_ID_NONE) {
            event.windowId = windowId;
        }
        addEvent(event);
    }

    function addEventByTabId(eventType, tabId) {
        addEventByTabAndWindowId(eventType, tabId,
                                 chrome.windows.WINDOW_ID_NONE);
    }

    setupWebsocket();

    chrome.windows.getAll(null, function (windows) {
        windows.forEach(function (window) {
            chrome.tabs.getAllInWindow(window.id, function (tabs) {
                tabs.forEach(function (tab) {
                    addEventForTab("startup", tab);
                });
            });
        });
    });

    chrome.tabs.onCreated.addListener(function (tab) {
        addEventForTab("created", tab);
    });

    chrome.tabs.onRemoved.addListener(function (tabId) {
        addEventByTabId("removed", tabId);
    });

    chrome.tabs.onActiveChanged.addListener(function (tabId, selectInfo) {
        addEventByTabAndWindowId("activeChanged", tabId, selectInfo.windowId);
    });

    chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
        if (changeInfo.url) {
            addEventForTab("updated", tab);
        }
    });

    chrome.windows.onFocusChanged.addListener(function (windowId) {
        var query, event;
        if (windowId !== -1) {
            query = {"active": true, "windowId": windowId};
            chrome.tabs.query(query, function (tabs) {
                tabs.forEach(function (tab) {
                    addEventForTab("activeChanged", tab);
                });
            });
        } else {
            event = {"event": "unfocused",
                     "client_id": localStorage.client_id,
                     "windowId": windowId,
                     "time": new Date()};
            addEvent(event);
        }
    });

    chrome.browserAction.onClicked.addListener(function (tab) {
        addEventForTab("watched", tab);
    });
}());
<|endoftext|>


<commit_before>function createCollection() {

  var publishDate, publishTime, collectionId, collectionDate, collectionType, releaseUri;
  collectionId = $('#collectionname').val();
  collectionType = $('form input[type=radio]:checked').val();

  if (collectionType === 'scheduled') {
    publishTime  = parseInt($('#hour').val()) + parseInt($('#min').val());
    var toIsoDate = $('#date').datepicker("getDate");
    collectionDate = new Date(parseInt(new Date(toIsoDate).getTime()) + publishTime).toISOString();
  } else {
    collectionDate  = null;
  };

  if (collectionType === 'release') {
    releaseUri  = $('#collection-release').val();
  } else {
    releaseUri  = null;
  };

  // inline tests
  if (collectionId === '') {
    alert('This is not a valid collection name');
    return true;
  } if (collectionId.match(/\./)) {
    alert('This is not a valid collection name. You can not use dots');
    return true;
  } if ((collectionType === 'scheduled') && (isValidDate(new Date(collectionDate)))) {
    alert('This is not a valid date');
    return true;
  } if ((collectionType === 'scheduled') && (Date.parse(collectionDate) < new Date())) {
    alert('This is not a valid date');
    return true;
  } else {
    // Create the collection
    $.ajax({
      url: "/zebedee/collection",
      dataType: 'json',
      contentType: 'application/json',
      type: 'POST',
      data: JSON.stringify({name: collectionId, type: collectionType, publishDate: collectionDate, releaseUri: releaseUri}),
      success: function (collection) {
        Florence.setActiveCollection(collection);
        createWorkspace('', collection.id, 'browse');
      },
      error: function (response) {
        if(response.status === 409) {
          alert('A collection already exists with the name ' + collectionId);
        }
        else {
          handleApiError(response);
        }
      }
    });
  }
}

function isValidDate(d) {
  if (!isNaN(d.getTime()))
    {return false;}
  else
    {return true;}
}

<commit_msg>When creating a collection, send a release based collection with a scheduled type.<commit_after>function createCollection() {

  var publishTime, collectionId, collectionDate, collectionType, releaseUri;
  collectionId = $('#collectionname').val();
  collectionType = $('form input[type=radio]:checked').val();

  var publishType = collectionType;
  if (collectionType === 'release') {
    publishType = 'scheduled';
  }

  if (collectionType === 'scheduled') {
    publishTime  = parseInt($('#hour').val()) + parseInt($('#min').val());
    var toIsoDate = $('#date').datepicker("getDate");
    collectionDate = new Date(parseInt(new Date(toIsoDate).getTime()) + publishTime).toISOString();
  } else {
    collectionDate  = null;
  };

  if (collectionType === 'release') {
    releaseUri  = $('#collection-release').val();
  } else {
    releaseUri  = null;
  };

  // inline tests
  if (collectionId === '') {
    alert('This is not a valid collection name');
    return true;
  } if (collectionId.match(/\./)) {
    alert('This is not a valid collection name. You can not use dots');
    return true;
  } if ((collectionType === 'scheduled') && (isValidDate(new Date(collectionDate)))) {
    alert('This is not a valid date');
    return true;
  } if ((collectionType === 'scheduled') && (Date.parse(collectionDate) < new Date())) {
    alert('This is not a valid date');
    return true;
  } else {
    // Create the collection
    $.ajax({
      url: "/zebedee/collection",
      dataType: 'json',
      contentType: 'application/json',
      type: 'POST',
      data: JSON.stringify({name: collectionId, type: publishType, publishDate: collectionDate, releaseUri: releaseUri}),
      success: function (collection) {
        Florence.setActiveCollection(collection);
        createWorkspace('', collection.id, 'browse');
      },
      error: function (response) {
        if(response.status === 409) {
          alert('A collection already exists with the name ' + collectionId);
        }
        else {
          handleApiError(response);
        }
      }
    });
  }
}

function isValidDate(d) {
  if (!isNaN(d.getTime()))
    {return false;}
  else
    {return true;}
}

<|endoftext|>


<commit_before>/**
 * Copyright 2017 CA
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* @flow */
import React from 'react';
import darken from 'polished/lib/color/darken';
import lighten from 'polished/lib/color/lighten';
import rgba from 'polished/lib/color/rgba';
import { createStyledComponent, pxToEm } from '../../../../styles';
import { ThemeProvider } from '../../../../themes';
import IconCheck from '../../../../Icon/IconCheck';
import IconClose from '../../../../Icon/IconClose';
import Markdown from '../../Markdown';
import Heading from '../../SiteHeading';

type Props = {
  backgroundColor?: string,
  children?: string,
  className?: string,
  example?: React$Node,
  title?: string,
  type: 'do' | 'dont'
};

const styles = {
  root: ({ theme }) => ({
    [theme.bp_interior_bestPracticesMultiColumn]: {
      display: 'flex',
      alignItems: 'stretch',
      justifyContent: 'space-between'
    }
  }),
  example: ({ backgroundColor, theme }) => ({
    backgroundColor,
    border: `1px solid ${rgba(theme.borderColor, 0.3)}`,
    padding: theme.space_inset_md,

    [theme.bp_interior_bestPracticesMultiColumn]: {
      flex: `1 1 ${7 / 12 * 100}%`
    }
  }),
  header: ({ theme, type }) => ({
    backgroundColor:
      type === 'do'
        ? rgba(theme.borderColor_success, 0.1)
        : rgba(theme.borderColor_danger, 0.1),
    borderTop: `3px solid ${type === 'do'
      ? theme.borderColor_success
      : theme.borderColor_danger}`,
    padding: `${theme.baseline_1} ${theme.baseline_2}`,

    [theme.bp_interior_bestPracticesMultiColumn]: {
      borderLeft: `3px solid ${type === 'do'
        ? rgba(theme.borderColor_success, 0.6)
        : rgba(theme.borderColor_danger, 0.6)}`,
      borderTop: 0,
      flex: `1 1 ${5 / 12 * 100}%`
    },

    '& ::selection': {
      backgroundColor:
        type === 'do'
          ? rgba(theme.borderColor_success, 0.2)
          : rgba(theme.borderColor_danger, 0.2)
    },

    '& > [role="img"]': {
      backgroundColor:
        type === 'do'
          ? rgba(theme.borderColor_success, 0.2)
          : rgba(theme.borderColor_danger, 0.2),
      borderRadius: theme.baseline_3,
      fill:
        type === 'do'
          ? rgba(theme.borderColor_success, 0.5)
          : rgba(theme.borderColor_danger, 0.5),
      float: 'left',
      height: theme.baseline_3,
      marginRight: theme.baseline_2,
      padding: `${parseFloat(theme.baseline_1) / 2}em`,
      width: theme.baseline_3
    },

    // Markdown
    '& > div': {
      overflow: 'hidden',

      '& code': {
        backgroundColor:
          type === 'do'
            ? rgba(lighten(0.5, theme.borderColor_success), 0.5)
            : rgba(lighten(0.5, theme.borderColor_danger), 0.5)
      },

      '& a': {
        '&,&:hover,&:focus,&:active': {
          color:
            type === 'do'
              ? darken(0.1, theme.borderColor_success)
              : darken(0.1, theme.borderColor_danger)
        }
      },

      '& > p:last-child': {
        marginBottom: 0
      }
    }
  }),
  title: ({ theme, type }) => ({
    color:
      type === 'do'
        ? darken(0.1, theme.borderColor_success)
        : darken(0.1, theme.borderColor_danger),
    fontSize: theme.SiteHeading_fontSize_4,
    fontWeight: theme.fontWeight_regular,
    lineHeight: 1.1,
    margin: 0,

    [theme.bp_moreSpacious]: {
      fontSize: theme.SiteHeading_fontSize_4_wide
    }
  })
};

const Root = createStyledComponent('div', styles.root);
const Example = createStyledComponent('div', styles.example);
const Header = createStyledComponent('div', styles.header);
const Title = createStyledComponent(Heading, styles.title);

export default function DocPractice({
  backgroundColor,
  children,
  className,
  example,
  type
}: Props) {
  const iconProps = {
    size: pxToEm(50)
  };
  const icon =
    type === 'do' ? <IconCheck {...iconProps} /> : <IconClose {...iconProps} />;

  return (
    <Root className={className}>
      <Header type={type}>
        {icon}
        <Title level={4} type={type}>
          {type === 'do' ? 'Do' : 'Don’t'}
        </Title>
        <Markdown>{children}</Markdown>
      </Header>
      <Example backgroundColor={backgroundColor} type={type}>
        <ThemeProvider>
          {typeof example === 'string' ? (
            <Markdown>{example}</Markdown>
          ) : (
            example
          )}
        </ThemeProvider>
      </Example>
    </Root>
  );
}
<commit_msg>chore(website): Improve Best Practices Do & Don't block styles<commit_after>/**
 * Copyright 2017 CA
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* @flow */
import React from 'react';
import darken from 'polished/lib/color/darken';
import rgba from 'polished/lib/color/rgba';
import { createStyledComponent, pxToEm } from '../../../../styles';
import { mineralTheme, ThemeProvider } from '../../../../themes';
import IconCheck from '../../../../Icon/IconCheck';
import IconClose from '../../../../Icon/IconClose';
import Markdown from '../../Markdown';
import Heading from '../../SiteHeading';

type Props = {
  backgroundColor?: string,
  children?: string,
  className?: string,
  example?: React$Node,
  title?: string,
  type: 'do' | 'dont'
};

const themes = {
  do: {
    borderColor: darken(0.1, mineralTheme.borderColor_success),
    color_text_primary: mineralTheme.color_text_success,

    SiteLink_borderColor_focus: mineralTheme.color_text_success,
    SiteLink_color: mineralTheme.color_text_success,
    SiteLink_color_active: mineralTheme.color_text_success,
    SiteLink_color_hover: mineralTheme.color_text_success,
    SiteLink_color_focus: mineralTheme.color_text_success
  },
  dont: {
    borderColor: mineralTheme.borderColor_danger,
    color_text_primary: darken(0.1, mineralTheme.color_text_danger),

    SiteLink_borderColor_focus: darken(0.1, mineralTheme.color_text_danger),
    SiteLink_color: darken(0.1, mineralTheme.color_text_danger),
    SiteLink_color_active: darken(0.1, mineralTheme.color_text_danger),
    SiteLink_color_hover: darken(0.1, mineralTheme.color_text_danger),
    SiteLink_color_focus: darken(0.1, mineralTheme.color_text_danger)
  }
};

const styles = {
  root: ({ theme }) => ({
    [theme.bp_interior_bestPracticesMultiColumn]: {
      display: 'flex',
      alignItems: 'stretch',
      justifyContent: 'space-between'
    }
  }),
  example: ({ backgroundColor, theme }) => ({
    backgroundColor,
    border: `1px solid ${rgba(theme.borderColor, 0.3)}`,
    padding: theme.space_inset_md,

    [theme.bp_interior_bestPracticesMultiColumn]: {
      flex: `1 1 ${7 / 12 * 100}%`
    }
  }),
  header: ({ theme }) => ({
    backgroundColor: rgba(theme.borderColor, 0.1),
    borderTop: `3px solid ${rgba(theme.borderColor, 0.6)}`,
    padding: `${theme.baseline_1} ${theme.baseline_2}`,

    [theme.bp_interior_bestPracticesMultiColumn]: {
      borderLeft: `3px solid ${rgba(theme.borderColor, 0.6)}`,
      borderTop: 0,
      flex: `1 1 ${5 / 12 * 100}%`
    },

    '& ::selection': {
      backgroundColor: rgba(theme.color_text_primary, 0.2)
    },

    '& > [role="img"]': {
      backgroundColor: rgba(theme.borderColor, 0.2),
      borderRadius: theme.baseline_3,
      fill: rgba(theme.borderColor, 0.5),
      float: 'left',
      height: theme.baseline_3,
      marginRight: theme.baseline_2,
      padding: `${parseFloat(theme.baseline_1) / 2}em`,
      width: theme.baseline_3
    },

    // Markdown
    '& > div': {
      overflow: 'hidden',

      '& > p:last-child': {
        marginBottom: 0
      }
    }
  }),
  title: ({ theme }) => ({
    color: theme.color_text_primary,
    fontSize: theme.SiteHeading_fontSize_4,
    fontWeight: theme.fontWeight_regular,
    lineHeight: 1.1,
    margin: 0,

    [theme.bp_moreSpacious]: {
      fontSize: theme.SiteHeading_fontSize_4_wide
    }
  })
};

const Root = createStyledComponent('div', styles.root);
const Example = createStyledComponent('div', styles.example);
const Header = createStyledComponent('div', styles.header);
const Title = createStyledComponent(Heading, styles.title);

export default function DocPractice({
  backgroundColor,
  children,
  className,
  example,
  type
}: Props) {
  const iconProps = {
    size: pxToEm(50)
  };
  const icon =
    type === 'do' ? <IconCheck {...iconProps} /> : <IconClose {...iconProps} />;

  return (
    <Root className={className}>
      <ThemeProvider theme={themes[type]}>
        <Header>
          {icon}
          <Title level={4}>{type === 'do' ? 'Do' : 'Don’t'}</Title>
          <Markdown>{children}</Markdown>
        </Header>
      </ThemeProvider>
      <Example backgroundColor={backgroundColor}>
        <ThemeProvider>
          {typeof example === 'string' ? (
            <Markdown>{example}</Markdown>
          ) : (
            example
          )}
        </ThemeProvider>
      </Example>
    </Root>
  );
}
<|endoftext|>