<commit_before>YUI.add("stream-users", function(Y) {
    "use strict";

    var falco = Y.namespace("Falco"),
        streams = Y.namespace("Falco.Streams"),
        Users;

    Users = function() {};

    Users.prototype = {
        _ids   : {},

        _create : function() {
            var users = Object.keys(this._ids),
                stream;

            if(!users.length) {
                return;
            }

            stream = falco.twitter.stream("statuses/filter", {
                follow : users
            });

            stream.on("tweet", this._tweet.bind(this));

            this._stream = stream;
        },

        _tweet : function(data) {
            if(!this._ids[data.user.id_str]) {
                return;
            }

            this.fire("tweet", {
                tweet : data,
                src   : "users"
            });
        },

        ids : function(ids) {
            var self = this;

            // if the first argument isn't an array assume we got called like
            // ids(id, ..., id) & turn that into an array
            if(!Array.isArray(ids)) {
                ids = Array.prototype.splice.apply(arguments);
            }

            ids.forEach(function(id) {
                if(!id || (id in self._ids)) {
                    return;
                }

                self._ids[id] = 1;
            });

            this.start();
        }
    };

    Y.augment(Users, streams.Base);

    streams.Users = Users;
    streams.users = new Users();

}, "@VERSION@", {
    requires : [
        // YUI
        "oop",
        "event-custom",

        // Streams
        "stream-base"
    ]
});
<commit_msg>Minor clean up<commit_after>YUI.add("stream-users", function(Y) {
    "use strict";

    var falco   = Y.namespace("Falco"),
        streams = Y.namespace("Falco.Streams"),
        Users;

    Users = function() {};

    Users.prototype = {
        _ids   : {},

        _create : function() {
            var users = Object.keys(this._ids),
                stream;

            if(!users.length) {
                return;
            }

            stream = falco.twitter.stream("statuses/filter", {
                follow : users
            });

            stream.on("tweet", this._tweet.bind(this));

            this._stream = stream;
        },

        _tweet : function(data) {
            // filter out users we don't recognize
            if(!this._ids[data.user.id_str]) {
                return;
            }

            this.fire("tweet", {
                tweet : data,
                src   : "users"
            });
        },

        ids : function(ids) {
            var self = this;

            // if the first argument isn't an array assume we got called like
            // ids(id, ..., id) & turn that into an array
            if(!Array.isArray(ids)) {
                ids = Array.prototype.splice.apply(arguments);
            }

            ids.forEach(function(id) {
                if(!id || (id in self._ids)) {
                    return;
                }

                self._ids[id] = 1;
            });

            this.start();
        }
    };

    Y.augment(Users, streams.Base);

    streams.Users = Users;
    streams.users = new Users();

}, "@VERSION@", {
    requires : [
        // YUI
        "oop",
        "event-custom",

        // Streams
        "stream-base"
    ]
});
<|endoftext|>


<commit_before>import React from 'react'

export const ReactReduxContext = React.createContext(null)

export default ReactReduxContext
<commit_msg>#1461 Add a displayName on ReactReduxContext (#1462)<commit_after>import React from 'react'

export const ReactReduxContext = React.createContext(null)

ReactReduxContext.displayName = 'ReactRedux'

export default ReactReduxContext
<|endoftext|>


<commit_before>desc("description");

task("example", ["dependency"], function(){
  console.log('world.');
});

task("dependency", function(){
  console.log("hello,");
});
<commit_msg>added a default jake task (just run jake to run jaketest.js)<commit_after>task("default", function() {
    console.log("default");
});

desc("description");

task("example", ["dependency"], function(){
  console.log('world.');
});

task("dependency", function(){
  console.log("hello,");
});
<|endoftext|>


<commit_before>/**
 * HEY YOU, DON'T EDIT THIS FILE!
 * It is assembled by a grunt script. You should be editing the files in the src
 * folder and then run grunt in the nodebotui root
 **/

var nodebotui = (function () {

  var socket, boards;

  /**
   * Board
   * @constructor
   *
   * @param {Object} opts
   */
  var Board = function( opts ) {

    if ( !(this instanceof Board) ) {
      return new Board( opts );
    }

    this._element = opts.element;

    this.getInputs();

  }

  /**
   * Method to loop through all the inputs within a form, if they
   * have a data-device-type defined then initialize them and
   * associate them with the board
   */
  Board.prototype.getInputs = function() {
    var inputs = document.getElementById(this._element).getElementsByTagName('input');
    for (i = 0; i < inputs.length; i++) {
      if (inputs[i].hasAttribute('data-device-type')) {
        this[inputs[i].id] = new Input({'element': inputs[i], 'board': this._element });
      }
    }
  }

  /**
   * Method to initialize all the input devices and make sure
   * that their initial values are set on the server side
   */
  Board.prototype.initialize = function() {
    _each(this, function(device, key) {
      if (key.substring(0,1) !== '_' && device._initialize) {
        device._initialize();
      }
    });
  }

  /**
   * Input
   * @constructor
   *
   * @param {Object} opts
   */
  var Input = function( opts ) {

    if ( !(this instanceof Input) ) {
      return new Input( opts );
    }

    // Cache the element and set common attriubutes
    var el = document.getElementById(opts.element.id);
    this._board = opts.board;
    this._element = el.id;
    this.pin = el.getAttribute('data-pin');

    // Extend this object with the devicetype properties and methods
    this.dataDeviceType = el.getAttribute('data-device-type');
    _extend(this, deviceTypes[this.dataDeviceType]);

    // Extend this object with inputType properties and methods
    this.inputType = el.getAttribute('type');
    _extend(this, inputTypes[this.inputType]);

    // Bind event listeners
    this._listen(el, this);

    // Add required methods to the object
    _each(this._methods, function(method, index) {
      this[method] = deviceMethods[method];
    }, this);

    delete this._methods;

  }

  /**
   * These are all the device types defined in Johnny-Five. These
   * extend our Input objects (not elements)
   *
   * min - Minimum value for the device type
   * max - Maximum value for the device type
   * initial - Initial value for the device type
   * _methods - A list of deviceMethods names that apply to the device type
   */
  var deviceTypes = {
    Led: {
      min: 0,
      max: 255,
      _methods: ['on', 'off'] //, 'toggle', 'brightness', 'pulse', 'fade', 'fadeIn', 'fadeOut', 'strobe', 'stop']
    },
    Servo: {
      min: 0,
      max: 180,
      initial: 90,
      _methods: ['move'] //, 'center', 'sweep'
    }
  }

  /**
   * All the methods that are defined in Johnny-Five. These extend
   * our Input objects (not elements)
   */
   var deviceMethods = {
    on: function() {
      socket.emit('call', { "board": this._board, "device": this._element, "method": "on" });
      this._update(true);
    },
    off: function() {
      socket.emit('call', { "board": this._board, "device": this._element, "method": "off" });
      this._update(false);
    },
    move: function(value) {
      if (value === null) {
        value = Number(document.getElementById(this._element).value)
      }
      socket.emit('call', { "board": this._board, "device": this._element, "method": "move", params: value });
      this._update( value );
    }
  }

  /**
   * These are all the HTML input types we recognize.
   * Inputs can be standalone or grouped to form a browser control.
   *
   * _listen - A function that binds necessary event listeners to the <input> elements
   */
  var inputTypes = {

    /**
     * input type="checkbox"
     *
     * Can be bound to an LED
     **/
    checkbox: {
      _listen: function(el, input) {
        el.addEventListener('change', function() {
          el.checked ? input.on() : input.off();
        });
      },
      _update: function(status) {
        document.getElementById(this._element).checked = status;
      }
    },

    /**
     * input type="range"
     *
     * Can be bound to a servo
     **/
     range: {
      _listen: function(el, input) {
        el.addEventListener('change', function() {
          input.move()
        });
      },
      _update: function(status) {
        document.getElementById(this._element).value = status;
      },
      _initialize: function() {
        var el = document.getElementById(this._element);

        this.max = el.getAttribute('max') || this.max;
        el.setAttribute('max', this.max);

        this.min = el.getAttribute('min') || this.min;
        el.setAttribute('min', this.min);
      }
    }
  }

  /**
   * The following is, ahem, borrowed code
   */

  // extend function from underscore.js http://underscorejs.org/#extend
  function _extend(obj) {
    _each(Array.prototype.slice.call(arguments, 1), function(source) {
      if (source) {
        for (var prop in source) {
          obj[prop] = source[prop];
        }
      }
    });
    return obj;
  };

  // each function from underscore.js http://underscorejs.org/#each
  function _each(obj, iterator, context) {
    if (obj == null) return;
    if (Array.prototype.forEach && obj.forEach === Array.prototype.forEach) {
      obj.forEach(iterator, context);
    } else if (obj.length === +obj.length) {
      for (var i = 0, length = obj.length; i < length; i++) {
        if (iterator.call(context, obj[i], i, obj) === {}) return;
      }
    } else {
      var keys = _keys(obj);
      for (var i = 0, length = keys.length; i < length; i++) {
        if (iterator.call(context, obj[keys[i]], keys[i], obj) === {}) return;
      }
    }
  };

  // keys function from underscore.js http://underscorejs.org/#keys
  function _keys(obj) {
    if (obj !== Object(obj)) throw new TypeError('Invalid object');
    var keys = [];
    for (var key in obj) if (hasOwnProperty.call(obj, key)) keys.push(key);
    return keys;
  };

  /**
   * Loop through the forms in the web page. For each one that has a
   * data-device-type attribute set to "board" we are defining a
   * a new board to pass to Johnny-Five
   */
  function _getBoards() {

    var boards = {}, forms = document.getElementsByTagName ('form'), i;

    for (i = 0; i < forms.length; i++) {
      if (forms[i].getAttribute('data-device-type') === 'board') {
        boards[forms[i].id] = new Board({ 'element': forms[i].id});

      }
    }

    return boards;
  }

  /**
   * This bit loads the socket.io client script asynchronously
   * and then fires our _getBoards function
   **/
  var script = document.createElement('script');
  script.type = 'text/javascript';
  script.async = true;

  /**
   * Find the src for this script so we can request socket.io
   * from the same server
   **/
  var scripts = document.getElementsByTagName('script'), len = scripts.length, re = /nodebotui-client\.js$/, src, nbuiScriptSrc;
  while (len--) {
    src = scripts[len].src;
    if (src && src.match(re)) {
      nbuiScriptSrc = src;
      break;
    }
  }

  script.onload = function(){

      socket = io.connect(nbuiScriptSrc.replace('/nodebotui/nodebotui-client.js', ''));

      // tell the server to initialize our new boards
      _each(boards, function( board, key) {
        socket.emit('new board', board );
      });

      // This is where we listen for events from the server
      socket.on('board ready', function( opts ) {
        boards[opts.id]._ready = true;
        boards[opts.id].initialize();
        console.log('board ready');
      });

  };

  // Insert script element for socket.io
  script.src = nbuiScriptSrc.replace('nodebotui/nodebotui-client.js', 'socket.io/socket.io.js');
  document.getElementsByTagName('head')[0].appendChild(script);

  //Initialize boards
  boards =  _getBoards();

  // assign our boards object to nodebotui global
  return boards;

})();<commit_msg>New build<commit_after>/**
 * HEY, DON'T EDIT THIS FILE!
 * It is assembled by a grunt script. You should be edit the files in the src
 * folder and then run grunt in the nodebotui root
 **/

