<commit_before>/*
 * Copyright (C) 2015-2018 Dubalu LLC. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

module.exports.mul64 = (lhs, rhs) => {

  const l0 = lhs[1] & 0xffff;
  const l1 = (lhs[1] >> 16) & 0xffff;
  const l2 = lhs[0] & 0xffff;
  const l3 = (lhs[0] >> 16) & 0xffff;

  const r0 = rhs[1] & 0xffff;
  const r1 = (rhs[1] >> 16) & 0xffff;
  const r2 = rhs[0] & 0xffff;
  const r3 = (rhs[0] >> 16) & 0xffff;

  let c;
  let t0 = 0, t1 = 0, t2 = 0, t3 = 0;

  c = 0;
  c += t0 + r0 * l0; t0 = c & 0xffff; c >>= 16;
  c += t1 + r1 * l0; t1 = c & 0xffff; c >>= 16;
  c += t2 + r2 * l0; t2 = c & 0xffff; c >>= 16;
  c += t3 + r3 * l0; t3 = c & 0xffff; c >>= 16;

  c = 0;
  c += t1 + r0 * l1; t1 = c & 0xffff; c >>= 16;
  c += t2 + r1 * l1; t2 = c & 0xffff; c >>= 16;
  c += t3 + r2 * l1; t3 = c & 0xffff; c >>= 16;

  c = 0;
  c += t2 + r0 * l2; t2 = c & 0xffff; c >>= 16;
  c += t3 + r1 * l2; t3 = c & 0xffff; c >>= 16;

  c = 0;
  c += t3 + r0 * l3; t3 = c & 0xffff; c >>= 16;

  return [ ((t3 << 16) | t2) >>> 0, ((t1 << 16) | t0) >>> 0 ];
}

module.exports.add64 = (lhs, rhs) => {
  const l0 = lhs[1] & 0xffff;
  const l1 = (lhs[1] >> 16) & 0xffff;
  const l2 = lhs[0] & 0xffff;
  const l3 = (lhs[0] >> 16) & 0xffff;

  const r0 = rhs[1] & 0xffff;
  const r1 = (rhs[1] >> 16) & 0xffff;
  const r2 = rhs[0] & 0xffff;
  const r3 = (rhs[0] >> 16) & 0xffff;

  let c;
  let t0 = 0, t1 = 0, t2 = 0, t3 = 0;

  c = 0;
  c += l0 + r0; t0 = c & 0xffff; c >>= 16;
  c += l1 + r1; t1 = c & 0xffff; c >>= 16;
  c += l2 + r2; t2 = c & 0xffff; c >>= 16;
  c += l3 + r3; t3 = c & 0xffff; c >>= 16;

  return [ ((t3 << 16) | t2) >>> 0, ((t1 << 16) | t0) >>> 0 ];
}

module.exports.sub64 = (lhs, rhs) => {
  const l0 = lhs[1] & 0xffff;
  const l1 = (lhs[1] >> 16) & 0xffff;
  const l2 = lhs[0] & 0xffff;
  const l3 = (lhs[0] >> 16) & 0xffff;

  const r0 = rhs[1] & 0xffff;
  const r1 = (rhs[1] >> 16) & 0xffff;
  const r2 = rhs[0] & 0xffff;
  const r3 = (rhs[0] >> 16) & 0xffff;

  let b = 0;
  let t0 = 0, t1 = 0, t2 = 0, t3 = 0;

  b = l0 - r0 - b; b < 0 ? ( t0 = b + 65536, b = 1 ) : ( t0 = b & 0xffff, b = 0 );
  b = l1 - r1 - b; b < 0 ? ( t1 = b + 65536, b = 1 ) : ( t1 = b & 0xffff, b = 0 );
  b = l2 - r2 - b; b < 0 ? ( t2 = b + 65536, b = 1 ) : ( t2 = b & 0xffff, b = 0 );
  b = l3 - r3 - b; b < 0 ? ( t3 = b + 65536, b = 1 ) : ( t3 = b & 0xffff, b = 0 );
  return [ ((t3 << 16) | t2) >>> 0, ((t1 << 16) | t0) >>> 0 ];
}

module.exports.div64 = (n, d) => {
  const n0 = n[1] & 0xffff;
  const n1 = (n[1] >> 16) & 0xffff;
  const n2 = n[0] & 0xffff;
  const n3 = (n[0] >> 16) & 0xffff;
  d = d & 0xffff;

  let q0 = 0, q1 = 0, q2 = 0, q3 = 0;
  let r = 0;

  n = (r << 16 | n3); q3 = n / d; r = n % d;
  n = (r << 16 | n2); q2 = n / d; r = n % d;
  n = (r << 16 | n1); q1 = n / d; r = n % d;
  n = (r << 16 | n0); q0 = n / d; r = n % d;

  return [ ((q3 << 16) | q2) >>> 0, ((q1 << 16) | q0) >>> 0 ];
}

module.exports._add64 = (lhs, rhs) => {
  const l0 = (lhs[1] & 0xffffffff) >>> 0;
  const l1 = (lhs[0] & 0xffffffff) >>> 0;

  const r0 = (rhs[1] & 0xffffffff) >>> 0;
  const r1 = (rhs[0] & 0xffffffff) >>> 0;

  let c;
  let t0 = 0, t1 = 0;

  c = 0;
  c += l0 + r0; t0 = c & 0xffffffff; c >>= 32;
  c += l1 + r1; t1 = c & 0xffffffff; c >>= 32;

  return [ t1 >>> 0, t0 >>> 0 ];
}

module.exports.shr64 = (n, bits) => {
  const n0 = (n[1] & 0xffffffff) >>> 0;
  const n1 = (n[0] & 0xffffffff) >>> 0;

  let t0 = 0, t1 = 0;

  t0 = ((n1 << (32 - bits)) & 0xffffffff) | n0 >>> bits;
  t1 = n1 >>> bits;

  return [ t1 >>> 0, t0 >>> 0 ];
}

module.exports.shl64 = (n, bits) => {
  const n0 = (n[1] & 0xffffffff) >>> 0;
  const n1 = (n[0] & 0xffffffff) >>> 0;

  let t0 = 0, t1 = 0;

  t1 = n1 << bits | ((n0 >>> (32 - bits))  & 0xffffffff);
  t0 = n0 << bits;

  return [ t1 >>> 0, t0 >>> 0 ];
}

module.exports.xor64 = (lhs, rhs) => {
  const l0 = (lhs[1] & 0xffffffff) >>> 0;
  const l1 = (lhs[0] & 0xffffffff) >>> 0;

  const r0 = (rhs[1] & 0xffffffff) >>> 0;
  const r1 = (rhs[0] & 0xffffffff) >>> 0;

  return [ (r1 ^ l1) >>> 0 , (r0 ^ l0) >>> 0 ];
}

module.exports.and64 = (lhs, rhs) => {
  const l0 = (lhs[1] & 0xffffffff) >>> 0;
  const l1 = (lhs[0] & 0xffffffff) >>> 0;

  const r0 = (rhs[1] & 0xffffffff) >>> 0;
  const r1 = (rhs[0] & 0xffffffff) >>> 0;

  return [ (r1 & l1) >>> 0 , (r0 & l0) >>> 0 ];
}

module.exports.or64 = (lhs, rhs) => {
  const l0 = (lhs[1] & 0xffffffff) >>> 0;
  const l1 = (lhs[0] & 0xffffffff) >>> 0;

  const r0 = (rhs[1] & 0xffffffff) >>> 0;
  const r1 = (rhs[0] & 0xffffffff) >>> 0;

  return [ (r1 | l1) >>> 0 , (r0 | l0) >>> 0 ];
}

module.exports.shr128 = (n, bits) => { // assumes bits < 32
  const n3 = (n[0] & 0xffffffff) >>> 0;
  const n2 = (n[1] & 0xffffffff) >>> 0;
  const n1 = (n[2] & 0xffffffff) >>> 0;
  const n0 = (n[3] & 0xffffffff) >>> 0;

  let t0 = 0, t1 = 0, t3 = 0, t4 = 0;

  t0 = ((n1 << (32 - bits)) & 0xffffffff) | n0 >>> bits;
  t1 = ((n2 << (32 - bits)) & 0xffffffff) | n1 >>> bits;
  t2 = ((n3 << (32 - bits)) & 0xffffffff) | n2 >>> bits;
  t3 = n3 >>> bits;

  return [ t3 >>> 0, t2 >>> 0 , t1 >>> 0, t0 >>> 0 ];
}<commit_msg>Missing variable declaration<commit_after>/*
 * Copyright (C) 2015-2018 Dubalu LLC. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

module.exports.mul64 = (lhs, rhs) => {

  const l0 = lhs[1] & 0xffff;
  const l1 = (lhs[1] >> 16) & 0xffff;
  const l2 = lhs[0] & 0xffff;
  const l3 = (lhs[0] >> 16) & 0xffff;

  const r0 = rhs[1] & 0xffff;
  const r1 = (rhs[1] >> 16) & 0xffff;
  const r2 = rhs[0] & 0xffff;
  const r3 = (rhs[0] >> 16) & 0xffff;

  let c;
  let t0 = 0, t1 = 0, t2 = 0, t3 = 0;

  c = 0;
  c += t0 + r0 * l0; t0 = c & 0xffff; c >>= 16;
  c += t1 + r1 * l0; t1 = c & 0xffff; c >>= 16;
  c += t2 + r2 * l0; t2 = c & 0xffff; c >>= 16;
  c += t3 + r3 * l0; t3 = c & 0xffff; c >>= 16;

  c = 0;
  c += t1 + r0 * l1; t1 = c & 0xffff; c >>= 16;
  c += t2 + r1 * l1; t2 = c & 0xffff; c >>= 16;
  c += t3 + r2 * l1; t3 = c & 0xffff; c >>= 16;

  c = 0;
  c += t2 + r0 * l2; t2 = c & 0xffff; c >>= 16;
  c += t3 + r1 * l2; t3 = c & 0xffff; c >>= 16;

  c = 0;
  c += t3 + r0 * l3; t3 = c & 0xffff; c >>= 16;

  return [ ((t3 << 16) | t2) >>> 0, ((t1 << 16) | t0) >>> 0 ];
}

module.exports.add64 = (lhs, rhs) => {
  const l0 = lhs[1] & 0xffff;
  const l1 = (lhs[1] >> 16) & 0xffff;
  const l2 = lhs[0] & 0xffff;
  const l3 = (lhs[0] >> 16) & 0xffff;

  const r0 = rhs[1] & 0xffff;
  const r1 = (rhs[1] >> 16) & 0xffff;
  const r2 = rhs[0] & 0xffff;
  const r3 = (rhs[0] >> 16) & 0xffff;

  let c;
  let t0 = 0, t1 = 0, t2 = 0, t3 = 0;

  c = 0;
  c += l0 + r0; t0 = c & 0xffff; c >>= 16;
  c += l1 + r1; t1 = c & 0xffff; c >>= 16;
  c += l2 + r2; t2 = c & 0xffff; c >>= 16;
  c += l3 + r3; t3 = c & 0xffff; c >>= 16;

  return [ ((t3 << 16) | t2) >>> 0, ((t1 << 16) | t0) >>> 0 ];
}

module.exports.sub64 = (lhs, rhs) => {
  const l0 = lhs[1] & 0xffff;
  const l1 = (lhs[1] >> 16) & 0xffff;
  const l2 = lhs[0] & 0xffff;
  const l3 = (lhs[0] >> 16) & 0xffff;

  const r0 = rhs[1] & 0xffff;
  const r1 = (rhs[1] >> 16) & 0xffff;
  const r2 = rhs[0] & 0xffff;
  const r3 = (rhs[0] >> 16) & 0xffff;

  let b = 0;
  let t0 = 0, t1 = 0, t2 = 0, t3 = 0;

  b = l0 - r0 - b; b < 0 ? ( t0 = b + 65536, b = 1 ) : ( t0 = b & 0xffff, b = 0 );
  b = l1 - r1 - b; b < 0 ? ( t1 = b + 65536, b = 1 ) : ( t1 = b & 0xffff, b = 0 );
  b = l2 - r2 - b; b < 0 ? ( t2 = b + 65536, b = 1 ) : ( t2 = b & 0xffff, b = 0 );
  b = l3 - r3 - b; b < 0 ? ( t3 = b + 65536, b = 1 ) : ( t3 = b & 0xffff, b = 0 );
  return [ ((t3 << 16) | t2) >>> 0, ((t1 << 16) | t0) >>> 0 ];
}

module.exports.div64 = (n, d) => {
  const n0 = n[1] & 0xffff;
  const n1 = (n[1] >> 16) & 0xffff;
  const n2 = n[0] & 0xffff;
  const n3 = (n[0] >> 16) & 0xffff;
  d = d & 0xffff;

  let q0 = 0, q1 = 0, q2 = 0, q3 = 0;
  let r = 0;

  n = (r << 16 | n3); q3 = n / d; r = n % d;
  n = (r << 16 | n2); q2 = n / d; r = n % d;
  n = (r << 16 | n1); q1 = n / d; r = n % d;
  n = (r << 16 | n0); q0 = n / d; r = n % d;

  return [ ((q3 << 16) | q2) >>> 0, ((q1 << 16) | q0) >>> 0 ];
}

module.exports._add64 = (lhs, rhs) => {
  const l0 = (lhs[1] & 0xffffffff) >>> 0;
  const l1 = (lhs[0] & 0xffffffff) >>> 0;

  const r0 = (rhs[1] & 0xffffffff) >>> 0;
  const r1 = (rhs[0] & 0xffffffff) >>> 0;

  let c;
  let t0 = 0, t1 = 0;

  c = 0;
  c += l0 + r0; t0 = c & 0xffffffff; c >>= 32;
  c += l1 + r1; t1 = c & 0xffffffff; c >>= 32;

  return [ t1 >>> 0, t0 >>> 0 ];
}

module.exports.shr64 = (n, bits) => {
  const n0 = (n[1] & 0xffffffff) >>> 0;
  const n1 = (n[0] & 0xffffffff) >>> 0;

  let t0 = 0, t1 = 0;

  t0 = ((n1 << (32 - bits)) & 0xffffffff) | n0 >>> bits;
  t1 = n1 >>> bits;

  return [ t1 >>> 0, t0 >>> 0 ];
}

module.exports.shl64 = (n, bits) => {
  const n0 = (n[1] & 0xffffffff) >>> 0;
  const n1 = (n[0] & 0xffffffff) >>> 0;

  let t0 = 0, t1 = 0;

  t1 = n1 << bits | ((n0 >>> (32 - bits))  & 0xffffffff);
  t0 = n0 << bits;

  return [ t1 >>> 0, t0 >>> 0 ];
}

module.exports.xor64 = (lhs, rhs) => {
  const l0 = (lhs[1] & 0xffffffff) >>> 0;
  const l1 = (lhs[0] & 0xffffffff) >>> 0;

  const r0 = (rhs[1] & 0xffffffff) >>> 0;
  const r1 = (rhs[0] & 0xffffffff) >>> 0;

  return [ (r1 ^ l1) >>> 0 , (r0 ^ l0) >>> 0 ];
}

module.exports.and64 = (lhs, rhs) => {
  const l0 = (lhs[1] & 0xffffffff) >>> 0;
  const l1 = (lhs[0] & 0xffffffff) >>> 0;

  const r0 = (rhs[1] & 0xffffffff) >>> 0;
  const r1 = (rhs[0] & 0xffffffff) >>> 0;

  return [ (r1 & l1) >>> 0 , (r0 & l0) >>> 0 ];
}

module.exports.or64 = (lhs, rhs) => {
  const l0 = (lhs[1] & 0xffffffff) >>> 0;
  const l1 = (lhs[0] & 0xffffffff) >>> 0;

  const r0 = (rhs[1] & 0xffffffff) >>> 0;
  const r1 = (rhs[0] & 0xffffffff) >>> 0;

  return [ (r1 | l1) >>> 0 , (r0 | l0) >>> 0 ];
}

module.exports.shr128 = (n, bits) => { // assumes bits < 32
  const n3 = (n[0] & 0xffffffff) >>> 0;
  const n2 = (n[1] & 0xffffffff) >>> 0;
  const n1 = (n[2] & 0xffffffff) >>> 0;
  const n0 = (n[3] & 0xffffffff) >>> 0;

  let t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0;

  t0 = ((n1 << (32 - bits)) & 0xffffffff) | n0 >>> bits;
  t1 = ((n2 << (32 - bits)) & 0xffffffff) | n1 >>> bits;
  t2 = ((n3 << (32 - bits)) & 0xffffffff) | n2 >>> bits;
  t3 = n3 >>> bits;

  return [ t3 >>> 0, t2 >>> 0 , t1 >>> 0, t0 >>> 0 ];
}<|endoftext|>


<commit_before>import React from 'react';

import ReactMarkdown from 'react-markdown';

import Editor from './editor';
import { updateCellSource } from '../../actions';

export default class MarkdownCell extends React.Component {
  static displayName = 'MarkdownCell';

  static propTypes = {
    cell: React.PropTypes.any,
    id: React.PropTypes.string,
  };

  static contextTypes = {
    dispatch: React.PropTypes.func,
  };

  constructor(props) {
    super(props);
    this.state = {
      view: true,
      // HACK: We'll need to handle props and state change better here
      source: this.props.cell.get('source'),
    };
  }

  componentWillReceiveProps(nextProps) {
    this.setState({
      source: nextProps.cell.get('source'),
    });
  }

  keyDown(e) {
    if (!e.shiftKey || e.key !== 'Enter') {
      return;
    }
    this.setState({ view: true });
  }

  render() {
    return (
        (this.state && this.state.view) ?

 this.setState({ view: false }) }>
            <ReactMarkdown source={
              this.state.source !== '' ?
                this.state.source :
                `*Empty markdown cell, double click me to add content.*`} />

 :

            <Editor language='markdown'
                    id={this.props.id}
                    input={this.state.source}
                    onChange={
                      (text) => {
                        this.setState({
                          source: text,
                        });
                        this.context.dispatch(updateCellSource(this.props.id, text));
                      }
                    }/>

    );
  }
}
<commit_msg>Empty strings are not true<commit_after>import React from 'react';

import ReactMarkdown from 'react-markdown';

import Editor from './editor';
import { updateCellSource } from '../../actions';

export default class MarkdownCell extends React.Component {
  static displayName = 'MarkdownCell';

  static propTypes = {
    cell: React.PropTypes.any,
    id: React.PropTypes.string,
  };

  static contextTypes = {
    dispatch: React.PropTypes.func,
  };

  constructor(props) {
    super(props);
    this.state = {
      view: true,
      // HACK: We'll need to handle props and state change better here
      source: this.props.cell.get('source'),
    };
  }

  componentWillReceiveProps(nextProps) {
    this.setState({
      source: nextProps.cell.get('source'),
    });
  }

  keyDown(e) {
    if (!e.shiftKey || e.key !== 'Enter') {
      return;
    }
    this.setState({ view: true });
  }

  render() {
    return (
        (this.state && this.state.view) ?

 this.setState({ view: false }) }>
            <ReactMarkdown source={
              this.state.source ?
                this.state.source :
                `*Empty markdown cell, double click me to add content.*`} />

 :

            <Editor language='markdown'
                    id={this.props.id}
                    input={this.state.source}
                    onChange={
                      (text) => {
                        this.setState({
                          source: text,
                        });
                        this.context.dispatch(updateCellSource(this.props.id, text));
                      }
                    }/>

    );
  }
}
<|endoftext|>


<commit_before>import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import * as coursesAction from '../../actions/courseActions';

class CoursesPage extends React.Component{
  constructor(props){
    super(props);

    this.state = {
      course : {
        title : null
      }
    }
  }

  onClickSave = () => {
    this.props.createCourse(this.state.course);
  };

  onTitleChange = (event) => {
    const course = this.state.course;
    course.title = event.target.value;
    this.setState({
      course : course
    });
  };

  renderCourseRow = (course, index) => {
    return
{course.title}

  };

  render(){
    return (

Courses

        {
          this.props.courses.map(this.renderCourseRow)
        }

Add Course

        <input
          type="text"
          onChange={this.onTitleChange}
          value={this.state.course.title}
        />
        <input
          type="button"
          value="save"
          onClick={this.onClickSave}
        />

    );
  }
}
CoursesPage.propTypes = {
  courses : PropTypes.array.isRequired,
  createCourse : PropTypes.func.isRequired
};

const mapStateToProps = (state, ownProps) => {
  return {
    courses : state.courses
  };
};

const mapDispatchToProps = (dispatch) => {
  return {
    createCourse : course => dispatch(coursesAction.createCourse(course))
  };
};

export default connect(mapStateToProps,mapDispatchToProps)(CoursesPage);
<commit_msg>using bindActionCreators instead of call using dispatch manually<commit_after>import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as coursesAction from '../../actions/courseActions';

class CoursesPage extends React.Component{
  constructor(props){
    super(props);

    this.state = {
      course : {
        title : null
      }
    }
  }

  onClickSave = () => {
    this.props.actions.createCourse(this.state.course);
  };

  onTitleChange = (event) => {
    const course = this.state.course;
    course.title = event.target.value;
    this.setState({
      course : course
    });
  };

  renderCourseRow = (course, index) => {
    return
{course.title}

  };

  render(){
    return (

Courses

        {
          this.props.courses.map(this.renderCourseRow)
        }

Add Course

        <input
          type="text"
          onChange={this.onTitleChange}
          value={this.state.course.title}
        />
        <input
          type="button"
          value="save"
          onClick={this.onClickSave}
        />

    );
  }
}
CoursesPage.propTypes = {
  courses : PropTypes.array.isRequired,
  actions : PropTypes.object.isRequired,
  createCourse : PropTypes.func.isRequired
};

const mapStateToProps = (state, ownProps) => {
  return {
    courses : state.courses
  };
};

const mapDispatchToProps = (dispatch) => {
  return {
    actions : bindActionCreators(coursesAction, dispatch),
    createCourse : bindActionCreators(coursesAction.createCourse, dispatch)
  };
};

export default connect(mapStateToProps,mapDispatchToProps)(CoursesPage);
<|endoftext|>


<commit_before>// dependencies
//
import React from 'react';
import update from 'react-addons-update';
import uniqueId from 'lodash/uniqueId';
import isArray from 'lodash/isArray';
import classnames from 'classnames';

/*

        Cycles

                <input type="checkbox" value="1">
                1

                <input type="checkbox" value="2">
                2

*/

