<commit_before>var View = require('ampersand-view');
var templates = require('../templates');
var dc = require('dc');
var d3 = require('d3');

module.exports = View.extend({
    template: templates.includes.histogram,
    bindings: {
        'model.filter': '[data-hook~=blank]',
        'model.missing': '[data-hook~=missing]',
        'model.filtermin': '[data-hook~=fmin]',
        'model.filtermax': '[data-hook~=fmax]',
    },
    initialize: function() {
        var self = this;

        // re-render when a different filter is selected
        this.model.on( 'change:filter', function() {self.cleanup(); self.render();} );

        // when the view is removed, also do our own cleanup
        this.once('remove',this.cleanup, this);
    },
    cleanup: function () {
        if( this._chart ) {
            // remove filter
            this._chart.filterAll();

            // re-render other plots
            dc.renderAll();
        }
    },
    render: function() {
        this.renderWithTemplate(this);

        if(this.model.filter) {

            var _dx = window.app.filters.get(this.model.filter).get('_dx');
            var group = _dx.group();

            // Deal with missing data (set to Infinity):
            // get the (sorted) groupings (key, value), where the last element, [lenght-1],
            // counts the number of missing data points, if any.
            var all = group.all();
            var min = all[0].key;
            var max;
            if( all[all.length-1].key == Infinity ) {
                max  = all[all.length - 2].key;
                this.model.missing = all[all.length-1].value;
            }
            else {
                max = all[all.length - 1].key;
                this.model.missing = 0;
            }

            // Options:
            // mouseZoomable : does not work well in comibination when using a trackpad
            // elasticX : when set to true, and the data contains Infinity, goes bonkers.

            var self = this; // needed for renderlet callback to update model
            var chart = dc.barChart(this.queryByHook('barchart'));
            chart
                .width(250)
                .height(250)
                .brushOn(true)
                .mouseZoomable(false)
                .elasticX(false)
                .elasticY(true)
                .yAxisLabel("This is the Y Axis!")
                .dimension(_dx)
                .group(_dx.group())
                .x(d3.scale.linear().domain([min,max]))
                .transitionDuration(0)
                .on('postRedraw', function(chart) {

                    // listen to 'postRedraw', not 'filtered':
                    // when the chart is removed, it also removes it filter and emits a 'filtered' event

                    if(chart.hasFilter()) {

                        // get the active (and only) filter and update the model
                        var range = chart.filters()[0];

                        self.model.filtermin = range[0];
                        self.model.filtermax = range[1];
                    }
                    else {
                        self.model.filtermin = undefined;
                        self.model.filtermax = undefined;
                    }
                });

            if (typeof this.model.filtermin != 'undefined') {
                chart.filter([this.model.filtermin, this.model.filtermax]);
            }

            chart.render();
            this._chart = chart;
        }
    },
});

<commit_msg>histogram: use crossfilter groups<commit_after>var View = require('ampersand-view');
var templates = require('../templates');
var dc = require('dc');
var d3 = require('d3');

module.exports = View.extend({
    template: templates.includes.histogram,
    bindings: {
        'model.filter': '[data-hook~=blank]',
        'model.missing': '[data-hook~=missing]',
        'model.filtermin': '[data-hook~=fmin]',
        'model.filtermax': '[data-hook~=fmax]',
    },
    initialize: function() {
        var self = this;

        // re-render when a different filter is selected
        this.model.on( 'change:filter', function() {self.cleanup(); self.render();} );

        // when the view is removed, also do our own cleanup
        this.once('remove',this.cleanup, this);
    },
    cleanup: function () {
        if( this._chart ) {
            // remove filter
            this._chart.filterAll();

            // re-render other plots
            dc.renderAll();
        }
    },
    render: function() {
        this.renderWithTemplate(this);

        if(this.model.filter) {

            var _dx = window.app.filters.get(this.model.filter).get('_dx');
            var group = _dx.group();

            // Deal with missing data (set to Infinity):
            // get the (sorted) groupings (key, value), where the last element, [lenght-1],
            // counts the number of missing data points, if any.
            var all = group.all();
            group.dispose();

            var min = all[0].key;
            var max;
            if( all[all.length-1].key == Infinity ) {
                max  = all[all.length - 2].key;
                this.model.missing = all[all.length-1].value;
            }
            else {
                max = all[all.length - 1].key;
                this.model.missing = 0;
            }

            // Create a grouping using 200 bins spanning the [min,max] range
            // This prevents the default identity grouping giving len(data) groups for floats,
            // and makes rendering and calculating much faster.
            var binsize = (max - min) / 200.0;

            var grouping = function (d) {
                var bin =  Math.round( (d-min) / binsize );
                return min + bin * binsize;
            };
            group = _dx.group( grouping );

            // Options:
            // mouseZoomable : does not work well in comibination when using a trackpad
            // elasticX : when set to true, and the data contains Infinity, goes bonkers.

            var self = this; // needed for renderlet callback to update model
            var chart = dc.barChart(this.queryByHook('barchart'));
            chart
                .width(250)
                .height(250)
                .brushOn(true)
                .mouseZoomable(false)
                .elasticX(false)
                .elasticY(true)
                .yAxisLabel("This is the Y Axis!")
                .dimension(_dx)
                .group(group, this.model.filter)
                .x(d3.scale.linear().domain([min,max]))
                .transitionDuration(0)
                .on('postRedraw', function(chart) {

                    // listen to 'postRedraw', not 'filtered':
                    // when the chart is removed, it also removes it filter and emits a 'filtered' event

                    if(chart.hasFilter()) {

                        // get the active (and only) filter and update the model
                        var range = chart.filters()[0];

                        self.model.filtermin = range[0];
                        self.model.filtermax = range[1];
                    }
                    else {
                        self.model.filtermin = undefined;
                        self.model.filtermax = undefined;
                    }
                });

            if (typeof this.model.filtermin != 'undefined') {
                chart.filter([this.model.filtermin, this.model.filtermax]);
            }

            chart.render();
            this._chart = chart;
        }
    },
});

<|endoftext|>


<commit_before>// Adapted from code by Matt Walters https://www.mattwalters.net/posts/hugo-and-lunr/

var idx = null;         // Lunr index
var resultDetails = []; // Will hold the data for the search results (titles and summaries)
var $searchInput;       // The search box element in the navbar
var $searchResults;     // Results shown in the navbar

$(window).on('load', function() {
    // Set up for an Ajax call to request the JSON data file that is created by
    // Hugo's build process, with the template we added above
    var request = new XMLHttpRequest();
    var query = '';

    // Get dom objects for the elements we'll be interacting with
    $searchResults = document.getElementById('search-results');
    $searchInput   = document.getElementById('search-input');

    request.overrideMimeType("application/json");
    request.open("GET", "/index.json", true); // Request the JSON file created during build
    request.onload = function() {
    if (request.status >= 200 && request.status < 400) {
        // Success response received in requesting the search-index file
        var searchDocuments = JSON.parse(request.responseText);

        // Build the index so Lunr can search it.  The `ref` field will hold the URL
        // to the page/post.  title, excerpt, and body will be fields searched.
        idx = lunr(function lunrIndex() {
        this.ref('ref');
        this.field('title');
        this.field('body');

          // Loop through all the items in the JSON file and add them to the index
          // so they can be searched.
        searchDocuments.forEach(function(doc) {
            this.add(doc);
            resultDetails[doc.ref] = {
                'title': doc.title,
                'excerpt': doc.excerpt,
            };
        }, this);
        });
    } else {
        $searchResults.innerHTML = '

Error loading search results

';
    }
    };

    request.onerror = function() {
    $searchResults.innerHTML = '

Error loading search results

';
    };

    // Send the request to load the JSON
    request.send();

    // Register handler for the search input field
    registerSearchHandler();
});

function registerSearchHandler() {
    $searchInput.oninput = function(event) {
    var query = event.target.value;
      var results = search(query);  // Perform the search

      // Render search results
    renderSearchResults(results);

    // Remove search results if the user empties the search phrase input field
    if ($searchInput.value == '') {
        $searchResults.innerHTML = '';
    }
    }
}

function renderSearchResults(results) {
    // Create a list of results
    if (results.length > 0) {
    var ul = document.createElement('ul');
    results.forEach(function(result) {
        // Create result item
        var li = document.createElement('li');
        li.innerHTML = '' + resultDetails[result.ref].title + '
' + resultDetails[result.ref].excerpt + '...';
        ul.appendChild(li);
    });

      // Remove any existing content so results aren't continually added as the user types
    while ($searchResults.hasChildNodes()) {
        $searchResults.removeChild(
        $searchResults.lastChild
        );
    }
    } else {
        $searchResults.innerHTML = '

No results found

';
        }
    // Render the list
    $searchResults.appendChild(ul);
}

function search(query) {
    return idx.search(query);
}
// Disables enter key on input fields except textarea
$(document).on("keydown", ":input:not(textarea)", function(event) {
    return event.key != "Enter";
});<commit_msg>limit offline search max result count<commit_after>// Adapted from code by Matt Walters https://www.mattwalters.net/posts/hugo-and-lunr/

var idx = null;         // Lunr index
var resultDetails = []; // Will hold the data for the search results (titles and summaries)
var $searchInput;       // The search box element in the navbar
var $searchResults;     // Results shown in the navbar