var nodebotui = (function () {

  var socket, boards;

  /**
   * Board
   * @constructor
   *
   * @param {Object} opts
   */
  var Board = function( opts ) {

    if ( !(this instanceof Board) ) {
      return new Board( opts );
    }

    this._element = opts.element;

    this.getInputs();

  }

  /**
   * Method to loop through all the inputs within a form, if they
   * have a data-device-type defined then initialize them and
   * associate them with the board
   */
  Board.prototype.getInputs = function() {

    var inputs = document.getElementById(this._element).getElementsByTagName('input');
    for (i = 0; i < inputs.length; i++) {
      if (inputs[i].hasAttribute('data-device-type')) {
        this[inputs[i].id] = new Input({'element': inputs[i], 'board': this._element });
      }
    }

    var fieldsets = document.getElementById(this._element).getElementsByTagName('fieldset');
    for (i = 0; i < fieldsets.length; i++) {
      if (fieldsets[i].hasAttribute('data-device-type')) {
        this['_'+fieldsets[i].id] = new BrowserControl({'element': fieldsets[i], 'board': this._element });
      }
    }

  }

  /**
   * Method to initialize all the input devices and make sure
   * that their initial values are set on the server side
   */
  Board.prototype.initialize = function() {
    _each(this, function(device, key) {
      if (key.substring(0,1) !== '_' && device._initialize) {
        device._initialize();
      }
    });
  }

  /**
   * Input
   * @constructor
   *
   * @param {Object} opts
   */
  var Input = function( opts ) {

    if ( !(this instanceof Input) ) {
      return new Input( opts );
    }

    // Cache the element and set common attriubutes
    var el = document.getElementById(opts.element.id);
    this._board = opts.board;
    this._element = el.id;
    this.pin = el.getAttribute('data-pin');

    // Extend this object with the devicetype properties and methods
    this.dataDeviceType = el.getAttribute('data-device-type');
    _extend(this, deviceTypes[this.dataDeviceType]);

    // Extend this object with inputType properties and methods
    this.inputType = el.getAttribute('type');
    _extend(this, inputTypes[this.inputType]);

    // Bind event listeners
    this._listen(el, this);

    // Add required methods to the object
    _each(this._methods, function(method, index) {
      this[method] = deviceMethods[method];
    }, this);

    delete this._methods;

  }

  /**
   * Browser Control
   * @constructor
   *
   * @param {Object} opts
   */
  var BrowserControl = function( opts ) {

    if ( !(this instanceof BrowserControl) ) {
      return new BrowserControl( opts );
    }

    // Cache the element and set common attributes
    var el = document.getElementById(opts.element.id);
    this._board = opts.board;
    this._element = el.id;

    // Extend this object with the browser control properties and methods
    this.dataDeviceType = el.getAttribute('data-device-type');
    _extend(this, browserControls[this.dataDeviceType]);

    // Initialize object
    this._initialize(el, this);

    // Bind event listeners
    this._listen(el, this);

  }

  /**
   * These are all the device types defined in Johnny-Five. These
   * extend our Input objects (not elements)
   *
   * min - Minimum value for the device type
   * max - Maximum value for the device type
   * initial - Initial value for the device type
   * _methods - A list of deviceMethods names that apply to the device type
   */
  var deviceTypes = {
    Led: {
      min: 0,
      max: 255,
      _methods: ['on', 'off'] //, 'toggle', 'brightness', 'pulse', 'fade', 'fadeIn', 'fadeOut', 'strobe', 'stop']
    },
    Servo: {
      min: 0,
      max: 180,
      initial: 90,
      _methods: ['move'] //, 'center', 'sweep'
    }
  }

  /**
   * All the methods that are defined in Johnny-Five. These extend
   * our Input objects (not elements)
   */
   var deviceMethods = {
    on: function() {
      if (socket) {
        socket.emit('call', { "board": this._board, "device": this._element, "method": "on" });
      }
      this._update(true);
    },
    off: function() {
      if (socket) {
        socket.emit('call', { "board": this._board, "device": this._element, "method": "off" });
      }
      this._update(false);
    },
    move: function(value) {
      if (value === null) {
        value = Number(document.getElementById(this._element).value)
      }
      if (socket) {
        socket.emit('call', { "board": this._board, "device": this._element, "method": "move", params: value });
      }
      this._update( value );
    }
  }

  /**
   * These are browser controls.
   * Browser controls are groups of inputs working in concert
   *
   * _listen - A function that binds necessary event listeners to the <input> elements
   */
  var browserControls = {

    /**
     * fieldset data-device-type="orientation"
     *
     * A group of two or three ranges
     **/
    Orientation: {

      /**
       * On deviceorientation check to see if there are inputs for each of the three axes
       * If so, move that range input
       **/
      _listen: function(el, browserControl) {
        window.addEventListener('deviceorientation', function(event) {
          _each(['alpha', 'beta', 'gamma'], function (prefix) {
            if (browserControl[prefix+'Input']) {
              boards[browserControl._board][browserControl[prefix+'Input']].move((event[prefix]+180)/2);
            }
          }, this);
        });
      },

      _update: function(alpha, beta, gamma) {
        //todo
      },

      /**
       * Bind each of the inputs associated with the browser control with
       * the appropriate axis
       **/
       _initialize: function(el, browserControl) {
        var inputs = document.getElementById(this._element).getElementsByTagName('input');
        for (i = 0; i < inputs.length; i++) {
          if (inputs[i].hasAttribute('data-axis')) {
            _each(['alpha', 'beta', 'gamma'], function (prefix) {
              if (inputs[i].getAttribute('data-axis') === prefix) {
                this[prefix+'Input'] = inputs[i].id;
              }
            }, this);
          }
        }

      }
    }
  }
  /**
   * These are all the HTML input types we recognize.
   * Inputs can be standalone or grouped to form a browser control.
   *
   * _listen - A function that binds necessary event listeners to the <input> elements
   */
  var inputTypes = {

    /**
     * input type="checkbox"
     *
     * Can be bound to an LED
     **/
    checkbox: {
      _listen: function(el, input) {
        el.addEventListener('change', function() {
          el.checked ? input.on() : input.off();
        });
      },
      _update: function(status) {
        document.getElementById(this._element).checked = status;
      }
    },

    /**
     * input type="range"
     *
     * Can be bound to a servo
     **/
     range: {
      _listen: function(el, input) {
        el.addEventListener('change', function() {
          input.move()
        });
      },
      _update: function(status) {
        document.getElementById(this._element).value = status;
      },
      _initialize: function() {
        var el = document.getElementById(this._element);

        this.max = el.getAttribute('max') || this.max;
        el.setAttribute('max', this.max);

        this.min = el.getAttribute('min') || this.min;
        el.setAttribute('min', this.min);
      }
    }
  }

  /**
   * These are browser controls.
   * Browser controls are groups of inputs working in concert
   *
   * _listen - A function that binds necessary event listeners to the <input> elements
   */
  var browserControls = {

    /**
     * fieldset data-device-type="orientation"
     *
     * A group of two or three ranges
     **/
    Orientation: {

      /**
       * On deviceorientation check to see if there are inputs for each of the three axes
       * If so, move that range input
       **/
      _listen: function(el, browserControl) {
        window.addEventListener('deviceorientation', function(event) {
          _each(['alpha', 'beta', 'gamma'], function (prefix) {
            if (browserControl[prefix+'Input']) {
              boards[browserControl._board][browserControl[prefix+'Input']].move((event[prefix]+180)/2);
            }
          }, this);
        });
      },

      _update: function(alpha, beta, gamma) {
        //todo
      },

      /**
       * Bind each of the inputs associated with the browser control with
       * the appropriate axis
       **/
       _initialize: function(el, browserControl) {
        var inputs = document.getElementById(this._element).getElementsByTagName('input');
        for (i = 0; i < inputs.length; i++) {
          if (inputs[i].hasAttribute('data-axis')) {
            _each(['alpha', 'beta', 'gamma'], function (prefix) {
              if (inputs[i].getAttribute('data-axis') === prefix) {
                this[prefix+'Input'] = inputs[i].id;
              }
            }, this);
          }
        }

      }
    }
  }
  /**
   * The following is, ahem, borrowed code
   */

  // extend function from underscore.js http://underscorejs.org/#extend
  function _extend(obj) {
    _each(Array.prototype.slice.call(arguments, 1), function(source) {
      if (source) {
        for (var prop in source) {
          obj[prop] = source[prop];
        }
      }
    });
    return obj;
  };

  // each function from underscore.js http://underscorejs.org/#each
  function _each(obj, iterator, context) {
    if (obj == null) return;
    if (Array.prototype.forEach && obj.forEach === Array.prototype.forEach) {
      obj.forEach(iterator, context);
    } else if (obj.length === +obj.length) {
      for (var i = 0, length = obj.length; i < length; i++) {
        if (iterator.call(context, obj[i], i, obj) === {}) return;
      }
    } else {
      var keys = _keys(obj);
      for (var i = 0, length = keys.length; i < length; i++) {
        if (iterator.call(context, obj[keys[i]], keys[i], obj) === {}) return;
      }
    }
  };

  // keys function from underscore.js http://underscorejs.org/#keys
  function _keys(obj) {
    if (obj !== Object(obj)) throw new TypeError('Invalid object');
    var keys = [];
    for (var key in obj) if (hasOwnProperty.call(obj, key)) keys.push(key);
    return keys;
  };

  /**
   * Loop through the forms in the web page. For each one that has a
   * data-device-type attribute set to "board" we are defining a
   * a new board to pass to Johnny-Five
   */
  function _getBoards() {

    var boards = {}, forms = document.getElementsByTagName ('form'), i;

    for (i = 0; i < forms.length; i++) {
      if (forms[i].getAttribute('data-device-type') === 'board') {
        boards[forms[i].id] = new Board({ 'element': forms[i].id});

      }
    }

    return boards;
  }

  /**
   * This bit loads the socket.io client script asynchronously
   * and then fires our _getBoards function
   **/
  var script = document.createElement('script');
  script.type = 'text/javascript';
  script.async = true;

  /**
   * Find the src for this script so we can request socket.io
   * from the same server
   **/
  var scripts = document.getElementsByTagName('script'), len = scripts.length, re = /nodebotui-client\.js$/, src, nbuiScriptSrc;
  while (len--) {
    src = scripts[len].src;
    if (src && src.match(re)) {
      nbuiScriptSrc = src;
      break;
    }
  }

  script.onload = function(){

      socket = io.connect(nbuiScriptSrc.replace('/nodebotui/nodebotui-client.js', ''));

      // tell the server to initialize our new boards
      _each(boards, function( board, key) {
        socket.emit('new board', board );
      });

      // This is where we listen for events from the server
      socket.on('board ready', function( opts ) {
        boards[opts.id]._ready = true;
        boards[opts.id].initialize();
        console.log('board ready');
      });

  };

  // Insert script element for socket.io
  script.src = nbuiScriptSrc.replace('nodebotui/nodebotui-client.js', 'socket.io/socket.io.js');
  document.getElementsByTagName('head')[0].appendChild(script);

  //Initialize boards
  boards =  _getBoards();

  // assign our boards object to nodebotui global
  return boards;

})();<|endoftext|>