class CheckboxGroup extends React.Component {
    constructor(props) {
        super(props);

        let value = [];

        if (this.props.value) {
            value = isArray(this.props.value) ? this.props.value : [this.props.value];
        }

        this.id = this.props.id || uniqueId('form-checkboxgroup-');

        this.state = {
            value,
            hasBeenClicked: false,
        };

        this.onClick = this.onClick.bind(this);
    }

    componentWillMount() {
        // determine if it's valid
        //
        const isValid = !this.props.required || this.state.value.length;

        // call the `onChildValidationEvent` handler once with
        // `hasValidated`=false, just to ensure that the parent knows about this
        // child
        //
        if (this.context.onChildValidationEvent) {
            this.context.onChildValidationEvent(
                this.id,
                false,
                isValid ? null : 'At least one item must be checked'
            );
        }
    }

    onClick(value) {
        const idx = this.state.value.findIndex((val) => val === value);

        let delta = null;
        if (idx === -1) {
            delta = { hasBeenClicked: { $set: true }, value: { $push: [value] } };
        } else {
            delta = { hasBeenClicked: { $set: true }, value: { $splice: [[idx, 1]] } };
        }

        this.setState((state) => update(state, delta), () => {
            if (this.props.onChange) {
                this.props.onChange(this.state.value);
            }

            const isValid = !this.props.required || this.state.value.length;

            if (this.context.onChildValidationEvent) {
                this.context.onChildValidationEvent(
                    this.id,
                    true,
                    isValid ? null : 'At least one item must be checked'
                );
            }
        });
    }