$(window).on('load', function() {
    // Set up for an Ajax call to request the JSON data file that is created by
    // Hugo's build process, with the template we added above
    var request = new XMLHttpRequest();
    var query = '';

    // Get dom objects for the elements we'll be interacting with
    $searchResults = document.getElementById('search-results');
    $searchInput   = document.getElementById('search-input');

    request.overrideMimeType("application/json");
    request.open("GET", "/index.json", true); // Request the JSON file created during build
    request.onload = function() {
    if (request.status >= 200 && request.status < 400) {
        // Success response received in requesting the search-index file
        var searchDocuments = JSON.parse(request.responseText);

        // Build the index so Lunr can search it.  The `ref` field will hold the URL
        // to the page/post.  title, excerpt, and body will be fields searched.
        idx = lunr(function lunrIndex() {
        this.ref('ref');
        this.field('title');
        this.field('body');

          // Loop through all the items in the JSON file and add them to the index
          // so they can be searched.
        searchDocuments.forEach(function(doc) {
            this.add(doc);
            resultDetails[doc.ref] = {
                'title': doc.title,
                'excerpt': doc.excerpt,
            };
        }, this);
        });
    } else {
        $searchResults.innerHTML = '

Error loading search results

';
    }
    };

    request.onerror = function() {
    $searchResults.innerHTML = '

Error loading search results

';
    };

    // Send the request to load the JSON
    request.send();

    // Register handler for the search input field
    registerSearchHandler();
});

function registerSearchHandler() {
    $searchInput.oninput = function(event) {
    var query = event.target.value;
      var results = search(query).slice(0, 10);  // Perform the search

      // Render search results
    renderSearchResults(results);

    // Remove search results if the user empties the search phrase input field
    if ($searchInput.value == '') {
        $searchResults.innerHTML = '';
    }
    }
}

function renderSearchResults(results) {
    // Create a list of results
    if (results.length > 0) {
    var ul = document.createElement('ul');
    results.forEach(function(result) {
        // Create result item
        var li = document.createElement('li');
        li.innerHTML = '' + resultDetails[result.ref].title + '
' + resultDetails[result.ref].excerpt + '...';
        ul.appendChild(li);
    });

      // Remove any existing content so results aren't continually added as the user types
    while ($searchResults.hasChildNodes()) {
        $searchResults.removeChild(
        $searchResults.lastChild
        );
    }
    } else {
        $searchResults.innerHTML = '

No results found

';
        }
    // Render the list
    $searchResults.appendChild(ul);
}

function search(query) {
    return idx.search(query);
}
// Disables enter key on input fields except textarea
$(document).on("keydown", ":input:not(textarea)", function(event) {
    return event.key != "Enter";
});<|endoftext|>


<commit_before>import React from "react";
import PropTypes from "prop-types";
import cx from "classnames";
import { ButtonCore } from "../Button";
import messages from "../utils/messages.js";

/**
 * Master Dropdown Component
 * Only used for dropdown component
 * Supports required, disabled, custom width, custom error messaging, onclick and onchange functions
 */
class Dropdown extends React.PureComponent {
  constructor(props) {
    super(props);
    this.state = {
      "active": false,
      "output": this._getOutput(),
      "value": this._getValue() || '',
      "valid": true,
      "errorMessage": "This field is required",
      "onChange": "",
      "focus": false,
      "zIndex": false
    };
    let isFocus = false; // eslint-disable-line no-unused-vars
  }

  _getOutput = () => {
    let count = React.Children.count(this.props.children);
    for(let i = 0; i < count; i++) {
      if(this.props.children[i].props.selected){
        return this.props.children[i].props.children
      }
    }
    if(this.props.defaultText){
      return this.props.defaultText;
    } else {
      return messages.selectOne;
    }
  }

  _getValue = () => {
    let count = React.Children.count(this.props.children);

    for(let i = 0; i < count; i++) {
      if(this.props.children[i].props.selected){
        return this.props.children[i].props.value;
      }
    }

    return null;
  }

  /**
   * closes dropdown click outside of browser window
   */
  _onWindowBlur = () => {
    if (this.state.active === true) {
      this.setState({
        "active": false,
        "zIndex": false
      });
    }
  };

  /**
   *  _clickHandler is used when the dropdown option is selected.
   *
   */
  _clickHandler = (i, event) => {
    if(this.props.disabled) {
      return;
    }

    event.persist();

    if (
      typeof this.props.onBeforeChange === "undefined" ||
      this.props.onBeforeChange(this.state.active)
    ) {
      const output = this.props.children[i].props.children;
      const inputValue = this.props.children[i].props.value;
      this.setState({
        "index": i,
        "output": output,
        "active": !this.state.active,
        "value": inputValue,
        "valid": true,
        "zIndex": false
      }, function(){
         this._validationHandler(this.props.errorCallback);
         if (this.props.onChange) {
           this._customOnChangeEvent(event);
         }
         if (this.props.onClick) {
           this._customOnClickEvent(event);
         }
      });
    }
  };

  _customOnChangeEvent = event => {
    /* Pass the event object, and a data object to the click handler.
    The data object contains a boolean for whether the dropdown was
    changed or not, plus all the props passed to the object.  */
    event.persist();
    this.props.onChange(this.state.value, event);
  };

  _customOnClickEvent = event => {
    /* Pass the event object, and a data object to the click handler.
      The data object contains a boolean for whether the dropdown was
      clicked or not, plus all the props passed to the object.  */
    event.persist();
    this.props.onClick(this.state.value, event);
  };

  _toggle = (focus, event) => {
    /* Toggles the dropdown from active to inactive state, sets valid to true and zIndex to true.
      Active is used to show/hide options, valid is used to show/hide error messaging related to validation and zIndex sets a class on the component to ensure it has the proper index on the DOM
     */
    let canProceed = true;
    if(focus === "click" && this.isFocus) {
      canProceed = false;
      this.isFocus = false;
    } else if (focus === true) {
      this.isFocus = true;
    }
    if (!this.props.disabled && canProceed) {
      const action = focus && !this.state.active ? true : false;
      if(action){
        this.setState({
          "focus": true,
          "active": true,
          "valid": true,
          "zIndex": true
        });
      } else {
        /* When the user exits dropdown the state is change for focus and validation method is called
         */
        this.setState({ "focus": false, "active": false, "zIndex": false });
        this._validationHandler(this.props.errorCallback);
      }

      if (this.props.onClick) {
        this._customOnClickEvent(event);
      }
    }
  };

  _validationHandler = callback => {
    /* Checks that required has been set to true and determines if errorCallback message was passed in a custom error message.
      Also sets state of valid depending on user action
      */
    let validationObject = {
      "valid":
        this.props.required && this.state.value !== "" ||
          !this.props.required,
      "message": this.state.errorMessage
    };
    if(callback){
      validationObject = callback(event, this.state.value);
    }
    this.setState({
      "valid": validationObject.valid,
      "errorMessage": validationObject.message
    });
  };

  _keyDown = event => {
    const indexValid = typeof this.state.index === "number";
    let newIndex;
    event.preventDefault();
    if (event.key === "ArrowDown") {
      newIndex = indexValid ? this.state.index + 1 : 0;
      let count = React.Children.count(this.props.children);
      if(newIndex < count) {
        this.setState({
          "index": newIndex
        });
      }
    } else if (event.key === "ArrowUp") {
      newIndex = this.state.index - 1;
      if(newIndex >= 0) {
        this.setState({
          "index": newIndex
        });
      }
    } else if (event.key === "Enter") {
      this._clickHandler(this.state.index, event);
    }
  }

  render() {
    const {
      children,
      className,
      required,
      customLabel,
      buttonWidth,
      disabled,
      name
    } = this.props;
    const active = this.state.active;
    const error = !this.state.valid && !disabled ? true : false;
    let zIndex = this.state.zIndex ? true : false;
    const classes = cx({
      "active": active,
      "container": true,
      "zIndex": zIndex
    });

    const buttonClasses = cx({
      "ra_dropdown__dropdown-button": true,
      "ra_dropdown__error": error,
      "ra_dropdown__disabledClass": disabled
    });

    // Builds the option list from the children passed in
    // firstChild, lastChild and selected each have unique styling and those classes are added here
    const bound_children = children.map((child, i) => {
      let count = React.Children.count(this.props.children);
      let childClasses = cx({
        "ra_dropdown__selected": i === this.state.index,
        "ra_dropdown__firstChild": i === 0,
        "ra_dropdown__lastChild": i === count - 1
      });
      let kid =

 { // onMouseDown fires before onBlur. If changed to onClick it will fire after onBlur and not work.
            this._clickHandler(i, e);
          }}
        >
          {child}

      ;
      return kid;
    });

    const dropdownButtonClasses = cx(buttonClasses);

    return (

 {
          this._toggle(true, e);
        }}
        onBlur={(e) => {
          this._toggle(false, e);
        }}
        onKeyDown={e => {
          this._keyDown(e);
        }}
      >
        {customLabel &&

            {customLabel}
            {required &&
              *
            }

        }