<commit_before>
var React = require('react-native');
var Comments = require('../../Comments');
var Footer = require('../Footer');
var styles = require('../../../../styles.js');
var HOST = require('../../../../config.js');

var { View, Text, TouchableOpacity, Image, StyleSheet, } = React;

var Message = React.createClass({

  getInitialState: function(){
    return { numHearts: this.props.message.votes }
  },

  componentWillReceiveProps: function(props){
    this.setState({ numHearts: props.message.votes })
  },

  render: function(message) {
    var {votes, messageString, image, ...footer} = this.props.message;

    var thumbnail;

    if (image){
      thumbnail = <Thumbnail uri={image} fullResolution={false} />
    }

    return (
      <View style={[styles.buttonContents, {flexDirection: 'column'}]}>
        <TouchableOpacity onPress={this._onPressMessage}>
          <View>
            {thumbnail}
            <Text></Text>
            <Text style={styles.messageText}>
              {messageString}
            </Text>
            <Text></Text>
            <Text></Text>
          </View>
        </TouchableOpacity>
        <Footer
          {...footer}
          numHearts={this.state.numHearts}
          userToken={this.props.userToken}
          updateHearts={this._updateHearts.bind(this)}
        />
        <View style={{height: 1,backgroundColor: '#f4f4f4',marginTop:10,}} />
      </View>

    );
  },

  _onPressMessage: function() {

    var {message, ...props} = this.props;
    var {votes, ...message} = this.props.message;
    var numHearts = this.state.numHearts;
    var fetchMessages = this._updateHearts.bind(this);

    this.props.navigator.push({
      component: Comments,
      passProps: Object.assign(
        {...message},
        {...props},
        {numHearts},
        {fetchMessages}),
    })
  },

  _updateHearts: function(){
    var increment = this.state.numHearts + 1;
    this.setState({numHearts: increment})
  }

});

module.exports = Message
<commit_msg>messages/ refering to correct thumbnail component<commit_after>
var React = require('react-native');
var Comments = require('../../Comments');
var Footer = require('../Footer');
var styles = require('../../../../styles.js');
var Thumbnail = require('../../Thumbnails');
var HOST = require('../../../../config.js');

var { View, Text, TouchableOpacity, Image, StyleSheet, } = React;

var Message = React.createClass({

  getInitialState: function(){
    return { numHearts: this.props.message.votes }
  },

  componentWillReceiveProps: function(props){
    this.setState({ numHearts: props.message.votes })
  },

  render: function(message) {
    var {votes, messageString, image, ...footer} = this.props.message;

    var thumbnail;

    if (image){
      thumbnail = <Thumbnail uri={image} fullResolution={false} />
    }

    return (
      <View style={[styles.buttonContents, {flexDirection: 'column'}]}>
        <TouchableOpacity onPress={this._onPressMessage}>
          <View>
            {thumbnail}
            <Text></Text>
            <Text style={styles.messageText}>
              {messageString}
            </Text>
            <Text></Text>
            <Text></Text>
          </View>
        </TouchableOpacity>
        <Footer
          {...footer}
          numHearts={this.state.numHearts}
          userToken={this.props.userToken}
          updateHearts={this._updateHearts.bind(this)}
        />
        <View style={{height: 1,backgroundColor: '#f4f4f4',marginTop:10,}} />
      </View>

    );
  },

  _onPressMessage: function() {

    var {message, ...props} = this.props;
    var {votes, ...message} = this.props.message;
    var numHearts = this.state.numHearts;
    var fetchMessages = this._updateHearts.bind(this);

    this.props.navigator.push({
      component: Comments,
      passProps: Object.assign(
        {...message},
        {...props},
        {numHearts},
        {fetchMessages}),
    })
  },

  _updateHearts: function(){
    var increment = this.state.numHearts + 1;
    this.setState({numHearts: increment})
  }

});

module.exports = Message
<|endoftext|>


<commit_before>var stream   = require('stream')
  , util     = require('util')
  , request  = require('superagent')
  , oauth1a  = require('../auth/oauth1a')

  , Transform = stream.Transform

  , FB_GRAPH_URL = 'https://graph.facebook.com/'

  , FB_DEFAULT_PAGE_SUBSCRIBED_FIELDS = [

    'feed'

  ]
;

function PubSubConnection(){

};

PubSubConnection.prototype.verify = function(){

};

PubSubConnection.prototype.verify = function(){

};

function Facebook(opts){

  Transform.call(this, {
    objectMode: true
  });

  this.appId          = opts.appId;
  this.appSecret      = opts.appSecret;
  this.appAccessToken = this.getApplicationToken();

  this.subscriptionCallbackURL = opts.subscriptionCallbackURL;
  this.subscribedFields = FB_DEFAULT_PAGE_SUBSCRIBED_FIELDS;
};
util.inherits(Facebook, Transform);

Facebook.prototype.getApplicationToken = function(cb){
  if( cb){
    this._graphRequest( 'oauth/access_token', 'GET', {
      client_id:      this.appId,
      client_secret:  this.appSecret,
      grant_type:     'client_credentials'
    }, function(res){
      cb&&cb(res);
    });
  }
  return [
    this.appId,
    this.appSecret
  ].join('|');
};

Facebook.prototype.getTokenInfo = function(token, cb){
  this._graphRequest( 'debug_token', 'GET', {
    input_token:  token,
    access_token: this.appAccessToken
  }, function(res){
    cb&&cb(res);
  });
};

Facebook.prototype.exchangeToken = function(token, cb){
  this._graphRequest( 'oauth/access_token', 'GET', {
    grant_type:         'fb_exchange_token',
    client_id:          this.appId,
    client_secret:      this.appSecret,
    fb_exchange_token:  token
  }, function(res){
    cb&&cb(res);
  });
};

Facebook.prototype.subscribe = function(token,cb){
  this._graphRequest( this.appId + '/subscriptions', 'POST', {
    object:       'page',
    callback_url: this.subscriptionCallbackURL,
    fields:       this.subscribedFields.join(','),
    verify_token: oauth1a.makeNonce(),
    access_token: this.appAccessToken
  },function(res){
    cb&&cb(res);
  });
};

Facebook.prototype.pipeSubscription = function(){
  var _this = this;
  return function(req, res, next){
    res.pipe( this);
    next();
  };
};

Facebook.prototype.verify = function(){
  var _this = this;
  return function(req, res, next){

    console.log(req.query);

    res.send();
    next();
  };
};

Facebook.prototype._transform = function(chunk, encoding, done){

  this.push(chunk);

  done();
};

Facebook.prototype._graphRequest = function(endpoint, method, data, cb){

  var url = FB_GRAPH_URL + endpoint
    , req
  ;

  switch( method.toUpperCase()){

    case 'GET':

      req = request.get(url)
              .query(data)
              .end( function(res){
                cb&&cb( res);
              })
      break;

    case 'POST':

      req = request.post(url)
              .query(data)
              .send(data)
              .end( function(res){
                cb&&cb( res);
              })
      break;

  }

};

module.exports = Facebook;
<commit_msg>Added endpoints to install page tab and manage pushpub connections<commit_after>var stream   = require('stream')
  , util     = require('util')
  , crypto   = require('crypto')
  , request  = require('superagent')
  , oauth1a  = require('../auth/oauth1a')

  , Transform = stream.Transform

  , FB_GRAPH_URL = 'https://graph.facebook.com/'

  , FB_DEFAULT_PAGE_SUBSCRIBED_FIELDS = [

    'feed'

  ]
;

function Page(id, accessToken){
  this.id = id;
  this.accessToken = accessToken;
};

function PubSubConnection(secret){
  this.appSecret = secret;
};

PubSubConnection.prototype.verifyToken = function(){
  return this.verifyToken = oauth1a.makeNonce();
};

PubSubConnection.prototype.validateSignature = function(data){
  var decipher = crypto.createDecipher('AES-128-CBC-HMAC-SHA1', this.appSecret);
  decipher.update('sha1='+data, 'hex');
  return decipher.final('utf8');
};

function Facebook(opts){

  Transform.call(this, {
    objectMode: true
  });

  this.appId          = opts.appId;
  this.appSecret      = opts.appSecret;
  this.appAccessToken = this.getApplicationToken();

  this.subscriptionCallbackURL = opts.subscriptionCallbackURL;
  this.subscribedFields = FB_DEFAULT_PAGE_SUBSCRIBED_FIELDS;

  this._pubsubConnections = {};
};
util.inherits(Facebook, Transform);

Facebook.prototype.getApplicationToken = function(cb){
  if( cb){
    this._graphRequest( 'oauth/access_token', 'GET', {
      client_id:      this.appId,
      client_secret:  this.appSecret,
      grant_type:     'client_credentials'
    }, function(res){
      cb&&cb(res);
    });
  }
  return [
    this.appId,
    this.appSecret
  ].join('|');
};

