<commit_before>import {describe, it} from 'mocha';
import {expect} from 'chai';

import * as Utils from '../../src/core/utils';

describe('utils', () => {

    describe('parseNumber', () => {
        const {parseNumber} = Utils;

        const success = (input, expected) => () => {
            expect(parseNumber(input)).to.equal(expected);
        };
        const failure = (input) => () => {
            expect(parseNumber(input)).to.be.NaN;
        };

        it("fails on the empty string", failure(''));
        it("parses a decimal zero", success('0', 0));
        it("parses a hex zero", success('x0', 0));
        it("parses a decimal value", success('123', 123));
        it("parses a negative decimal value", success('-123', -123));
        it("fails on double negations", failure('--123'));
        it("parses a hex value with 'x'", success('x90ab', 0x90AB));
        it("parses a capital hex value with 'x'", success('x90AB', 0x90AB));
        it("fails on a hex value with '0x'", failure('0x123'));
        it("parses a negative hex value", success('-x123', -0x123));
        it("fails on hex input with non-hex values", failure('xDEFG'));
        it("fails on decimal input with non-digits", failure('90AB'));
    });

    describe('toHexString', () => {
        const {toHexString} = Utils;

        it("has a leading 'x' prefix", () => {
            expect(toHexString(0x1234)).to.equal('x1234');
        });

        it("zero-pads three-digit strings", () => {
            expect(toHexString(0x234)).to.equal('x0234');
        });

        it("zero-pads two-digit strings", () => {
            expect(toHexString(0x34)).to.equal('x0034');
        });

        it("zero-pads one-digit strings", () => {
            expect(toHexString(0x4)).to.equal('x0004');
        });

        it("zero-pads zero", () => {
            expect(toHexString(0x0)).to.equal('x0000');
        });

        it("allows strings more than four digits without clipping", () => {
            expect(toHexString(0x12345)).to.equal('x12345');
        });

        it("allows other padding values", () => {
            expect(toHexString(0x1234, 6)).to.equal('x001234');
        });

        it("allows other prefixes", () => {
            expect(toHexString(0x1234, undefined, 'y')).to.equal('y1234');
        });

        it("allows other prefixes and padding values together", () => {
            expect(toHexString(0x1234, 6, 'y')).to.equal('y001234');
        });

    });

    describe('toInt16', () => {
        const test = (input, expected) => () => {
            expect(Utils.toInt16(input)).to.equal(expected);
        };

        it("is the identity for 0", test(0, 0));
        it("is the identity for 1", test(1, 1));
        it("is the identity for -1", test(-1, -1));

        it("takes 0x10000 to 0", test(0x10000, 0));
        it("takes 0x20000 to 0", test(0x20000, 0));
        it("takes 0x1FFFF to -1", test(0x1FFFF, -1));
        it("takes 0x54321 to 0x4321", test(0x54321, 0x4321));
        it("takes 0xABCDE to 0xBCDE - 0x10000",
            test(0xABCDE, 0xBCDE - 0x10000));
    });

    describe('toUint16', () => {
        const test = (input, expected) => () => {
            expect(Utils.toUint16(input)).to.equal(expected);
        };

        it("is the identity for 0", test(0, 0));
        it("is the identity for 1", test(1, 1));
        it("takes -1 to 0xFFFF", test(-1, 0xFFFF));

        it("takes 0x10000 to 0", test(0x10000, 0));
        it("takes 0x20000 to 0", test(0x20000, 0));
        it("takes 0x1FFFF to 0xFFFF", test(0x1FFFF, 0xFFFF));
        it("takes 0x54321 to 0x4321", test(0x54321, 0x4321));
        it("takes 0xABCDE to 0xBCDE", test(0xABCDE, 0xBCDE));
    });

    describe('signExtend16', () => {
        const test = (input, bits, expected) => () => {
            expect(Utils.signExtend16(input, bits)).to.equal(expected);
        };

        it("takes bits 00000 to 0",   test(0b00000, 5, 0));
        it("takes bits 00001 to 1",   test(0b00001, 5, 1));
        it("takes bits 00010 to 2",   test(0b00010, 5, 2));
        it("takes bits 01111 to 15",  test(0b01111, 5, 15));
        it("takes bits 10000 to -16", test(0b10000, 5, -16));
        it("takes bits 10001 to -15", test(0b10001, 5, -15));
        it("takes bits 10010 to -14", test(0b10010, 5, -14));
        it("takes bits 11111 to -1",  test(0b11111, 5, -1));
    });

    describe('getConditionCode', () => {
        const test = (psr, expected) => () => {
            expect(Utils.getConditionCode(psr)).to.equal(expected);
        };

        it("handles the negative case", test(0x8004, -1));
        it("handles the zero case", test(0x8002, 0));
        it("handles the positive case", test(0x8001, 1));

        it("fails when none of the bits is set", test(0x8000, null));
        it("fails when two of the bits are set", test(0x8003, null));
        it("fails when all the bits are set", test(0x8007, null));
    });

    describe('formatConditionCode', () => {
        const test = (psr, expected) => () => {
            expect(Utils.formatConditionCode(psr)).to.equal(expected);
        };

        it("handles the negative case", test(0x8004, "N"));
        it("handles the zero case", test(0x8002, "Z"));
        it("handles the positive case", test(0x8001, "P"));

        it("fails when none of the bits is set", test(0x8000, "Invalid"));
        it("fails when two of the bits are set", test(0x8003, "Invalid"));
        it("fails when all the bits are set", test(0x8007, "Invalid"));
    });

});
<commit_msg>Refactor Utils tests to not destructure<commit_after>import {describe, it} from 'mocha';
import {expect} from 'chai';

import * as Utils from '../../src/core/utils';

describe('utils', () => {

    describe('parseNumber', () => {
        const success = (input, expected) => () => {
            expect(Utils.parseNumber(input)).to.equal(expected);
        };
        const failure = (input) => () => {
            expect(Utils.parseNumber(input)).to.be.NaN;
        };

        it("fails on the empty string", failure(''));
        it("parses a decimal zero", success('0', 0));
        it("parses a hex zero", success('x0', 0));
        it("parses a decimal value", success('123', 123));
        it("parses a negative decimal value", success('-123', -123));
        it("fails on double negations", failure('--123'));
        it("parses a hex value with 'x'", success('x90ab', 0x90AB));
        it("parses a capital hex value with 'x'", success('x90AB', 0x90AB));
        it("fails on a hex value with '0x'", failure('0x123'));
        it("parses a negative hex value", success('-x123', -0x123));
        it("fails on hex input with non-hex values", failure('xDEFG'));
        it("fails on decimal input with non-digits", failure('90AB'));
    });

    describe('toHexString', () => {
        const test = (input, expected) => () => {
            expect(Utils.toHexString(input)).to.equal(expected);
        };

        it("has a leading 'x' prefix", test(0x1234, 'x1234'));
        it("zero-pads three-digit strings", test(0x234, 'x0234'));
        it("zero-pads two-digit strings", test(0x34, 'x0034'));
        it("zero-pads one-digit strings", test(0x4, 'x0004'));
        it("zero-pads zero", test(0x0, 'x0000'));
        it("allows strings more than four digits without clipping",
            test(0x12345, 'x12345'));

        it("allows other padding values", () => {
            expect(Utils.toHexString(0x1234, 6)).to.equal('x001234');
        });

        it("allows other prefixes", () => {
            expect(Utils.toHexString(0x1234, undefined, 'y'))
                .to.equal('y1234');
        });

        it("allows other prefixes and padding values together", () => {
            expect(Utils.toHexString(0x1234, 6, 'y')).to.equal('y001234');
        });

    });

    describe('toInt16', () => {
        const test = (input, expected) => () => {
            expect(Utils.toInt16(input)).to.equal(expected);
        };

        it("is the identity for 0", test(0, 0));
        it("is the identity for 1", test(1, 1));
        it("is the identity for -1", test(-1, -1));

        it("takes 0x10000 to 0", test(0x10000, 0));
        it("takes 0x20000 to 0", test(0x20000, 0));
        it("takes 0x1FFFF to -1", test(0x1FFFF, -1));
        it("takes 0x54321 to 0x4321", test(0x54321, 0x4321));
        it("takes 0xABCDE to 0xBCDE - 0x10000",
            test(0xABCDE, 0xBCDE - 0x10000));
    });

    describe('toUint16', () => {
        const test = (input, expected) => () => {
            expect(Utils.toUint16(input)).to.equal(expected);
        };

        it("is the identity for 0", test(0, 0));
        it("is the identity for 1", test(1, 1));
        it("takes -1 to 0xFFFF", test(-1, 0xFFFF));

        it("takes 0x10000 to 0", test(0x10000, 0));
        it("takes 0x20000 to 0", test(0x20000, 0));
        it("takes 0x1FFFF to 0xFFFF", test(0x1FFFF, 0xFFFF));
        it("takes 0x54321 to 0x4321", test(0x54321, 0x4321));
        it("takes 0xABCDE to 0xBCDE", test(0xABCDE, 0xBCDE));
    });

    describe('signExtend16', () => {
        const test = (input, bits, expected) => () => {
            expect(Utils.signExtend16(input, bits)).to.equal(expected);
        };

        it("takes bits 00000 to 0",   test(0b00000, 5, 0));
        it("takes bits 00001 to 1",   test(0b00001, 5, 1));
        it("takes bits 00010 to 2",   test(0b00010, 5, 2));
        it("takes bits 01111 to 15",  test(0b01111, 5, 15));
        it("takes bits 10000 to -16", test(0b10000, 5, -16));
        it("takes bits 10001 to -15", test(0b10001, 5, -15));
        it("takes bits 10010 to -14", test(0b10010, 5, -14));
        it("takes bits 11111 to -1",  test(0b11111, 5, -1));
    });

    describe('getConditionCode', () => {
        const test = (psr, expected) => () => {
            expect(Utils.getConditionCode(psr)).to.equal(expected);
        };

        it("handles the negative case", test(0x8004, -1));
        it("handles the zero case", test(0x8002, 0));
        it("handles the positive case", test(0x8001, 1));

        it("fails when none of the bits is set", test(0x8000, null));
        it("fails when two of the bits are set", test(0x8003, null));
        it("fails when all the bits are set", test(0x8007, null));
    });

    describe('formatConditionCode', () => {
        const test = (psr, expected) => () => {
            expect(Utils.formatConditionCode(psr)).to.equal(expected);
        };

        it("handles the negative case", test(0x8004, "N"));
        it("handles the zero case", test(0x8002, "Z"));
        it("handles the positive case", test(0x8001, "P"));

        it("fails when none of the bits is set", test(0x8000, "Invalid"));
        it("fails when two of the bits are set", test(0x8003, "Invalid"));
        it("fails when all the bits are set", test(0x8007, "Invalid"));
    });

});
<|endoftext|>


<commit_before>/* Licensed under the MIT license:
 * http://www.opensource.org/licenses/mit-license.php
 * Copyright (c) 2010 Mr.doob, rhyolight, bebraw
 */