    render() {

        const classes = classnames('form-group', {
            'has-error': this.props.required && this.state.hasBeenClicked && !this.state.value.length,
        });

        const helpBlock = this.props.required && this.state.hasBeenClicked && !this.state.value.length
            ? At least one item must be checked
            : '';

        return (

                {this.props.label
                    ? {this.props.label}
                    : null
                }
                {this.props.options.map((opt) =>

                            <input
                                type="checkbox"
                                value={opt.value}
                                checked={this.state.value.findIndex((val) => val === opt.value) !== -1}
                                onChange={() => this.onClick(opt.value)}
                                onClick={() => this.onClick(opt.value)}
                            />
                            {opt.name}

                )}
                {helpBlock}

        );
    }
}

// set the context types for values received from higher up the food chain
//
CheckboxGroup.contextTypes = {
    onChildValidationEvent: React.PropTypes.func,
};

export default CheckboxGroup;
<commit_msg>added pull-left to checkbox items<commit_after>// dependencies
//
import React from 'react';
import update from 'react-addons-update';
import uniqueId from 'lodash/uniqueId';
import isArray from 'lodash/isArray';
import classnames from 'classnames';

/*

        Cycles

                <input type="checkbox" value="1">
                1

                <input type="checkbox" value="2">
                2

*/

class CheckboxGroup extends React.Component {
    constructor(props) {
        super(props);

        let value = [];

        if (this.props.value) {
            value = isArray(this.props.value) ? this.props.value : [this.props.value];
        }

        this.id = this.props.id || uniqueId('form-checkboxgroup-');

        this.state = {
            value,
            hasBeenClicked: false,
        };

        this.onClick = this.onClick.bind(this);
    }

    componentWillMount() {
        // determine if it's valid
        //
        const isValid = !this.props.required || this.state.value.length;

        // call the `onChildValidationEvent` handler once with
        // `hasValidated`=false, just to ensure that the parent knows about this
        // child
        //
        if (this.context.onChildValidationEvent) {
            this.context.onChildValidationEvent(
                this.id,
                false,
                isValid ? null : 'At least one item must be checked'
            );
        }
    }

    onClick(value) {
        const idx = this.state.value.findIndex((val) => val === value);

        let delta = null;
        if (idx === -1) {
            delta = { hasBeenClicked: { $set: true }, value: { $push: [value] } };
        } else {
            delta = { hasBeenClicked: { $set: true }, value: { $splice: [[idx, 1]] } };
        }

        this.setState((state) => update(state, delta), () => {
            if (this.props.onChange) {
                this.props.onChange(this.state.value);
            }

            const isValid = !this.props.required || this.state.value.length;

            if (this.context.onChildValidationEvent) {
                this.context.onChildValidationEvent(
                    this.id,
                    true,
                    isValid ? null : 'At least one item must be checked'
                );
            }
        });
    }

    render() {

        const classes = classnames('form-group', {
            'has-error': this.props.required && this.state.hasBeenClicked && !this.state.value.length,
        });

        const helpBlock = this.props.required && this.state.hasBeenClicked && !this.state.value.length
            ? At least one item must be checked
            : '';

        return (

                {this.props.label
                    ? {this.props.label}
                    : null
                }
                {this.props.options.map((opt) =>

                            <input
                                type="checkbox"
                                value={opt.value}
                                checked={this.state.value.findIndex((val) => val === opt.value) !== -1}
                                onChange={() => this.onClick(opt.value)}
                                onClick={() => this.onClick(opt.value)}
                            />
                            {opt.name}

                )}
                {helpBlock}

        );
    }
}

// set the context types for values received from higher up the food chain
//
CheckboxGroup.contextTypes = {
    onChildValidationEvent: React.PropTypes.func,
};

export default CheckboxGroup;
<|endoftext|>


<commit_before>import React from 'react';

import { findDOMNode } from 'react-dom';
import {Link} from 'react-router';
import ReactPlayer from 'react-player';
import Duration from './Duration'
import screenfull from 'screenfull';

class VideoPlayer extends React.Component {
    constructor(props, context){
        super(props, context);
        this.router = context.router;
        this.sourceURL = props.location.query.src;
        this.state = {
            url:this.sourceURL,
            playing: true,
            volume: 0.8,
            played: 0,
            playbackRate: 1.0,
            loaded: 0,
            duration: 0,
            width : 480,
            height: 270
        }
        this.playPause = this.playPause.bind(this);
        this.onClickFullScreen = this.onClickFullScreen.bind(this);
        this.onSeekMouseDown = this.onSeekMouseDown.bind(this);
        this.onSeekChange = this.onSeekChange.bind(this);
        this.onSeekMouseUp = this.onSeekMouseUp.bind(this);
        this.onProgress = this.onProgress.bind(this);
        this.onBackOneSec = this.onBackOneSec.bind(this);
        this.onFwdOneSec =  this.onFwdOneSec.bind(this);
        this.onClickBackToPlayList = this.onClickBackToPlayList.bind(this);

    }

    playPause(){
        this.setState({playing: !this.state.playing})
    }

    onBackOneSec(){
        let played = this.state.played;
        let duration = this.state.duration;
        let backOneSec = ((played * duration) - 1) / duration
        this.setState({played : backOneSec ? backOneSec : played })

    }

    onFwdOneSec(){
        let played = this.state.played;
        let duration = this.state.duration;
        let fwdOneSec = ((played * duration) + 1) / duration
        this.setState({played : fwdOneSec ? fwdOneSec : played })
    }

    onSeekMouseDown(e){
        this.setState({seeking: true})
    }

    onSeekChange(e){
        this.setState({played: parseFloat(e.target.value) })
    }

    onSeekMouseUp(e){
        this.setState({seeking: false})
        this.player.seekTo(parseFloat(e.target.value))
    }

    onProgress(state){
        if (!this.state.seeking){
            this.setState(state)
        }
    }

    onClickFullScreen(){
        screenfull.request(findDOMNode(this.player))
    }

    onClickBackToPlayList(){
        this.props.router.push('/')
    }

    render(){

        const {
            url, playing, volume,
            played, loaded, duration,
            playbackRate, width, height
        } = this.state;
        return (

Video Player

<Link to="/" >Back</Link>

                <ReactPlayer
                    ref = {player => {this.player = player}}
                    className="react-player"
                    width={width}
                    height={height}
                    url={url}
                    playing={playing}
                    playbackRate = {playbackRate}
                    volume = {volume}
                    onReady = {()=>console.log('onReady')}
                    onStart = {()=>console.log('onStart')}
                    onPlay = {() => this.setState({playing: true})}
                    onPause = {()=> this.setState({playing: false})}
                    onBuffer = {()=>console.log('onBuffer')}
                    onEnded = {()=> this.setState({playing:false})}
                    onError = {e => console.log('onError', e)}
                    onProgress = {this.onProgress}
                    onDuration={duration => this.setState({duration})}
                />

                            <button onClick={this.playPause}>{playing ? 'Pause' : 'Play'}</button>
                            <button onClick={this.onBackOneSec} >-1sec</button>
                            <button onClick={this.onFwdOneSec} >+1sec</button>
                            <button onClick={this.onClickFullScreen}>Fullscreen</button>
                            <button onClick={this.onClickBackToPlayList}>Back to Play List</button>

                            <input
                                type='range' min={0} max={1} step='any'
                                value={played}
                                onMouseDown={this.onSeekMouseDown}
                                onChange={this.onSeekChange}
                                onMouseUp={this.onSeekMouseUp}
                            />
                            <Duration seconds={duration * played} />/<Duration seconds={duration * (1 - played)} />

        )
    }
}

export default VideoPlayer;<commit_msg>Works on Desktop<commit_after>import React from 'react';

import { findDOMNode } from 'react-dom';
import {Link} from 'react-router';
import ReactPlayer from 'react-player';
import Duration from './Duration'
import screenfull from 'screenfull';

class VideoPlayer extends React.Component {
    constructor(props, context){
        super(props, context);
        this.router = context.router;
        this.sourceURL = props.location.query.src;
        this.state = {
            url:this.sourceURL,
            playing: true,
            volume: 0.8,
            played: 0,
            playbackRate: 1.0,
            loaded: 0,
            duration: 0,
            width : 480,
            height: 270
        }
        this.playPause = this.playPause.bind(this);
        this.onClickFullScreen = this.onClickFullScreen.bind(this);
        this.onSeekMouseDown = this.onSeekMouseDown.bind(this);
        this.onSeekChange = this.onSeekChange.bind(this);
        this.onSeekMouseUp = this.onSeekMouseUp.bind(this);
        this.onProgress = this.onProgress.bind(this);
        this.onBackOneSec = this.onBackOneSec.bind(this);
        this.onFwdOneSec =  this.onFwdOneSec.bind(this);
        this.onClickBackToPlayList = this.onClickBackToPlayList.bind(this);

    }

    playPause(){
        this.setState({playing: !this.state.playing})
    }