Facebook.prototype.getTokenInfo = function(token, cb){
  this._graphRequest( 'debug_token', 'GET', {
    input_token:  token,
    access_token: this.appAccessToken
  }, function(res){
    cb&&cb(res);
  });
};

Facebook.prototype.exchangeToken = function(token, cb){
  this._graphRequest( 'oauth/access_token', 'GET', {
    grant_type:         'fb_exchange_token',
    client_id:          this.appId,
    client_secret:      this.appSecret,
    fb_exchange_token:  token
  }, function(res){
    cb&&cb(res);
  });
};

Facebook.prototype.subscribe = function(token,cb){

  var conn = new PubSubConnection( this.appSecret)
    , verify_token = conn.verifyToken()
  ;
  this._pubsubConnections[ verify_token] = conn;

  this._graphRequest( this.appId + '/subscriptions', 'POST', {
    object:       'page',
    callback_url: this.subscriptionCallbackURL,
    fields:       this.subscribedFields.join(','),
    verify_token: verify_token,
    access_token: this.appAccessToken
  },function(res){
    cb&&cb(res);
  });
};

Facebook.prototype.getPages = function(user, token, cb){
  this._graphRequest( user + '/accounts', 'GET', {
    access_token: token
  }, function(res){
    cb&&cb(res);
  });
};

Facebook.prototype.install = function(pageId, token, cb){
  this._graphRequest( pageId + '/tabs', 'POST', {
    app_id: this.appId,
    access_token: token
  }, function(res){
    cb&&cb(res);
  });
};

Facebook.prototype.uninstall = function(pageId, token){
  this._graphRequest( pageId + '/tabs/app_' + this.appId, 'DELETE', {
    access_token: token
  }, function(){

  });
};

Facebook.prototype.pipeSubscription = function(){
  var _this = this;
  return function(req, res, next){
    console.log(res.get('X-Hub-Signature'));
    res.pipe( this);
    next();
  };
};

Facebook.prototype.isVerifyToken = function(token){
  return this._pubsubConnections.hasOwnProperty( token);
};

Facebook.prototype.verify = function(){
  var _this = this;
  return function(req, res){
    if( _this.isVerifyToken( req.query['hub.verify_token'])){
      res.send( req.query['hub.challenge']);
    }else {
      res.send(401);
    }
  };
};

Facebook.prototype._transform = function(chunk, encoding, done){

  this.push(chunk);

  done();
};

Facebook.prototype._graphRequest = function(endpoint, method, data, cb){

  var url = FB_GRAPH_URL + endpoint
    , req
  ;

  switch( method.toUpperCase()){

    case 'GET':

      req = request.get(url)
              .query(data)
              .end( function(res){
                cb&&cb( res);
              })
      break;

    case 'POST':

      req = request.post(url)
              .query(data)
              .send(data)
              .end( function(res){
                cb&&cb( res);
              })
      break;

  }

};

module.exports = Facebook;
<|endoftext|>


<commit_before>import sinon from 'sinon';
import chai, { expect } from 'chai';
import sinonChai from 'sinon-chai';
import { linkedChain } from '../lib';

chai.use(sinonChai);

describe('linkedChain', () => {
  it('is properly exported', () => {
    expect(linkedChain).to.be.a('function');
  });

  it('returns a reducer', () => {
    expect(linkedChain([])).to.be.a('function');
  });

  it('returns the supplied state for an empty chain', () => {
    const state = { foo: 'bar' };
    const reducerChain = linkedChain([]);

    expect(reducerChain(state)).to.equal(state);
  });

  it('calls the reducer in the chain with state, action and next if it accepts', () => {
    const reducer = sinon.spy((state, action, next) => {});
    const state = { foo: 'bar' };
    const action = { type: 'action' };
    const reducerChain = linkedChain([reducer]);

    reducerChain(state, action);

    expect(reducer).to.have.been.calledWith(state, action, sinon.match.func);
  });

  it('calls the first reducer in the chain only with state and action if it doesn\'t take next', () => {
    const reducer = sinon.spy((state, action) => {});
    const state = { foo: 'bar' };
    const action = { type: 'action' };
    const reducerChain = linkedChain([reducer]);

    reducerChain(state, action);

    expect(reducer).to.have.been.calledWith(state, action);
  });

  it('automatically calls the second reducer if the first reducer doesnt take next', () => {
    const first = () => 'test';
    const state = { foo: 'bar' };
    const action = { type: 'action' };
    const second = sinon.spy();
    const reducerChain = linkedChain([first, second]);

    reducerChain(state, action);
    expect(second).to.have.been.calledWith('test', action);
  });

  it('doesnt\'t call the second reducer if the first reducer takes next', () => {
    const first = (state, action, next) => 'test';
    const state = { foo: 'bar' };
    const action = { type: 'action' };
    const second = sinon.spy();
    const reducerChain = linkedChain([first, second]);

    reducerChain(state, action);
    expect(second).to.not.have.been.called;
  });
});
<commit_msg>Fix lint<commit_after>import sinon from 'sinon';
import chai, { expect } from 'chai';
import sinonChai from 'sinon-chai';
import { linkedChain } from '../lib';

chai.use(sinonChai);

describe('linkedChain', () => {
  it('is properly exported', () => {
    expect(linkedChain).to.be.a('function');
  });

  it('returns a reducer', () => {
    expect(linkedChain([])).to.be.a('function');
  });

  it('returns the supplied state for an empty chain', () => {
    const state = { foo: 'bar' };
    const reducerChain = linkedChain([]);

    expect(reducerChain(state)).to.equal(state);
  });

  it('calls the reducer in the chain with state, action and next if it accepts', () => {
    const reducer = sinon.spy((state, action, next) => {}); // eslint-disable-line no-unused-vars
    const state = { foo: 'bar' };
    const action = { type: 'action' };
    const reducerChain = linkedChain([reducer]);

    reducerChain(state, action);

    expect(reducer).to.have.been.calledWith(state, action, sinon.match.func);
  });

  it('calls the first reducer in the chain only with state and action if it doesn\'t take next', () => {
    const reducer = sinon.spy((state, action) => {}); // eslint-disable-line no-unused-vars
    const state = { foo: 'bar' };
    const action = { type: 'action' };
    const reducerChain = linkedChain([reducer]);

    reducerChain(state, action);

    expect(reducer).to.have.been.calledWith(state, action);
  });

  it('automatically calls the second reducer if the first reducer doesnt take next', () => {
    const first = () => 'test';
    const state = { foo: 'bar' };
    const action = { type: 'action' };
    const second = sinon.spy();
    const reducerChain = linkedChain([first, second]);

    reducerChain(state, action);
    expect(second).to.have.been.calledWith('test', action);
  });

  it('doesnt\'t call the second reducer if the first reducer takes next', () => {
    const first = (state, action, next) => 'test'; // eslint-disable-line no-unused-vars
    const state = { foo: 'bar' };
    const action = { type: 'action' };
    const second = sinon.spy();
    const reducerChain = linkedChain([first, second]);

    reducerChain(state, action);
    expect(second).to.not.have.been.called;
  });
});
<|endoftext|>


<commit_before>'use strict';

var assert = require('proclaim');
var cookie = require('../lib').constructor.cookie;

describe('cookie', function() {
  before(function() {
    // Just to make sure that
    // URIError is never thrown here.
    document.cookie = 'bad=%';
  });

  afterEach(function() {
    // reset to defaults
    cookie.options({});
    cookie.remove('x');
  });

  describe('#get', function() {
    it('should not not get an empty cookie', function() {
      assert(cookie.get('abc') === null);
    });

    it('should get an existing cookie', function() {
      cookie.set('x', { a: 'b' });
      assert.deepEqual(cookie.get('x'), { a: 'b' });
    });

    it('should not throw an error on a malformed cookie', function() {
      document.cookie = 'x=y';
      assert(cookie.get('x') === null);
    });
  });

  describe('#set', function() {
    it('should set a cookie', function() {
      cookie.set('x', { a: 'b' });
      assert.deepEqual(cookie.get('x'), { a: 'b' });
    });
  });

  describe('#remove', function() {
    it('should remove a cookie', function() {
      cookie.set('x', { a: 'b' });
      assert.deepEqual(cookie.get('x'), { a: 'b' });
      cookie.remove('x');
      assert(cookie.get('x') === null);
    });
  });

  describe('#options', function() {
    it('should save options', function() {
      cookie.options({ path: '/xyz' });
      assert(cookie.options().path === '/xyz');
      assert(cookie.options().maxage === 31536000000);
    });

    it('should set the domain correctly', function() {
      cookie.options({ domain: '' });
      assert(cookie.options().domain === '');
    });

    it('should fallback to `domain=null` when it cant set the test cookie', function() {
      cookie.options({ domain: 'baz.com' });
      assert(cookie.options().domain === null);
      assert(cookie.get('ajs:test') === null);
    });

    // TODO: unskip once we don't use `window`, instead mock it :/
    it.skip('should set domain localhost to `""`', function() {
      cookie.options({});
      assert(cookie.options().domain === '');
    });
  });
});
<commit_msg>Clear cookies before each cookie test<commit_after>'use strict';

var assert = require('proclaim');
var cookie = require('../lib').constructor.cookie;

describe('cookie', function() {
  beforeEach(function() {
    // Just to make sure that
    // URIError is never thrown here.
    document.cookie = 'bad=%';
  });

  afterEach(function() {
    // reset to defaults
    cookie.options({});
    cookie.remove('x');
  });

  describe('#get', function() {
    it('should not not get an empty cookie', function() {
      assert(cookie.get('abc') === null);
    });

    it('should get an existing cookie', function() {
      cookie.set('x', { a: 'b' });
      assert.deepEqual(cookie.get('x'), { a: 'b' });
    });

    it('should not throw an error on a malformed cookie', function() {
      document.cookie = 'x=y';
      assert(cookie.get('x') === null);
    });
  });

  describe('#set', function() {
    it('should set a cookie', function() {
      cookie.set('x', { a: 'b' });
      assert.deepEqual(cookie.get('x'), { a: 'b' });
    });
  });

  describe('#remove', function() {
    it('should remove a cookie', function() {
      cookie.set('x', { a: 'b' });
      assert.deepEqual(cookie.get('x'), { a: 'b' });
      cookie.remove('x');
      assert(cookie.get('x') === null);
    });
  });

  describe('#options', function() {
    it('should save options', function() {
      cookie.options({ path: '/xyz' });
      assert(cookie.options().path === '/xyz');
      assert(cookie.options().maxage === 31536000000);
    });

    it('should set the domain correctly', function() {
      cookie.options({ domain: '' });
      assert(cookie.options().domain === '');
    });

    it('should fallback to `domain=null` when it cant set the test cookie', function() {
      cookie.options({ domain: 'baz.com' });
      assert(cookie.options().domain === null);
      assert(cookie.get('ajs:test') === null);
    });

    // TODO: unskip once we don't use `window`, instead mock it :/
    it.skip('should set domain localhost to `""`', function() {
      cookie.options({});
      assert(cookie.options().domain === '');
    });
  });
});
<|endoftext|>