 { this._toggle("click", e); }} styleName={"content"}>

            <ButtonCore
              styleName={"buttonClass"}
              className={dropdownButtonClasses}
            >
              {this.state.output}
            </ButtonCore>

          {this.state.active &&

{bound_children}

          }
          <input type="hidden" value={this.state.value} />

        {error &&

            {this.state.errorMessage}

        }

    );
  }
}

Dropdown.propTypes = {
  /**
   * Text for dropdown label
   * @examples 'Some Label'
   */
  "customLabel": PropTypes.string,

  /* Boolean value taht tells the dropdown whether to
    be open or not.*/
  "active": PropTypes.bool,

  /* Boolean value that tells the dropdown whether the value is valid and controls error message is returns false.*/
  "valid": PropTypes.bool,

  /**
   * If included, dropdown is disabled
   * @examples <Dropdown disabled />, <Dropdown disabled={true} />
   */
  "disabled": PropTypes.bool,

  /**
   * Allows user to pass a callback for click events.
   */
  "onClick": PropTypes.func,

  /**
   * Allows user to pass a function to be executed when the dropdown state is changed.
   */
  "onChange": PropTypes.func,

  /**
   * If included, dropdown will return and error onBlur or onChange if not checked.
   */
  "required": PropTypes.bool,

  /**
   * Allows the user to pass a function for custom validation. Should return either true or false.
   */
  "errorCallback": PropTypes.func,

  /* . */
  "clickEvent": PropTypes.func,

  /* The children elements to be wrapped by the dropdown menu. */
  "children": PropTypes.node.isRequired,

  /* Pass CSS styles to className to set them on the dropdown component. */
  "className": PropTypes.string,

  /* The name of the key value used when submitting the dropdown value. */
  "name": PropTypes.string,

  /* Default text to show in collapsed dropdown on initial render */
  "defaultText": PropTypes.string,

  /* Allows user to ask for user feedback before changing the selected value of the Dropdown. */
  "onBeforeChange": PropTypes.func,

  /* Allows user to set custom width of dropdown */
  "buttonWidth": PropTypes.string
};

Dropdown.defaultProps = {
  "className": "",
  "buttonWidth": "160",
  "required": false,
  "disabled": false
};

export default Dropdown;
<commit_msg>Unnest Click Handler logic.<commit_after>import React from "react";
import PropTypes from "prop-types";
import cx from "classnames";
import { ButtonCore } from "../Button";
import messages from "../utils/messages.js";

/**
 * Master Dropdown Component
 * Only used for dropdown component
 * Supports required, disabled, custom width, custom error messaging, onclick and onchange functions
 */
class Dropdown extends React.PureComponent {
  constructor(props) {
    super(props);
    this.state = {
      active: false,
      output: this._getOutput(),
      value: this._getValue() || "",
      valid: true,
      errorMessage: "This field is required",
      onChange: "",
      focus: false,
      zIndex: false
    };
    let isFocus = false; // eslint-disable-line no-unused-vars
  }

  _getOutput = () => {
    let count = React.Children.count(this.props.children);
    for (let i = 0; i < count; i++) {
      if (this.props.children[i].props.selected) {
        return this.props.children[i].props.children;
      }
    }
    if (this.props.defaultText) {
      return this.props.defaultText;
    } else {
      return messages.selectOne;
    }
  };

  _getValue = () => {
    let count = React.Children.count(this.props.children);

    for (let i = 0; i < count; i++) {
      if (this.props.children[i].props.selected) {
        return this.props.children[i].props.value;
      }
    }

    return null;
  };

  /**
   * closes dropdown click outside of browser window
   */
  _onWindowBlur = () => {
    if (this.state.active === true) {
      this.setState({
        active: false,
        zIndex: false
      });
    }
  };

  /**
   *  _clickHandler is used when the dropdown option is selected.
   *
   */
  _clickHandler = (i, event) => {
    if (this.props.disabled) {
      return;
    }

    event.persist();

    if (typeof this.props.onBeforeChange !== "undefined") {
      if (this.props.onBeforeChange(this.state.active) === false) {
        return;
      }
    }

    const output = this.props.children[i].props.children;
    const inputValue = this.props.children[i].props.value;
    this.setState(
      {
        index: i,
        output: output,
        active: !this.state.active,
        value: inputValue,
        valid: true,
        zIndex: false
      },
      function() {
        this._validationHandler(this.props.errorCallback);
        if (this.props.onChange) {
          this._customOnChangeEvent(event);
        }
        if (this.props.onClick) {
          this._customOnClickEvent(event);
        }
      }
    );
  };

  _customOnChangeEvent = event => {
    /* Pass the event object, and a data object to the click handler.
    The data object contains a boolean for whether the dropdown was
    changed or not, plus all the props passed to the object.  */
    event.persist();
    this.props.onChange(this.state.value, event);
  };

  _customOnClickEvent = event => {
    /* Pass the event object, and a data object to the click handler.
      The data object contains a boolean for whether the dropdown was
      clicked or not, plus all the props passed to the object.  */
    event.persist();
    this.props.onClick(this.state.value, event);
  };

  _toggle = (focus, event) => {
    /* Toggles the dropdown from active to inactive state, sets valid to true and zIndex to true.
      Active is used to show/hide options, valid is used to show/hide error messaging related to validation and zIndex sets a class on the component to ensure it has the proper index on the DOM
     */
    let canProceed = true;
    if (focus === "click" && this.isFocus) {
      canProceed = false;
      this.isFocus = false;
    } else if (focus === true) {
      this.isFocus = true;
    }
    if (!this.props.disabled && canProceed) {
      const action = focus && !this.state.active ? true : false;
      if (action) {
        this.setState({
          focus: true,
          active: true,
          valid: true,
          zIndex: true
        });
      } else {
        /* When the user exits dropdown the state is change for focus and validation method is called
         */
        this.setState({ focus: false, active: false, zIndex: false });
        this._validationHandler(this.props.errorCallback);
      }

      if (this.props.onClick) {
        this._customOnClickEvent(event);
      }
    }
  };

  _validationHandler = callback => {
    /* Checks that required has been set to true and determines if errorCallback message was passed in a custom error message.
      Also sets state of valid depending on user action
      */
    let validationObject = {
      valid:
        (this.props.required && this.state.value !== "") ||
        !this.props.required,
      message: this.state.errorMessage
    };
    if (callback) {
      validationObject = callback(event, this.state.value);
    }
    this.setState({
      valid: validationObject.valid,
      errorMessage: validationObject.message
    });
  };

  _keyDown = event => {
    const indexValid = typeof this.state.index === "number";
    let newIndex;
    event.preventDefault();
    if (event.key === "ArrowDown") {
      newIndex = indexValid ? this.state.index + 1 : 0;
      let count = React.Children.count(this.props.children);
      if (newIndex < count) {
        this.setState({
          index: newIndex
        });
      }
    } else if (event.key === "ArrowUp") {
      newIndex = this.state.index - 1;
      if (newIndex >= 0) {
        this.setState({
          index: newIndex
        });
      }
    } else if (event.key === "Enter") {
      this._clickHandler(this.state.index, event);
    }
  };

  render() {
    const {
      children,
      className,
      required,
      customLabel,
      buttonWidth,
      disabled,
      name
    } = this.props;
    const active = this.state.active;
    const error = !this.state.valid && !disabled ? true : false;
    let zIndex = this.state.zIndex ? true : false;
    const classes = cx({
      active: active,
      container: true,
      zIndex: zIndex
    });

    const buttonClasses = cx({
      "ra_dropdown__dropdown-button": true,
      ra_dropdown__error: error,
      ra_dropdown__disabledClass: disabled
    });

    // Builds the option list from the children passed in
    // firstChild, lastChild and selected each have unique styling and those classes are added here
    const bound_children = children.map((child, i) => {
      let count = React.Children.count(this.props.children);
      let childClasses = cx({
        ra_dropdown__selected: i === this.state.index,
        ra_dropdown__firstChild: i === 0,
        ra_dropdown__lastChild: i === count - 1
      });
      let kid = (

 {
            // onMouseDown fires before onBlur. If changed to onClick it will fire after onBlur and not work.
            this._clickHandler(i, e);
          }}
        >
          {child}

      );
      return kid;
    });

    const dropdownButtonClasses = cx(buttonClasses);

    return (

 {
          this._toggle(true, e);
        }}
        onBlur={e => {
          this._toggle(false, e);
        }}
        onKeyDown={e => {
          this._keyDown(e);
        }}
      >
        {customLabel &&

            {customLabel}
            {required && *}

}

 {
            this._toggle("click", e);
          }}
          styleName={"content"}
        >

            <ButtonCore
              styleName={"buttonClass"}
              className={dropdownButtonClasses}
            >

                {this.state.output}

            </ButtonCore>

          {this.state.active &&

              {bound_children}

}
          <input type="hidden" value={this.state.value} />

        {error &&

            {this.state.errorMessage}
          }

    );
  }
}