    onBackOneSec(){
        let played = this.state.played;
        let duration = this.state.duration;
        let backOneSec = ((played * duration) - 1) / duration
        this.setState({played : backOneSec ? backOneSec : played })
        this.player.seekTo(backOneSec ? backOneSec : played)

    }

    onFwdOneSec(){
        let played = this.state.played;
        let duration = this.state.duration;
        let fwdOneSec = ((played * duration) + 1) / duration
        this.setState({played : fwdOneSec ? fwdOneSec : played })
        this.player.seekTo(fwdOneSec ? fwdOneSec : played)
    }

    onSeekMouseDown(e){
        this.setState({seeking: true})
    }

    onSeekChange(e){
        this.setState({played: parseFloat(e.target.value) })
    }

    onSeekMouseUp(e){
        this.setState({seeking: false})
        this.player.seekTo(parseFloat(e.target.value))
    }

    onProgress(state){
        if (!this.state.seeking){
            this.setState(state)
        }
    }

    onClickFullScreen(){
        screenfull.request(findDOMNode(this.player))
    }

    onClickBackToPlayList(){
        this.props.router.push('/')
    }

    render(){

        const {
            url, playing, volume,
            played, loaded, duration,
            playbackRate, width, height
        } = this.state;
        return (

Video Player

<Link to="/" >Back</Link>

                <ReactPlayer
                    ref = {player => {this.player = player}}
                    className="react-player"
                    width={width}
                    height={height}
                    url={url}
                    playing={playing}
                    playbackRate = {playbackRate}
                    volume = {volume}
                    onReady = {()=>console.log('onReady')}
                    onStart = {()=>console.log('onStart')}
                    onPlay = {() => this.setState({playing: true})}
                    onPause = {()=> this.setState({playing: false})}
                    onBuffer = {()=>console.log('onBuffer')}
                    onEnded = {()=> this.setState({playing:false})}
                    onError = {e => console.log('onError', e)}
                    onProgress = {this.onProgress}
                    onDuration={duration => this.setState({duration})}
                />

                            <button onClick={this.playPause}>{playing ? 'Pause' : 'Play'}</button>
                            <button onClick={this.onBackOneSec} >-1sec</button>
                            <button onClick={this.onFwdOneSec} >+1sec</button>
                            <button onClick={this.onClickFullScreen}>Fullscreen</button>
                            <button onClick={this.onClickBackToPlayList}>Back to Play List</button>

                            <input
                                type='range' min={0} max={1} step='any'
                                value={played}
                                onMouseDown={this.onSeekMouseDown}
                                onChange={this.onSeekChange}
                                onMouseUp={this.onSeekMouseUp}
                            />
                            <Duration seconds={duration * played} />/<Duration seconds={duration * (1 - played)} />

        )
    }
}

export default VideoPlayer;<|endoftext|>


<commit_before>'use strict'
import React, {Component} from 'react'
import styles from './style.module.css'
import Link from 'gatsby-link'
import marked from 'marked'
import {Box, Thread} from 'react-disqussion'
import {timestampToSeconds} from '../../utils/time'

const PLAYBACK_RATES = [1.0, 1.2, 1.4, 1.6, 1.8, 2.0]

class PodcastPage extends Component {
  constructor(props) {
    super(props)
    this.handleTimeClick = this.handleTimeClick.bind(this)
  }

  handleTimeClick(e) {
    const {target} = e
    if (target.classList.contains('podcast_time')) {
      e.preventDefault()
      this.audioEl.currentTime = timestampToSeconds(target.innerText)
    }
  }

  handleSpeedClick(speed) {
    this.audioEl.playbackRate = speed
  }

  render() {
    const {data, id} = this.props.pathContext
    const {node: {title, link, notes, formatedDate}} = data

    return (

          <Link to='/podcast/'>{'<'} назад</Link>

{title}

          <date className={styles.header_date}>{formatedDate}</date>

          <audio
            className={styles.player_audio}
            controls='controls'
            preload='metadata'
            src={link}
            ref={el => (this.audioEl = el)} />

            {PLAYBACK_RATES.map(speed => (
              <button
                key={speed}
                className={styles.player_controls_item}
                onClick={() => this.handleSpeedClick(speed)}>
                {speed}
              </button>
            ))}

        <Box shortname='http-spb-frontend-ru'>
          <Thread
            url={`http://spb-frontend.ru/podcast/${id + 1}`}
            title={title}
            identifier={`podcast-${id + 1}`} />
        </Box>

    )
  }
}

export default PodcastPage
<commit_msg>Добавляет метку времени в url страницы с выпуском подкаста<commit_after>'use strict'
import React, {Component} from 'react'
import styles from './style.module.css'
import Link, {navigateTo} from 'gatsby-link'
import marked from 'marked'
import {Box, Thread} from 'react-disqussion'
import {timestampToSeconds} from '../../utils/time'

const PLAYBACK_RATES = [1.0, 1.2, 1.4, 1.6, 1.8, 2.0]

class PodcastPage extends Component {
  constructor(props) {
    super(props)
    this.handleTimeClick = this.handleTimeClick.bind(this)
    this.handleTimeUpdate = this.handleTimeUpdate.bind(this)
    this.initialTimeHash = `#t=${this.getInitialTime()}`
  }

  handleTimeClick(e) {
    const {target} = e
    if (target.classList.contains('podcast_time')) {
      e.preventDefault()
      this.audioEl.currentTime = timestampToSeconds(target.innerText)
    }
  }

  handleSpeedClick(speed) {
    this.audioEl.playbackRate = speed
  }

  handleTimeUpdate() {
    const time = Math.trunc(this.audioEl.currentTime)
    navigateTo(`${location.pathname}?time=${time}`)
  }

  getInitialTime() {
    const rgexRes = location.search.match(/time=(\d+)/)
    return rgexRes ? rgexRes[1] : 0
  }

  render() {
    const {data, id} = this.props.pathContext
    const {node: {title, link, notes, formatedDate}} = data

    return (

          <Link to='/podcast/'>{'<'} назад</Link>

{title}

          <date className={styles.header_date}>{formatedDate}</date>

          <audio
            className={styles.player_audio}
            controls='controls'
            preload='metadata'
            src={link + this.initialTimeHash}
            onTimeUpdate={this.handleTimeUpdate}
            ref={el => (this.audioEl = el)} />

            {PLAYBACK_RATES.map(speed => (
              <button
                key={speed}
                className={styles.player_controls_item}
                onClick={() => this.handleSpeedClick(speed)}>
                {speed}
              </button>
            ))}

        <Box shortname='http-spb-frontend-ru'>
          <Thread
            url={`http://spb-frontend.ru/podcast/${id + 1}`}
            title={title}
            identifier={`podcast-${id + 1}`} />
        </Box>

    )
  }
}

export default PodcastPage
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// @flow

/**
 * VirtualList implements a virtualized component. This means it doesn't
 * render only the items that are currently displayed, and makes long list
 * manageable in the Web platform.
 * This implementation has some unique features that make it especially
 * performant, but also comes with constraints.
 *
 * The items are organized in chunks:
 *
 * .
 * | 16 items
 * |
 * -
 * |
 * | 16 items
 * |
 * -
 * |
 * | 16 items
 * .
 *
 * Depending on the current scroll position, the logic decides which chunk(s)
 * will be rendered. If `disableOverscan` is false (this is the default) we
 * render more items before and after the visible part so that when scrolling
 * the user doesn't see a white background while the app catches up.
 *
 * Using chunks avoids the need to render often, and thus improves performance.
 */

import * as React from 'react';
import classNames from 'classnames';
import range from 'array-range';

import type { CssPixels } from 'firefox-profiler/types';

type RenderItem<Item> = (Item, number, number) => React.Node;

type VirtualListRowProps<Item> = {|
  +renderItem: RenderItem<Item>,
  +item: Item,
  +index: number,
  +columnIndex: number,
  // These properties are not used directly, but are needed for strict equality
  // checks so that the components update correctly.
  // * `forceRenderControl` is used when we want to update one row or a few rows only,
  //   this is typically when the selection changes and both the old and the new
  //   selection need to be changed.
  //   It needs to change whenever the row should be updated, so it should be
  //   computed from the values that control these update.
  +forceRenderItem: string,
  // * `items` contains the full items, so that we update the whole list
  //   whenever the source changes. This is necessary because often `item` is a
  //   native value (eg a number), and shallow checking only `item` won't always
  //   give the expected behavior.
  +items: $ReadOnlyArray<Item>,
  // * `forceRender` is passed through directly from the main VirtualList
  //   component to the row as a way to update the full list for reasons
  //   unbeknownst to this component. This can be used for example in chart-like
  //   panels where we'd want to redraw if some source value necessary to the
  //   computation changes.
  +forceRender?: number | string,
|};

class VirtualListRow<Item> extends React.PureComponent<
  VirtualListRowProps<Item>
> {
  render() {
    const { renderItem, item, index, columnIndex } = this.props;
    return renderItem(item, index, columnIndex);
  }
}

type VirtualListInnerChunkProps<Item> = {|
  +className: string,
  +renderItem: RenderItem<Item>,
  +items: $ReadOnlyArray<Item>,
  +specialItems: $ReadOnlyArray<Item | void>,
  +visibleRangeStart: number,
  +visibleRangeEnd: number,
  +columnIndex: number,
  +forceRender?: number | string,
|};

class VirtualListInnerChunk<Item> extends React.PureComponent<
  VirtualListInnerChunkProps<Item>
> {
  render() {
    const {
      className,
      renderItem,
      items,
      specialItems,
      visibleRangeStart,
      visibleRangeEnd,
      columnIndex,
      forceRender,
    } = this.props;

    return (

        {range(
          visibleRangeStart,
          Math.max(visibleRangeStart, visibleRangeEnd)
        ).map((i) => {
          const item = items[i];

          // We compute forceRenderItem from the first position of item in the list,
          // and the number of occurrences. Indeed we want to rerender this
          // specific item whenever one of these values changes.
          const firstPosOfItem = specialItems.indexOf(item);
          const countOfItem = specialItems.reduce(
            (acc, specialItem) => (specialItem === item ? acc + 1 : acc),
            0
          );
          const forceRenderItem = `${firstPosOfItem}|${countOfItem}`;

          return (
            <VirtualListRow
              key={i}
              index={i}
              columnIndex={columnIndex}
              renderItem={renderItem}
              item={item}
              items={items}
              forceRenderItem={forceRenderItem}
              forceRender={forceRender}
            />
          );
        })}

    );
  }
}

type VirtualListInnerProps<Item> = {|
  +itemHeight: CssPixels,
  +className: string,
  +renderItem: RenderItem<Item>,
  +items: $ReadOnlyArray<Item>,
  +specialItems: $ReadOnlyArray<Item | void>,
  +visibleRangeStart: number,
  +visibleRangeEnd: number,
  +columnIndex: number,
  +containerWidth: CssPixels,
  +forceRender?: number | string,
|};

class VirtualListInner<Item> extends React.PureComponent<
  VirtualListInnerProps<Item>