<commit_before>import React from 'react'
import { A, IndexA } from './A'
import Icon from './Icon'
import { VelocityTransitionGroup } from 'velocity-react'
import { isEmpty, filter } from 'lodash'
import { tagUrl } from '../routes'
import { isMobile } from '../client/util'

export const leftNavWidth = 208 // this value is dupicated in CSS
export const leftNavEasing = [70, 25]

const animations = {
  enter: {
    animation: {translateX: [0, '-100%']},
    easing: leftNavEasing
  },
  leave: {
    animation: {translateX: '-100%'},
    easing: leftNavEasing
  }
}

export const MenuButton = ({ onClick, label }) =>

    {label || 'Menu'}

export const TopicList = ({ tags, slug }) => {
  let followedTags = filter(tags, t => t.followed && !t.created)
  let createdTags = filter(tags, t => t.created && t.name !== 'chat')

  const TagLink = ({ name }) => {
    var allTopics = name === 'all-topics'
    var AComponent = allTopics ? IndexA : A
    return

      <AComponent to={tagUrl(name, slug)}>
        •  # {name}
      </AComponent>

  }

  return

TOPICS ({followedTags.length + 1})

    <TagLink name='all-topics'/>
    {!isEmpty(followedTags) && followedTags.map(tag => <TagLink name={tag.name} key={tag.name} />)}
    {!isEmpty(createdTags) &&
TOPICS CREATED ({createdTags.length})
}
    {!isEmpty(createdTags) && createdTags.map(tag => <TagLink name={tag.name} key={tag.name} />)}

}

export const LeftNav = ({ opened, community, tags, close, canModerate, canInvite }) => {
  const { slug } = community || {}
  const onMenuClick = event => {
    close()
    event.stopPropagation()
  }

  return <VelocityTransitionGroup {...animations}>
    {opened &&  isMobile() && close()}>
      <MenuButton onClick={onMenuClick}/>

          <IndexA to={slug ? `/c/${slug}` : '/'}>
            <Icon name='Comment-Alt'/> Conversations
          </IndexA>

          <A to={slug ? `/c/${slug}/events` : '/events'}>
            <Icon name='Calendar'/> Events

          <A to={slug ? `/c/${slug}/projects` : '/projects'}>
            <Icon name='road' glyphicon={true}/> Projects

        {community &&

          <A to={`/c/${slug}/members`}>
            <Icon name='Users'/> Members

}
        {community &&

          <A to={`/c/${slug}/about`}>
            <Icon name='question-sign' glyphicon={true}/> About

}
        {canInvite &&

          <A to={`/c/${slug}/invite`}>
            <Icon name='Mail'/> Invite

}
        {canModerate &&

          <A to={`/c/${slug}/settings`}>
            <Icon name='cog' glyphicon={true}/> Settings

}

      {!isEmpty(tags) && <TopicList tags={tags} slug={slug} />}
    }
  </VelocityTransitionGroup>
}

export default LeftNav
<commit_msg>leftnav icons<commit_after>import React from 'react'
import { A, IndexA } from './A'
import Icon from './Icon'
import { VelocityTransitionGroup } from 'velocity-react'
import { isEmpty, filter } from 'lodash'
import { tagUrl } from '../routes'
import { isMobile } from '../client/util'

export const leftNavWidth = 208 // this value is dupicated in CSS
export const leftNavEasing = [70, 25]

const animations = {
  enter: {
    animation: {translateX: [0, '-100%']},
    easing: leftNavEasing
  },
  leave: {
    animation: {translateX: '-100%'},
    easing: leftNavEasing
  }
}

export const MenuButton = ({ onClick, label }) =>

    {label || 'Menu'}

export const TopicList = ({ tags, slug }) => {
  let followedTags = filter(tags, t => t.followed && !t.created)
  let createdTags = filter(tags, t => t.created && t.name !== 'chat')

  const TagLink = ({ name }) => {
    var allTopics = name === 'all-topics'
    var AComponent = allTopics ? IndexA : A
    return

      <AComponent to={tagUrl(name, slug)}>
        •  # {name}
      </AComponent>

  }

  return

TOPICS ({followedTags.length + 1})

    <TagLink name='all-topics'/>
    {!isEmpty(followedTags) && followedTags.map(tag => <TagLink name={tag.name} key={tag.name} />)}
    {!isEmpty(createdTags) &&
TOPICS CREATED ({createdTags.length})
}
    {!isEmpty(createdTags) && createdTags.map(tag => <TagLink name={tag.name} key={tag.name} />)}

}

export const LeftNav = ({ opened, community, tags, close, canModerate, canInvite }) => {
  const { slug } = community || {}
  const onMenuClick = event => {
    close()
    event.stopPropagation()
  }

  return <VelocityTransitionGroup {...animations}>
    {opened &&  isMobile() && close()}>
      <MenuButton onClick={onMenuClick}/>

          <IndexA to={slug ? `/c/${slug}` : '/'}>
            <Icon name='Comment-Alt'/> Conversations
          </IndexA>

          <A to={slug ? `/c/${slug}/events` : '/events'}>
            <Icon name='Calendar'/> Events

          <A to={slug ? `/c/${slug}/projects` : '/projects'}>
            <Icon name='ProjectorScreen'/> Projects

        {community &&

          <A to={`/c/${slug}/members`}>
            <Icon name='Users'/> Members

}
        {community &&

          <A to={`/c/${slug}/about`}>
            <Icon name='Help'/> About

}
        {canInvite &&

          <A to={`/c/${slug}/invite`}>
            <Icon name='Mail'/> Invite

}
        {canModerate &&

          <A to={`/c/${slug}/settings`}>
            <Icon name='Settings'/> Settings

}

      {!isEmpty(tags) && <TopicList tags={tags} slug={slug} />}
    }
  </VelocityTransitionGroup>
}

export default LeftNav
<|endoftext|>


<commit_before>/* Copyright 2016 Google Inc.
 *
 * 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. */

(function(window) {
  'use strict';

  var app = {},
      proto = document.querySelector('.proto'),
      movers,
      bodySize = document.body.getBoundingClientRect(),
      ballSize = proto.getBoundingClientRect(),
      maxHeight = Math.floor(bodySize.height - ballSize.height),
      maxWidth = 97, // 100vw - width of square (3vw)
      incrementor = 10,
      distance = 3,
      frame,
      minimum = 10,
      subtract = document.querySelector('.subtract'),
      add = document.querySelector('.add');

  app.optimize = false;
  app.count = minimum;

  app.init = function () {
    if (movers) {
      bodySize = document.body.getBoundingClientRect();
      for (var i = 0; i < movers.length; i++) {
        document.body.removeChild(movers[i]);
      }
      document.body.appendChild(proto);
      ballSize = proto.getBoundingClientRect();
      document.body.removeChild(proto);
      maxHeight = Math.floor(bodySize.height - ballSize.height);
    }
    for (var i = 0; i < app.count; i++) {
      var m = proto.cloneNode();
      var top = Math.floor(Math.random() * (maxHeight));
      if (top === maxHeight) {
        m.classList.add('up');
      } else {
        m.classList.add('down');
      }
      m.style.left = (i / (app.count / maxWidth)) + 'vw';
      m.style.top = top + 'px';
      document.body.appendChild(m);
    }
    movers = document.querySelectorAll('.mover');
  };

  app.update = function (timestamp) {
    for (var i = 0; i < app.count; i++) {
      var m = movers[i];
      if (!app.optimize) {
        var pos = m.classList.contains('down') ?
            m.offsetTop + distance : m.offsetTop - distance;
        if (pos < 0) pos = 0;
        if (pos > maxHeight) pos = maxHeight;
        m.style.top = pos + 'px';
        if (m.offsetTop === 0) {
          m.classList.remove('up');
          m.classList.add('down');
        }
        if (m.offsetTop === maxHeight) {
          m.classList.remove('down');
          m.classList.add('up');
        }
      } else {
        var pos = parseInt(m.style.top.slice(0, m.style.top.indexOf('px')));
        m.classList.contains('down') ? pos += distance : pos -= distance;
        if (pos < 0) pos = 0;
        if (pos > maxHeight) pos = maxHeight;
        m.style.top = pos + 'px';
        if (pos === 0) {
          m.classList.remove('up');
          m.classList.add('down');
        }
        if (pos === maxHeight) {
          m.classList.remove('down');
          m.classList.add('up');
        }
      }
    }
    frame = window.requestAnimationFrame(app.update);
  }

  document.querySelector('.stop').addEventListener('click', function (e) {
    if (e.target.textContent === 'Stop') {
      cancelAnimationFrame(frame);
      e.target.textContent = 'Start';
    } else {
      frame = window.requestAnimationFrame(app.update);
      e.target.textContent = 'Stop';
    }
  });

  document.querySelector('.optimize').addEventListener('click', function (e) {
    if (e.target.textContent === 'Optimize') {
      app.optimize = true;
      e.target.textContent = 'Un-Optimize';
    } else {
      app.optimize = false;
      e.target.textContent = 'Optimize';
    }
  });

  add.addEventListener('click', function (e) {
    cancelAnimationFrame(frame);
    app.count += incrementor;
    subtract.disabled = false;
    app.init();
    frame = requestAnimationFrame(app.update);
  });

  subtract.addEventListener('click', function () {
    cancelAnimationFrame(frame);
    app.count -= incrementor;
    app.init();
    frame = requestAnimationFrame(app.update);
    if (app.count === minimum) {
      subtract.disabled = true;
    }
  });

  function debounce(func, wait, immediate) {
    var timeout;
    return function() {
      var context = this, args = arguments;
      var later = function() {
        timeout = null;
        if (!immediate) func.apply(context, args);
      };
      var callNow = immediate && !timeout;
      clearTimeout(timeout);
      timeout = setTimeout(later, wait);
      if (callNow) func.apply(context, args);
    };
  };

  var onResize = debounce(function () {
    cancelAnimationFrame(frame);
    app.init();
    frame = requestAnimationFrame(app.update);
  }, 500);

  window.addEventListener('resize', onResize);

  add.textContent = 'Add ' + incrementor;
  subtract.textContent = 'Subtract ' + incrementor;
  document.body.removeChild(proto);
  proto.classList.remove('.proto');
  app.init();
  window.app = app;
  frame = window.requestAnimationFrame(app.update);

})(window);
<commit_msg>Fix issue of  button not working after resizing<commit_after>/* Copyright 2016 Google Inc.
 *
 * 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. */