Dropdown.propTypes = {
  /**
   * Text for dropdown label
   * @examples 'Some Label'
   */
  customLabel: PropTypes.string,

  /* Boolean value taht tells the dropdown whether to
    be open or not.*/
  active: PropTypes.bool,

  /* Boolean value that tells the dropdown whether the value is valid and controls error message is returns false.*/
  valid: PropTypes.bool,

  /**
   * If included, dropdown is disabled
   * @examples <Dropdown disabled />, <Dropdown disabled={true} />
   */
  disabled: PropTypes.bool,

  /**
   * Allows user to pass a callback for click events.
   */
  onClick: PropTypes.func,

  /**
   * Allows user to pass a function to be executed when the dropdown state is changed.
   */
  onChange: PropTypes.func,

  /**
   * If included, dropdown will return and error onBlur or onChange if not checked.
   */
  required: PropTypes.bool,

  /**
   * Allows the user to pass a function for custom validation. Should return either true or false.
   */
  errorCallback: PropTypes.func,

  /* . */
  clickEvent: PropTypes.func,

  /* The children elements to be wrapped by the dropdown menu. */
  children: PropTypes.node.isRequired,

  /* Pass CSS styles to className to set them on the dropdown component. */
  className: PropTypes.string,

  /* The name of the key value used when submitting the dropdown value. */
  name: PropTypes.string,

  /* Default text to show in collapsed dropdown on initial render */
  defaultText: PropTypes.string,

  /* Allows user to ask for user feedback before changing the selected value of the Dropdown. */
  onBeforeChange: PropTypes.func,

  /* Allows user to set custom width of dropdown */
  buttonWidth: PropTypes.string
};

Dropdown.defaultProps = {
  className: "",
  buttonWidth: "160",
  required: false,
  disabled: false
};

export default Dropdown;
<|endoftext|>


<commit_before>Package.describe({
    name: 'bixin:meteor-plyr',
    summary: 'A simple, accessible HTML5 media player.',
    version: '0.0.3',
    git: 'https://github.com/wbashir/meteor-plyr'
});

Package.onUse(function(api) {
    api.versionsFrom('1.0');

    api.use('jquery', 'client');

    var path = Npm.require('path');
    var assetPath = path.join('files/sprite/');

    api.addFiles('files/plyr.css', 'client');
    api.addFiles('files/plyr.js', 'client');
    api.addFiles('sprite.svg', 'client');
    //var assetFiles = [
    //    assetPath + 'icon-captions-off.svg',
    //    assetPath + 'icon-captions-on.svg',
    //    assetPath + 'icon-enter-fullscreen.svg',
    //    assetPath + 'icon-exit-fullscreen.svg',
    //    assetPath + 'icon-fast-forward.svg',
    //    assetPath + 'icon-muted.svg',
    //    assetPath + 'icon-pause.svg',
    //    assetPath + 'icon-play.svg',
    //    assetPath + 'icon-restart.svg',
    //    assetPath + 'icon-rewind.svg',
    //    assetPath + 'icon-volume.svg'
    //];
    //api.add_files(assetFiles, 'client');

});
<commit_msg>Bump ver to relocate on atmosphere<commit_after>Package.describe({
    name: 'bixin:meteor-plyr',
    summary: 'A simple, accessible HTML5 media player.',
    version: '0.0.4',
    git: 'https://github.com/wbashir/meteor-plyr'
});

Package.onUse(function(api) {
    api.versionsFrom('1.0');

    api.use('jquery', 'client');

    var path = Npm.require('path');
    var assetPath = path.join('files/sprite/');

    api.addFiles('files/plyr.css', 'client');
    api.addFiles('files/plyr.js', 'client');
    api.addFiles('sprite.svg', 'client');
    //var assetFiles = [
    //    assetPath + 'icon-captions-off.svg',
    //    assetPath + 'icon-captions-on.svg',
    //    assetPath + 'icon-enter-fullscreen.svg',
    //    assetPath + 'icon-exit-fullscreen.svg',
    //    assetPath + 'icon-fast-forward.svg',
    //    assetPath + 'icon-muted.svg',
    //    assetPath + 'icon-pause.svg',
    //    assetPath + 'icon-play.svg',
    //    assetPath + 'icon-restart.svg',
    //    assetPath + 'icon-rewind.svg',
    //    assetPath + 'icon-volume.svg'
    //];
    //api.add_files(assetFiles, 'client');

});
<|endoftext|>


<commit_before>'use strict'

importScripts('https://cdn.socket.io/socket.io-1.1.0.js')

let events = {}
let port, socket, roomName, namespace, users, me, locations, volumes, dimensions, screen, distances

let easyrtcids = new Map()

let translate = {x: 0, y: 0}

function ready (p) {
  port = p

  port.onmessage = handleMessage
  port.onerror = handleError

  on('room-name', handleRoomName)
}

function handleRoomName (roomName) {
  roomName = roomName
  namespace = '/' + roomName
  socket = io(namespace)
  initialize()
}

function initialize () {
  on('me', announceMe)
  on('trash-me', trashMe)
  on('my-location', announceLocation)
  on('my-volume', announceVolume)
  on('my-announcement', announceAnnouncement)
  on('my-response', announceResponse)
  on('request-announcement', requestAnnouncement)

  on('screen', receiveScreen)

  socket.on('connect', handleUsers)
  socket.on('users', handleUsers)
  socket.on('locations', handleLocations)
  socket.on('volumes', handleVolumes)
  socket.on('dimensions', handleDimensions)
  socket.on('announcement', handleAnnouncement)
  socket.on('distances', handleDistances)
}

function on(event, fn) {
  if (!events[event]) {
    events[event] = []
  }
  events[event].push(fn)
}

function off(event, fn) {
  events[event] = events[event].filter(f => f != fn)
}

function handleMessage (msg) {
  if (!msg.data.event) {
    handleError(`Worker posted message without event descriptor: ${msg.data}`)
  }

  if (events[msg.data.event]) {
    events[msg.data.event].forEach(fn => {
      fn(msg.data.data)
    })
  }
}

function handleError (err) {
  emit('error', JSON.stringify(err))
}

function emit (event, data) {
  port.postMessage({event, data})
}

function handleUsers (data) {
  const map = new Map(data)
  if (data) {
    users = map
    emit('users', [...users])
  }

  map.forEach((user, uid) => {
    easyrtcids.set(uid, user.easyrtcid)
  })
}

function announceMe (newme) {
  me = newme
  socket.emit('me', me)
}

function trashMe () {
  me = null
  socket.emit('trash-me')
}

function announceLocation (loc) {
  socket.emit('my-location', loc)
}

function announceAnnouncement (msg) {
  socket.emit('my-announcement', msg)
}

function announceResponse (data) {
  const {announcement, type, reason, date} = data
  const user = me.id
  const userAvatar = me.avatar
  const userName = me.name
  announcement.responses[type].push({user, reason, date, userAvatar, userName})
  socket.emit('my-announcement', announcement)
}

function requestAnnouncement () {
  socket.emit('request-announcement')
}

function announceVolume (vol) {
  socket.emit('my-volume', vol)
}

function handleLocations (data) {
  locations = new Map(data)
  emit('locations', [...locations])

  const myLocation = locations.get(me.id)
  if (isInFourth(myLocation)) {
    translate = calcTranslate(myLocation)
    emit('translate', translate)
  }
}

function handleVolumes (data) {
  volumes = new Map(data)
  emit('volumes', [...volumes])
}

function handleDimensions (data) {
  dimensions = data
  emit('dimensions', dimensions)
}

function handleAnnouncement (data) {
  if (data) {
    emit('announcement', data)
  }
}

function handleDistances (data) {
  if (data) {
    let copy = {}
    for (let uid in data) {
      copy[easyrtcids.get(uid)] = data[uid]
    }
    emit('distances', copy)
    distances = copy
  }
}

function receiveScreen (size) {
  screen = {
    x: size.x,
    y: size.y
  }
}

function calcTranslate (loc) {
  if (loc) {
    const x = (-1) * loc.x + (screen.x / 2) - 25
    const y = (-1) * loc.y + (screen.y / 2) - 25
    return {x, y}
  } else {
    return {x: 0, y: 0}
  }
}

function isInFourth (loc) {
  let display
  let edge = {}
  if (loc) {
    display = {
      x: loc.x + translate.x,
      y: loc.y + translate.y
    }
  } else {
    display = {x: 0, y: 0}
  }

  edge.w = screen.x / 6
  edge.h = screen.y / 6
  if ((display.x < edge.w) || (display.x > (screen.x - edge.w)))
    return true
  if ((display.y < edge.h) || (display.y > (screen.y - edge.h)))
    return true
  return false
}

onconnect = function (e) {
  ready(e.ports[0])
}
<commit_msg>don't executre functions before me is defined<commit_after>'use strict'

importScripts('https://cdn.socket.io/socket.io-1.1.0.js')

let events = {}
let port, socket, roomName, namespace, users, me, locations, volumes, dimensions, screen, distances

let easyrtcids = new Map()

let translate = {x: 0, y: 0}

function ready (p) {
  port = p

  port.onmessage = handleMessage
  port.onerror = handleError

  on('room-name', handleRoomName)
}

function handleRoomName (roomName) {
  roomName = roomName
  namespace = '/' + roomName
  socket = io(namespace)
  initialize()
}

function initialize () {
  on('me', announceMe)
  on('trash-me', trashMe)
  on('my-location', announceLocation)
  on('my-volume', announceVolume)
  on('my-announcement', announceAnnouncement)
  on('my-response', announceResponse)
  on('request-announcement', requestAnnouncement)

  on('screen', receiveScreen)

  socket.on('connect', handleUsers)
  socket.on('users', handleUsers)
  socket.on('locations', handleLocations)
  socket.on('volumes', handleVolumes)
  socket.on('dimensions', handleDimensions)
  socket.on('announcement', handleAnnouncement)
  socket.on('distances', handleDistances)
}