> {
  _container: ?HTMLElement;

  _takeContainerRef = (element: ?HTMLDivElement) => {
    this._container = element;
  };

  /* This method is used by users of this component. */
  /* eslint-disable-next-line react/no-unused-class-component-methods */
  getBoundingClientRect() {
    if (this._container) {
      return this._container.getBoundingClientRect();
    }
    return new DOMRect(0, 0, 0, 0);
  }

  render() {
    const {
      itemHeight,
      className,
      renderItem,
      items,
      specialItems,
      visibleRangeStart,
      visibleRangeEnd,
      columnIndex,
      containerWidth,
      forceRender,
    } = this.props;

    const chunkSize = 16;
    const startChunkIndex = Math.floor(visibleRangeStart / chunkSize);
    const endChunkIndex = Math.ceil(visibleRangeEnd / chunkSize);
    const chunks = range(
      startChunkIndex,
      Math.max(startChunkIndex, endChunkIndex)
    ).map((c) => c * chunkSize);

    return (
      <div
        className={className}
        ref={this._takeContainerRef}
        // Add padding to list height to account for overlay scrollbars.
        style={{
          height: `${(items.length + 1) * itemHeight}px`,
          minWidth: columnIndex === 1 ? containerWidth : undefined,
        }}
      >
        <div
          className={`${className}TopSpacer`}
          key={-1}
          style={{ height: Math.max(0, visibleRangeStart) * itemHeight + 'px' }}
        />
        {chunks.map((chunkStart) => {
          return (
            <VirtualListInnerChunk
              className={`${className}InnerChunk`}
              key={chunkStart}
              visibleRangeStart={Math.max(chunkStart, visibleRangeStart)}
              visibleRangeEnd={Math.min(
                chunkStart + chunkSize,
                visibleRangeEnd
              )}
              columnIndex={columnIndex}
              renderItem={renderItem}
              items={items}
              specialItems={specialItems}
              forceRender={forceRender}
            />
          );
        })}

    );
  }
}

type VirtualListProps<Item> = {|
  +itemHeight: CssPixels,
  +className: string,
  +renderItem: RenderItem<Item>,
  +items: $ReadOnlyArray<Item>,
  +focusable: boolean,
  +specialItems: $ReadOnlyArray<Item | void>,
  +onKeyDown: (SyntheticKeyboardEvent<>) => void,
  +onCopy: (ClipboardEvent) => void,
  // This is called when the mouse leaves the list as it is rendered. That is if
  // there isn't enough item to fill the component's height, and the user moves
  // the mouse below the items, this callback would be called.
  +onMouseLeaveRenderedList?: () => void,
  // Set `disableOverscan` to `true` when you expect a lot of updates in a short
  // time: this will render only the visible part, which makes each update faster.
  +disableOverscan: boolean,
  +columnCount: number,
  +containerWidth: CssPixels,
  // `forceRender` is passed through directly from the main VirtualList
  // component to the row as a way to update the full list for reasons
  // unbeknownst to this component. This can be used for example in chart-like
  // panels where we'd want to redraw if some source value necessary to the
  // computation changes.
  +forceRender?: number | string,
  // The next 3 props will be applied to the underlying DOM element.
  // They're important for accessibility (especially focus and navigation).
  +ariaLabel?: string,
  +ariaRole?: string,
  // Aria-activedescendant specifies the children's "virtual" focus.
  +ariaActiveDescendant?: null | string,
|};

type Geometry = {
  // getBoundingClientRect in the Flow definitions is wrong, and labels the return values
  // as a ClientRect, and not a DOMRect. https://github.com/facebook/flow/issues/5475
  //
  // Account for that here:
  outerRect: DOMRect | ClientRect,
  innerRectY: CssPixels,
};

export class VirtualList<Item> extends React.PureComponent<
  VirtualListProps<Item>
> {
  _container: {| current: HTMLDivElement | null |} = React.createRef();
  _inner: {| current: VirtualListInner<Item> | null |} = React.createRef();
  _geometry: ?Geometry;

  componentDidMount() {
    document.addEventListener('copy', this._onCopy, false);
    const container = this._container.current;
    if (!container) {
      throw new Error(
        'The container was assumed to exist while mounting The VirtualList.'
      );
    }
    container.addEventListener('scroll', this._onScroll);
    this._onScroll(); // for initial size
  }

  componentWillUnmount() {
    document.removeEventListener('copy', this._onCopy, false);
    const container = this._container.current;
    if (!container) {
      throw new Error(
        'The container was assumed to exist while unmounting The VirtualList.'
      );
    }
    container.removeEventListener('scroll', this._onScroll);
  }

  _onScroll = () => {
    this._geometry = this._queryGeometry();
    this.forceUpdate();
  };

  _onCopy = (event: ClipboardEvent) => {
    if (document.activeElement === this._container.current) {
      this.props.onCopy(event);
    }
  };

  _queryGeometry(): Geometry | void {
    const container = this._container.current;
    const inner = this._inner.current;
    if (!container || !inner) {
      return undefined;
    }
    const outerRect = container.getBoundingClientRect();
    const innerRectY = inner.getBoundingClientRect().top;
    return { outerRect, innerRectY };
  }

  computeVisibleRange() {
    const { itemHeight, disableOverscan } = this.props;
    if (!this._geometry) {
      return { visibleRangeStart: 0, visibleRangeEnd: 100 };
    }
    const { outerRect, innerRectY } = this._geometry;
    const overscan = disableOverscan ? 0 : 25;
    const chunkSize = 16;
    let visibleRangeStart =
      Math.floor((outerRect.top - innerRectY) / itemHeight) - overscan;
    let visibleRangeEnd =
      Math.ceil((outerRect.bottom - innerRectY) / itemHeight) + overscan;
    if (!disableOverscan) {
      visibleRangeStart = Math.floor(visibleRangeStart / chunkSize) * chunkSize;
      visibleRangeEnd = Math.ceil(visibleRangeEnd / chunkSize) * chunkSize;
    }
    return { visibleRangeStart, visibleRangeEnd };
  }

  /* This method is used by users of this component. */
  /* eslint-disable-next-line react/no-unused-class-component-methods */
  scrollItemIntoView(itemIndex: number, offsetX: CssPixels) {
    const container = this._container.current;
    if (!container) {
      return;
    }
    const itemTop = itemIndex * this.props.itemHeight;
    const itemBottom = itemTop + this.props.itemHeight;

    if (container.scrollTop > itemTop) {
      container.scrollTop = itemTop;
    } else if (container.scrollTop + container.clientHeight < itemBottom) {
      container.scrollTop = Math.min(
        itemTop,
        itemBottom - container.clientHeight
      );
    }

    const interestingWidth = 400;
    const itemLeft = offsetX;
    const itemRight = itemLeft + interestingWidth;

    if (container.scrollLeft > itemLeft) {
      container.scrollLeft = itemLeft;
    } else if (container.scrollLeft + container.clientWidth < itemRight) {
      container.scrollLeft = Math.min(
        itemLeft,
        itemRight - container.clientWidth
      );
    }
  }

  /* This method is used by users of this component. */
  /* eslint-disable-next-line react/no-unused-class-component-methods */
  focus() {
    const container = this._container.current;
    if (container) {
      container.focus();
    }
  }

  _onMouseLeaveInnerWrapper = () => {
    if (this.props.onMouseLeaveRenderedList) {
      this.props.onMouseLeaveRenderedList();
    }
  };

  render() {
    const {
      itemHeight,
      className,
      renderItem,
      items,
      focusable,
      specialItems,
      onKeyDown,
      containerWidth,
      forceRender,
      ariaRole,
      ariaLabel,
      ariaActiveDescendant,
    } = this.props;
    const columnCount = this.props.columnCount || 1;
    const { visibleRangeStart, visibleRangeEnd } = this.computeVisibleRange();
    return (

        <div
          className={`${className}InnerWrapper`}
          onMouseLeave={this._onMouseLeaveInnerWrapper}
        >
          {range(columnCount).map((columnIndex) => (
            <VirtualListInner
              className={classNames(
                `${className}Inner`,
                `${className}Inner${columnIndex}`
              )}
              visibleRangeStart={Math.max(0, visibleRangeStart)}
              visibleRangeEnd={Math.min(items.length, visibleRangeEnd)}
              itemHeight={itemHeight}
              renderItem={renderItem}
              items={items}
              specialItems={specialItems}
              columnIndex={columnIndex}
              containerWidth={containerWidth}
              forceRender={forceRender}
              key={columnIndex}
              ref={columnIndex === 0 ? this._inner : undefined}
            />
          ))}

    );
  }
}
<commit_msg>Make onCopy and onKeyDown optional on VirtualList.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// @flow

/**
 * VirtualList implements a virtualized component. This means it doesn't
 * render only the items that are currently displayed, and makes long list
 * manageable in the Web platform.
 * This implementation has some unique features that make it especially
 * performant, but also comes with constraints.
 *
 * The items are organized in chunks:
 *
 * .
 * | 16 items
 * |
 * -
 * |
 * | 16 items
 * |
 * -
 * |
 * | 16 items
 * .
 *
 * Depending on the current scroll position, the logic decides which chunk(s)
 * will be rendered. If `disableOverscan` is false (this is the default) we
 * render more items before and after the visible part so that when scrolling
 * the user doesn't see a white background while the app catches up.
 *
 * Using chunks avoids the need to render often, and thus improves performance.
 */

import * as React from 'react';
import classNames from 'classnames';
import range from 'array-range';

import type { CssPixels } from 'firefox-profiler/types';

type RenderItem<Item> = (Item, number, number) => React.Node;

type VirtualListRowProps<Item> = {|
  +renderItem: RenderItem<Item>,
  +item: Item,
  +index: number,
  +columnIndex: number,
  // These properties are not used directly, but are needed for strict equality
  // checks so that the components update correctly.
  // * `forceRenderControl` is used when we want to update one row or a few rows only,
  //   this is typically when the selection changes and both the old and the new
  //   selection need to be changed.
  //   It needs to change whenever the row should be updated, so it should be
  //   computed from the values that control these update.
  +forceRenderItem: string,
  // * `items` contains the full items, so that we update the whole list
  //   whenever the source changes. This is necessary because often `item` is a
  //   native value (eg a number), and shallow checking only `item` won't always
  //   give the expected behavior.
  +items: $ReadOnlyArray<Item>,
  // * `forceRender` is passed through directly from the main VirtualList
  //   component to the row as a way to update the full list for reasons
  //   unbeknownst to this component. This can be used for example in chart-like
  //   panels where we'd want to redraw if some source value necessary to the
  //   computation changes.
  +forceRender?: number | string,
|};

class VirtualListRow<Item> extends React.PureComponent<
  VirtualListRowProps<Item>
> {
  render() {
    const { renderItem, item, index, columnIndex } = this.props;
    return renderItem(item, index, columnIndex);
  }
}

type VirtualListInnerChunkProps<Item> = {|
  +className: string,
  +renderItem: RenderItem<Item>,
  +items: $ReadOnlyArray<Item>,
  +specialItems: $ReadOnlyArray<Item | void>,
  +visibleRangeStart: number,
  +visibleRangeEnd: number,
  +columnIndex: number,
  +forceRender?: number | string,
|};