(function(window) {
  'use strict';

  var app = {},
      proto = document.querySelector('.proto'),
      movers,
      bodySize = document.body.getBoundingClientRect(),
      ballSize = proto.getBoundingClientRect(),
      maxHeight = Math.floor(bodySize.height - ballSize.height),
      maxWidth = 97, // 100vw - width of square (3vw)
      incrementor = 10,
      distance = 3,
      frame,
      minimum = 10,
      subtract = document.querySelector('.subtract'),
      add = document.querySelector('.add');

  app.optimize = false;
  app.count = minimum;
  app.enableApp = true;

  app.init = function () {
    if (movers) {
      bodySize = document.body.getBoundingClientRect();
      for (var i = 0; i < movers.length; i++) {
        document.body.removeChild(movers[i]);
      }
      document.body.appendChild(proto);
      ballSize = proto.getBoundingClientRect();
      document.body.removeChild(proto);
      maxHeight = Math.floor(bodySize.height - ballSize.height);
    }
    for (var i = 0; i < app.count; i++) {
      var m = proto.cloneNode();
      var top = Math.floor(Math.random() * (maxHeight));
      if (top === maxHeight) {
        m.classList.add('up');
      } else {
        m.classList.add('down');
      }
      m.style.left = (i / (app.count / maxWidth)) + 'vw';
      m.style.top = top + 'px';
      document.body.appendChild(m);
    }
    movers = document.querySelectorAll('.mover');
  };

  app.update = function (timestamp) {
    for (var i = 0; i < app.count; i++) {
      var m = movers[i];
      if (!app.optimize) {
        var pos = m.classList.contains('down') ?
            m.offsetTop + distance : m.offsetTop - distance;
        if (pos < 0) pos = 0;
        if (pos > maxHeight) pos = maxHeight;
        m.style.top = pos + 'px';
        if (m.offsetTop === 0) {
          m.classList.remove('up');
          m.classList.add('down');
        }
        if (m.offsetTop === maxHeight) {
          m.classList.remove('down');
          m.classList.add('up');
        }
      } else {
        var pos = parseInt(m.style.top.slice(0, m.style.top.indexOf('px')));
        m.classList.contains('down') ? pos += distance : pos -= distance;
        if (pos < 0) pos = 0;
        if (pos > maxHeight) pos = maxHeight;
        m.style.top = pos + 'px';
        if (pos === 0) {
          m.classList.remove('up');
          m.classList.add('down');
        }
        if (pos === maxHeight) {
          m.classList.remove('down');
          m.classList.add('up');
        }
      }
    }
    frame = window.requestAnimationFrame(app.update);
  }

  document.querySelector('.stop').addEventListener('click', function (e) {
    if (app.enableApp) {
      cancelAnimationFrame(frame);
      e.target.textContent = 'Start';
      app.enableApp = false;
    } else {
      frame = window.requestAnimationFrame(app.update);
      e.target.textContent = 'Stop';
      app.enableApp = true;
    }
  });

  document.querySelector('.optimize').addEventListener('click', function (e) {
    if (e.target.textContent === 'Optimize') {
      app.optimize = true;
      e.target.textContent = 'Un-Optimize';
    } else {
      app.optimize = false;
      e.target.textContent = 'Optimize';
    }
  });

  add.addEventListener('click', function (e) {
    cancelAnimationFrame(frame);
    app.count += incrementor;
    subtract.disabled = false;
    app.init();
    frame = requestAnimationFrame(app.update);
  });

  subtract.addEventListener('click', function () {
    cancelAnimationFrame(frame);
    app.count -= incrementor;
    app.init();
    frame = requestAnimationFrame(app.update);
    if (app.count === minimum) {
      subtract.disabled = true;
    }
  });

  function debounce(func, wait, immediate) {
    var timeout;
    return function() {
      var context = this, args = arguments;
      var later = function() {
        timeout = null;
        if (!immediate) func.apply(context, args);
      };
      var callNow = immediate && !timeout;
      clearTimeout(timeout);
      timeout = setTimeout(later, wait);
      if (callNow) func.apply(context, args);
    };
  };

  var onResize = debounce(function () {
    if (app.enableApp) {
        cancelAnimationFrame(frame);
        app.init();
        frame = requestAnimationFrame(app.update);
    }
  }, 500);

  window.addEventListener('resize', onResize);

  add.textContent = 'Add ' + incrementor;
  subtract.textContent = 'Subtract ' + incrementor;
  document.body.removeChild(proto);
  proto.classList.remove('.proto');
  app.init();
  window.app = app;
  frame = window.requestAnimationFrame(app.update);

})(window);
<|endoftext|>


<commit_before>/** @jsx m */

"use strict";
require("./SearchPage.scss");

var _ = require("underscore");

var OnUnload = require("../utils/OnUnload.js");
var Layout = require("../layouts/DefaultLayout.js");
var SearchResults = require("../components/SearchResults.js");
var SearchFilter = require("../components/SearchFilter.js");

var SearchPage = {};

SearchPage.controller = function(options) {
  OnUnload(this);
  options = _.extend({id: "SearchPage"}, options);
  this.controllers.layout = new Layout.controller(options);
  this.controllers.searchResults = new SearchResults.controller({user: options.user});
};

SearchPage.view = function(ctrl) {
  var content = (

          Filter results Clear all

              Sort by
              Relavance
              Date

            {ctrl.controllers.searchResults.results.total} Results

            {new SearchFilter.view()}

            {new SearchResults.view(ctrl.controllers.searchResults)}

  );

  return new Layout.view(ctrl.controllers.layout, content);
};

module.exports = SearchPage;
<commit_msg>SearchPage: don't show count until the results are loaded.<commit_after>/** @jsx m */

"use strict";
require("./SearchPage.scss");

var _ = require("underscore");

var OnUnload = require("../utils/OnUnload.js");
var Layout = require("../layouts/DefaultLayout.js");
var SearchResults = require("../components/SearchResults.js");
var SearchFilter = require("../components/SearchFilter.js");

var SearchPage = {};

SearchPage.controller = function(options) {
  OnUnload(this);
  options = _.extend({id: "SearchPage"}, options);
  this.controllers.layout = new Layout.controller(options);
  this.controllers.searchResults = new SearchResults.controller({user: options.user});
};

SearchPage.view = function(ctrl) {
  var results = ctrl.controllers.searchResults.results;
  if (!results.loading) {
    var count = "" + results.total + " Results";
  }
  var content = (

          Filter results Clear all

              Sort by
              Relavance
              Date

            {count}

            {new SearchFilter.view()}

            {new SearchResults.view(ctrl.controllers.searchResults)}

  );

  return new Layout.view(ctrl.controllers.layout, content);
};

module.exports = SearchPage;
<|endoftext|>


<commit_before>/**
 * @copyright Copyright 2015 Google Inc. All rights reserved.
 *
 * 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.
 *
 * @fileoverview containerService is an angular service that provides
 *     management and manipulation (not state) of containers in a dashboard.
 *
 * @author joemu@google.com (Joe Allan Muharsky)
 */

goog.provide('p3rf.perfkit.explorer.components.container.ContainerService');