function on(event, fn) {
  if (!events[event]) {
    events[event] = []
  }
  events[event].push(fn)
}

function off(event, fn) {
  events[event] = events[event].filter(f => f != fn)
}

function handleMessage (msg) {
  if (!msg.data.event) {
    handleError(`Worker posted message without event descriptor: ${msg.data}`)
  }

  if (events[msg.data.event]) {
    events[msg.data.event].forEach(fn => {
      fn(msg.data.data)
    })
  }
}

function handleError (err) {
  emit('error', JSON.stringify(err))
}

function emit (event, data) {
  port.postMessage({event, data})
}

function handleUsers (data) {
  const map = new Map(data)
  if (data) {
    users = map
    emit('users', [...users])
  }

  map.forEach((user, uid) => {
    easyrtcids.set(uid, user.easyrtcid)
  })
}

function announceMe (newme) {
  me = newme
  socket.emit('me', me)
}

function trashMe () {
  me = null
  socket.emit('trash-me')
}

function announceLocation (loc) {
  socket.emit('my-location', loc)
}

function announceAnnouncement (msg) {
  socket.emit('my-announcement', msg)
}

function announceResponse (data) {
  if (!me) return handleError('Cannot announce response - me is not defined')

  const {announcement, type, reason, date} = data
  const user = me.id
  const userAvatar = me.avatar
  const userName = me.name
  announcement.responses[type].push({user, reason, date, userAvatar, userName})
  socket.emit('my-announcement', announcement)
}

function requestAnnouncement () {
  socket.emit('request-announcement')
}

function announceVolume (vol) {
  socket.emit('my-volume', vol)
}

function handleLocations (data) {
  if (!me) return null

  locations = new Map(data)
  emit('locations', [...locations])

  const myLocation = locations.get(me.id)
  if (isInFourth(myLocation)) {
    translate = calcTranslate(myLocation)
    emit('translate', translate)
  }
}

function handleVolumes (data) {
  volumes = new Map(data)
  emit('volumes', [...volumes])
}

function handleDimensions (data) {
  dimensions = data
  emit('dimensions', dimensions)
}

function handleAnnouncement (data) {
  if (data) {
    emit('announcement', data)
  }
}

function handleDistances (data) {
  if (data) {
    let copy = {}
    for (let uid in data) {
      copy[easyrtcids.get(uid)] = data[uid]
    }
    emit('distances', copy)
    distances = copy
  }
}

function receiveScreen (size) {
  screen = {
    x: size.x,
    y: size.y
  }
}

function calcTranslate (loc) {
  if (loc) {
    const x = (-1) * loc.x + (screen.x / 2) - 25
    const y = (-1) * loc.y + (screen.y / 2) - 25
    return {x, y}
  } else {
    return {x: 0, y: 0}
  }
}

function isInFourth (loc) {
  let display
  let edge = {}
  if (loc) {
    display = {
      x: loc.x + translate.x,
      y: loc.y + translate.y
    }
  } else {
    display = {x: 0, y: 0}
  }

  edge.w = screen.x / 6
  edge.h = screen.y / 6
  if ((display.x < edge.w) || (display.x > (screen.x - edge.w)))
    return true
  if ((display.y < edge.h) || (display.y > (screen.y - edge.h)))
    return true
  return false
}

onconnect = function (e) {
  ready(e.ports[0])
}
<|endoftext|>


<commit_before>'use strict';

streamaApp.factory('playerService', [
  '$stateParams', '$sce', '$state', '$rootScope', 'socketService', 'apiService', '$interval',
  function ($stateParams, $sce, $state, $rootScope, socketService, apiService, $interval) {

    var videoData = null;

    var videoOptions = {
      customStartingTime: 0,
      rememberVolumeSetting: true,
      videoMetaTitle: '',
      videoMetaSubtitle: '',
      videoMetaDescription: '',
      videoSrc: '',
      videoType: '',
      videoTrack: '',
      videoOverlayEnabled: true,
      showEpisodeBrowser: false,
      showNextButton: false,
      showSocketSession: true,
      episodeList: [],
      selectedEpisodes: [],
      currentEpisode: {},
      onSocketSessionCreate: angular.noop,
      onTimeChange: angular.noop,
      onError: angular.noop,
      onPlay: angular.noop,
      onPause: angular.noop,
      onClose: angular.noop,
      onNext: angular.noop
    };

    return {

      setVideoOptions: function (video) {
        videoData = video;
        videoOptions.videoSrc = $sce.trustAsResourceUrl(video.files[0].src);
        videoOptions.videoType = video.files[0].contentType;

        if(video.subtitles && video.subtitles.length){
          videoOptions.videoTrack = $sce.trustAsResourceUrl(video.subtitles[0].src);
        }

        videoOptions.videoMetaTitle = (video.show ? video.show.name : video.title);
        videoOptions.videoMetaSubtitle = (video.show ? video.episodeString + ' - ' + video.name : video.release_date.substring(0, 4));
        videoOptions.videoMetaDescription = video.overview;

        if(videoData.nextEpisode){
          videoOptions.showNextButton = true;
        }

        if(videoData.show){
          videoOptions.showEpisodeBrowser = true;

          apiService.tvShow.episodesForTvShow(videoData.show.id).success(function (episodes) {
            videoOptions.episodeList = _.groupBy(episodes, 'season_number');
            videoOptions.selectedEpisodes = videoOptions.episodeList[videoData.season_number];
            videoOptions.currentEpisode = {
              episode: videoData.episode_number,
              season: videoData.season_number
            };
          });
        }

        if($stateParams.currentTime){
          videoOptions.customStartingTime = $stateParams.currentTime;
        }
        else if(video.viewedStatus){
          videoOptions.customStartingTime = video.viewedStatus.currentPlayTime;
        }else{
          videoOptions.customStartingTime = 0;
        }

        videoOptions.onPlay = this.onVideoPlay.bind(videoOptions);
        videoOptions.onPause = this.onVideoPause.bind(videoOptions);
        videoOptions.onError = this.onVideoError.bind(videoOptions);
        videoOptions.onTimeChange = this.onVideoTimeChange.bind(videoOptions);
        videoOptions.onClose = this.onVideoClose.bind(videoOptions);
        videoOptions.onNext = this.onNext.bind(videoOptions);
        videoOptions.onSocketSessionCreate = this.onSocketSessionCreate.bind(videoOptions);

        return videoOptions;
      },

      viewingStatusSaveInterval: null,

      onVideoPlay: function (videoElement, socketData) {
        var that = this;
        console.log('%c onVideoPlay', 'color: deeppink; font-weight: bold; text-shadow: 0 0 5px deeppink;');

        that.viewingStatusSaveInterval = $interval(function() {
          var params = {videoId: videoData.id, currentTime: videoElement.currentTime, runtime: videoElement.duration};

          if(params.runtime && params.videoId){
            apiService.viewingStatus.save(params);
          }
        }, 5000);

        if($stateParams.sessionId && !socketData){
          console.log('%c send socket event PLAY', 'color: deeppink; font-weight: bold; text-shadow: 0 0 5px deeppink;');
          apiService.websocket.triggerPlayerAction({socketSessionId: $stateParams.sessionId, playerAction: 'play', currentPlayerTime: videoElement.currentTime});
        }
      },

      onVideoPause: function (videoElement, socketData) {
        console.log('%c onVideoPause', 'color: deeppink; font-weight: bold; text-shadow: 0 0 5px deeppink;', socketData);
        var that = this;
        $interval.cancel(that.viewingStatusSaveInterval);

        if($stateParams.sessionId && socketData){
          if(videoElement.currentTime+1.5 > socketData.currentPlayerTime || videoElement.currentTime-1.5 < socketData.currentPlayerTime){
            videoElement.currentTime = socketData.currentPlayerTime;
          }
        }

        if($stateParams.sessionId && !socketData){
          console.log('%c send socket event PAUSE', 'color: deeppink; font-weight: bold; text-shadow: 0 0 5px deeppink;');
          apiService.websocket.triggerPlayerAction({socketSessionId: $stateParams.sessionId, playerAction: 'pause', currentPlayerTime: videoElement.currentTime});
        }
      },

      onVideoClose: function () {
        console.log('%c onVideoClose', 'color: deeppink; font-weight: bold; text-shadow: 0 0 5px deeppink;');
        var that = this;
        $state.go('dash', {});
      },

      onVideoError: function () {
        var that = this;
        console.log('%c onVideoError', 'color: deeppink; font-weight: bold; text-shadow: 0 0 5px deeppink;');

        if($state.current.name == 'player'){
          alertify.alert('There seems to be a problem adding the video-file to the player. This is most likely due to a codec-problem. ' +
            'Try converting it to a compatible HTML5 codec, remove the currently attached file and re-add it. If the codecs are fine, check the error log of the server.', function () {
            if($rootScope.currentUser.authorities.length){
              if(video.show){
                $state.go('admin.show', {showId: videoData.show.id});
              }else{
                $state.go('admin.movie', {movieId: videoData.id});
              }
            }else{
              $state.go('dash', {});
            }

          });
        }
      },

      onVideoTimeChange: function (slider, duration) {
        var params = {videoId: videoData.id, currentTime: slider.value, runtime: duration};
        apiService.viewingStatus.save(params);

        if($stateParams.sessionId){
          apiService.websocket.triggerPlayerAction({socketSessionId: $stateParams.sessionId, playerAction: 'timeChange', currentPlayerTime: slider.value});
        }
      },

      onSocketSessionCreate: function () {
        alertify.confirm('By creating a new session you will be redirected back to this player, but this time you will ' +
          'have a unique session ID in the url. Share this with your friends to have a syncronized watching experience with them!', function (confirmed) {
          if(confirmed){
            $stateParams.sessionId = socketService.getUUID();
            $state.go($state.current, $stateParams, {reload: true});
          }
        });
      },

      handleMissingFileError: function (video) {
        var hasError = false;

        if(!video.files || !video.files.length){
          hasError = true;
          alertify.alert('There is a problem with this content. It seems you removed the associated video fime from it.', function () {
            if($rootScope.currentUser.authorities.length){
              if(video.show){
                $state.go('admin.show', {showId: video.show.id});
              }else{
                $state.go('admin.movie', {movieId: video.id});
              }
            }else{
              $state.go('dash', {});
            }

          });
        }

        return hasError;
      },

      handleWrongBasepathError: function (video) {
        var hasError = false;
        var videoSource = video.files[0].src;
        var basePath = location.origin + $('base').attr('href');

        if(videoSource.indexOf(basePath) == -1){
          hasError = true;
          alertify.alert('You video get\'s included using the wrong Base Path, but you are browsing the page via "'+basePath+'". ' +
            'Make sure you set the correct Base Path in the settings and that you are using it to browse the application.', function () {
            if(_.find($rootScope.currentUser.authorities, {authority: "ROLE_ADMIN"})){
              $state.go('admin.settings');
            }else{
              $state.go('dash', {});
            }

          });
        }
        return hasError;
      },

      registerSocketListener: function () {
        if($stateParams.sessionId){
          socketService.registerPlayerSessonListener($stateParams.sessionId);
        }
      },

      destroyPlayer: function () {
        console.log('%c $stateChangeSuccess', 'color: deeppink; font-weight: bold; text-shadow: 0 0 5px deeppink;');
        var that = this;
        $interval.cancel(that.viewingStatusSaveInterval);
        socketService.unsubscribe();
      },

      handleSocketEvent: function (data) {
        if(data.browserSocketUUID != socketService.browserSocketUUID){
          console.log('%c handleSocketEvent', 'color: deeppink; font-weight: bold; text-shadow: 0 0 5px deeppink;');
          switch (data.playerAction){
            case 'play':
              $rootScope.$broadcast('triggerVideoPlay', data);
              break;
            case 'pause':
              $rootScope.$broadcast('triggerVideoPause', data);
              break;
            case 'timeChange':
              $rootScope.$broadcast('triggerVideoTimeChange', data);
              break;
          }
        }
      },

      onNext: function () {
        $state.go('player', {videoId: videoData.nextEpisode.id});
      }
    };
}]);
<commit_msg>fixed redirect<commit_after>'use strict';