class VirtualListInnerChunk<Item> extends React.PureComponent<
  VirtualListInnerChunkProps<Item>
> {
  render() {
    const {
      className,
      renderItem,
      items,
      specialItems,
      visibleRangeStart,
      visibleRangeEnd,
      columnIndex,
      forceRender,
    } = this.props;

    return (

        {range(
          visibleRangeStart,
          Math.max(visibleRangeStart, visibleRangeEnd)
        ).map((i) => {
          const item = items[i];

          // We compute forceRenderItem from the first position of item in the list,
          // and the number of occurrences. Indeed we want to rerender this
          // specific item whenever one of these values changes.
          const firstPosOfItem = specialItems.indexOf(item);
          const countOfItem = specialItems.reduce(
            (acc, specialItem) => (specialItem === item ? acc + 1 : acc),
            0
          );
          const forceRenderItem = `${firstPosOfItem}|${countOfItem}`;

          return (
            <VirtualListRow
              key={i}
              index={i}
              columnIndex={columnIndex}
              renderItem={renderItem}
              item={item}
              items={items}
              forceRenderItem={forceRenderItem}
              forceRender={forceRender}
            />
          );
        })}

    );
  }
}

type VirtualListInnerProps<Item> = {|
  +itemHeight: CssPixels,
  +className: string,
  +renderItem: RenderItem<Item>,
  +items: $ReadOnlyArray<Item>,
  +specialItems: $ReadOnlyArray<Item | void>,
  +visibleRangeStart: number,
  +visibleRangeEnd: number,
  +columnIndex: number,
  +containerWidth: CssPixels,
  +forceRender?: number | string,
|};

class VirtualListInner<Item> extends React.PureComponent<
  VirtualListInnerProps<Item>
> {
  _container: ?HTMLElement;

  _takeContainerRef = (element: ?HTMLDivElement) => {
    this._container = element;
  };

  /* This method is used by users of this component. */
  /* eslint-disable-next-line react/no-unused-class-component-methods */
  getBoundingClientRect() {
    if (this._container) {
      return this._container.getBoundingClientRect();
    }
    return new DOMRect(0, 0, 0, 0);
  }

  render() {
    const {
      itemHeight,
      className,
      renderItem,
      items,
      specialItems,
      visibleRangeStart,
      visibleRangeEnd,
      columnIndex,
      containerWidth,
      forceRender,
    } = this.props;

    const chunkSize = 16;
    const startChunkIndex = Math.floor(visibleRangeStart / chunkSize);
    const endChunkIndex = Math.ceil(visibleRangeEnd / chunkSize);
    const chunks = range(
      startChunkIndex,
      Math.max(startChunkIndex, endChunkIndex)
    ).map((c) => c * chunkSize);

    return (
      <div
        className={className}
        ref={this._takeContainerRef}
        // Add padding to list height to account for overlay scrollbars.
        style={{
          height: `${(items.length + 1) * itemHeight}px`,
          minWidth: columnIndex === 1 ? containerWidth : undefined,
        }}
      >
        <div
          className={`${className}TopSpacer`}
          key={-1}
          style={{ height: Math.max(0, visibleRangeStart) * itemHeight + 'px' }}
        />
        {chunks.map((chunkStart) => {
          return (
            <VirtualListInnerChunk
              className={`${className}InnerChunk`}
              key={chunkStart}
              visibleRangeStart={Math.max(chunkStart, visibleRangeStart)}
              visibleRangeEnd={Math.min(
                chunkStart + chunkSize,
                visibleRangeEnd
              )}
              columnIndex={columnIndex}
              renderItem={renderItem}
              items={items}
              specialItems={specialItems}
              forceRender={forceRender}
            />
          );
        })}

    );
  }
}

type VirtualListProps<Item> = {|
  +itemHeight: CssPixels,
  +className: string,
  +renderItem: RenderItem<Item>,
  +items: $ReadOnlyArray<Item>,
  +focusable: boolean,
  +specialItems: $ReadOnlyArray<Item | void>,
  +onKeyDown?: (SyntheticKeyboardEvent<>) => void,
  +onCopy?: (ClipboardEvent) => void,
  // This is called when the mouse leaves the list as it is rendered. That is if
  // there isn't enough item to fill the component's height, and the user moves
  // the mouse below the items, this callback would be called.
  +onMouseLeaveRenderedList?: () => void,
  // Set `disableOverscan` to `true` when you expect a lot of updates in a short
  // time: this will render only the visible part, which makes each update faster.
  +disableOverscan: boolean,
  +columnCount: number,
  +containerWidth: CssPixels,
  // `forceRender` is passed through directly from the main VirtualList
  // component to the row as a way to update the full list for reasons
  // unbeknownst to this component. This can be used for example in chart-like
  // panels where we'd want to redraw if some source value necessary to the
  // computation changes.
  +forceRender?: number | string,
  // The next 3 props will be applied to the underlying DOM element.
  // They're important for accessibility (especially focus and navigation).
  +ariaLabel?: string,
  +ariaRole?: string,
  // Aria-activedescendant specifies the children's "virtual" focus.
  +ariaActiveDescendant?: null | string,
|};

type Geometry = {
  // getBoundingClientRect in the Flow definitions is wrong, and labels the return values
  // as a ClientRect, and not a DOMRect. https://github.com/facebook/flow/issues/5475
  //
  // Account for that here:
  outerRect: DOMRect | ClientRect,
  innerRectY: CssPixels,
};

export class VirtualList<Item> extends React.PureComponent<
  VirtualListProps<Item>
> {
  _container: {| current: HTMLDivElement | null |} = React.createRef();
  _inner: {| current: VirtualListInner<Item> | null |} = React.createRef();
  _geometry: ?Geometry;

  componentDidMount() {
    document.addEventListener('copy', this._onCopy, false);
    const container = this._container.current;
    if (!container) {
      throw new Error(
        'The container was assumed to exist while mounting The VirtualList.'
      );
    }
    container.addEventListener('scroll', this._onScroll);
    this._onScroll(); // for initial size
  }

  componentWillUnmount() {
    document.removeEventListener('copy', this._onCopy, false);
    const container = this._container.current;
    if (!container) {
      throw new Error(
        'The container was assumed to exist while unmounting The VirtualList.'
      );
    }
    container.removeEventListener('scroll', this._onScroll);
  }

  _onScroll = () => {
    this._geometry = this._queryGeometry();
    this.forceUpdate();
  };

  _onCopy = (event: ClipboardEvent) => {
    const { onCopy } = this.props;
    if (onCopy && document.activeElement === this._container.current) {
      onCopy(event);
    }
  };

  _queryGeometry(): Geometry | void {
    const container = this._container.current;
    const inner = this._inner.current;
    if (!container || !inner) {
      return undefined;
    }
    const outerRect = container.getBoundingClientRect();
    const innerRectY = inner.getBoundingClientRect().top;
    return { outerRect, innerRectY };
  }

  computeVisibleRange() {
    const { itemHeight, disableOverscan } = this.props;
    if (!this._geometry) {
      return { visibleRangeStart: 0, visibleRangeEnd: 100 };
    }
    const { outerRect, innerRectY } = this._geometry;
    const overscan = disableOverscan ? 0 : 25;
    const chunkSize = 16;
    let visibleRangeStart =
      Math.floor((outerRect.top - innerRectY) / itemHeight) - overscan;
    let visibleRangeEnd =
      Math.ceil((outerRect.bottom - innerRectY) / itemHeight) + overscan;
    if (!disableOverscan) {
      visibleRangeStart = Math.floor(visibleRangeStart / chunkSize) * chunkSize;
      visibleRangeEnd = Math.ceil(visibleRangeEnd / chunkSize) * chunkSize;
    }
    return { visibleRangeStart, visibleRangeEnd };
  }

  /* This method is used by users of this component. */
  /* eslint-disable-next-line react/no-unused-class-component-methods */
  scrollItemIntoView(itemIndex: number, offsetX: CssPixels) {
    const container = this._container.current;
    if (!container) {
      return;
    }
    const itemTop = itemIndex * this.props.itemHeight;
    const itemBottom = itemTop + this.props.itemHeight;

    if (container.scrollTop > itemTop) {
      container.scrollTop = itemTop;
    } else if (container.scrollTop + container.clientHeight < itemBottom) {
      container.scrollTop = Math.min(
        itemTop,
        itemBottom - container.clientHeight
      );
    }

    const interestingWidth = 400;
    const itemLeft = offsetX;
    const itemRight = itemLeft + interestingWidth;

    if (container.scrollLeft > itemLeft) {
      container.scrollLeft = itemLeft;
    } else if (container.scrollLeft + container.clientWidth < itemRight) {
      container.scrollLeft = Math.min(
        itemLeft,
        itemRight - container.clientWidth
      );
    }
  }

  /* This method is used by users of this component. */
  /* eslint-disable-next-line react/no-unused-class-component-methods */
  focus() {
    const container = this._container.current;
    if (container) {
      container.focus();
    }
  }

  _onMouseLeaveInnerWrapper = () => {
    if (this.props.onMouseLeaveRenderedList) {
      this.props.onMouseLeaveRenderedList();
    }
  };

  render() {
    const {
      itemHeight,
      className,
      renderItem,
      items,
      focusable,
      specialItems,
      onKeyDown,
      containerWidth,
      forceRender,
      ariaRole,
      ariaLabel,
      ariaActiveDescendant,
    } = this.props;
    const columnCount = this.props.columnCount || 1;
    const { visibleRangeStart, visibleRangeEnd } = this.computeVisibleRange();
    return (

        <div
          className={`${className}InnerWrapper`}
          onMouseLeave={this._onMouseLeaveInnerWrapper}
        >
          {range(columnCount).map((columnIndex) => (
            <VirtualListInner
              className={classNames(
                `${className}Inner`,
                `${className}Inner${columnIndex}`
              )}
              visibleRangeStart={Math.max(0, visibleRangeStart)}
              visibleRangeEnd={Math.min(items.length, visibleRangeEnd)}
              itemHeight={itemHeight}
              renderItem={renderItem}
              items={items}
              specialItems={specialItems}
              columnIndex={columnIndex}
              containerWidth={containerWidth}
              forceRender={forceRender}
              key={columnIndex}
              ref={columnIndex === 0 ? this._inner : undefined}
            />
          ))}

    );
  }
}
<|endoftext|>