goog.scope(function() {
const explorer = p3rf.perfkit.explorer;

/**
 * See module docstring for more information about purpose and usage.
 *
 * @ngInject
 * @export
 */
explorer.components.container.ContainerService = class {
  constructor(arrayUtilService, dashboardService, explorerStateService) {
    /** @export {!ArrayUtilService} */
    this.arrayUtilSvc = arrayUtilService;

    /** @export {!DashboardService} */
    this.dashboardSvc = dashboardService;

    /** @export {!ExplorerStateService} */
    this.explorerStateSvc = explorerStateService;
  };

  /**
   * Checks for a valid container and executes the specified move function.
   *
   * @param {!Function.<!Array, !ContainerWidgetModel>} moveFunction The
   *     function to execute.
   * @param {?ContainerWidgetModel=} opt_container The target for the move,
   *     if provided.  Otherwise, the selected container is used.
   */
  move_(moveFunction, opt_container) {
    if (!goog.isDefAndNotNull(opt_container) &&
        !goog.isDefAndNotNull(this.dashboardSvc.selectedContainer)) {
     throw new Error(
        moveFunction.name + ' failed: no container specified or selected.');
    }

    moveFunction(
        this.dashboardSvc.current.model.children,
        opt_container || this.dashboardSvc.selectedContainer
    );
  };

  /**
   * Moves the specified or selected container to the first position.
   *
   * @param {?ContainerWidgetModel=} opt_container The target for the move,
   *     if provided.  Otherwise, the selected container is used.
   * @export
   */
  moveFirst(opt_container) {
    this.move_(
        angular.bind(this.arrayUtilSvc, this.arrayUtilSvc.moveFirst), opt_container);
  };

  /**
   * Moves the specified or selected container to the previous position.
   *
   * @param {?ContainerWidgetModel=} opt_container The target for the move,
   *     if provided.  Otherwise, the selected container is used.
   * @export
   */
  movePrevious(opt_container) {
    this.move_(
        angular.bind(this.arrayUtilSvc, this.arrayUtilSvc.movePrevious), opt_container);
  };

  /**
   * Moves the specified or selected container to the next position.
   *
   * @param {?ContainerWidgetModel=} opt_container The target for the move,
   *     if provided.  Otherwise, the selected container is used.
   * @export
   */
  moveNext(opt_container) {
    this.move_(
        angular.bind(this.arrayUtilSvc, this.arrayUtilSvc.moveNext), opt_container);
  };

  /**
   * Moves the specified or selected container to the last position.
   *
   * @param {?ContainerWidgetModel=} opt_container The target for the move,
   *     if provided.  Otherwise, the selected container is used.
   * @export
   */
  moveLast(opt_container) {
    this.move_(
        angular.bind(this.arrayUtilSvc, this.arrayUtilSvc.moveLast), opt_container);
  };

  /**
   * Adds a new container with one new widget and optionally selects it.
   *
   * @param {boolean=} opt_autoCreateWidget If true, automatically adds
   *    a widget to the container.
   * @param {boolean=} opt_autoSelect If true, automatically selects
   *    the widget and/or container.
   * @return {!ContainerWidgetConfig}
   * @export
   */
  insert(opt_autoCreateWidget = true, opt_autoSelect = true) {
    let container = this.dashboardSvc.newContainer(opt_autoCreateWidget);

    this.dashboardSvc.containers.push(container);
    this.explorerStateSvc.containers.add(container);

    if (opt_autoSelect) {
      this.dashboardSvc.selectContainer(container);
    }

    return container;
  };

  /**
   * Adds a new container with one new widget and optionally selects it.
   *
   * @param {number} index
   * @param {boolean=} opt_autoCreateWidget If true, automatically adds
   *    a widget to the container.
   * @param {boolean=} opt_autoSelect If true, automatically selects
   *    the widget and/or container.
   * @return {!ContainerWidgetConfig}
   * @export
   */
  insertAt(index, opt_autoCreateWidget = true, opt_autoSelect = true) {
    let container = this.dashboardSvc.newContainer(opt_autoCreateWidget);

    goog.array.insertAt(this.dashboardSvc.containers, container, index);
    this.explorerStateSvc.containers.add(container);

    if (opt_autoSelect) {
      this.dashboardSvc.selectContainer(container);
    }

    return container;
  };

  /**
   * Adds a new container and inserts it after the provided one.
   *
   * @param {?ContainerWidgetConfig=} opt_container The container that will
   *     precede the newly created one.  If not provided, the selected
   *     container is used.
   * @export
   */
  insertAfter(
      opt_container, opt_autoCreateWidget = true, opt_autoSelect = true) {
    let container = this.dashboardSvc.newContainer(opt_autoCreateWidget);
    let targetContainer = opt_container || this.dashboardSvc.selectedContainer;

    if (!goog.isDefAndNotNull(targetContainer)) {
      throw new Error(
          'insertAfter failed: No container provided or selected.');
    };

    this.arrayUtilSvc.insertAfter(
        this.dashboardSvc.containers, container, targetContainer);
    this.explorerStateSvc.containers.add(container);

    if (opt_autoSelect) {
      this.dashboardSvc.selectContainer(container);
    }

    return container;
  }

  /**
   * Adds a new container and inserts it before the provided one.
   *
   * @param {?ContainerWidgetConfig=} opt_container The container that will
   *     follow the newly created one.  If not provided, the selected
   *     container is used.
   */
  insertBefore(
      opt_container, opt_autoCreateWidget = true, opt_autoSelect = true) {
    let container = this.dashboardSvc.newContainer(opt_autoCreateWidget);
    let targetContainer = opt_container || this.dashboardSvc.selectedContainer;

    if (!goog.isDefAndNotNull(targetContainer)) {
      throw new Error(
          'insertBefore failed: No container provided or selected.');
    };

    this.arrayUtilSvc.insertBefore(
        this.dashboardSvc.containers, container, targetContainer);
    this.explorerStateSvc.containers.add(container);

    if (opt_autoSelect) {
      this.dashboardSvc.selectContainer(container);
    }

    return container;
  }

  /**
   * Removes the specified container from the current dashboard.
   *
   * @param {?ContainerWidgetConfig=} opt_container The container that will
   *     follow the newly created one.  If not provided, the selected
   *     container is used.
   * @export
   */
  remove(opt_container) {
    let targetContainer = opt_container || this.dashboardSvc.selectedContainer;

    if (this.dashboardSvc.selectedWidget.state().parent === targetContainer) {
      this.unselectWidget();
    }

    this.move_(
        angular.bind(arrayUtilSvc, arrayUtilSvc.remove), targetContainer);
    delete this.explorerStateSvc.containers.all[targetContainer.model.id];
  }
};
const ContainerService = explorer.components.container.ContainerService;

});  // goog.scope
<commit_msg>Fix issue with deleting containers.<commit_after>/**
 * @copyright Copyright 2015 Google Inc. All rights reserved.
 *
 * 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.
 *
 * @fileoverview containerService is an angular service that provides
 *     management and manipulation (not state) of containers in a dashboard.
 *
 * @author joemu@google.com (Joe Allan Muharsky)
 */

goog.provide('p3rf.perfkit.explorer.components.container.ContainerService');

goog.scope(function() {
const explorer = p3rf.perfkit.explorer;

/**
 * See module docstring for more information about purpose and usage.
 *
 * @ngInject
 * @export
 */
explorer.components.container.ContainerService = class {
  constructor(arrayUtilService, dashboardService, explorerStateService) {
    /** @export {!ArrayUtilService} */
    this.arrayUtilSvc = arrayUtilService;

    /** @export {!DashboardService} */
    this.dashboardSvc = dashboardService;

    /** @export {!ExplorerStateService} */
    this.explorerStateSvc = explorerStateService;
  };

  /**
   * Checks for a valid container and executes the specified move function.
   *
   * @param {!Function.<!Array, !ContainerWidgetModel>} moveFunction The
   *     function to execute.
   * @param {?ContainerWidgetModel=} opt_container The target for the move,
   *     if provided.  Otherwise, the selected container is used.
   */
  move_(moveFunction, opt_container) {
    if (!goog.isDefAndNotNull(opt_container) &&
        !goog.isDefAndNotNull(this.dashboardSvc.selectedContainer)) {
     throw new Error(
        moveFunction.name + ' failed: no container specified or selected.');
    }

    moveFunction(
        this.dashboardSvc.current.model.children,
        opt_container || this.dashboardSvc.selectedContainer
    );
  };

  /**
   * Moves the specified or selected container to the first position.
   *
   * @param {?ContainerWidgetModel=} opt_container The target for the move,
   *     if provided.  Otherwise, the selected container is used.
   * @export
   */
  moveFirst(opt_container) {
    this.move_(
        angular.bind(this.arrayUtilSvc, this.arrayUtilSvc.moveFirst), opt_container);
  };

  /**
   * Moves the specified or selected container to the previous position.
   *
   * @param {?ContainerWidgetModel=} opt_container The target for the move,
   *     if provided.  Otherwise, the selected container is used.
   * @export
   */
  movePrevious(opt_container) {
    this.move_(
        angular.bind(this.arrayUtilSvc, this.arrayUtilSvc.movePrevious), opt_container);
  };

  /**
   * Moves the specified or selected container to the next position.
   *
   * @param {?ContainerWidgetModel=} opt_container The target for the move,
   *     if provided.  Otherwise, the selected container is used.
   * @export
   */
  moveNext(opt_container) {
    this.move_(
        angular.bind(this.arrayUtilSvc, this.arrayUtilSvc.moveNext), opt_container);
  };

  /**
   * Moves the specified or selected container to the last position.
   *
   * @param {?ContainerWidgetModel=} opt_container The target for the move,
   *     if provided.  Otherwise, the selected container is used.
   * @export
   */
  moveLast(opt_container) {
    this.move_(
        angular.bind(this.arrayUtilSvc, this.arrayUtilSvc.moveLast), opt_container);
  };

  /**
   * Adds a new container with one new widget and optionally selects it.
   *
   * @param {boolean=} opt_autoCreateWidget If true, automatically adds
   *    a widget to the container.
   * @param {boolean=} opt_autoSelect If true, automatically selects
   *    the widget and/or container.
   * @return {!ContainerWidgetConfig}
   * @export
   */
  insert(opt_autoCreateWidget = true, opt_autoSelect = true) {
    let container = this.dashboardSvc.newContainer(opt_autoCreateWidget);

    this.dashboardSvc.containers.push(container);
    this.explorerStateSvc.containers.add(container);

    if (opt_autoSelect) {
      this.dashboardSvc.selectContainer(container);
    }

    return container;
  };

  /**
   * Adds a new container with one new widget and optionally selects it.
   *
   * @param {number} index
   * @param {boolean=} opt_autoCreateWidget If true, automatically adds
   *    a widget to the container.
   * @param {boolean=} opt_autoSelect If true, automatically selects
   *    the widget and/or container.
   * @return {!ContainerWidgetConfig}
   * @export
   */
  insertAt(index, opt_autoCreateWidget = true, opt_autoSelect = true) {
    let container = this.dashboardSvc.newContainer(opt_autoCreateWidget);

    goog.array.insertAt(this.dashboardSvc.containers, container, index);
    this.explorerStateSvc.containers.add(container);

    if (opt_autoSelect) {
      this.dashboardSvc.selectContainer(container);
    }

    return container;
  };

  /**
   * Adds a new container and inserts it after the provided one.
   *
   * @param {?ContainerWidgetConfig=} opt_container The container that will
   *     precede the newly created one.  If not provided, the selected
   *     container is used.
   * @export
   */
  insertAfter(
      opt_container, opt_autoCreateWidget = true, opt_autoSelect = true) {
    let container = this.dashboardSvc.newContainer(opt_autoCreateWidget);
    let targetContainer = opt_container || this.dashboardSvc.selectedContainer;

    if (!goog.isDefAndNotNull(targetContainer)) {
      throw new Error(
          'insertAfter failed: No container provided or selected.');
    };

    this.arrayUtilSvc.insertAfter(
        this.dashboardSvc.containers, container, targetContainer);
    this.explorerStateSvc.containers.add(container);

    if (opt_autoSelect) {
      this.dashboardSvc.selectContainer(container);
    }

    return container;
  }

  /**
   * Adds a new container and inserts it before the provided one.
   *
   * @param {?ContainerWidgetConfig=} opt_container The container that will
   *     follow the newly created one.  If not provided, the selected
   *     container is used.
   */
  insertBefore(
      opt_container, opt_autoCreateWidget = true, opt_autoSelect = true) {
    let container = this.dashboardSvc.newContainer(opt_autoCreateWidget);
    let targetContainer = opt_container || this.dashboardSvc.selectedContainer;

    if (!goog.isDefAndNotNull(targetContainer)) {
      throw new Error(
          'insertBefore failed: No container provided or selected.');
    };

    this.arrayUtilSvc.insertBefore(
        this.dashboardSvc.containers, container, targetContainer);
    this.explorerStateSvc.containers.add(container);

    if (opt_autoSelect) {
      this.dashboardSvc.selectContainer(container);
    }

    return container;
  }

  /**
   * Removes the specified container from the current dashboard.
   *
   * @param {?ContainerWidgetConfig=} opt_container The container that will
   *     follow the newly created one.  If not provided, the selected
   *     container is used.
   * @export
   */
  remove(opt_container) {
    let targetContainer = opt_container || this.dashboardSvc.selectedContainer;

    if (this.dashboardSvc.selectedWidget.state().parent === targetContainer) {
      this.dashboardSvc.unselectWidget();
    }

    this.move_(
        angular.bind(this.arrayUtilSvc, this.arrayUtilSvc.remove), targetContainer);
    delete this.explorerStateSvc.containers.all[targetContainer.model.id];
  }
};
const ContainerService = explorer.components.container.ContainerService;

});  // goog.scope
<|endoftext|>