streamaApp.factory('playerService', [
  '$stateParams', '$sce', '$state', '$rootScope', 'socketService', 'apiService', '$interval',
  function ($stateParams, $sce, $state, $rootScope, socketService, apiService, $interval) {

    var videoData = null;

    var videoOptions = {
      customStartingTime: 0,
      rememberVolumeSetting: true,
      videoMetaTitle: '',
      videoMetaSubtitle: '',
      videoMetaDescription: '',
      videoSrc: '',
      videoType: '',
      videoTrack: '',
      videoOverlayEnabled: true,
      showEpisodeBrowser: false,
      showNextButton: false,
      showSocketSession: true,
      episodeList: [],
      selectedEpisodes: [],
      currentEpisode: {},
      onSocketSessionCreate: angular.noop,
      onTimeChange: angular.noop,
      onError: angular.noop,
      onPlay: angular.noop,
      onPause: angular.noop,
      onClose: angular.noop,
      onNext: angular.noop
    };

    return {

      setVideoOptions: function (video) {
        videoData = video;
        videoOptions.videoSrc = $sce.trustAsResourceUrl(video.files[0].src);
        videoOptions.videoType = video.files[0].contentType;

        if(video.subtitles && video.subtitles.length){
          videoOptions.videoTrack = $sce.trustAsResourceUrl(video.subtitles[0].src);
        }

        videoOptions.videoMetaTitle = (video.show ? video.show.name : video.title);
        videoOptions.videoMetaSubtitle = (video.show ? video.episodeString + ' - ' + video.name : video.release_date.substring(0, 4));
        videoOptions.videoMetaDescription = video.overview;

        if(videoData.nextEpisode){
          videoOptions.showNextButton = true;
        }

        if(videoData.show){
          videoOptions.showEpisodeBrowser = true;

          apiService.tvShow.episodesForTvShow(videoData.show.id).success(function (episodes) {
            videoOptions.episodeList = _.groupBy(episodes, 'season_number');
            videoOptions.selectedEpisodes = videoOptions.episodeList[videoData.season_number];
            videoOptions.currentEpisode = {
              episode: videoData.episode_number,
              season: videoData.season_number
            };
          });
        }

        if($stateParams.currentTime){
          videoOptions.customStartingTime = $stateParams.currentTime;
        }
        else if(video.viewedStatus){
          videoOptions.customStartingTime = video.viewedStatus.currentPlayTime;
        }else{
          videoOptions.customStartingTime = 0;
        }

        videoOptions.onPlay = this.onVideoPlay.bind(videoOptions);
        videoOptions.onPause = this.onVideoPause.bind(videoOptions);
        videoOptions.onError = this.onVideoError.bind(videoOptions);
        videoOptions.onTimeChange = this.onVideoTimeChange.bind(videoOptions);
        videoOptions.onClose = this.onVideoClose.bind(videoOptions);
        videoOptions.onNext = this.onNext.bind(videoOptions);
        videoOptions.onSocketSessionCreate = this.onSocketSessionCreate.bind(videoOptions);

        return videoOptions;
      },

      viewingStatusSaveInterval: null,

      onVideoPlay: function (videoElement, socketData) {
        var that = this;
        console.log('%c onVideoPlay', 'color: deeppink; font-weight: bold; text-shadow: 0 0 5px deeppink;');

        that.viewingStatusSaveInterval = $interval(function() {
          var params = {videoId: videoData.id, currentTime: videoElement.currentTime, runtime: videoElement.duration};

          if(params.runtime && params.videoId){
            apiService.viewingStatus.save(params);
          }
        }, 5000);

        if($stateParams.sessionId && !socketData){
          console.log('%c send socket event PLAY', 'color: deeppink; font-weight: bold; text-shadow: 0 0 5px deeppink;');
          apiService.websocket.triggerPlayerAction({socketSessionId: $stateParams.sessionId, playerAction: 'play', currentPlayerTime: videoElement.currentTime});
        }
      },

      onVideoPause: function (videoElement, socketData) {
        console.log('%c onVideoPause', 'color: deeppink; font-weight: bold; text-shadow: 0 0 5px deeppink;', socketData);
        var that = this;
        $interval.cancel(that.viewingStatusSaveInterval);

        if($stateParams.sessionId && socketData){
          if(videoElement.currentTime+1.5 > socketData.currentPlayerTime || videoElement.currentTime-1.5 < socketData.currentPlayerTime){
            videoElement.currentTime = socketData.currentPlayerTime;
          }
        }

        if($stateParams.sessionId && !socketData){
          console.log('%c send socket event PAUSE', 'color: deeppink; font-weight: bold; text-shadow: 0 0 5px deeppink;');
          apiService.websocket.triggerPlayerAction({socketSessionId: $stateParams.sessionId, playerAction: 'pause', currentPlayerTime: videoElement.currentTime});
        }
      },

      onVideoClose: function () {
        console.log('%c onVideoClose', 'color: deeppink; font-weight: bold; text-shadow: 0 0 5px deeppink;');
        var that = this;
        $state.go('dash', {});
      },

      onVideoError: function () {
        var that = this;
        console.log('%c onVideoError', 'color: deeppink; font-weight: bold; text-shadow: 0 0 5px deeppink;');

        if($state.current.name == 'player'){
          alertify.alert('There seems to be a problem adding the video-file to the player. This is most likely due to a codec-problem. ' +
            'Try converting it to a compatible HTML5 codec, remove the currently attached file and re-add it. If the codecs are fine, check the error log of the server.', function () {
            if($rootScope.currentUser.authorities.length){
              if(videoData.show){
                $state.go('admin.show', {showId: videoData.show.id});
              }else{
                $state.go('admin.movie', {movieId: videoData.id});
              }
            }else{
              $state.go('dash', {});
            }

          });
        }
      },

      onVideoTimeChange: function (slider, duration) {
        var params = {videoId: videoData.id, currentTime: slider.value, runtime: duration};
        apiService.viewingStatus.save(params);

        if($stateParams.sessionId){
          apiService.websocket.triggerPlayerAction({socketSessionId: $stateParams.sessionId, playerAction: 'timeChange', currentPlayerTime: slider.value});
        }
      },

      onSocketSessionCreate: function () {
        alertify.confirm('By creating a new session you will be redirected back to this player, but this time you will ' +
          'have a unique session ID in the url. Share this with your friends to have a syncronized watching experience with them!', function (confirmed) {
          if(confirmed){
            $stateParams.sessionId = socketService.getUUID();
            $state.go($state.current, $stateParams, {reload: true});
          }
        });
      },

      handleMissingFileError: function (video) {
        var hasError = false;

        if(!video.files || !video.files.length){
          hasError = true;
          alertify.alert('There is a problem with this content. It seems you removed the associated video fime from it.', function () {
            if($rootScope.currentUser.authorities.length){
              if(video.show){
                $state.go('admin.show', {showId: video.show.id});
              }else{
                $state.go('admin.movie', {movieId: video.id});
              }
            }else{
              $state.go('dash', {});
            }

          });
        }

        return hasError;
      },

      handleWrongBasepathError: function (video) {
        var hasError = false;
        var videoSource = video.files[0].src;
        var basePath = location.origin + $('base').attr('href');

        if(videoSource.indexOf(basePath) == -1){
          hasError = true;
          alertify.alert('You video get\'s included using the wrong Base Path, but you are browsing the page via "'+basePath+'". ' +
            'Make sure you set the correct Base Path in the settings and that you are using it to browse the application.', function () {
            if(_.find($rootScope.currentUser.authorities, {authority: "ROLE_ADMIN"})){
              $state.go('admin.settings');
            }else{
              $state.go('dash', {});
            }

          });
        }
        return hasError;
      },

      registerSocketListener: function () {
        if($stateParams.sessionId){
          socketService.registerPlayerSessonListener($stateParams.sessionId);
        }
      },

      destroyPlayer: function () {
        console.log('%c $stateChangeSuccess', 'color: deeppink; font-weight: bold; text-shadow: 0 0 5px deeppink;');
        var that = this;
        $interval.cancel(that.viewingStatusSaveInterval);
        socketService.unsubscribe();
      },

      handleSocketEvent: function (data) {
        if(data.browserSocketUUID != socketService.browserSocketUUID){
          console.log('%c handleSocketEvent', 'color: deeppink; font-weight: bold; text-shadow: 0 0 5px deeppink;');
          switch (data.playerAction){
            case 'play':
              $rootScope.$broadcast('triggerVideoPlay', data);
              break;
            case 'pause':
              $rootScope.$broadcast('triggerVideoPause', data);
              break;
            case 'timeChange':
              $rootScope.$broadcast('triggerVideoTimeChange', data);
              break;
          }
        }
      },

      onNext: function () {
        $state.go('player', {videoId: videoData.nextEpisode.id});
      }
    };
}]);
<|endoftext|>