function settarget() {
    this.init();
}
settarget.prototype = {
    init: function () {
        var canvasDiameter = 1 + PROJECTIONTARGETRADIUS * 2;

        $('#main').append('<canvas id="projectionTarget"' +
            ' width="' + canvasDiameter + '"' +
            ' height="' + canvasDiameter + '"' +
            ' style="z-index:100;position:absolute"></canvas>')

        var overlayCanvas = new ProxyCanvas('projectionTarget');

        overlayCanvas.cross(new Point(10, 10), PROJECTIONTARGETRADIUS,
            PROJECTIONOVERLAYCOLOR, PROJECTIONOVERLAYALPHA);
    },
    onPress: function ( initialValue, targetValue ) {
        var topCoord = initialValue.y - PROJECTIONTARGETRADIUS;
        var leftCoord = initialValue.x - PROJECTIONTARGETRADIUS;

        $('#projectionTarget').css('top', topCoord + 'px').
            css('left', leftCoord + 'px');

        targetValue.x = initialValue.x;
        targetValue.y = initialValue.y;
    }
}
<commit_msg>Rendered initial location of projection target to right place.<commit_after>/* Licensed under the MIT license:
 * http://www.opensource.org/licenses/mit-license.php
 * Copyright (c) 2010 Mr.doob, rhyolight, bebraw
 */
function settarget() {
    this.init();
}
settarget.prototype = {
    init: function () {
        var canvasDiameter = 1 + PROJECTIONTARGETRADIUS * 2;

        $('#main').append('<canvas id="projectionTarget"' +
            ' width="' + canvasDiameter + '"' +
            ' height="' + canvasDiameter + '"' +
            ' style="z-index:100;position:absolute"></canvas>')

        var overlayCanvas = new ProxyCanvas('projectionTarget');

        overlayCanvas.cross(new Point(10, 10), PROJECTIONTARGETRADIUS,
            PROJECTIONOVERLAYCOLOR, PROJECTIONOVERLAYALPHA);

        this.onPress(new Point(), new Point());
    },
    onPress: function ( initialValue, targetValue ) {
        var topCoord = initialValue.y - PROJECTIONTARGETRADIUS;
        var leftCoord = initialValue.x - PROJECTIONTARGETRADIUS;

        $('#projectionTarget').css('top', topCoord + 'px').
            css('left', leftCoord + 'px');

        targetValue.x = initialValue.x;
        targetValue.y = initialValue.y;
    }
}
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { scanAction, changeIpAction, changePortAction } from 'redux/modules/scanner';
import TextField from 'material-ui/TextField';
import RaisedButton from 'material-ui/RaisedButton';

const primaryButton = true;

export class ScanView extends Component {
  static propTypes = {
    ip: PropTypes.string.isRequired,
    port: PropTypes.string.isRequired,
    result: PropTypes.array.isRequired,
    scanning: PropTypes.bool.isRequired,
    changeIpAction: PropTypes.func.isRequired,
    changePortAction: PropTypes.func.isRequired,
    scanAction: PropTypes.func.isRequired
  }

  constructor () {
    super();
    this.changeIp = this.changeIp.bind(this);
    this.changePort = this.changePort.bind(this);
  }

  get result () {
    if (this.props.scanning) {
      return 'scanning...';
    } else {
      return this.props.result;
    }
  }

  changeIp (event) {
    this.props.changeIpAction(event.target.value);
  }

  changePort (event) {
    this.props.changePortAction(event.target.value);
  }

  render () {
    return (

          <TextField
            hintText='192.168.1.0/24'
            floatingLabelText='IP-address'
            value={this.props.ip}
            onChange={this.changeIp}
          />
          <TextField
            hintText='1024-65535'
            floatingLabelText='Port (range)'
            value={this.props.port}
            onChange={this.changePort}
          />

          <RaisedButton
            label='Scan'
            primary={primaryButton}
            onClick={this.props.scanAction} />

Result:

            {this.result.map((line, index) => {
              return (

{line}

              );
            })}

    );
  }
}

const mapActionCreators = {
  scanAction,
  changeIpAction,
  changePortAction
};

const mapStateToProps = (state) => ({
  ip: state.scan.ip,
  port: state.scan.port,
  result: state.scan.result,
  scanning: state.scan.scanning
});

export default connect(mapStateToProps, mapActionCreators)(ScanView);
<commit_msg>Fix scanning loading error<commit_after>import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { scanAction, changeIpAction, changePortAction } from 'redux/modules/scanner';
import TextField from 'material-ui/TextField';
import RaisedButton from 'material-ui/RaisedButton';

const primaryButton = true;

export class ScanView extends Component {
  static propTypes = {
    ip: PropTypes.string.isRequired,
    port: PropTypes.string.isRequired,
    result: PropTypes.array.isRequired,
    scanning: PropTypes.bool.isRequired,
    changeIpAction: PropTypes.func.isRequired,
    changePortAction: PropTypes.func.isRequired,
    scanAction: PropTypes.func.isRequired
  }

  constructor () {
    super();
    this.changeIp = this.changeIp.bind(this);
    this.changePort = this.changePort.bind(this);
  }

  get result () {
    if (this.props.scanning) {
      return ['scanning...'];
    } else {
      return this.props.result;
    }
  }

  changeIp (event) {
    this.props.changeIpAction(event.target.value);
  }

  changePort (event) {
    this.props.changePortAction(event.target.value);
  }

  render () {
    return (

          <TextField
            hintText='192.168.1.0/24'
            floatingLabelText='IP-address'
            value={this.props.ip}
            onChange={this.changeIp}
          />
          <TextField
            hintText='1024-65535'
            floatingLabelText='Port (range)'
            value={this.props.port}
            onChange={this.changePort}
          />

          <RaisedButton
            label='Scan'
            primary={primaryButton}
            onClick={this.props.scanAction} />

Result:

            {this.result.map((line, index) => {
              return (

{line}

              );
            })}

    );
  }
}

const mapActionCreators = {
  scanAction,
  changeIpAction,
  changePortAction
};

const mapStateToProps = (state) => ({
  ip: state.scan.ip,
  port: state.scan.port,
  result: state.scan.result,
  scanning: state.scan.scanning
});

export default connect(mapStateToProps, mapActionCreators)(ScanView);
<|endoftext|>


<commit_before>var pull = require('pull-stream')
var paramap = require('pull-paramap')
var plugs = require('patchbay/plugs')
var cont = require('cont')
var ref = require('ssb-ref')

var sbot_log = plugs.first(exports.sbot_log = [])
var sbot_get = plugs.first(exports.sbot_get = [])
var sbot_user_feed = plugs.first(exports.sbot_user_feed = [])
var message_unbox = plugs.first(exports.message_unbox = [])
var get_id = plugs.first(exports.get_id = [])
var feed_summary = plugs.first(exports.feed_summary = [])

exports.screen_view = function (path) {
  if (path === '/notifications') {
    var oldest = null
    var id = get_id()
    var ids = {
      [id]: true
    }

    getFirstMessage(id, function (err, msg) {
      if (err) return console.error(err)
      if (!oldest || msg.value.timestamp < oldest) {
        oldest = msg.value.timestamp
      }
    })

    return feed_summary((opts) => {
      if (opts.old === false) {
        return pull(
          sbot_log(opts),
          unbox(),
          notifications(ids),
          pull.filter()
        )
      } else {
        return pull(
          sbot_log(opts),
          unbox(),
          notifications(ids),
          pull.filter(),
          pull.take(function (msg) {
            // abort stream after we pass the oldest messages of our feeds
            return !oldest || msg.value.timestamp > oldest
          })
        )
      }
    }, [], {
      windowSize: 200,
      filter: (group) => {
        return (
          ((group.message || group.type !== 'message') && (group.author !== id || group.digs.size)) || (
            group.repliesFrom && group.repliesFrom.size && (
              !group.repliesFrom.has(id) || group.repliesFrom.size > 1
            )
          )
        )
      }
    })
  }
}

function unbox () {
  return pull(
    pull.map(function (msg) {
      return msg.value && typeof msg.value.content === 'string'
        ? message_unbox(msg)
        : msg
    }),
    pull.filter(Boolean)
  )
}

function notifications (ourIds) {
  function linksToUs (link) {
    return link && link.link in ourIds
  }

  function isOurMsg (id, cb) {
    if (!id) return cb(null, false)
    if (typeof id === 'object' && typeof id.link === 'string') id = id.link
    if (!ref.isMsg(id)) return cb(null, false)
    sbot_get(id, function (err, msg) {
      if (err && err.name === 'NotFoundError') cb(null, false)
      else if (err) cb(err)
      else if (msg.content.type === 'issue' || msg.content.type === 'pull-request') {
        isOurMsg(msg.content.repo || msg.content.project, cb)
      } else {
        cb(err, msg.author in ourIds)
      }
    })
  }

  function isAnyOurMessage (msg, ids, cb) {
    cont.para(ids.map(function (id) {
      return function (cb) { isOurMsg(id, cb) }
    }))(function (err, results) {
      if (err) cb(err)
      else if (results.some(Boolean)) cb(null, msg)
      else cb()
    })
  }

  return paramap(function (msg, cb) {
    var c = msg.value && msg.value.content
    if (!c || typeof c !== 'object') return cb()
    if (msg.value.author in ourIds) return cb(null, msg)

    if (c.mentions && Array.isArray(c.mentions) && c.mentions.some(linksToUs)) {
      return cb(null, msg)
    }

    if (msg.private) {
      return cb(null, msg)
    }

    switch (c.type) {
      case 'post':
        if (c.branch || c.root) {
          return isAnyOurMessage(msg, [].concat(c.branch, c.root), cb)
        } else {
          return cb()
        }
      case 'contact':
        return cb(null, c.contact in ourIds ? msg : null)
      case 'vote':
        if (c.vote && c.vote.link)
          return isOurMsg(c.vote.link, function (err, isOurs) {
            cb(err, isOurs ? msg : null)
          })
          else return cb()
      case 'issue':
      case 'pull-request':
        return isOurMsg(c.project || c.repo, function (err, isOurs) {
          cb(err, isOurs ? msg : null)
        })
      case 'issue-edit':
        return isAnyOurMessage(msg, [c.issue].concat(c.issues), cb)
      default:
        cb()
    }
  }, 4)
}

function getFirstMessage (feedId, cb) {
  sbot_user_feed({id: feedId, gte: 0, limit: 1})(null, cb)
}
<commit_msg>whoops, fix stuff<commit_after>var pull = require('pull-stream')
var paramap = require('pull-paramap')
var plugs = require('patchbay/plugs')
var cont = require('cont')
var ref = require('ssb-ref')

var sbot_log = plugs.first(exports.sbot_log = [])
var sbot_get = plugs.first(exports.sbot_get = [])
var sbot_user_feed = plugs.first(exports.sbot_user_feed = [])
var message_unbox = plugs.first(exports.message_unbox = [])
var get_id = plugs.first(exports.get_id = [])
var feed_summary = plugs.first(exports.feed_summary = [])