<commit_before>/* This file is a part of @mdn/browser-compat-data
 * See LICENSE file for more information. */

'use strict';

const chalk = require('chalk');
const HTMLParser = require('@desertnet/html-parser');
const { Logger, VALID_ELEMENTS } = require('../utils.js');

const parser = new HTMLParser();

/**
 * @typedef {import('../../types').Identifier} Identifier
 * @typedef {import('../utils').Logger} Logger
 */

/**
 * Recursively test a DOM node for valid elements
 *
 * @param {object} node The DOM node to test
 * @param {string} browser The browser the notes belong to
 * @param {string} feature The identifier of the feature
 * @param {logger} logger The logger to output errors to
 * @returns {void}
 */
const testNode = (node, browser, feature, logger) => {
  if (node.type == 'TAG') {
    const tag = node.tagName?.toLowerCase();
    if (tag && !VALID_ELEMENTS.includes(tag)) {
      // Ensure we're only using select nodes
      logger.error(
        chalk`Notes for {bold ${feature}} in {bold ${browser}} have a {bold disallowed HTML element} ({bold <${tag}>}).  Allowed HTML elements are: ${VALID_ELEMENTS.join(
          ', ',
        )}`,
      );
    }

    // Ensure nodes only contain specific attributes
    const attrs = node.attributes.map((x) => x._name);
    if (tag === 'a') {
      if (attrs.length !== 1 || !attrs.includes('href')) {
        // Ensure 'a' nodes only contain an 'href'
        logger.error(
          chalk`Notes for {bold ${feature}} in {bold ${browser}} have an HTML element ({bold <${tag}>}) with {bold attributes other than href}. {bold <a>} elements may only have an {bold href} attribute.`,
        );
      }
    } else {
      if (attrs.length > 0) {
        // Ensure nodes (besides 'a') contain no attributes
        logger.error(
          chalk`Notes for {bold ${feature}} in {bold ${browser}} have an HTML element ({bold <${tag}>}) with {bold attributes}. Elements other than {bold <a>} may {bold not} have any attributes.`,
        );
      }
    }
  }

  for (let childNode of node.children || []) {
    testNode(childNode, browser, feature, logger);
  }
};

/**
 * Test a string for valid HTML
 *
 * @param {string} string The string to test
 * @param {string} browser The browser the notes belong to
 * @param {string} feature The identifier of the feature
 * @param {logger} logger The logger to output errors to
 * @returns {void}
 */
const validateHTML = (string, browser, feature, logger) => {
  const htmlErrors = HTMLParser.validate(string);

  if (htmlErrors.length === 0) {
    // If HTML is valid, ensure we're only using valid elements
    testNode(parser.parse(string), browser, feature, logger);
  } else {
    logger.error(
      chalk`Notes for {bold ${feature}} in {bold ${browser}} have invalid HTML: ${htmlErrors
        .map((x) => x._message)
        .flat()}`,
    );
  }

  if (string.includes('  ')) {
    logger.error(
      chalk`Notes for {bold ${feature}} in {bold ${browser}} have double-spaces. Notes are required to have single spaces only.`,
    );
  }
};

/**
 * Check the notes in the data
 *
 * @param {string|string[]} notes The notes to test
 * @param {string} browser The browser the notes belong to
 * @param {string} feature The identifier of the feature
 * @param {logger} logger The logger to output errors to
 * @returns {void}
 */
const checkNotes = (notes, browser, feature, logger) => {
  if (Array.isArray(notes)) {
    for (let note of notes) {
      validateHTML(note, browser, feature, logger);
    }
  } else {
    validateHTML(notes, browser, feature, logger);
  }
};

/**
 * Process the data for notes errors
 *
 * @param {Identifier} data The data to test
 * @param {logger} logger The logger to output errors to
 * @param {string} [feature] The identifier of the feature
 * @returns {void}
 */
const processData = (data, logger, feature) => {
  for (const prop in data) {
    if (prop === '__compat' && data[prop].support) {
      let statement = data[prop].support;
      for (const browser in statement) {
        if (Array.isArray(statement[browser])) {
          for (let s of statement[browser]) {
            if (s.notes) checkNotes(s.notes, browser, feature, logger);
          }
        } else {
          if (statement[browser].notes)
            checkNotes(statement[browser].notes, browser, feature, logger);
        }
      }
    }
    const sub = data[prop];
    if (typeof sub === 'object') {
      processData(sub, logger, feature ? `${feature}.${prop}` : `${prop}`);
    }
  }
};

/**
 * Test the data for notes errors
 *
 * @param {string} filename The file to test
 * @returns {boolean} Whether the file had any errors
 */
const testNotes = (filename) => {
  /** @type {Identifier} */
  const data = require(filename);
  const logger = new Logger('Notes');

  processData(data, logger);

  logger.emit();
  return logger.hasErrors();
};

module.exports = testNotes;
<commit_msg>Prevent newlines in notes (#16164)<commit_after>/* This file is a part of @mdn/browser-compat-data
 * See LICENSE file for more information. */

'use strict';

const chalk = require('chalk');
const HTMLParser = require('@desertnet/html-parser');
const { Logger, VALID_ELEMENTS } = require('../utils.js');

const parser = new HTMLParser();

/**
 * @typedef {import('../../types').Identifier} Identifier
 * @typedef {import('../utils').Logger} Logger
 */

/**
 * Recursively test a DOM node for valid elements
 *
 * @param {object} node The DOM node to test
 * @param {string} browser The browser the notes belong to
 * @param {string} feature The identifier of the feature
 * @param {logger} logger The logger to output errors to
 * @returns {void}
 */
const testNode = (node, browser, feature, logger) => {
  if (node.type == 'TAG') {
    const tag = node.tagName?.toLowerCase();
    if (tag && !VALID_ELEMENTS.includes(tag)) {
      // Ensure we're only using select nodes
      logger.error(
        chalk`Notes for {bold ${feature}} in {bold ${browser}} have a {bold disallowed HTML element} ({bold <${tag}>}).  Allowed HTML elements are: ${VALID_ELEMENTS.join(
          ', ',
        )}`,
      );
    }

    // Ensure nodes only contain specific attributes
    const attrs = node.attributes.map((x) => x._name);
    if (tag === 'a') {
      if (attrs.length !== 1 || !attrs.includes('href')) {
        // Ensure 'a' nodes only contain an 'href'
        logger.error(
          chalk`Notes for {bold ${feature}} in {bold ${browser}} have an HTML element ({bold <${tag}>}) with {bold attributes other than href}. {bold <a>} elements may only have an {bold href} attribute.`,
        );
      }
    } else {
      if (attrs.length > 0) {
        // Ensure nodes (besides 'a') contain no attributes
        logger.error(
          chalk`Notes for {bold ${feature}} in {bold ${browser}} have an HTML element ({bold <${tag}>}) with {bold attributes}. Elements other than {bold <a>} may {bold not} have any attributes.`,
        );
      }
    }
  }

  for (let childNode of node.children || []) {
    testNode(childNode, browser, feature, logger);
  }
};

/**
 * Test a string for valid HTML
 *
 * @param {string} string The string to test
 * @param {string} browser The browser the notes belong to
 * @param {string} feature The identifier of the feature
 * @param {logger} logger The logger to output errors to
 * @returns {void}
 */
const validateHTML = (string, browser, feature, logger) => {
  const htmlErrors = HTMLParser.validate(string);

  if (htmlErrors.length === 0) {
    // If HTML is valid, ensure we're only using valid elements
    testNode(parser.parse(string), browser, feature, logger);
  } else {
    logger.error(
      chalk`Notes for {bold ${feature}} in {bold ${browser}} have invalid HTML: ${htmlErrors
        .map((x) => x._message)
        .flat()}`,
    );
  }

  if (string.includes('  ')) {
    logger.error(
      chalk`Notes for {bold ${feature}} in {bold ${browser}} have double-spaces. Notes are required to have single spaces only.`,
    );
  }

  if (string.includes('\n')) {
    logger.error(
      chalk`Notes for {bold ${feature}} in {bold ${browser}} may not contain newlines.`,
    );
  }
};

/**
 * Check the notes in the data
 *
 * @param {string|string[]} notes The notes to test
 * @param {string} browser The browser the notes belong to
 * @param {string} feature The identifier of the feature
 * @param {logger} logger The logger to output errors to
 * @returns {void}
 */
const checkNotes = (notes, browser, feature, logger) => {
  if (Array.isArray(notes)) {
    for (let note of notes) {
      validateHTML(note, browser, feature, logger);
    }
  } else {
    validateHTML(notes, browser, feature, logger);
  }
};

/**
 * Process the data for notes errors
 *
 * @param {Identifier} data The data to test
 * @param {logger} logger The logger to output errors to
 * @param {string} [feature] The identifier of the feature
 * @returns {void}
 */
const processData = (data, logger, feature) => {
  for (const prop in data) {
    if (prop === '__compat' && data[prop].support) {
      let statement = data[prop].support;
      for (const browser in statement) {
        if (Array.isArray(statement[browser])) {
          for (let s of statement[browser]) {
            if (s.notes) checkNotes(s.notes, browser, feature, logger);
          }
        } else {
          if (statement[browser].notes)
            checkNotes(statement[browser].notes, browser, feature, logger);
        }
      }
    }
    const sub = data[prop];
    if (typeof sub === 'object') {
      processData(sub, logger, feature ? `${feature}.${prop}` : `${prop}`);
    }
  }
};

/**
 * Test the data for notes errors
 *
 * @param {string} filename The file to test
 * @returns {boolean} Whether the file had any errors
 */
const testNotes = (filename) => {
  /** @type {Identifier} */
  const data = require(filename);
  const logger = new Logger('Notes');

  processData(data, logger);

  logger.emit();
  return logger.hasErrors();
};

module.exports = testNotes;
<|endoftext|>