<commit_before>/* global angular: true */

angular.module('webPGQ.services')
    .service('graph', ['dagreD3', function(dagreD3)
    {
        var maxTotalCost, lastNodeDefID = 0;

        var nodeTypeIcons = [
            'Aggregate',
            'Append',
            'Function Scan',
            'Hash Anti Join',
            'Hash Join',
            'Hash Setop Except',
            'Hash Setop Intersect',
            'Hash Setop Unknown',
            'Hash',
            'Index Scan',
            'Index Only Scan',
            'Limit',
            'Materialize',
            'Nested Loop',
            'Result',
            'Seq Scan',
            'Sort',
            'Window Agg'
        ];

        var initPlanRefRE = /\$\d+\b/g;
        var nodeRefRE = /\$\d+\b|\((CTE [^)]+|SubPlan \d+)\)/g;

        function findReferences(key, val)
        {
            var match;
            if(key == 'Subplan Name')
            {
                if((match = initPlanRefRE.exec(val)) !== null)
                {
                    this.refName = match[0];
                }
                else
                {
                    this.refName = val;
                } // end if
            }
            else if(key == 'CTE Name')
            {
                this.references.push({name: 'CTE ' + val, field: key});
            }
            else
            {
                while((match = nodeRefRE.exec(val)) !== null)
                {
                    this.references.push({name: match[1] || match[0], field: key});
                } // end while
            } // end if
        } // end findReferences

        var nodeIDsByRef = {};

        var graphService = {
            nodesFromPlan: function(graph, plan)
            {
                maxTotalCost = Math.max(maxTotalCost, plan['Total Cost']);

                var metadata = {};
                var thisNode = {references: []};
                for(var key in plan)
                {
                    if(key != 'Plans')
                    {
                        var val = plan[key];
                        metadata[key] = val;

                        if(Array.isArray(val))
                        {
                            val.forEach(findReferences.bind(thisNode, key));
                        }
                        else
                        {
                            findReferences.call(thisNode, key, val);
                        } // end if
                    } // end if
                } // end for

                if(thisNode.references.length > 0)
                {
                    // Defer calculating `References` until later, after we've fully populated nodeIDsByRef.
                    var fullRefs;
                    Object.defineProperty(metadata, 'References', {
                        enumerable: true,
                        get: function()
                        {
                            if(!fullRefs)
                            {
                                fullRefs = thisNode.references.map(function(ref)
                                {
                                    ref.id = nodeIDsByRef[ref.name];
                                    return ref;
                                });
                            } // end if
                            return fullRefs;
                        }
                    });
                } // end if

                var useDef;
                if(nodeTypeIcons.indexOf(plan['Node Type']) != -1)
                {
                    useDef = 'node-def-' + (++lastNodeDefID);
                } // end if

                var thisNodeID = graph.addNode(null, {
                    label: plan['Node Type'] + (plan.Strategy ? ' ' + plan.Strategy : ''),
                    metadata: metadata,
                    style: 'fill: #bbb',
                    useDef: useDef,
                    iconURL: 'icons/' + plan['Node Type'] + '.svg'
                });

                if(thisNode.refName)
                {
                    nodeIDsByRef[thisNode.refName] = thisNodeID;
                } // end if

                (plan.Plans || [])
                    .forEach(function(childPlan)
                    {
                        var childInfo = graphService.nodesFromPlan(graph, childPlan);

                        // Calculate an edge size between 1 and 40 pixels, based on percentage of the max total cost.
                        var edgeSize = (childInfo['Total Cost'] / maxTotalCost * 39) + 1;

                        graph.addEdge(null, childInfo.id, thisNodeID, {
                            label: childInfo['Startup Cost'] + '..' + childInfo['Total Cost'],
                            style: 'stroke-width: ' + edgeSize + 'px',
                        });
                    });

                return {
                    id: thisNodeID,
                    'Plan Rows': plan['Plan Rows'],
                    'Plan Width': plan['Plan Width'],
                    'Startup Cost': plan['Startup Cost'],
                    'Total Cost': plan['Total Cost'],
                };
            }, // end nodesFromPlan

            fromPlan: function(plans)
            {
                // Create a new directed graph
                var graph = new dagreD3.Digraph();

                maxTotalCost = 0;

                plans.forEach(function(plan)
                {
                    graphService.nodesFromPlan(graph, plan.Plan);
                });

                return graph;
            } // end graphFromPlan
        }; // end graphService

        return graphService;
    }]);
<commit_msg>Parse string plans as JSON, since PostgreSQL < 9.2 returns JSON plans as strings.<commit_after>/* global angular: true */