exports.screen_view = function (path) {
  if (path === '/notifications') {
    var oldest = null
    var id = get_id()
    var ids = {
      [id]: true
    }

    getFirstMessage(id, function (err, msg) {
      if (err) return console.error(err)
      if (!oldest || msg.value.timestamp < oldest) {
        oldest = msg.value.timestamp
      }
    })

    return feed_summary((opts) => {
      if (opts.old === false) {
        return pull(
          sbot_log(opts),
          unbox(),
          notifications(ids),
          pull.filter()
        )
      } else {
        return pull(
          sbot_log(opts),
          unbox(),
          notifications(ids),
          pull.filter(),
          pull.take(function (msg) {
            // abort stream after we pass the oldest messages of our feeds
            return !oldest || msg.value.timestamp > oldest
          })
        )
      }
    }, [], {
      windowSize: 200,
      filter: (group) => {
        return (
          ((group.message || group.type !== 'message') && (group.author !== id || group.digs && group.digs.size)) || (
            group.repliesFrom && group.repliesFrom.size && (
              !group.repliesFrom.has(id) || group.repliesFrom.size > 1
            )
          )
        )
      }
    })
  }
}

function unbox () {
  return pull(
    pull.map(function (msg) {
      return msg.value && typeof msg.value.content === 'string'
        ? message_unbox(msg)
        : msg
    }),
    pull.filter(Boolean)
  )
}

function notifications (ourIds) {
  function linksToUs (link) {
    return link && link.link in ourIds
  }

  function isOurMsg (id, cb) {
    if (!id) return cb(null, false)
    if (typeof id === 'object' && typeof id.link === 'string') id = id.link
    if (!ref.isMsg(id)) return cb(null, false)
    sbot_get(id, function (err, msg) {
      if (err && err.name === 'NotFoundError') cb(null, false)
      else if (err) cb(err)
      else if (msg.content.type === 'issue' || msg.content.type === 'pull-request') {
        isOurMsg(msg.content.repo || msg.content.project, cb)
      } else {
        cb(err, msg.author in ourIds)
      }
    })
  }

  function isAnyOurMessage (msg, ids, cb) {
    cont.para(ids.map(function (id) {
      return function (cb) { isOurMsg(id, cb) }
    }))(function (err, results) {
      if (err) cb(err)
      else if (results.some(Boolean)) cb(null, msg)
      else cb()
    })
  }

  return paramap(function (msg, cb) {
    var c = msg.value && msg.value.content
    if (!c || typeof c !== 'object') return cb()
    if (msg.value.author in ourIds) return cb(null, msg)

    if (c.mentions && Array.isArray(c.mentions) && c.mentions.some(linksToUs)) {
      return cb(null, msg)
    }

    if (msg.private) {
      return cb(null, msg)
    }

    switch (c.type) {
      case 'post':
        if (c.branch || c.root) {
          return isAnyOurMessage(msg, [].concat(c.branch, c.root), cb)
        } else {
          return cb()
        }
      case 'contact':
        return cb(null, c.contact in ourIds ? msg : null)
      case 'vote':
        if (c.vote && c.vote.link)
          return isOurMsg(c.vote.link, function (err, isOurs) {
            cb(err, isOurs ? msg : null)
          })
          else return cb()
      case 'issue':
      case 'pull-request':
        return isOurMsg(c.project || c.repo, function (err, isOurs) {
          cb(err, isOurs ? msg : null)
        })
      case 'issue-edit':
        return isAnyOurMessage(msg, [c.issue].concat(c.issues), cb)
      default:
        cb()
    }
  }, 4)
}

function getFirstMessage (feedId, cb) {
  sbot_user_feed({id: feedId, gte: 0, limit: 1})(null, cb)
}
<|endoftext|>


<commit_before>'use strict';

/*
 * Copyright 2014 Next Century Corporation
 * 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.
 *
 */

/**
 * This Angular JS directive adds a basic widget that contain Neon visualizations.  Widgets are
 * defined by an Angular Gridster configuration object and include resize handlers, a resize bar,
 * and a simple icon for expanding to a max column/row size.
 * @example
 *    <div visualization-widget gridster-configs="items" gridster-config-index="0"></div>
 *
 * @namespace neonDemo.directives
 * @class visulizationWidget
 * @constructor
 */
angular.module('neonDemo.directives').directive('visualizationWidget', ["config", "$compile", function(config, $compile) {
    var MAXIMIZED_COLUMN_SIZE = 6;
    var MAXIMIZED_ROW_SIZE = 4;

    return {
        restrict: 'A',
        scope: {
            gridsterConfigs: "=",
            gridsterConfigIndex: "="
        },
        template: '

' +
                '' +
                '   ' +
                '' +
                '' +
                '   ' +
                '' +
                '

',
        // templateUrl: "partials/directives/visualizationWidget.html",
        link: function($scope, $element) {
            // Create our widget.  Here, we are assuming the visualization is
            // implementated as an attribute directive.
            var widgetElement = document.createElement("div");
            widgetElement.setAttribute($scope.gridsterConfigs[$scope.gridsterConfigIndex].type, "");

            if(config.hideAdvancedOptions) {
                widgetElement.setAttribute("hide-advanced-options", true);
            }
            if(config.hideHeader) {
                widgetElement.setAttribute("hide-header", true);
            }
            if(config.hideCloseButton) {
                $scope.hideCloseButton = config.hideCloseButton;
            }

            // Pass along any bindings.
            if($scope.gridsterConfigs[$scope.gridsterConfigIndex] &&
                $scope.gridsterConfigs[$scope.gridsterConfigIndex].bindings) {
                var bindings = $scope.gridsterConfigs[$scope.gridsterConfigIndex].bindings;
                for(var prop in bindings) {
                    if(bindings.hasOwnProperty(prop)) {
                        widgetElement.setAttribute(prop, bindings[prop]);
                    }
                }
            }

            $element.append($compile(widgetElement)($scope));

            /**
             * Toggles the visualization widget between default and maximized views.
             * @method toggleSize
             */
            $scope.toggleSize = function() {
                if($scope.oldSize) {
                    XDATA.userALE.log({
                        activity: "alter",
                        action: "click",
                        elementId: "workspace",
                        elementType: "workspace",
                        elementSub: "layout",
                        elementGroup: "top",
                        source: "user",
                        tags: ["visualization", "minimize"]
                    });
                    $scope.gridsterConfigs[$scope.gridsterConfigIndex].sizeX = $scope.oldSize.sizeX;
                    $scope.gridsterConfigs[$scope.gridsterConfigIndex].sizeY = $scope.oldSize.sizeY;
                    $scope.gridsterConfigs[$scope.gridsterConfigIndex].col = $scope.oldSize.col;
                    $scope.gridsterConfigs[$scope.gridsterConfigIndex].row = $scope.oldSize.row;
                    $scope.oldSize = null;
                } else {
                    XDATA.userALE.log({
                        activity: "alter",
                        action: "click",
                        elementId: "workspace",
                        elementType: "workspace",
                        elementSub: "layout",
                        elementGroup: "top",
                        source: "user",
                        tags: ["visualization", "maximize"]
                    });
                    $scope.oldSize = {
                        col: $scope.gridsterConfigs[$scope.gridsterConfigIndex].col,
                        row: $scope.gridsterConfigs[$scope.gridsterConfigIndex].row,
                        sizeX: $scope.gridsterConfigs[$scope.gridsterConfigIndex].sizeX,
                        sizeY: $scope.gridsterConfigs[$scope.gridsterConfigIndex].sizeY
                    };
                    $scope.gridsterConfigs[$scope.gridsterConfigIndex].col = 0;
                    $scope.gridsterConfigs[$scope.gridsterConfigIndex].sizeX = MAXIMIZED_COLUMN_SIZE;
                    $scope.gridsterConfigs[$scope.gridsterConfigIndex].sizeY = Math.max(MAXIMIZED_ROW_SIZE,
                        $scope.gridsterConfigs[$scope.gridsterConfigIndex].sizeY);
                }
            };

            /**
             * Remove ourselves from the visualization list.
             * @method remove
             */
            $scope.remove = function() {
                XDATA.userALE.log({
                        activity: "remove",
                        action: "click",
                        elementId: "workspace",
                        elementType: "workspace",
                        elementSub: $scope.gridsterConfigs[$scope.gridsterConfigIndex].type,
                        elementGroup: "top",
                        source: "user",
                        tags: ["remove", $scope.gridsterConfigs[$scope.gridsterConfigIndex].type]
                    });
                $scope.gridsterConfigs.splice($scope.gridsterConfigIndex, 1);
            };
        }
    };
}]);
<commit_msg>NEON-1616 Fix expand icon to fit visualization to window using config value<commit_after>'use strict';

/*
 * Copyright 2014 Next Century Corporation
 * 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.
 *
 */

/**
 * This Angular JS directive adds a basic widget that contain Neon visualizations.  Widgets are
 * defined by an Angular Gridster configuration object and include resize handlers, a resize bar,
 * and a simple icon for expanding to a max column/row size.
 * @example
 *    <div visualization-widget gridster-configs="items" gridster-config-index="0"></div>
 *
 * @namespace neonDemo.directives
 * @class visulizationWidget
 * @constructor
 */
angular.module('neonDemo.directives').directive('visualizationWidget', ["config", "$compile", function(config, $compile) {

    return {
        restrict: 'A',
        scope: {
            gridsterConfigs: "=",
            gridsterConfigIndex: "="
        },
        template: '

' +
                '' +
                '   ' +
                '' +
                '' +
                '   ' +
                '' +
                '

',
        // templateUrl: "partials/directives/visualizationWidget.html",
        link: function($scope, $element) {
            var MAXIMIZED_COLUMN_SIZE = config.gridsterColumns || 6;
            var MAXIMIZED_ROW_SIZE = MAXIMIZED_COLUMN_SIZE * (2 / 3);

            // Create our widget.  Here, we are assuming the visualization is
            // implementated as an attribute directive.
            var widgetElement = document.createElement("div");
            widgetElement.setAttribute($scope.gridsterConfigs[$scope.gridsterConfigIndex].type, "");

            if(config.hideAdvancedOptions) {
                widgetElement.setAttribute("hide-advanced-options", true);
            }
            if(config.hideHeader) {
                widgetElement.setAttribute("hide-header", true);
            }
            if(config.hideCloseButton) {
                $scope.hideCloseButton = config.hideCloseButton;
            }

            // Pass along any bindings.
            if($scope.gridsterConfigs[$scope.gridsterConfigIndex] &&
                $scope.gridsterConfigs[$scope.gridsterConfigIndex].bindings) {
                var bindings = $scope.gridsterConfigs[$scope.gridsterConfigIndex].bindings;
                for(var prop in bindings) {
                    if(bindings.hasOwnProperty(prop)) {
                        widgetElement.setAttribute(prop, bindings[prop]);
                    }
                }
            }

            $element.append($compile(widgetElement)($scope));

            /**
             * Toggles the visualization widget between default and maximized views.
             * @method toggleSize
             */
            $scope.toggleSize = function() {
                if($scope.oldSize) {
                    XDATA.userALE.log({
                        activity: "alter",
                        action: "click",
                        elementId: "workspace",
                        elementType: "workspace",
                        elementSub: "layout",
                        elementGroup: "top",
                        source: "user",
                        tags: ["visualization", "minimize"]
                    });
                    $scope.gridsterConfigs[$scope.gridsterConfigIndex].sizeX = $scope.oldSize.sizeX;
                    $scope.gridsterConfigs[$scope.gridsterConfigIndex].sizeY = $scope.oldSize.sizeY;
                    $scope.gridsterConfigs[$scope.gridsterConfigIndex].col = $scope.oldSize.col;
                    $scope.gridsterConfigs[$scope.gridsterConfigIndex].row = $scope.oldSize.row;
                    $scope.oldSize = null;
                } else {
                    XDATA.userALE.log({
                        activity: "alter",
                        action: "click",
                        elementId: "workspace",
                        elementType: "workspace",
                        elementSub: "layout",
                        elementGroup: "top",
                        source: "user",
                        tags: ["visualization", "maximize"]
                    });
                    $scope.oldSize = {
                        col: $scope.gridsterConfigs[$scope.gridsterConfigIndex].col,
                        row: $scope.gridsterConfigs[$scope.gridsterConfigIndex].row,
                        sizeX: $scope.gridsterConfigs[$scope.gridsterConfigIndex].sizeX,
                        sizeY: $scope.gridsterConfigs[$scope.gridsterConfigIndex].sizeY
                    };
                    $scope.gridsterConfigs[$scope.gridsterConfigIndex].col = 0;
                    $scope.gridsterConfigs[$scope.gridsterConfigIndex].sizeX = MAXIMIZED_COLUMN_SIZE;
                    $scope.gridsterConfigs[$scope.gridsterConfigIndex].sizeY = Math.max(MAXIMIZED_ROW_SIZE,
                        $scope.gridsterConfigs[$scope.gridsterConfigIndex].sizeY);
                }
            };

            /**
             * Remove ourselves from the visualization list.
             * @method remove
             */
            $scope.remove = function() {
                XDATA.userALE.log({
                        activity: "remove",
                        action: "click",
                        elementId: "workspace",
                        elementType: "workspace",
                        elementSub: $scope.gridsterConfigs[$scope.gridsterConfigIndex].type,
                        elementGroup: "top",
                        source: "user",
                        tags: ["remove", $scope.gridsterConfigs[$scope.gridsterConfigIndex].type]
                    });
                $scope.gridsterConfigs.splice($scope.gridsterConfigIndex, 1);
            };
        }
    };
}]);
<|endoftext|>