<commit_before>/**
Attaches stand-alone container with editable-form to HTML element. Element is used only for positioning, value is not stored anywhere.

This method applied internally in $().editable(). You should subscribe on it's events (save / cancel) to get profit of it.

Final realization can be different: bootstrap-popover, jqueryui-tooltip, poshytip, inline-div. It depends on which js file you include.

Applied as jQuery method.

@class editableContainer
@uses editableform
**/
(function ($) {

    var Popup = function (element, options) {
        this.init(element, options);
    };

    var Inline = function (element, options) {
        this.init(element, options);
    };

    //methods
    Popup.prototype = {
        containerName: null, //tbd in child class
        innerCss: null, //tbd in child class
        init: function(element, options) {
            this.$element = $(element);
            //since 1.4.1 container do not use data-* directly as they already merged into options.
            this.options = $.extend({}, $.fn.editableContainer.defaults, options);
            this.splitOptions();

            //set scope of form callbacks to element
            this.formOptions.scope = this.$element[0];

            this.initContainer();

            //bind 'destroyed' listener to destroy container when element is removed from dom
            this.$element.on('destroyed', $.proxy(function(){
                this.destroy();
            }, this));

            //attach document handler to close containers on click / escape
            if(!$(document).data('editable-handlers-attached')) {
                //close all on escape
                $(document).on('keyup.editable', function (e) {
                    if (e.which === 27) {
                        $('.editable-open').editableContainer('hide');
                        //todo: return focus on element
                    }
                });

                //close containers when click outside
                $(document).on('click.editable', function(e) {
                    var $target = $(e.target), i,
                        exclude_classes = ['.editable-container',
                                           '.ui-datepicker-header',
                                           '.modal-backdrop',
                                           '.bootstrap-wysihtml5-insert-image-modal',
                                           '.bootstrap-wysihtml5-insert-link-modal'];

                    //if click inside one of exclude classes --> no nothing
                    for(i=0; i<exclude_classes.length; i++) {
                         if($target.is(exclude_classes[i]) || $target.parents(exclude_classes[i]).length) {
                             return;
                         }
                    }

                    //close all open containers (except one - target)
                    Popup.prototype.closeOthers(e.target);
                });

                $(document).data('editable-handlers-attached', true);
            }
        },

        //split options on containerOptions and formOptions
        splitOptions: function() {
            this.containerOptions = {};
            this.formOptions = {};
            var cDef = $.fn[this.containerName].defaults;
            //keys defined in container defaults go to container, others go to form
            for(var k in this.options) {
              if(k in cDef) {
                 this.containerOptions[k] = this.options[k];
              } else {
                 this.formOptions[k] = this.options[k];
              }
            }
        },

        /*
        Returns jquery object of container
        @method tip()
        */
        tip: function() {
            return this.container() ? this.container().$tip : null;
        },

        /* returns container object */
        container: function() {
            return this.$element.data(this.containerName);
        },

        call: function() {
            this.$element[this.containerName].apply(this.$element, arguments);
        },

        initContainer: function(){
            this.call(this.containerOptions);
        },

        renderForm: function() {
            this.$form
            .editableform(this.formOptions)
            .on({
                save: $.proxy(this.save, this), //click on submit button (value changed)
                nochange: $.proxy(function(){ this.hide('nochange'); }, this), //click on submit button (value NOT changed)
                cancel: $.proxy(function(){ this.hide('cancel'); }, this), //click on calcel button
                show: $.proxy(this.setPosition, this), //re-position container every time form is shown (occurs each time after loading state)
                rendering: $.proxy(this.setPosition, this), //this allows to place container correctly when loading shown
                resize: $.proxy(this.setPosition, this), //this allows to re-position container when form size is changed
                rendered: $.proxy(function(){
                    /**
                    Fired when container is shown and form is rendered (for select will wait for loading dropdown options)

                    @event shown
                    @param {Object} event event object
                    @example
                    $('#username').on('shown', function() {
                        var editable = $(this).data('editable');
                        editable.input.$input.val('overwriting value of input..');
                    });
                    **/
                    this.$element.triggerHandler('shown');
                }, this)
            })
            .editableform('render');
        },

        /**
        Shows container with form
        @method show()
        @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
        **/
        /* Note: poshytip owerwrites this method totally! */
        show: function (closeAll) {
            this.$element.addClass('editable-open');
            if(closeAll !== false) {
                //close all open containers (except this)
                this.closeOthers(this.$element[0]);
            }

            //show container itself
            this.innerShow();
            this.tip().addClass('editable-container');

            /*
            Currently, form is re-rendered on every show.
            The main reason is that we dont know, what container will do with content when closed:
            remove(), detach() or just hide().

            Detaching form itself before hide and re-insert before show is good solution,
            but visually it looks ugly, as container changes size before hide.
            */

            //if form already exist - delete previous data
            if(this.$form) {
                //todo: destroy prev data!
                //this.$form.destroy();
            }

            this.$form = $('
');

            //insert form into container body
            if(this.tip().is(this.innerCss)) {
                //for inline container
                this.tip().append(this.$form);
            } else {
                this.tip().find(this.innerCss).append(this.$form);
            }

            //render form
            this.renderForm();
        },

        /**
        Hides container with form
        @method hide()
        @param {string} reason Reason caused hiding. Can be save|cancel|onblur|nochange|undefined (=manual)
        **/
        hide: function(reason) {
            if(!this.tip() || !this.tip().is(':visible') || !this.$element.hasClass('editable-open')) {
                return;
            }

            this.$element.removeClass('editable-open');
            this.innerHide();

            /**
            Fired when container was hidden. It occurs on both save or cancel.

            @event hidden
            @param {object} event event object
            @param {string} reason Reason caused hiding. Can be save|cancel|onblur|nochange|undefined (=manual)
            @example
            $('#username').on('hidden', function(e, reason) {
                if(reason === 'save' || reason === 'cancel') {
                    //auto-open next editable
                    $(this).closest('tr').next().find('.editable').editable('show');
                }
            });
            **/
            this.$element.triggerHandler('hidden', reason);
        },

        /* internal show method. To be overwritten in child classes */
        innerShow: function () {

        },

        /* internal hide method. To be overwritten in child classes */
        innerHide: function () {

        },

        /**
        Toggles container visibility (show / hide)
        @method toggle()
        @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
        **/
        toggle: function(closeAll) {
            if(this.container() && this.tip() && this.tip().is(':visible')) {
                this.hide();
            } else {
                this.show(closeAll);
            }
        },

        /*
        Updates the position of container when content changed.
        @method setPosition()
        */
        setPosition: function() {
            //tbd in child class
        },

        save: function(e, params) {
            /**
            Fired when new value was submitted. You can use $(this).data('editableContainer') inside handler to access to editableContainer instance

            @event save
            @param {Object} event event object
            @param {Object} params additional params
            @param {mixed} params.newValue submitted value
            @param {Object} params.response ajax response
            @example
            $('#username').on('save', function(e, params) {
                //assuming server response: '{success: true}'
                var pk = $(this).data('editableContainer').options.pk;
                if(params.response && params.response.success) {
                    alert('value: ' + params.newValue + ' with pk: ' + pk + ' saved!');
                } else {
                    alert('error!');
                }
            });
            **/
            this.$element.triggerHandler('save', params);

            //hide must be after trigger, as saving value may require methods od plugin, applied to input
            this.hide('save');
        },

        /**
        Sets new option

        @method option(key, value)
        @param {string} key
        @param {mixed} value
        **/
        option: function(key, value) {
            this.options[key] = value;
            if(key in this.containerOptions) {
                this.containerOptions[key] = value;
                this.setContainerOption(key, value);
            } else {
                this.formOptions[key] = value;
                if(this.$form) {
                    this.$form.editableform('option', key, value);
                }
            }
        },

        setContainerOption: function(key, value) {
            this.call('option', key, value);
        },

        /**
        Destroys the container instance
        @method destroy()
        **/
        destroy: function() {
            this.hide();
            this.innerDestroy();
            this.$element.off('destroyed');
            this.$element.removeData('editableContainer');
        },

        /* to be overwritten in child classes */
        innerDestroy: function() {

        },

        /*
        Closes other containers except one related to passed element.
        Other containers can be cancelled or submitted (depends on onblur option)
        */
        closeOthers: function(element) {
            $('.editable-open').each(function(i, el){
                //do nothing with passed element and it's children
                if(el === element || $(el).find(element).length) {
                    return;
                }

                //otherwise cancel or submit all open containers
                var $el = $(el),
                ec = $el.data('editableContainer');

                if(!ec) {
                    return;
                }

                if(ec.options.onblur === 'cancel') {
                    $el.data('editableContainer').hide('onblur');
                } else if(ec.options.onblur === 'submit') {
                    $el.data('editableContainer').tip().find('form').submit();
                }
            });

        },

        /**
        Activates input of visible container (e.g. set focus)
        @method activate()
        **/
        activate: function() {
            if(this.tip && this.tip().is(':visible') && this.$form) {
               this.$form.data('editableform').input.activate();
            }
        }

    };

    /**
    jQuery method to initialize editableContainer.

    @method $().editableContainer(options)
    @params {Object} options
    @example
    $('#edit').editableContainer({
        type: 'text',
        url: '/post',
        pk: 1,
        value: 'hello'
    });
    **/
    $.fn.editableContainer = function (option) {
        var args = arguments;
        return this.each(function () {
            var $this = $(this),
            dataKey = 'editableContainer',
            data = $this.data(dataKey),
            options = typeof option === 'object' && option,
            Constructor = (options.mode === 'inline') ? Inline : Popup;

            if (!data) {
                $this.data(dataKey, (data = new Constructor(this, options)));
            }

            if (typeof option === 'string') { //call method
                data[option].apply(data, Array.prototype.slice.call(args, 1));
            }
        });
    };

    //store constructors
    $.fn.editableContainer.Popup = Popup;
    $.fn.editableContainer.Inline = Inline;

    //defaults
    $.fn.editableContainer.defaults = {
        /**
        Initial value of form input

        @property value
        @type mixed
        @default null
        @private
        **/
        value: null,
        /**
        Placement of container relative to element. Can be top|right|bottom|left. Not used for inline container.

        @property placement
        @type string
        @default 'top'
        **/
        placement: 'top',
        /**
        Whether to hide container on save/cancel.

        @property autohide
        @type boolean
        @default true
        @private
        **/
        autohide: true,
        /**
        Action when user clicks outside the container. Can be cancel|submit|ignore.
        Setting ignore allows to have several containers open.

        @property onblur
        @type string
        @default 'cancel'
        @since 1.1.1
        **/
        onblur: 'cancel',

        /**
        Animation speed (inline mode)
        @property anim
        @type string
        @default 'fast'
        **/
        anim: 'fast',

        /**
        Mode of editable, can be `popup` or `inline`

        @property mode
        @type string
        @default 'popup'
        @since 1.4.0
        **/
        mode: 'popup'
    };

    /*
    * workaround to have 'destroyed' event to destroy popover when element is destroyed
    * see http://stackoverflow.com/questions/2200494/jquery-trigger-event-when-an-element-is-removed-from-the-dom
    */
    jQuery.event.special.destroyed = {
        remove: function(o) {
            if (o.handler) {
                o.handler();
            }
        }
    };

}(window.jQuery));
<commit_msg>container anim option default to false instead of fast - looks better<commit_after>/**
Attaches stand-alone container with editable-form to HTML element. Element is used only for positioning, value is not stored anywhere.

This method applied internally in $().editable(). You should subscribe on it's events (save / cancel) to get profit of it.

Final realization can be different: bootstrap-popover, jqueryui-tooltip, poshytip, inline-div. It depends on which js file you include.

Applied as jQuery method.

@class editableContainer
@uses editableform
**/
(function ($) {

    var Popup = function (element, options) {
        this.init(element, options);
    };

    var Inline = function (element, options) {
        this.init(element, options);
    };

    //methods
    Popup.prototype = {
        containerName: null, //tbd in child class
        innerCss: null, //tbd in child class
        init: function(element, options) {
            this.$element = $(element);
            //since 1.4.1 container do not use data-* directly as they already merged into options.
            this.options = $.extend({}, $.fn.editableContainer.defaults, options);
            this.splitOptions();

            //set scope of form callbacks to element
            this.formOptions.scope = this.$element[0];

            this.initContainer();

            //bind 'destroyed' listener to destroy container when element is removed from dom
            this.$element.on('destroyed', $.proxy(function(){
                this.destroy();
            }, this));

            //attach document handler to close containers on click / escape
            if(!$(document).data('editable-handlers-attached')) {
                //close all on escape
                $(document).on('keyup.editable', function (e) {
                    if (e.which === 27) {
                        $('.editable-open').editableContainer('hide');
                        //todo: return focus on element
                    }
                });

                //close containers when click outside
                $(document).on('click.editable', function(e) {
                    var $target = $(e.target), i,
                        exclude_classes = ['.editable-container',
                                           '.ui-datepicker-header',
                                           '.modal-backdrop',
                                           '.bootstrap-wysihtml5-insert-image-modal',
                                           '.bootstrap-wysihtml5-insert-link-modal'];

                    //if click inside one of exclude classes --> no nothing
                    for(i=0; i<exclude_classes.length; i++) {
                         if($target.is(exclude_classes[i]) || $target.parents(exclude_classes[i]).length) {
                             return;
                         }
                    }

                    //close all open containers (except one - target)
                    Popup.prototype.closeOthers(e.target);
                });

                $(document).data('editable-handlers-attached', true);
            }
        },

        //split options on containerOptions and formOptions
        splitOptions: function() {
            this.containerOptions = {};
            this.formOptions = {};
            var cDef = $.fn[this.containerName].defaults;
            //keys defined in container defaults go to container, others go to form
            for(var k in this.options) {
              if(k in cDef) {
                 this.containerOptions[k] = this.options[k];
              } else {
                 this.formOptions[k] = this.options[k];
              }
            }
        },

        /*
        Returns jquery object of container
        @method tip()
        */
        tip: function() {
            return this.container() ? this.container().$tip : null;
        },

        /* returns container object */
        container: function() {
            return this.$element.data(this.containerName);
        },

        call: function() {
            this.$element[this.containerName].apply(this.$element, arguments);
        },

        initContainer: function(){
            this.call(this.containerOptions);
        },

        renderForm: function() {
            this.$form
            .editableform(this.formOptions)
            .on({
                save: $.proxy(this.save, this), //click on submit button (value changed)
                nochange: $.proxy(function(){ this.hide('nochange'); }, this), //click on submit button (value NOT changed)
                cancel: $.proxy(function(){ this.hide('cancel'); }, this), //click on calcel button
                show: $.proxy(this.setPosition, this), //re-position container every time form is shown (occurs each time after loading state)
                rendering: $.proxy(this.setPosition, this), //this allows to place container correctly when loading shown
                resize: $.proxy(this.setPosition, this), //this allows to re-position container when form size is changed
                rendered: $.proxy(function(){
                    /**
                    Fired when container is shown and form is rendered (for select will wait for loading dropdown options)

                    @event shown
                    @param {Object} event event object
                    @example
                    $('#username').on('shown', function() {
                        var editable = $(this).data('editable');
                        editable.input.$input.val('overwriting value of input..');
                    });
                    **/
                    this.$element.triggerHandler('shown');
                }, this)
            })
            .editableform('render');
        },

        /**
        Shows container with form
        @method show()
        @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
        **/
        /* Note: poshytip owerwrites this method totally! */
        show: function (closeAll) {
            this.$element.addClass('editable-open');
            if(closeAll !== false) {
                //close all open containers (except this)
                this.closeOthers(this.$element[0]);
            }

            //show container itself
            this.innerShow();
            this.tip().addClass('editable-container');

            /*
            Currently, form is re-rendered on every show.
            The main reason is that we dont know, what container will do with content when closed:
            remove(), detach() or just hide().

            Detaching form itself before hide and re-insert before show is good solution,
            but visually it looks ugly, as container changes size before hide.
            */

            //if form already exist - delete previous data
            if(this.$form) {
                //todo: destroy prev data!
                //this.$form.destroy();
            }

            this.$form = $('
');

            //insert form into container body
            if(this.tip().is(this.innerCss)) {
                //for inline container
                this.tip().append(this.$form);
            } else {
                this.tip().find(this.innerCss).append(this.$form);
            }

            //render form
            this.renderForm();
        },

        /**
        Hides container with form
        @method hide()
        @param {string} reason Reason caused hiding. Can be save|cancel|onblur|nochange|undefined (=manual)
        **/
        hide: function(reason) {
            if(!this.tip() || !this.tip().is(':visible') || !this.$element.hasClass('editable-open')) {
                return;
            }

            this.$element.removeClass('editable-open');
            this.innerHide();

            /**
            Fired when container was hidden. It occurs on both save or cancel.

            @event hidden
            @param {object} event event object
            @param {string} reason Reason caused hiding. Can be save|cancel|onblur|nochange|undefined (=manual)
            @example
            $('#username').on('hidden', function(e, reason) {
                if(reason === 'save' || reason === 'cancel') {
                    //auto-open next editable
                    $(this).closest('tr').next().find('.editable').editable('show');
                }
            });
            **/
            this.$element.triggerHandler('hidden', reason);
        },

        /* internal show method. To be overwritten in child classes */
        innerShow: function () {

        },

        /* internal hide method. To be overwritten in child classes */
        innerHide: function () {

        },

        /**
        Toggles container visibility (show / hide)
        @method toggle()
        @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
        **/
        toggle: function(closeAll) {
            if(this.container() && this.tip() && this.tip().is(':visible')) {
                this.hide();
            } else {
                this.show(closeAll);
            }
        },

        /*
        Updates the position of container when content changed.
        @method setPosition()
        */
        setPosition: function() {
            //tbd in child class
        },

        save: function(e, params) {
            /**
            Fired when new value was submitted. You can use $(this).data('editableContainer') inside handler to access to editableContainer instance

            @event save
            @param {Object} event event object
            @param {Object} params additional params
            @param {mixed} params.newValue submitted value
            @param {Object} params.response ajax response
            @example
            $('#username').on('save', function(e, params) {
                //assuming server response: '{success: true}'
                var pk = $(this).data('editableContainer').options.pk;
                if(params.response && params.response.success) {
                    alert('value: ' + params.newValue + ' with pk: ' + pk + ' saved!');
                } else {
                    alert('error!');
                }
            });
            **/
            this.$element.triggerHandler('save', params);

            //hide must be after trigger, as saving value may require methods od plugin, applied to input
            this.hide('save');
        },

        /**
        Sets new option

        @method option(key, value)
        @param {string} key
        @param {mixed} value
        **/
        option: function(key, value) {
            this.options[key] = value;
            if(key in this.containerOptions) {
                this.containerOptions[key] = value;
                this.setContainerOption(key, value);
            } else {
                this.formOptions[key] = value;
                if(this.$form) {
                    this.$form.editableform('option', key, value);
                }
            }
        },

        setContainerOption: function(key, value) {
            this.call('option', key, value);
        },

        /**
        Destroys the container instance
        @method destroy()
        **/
        destroy: function() {
            this.hide();
            this.innerDestroy();
            this.$element.off('destroyed');
            this.$element.removeData('editableContainer');
        },

        /* to be overwritten in child classes */
        innerDestroy: function() {

        },

        /*
        Closes other containers except one related to passed element.
        Other containers can be cancelled or submitted (depends on onblur option)
        */
        closeOthers: function(element) {
            $('.editable-open').each(function(i, el){
                //do nothing with passed element and it's children
                if(el === element || $(el).find(element).length) {
                    return;
                }

                //otherwise cancel or submit all open containers
                var $el = $(el),
                ec = $el.data('editableContainer');

                if(!ec) {
                    return;
                }

                if(ec.options.onblur === 'cancel') {
                    $el.data('editableContainer').hide('onblur');
                } else if(ec.options.onblur === 'submit') {
                    $el.data('editableContainer').tip().find('form').submit();
                }
            });

        },

        /**
        Activates input of visible container (e.g. set focus)
        @method activate()
        **/
        activate: function() {
            if(this.tip && this.tip().is(':visible') && this.$form) {
               this.$form.data('editableform').input.activate();
            }
        }

    };

    /**
    jQuery method to initialize editableContainer.

    @method $().editableContainer(options)
    @params {Object} options
    @example
    $('#edit').editableContainer({
        type: 'text',
        url: '/post',
        pk: 1,
        value: 'hello'
    });
    **/
    $.fn.editableContainer = function (option) {
        var args = arguments;
        return this.each(function () {
            var $this = $(this),
            dataKey = 'editableContainer',
            data = $this.data(dataKey),
            options = typeof option === 'object' && option,
            Constructor = (options.mode === 'inline') ? Inline : Popup;

            if (!data) {
                $this.data(dataKey, (data = new Constructor(this, options)));
            }

            if (typeof option === 'string') { //call method
                data[option].apply(data, Array.prototype.slice.call(args, 1));
            }
        });
    };

    //store constructors
    $.fn.editableContainer.Popup = Popup;
    $.fn.editableContainer.Inline = Inline;

    //defaults
    $.fn.editableContainer.defaults = {
        /**
        Initial value of form input

        @property value
        @type mixed
        @default null
        @private
        **/
        value: null,
        /**
        Placement of container relative to element. Can be top|right|bottom|left. Not used for inline container.

        @property placement
        @type string
        @default 'top'
        **/
        placement: 'top',
        /**
        Whether to hide container on save/cancel.

        @property autohide
        @type boolean
        @default true
        @private
        **/
        autohide: true,
        /**
        Action when user clicks outside the container. Can be cancel|submit|ignore.
        Setting ignore allows to have several containers open.

        @property onblur
        @type string
        @default 'cancel'
        @since 1.1.1
        **/
        onblur: 'cancel',

        /**
        Animation speed (inline mode)
        @property anim
        @type string
        @default false
        **/
        anim: false,

        /**
        Mode of editable, can be `popup` or `inline`

        @property mode
        @type string
        @default 'popup'
        @since 1.4.0
        **/
        mode: 'popup'
    };

    /*
    * workaround to have 'destroyed' event to destroy popover when element is destroyed
    * see http://stackoverflow.com/questions/2200494/jquery-trigger-event-when-an-element-is-removed-from-the-dom
    */
    jQuery.event.special.destroyed = {
        remove: function(o) {
            if (o.handler) {
                o.handler();
            }
        }
    };

}(window.jQuery));
<|endoftext|>