angular.module('webPGQ.services')
    .service('graph', ['dagreD3', function(dagreD3)
    {
        var maxTotalCost, lastNodeDefID = 0;

        var nodeTypeIcons = [
            'Aggregate',
            'Append',
            'Function Scan',
            'Hash Anti Join',
            'Hash Join',
            'Hash Setop Except',
            'Hash Setop Intersect',
            'Hash Setop Unknown',
            'Hash',
            'Index Scan',
            'Index Only Scan',
            'Limit',
            'Materialize',
            'Nested Loop',
            'Result',
            'Seq Scan',
            'Sort',
            'Window Agg'
        ];

        var initPlanRefRE = /\$\d+\b/g;
        var nodeRefRE = /\$\d+\b|\((CTE [^)]+|SubPlan \d+)\)/g;

        function findReferences(key, val)
        {
            var match;
            if(key == 'Subplan Name')
            {
                if((match = initPlanRefRE.exec(val)) !== null)
                {
                    this.refName = match[0];
                }
                else
                {
                    this.refName = val;
                } // end if
            }
            else if(key == 'CTE Name')
            {
                this.references.push({name: 'CTE ' + val, field: key});
            }
            else
            {
                while((match = nodeRefRE.exec(val)) !== null)
                {
                    this.references.push({name: match[1] || match[0], field: key});
                } // end while
            } // end if
        } // end findReferences

        var nodeIDsByRef = {};

        var graphService = {
            nodesFromPlan: function(graph, plan)
            {
                maxTotalCost = Math.max(maxTotalCost, plan['Total Cost']);

                var metadata = {};
                var thisNode = {references: []};
                for(var key in plan)
                {
                    if(key != 'Plans')
                    {
                        var val = plan[key];
                        metadata[key] = val;

                        if(Array.isArray(val))
                        {
                            val.forEach(findReferences.bind(thisNode, key));
                        }
                        else
                        {
                            findReferences.call(thisNode, key, val);
                        } // end if
                    } // end if
                } // end for

                if(thisNode.references.length > 0)
                {
                    // Defer calculating `References` until later, after we've fully populated nodeIDsByRef.
                    var fullRefs;
                    Object.defineProperty(metadata, 'References', {
                        enumerable: true,
                        get: function()
                        {
                            if(!fullRefs)
                            {
                                fullRefs = thisNode.references.map(function(ref)
                                {
                                    ref.id = nodeIDsByRef[ref.name];
                                    return ref;
                                });
                            } // end if
                            return fullRefs;
                        }
                    });
                } // end if

                var useDef;
                if(nodeTypeIcons.indexOf(plan['Node Type']) != -1)
                {
                    useDef = 'node-def-' + (++lastNodeDefID);
                } // end if

                var thisNodeID = graph.addNode(null, {
                    label: plan['Node Type'] + (plan.Strategy ? ' ' + plan.Strategy : ''),
                    metadata: metadata,
                    style: 'fill: #bbb',
                    useDef: useDef,
                    iconURL: 'icons/' + plan['Node Type'] + '.svg'
                });

                if(thisNode.refName)
                {
                    nodeIDsByRef[thisNode.refName] = thisNodeID;
                } // end if

                (plan.Plans || [])
                    .forEach(function(childPlan)
                    {
                        var childInfo = graphService.nodesFromPlan(graph, childPlan);

                        // Calculate an edge size between 1 and 40 pixels, based on percentage of the max total cost.
                        var edgeSize = (childInfo['Total Cost'] / maxTotalCost * 39) + 1;

                        graph.addEdge(null, childInfo.id, thisNodeID, {
                            label: childInfo['Startup Cost'] + '..' + childInfo['Total Cost'],
                            style: 'stroke-width: ' + edgeSize + 'px',
                        });
                    });

                return {
                    id: thisNodeID,
                    'Plan Rows': plan['Plan Rows'],
                    'Plan Width': plan['Plan Width'],
                    'Startup Cost': plan['Startup Cost'],
                    'Total Cost': plan['Total Cost'],
                };
            }, // end nodesFromPlan

            fromPlan: function(plans)
            {
                // Create a new directed graph
                var graph = new dagreD3.Digraph();

                maxTotalCost = 0;

                if(typeof plans == 'string')
                {
                    // PostgreSQL < 9.2 returns JSON plans as strings.
                    plans = JSON.parse(plans);
                } // end if

                plans.forEach(function(plan)
                {
                    graphService.nodesFromPlan(graph, plan.Plan);
                });

                return graph;
            } // end graphFromPlan
        }; // end graphService

        return graphService;
    }]);
<|endoftext|>


<commit_before>import React, { PropTypes } from "react";
import cx from 'classNames';

/**
 * Simple component for a basic checkbox
 */
class Checkbox extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      "active": false,
      "onChange": this.props.onChange
    };
  }

  _clickHandler = (event) => {
    const selected = event.target.innerText;
    if (this.wrapperRef && this.wrapperRef.contains(event.target)) {
      console.log("state-inner?: ", this.state.active)

      this.setState({
        'active': !this.state.active
      })
    }
    console.log("state-outer?: ", this.state.active)
  };

  render() {
    const { title, label, checked, disabled, inline, className, defaultChecked, ...props } = this.props;
    const inlineText = inline ? "inline" : "";
    const componentClasses = cx({
      "block": !inline,
      inline,
      disabled,
      [className]: className
    });

    const controlStyle = cx({
      "control": true,
      "control--checkbox": true
    })

    const title_label = title ? title : label;

    const classes = cx(inline);
    const labelClasses = cx("control__indicator", inlineText);

    return (
       { this.wrapperRef = checkbox; }}>
        <CheckboxInput checked={this.state.active} disabled={disabled} defaultChecked={this.props.defaultChecked} name={label}/>

        {label &&

{label}

}

    );
  }
}

class CheckboxInput extends React.Component {

  render() {
    const { checked, className, defaultChecked, disabled, ...props } = this.props;
    console.log(this.props)
    return (
      <input
        {...props}
        type="checkbox"
        styleName={"input"}
        disabled={this.props.disabled}
        checked={this.props.checked}
        value={this.props.checked}
        defaultChecked={this.props.defaultChecked}
      />
    );
  }
}

CheckboxInput.propTypes = {
  /**
   * Defines if checkbox should be checked on load.
   */
  "checked": PropTypes.bool,
  /**
   * Defines if checkbox should be checked on load.
   */
  "defaultChecked": PropTypes.bool
};

CheckboxInput.defaultProps = { "className": "", "checked": false, "defaultChecked": false};

Checkbox.propTypes = {
  /**
   * Text for checkbox label
   * @examples 'Some Label'
   */
  "label": PropTypes.string,
  /**
   * A css class name that will be appended to the wrapping  element around the <input> and  elements.
   */
  "className": PropTypes.string,
  /**
   * If included, checkbox is disabled
   * @examples <Checkbox disabled />, <Checkbox disabled={true} />
   */
  "disabled": PropTypes.bool,
  /**
   * If included, renders the checkbox and it's label inline, so it can be side-by-side to other content.
   */
  "inline": PropTypes.bool,
  /**
   * Text for checkbox label title. (i.e. "alt-text" for checkboxes, useful for accessibility). If not provided, will be label text.
   * @examples 'Some Title'
   */
  "title": PropTypes.string,
  /**
   * Defines if checkbox should be checked on load.
   */
  "defaultChecked": PropTypes.bool,
  /**
   * Defines if checkbox should be checked on load.
   */
  "checked": PropTypes.bool
};

Checkbox.defaultProps = { "className": "", "disabled": false, "inline": false };

Checkbox.styleguide = {
  "category": "Form Components",
  "index": "3.2",
  "example":
    `

Checkboxes

  <Checkbox
    defaultChecked={true}
    label="Checked"
  />
  <Checkbox
    defaultChecked={false}
    label="Not checked"

  />
  <Checkbox
    defaultChecked={true}
    label="Disabled checkbox"
    disabled
  />
  <Checkbox
    defaultChecked={false}
    label="Inline checkbox 1"
    inline
  />
  <Checkbox
    defaultChecked={false}
    label="Inline checkbox 2"
    inline
  />

`

};

export default Checkbox;<commit_msg>checkbox complete<commit_after>import React, { PropTypes } from "react";
import cx from 'classNames';

/**
 * Simple component for a basic checkbox
 */
class Checkbox extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      "inputValue": '',
      "checkValue": false,
      "checked": this.props.defaultChecked || false
    };
  }

  _clickHandler = (event) => {
    if (this.wrapperRef && this.wrapperRef.contains(event.target)) {
      if(this.state.inputValue === "off" || this.state.inputValue === "" || this.state.inputValue === null) {
        this.setState({'inputValue': "on"});
      } else {
        this.setState({'inputValue': "off"});
      }

      if(this.state.defaultChecked === true){
        this.setState({'checked': false})
      } else {
        this.setState({'checked': true})
      }

    }
  };

  render() {
    const { title, label, checked, disabled, inline, className, defaultChecked, ...props } = this.props;
    const inlineText = inline ? "inline" : "";
    const controlStyle = cx({
      "control": true,
      "control--checkbox": true
    })

    const title_label = title ? title : label;
    const labelClasses = cx("control__indicator", inlineText);

    return (
       { this.wrapperRef = checkbox; }}>
        <input
            {...props}
            name={label}
            type="checkbox"
            disabled={disabled}
            value={this.state.inputValue}
            defaultChecked={this.state.checked}
            />

        {label &&

{label}

}

    );
  }
}

Checkbox.propTypes = {
  /**
   * Text for checkbox label
   * @examples 'Some Label'
   */
  "label": PropTypes.string,
  /**
   * A css class name that will be appended to the wrapping  element around the <input> and  elements.
   */
  "className": PropTypes.string,
  /**
   * If included, checkbox is disabled
   * @examples <Checkbox disabled />, <Checkbox disabled={true} />
   */
  "disabled": PropTypes.bool,
  /**
   * If included, renders the checkbox and it's label inline, so it can be side-by-side to other content.
   */
  "inline": PropTypes.bool,
  /**
   * Text for checkbox label title. (i.e. "alt-text" for checkboxes, useful for accessibility). If not provided, will be label text.
   * @examples 'Some Title'
   */
  "title": PropTypes.string,
  /**
   * Defines if checkbox should be checked on load.
   */
  "defaultChecked": PropTypes.bool,
  /**
   * Defines if checkbox should be checked on load.
   */
  "checked": PropTypes.bool
};

Checkbox.defaultProps = { "className": "", "disabled": false, "inline": false, "checked": false };

Checkbox.styleguide = {
  "category": "Form Components",
  "index": "3.2",
  "example":
    `

Checkboxes

  <Checkbox
    defaultChecked={true}
    label="Checked"
  />
  <Checkbox
    defaultChecked={false}
    label="Not checked"

  />
  <Checkbox
    defaultChecked={true}
    label="Disabled checkbox"
    disabled
  />
  <Checkbox
    defaultChecked={false}
    label="Inline checkbox 1"
    inline
  />
  <Checkbox
    defaultChecked={false}
    label="Inline checkbox 2"
    inline
  />

`

};

export default Checkbox;<|endoftext|>