<commit_before>define(['underscore', 'backbone', 'core', 'utils',
    'widgets/Text',
    'less!chf-stash.less'], function (_, Backbone, CherryForms, Utils) {
    "use strict";
    var Models = CherryForms.Models,
        Schema = Models.Schema,

        Fields = CherryForms.Fields,
        Field = Fields.Field,
        TextField = Fields.Text,

        Widgets = CherryForms.Widgets,
        Widget = Widgets.Widget,
        TextWidget = Widgets.Text,

        Templates = CherryForms.Templates,
        Events = CherryForms.Events,

        Unset = Utils.Unset,
        isUnset = Utils.isUnset,

        ResourceType = Models.Document.extend({
            tagged: function (tags) {
                if (_.isArray(tags) && tags.length) {
                    return Boolean(_.intersection(this.get('tags'), tags).length);
                }
                return true;
            },

            filter: function (filter) {
                var weight, matched, index, chars, pattern, re, replace,
                    id = String(this.id);
                if (filter) {
                    index = id.indexOf(filter);
                    if (index > -1) {
                        if (index === 0) {
                            weight = 2;
                        } else {
                            weight = index / id.length + 1;
                        }
                        matched = id.replace(filter, '' + filter + '');
                    } else {
                        chars = filter.replace(/\s+/, '').split('');
                        pattern = _.map(chars, function (c) {
                            return '(' + c + ')';
                        }).join(' (.*) ').split(/\s+/);
                        re = new RegExp(pattern.join(''), 'i');
                        if (re.test(id)) {
                            weight = 1;
                            replace = _.map(pattern, function (s, i) {
                                var r = '$' + (i + 1);
                                if (i % 2 === 0) {
                                    return '' + r + '';
                                }
                                return r;
                            }).join('');
                            matched = id.replace(re, replace);
                        } else {
                            weight = 0;
                            matched = id;
                        }
                    }
                } else {
                    weight = 1;
                    matched = id;
                }
                this.set({weight: weight, matched: matched});
                return weight;
            }
        }),

        DocumentsCollection = Models.DocumentsCollection,
        ResourcesCollection = DocumentsCollection.extend({
            model: ResourceType,

            initialize: function () {
                DocumentsCollection.prototype.initialize.apply(this, arguments);
                this
                    .on('add', this.resetTags, this)
                    .on('remove', this.resetTags, this)
                    .on('reset', this.resetTags, this);
            },

            resetTags: function () {
                this._tags = undefined;
            },

            getTags: function () {
                if (_.isUndefined(this._tags)) {
                    var tagSet = {};
                    this.each(function (resource) {
                        var tags = resource.get('tags');
                        if (_.isArray(tags)) {
                            _.each(tags, function (tag) {
                                tagSet[tag] = true;
                            });
                        }
                    });
                    this._tags = _.keys(tagSet);
                    this._tags.sort();
                }
                return this._tags;
            },

            filterResources: function (tags, filter) {
                var filtered = [];
                this.each(function (resource) {
                    if (resource.tagged(tags)) {
                        var weight = resource.filter(filter);
                        if (weight) {
                            filtered.push({
                                weight: weight,
                                resource: resource
                            });
                        }
                    }
                }, this);
                filtered = _.sortBy(filtered, function (entry) {
                    return (2 - entry.weight).toFixed(2) + '_' + entry.resource.id;
                });
                return _.pluck(filtered, 'resource');
            }
        }),
        resources = {},

        Stash = Backbone.Model.extend({
            each: function (iterator, context) {
                _.each(this.attributes, iterator, context);
            },

            map: function (iterator, context) {
                return _.map(this.attributes, iterator, context);
            }
        }),

        STASH_VALUE_PATTERN = /^\d+(:\d+)?([%!#*]\d+)?$/,
        StashItemField = TextField.extend({
            pattern: /^\d+(:\d+)?([%!#*]\d+)?$/
        }),

        StashItemWidget = TextWidget.extend({
            FieldModel: StashItemField,
            className: 'chf-field-item',
            template: _.template(
                '
' +
                    '
{{ label }}
' +
                    '
' +
                        '
' +
                            '<input type="text" id="{{ input_id }}" value="{{ value }}" class="input-medium">' +
                            '' +
                            '' +
                        '
' +
                    '
' +
                '
'
            ),

            events: function () {
                var events = TextWidget.prototype.events.call(this);
                events['click .' + this.model.get('clear_button')] = 'clearValue';
                events['click .' + this.model.get('add_button')] = 'addValue';
                return events;
            },

            clearValue: function () {
                this.model.unsetValue();
                this.$el.detach();
            },

            addValue: function () {
                var num = Number(this.model.value);
                if (_.isNumber(num)) {
                    this.model.set('value', num + 1);
                }
            }
        }),

        CreateItemWidget = TextWidget.extend({
            template: _.template('
' +
                '
' +
                    '<input type="text" id="{{ input_id }}" class="input-medium" placeholder="Enter resource id...">' +
                    '' +
                '
' +
            '
')
        }),

        ResourcesFilterWidget = TextWidget.extend({
            events: function (events) {
                events = events || {};
                events['keyup #' + this.getInputID()] = 'onChange';
                return events;
            }
        }),

        StashField = Fields.Stash = Field.extend({
            defaults: function () {
                return (_.extend({}, Field.prototype.defaults.call(this), {
                    'editor_class': 'chf-field-editor',
                    'selector_class': 'chf-field-selector',
                    'selector_content_class': 'chf-field-selector',
                    'tags_class': 'chf-field-tags',
                    'toggle_class': 'chf-stash-toggle',
                    'filters_class': 'chf-stash-filters',
                    'filter_class': 'chf-stash-filter',
                    'resources_class': 'chf-stash-resources',
                    'resource_class': 'chf-field-item',
                    'create_class': 'chf-stash-create',
                    'item_class': 'chf-field-item',
                    'clear_button': 'chf-btn-clear',
                    'add_button': 'chf-btn-add',
                    'resources_url': '/widgets/resources'
                }));
            },

            initialize: function () {
                this.schema = new Schema();
                this.schema
                    .on(Events.FIELD_CHANGE, this._onFieldChange, this)
                    .on(Events.FIELD_CLEAR, this._onFieldClear, this);
                this.createItemField = new Fields.Identifier(_.pick(this.toJSON(), 'add_button'));
                Field.prototype.initialize.apply(this, arguments);

                // Init resources collection
                var url = this.get('resources_url'),
                    resourcesCollection = resources[url];
                if (_.isUndefined(resourcesCollection)) {
                    resourcesCollection = new ResourcesCollection({'url': url});
                }
                this.resourcesCollection = resourcesCollection;
            },

            processValue: function () {
                var value = this.get('value'),
                    input_id = this.get('input_id');
                if (_.isUndefined(value)) {
                    this.unsetValue();
                } else {
                    this.value = new Stash(value);
                    this.value.on('change', this._onStashChange, this);
                    this.schema.reset(this.value.map(function (value, res) {
                        return this._createItemField(res, value);
                    }, this));
                }
                this.trigger(Events.FIELD_CHANGE, this);
            },

            unsetValue: function () {
                if (!isUnset(this.value)) {
                    this.value = new Unset();
                    this.schema.reset();
                    this.trigger(Events.FIELD_CLEAR, this);
                }
            },

            _createItemField: function (res, value) {
                return new StashItemField(_.extend({
                    field: res,
                    value: value
                }, _.pick(this.toJSON(), 'add_button', 'clear_button')));
            },

            _onFieldChange: function (field) {
                var res = field.get('field');
                this.value.set(res, field.value, {silent: true});
                this.trigger(Events.FIELD_CHANGE, this);
            },

            _onFieldClear: function (field) {
                this.value.unset(field.get('field'), {silent: true});
                this.schema.remove(field);
                this.trigger(Events.FIELD_CHANGE, this);
            },

            _onStashChange: function () {
                var schema = this.schema,
                    field;
                this.value.each(function (value, res) {
                    field = schema.find(function (field) {
                        return field.get('field') === res;
                    });
                    if (_.isUndefined(field)) {
                        schema.add(this._createItemField(res, value));
                    }
                }, this);
            }
        });

    Widgets.Stash = Widget.extend({
        FieldModel: StashField,
        template: _.template('
' +
            '{{ label }}' +
            '
' +
                '
' +
                    '

' +
                '
' +
                '
' +
                    '
' +
                        '
' +
                            'New' +
                            'Filter' +
                            'Tags' +
                        '
' +
                    '
' +
                    '

' +
                '
' +
            '
' +
        '
'),

        events: function () {
            var events = {};
            events['click .' + this.model.get('create_class') + ' .' + this.model.get('add_button')] = '_createItem';
            return events;
        },

        getEditor: function () {

        },

        render: function () {
            Widget.prototype.render.call(this);
            var schema = this.model.schema
                .on('add', this._onItemFieldAdded, this)
                .on('reset', this._onSchemaReset, this);
            this.$editor = this.$('.' + this.model.get('editor_class')).append(
                schema.map(this._renderItemField)
            );
            this.createItemWidget = new CreateItemWidget({
                model: this.model.createItemField,
                el: this.$('.' + this.model.get('create_class'))
            });
            this.createItemWidget.render();
        },

        _renderItemField: function (field) {
            var widget = new StashItemWidget({
                model: field,
                id: field.id
            });
            return widget.render().el;
        },

        _createItem: function () {
            var newItemID = this.model.createItemField.value;
            if (!isUnset(newItemID)) {
                this.model.value.set(newItemID, 1);
            }
        },

        _onItemFieldAdded: function (field) {
            this.$editor.append(this._renderItemField(field));
        },

        _onSchemaReset: function () {
            this.$editor
                .empty()
                .append(this.schema.map(this._renderItemField, this));
        }
    });

    Templates.Stash = _.template(
        '{{ label }}
' +
                '
' +
                    '

' +
                '
' +
                '
' +
                    '
' +
                        '
' +
                            'Filter' +
                            'Tags' +
                        '
' +
                    '
' +
                    '

' +
                    '

' +
                '
' +
            '
'
    );

    Templates.StashResourceFilter = _.template(
        '<input type="text" id="{{ input_id }}" value="{{ value }}" ' +
            'class="input-block-level" placeholder="Type here to filter resources...">'
    );
    Templates.StashResource = _.template('{{ matched }}');
});<commit_msg>0.6.2 - small fixes to css and styles in stash.js<commit_after>define(['underscore', 'backbone', 'core', 'utils',
    'widgets/Text',
    'less!chf-stash.less'], function (_, Backbone, CherryForms, Utils) {
    "use strict";
    var Models = CherryForms.Models,
        Schema = Models.Schema,

        Fields = CherryForms.Fields,
        Field = Fields.Field,
        TextField = Fields.Text,

        Widgets = CherryForms.Widgets,
        Widget = Widgets.Widget,
        TextWidget = Widgets.Text,

        Templates = CherryForms.Templates,
        Events = CherryForms.Events,

        Unset = Utils.Unset,
        isUnset = Utils.isUnset,

        ResourceType = Models.Document.extend({
            tagged: function (tags) {
                if (_.isArray(tags) && tags.length) {
                    return Boolean(_.intersection(this.get('tags'), tags).length);
                }
                return true;
            },

            filter: function (filter) {
                var weight, matched, index, chars, pattern, re, replace,
                    id = String(this.id);
                if (filter) {
                    index = id.indexOf(filter);
                    if (index > -1) {
                        if (index === 0) {
                            weight = 2;
                        } else {
                            weight = index / id.length + 1;
                        }
                        matched = id.replace(filter, '' + filter + '');
                    } else {
                        chars = filter.replace(/\s+/, '').split('');
                        pattern = _.map(chars, function (c) {
                            return '(' + c + ')';
                        }).join(' (.*) ').split(/\s+/);
                        re = new RegExp(pattern.join(''), 'i');
                        if (re.test(id)) {
                            weight = 1;
                            replace = _.map(pattern, function (s, i) {
                                var r = '$' + (i + 1);
                                if (i % 2 === 0) {
                                    return '' + r + '';
                                }
                                return r;
                            }).join('');
                            matched = id.replace(re, replace);
                        } else {
                            weight = 0;
                            matched = id;
                        }
                    }
                } else {
                    weight = 1;
                    matched = id;
                }
                this.set({weight: weight, matched: matched});
                return weight;
            }
        }),

        DocumentsCollection = Models.DocumentsCollection,
        ResourcesCollection = DocumentsCollection.extend({
            model: ResourceType,

            initialize: function () {
                DocumentsCollection.prototype.initialize.apply(this, arguments);
                this
                    .on('add', this.resetTags, this)
                    .on('remove', this.resetTags, this)
                    .on('reset', this.resetTags, this);
            },

            resetTags: function () {
                this._tags = undefined;
            },

            getTags: function () {
                if (_.isUndefined(this._tags)) {
                    var tagSet = {};
                    this.each(function (resource) {
                        var tags = resource.get('tags');
                        if (_.isArray(tags)) {
                            _.each(tags, function (tag) {
                                tagSet[tag] = true;
                            });
                        }
                    });
                    this._tags = _.keys(tagSet);
                    this._tags.sort();
                }
                return this._tags;
            },

            filterResources: function (tags, filter) {
                var filtered = [];
                this.each(function (resource) {
                    if (resource.tagged(tags)) {
                        var weight = resource.filter(filter);
                        if (weight) {
                            filtered.push({
                                weight: weight,
                                resource: resource
                            });
                        }
                    }
                }, this);
                filtered = _.sortBy(filtered, function (entry) {
                    return (2 - entry.weight).toFixed(2) + '_' + entry.resource.id;
                });
                return _.pluck(filtered, 'resource');
            }
        }),
        resources = {},

        Stash = Backbone.Model.extend({
            each: function (iterator, context) {
                _.each(this.attributes, iterator, context);
            },

            map: function (iterator, context) {
                return _.map(this.attributes, iterator, context);
            }
        }),

        STASH_VALUE_PATTERN = /^\d+(:\d+)?([%!#*]\d+)?$/,
        StashItemField = TextField.extend({
            pattern: /^\d+(:\d+)?([%!#*]\d+)?$/
        }),

        StashItemWidget = TextWidget.extend({
            FieldModel: StashItemField,
            className: 'chf-field-item',
            template: _.template(
                '
' +
                    '
{{ label }}
' +
                    '
' +
                        '
' +
                            '<input type="text" id="{{ input_id }}" value="{{ value }}" class="input-small">' +
                            '' +
                            '' +
                        '
' +
                    '
' +
                '
'
            ),

            events: function () {
                var events = TextWidget.prototype.events.call(this);
                events['click .' + this.model.get('clear_button')] = 'clearValue';
                events['click .' + this.model.get('add_button')] = 'addValue';
                return events;
            },

            clearValue: function () {
                this.model.unsetValue();
                this.$el.detach();
            },

            addValue: function () {
                var num = Number(this.model.value);
                if (_.isNumber(num)) {
                    this.model.set('value', num + 1);
                }
            }
        }),

        CreateItemWidget = TextWidget.extend({
            template: _.template('
' +
                '
' +
                    '<input type="text" id="{{ input_id }}" class="input-medium" placeholder="Enter resource id...">' +
                    '' +
                '
' +
            '
')
        }),

        ResourcesFilterWidget = TextWidget.extend({
            events: function (events) {
                events = events || {};
                events['keyup #' + this.getInputID()] = 'onChange';
                return events;
            }
        }),

        StashField = Fields.Stash = Field.extend({
            defaults: function () {
                return (_.extend({}, Field.prototype.defaults.call(this), {
                    'editor_class': 'chf-field-editor',
                    'selector_class': 'chf-field-selector',
                    'selector_content_class': 'chf-field-selector',
                    'tags_class': 'chf-field-tags',
                    'toggle_class': 'chf-stash-toggle',
                    'filters_class': 'chf-stash-filters',
                    'filter_class': 'chf-stash-filter',
                    'resources_class': 'chf-stash-resources',
                    'resource_class': 'chf-field-item',
                    'create_class': 'chf-stash-create',
                    'item_class': 'chf-field-item',
                    'clear_button': 'chf-btn-clear',
                    'add_button': 'chf-btn-add',
                    'resources_url': '/widgets/resources'
                }));
            },

            initialize: function () {
                this.schema = new Schema();
                this.schema
                    .on(Events.FIELD_CHANGE, this._onFieldChange, this)
                    .on(Events.FIELD_CLEAR, this._onFieldClear, this);
                this.createItemField = new Fields.Identifier(_.pick(this.toJSON(), 'add_button'));
                Field.prototype.initialize.apply(this, arguments);

                // Init resources collection
                var url = this.get('resources_url'),
                    resourcesCollection = resources[url];
                if (_.isUndefined(resourcesCollection)) {
                    resourcesCollection = new ResourcesCollection({'url': url});
                }
                this.resourcesCollection = resourcesCollection;
            },

            processValue: function () {
                var value = this.get('value'),
                    input_id = this.get('input_id');
                if (_.isUndefined(value)) {
                    this.unsetValue();
                } else {
                    this.value = new Stash(value);
                    this.value.on('change', this._onStashChange, this);
                    this.schema.reset(this.value.map(function (value, res) {
                        return this._createItemField(res, value);
                    }, this));
                }
                this.trigger(Events.FIELD_CHANGE, this);
            },

            unsetValue: function () {
                if (!isUnset(this.value)) {
                    this.value = new Unset();
                    this.schema.reset();
                    this.trigger(Events.FIELD_CLEAR, this);
                }
            },

            _createItemField: function (res, value) {
                return new StashItemField(_.extend({
                    field: res,
                    value: value
                }, _.pick(this.toJSON(), 'add_button', 'clear_button')));
            },

            _onFieldChange: function (field) {
                var res = field.get('field');
                this.value.set(res, field.value, {silent: true});
                this.trigger(Events.FIELD_CHANGE, this);
            },

            _onFieldClear: function (field) {
                this.value.unset(field.get('field'), {silent: true});
                this.schema.remove(field);
                this.trigger(Events.FIELD_CHANGE, this);
            },

            _onStashChange: function () {
                var schema = this.schema,
                    field;
                this.value.each(function (value, res) {
                    field = schema.find(function (field) {
                        return field.get('field') === res;
                    });
                    if (_.isUndefined(field)) {
                        schema.add(this._createItemField(res, value));
                    }
                }, this);
            }
        });

    Widgets.Stash = Widget.extend({
        FieldModel: StashField,
        template: _.template('
' +
            '{{ label }}' +
            '
' +
                '
' +
                    '

' +
                '
' +
                '
' +
                    '
' +
                        '
' +
                            'New' +
                            'Filter' +
                            'Tags' +
                        '
' +
                    '
' +
                    '

' +
                '
' +
            '
' +
        '
'),

        events: function () {
            var events = {};
            events['click .' + this.model.get('create_class') + ' .' + this.model.get('add_button')] = '_createItem';
            return events;
        },

        getEditor: function () {

        },

        render: function () {
            Widget.prototype.render.call(this);
            var schema = this.model.schema
                .on('add', this._onItemFieldAdded, this)
                .on('reset', this._onSchemaReset, this);
            this.$editor = this.$('.' + this.model.get('editor_class')).append(
                schema.map(this._renderItemField)
            );
            this.createItemWidget = new CreateItemWidget({
                model: this.model.createItemField,
                el: this.$('.' + this.model.get('create_class'))
            });
            this.createItemWidget.render();
        },

        _renderItemField: function (field) {
            var widget = new StashItemWidget({
                model: field,
                id: field.id
            });
            return widget.render().el;
        },

        _createItem: function () {
            var newItemID = this.model.createItemField.value;
            if (!isUnset(newItemID)) {
                this.model.value.set(newItemID, 1);
            }
        },

        _onItemFieldAdded: function (field) {
            this.$editor.append(this._renderItemField(field));
        },

        _onSchemaReset: function () {
            this.$editor
                .empty()
                .append(this.schema.map(this._renderItemField, this));
        }
    });

    Templates.Stash = _.template(
        '{{ label }}
' +
                '
' +
                    '

' +
                '
' +
                '
' +
                    '
' +
                        '
' +
                            'Filter' +
                            'Tags' +
                        '
' +
                    '
' +
                    '

' +
                    '

' +
                '
' +
            '
'
    );

    Templates.StashResourceFilter = _.template(
        '<input type="text" id="{{ input_id }}" value="{{ value }}" ' +
            'class="input-block-level" placeholder="Type here to filter resources...">'
    );
    Templates.StashResource = _.template('{{ matched }}');
});<|endoftext|>


<commit_before>/**
 * Miscellaneous commands
 */

var fs = require('fs');
var moment = require('moment');
var request = require('request');

var messages = [
	"has vanished into nothingness!",
	"used Explosion!",
	"fell into the void.",
	"went into a cave without a repel!",
	"has left the building.",
	"was forced to give StevoDuhHero's mom an oil massage!",
	"was hit by Magikarp's Revenge!",
	"ate a bomb!",
	"is blasting off again!",
	"(Quit: oh god how did this get here i am not good with computer)",
	"was unfortunate and didn't get a cool message.",
	"{{user}}'s mama accidently kicked {{user}} from the server!"
];

exports.commands = {
	stafflist: 'authority',
	auth: 'authority',
	authlist: 'authority',
	authority: function (target, room, user, connection) {
		var rankLists = {};
		var ranks = Object.keys(Config.groups);
		for (var u in Users.usergroups) {
			var rank = Users.usergroups[u].charAt(0);
			// In case the usergroups.csv file is not proper, we check for the server ranks.
			if (ranks.indexOf(rank) > -1) {
				var name = Users.usergroups[u].substr(1);
				if (!rankLists[rank]) rankLists[rank] = [];
				if (name) rankLists[rank].push(((Users.getExact(name) && Users.getExact(name).connected) ? '**' + name + '**' : name));
			}
		}

		var buffer = [];
		Object.keys(rankLists).sort(function (a, b) {
			return (Config.groups[b] || {rank: 0}).rank - (Config.groups[a] || {rank: 0}).rank;
		}).forEach(function (r) {
			buffer.push((Config.groups[r] ? r + Config.groups[r].name + "s (" + rankLists[r].length + ")" : r) + ":\n" + rankLists[r].sort().join(", "));
		});

		if (!buffer.length) buffer = "This server has no auth.";
		connection.popup(buffer.join("\n\n"));
	},

	clearall: function (target, room, user) {
		if (!this.can('declare')) return false;
		if (room.battle) return this.sendReply("You cannot clearall in battle rooms.");

		var len = room.log.length;
		var users = [];
		while (len--) {
			room.log[len] = '';
		}
		for (var u in room.users) {
			users.push(u);
			Users.get(u).leaveRoom(room, Users.get(u).connections[0]);
		}
		len = users.length;
		setTimeout(function () {
			while (len--) {
				Users.get(users[len]).joinRoom(room, Users.get(users[len]).connections[0]);
			}
		}, 1000);
	},

	hide: function (target, room, user) {
		if (!this.can('lock')) return false;
		user.hiding = true;
		user.updateIdentity();
		this.sendReply("You have hidden your staff symbol.");
	},

	rk: 'kick',
	roomkick: 'kick',
	kick: function (target, room, user) {
		if (!target) return this.parse('/help kick');
		if (user.locked && !user.can('bypassall')) {
			return this.sendReply("You cannot do this while unable to talk.");
		}

		target = this.splitTarget(target);
		var targetUser = this.targetUser;
		if (!targetUser || !targetUser.connected) return this.sendReply("User \"" + this.targetUsername + "\" not found.");
		if (!this.can('mute', targetUser, room)) return false;

		this.addModCommand(targetUser.name + " was kicked from the room by " + user.name + ".");
		targetUser.popup("You were kicked from " + room.id + " by " + user.name + ".");
		targetUser.leaveRoom(room.id);
	},
	kickhelp: ["/kick - Kick a user out of a room. Requires: % @ # & ~"],

	masspm: 'pmall',
	pmall: function (target, room, user) {
		if (!this.can('pmall')) return false;
		if (!target) return this.parse('/help pmall');

		var pmName = ' Server PM [Do not reply]';

		for (var i in Users.users) {
			var message = '|pm|' + pmName + '|' + Users.users[i].getIdentity() + '|' + target;
			Users.users[i].send(message);
		}
	},
	pmallhelp: ["/pmall [message] - PM all users in the server."],

	staffpm: 'pmallstaff',
	pmstaff: 'pmallstaff',
	pmallstaff: function (target, room, user) {
		if (!this.can('forcewin')) return false;
		if (!target) return this.parse('/help pmallstaff');

		var pmName = ' Staff PM [Do not reply]';

		for (var i in Users.users) {
			if (Users.users[i].isStaff) {
				Users.users[i].send('|pm|' + pmName + '|' + Users.users[i].group + Users.users[i].name + '|' + target);
			}
		}
	},
	pmallstaffhelp: ["/pmallstaff [message] - Sends a PM to every staff member online."],

	d: 'poof',
	cpoof: 'poof',
	poof: function (target, room, user) {
		if (Config.poofOff) return this.sendReply("Poof is currently disabled.");
		if (target && !this.can('broadcast')) return false;
		if (room.id !== 'lobby') return false;
		var message = target || messages[Math.floor(Math.random() * messages.length)];
		if (message.indexOf('{{user}}') < 0) message = '{{user}} ' + message;
		message = message.replace(/{{user}}/g, user.name);
		if (!this.canTalk(message)) return false;

		var colour = '#' + [1, 1, 1].map(function () {
			var part = Math.floor(Math.random() * 0xaa);
			return (part < 0x10 ? '0' : '') + part.toString(16);
		}).join('');

		room.addRaw("~~ " + Tools.escapeHTML(message) + " ~~");
		user.disconnectAll();
	},
	poofhelp: ["/poof - Disconnects the user and leaves a message in the room."],

	poofon: function () {
		if (!this.can('poofoff')) return false;
		Config.poofOff = false;
		return this.sendReply("Poof is now enabled.");
	},
	poofonhelp: ["/poofon - Enable the use /poof command."],

	nopoof: 'poofoff',
	poofoff: function () {
		if (!this.can('poofoff')) return false;
		Config.poofOff = true;
		return this.sendReply("Poof is now disabled.");
	},
	poofoffhelp: ["/poofoff - Disable the use of the /poof command."],

	regdate: function (target, room, user) {
		if (!this.canBroadcast()) return;
		if (!target || target === "0") target = toId(user.userid);
		if (!target || target === "." || target === "," || target === "'") return this.parse('/help regdate');
		var username = toId(target);
		target = target.replace(/\s+/g, '');
		var self = this, data;
		request('http://pokemonshowdown.com/users/~' + target, function (error, response, content) {
			if (!(!error && response.statusCode === 200)) return;
			content = content + '';
			content = content.split("<em");
			if (content[1]) {
				content = content[1].split("
");
				if (content[0]) {
					content = content[0].split("");
					if (content[1]) {
						var regdate = content[1].split('')[0] + '.';
						data = Tools.escapeHTML(username) + " was registered on" + regdate;
					}
				}
			} else {
				data = Tools.escapeHTML(username) + " is not registered.";
			}
			self.sendReplyBox(Tools.escapeHTML(data));
			room.update();
		});
	},
	regdatehelp: ["/regdate - Please specify a valid username."],

	show: function (target, room, user) {
		if (!this.can('lock')) return false;
		user.hiding = false;
		user.updateIdentity();
		this.sendReply("You have revealed your staff symbol.");
	},

	sb: 'showdownboilerplate',
	showdownboilerplate: function (target, room, user) {
		if (!this.canBroadcast()) return;
		this.sendReply("|raw|This server uses Showdown-Boilerplate.");
	},
	showdownboilerplatehelp: ["/showdownboilerplate - Links to the Showdown-Boilerplate repository on Github."],

	seen: function (target, room, user) {
		if (!this.canBroadcast()) return;
		if (!target) return this.parse('/help seen');
		var targetUser = Users.get(target);
		if (targetUser && targetUser.connected) return this.sendReplyBox(targetUser.name + " is currently online.");
		target = Tools.escapeHTML(target);
		var seen = Seen[toId(target)];
		if (!seen) return this.sendReplyBox(target + " has never been online on this server.");
		this.sendReplyBox(target + " was last seen " + moment(seen).fromNow() + ".");
	},
	seenhelp: ["/seen - Shows when the user last connected on the server."],

	tell: function (target, room, user, connection) {
		if (!target) return this.parse('/help tell');
		target = this.splitTarget(target);
		var targetUser = this.targetUser;
		if (!target) {
			this.sendReply("You forgot the comma.");
			return this.parse('/help tell');
		}

		if (targetUser && targetUser.connected) {
			return this.parse('/pm ' + this.targetUsername + ', ' + target);
		}

		if (user.locked) return this.popupReply("You may not send offline messages when locked.");
		if (target.length > 255) return this.popupReply("Your message is too long to be sent as an offline message (>255 characters).");

		if (Config.tellrank === 'autoconfirmed' && !user.autoconfirmed) {
			return this.popupReply("You must be autoconfirmed to send an offline message.");
		} else if (!Config.tellrank || Config.groupsranking.indexOf(user.group) < Config.groupsranking.indexOf(Config.tellrank)) {
			return this.popupReply("You cannot send an offline message because offline messaging is " +
				(!Config.tellrank ? "disabled" : "only available to users of rank " + Config.tellrank + " and above") + ".");
		}

		var userid = toId(this.targetUsername);
		if (userid.length > 18) return this.popupReply("\"" + this.targetUsername + "\" is not a legal username.");

		var sendSuccess = Tells.addTell(user, userid, target);
		if (!sendSuccess) {
			if (sendSuccess === false) {
				return this.popupReply("User " + this.targetUsername + " has too many offline messages queued.");
			} else {
				return this.popupReply("You have too many outgoing offline messages queued. Please wait until some have been received or have expired.");
			}
		}
		return connection.send('|pm|' + user.getIdentity() + '|' +
			(targetUser ? targetUser.getIdentity() : ' ' + this.targetUsername) +
			"|/text This user is currently offline. Your message will be delivered when they are next online.");
	},
	tellhelp: ["/tell [username], [message] - Send a message to an offline user that will be received when they log in."]
};
<commit_msg>Update misc.js<commit_after>/**
 * Miscellaneous commands
 */

var fs = require('fs');
var moment = require('moment');
var request = require('request');

exports.commands = {
	clearall: function (target, room, user) {
		if (!this.can('declare')) return false;
		if (room.battle) return this.sendReply("You cannot clearall in battle rooms.");

		var len = room.log.length;
		var users = [];
		while (len--) {
			room.log[len] = '';
		}
		for (var u in room.users) {
			users.push(u);
			Users.get(u).leaveRoom(room, Users.get(u).connections[0]);
		}
		len = users.length;
		setTimeout(function () {
			while (len--) {
				Users.get(users[len]).joinRoom(room, Users.get(users[len]).connections[0]);
			}
		}, 1000);
	},

	masspm: 'pmall',
	pmall: function (target, room, user) {
		if (!this.can('pmall')) return false;
		if (!target) return this.parse('/help pmall');

		var pmName = ' Server PM [Do not reply]';

		for (var i in Users.users) {
			var message = '|pm|' + pmName + '|' + Users.users[i].getIdentity() + '|' + target;
			Users.users[i].send(message);
		}
	},
	pmallhelp: ["/pmall [message] - PM all users in the server."],

	staffpm: 'pmallstaff',
	pmstaff: 'pmallstaff',
	pmallstaff: function (target, room, user) {
		if (!this.can('forcewin')) return false;
		if (!target) return this.parse('/help pmallstaff');

		var pmName = ' Staff PM [Do not reply]';

		for (var i in Users.users) {
			if (Users.users[i].isStaff) {
				Users.users[i].send('|pm|' + pmName + '|' + Users.users[i].group + Users.users[i].name + '|' + target);
			}
		}
	},
	pmallstaffhelp: ["/pmallstaff [message] - Sends a PM to every staff member online."],

	regdate: function (target, room, user) {
		if (!this.canBroadcast()) return;
		if (!target || target === "0") target = toId(user.userid);
		if (!target || target === "." || target === "," || target === "'") return this.parse('/help regdate');
		var username = toId(target);
		target = target.replace(/\s+/g, '');
		var self = this, data;
		request('http://pokemonshowdown.com/users/~' + target, function (error, response, content) {
			if (!(!error && response.statusCode === 200)) return;
			content = content + '';
			content = content.split("<em");
			if (content[1]) {
				content = content[1].split("
");
				if (content[0]) {
					content = content[0].split("");
					if (content[1]) {
						var regdate = content[1].split('')[0] + '.';
						data = Tools.escapeHTML(username) + " was registered on" + regdate;
					}
				}
			} else {
				data = Tools.escapeHTML(username) + " is not registered.";
			}
			self.sendReplyBox(Tools.escapeHTML(data));
			room.update();
		});
	},
	regdatehelp: ["/regdate - Please specify a valid username."],

	sb: 'showdownboilerplate',
	showdownboilerplate: function (target, room, user) {
		if (!this.canBroadcast()) return;
		this.sendReply("|raw|This server uses Showdown-Boilerplate.");
	},
	showdownboilerplatehelp: ["/showdownboilerplate - Links to the Showdown-Boilerplate repository on Github."],

	seen: function (target, room, user) {
		if (!this.canBroadcast()) return;
		if (!target) return this.parse('/help seen');
		var targetUser = Users.get(target);
		if (targetUser && targetUser.connected) return this.sendReplyBox(targetUser.name + " is currently online.");
		target = Tools.escapeHTML(target);
		var seen = Seen[toId(target)];
		if (!seen) return this.sendReplyBox(target + " has never been online on this server.");
		this.sendReplyBox(target + " was last seen " + moment(seen).fromNow() + ".");
	},
	seenhelp: ["/seen - Shows when the user last connected on the server."],

	tell: function (target, room, user, connection) {
		if (!target) return this.parse('/help tell');
		target = this.splitTarget(target);
		var targetUser = this.targetUser;
		if (!target) {
			this.sendReply("You forgot the comma.");
			return this.parse('/help tell');
		}

		if (targetUser && targetUser.connected) {
			return this.parse('/pm ' + this.targetUsername + ', ' + target);
		}

		if (user.locked) return this.popupReply("You may not send offline messages when locked.");
		if (target.length > 255) return this.popupReply("Your message is too long to be sent as an offline message (>255 characters).");

		if (Config.tellrank === 'autoconfirmed' && !user.autoconfirmed) {
			return this.popupReply("You must be autoconfirmed to send an offline message.");
		} else if (!Config.tellrank || Config.groupsranking.indexOf(user.group) < Config.groupsranking.indexOf(Config.tellrank)) {
			return this.popupReply("You cannot send an offline message because offline messaging is " +
				(!Config.tellrank ? "disabled" : "only available to users of rank " + Config.tellrank + " and above") + ".");
		}

		var userid = toId(this.targetUsername);
		if (userid.length > 18) return this.popupReply("\"" + this.targetUsername + "\" is not a legal username.");

		var sendSuccess = Tells.addTell(user, userid, target);
		if (!sendSuccess) {
			if (sendSuccess === false) {
				return this.popupReply("User " + this.targetUsername + " has too many offline messages queued.");
			} else {
				return this.popupReply("You have too many outgoing offline messages queued. Please wait until some have been received or have expired.");
			}
		}
		return connection.send('|pm|' + user.getIdentity() + '|' +
			(targetUser ? targetUser.getIdentity() : ' ' + this.targetUsername) +
			"|/text This user is currently offline. Your message will be delivered when they are next online.");
	},
	tellhelp: ["/tell [username], [message] - Send a message to an offline user that will be received when they log in."]
};
<|endoftext|>


<commit_before>// (C) Copyright 2014-2016 Hewlett Packard Enterprise Development LP

import React, { Component, PropTypes } from 'react';
import classnames from 'classnames';
import CSSClassnames from '../utils/CSSClassnames';
import Props from '../utils/Props';
import Box from './Box';
import Tile from './Tile';
import Heading from './Heading';
import Paragraph from './Paragraph';
import Anchor from './Anchor';
import Layer from './Layer';
import Video from './Video';
import WatchIcon from './icons/base/Watch';

const CLASS_ROOT = CSSClassnames.CARD;

export default class Card extends Component {
  constructor (props) {
    super(props);
    this._onClick = this._onClick.bind(this);
    this.state = {
      activeVideo: false
    };
  }

  _onClick (event) {
    const { video } = this.props;

    if (video) {
      event.preventDefault();
      this.setState({ activeVideo : !this.state.activeVideo });
    }
  }

  _renderLink () {
    const { link, linkIcon, linkText } = this.props;

    if (link) {
      return (
        <Anchor className={`${CLASS_ROOT}__link`} primary={true} href={link}
          target="_blank" icon={linkIcon}>
          {linkText}
        </Anchor>
      );
    }

    return null;
  }

  _renderVideo () {
    const { video } = this.props;
    const { activeVideo } = this.state;
    let layerContent;
    let videoLayer;

    if (video && activeVideo) {
      if (video.source) {
        layerContent = (
          <Video>
            <source src={video.source} type={`video/${video.type}`}/>
          </Video>
        );
      } else {
        layerContent = video;
      }

      videoLayer = (
        <Layer onClose={this._onClick} closer={true} flush={true}>
          {layerContent}
        </Layer>
      );
    }

    return videoLayer;
  }

  _renderParagraph (contents, textSize, type) {
    if (typeof contents === 'string') {
      return (
        <Paragraph
          className={`${CLASS_ROOT}__${type}`}
          size={textSize}
          margin="none"
        >
          {contents}
        </Paragraph>
      );
    } else if (Array.isArray(contents)) {
      return contents.map((content, index) => (
        <Paragraph
          key={`${type}_${index}`}
          className={`${CLASS_ROOT}__${type}`}
          size={textSize}
          margin="none"
        >
          {content}
        </Paragraph>
      ));
    }
    return null;
  }

  render () {
    const { children, thumbnail, description, heading, headingStrong, label,
      onClick, video, direction, reverse, pad, className, textSize, paragraph,
      transparent } = this.props;
    const tileProps = Props.pick(this.props, Object.keys(Tile.propTypes));
    delete tileProps.onClick;
    delete tileProps.pad;

    const classes = classnames(
      CLASS_ROOT,
      {
        [`${CLASS_ROOT}--direction-${direction}`]: direction,
        [`${CLASS_ROOT}--selectable`]: (onClick || video),
        [`${CLASS_ROOT}--${textSize}`]: textSize
      },
      className
    );

    let labelTag;
    let headingTag;
    let paragraphSize;
    let descriptionSize;
    if (textSize === 'xlarge') {
      labelTag = 'h3';
      headingTag = 'h1';
      paragraphSize = 'xlarge';
      descriptionSize = 'large';
    } else if (textSize === 'large') {
      labelTag = 'h4';
      headingTag = 'h1';
      paragraphSize = 'xlarge';
      descriptionSize = 'large';
    } else if (textSize === 'medium') {
      labelTag = 'h4';
      headingTag = 'h2';
      paragraphSize = 'large';
      descriptionSize = 'medium';
    } else {
      labelTag = 'h5';
      headingTag = 'h3';
      paragraphSize = 'medium';
      descriptionSize = 'small';
    }

    let onCardClick = onClick;
    if (!onCardClick && video) {
      onCardClick = this._onClick;
    }

    const contentContainer = (
      <Box className={`${CLASS_ROOT}__content`} pad="medium">
        {label &&
          <Heading className={`${CLASS_ROOT}__label`}
            tag={labelTag} margin="none" uppercase={true}>
            {label}
          </Heading>
        }
        {heading &&
          <Heading className={`${CLASS_ROOT}__heading`}
            tag={headingTag} strong={headingStrong} margin="none">
            {heading}
          </Heading>
        }
        {this._renderParagraph(paragraph, paragraphSize, 'paragraph')}
        {this._renderParagraph(description, descriptionSize, 'description')}
        {children}
        {this._renderLink()}
      </Box>
    );

    let thumbnailContainer;
    if (thumbnail) {
      thumbnailContainer = (
        <Box className={`${CLASS_ROOT}__thumbnail`}
          backgroundImage={`url(${thumbnail})`}
          justify="center" align="center">
          {(video) ? <Anchor icon={<WatchIcon size="xlarge" />} /> : null}
        </Box>
      );
    }

    let first = thumbnailContainer;
    let second = contentContainer;
    let cardJustify;

    if (reverse) {
      first = contentContainer;
      second = thumbnailContainer;
      // align thumbnail to bottom of card for bottom cardPlacement
      cardJustify = 'between';
    }

    let cardPad = 'small';
    let cardFull;
    if (direction === 'row') {
      cardPad = {vertical: 'small'};
      cardFull = 'horizontal';
    }

    return (
      <Tile className={classes} onClick={onCardClick}
        pad={pad || cardPad} {...tileProps}>
        <Box className="flex" direction={direction} justify={cardJustify}
          full={cardFull} colorIndex={!transparent ? 'light-1' : ''}>
          {first}
          {second}
          {this._renderVideo()}
        </Box>
      </Tile>
    );
  }
};

Card.propTypes = {
  thumbnail: PropTypes.string,
  textSize: PropTypes.oneOf(['small', 'medium', 'large', 'xlarge']),
  label: PropTypes.string,
  heading: PropTypes.string,
  headingStrong: PropTypes.bool,
  paragraph: PropTypes.string,
  description: PropTypes.string,
  video: PropTypes.oneOfType([
    PropTypes.shape({
      source: PropTypes.string.isRequired,
      type: PropTypes.string
    }),
    PropTypes.element
  ]),
  reverse: PropTypes.bool,
  transparent: PropTypes.bool,
  link: PropTypes.string,
  linkText: PropTypes.string,
  linkIcon: PropTypes.element,
  onClick: PropTypes.func,
  ...Tile.propTypes
};

Card.defaultProps = {
  direction: 'column',
  textSize: 'medium',
  headingStrong: true,
  linkText: 'Learn More'
};
<commit_msg>Allow user to pass in custom link element<commit_after>// (C) Copyright 2014-2016 Hewlett Packard Enterprise Development LP

import React, { Component, PropTypes } from 'react';
import classnames from 'classnames';
import CSSClassnames from '../utils/CSSClassnames';
import Props from '../utils/Props';
import Box from './Box';
import Tile from './Tile';
import Heading from './Heading';
import Paragraph from './Paragraph';
import Anchor from './Anchor';
import Layer from './Layer';
import Video from './Video';
import WatchIcon from './icons/base/Watch';

const CLASS_ROOT = CSSClassnames.CARD;

export default class Card extends Component {
  constructor (props) {
    super(props);
    this._onClick = this._onClick.bind(this);
    this.state = {
      activeVideo: false
    };
  }

  _onClick (event) {
    const { video } = this.props;

    if (video) {
      event.preventDefault();
      this.setState({ activeVideo : !this.state.activeVideo });
    }
  }

  _renderLink () {
    const { link } = this.props;

    if (link) {
      return (
        <Box pad={{vertical: "small"}}>
          {link}
        </Box>
      );
    }

    return null;
  }

  _renderVideo () {
    const { video } = this.props;
    const { activeVideo } = this.state;
    let layerContent;
    let videoLayer;

    if (video && activeVideo) {
      if (video.source) {
        layerContent = (
          <Video>
            <source src={video.source} type={`video/${video.type}`}/>
          </Video>
        );
      } else {
        layerContent = video;
      }

      videoLayer = (
        <Layer onClose={this._onClick} closer={true} flush={true}>
          {layerContent}
        </Layer>
      );
    }

    return videoLayer;
  }

  _renderParagraph (contents, textSize, type) {
    if (typeof contents === 'string') {
      return (
        <Paragraph
          className={`${CLASS_ROOT}__${type}`}
          size={textSize}
          margin="none"
        >
          {contents}
        </Paragraph>
      );
    } else if (Array.isArray(contents)) {
      return contents.map((content, index) => (
        <Paragraph
          key={`${type}_${index}`}
          className={`${CLASS_ROOT}__${type}`}
          size={textSize}
          margin="none"
        >
          {content}
        </Paragraph>
      ));
    }
    return null;
  }

  render () {
    const { children, thumbnail, description, heading, headingStrong, label,
      onClick, video, direction, reverse, pad, className, textSize, paragraph,
      transparent } = this.props;
    const tileProps = Props.pick(this.props, Object.keys(Tile.propTypes));
    delete tileProps.onClick;
    delete tileProps.pad;

    const classes = classnames(
      CLASS_ROOT,
      {
        [`${CLASS_ROOT}--direction-${direction}`]: direction,
        [`${CLASS_ROOT}--selectable`]: (onClick || video),
        [`${CLASS_ROOT}--${textSize}`]: textSize
      },
      className
    );

    let labelTag;
    let headingTag;
    let paragraphSize;
    let descriptionSize;
    if (textSize === 'xlarge') {
      labelTag = 'h3';
      headingTag = 'h1';
      paragraphSize = 'xlarge';
      descriptionSize = 'large';
    } else if (textSize === 'large') {
      labelTag = 'h4';
      headingTag = 'h1';
      paragraphSize = 'xlarge';
      descriptionSize = 'large';
    } else if (textSize === 'medium') {
      labelTag = 'h4';
      headingTag = 'h2';
      paragraphSize = 'large';
      descriptionSize = 'medium';
    } else {
      labelTag = 'h5';
      headingTag = 'h3';
      paragraphSize = 'medium';
      descriptionSize = 'small';
    }

    let onCardClick = onClick;
    if (!onCardClick && video) {
      onCardClick = this._onClick;
    }

    const contentContainer = (
      <Box className={`${CLASS_ROOT}__content`} pad="medium">
        {label &&
          <Heading className={`${CLASS_ROOT}__label`}
            tag={labelTag} margin="none" uppercase={true}>
            {label}
          </Heading>
        }
        {heading &&
          <Heading className={`${CLASS_ROOT}__heading`}
            tag={headingTag} strong={headingStrong} margin="none">
            {heading}
          </Heading>
        }
        {this._renderParagraph(paragraph, paragraphSize, 'paragraph')}
        {this._renderParagraph(description, descriptionSize, 'description')}
        {children}
        {this._renderLink()}
      </Box>
    );

    let thumbnailContainer;
    if (thumbnail) {
      thumbnailContainer = (
        <Box className={`${CLASS_ROOT}__thumbnail`}
          backgroundImage={`url(${thumbnail})`}
          justify="center" align="center">
          {(video) ? <Anchor icon={<WatchIcon size="xlarge" />} /> : null}
        </Box>
      );
    }

    let first = thumbnailContainer;
    let second = contentContainer;
    let cardJustify;

    if (reverse) {
      first = contentContainer;
      second = thumbnailContainer;
      // align thumbnail to bottom of card for bottom cardPlacement
      cardJustify = 'between';
    }

    let cardPad = 'small';
    let cardFull;
    if (direction === 'row') {
      cardPad = {vertical: 'small'};
      cardFull = 'horizontal';
    }

    return (
      <Tile className={classes} onClick={onCardClick}
        pad={pad || cardPad} {...tileProps}>
        <Box className="flex" direction={direction} justify={cardJustify}
          full={cardFull} colorIndex={!transparent ? 'light-1' : ''}>
          {first}
          {second}
          {this._renderVideo()}
        </Box>
      </Tile>
    );
  }
};

Card.propTypes = {
  thumbnail: PropTypes.string,
  textSize: PropTypes.oneOf(['small', 'medium', 'large', 'xlarge']),
  label: PropTypes.string,
  heading: PropTypes.string,
  headingStrong: PropTypes.bool,
  paragraph: PropTypes.string,
  description: PropTypes.string,
  video: PropTypes.oneOfType([
    PropTypes.shape({
      source: PropTypes.string.isRequired,
      type: PropTypes.string
    }),
    PropTypes.element
  ]),
  reverse: PropTypes.bool,
  transparent: PropTypes.bool,
  link: PropTypes.string,
  linkText: PropTypes.string,
  linkIcon: PropTypes.element,
  onClick: PropTypes.func,
  ...Tile.propTypes
};

Card.defaultProps = {
  direction: 'column',
  textSize: 'medium',
  headingStrong: true,
  linkText: 'Learn More'
};
<|endoftext|>


<commit_before>var createStore = require('fluxible/utils/createStore'),
  _ = require('lodash');

var BikeshedBuilderStore = createStore({
  storeName: 'BikeshedBuilderStore',
  handlers: {
    'ADD_FILES_TO_BIKESHED': 'addFiles',
  },
  initialize: function () {
    Object.assign(this, {
      bikes: [],
      bikesIndex: {}
    });
  },

  addFiles: function (files) {
    files
      .filter(f => !this.bikesIndex[f.name])
      .forEach(f => {
        this.bikes.push({
          file: f,
          name: f.name,
          url: URL.createObjectURL(f)
        });
      });
    this.bikesIndex = _.indexBy(this.bikes, 'name');
    this.emitChange();
  },

  getBikes: function () {
    return this.bikes;
  },

  getState: function () {
    var {bikes} = this;
    return {bikes}
  },
  dehydrate: function () {
    return this.getState();
  },
  rehydrate: function (state) {
    Object.assign(this, state);
  }
});

module.exports = BikeshedBuilderStore;
<commit_msg>add remove bike action<commit_after>var createStore = require('fluxible/utils/createStore'),
  _ = require('lodash');

var BikeshedBuilderStore = createStore({
  storeName: 'BikeshedBuilderStore',
  handlers: {
    'ADD_FILES_TO_BIKESHED': 'addFiles',
  },
  initialize: function () {
    Object.assign(this, {
      bikes: [],
      bikesIndex: {}
    });
  },

  reindex: function () {
    this.bikesIndex = _.indexBy(this.bikes, 'name');
  },

  addFiles: function (files) {
    files
      .filter(f => !this.bikesIndex[f.name])
      .forEach(f => {
        this.bikes.push({
          file: f,
          name: f.name,
          url: URL.createObjectURL(f)
        });
      });
    this.reindex();
    this.emitChange();
  },

  removeBike: function (idx) {
    URL.revokeObjectURL(this.bikes[idx].url);
    this.bikes.splice(idx, 1);
    this.reindex();
    this.emitChange();
  },

  getBikes: function () {
    return this.bikes;
  },

  getState: function () {
    var {bikes} = this;
    return {bikes}
  },
  dehydrate: function () {
    return this.getState();
  },
  rehydrate: function (state) {
    Object.assign(this, state);
  }
});

module.exports = BikeshedBuilderStore;
<|endoftext|>


<commit_before>var chai = require('../..')
  , should = chai.should();

var deepObj = {
    green: { tea: 'matcha' }
  , teas: [
        'chai'
      , 'matcha'
      , { tea: 'konacha' }
    ]
};

var deepObj2 = {
    green: { tea: 'matcha' }
  , teas: [
        'chai'
      , 'oolong'
      , { tea: 'konacha' }
    ]
};

suite('object display', function () {

  test('property', function () {
    deepObj.should.have.property('chai');
  });

  test('deep equal', function () {
    deepObj.should.deep.equal(deepObj2);
  });

});
<commit_msg>test: [Assertion] configruable showDiff flag. Closes #132<commit_after>var chai = require('../..')
  , should = chai.should();

var deepObj = {
    green: { tea: 'matcha' }
  , teas: [
        'chai'
      , 'matcha'
      , { tea: 'konacha' }
    ]
};

var deepObj2 = {
    green: { tea: 'matcha' }
  , teas: [
        'chai'
      , 'oolong'
      , { tea: 'konacha' }
    ]
};

suite('object display', function () {

  test('property', function () {
    deepObj.should.have.property('chai');
  });

  test('deep equal', function () {
    deepObj.should.deep.equal(deepObj2);
  });

  test('deep equal no diff', function () {
    chai.Assertion.showDiff = false;
    deepObj.should.deep.equal(deepObj2);
    chai.Assertion.showDiff = true;
  });

});
<|endoftext|>


<commit_before>'use strict';

var _                = require('lodash');
var Navigation       = require('react-router').Navigation;

var CurrentUserStore = require('../stores/CurrentUserStore');

var AuthenticatedRouteMixin = {

  mixins: [Navigation],

  componentDidUpdate: function() {
    if ( _.isEmpty(CurrentUserStore.user) && CurrentUserStore.hasBeenChecked && this.isMounted() ) {
      this.replaceWith('Home');
    }
  }

};

module.exports = AuthenticatedRouteMixin;<commit_msg>fix authenticated route mixin logic<commit_after>'use strict';

var _                = require('lodash');
var Navigation       = require('react-router').Navigation;

var CurrentUserStore = require('../stores/CurrentUserStore');

var AuthenticatedRouteMixin = {

  mixins: [Navigation],

  _checkIfRedirect: function() {
    if ( _.isEmpty(CurrentUserStore.user) && CurrentUserStore.hasBeenChecked && this.isMounted() ) {
      this.replaceWith('Home');
    }
  },

  componentDidMount: function() {
    this._checkIfRedirect();
  },

  componentDidUpdate: function() {
    this._checkIfRedirect();
  }

};

module.exports = AuthenticatedRouteMixin;<|endoftext|>