<commit_before>/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule NavigationTransitioner
 * @flow
 */
'use strict';

const Animated = require('Animated');
const Easing = require('Easing');
const NavigationPropTypes = require('NavigationPropTypes');
const NavigationScenesReducer = require('NavigationScenesReducer');
const React = require('React');
const StyleSheet = require('StyleSheet');
const View = require('View');

import type {
  NavigationAnimatedValue,
  NavigationLayout,
  NavigationScene,
  NavigationState,
  NavigationTransitionProps,
  NavigationTransitionSpec,
} from 'NavigationTypeDefinition';

type Props = {
  configureTransition: (
    a: NavigationTransitionProps,
    b: ?NavigationTransitionProps,
  ) => NavigationTransitionSpec,
  navigationState: NavigationState,
  onTransitionEnd: () => void,
  onTransitionStart: () => void,
  render: (a: NavigationTransitionProps, b: ?NavigationTransitionProps) => any,
  style: any,
};

type State = {
  layout: NavigationLayout,
  position: NavigationAnimatedValue,
  progress: NavigationAnimatedValue,
  scenes: Array<NavigationScene>,
};

const {PropTypes} = React;

const DefaultTransitionSpec = {
  duration: 250,
  easing: Easing.inOut(Easing.ease),
  timing: Animated.timing,
};

class NavigationTransitioner extends React.Component<any, Props, State> {
  _onLayout: (event: any) => void;
  _onTransitionEnd: () => void;
  _prevTransitionProps: ?NavigationTransitionProps;
  _transitionProps: NavigationTransitionProps;

  props: Props;
  state: State;

  static propTypes = {
    configureTransition: PropTypes.func,
    navigationState: NavigationPropTypes.navigationState.isRequired,
    onTransitionEnd: PropTypes.func,
    onTransitionStart: PropTypes.func,
    render: PropTypes.func.isRequired,
  };

  constructor(props: Props, context: any) {
    super(props, context);

    // The initial layout isn't measured. Measured layout will be only available
    // when the component is mounted.
    const layout = {
      height: new Animated.Value(0),
      initHeight: 0,
      initWidth: 0,
      isMeasured: false,
      width: new Animated.Value(0),
    };

    this.state = {
      layout,
      position: new Animated.Value(this.props.navigationState.index),
      progress: new Animated.Value(1),
      scenes: NavigationScenesReducer([], this.props.navigationState),
    };

    this._prevTransitionProps = null;
    this._transitionProps = buildTransitionProps(props, this.state);
  }

  componentWillMount(): void {
    this._onLayout = this._onLayout.bind(this);
    this._onTransitionEnd = this._onTransitionEnd.bind(this);
  }

  componentWillReceiveProps(nextProps: Props): void {
    const nextScenes = NavigationScenesReducer(
      this.state.scenes,
      nextProps.navigationState,
      this.props.navigationState
    );

    if (nextScenes === this.state.scenes) {
      return;
    }

    const nextState = {
      ...this.state,
      scenes: nextScenes,
    };

    this._prevTransitionProps = this._transitionProps;
    this._transitionProps = buildTransitionProps(nextProps, nextState);

    const {
      position,
      progress,
    } = nextState;

    // get the transition spec.
    const transitionUserSpec = nextProps.configureTransition ?
      nextProps.configureTransition(
        this._transitionProps,
        this._prevTransitionProps,
      ) :
      null;

    const transitionSpec = {
      ...DefaultTransitionSpec,
      ...transitionUserSpec,
    };

    const {timing} = transitionSpec;
    delete transitionSpec.timing;

    progress.setValue(0);

    const animations = [
      timing(
        progress,
        {
          ...transitionSpec,
          toValue: 1,
        },
      ),
    ];

    if (nextProps.navigationState.index !== this.props.navigationState.index) {
      animations.push(
        timing(
          position,
          {
            ...transitionSpec,
            toValue: nextProps.navigationState.index,
          },
        ),
      );
    }

    // update scenes and play the transition
    this.setState(nextState, () => {
      nextProps.onTransitionStart && nextProps.onTransitionStart(
        this._transitionProps,
        this._prevTransitionProps,
      );
      Animated.parallel(animations).start(this._onTransitionEnd);
    });
  }

  render(): ReactElement<any> {
    return (
      <View
        onLayout={this._onLayout}
        style={[styles.main, this.props.style]}>
        {this.props.render(this._transitionProps, this._prevTransitionProps)}
      </View>
    );
  }

  _onLayout(event: any): void {
    const {height, width} = event.nativeEvent.layout;

    const layout = {
      ...this.state.layout,
      initHeight: height,
      initWidth: width,
      isMeasured: true,
    };

    layout.height.setValue(height);
    layout.width.setValue(width);

    const nextState = {
      ...this.state,
      layout,
    };

    this._transitionProps = buildTransitionProps(this.props, nextState);
    this.setState(nextState);
  }

  _onTransitionEnd(): void {
    const prevTransitionProps = this._prevTransitionProps;
    this._prevTransitionProps = null;

    const nextState = {
      ...this.state,
      scenes: this.state.scenes.filter(isSceneNotStale),
    };

    this._transitionProps = buildTransitionProps(this.props, nextState);

    this.setState(nextState, () => {
      this.props.onTransitionEnd && this.props.onTransitionEnd(
        this._transitionProps,
        prevTransitionProps,
      );
    });
  }
}

function buildTransitionProps(
  props: Props,
  state: State,
): NavigationTransitionProps {
  const {
    navigationState,
  } = props;

  const {
    layout,
    position,
    progress,
    scenes,
  } = state;

  return {
    layout,
    navigationState,
    position,
    progress,
    scenes,
    scene: scenes.find(isSceneActive),
  };
}

function isSceneNotStale(scene: NavigationScene): boolean {
  return !scene.isStale;
}

function isSceneActive(scene: NavigationScene): boolean {
  return scene.isActive;
}

const styles = StyleSheet.create({
  main: {
    flex: 1,
  },
});

module.exports = NavigationTransitioner;
<commit_msg>do not reset layout state if they are the same in NavigationTransitioner<commit_after>/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule NavigationTransitioner
 * @flow
 */
'use strict';

const Animated = require('Animated');
const Easing = require('Easing');
const NavigationPropTypes = require('NavigationPropTypes');
const NavigationScenesReducer = require('NavigationScenesReducer');
const React = require('React');
const StyleSheet = require('StyleSheet');
const View = require('View');

import type {
  NavigationAnimatedValue,
  NavigationLayout,
  NavigationScene,
  NavigationState,
  NavigationTransitionProps,
  NavigationTransitionSpec,
} from 'NavigationTypeDefinition';

type Props = {
  configureTransition: (
    a: NavigationTransitionProps,
    b: ?NavigationTransitionProps,
  ) => NavigationTransitionSpec,
  navigationState: NavigationState,
  onTransitionEnd: () => void,
  onTransitionStart: () => void,
  render: (a: NavigationTransitionProps, b: ?NavigationTransitionProps) => any,
  style: any,
};

type State = {
  layout: NavigationLayout,
  position: NavigationAnimatedValue,
  progress: NavigationAnimatedValue,
  scenes: Array<NavigationScene>,
};

const {PropTypes} = React;

const DefaultTransitionSpec = {
  duration: 250,
  easing: Easing.inOut(Easing.ease),
  timing: Animated.timing,
};

class NavigationTransitioner extends React.Component<any, Props, State> {
  _onLayout: (event: any) => void;
  _onTransitionEnd: () => void;
  _prevTransitionProps: ?NavigationTransitionProps;
  _transitionProps: NavigationTransitionProps;

  props: Props;
  state: State;

  static propTypes = {
    configureTransition: PropTypes.func,
    navigationState: NavigationPropTypes.navigationState.isRequired,
    onTransitionEnd: PropTypes.func,
    onTransitionStart: PropTypes.func,
    render: PropTypes.func.isRequired,
  };

  constructor(props: Props, context: any) {
    super(props, context);

    // The initial layout isn't measured. Measured layout will be only available
    // when the component is mounted.
    const layout = {
      height: new Animated.Value(0),
      initHeight: 0,
      initWidth: 0,
      isMeasured: false,
      width: new Animated.Value(0),
    };

    this.state = {
      layout,
      position: new Animated.Value(this.props.navigationState.index),
      progress: new Animated.Value(1),
      scenes: NavigationScenesReducer([], this.props.navigationState),
    };

    this._prevTransitionProps = null;
    this._transitionProps = buildTransitionProps(props, this.state);
  }

  componentWillMount(): void {
    this._onLayout = this._onLayout.bind(this);
    this._onTransitionEnd = this._onTransitionEnd.bind(this);
  }

  componentWillReceiveProps(nextProps: Props): void {
    const nextScenes = NavigationScenesReducer(
      this.state.scenes,
      nextProps.navigationState,
      this.props.navigationState
    );

    if (nextScenes === this.state.scenes) {
      return;
    }

    const nextState = {
      ...this.state,
      scenes: nextScenes,
    };

    this._prevTransitionProps = this._transitionProps;
    this._transitionProps = buildTransitionProps(nextProps, nextState);

    const {
      position,
      progress,
    } = nextState;

    // get the transition spec.
    const transitionUserSpec = nextProps.configureTransition ?
      nextProps.configureTransition(
        this._transitionProps,
        this._prevTransitionProps,
      ) :
      null;

    const transitionSpec = {
      ...DefaultTransitionSpec,
      ...transitionUserSpec,
    };

    const {timing} = transitionSpec;
    delete transitionSpec.timing;

    progress.setValue(0);

    const animations = [
      timing(
        progress,
        {
          ...transitionSpec,
          toValue: 1,
        },
      ),
    ];

    if (nextProps.navigationState.index !== this.props.navigationState.index) {
      animations.push(
        timing(
          position,
          {
            ...transitionSpec,
            toValue: nextProps.navigationState.index,
          },
        ),
      );
    }

    // update scenes and play the transition
    this.setState(nextState, () => {
      nextProps.onTransitionStart && nextProps.onTransitionStart(
        this._transitionProps,
        this._prevTransitionProps,
      );
      Animated.parallel(animations).start(this._onTransitionEnd);
    });
  }

  render(): ReactElement<any> {
    return (
      <View
        onLayout={this._onLayout}
        style={[styles.main, this.props.style]}>
        {this.props.render(this._transitionProps, this._prevTransitionProps)}
      </View>
    );
  }

  _onLayout(event: any): void {
    const {height, width} = event.nativeEvent.layout;
    if (this.state.layout.initWidth === width &&
      this.state.layout.initHeight === height) {
      return;
    }
    const layout = {
      ...this.state.layout,
      initHeight: height,
      initWidth: width,
      isMeasured: true,
    };

    layout.height.setValue(height);
    layout.width.setValue(width);

    const nextState = {
      ...this.state,
      layout,
    };

    this._transitionProps = buildTransitionProps(this.props, nextState);
    this.setState(nextState);
  }

  _onTransitionEnd(): void {
    const prevTransitionProps = this._prevTransitionProps;
    this._prevTransitionProps = null;

    const nextState = {
      ...this.state,
      scenes: this.state.scenes.filter(isSceneNotStale),
    };

    this._transitionProps = buildTransitionProps(this.props, nextState);

    this.setState(nextState, () => {
      this.props.onTransitionEnd && this.props.onTransitionEnd(
        this._transitionProps,
        prevTransitionProps,
      );
    });
  }
}

function buildTransitionProps(
  props: Props,
  state: State,
): NavigationTransitionProps {
  const {
    navigationState,
  } = props;

  const {
    layout,
    position,
    progress,
    scenes,
  } = state;

  return {
    layout,
    navigationState,
    position,
    progress,
    scenes,
    scene: scenes.find(isSceneActive),
  };
}

function isSceneNotStale(scene: NavigationScene): boolean {
  return !scene.isStale;
}

function isSceneActive(scene: NavigationScene): boolean {
  return scene.isActive;
}

const styles = StyleSheet.create({
  main: {
    flex: 1,
  },
});

module.exports = NavigationTransitioner;
<|endoftext|>


<commit_before>var grid_demo = {
  // static attributes
  domGrid: null,
  domContainer: null,
  domTarget: null,
  columns: [],
  rows: [],
  cellWidth: 0,
  cellHeight: 0,
  numX: 10,
  numY: 13,
  minX: 0,
  maxX: 0,
  minY: 0,
  maxY: 0,
  numBoxes: 0,

  /**
   * Get coordinates that reside within the grid
   * -
   *
   * @param double coord
   * @param boolean isX
   *
   * @return double
   */
  get_valid_midpoint: function(coord, isX){
    var min = isX ? this.minX : this.minY;
    var max = isX ? this.maxX : this.maxY;
    var values = isX ? this.columns : this.rows;
    if(coord > max)
      var valid =  max;
    else if(coord < min)
      var valid =  min;
    else
      var valid = coord;
    return values[this.get_nearest_coord(valid, isX)];
  },

  /**
   * Handle mouse down events
   *
   * @param event
   */
  start_new_box: function(event){
    event.preventDefault();

    // increment box count (mainly for color styling)
    grid_demo.numBoxes++;
    var colorClass = 'bg_' + (grid_demo.numBoxes % 4);

    var click_x = grid_demo.get_valid_midpoint(event.pageX, true);
    var click_y = grid_demo.get_valid_midpoint(event.pageY, false);

    var domNew = $('

')
      .addClass('layout_item')
      .addClass(colorClass)
      .css({
        'top': grid_demo.get_edge_from_midpoint(click_y, false, true),
        'left': grid_demo.get_edge_from_midpoint(click_x, true, true),
        'width': grid_demo.cellWidth,
        'height': grid_demo.cellHeight,
      });
    domNew.appendTo(grid_demo.domContainer);

    /**
     * Attach mousemove listener for the duration of the drag event
     */
    grid_demo.domContainer.on('mousemove', function(event){
      var move_x = grid_demo.get_valid_midpoint(event.pageX, true);
      var move_y = grid_demo.get_valid_midpoint(event.pageY, false);

      if(move_x < click_x){
        var neX = move_x;
        var swX = click_x;
      }
      else{
        var neX = click_x;
        var swX = move_x;
      }
      if(move_y < click_y){
        var neY = move_y;
        var swY = click_y;
      }
      else{
        var neY = click_y;
        var swY = move_y;
      }

      // get the all corner coords point
      neX = grid_demo.get_edge_from_midpoint(neX, true, true);
      neY = grid_demo.get_edge_from_midpoint(neY, false, true);
      swX = grid_demo.get_edge_from_midpoint(swX, true, false);
      swY = grid_demo.get_edge_from_midpoint(swY, false, false);

      // calc width and height from corners
      var width = swX - neX;
      var height = swY - neY;

      domNew.css({
        'width': width,
        'height': height,
        'top': neY,
        'left': neX,
      });
    });

    /**
     * Function finishes creation of the box and cleans up any
     * event listeners we no longer need
     */
    var finish_new_box = function(event){
      grid_demo.domContainer.off('mousemove');
      grid_demo.domContainer.off('mouseup');
      grid_demo.domContainer.off('mouseleave');

      // create the mirror box to display the code
      var domCodeBox = domNew.clone();
      var objPosition = domNew.position();
      var objGridPosition = grid_demo.domContainer.position();
      var objTargetPosition = grid_demo.domTarget.position();
      var offsetPadding = 16;

      domCodeBox.css({
        'top': objPosition.top - objGridPosition.top + objTargetPosition.top,
        'left': objPosition.left - objGridPosition.left + objTargetPosition.left,
        'width': domNew.width() - offsetPadding,
        'height': domNew.height() - offsetPadding
      }).removeClass('layout_item').addClass('layout_code');
      domCodeBox.appendTo(grid_demo.domTarget);

      // get the code text itself
      var code = grid_demo.get_rmq_code(domNew);
      domCodeBox.text(code).attr('title', code);
      domCodeBox.click({frame: code, sister: domNew}, attempt_delete);
      //domCodeBox.on('click', attempt_delete)
    };

    var attempt_delete = function(event){
      $( "#dialog-confirm" ).dialog({
      resizable: false,
      height:260,
      modal: true,
      buttons: {
        "Yes": function() {
        $(event.target).fadeOut();
        $(event.data.sister).fadeOut();
        $( this ).dialog( "close" );
        },
        Cancel: function() {
        $( this ).dialog( "close" );
        }
      }
      }).html("
Are you sure you would like to remove " + event.data.frame + "?
");
    };

    /**
     * Releasing mouse triggers completion of new box
     */
    grid_demo.domContainer.on('mouseup', finish_new_box);

    /**
     * If the mouse leaves the outer container, we will stop
     * creation of the box at the final coordinates
     */
    grid_demo.domContainer.on('mouseleave', finish_new_box);

  },

  /**
   * Generate rmq code for building a box on the grid
   *
   * @param domBox
   *
   * @return string
   */
  get_rmq_code: function(domBox){
    var start = this.get_nearest_midpoint(domBox, true);
    var end = this.get_nearest_midpoint(domBox, false);
    return 'st.frame = \'' + start + ':' + end + '\'';
  },

  /**
   * Get the coordinates of the nearest grid point
   *
   * @param domBox
   * @param isLeft
   *
   * @return string gridpoint
   */
  get_nearest_midpoint: function(domBox, isLeft){
    var position = domBox.position();
    var testpoint = {};
    if(isLeft){
      testpoint.x = position.left;
      testpoint.y = position.top;
    }
    else{
      testpoint.x = position.left + domBox.width();
      testpoint.y = position.top + domBox.height();
    }

    var column = this.num_to_alpha(this.get_nearest_coord(testpoint.x, true));
    var row = this.get_nearest_coord(testpoint.y, false);

    return column + '' + row;
  },

  /**
   * Function gets the nearest row or column to a point
   *
   * @param float coord
   * @param boolean isX
   *
   * @return int
   */
  get_nearest_coord: function(coord, isX){
    var midpoints = isX ? this.columns : this.rows;
    var numUnit = isX ? this.numX : this.numY;
    var closest = 0;
    var distance = 9999;
    for(var i = 0; i < numUnit; i++){
      var temp = Math.abs(midpoints[i] - coord);
      if(temp < distance){
        closest = i;
        distance = temp;
      }
    }
    return closest;
  },

  /**
   * Given a midpoint, find the relevant grid box edge
   *
   * @param midpoint
   * @param isX
   * @param isNE
   */
  get_edge_from_midpoint: function(midpoint, isX, isNE){
    var dimension = isX ? this.cellWidth : this.cellHeight;

    // since we're in the midpoint, we only add/subtract half
    dimension = dimension / 2;

    return isNE ? midpoint - dimension : midpoint + dimension;
  },

  /**
   * Convert numeric column (starting at 0) to alpha column name
   * - only built for lowercase a-z right now
   *
   * @param int
   *
   * @return char
   */
  num_to_alpha: function(x){
     return String.fromCharCode(97 + x);
  },

  /**
   * Build the grid html
   *
   * @return domGrid
   */
  build_grid: function(){

    // table header
    var domTable = $('

').addClass('grid_builder');
    var domThead = $('

');
    var domHeadRow = $('

');

    // empty cell for the y axis labels
    $('	').appendTo(domHeadRow);

    // x axis labels
    for(var i = 0; i < this.numX; i++)
      $('	').addClass('grid_label').text(this.num_to_alpha(i))
        .appendTo(domHeadRow);
    domHeadRow.appendTo(domThead);
    domThead.appendTo(domTable);

    // table body
    var domTbody = $('

');
    for(var i = 0; i < this.numY; i++){
       var domRow = $('

');
       $('	').addClass('grid_label').text(i).appendTo(domRow);
       for(var j = 0; j< this.numX; j++)
         $('	').addClass('grid_cell')
           .text(this.num_to_alpha(j) + i)
           .appendTo(domRow);
       domRow.appendTo(domTbody);
    }
    domTbody.appendTo(domTable);

    domTable.appendTo(this.domContainer);
    return domTable;
  },

  /**
   * Function initializes the grid demo
   *
   * @param domContainer
   * @param domGrid
   */
  init_grid: function(domContainer, domGrid, domTargetContainer){
    this.domContainer = domContainer;
    this.domTarget = domTargetContainer;

    this.domGrid = this.build_grid(this.domContainer);

    // set grid size restrictions
    var domFirstCell = this.domGrid.find('.grid_cell').first();
    this.cellWidth = domFirstCell.outerWidth() -
      parseInt(domFirstCell.css('border-left-width')) / 2 -
      parseInt(domFirstCell.css('border-right-width')) / 2;
    this.cellHeight = domFirstCell.outerHeight() -
      parseInt(domFirstCell.css('border-top-width')) / 2 -
      parseInt(domFirstCell.css('border-bottom-width')) / 2;

    // these help handle calculations getting thrown off by collapsed borders
    var borderOffsetX = parseInt(domFirstCell.css('border-left-width')) / 2;
    var borderOffsetY = parseInt(domFirstCell.css('border-top-width')) / 2;

    // store grid midpoints to use in code creation
    var domFirstCell = this.domGrid.find('.grid_cell').first();
    var width = domFirstCell.outerWidth();
    var midX = width / 2;
    var offsetX = domFirstCell.position().left + borderOffsetX;;
    var height = domFirstCell.outerHeight();
    var offsetY = domFirstCell.position().top + borderOffsetY;
    var midY = domFirstCell.height() / 2;
    for(var i = 0; i < this.numX; i++)
      this.columns.push(width * i + midX + offsetX - borderOffsetX);
    for(var i = 0; i < this.numY; i++)
      this.rows.push(height * i + midY + offsetY + borderOffsetY);

    // min / max values for easy access
    this.minX = this.columns[0];
    this.minY = this.rows[0];
    this.maxX = this.columns[this.columns.length-1];
    this.maxY = this.rows[this.rows.length-1];

    // start listeners
    this.domContainer.mousedown(this.start_new_box);
  }
}

$(document).ready(function(){
  grid_demo.init_grid($('#demo_grid'), $('.grid_builder').first(), $('#demo_code'));
  $(document).tooltip();
});

<commit_msg>Add double click event listener to code boxes<commit_after>var grid_demo = {
  // static attributes
  domGrid: null,
  domContainer: null,
  domTarget: null,
  columns: [],
  rows: [],
  cellWidth: 0,
  cellHeight: 0,
  numX: 10,
  numY: 13,
  minX: 0,
  maxX: 0,
  minY: 0,
  maxY: 0,
  numBoxes: 0,

  /**
   * Get coordinates that reside within the grid
   * -
   *
   * @param double coord
   * @param boolean isX
   *
   * @return double
   */
  get_valid_midpoint: function(coord, isX){
    var min = isX ? this.minX : this.minY;
    var max = isX ? this.maxX : this.maxY;
    var values = isX ? this.columns : this.rows;
    if(coord > max)
      var valid =  max;
    else if(coord < min)
      var valid =  min;
    else
      var valid = coord;
    return values[this.get_nearest_coord(valid, isX)];
  },

  /**
   * Handle mouse down events
   *
   * @param event
   */
  start_new_box: function(event){
    event.preventDefault();

    // increment box count (mainly for color styling)
    grid_demo.numBoxes++;
    var colorClass = 'bg_' + (grid_demo.numBoxes % 4);

    var click_x = grid_demo.get_valid_midpoint(event.pageX, true);
    var click_y = grid_demo.get_valid_midpoint(event.pageY, false);

    var domNew = $('

')
      .addClass('layout_item')
      .addClass(colorClass)
      .css({
        'top': grid_demo.get_edge_from_midpoint(click_y, false, true),
        'left': grid_demo.get_edge_from_midpoint(click_x, true, true),
        'width': grid_demo.cellWidth,
        'height': grid_demo.cellHeight,
      });
    domNew.appendTo(grid_demo.domContainer);

    /**
     * Attach mousemove listener for the duration of the drag event
     */
    grid_demo.domContainer.on('mousemove', function(event){
      var move_x = grid_demo.get_valid_midpoint(event.pageX, true);
      var move_y = grid_demo.get_valid_midpoint(event.pageY, false);

      if(move_x < click_x){
        var neX = move_x;
        var swX = click_x;
      }
      else{
        var neX = click_x;
        var swX = move_x;
      }
      if(move_y < click_y){
        var neY = move_y;
        var swY = click_y;
      }
      else{
        var neY = click_y;
        var swY = move_y;
      }

      // get the all corner coords point
      neX = grid_demo.get_edge_from_midpoint(neX, true, true);
      neY = grid_demo.get_edge_from_midpoint(neY, false, true);
      swX = grid_demo.get_edge_from_midpoint(swX, true, false);
      swY = grid_demo.get_edge_from_midpoint(swY, false, false);

      // calc width and height from corners
      var width = swX - neX;
      var height = swY - neY;

      domNew.css({
        'width': width,
        'height': height,
        'top': neY,
        'left': neX,
      });
    });

    /**
     * Function finishes creation of the box and cleans up any
     * event listeners we no longer need
     */
    var finish_new_box = function(event){
      grid_demo.domContainer.off('mousemove');
      grid_demo.domContainer.off('mouseup');
      grid_demo.domContainer.off('mouseleave');

      // create the mirror box to display the code
      var domCodeBox = domNew.clone();
      var objPosition = domNew.position();
      var objGridPosition = grid_demo.domContainer.position();
      var objTargetPosition = grid_demo.domTarget.position();
      var offsetPadding = 16;

      domCodeBox.css({
        'top': objPosition.top - objGridPosition.top + objTargetPosition.top,
        'left': objPosition.left - objGridPosition.left + objTargetPosition.left,
        'width': domNew.width() - offsetPadding,
        'height': domNew.height() - offsetPadding
      }).removeClass('layout_item').addClass('layout_code');
      domCodeBox.appendTo(grid_demo.domTarget);

      // get the code text itself
      var code = grid_demo.get_rmq_code(domNew);
      domCodeBox.text(code).attr('title', code);

      // clicked and timer are here to handle double click events
      domCodeBox.data({'clicked': false, 'timer': null, 'sister': domNew});
      domCodeBox.on('click', dispatch_codebox_clicks);
    };

    /**
     * Monitor clicks on a codebox and dispatch separate click / dblclick events
     */
    var dispatch_codebox_clicks = function(){
      var element = $(this);
      var isClicked = element.data('clicked');

      // double click successful
      if(isClicked){
        // clear out double click logic
        clearTimeout(element.data('timer'));
        element.data({'timer': null, 'clicked': false});

        console.log('double clicked');
        //delete_box(element);
      }

      // single click
      else{
        element.data({
          'clicked':  true,
          'timer': setTimeout(function(){
            element.data('clicked', false);
            console.log('single clicked');
          }, 250)
        });
      }
      return false;
    }

    /**
     * Releasing mouse triggers completion of new box
     */
    grid_demo.domContainer.on('mouseup', finish_new_box);

    /**
     * If the mouse leaves the outer container, we will stop
     * creation of the box at the final coordinates
     */
    grid_demo.domContainer.on('mouseleave', finish_new_box);

  },

  /**
   * Generate rmq code for building a box on the grid
   *
   * @param domBox
   *
   * @return string
   */
  get_rmq_code: function(domBox){
    var start = this.get_nearest_midpoint(domBox, true);
    var end = this.get_nearest_midpoint(domBox, false);
    return 'st.frame = \'' + start + ':' + end + '\'';
  },

  /**
   * Get the coordinates of the nearest grid point
   *
   * @param domBox
   * @param isLeft
   *
   * @return string gridpoint
   */
  get_nearest_midpoint: function(domBox, isLeft){
    var position = domBox.position();
    var testpoint = {};
    if(isLeft){
      testpoint.x = position.left;
      testpoint.y = position.top;
    }
    else{
      testpoint.x = position.left + domBox.width();
      testpoint.y = position.top + domBox.height();
    }

    var column = this.num_to_alpha(this.get_nearest_coord(testpoint.x, true));
    var row = this.get_nearest_coord(testpoint.y, false);

    return column + '' + row;
  },

  /**
   * Function gets the nearest row or column to a point
   *
   * @param float coord
   * @param boolean isX
   *
   * @return int
   */
  get_nearest_coord: function(coord, isX){
    var midpoints = isX ? this.columns : this.rows;
    var numUnit = isX ? this.numX : this.numY;
    var closest = 0;
    var distance = 9999;
    for(var i = 0; i < numUnit; i++){
      var temp = Math.abs(midpoints[i] - coord);
      if(temp < distance){
        closest = i;
        distance = temp;
      }
    }
    return closest;
  },

  /**
   * Given a midpoint, find the relevant grid box edge
   *
   * @param midpoint
   * @param isX
   * @param isNE
   */
  get_edge_from_midpoint: function(midpoint, isX, isNE){
    var dimension = isX ? this.cellWidth : this.cellHeight;

    // since we're in the midpoint, we only add/subtract half
    dimension = dimension / 2;

    return isNE ? midpoint - dimension : midpoint + dimension;
  },

  /**
   * Convert numeric column (starting at 0) to alpha column name
   * - only built for lowercase a-z right now
   *
   * @param int
   *
   * @return char
   */
  num_to_alpha: function(x){
     return String.fromCharCode(97 + x);
  },

  /**
   * Build the grid html
   *
   * @return domGrid
   */
  build_grid: function(){

    // table header
    var domTable = $('

').addClass('grid_builder');
    var domThead = $('

');
    var domHeadRow = $('

');

    // empty cell for the y axis labels
    $('	').appendTo(domHeadRow);

    // x axis labels
    for(var i = 0; i < this.numX; i++)
      $('	').addClass('grid_label').text(this.num_to_alpha(i))
        .appendTo(domHeadRow);
    domHeadRow.appendTo(domThead);
    domThead.appendTo(domTable);

    // table body
    var domTbody = $('

');
    for(var i = 0; i < this.numY; i++){
       var domRow = $('

');
       $('	').addClass('grid_label').text(i).appendTo(domRow);
       for(var j = 0; j< this.numX; j++)
         $('	').addClass('grid_cell')
           .text(this.num_to_alpha(j) + i)
           .appendTo(domRow);
       domRow.appendTo(domTbody);
    }
    domTbody.appendTo(domTable);

    domTable.appendTo(this.domContainer);
    return domTable;
  },

  /**
   * Function initializes the grid demo
   *
   * @param domContainer
   * @param domGrid
   */
  init_grid: function(domContainer, domGrid, domTargetContainer){
    this.domContainer = domContainer;
    this.domTarget = domTargetContainer;

    this.domGrid = this.build_grid(this.domContainer);

    // set grid size restrictions
    var domFirstCell = this.domGrid.find('.grid_cell').first();
    this.cellWidth = domFirstCell.outerWidth() -
      parseInt(domFirstCell.css('border-left-width')) / 2 -
      parseInt(domFirstCell.css('border-right-width')) / 2;
    this.cellHeight = domFirstCell.outerHeight() -
      parseInt(domFirstCell.css('border-top-width')) / 2 -
      parseInt(domFirstCell.css('border-bottom-width')) / 2;

    // these help handle calculations getting thrown off by collapsed borders
    var borderOffsetX = parseInt(domFirstCell.css('border-left-width')) / 2;
    var borderOffsetY = parseInt(domFirstCell.css('border-top-width')) / 2;

    // store grid midpoints to use in code creation
    var domFirstCell = this.domGrid.find('.grid_cell').first();
    var width = domFirstCell.outerWidth();
    var midX = width / 2;
    var offsetX = domFirstCell.position().left + borderOffsetX;;
    var height = domFirstCell.outerHeight();
    var offsetY = domFirstCell.position().top + borderOffsetY;
    var midY = domFirstCell.height() / 2;
    for(var i = 0; i < this.numX; i++)
      this.columns.push(width * i + midX + offsetX - borderOffsetX);
    for(var i = 0; i < this.numY; i++)
      this.rows.push(height * i + midY + offsetY + borderOffsetY);

    // min / max values for easy access
    this.minX = this.columns[0];
    this.minY = this.rows[0];
    this.maxX = this.columns[this.columns.length-1];
    this.maxY = this.rows[this.rows.length-1];

    // start listeners
    this.domContainer.mousedown(this.start_new_box);
  }
}

$(document).ready(function(){
  grid_demo.init_grid($('#demo_grid'), $('.grid_builder').first(), $('#demo_code'));
  $(document).tooltip();
});

<|endoftext|>


<commit_before>//////////////////////////////////////
// Exports

var pluginInterface;

exports.init = function() {
	pluginInterface = this ;

	EL.init((err) => {
		if(err) { // An error was occurred
			pluginInterface.log('Init error:'+JSON.stringify(err)) ;
		} else {
			pluginInterface.log('Start discovery.') ;

			EL.startDiscovery((err, res) => {
				if(err) { // Error handling
					console.log(err);
				} else {
					onELDeviceFound(res) ;
				}
			});

			EL.on('data', (res) => {
				if( res.message.esv !== 'INF' )	return ;

				registerELDevice(res.device.address , res.message.seoj).then(function(re){
					var dev = re.result ;
					if( !dev || dev.doc == null ) return ;
					var epcs = dev.doc.epc ;
					for( var propertyName in epcs ){
						if( epcs[propertyName].value != res.message.prop[0].epc ) continue ;
						var pv ;
						if( res.message.prop[0].buffer == null ) pv = null ;
						else {
							pv = [] ;
							var i8a = new Int8Array(res.message.prop[0].buffer) ;
							for( var ii=0 ; ii<i8a.length ; ++ii )	pv.push(i8a[ii]) ;
						}

						pluginInterface.publish( dev.deviceType+'.'+propertyName,[dev.uuid]
							,{propertyName:propertyName,propertyValue:pv} ) ;
					}
				}) ;
			});
		}
	});
};

// When unloaded
//	if( EL == undefined ) return ;
//	EL.stopDiscovery();

//////////////////////////////////////
// ECHONET Lite Setup

var EchonetLite = require('node-echonet-lite'); // https://www.npmjs.com/package/node-echonet-lite
var EL  = new EchonetLite({'type':'lan'}) ;

var EOJs = {};
var objDocs = {} ; // eoj_hex => objDoc map

function getEOJHexFromEOJArray( eojArray ){
	var ret = eojArray[1].toString(16); // Class code
	while( ret.length<2 ) ret = '0' + ret ;
	ret = eojArray[0].toString(16)+ret; // Class group code
	while( ret.length<4 ) ret = '0' + ret ;
	return ret.toUpperCase() ;
}

var addrToMakerCodeColonUUIDMap = {} ;

function getObjKey(address,eoj_hex){
	var ehl = eoj_hex.toLowerCase() ;
	return new Promise((rslv,rjct) => {
		// まだUUIDを問い合わせたことがない、新しいノードかもしれない
		if( addrToMakerCodeColonUUIDMap[address] == undefined ){
			EL.getPropertyValue( address , [0x0e,0xf0,0x01] , 0x83 , (err,res) => {
				var i8a = new Int8Array(res.message.prop[0].buffer ) ;
				i8a = Array.prototype.map.call(i8a, (en) => (en<0?256+en:en) ) ;

				var uuid = 'el'+(i8a[1]*0xffff+i8a[2]*0xff+i8a[3])+':0:' ;
				i8a.forEach((en)=>{uuid += ('0'+en.toString(16)).slice(-2);}) ;

				addrToMakerCodeColonUUIDMap[address] = uuid ;
				rslv( 'el'+ehl.substring(0,2)+':'+ehl.substring(2,4)+':'+addrToMakerCodeColonUUIDMap[address] ) ;
			}) ;

		} else rslv( 'el'+ehl.substring(0,2)+':'+ehl.substring(2,4)+':'+addrToMakerCodeColonUUIDMap[address] ) ;
	}) ;
}

function registerELDevice( address,eojArray ){

	return new Promise((acpt,rjct) => {

		var eoj_hex = getEOJHexFromEOJArray(eojArray) ;

		getObjKey(address,eoj_hex).then((objkey)=>{
			if( EOJs[objkey] !== undefined ){ acpt({success:true,result:EOJs[objkey]}); return ; } // previously found device

			pluginInterface.log('New ECHONET Object (EOJ) found:0x'+eoj_hex) ;

			var dev = {
				uuid:objkey
				,eoj:eojArray
				,deviceType:eoj_hex
				,description:'Unknown device'
				,nickname:'Unknown@' + address + '/0x' + eoj_hex
				,address:address
			} ;
			EOJs[objkey] = dev ;	// Temporal object is set.

			function onObjDocFound(objDoc){
				// Replace some properties.
				dev.deviceType = objDoc.deviceType ;
				dev.description = null ;
				dev.nickname = objDoc.deviceType + '@' + address + '/0x' + eoj_hex ;
				dev.doc = objDoc ;
			}
			function registerDevice(){
				if(pluginInterface != undefined){
					// Check additional properties
					var propToChk = [0x81,0x8b,0x8c,0x8d,0x8e] ;
					var description = {} ;
					function getAdditionalProperty(){
						if( propToChk.length == 0 ){
							// Device registration main
							dev.description = description ;
							pluginInterface.registerDevice( dev.uuid,dev.deviceType,dev.description,dev.nickname )
							.then((re) => {
								dev.deviceId = re.deviceId;
								//pluginInterface.log('Device '+eoj_hex+' ('+dev.deviceType+') registered.') ;
								acpt({success:true,result:dev}) ;
							} ).catch(function(err){
								acpt({success:false,reason:err}) ;
							}) ;
							return ;
						}
						var prop = propToChk.shift() ;
						var prop_hex = '0x'+('0'+prop.toString(16)).slice(-2).toUpperCase() ;
						EL.getPropertyValue(address,eojArray,prop,(err,res) => {
							for( key in res.message.data){
								description[key] = res.message.data[key] ;
							} ;
							if( err != null || res.message.prop[0].buffer == null ){	// Error
								description[prop_hex] = null ;
								getAdditionalProperty() ;
							} else {
								var i8a = new Int8Array(res.message.prop[0].buffer ) ;
								i8a = Array.prototype.map.call(i8a, (en) => (en<0?256+en:en) ) ;

								description[prop_hex] = i8a ;

								setTimeout(getAdditionalProperty,500) ;
							}
						}) ;
					}
					getAdditionalProperty() ;
				} else {
					rjct({success:false,result:dev,reason:'PluginInteface is undefined.'}) ;
				}
			}

			// Potential bug: What if second devices with same eoj_hex found before objDoc is read?
			if( objDocs[eoj_hex] === null ){		// not found
				registerDevice() ;
				return ;
			} else if( objDocs[eoj_hex] !== undefined ){	// already exists. No need to register procedures.
				onObjDocFound( objDocs[eoj_hex] ) ;
				registerDevice() ;
			} else {					// search from now
				objDocs[eoj_hex] = null ;
				setEOJDocs(eoj_hex, function(objDoc) {
					if (objDoc == null) {
						console.log('Document for ' + eoj_hex + ' not found.');
						return;
					}

					function genProcResult(bSet,uuidArray,argObj){
						return new Promise(function(acept2,rjct2){
							if( uuidArray.length == 0 ){
								rjct2('UUID array cannot be empty') ;
							} else {
								if( uuidArray.length>1 )
									console.log('two or more uuids cannot be accepted') ;

								//for( var objkey in EOJs ){
								//	var dev = EOJs[objkey] ;
								//	if( dev.deviceId != devid ) continue ;
									var dev = EOJs[uuidArray[0]] ;
									var args = [
										dev.address,dev.eoj,dev.doc.epc[argObj.propertyName].value
										,(err,res)=>{
											if( err )	rjct2(res) ;
											else {
												var pv ;
												if( res.message.prop[0].buffer == null ) pv = argObj.propertyValue ;
												else {
													pv = [] ;
													var i8a = new Int8Array(res.message.prop[0].buffer) ;
													for( var ii=0 ; ii<i8a.length ; ++ii )	pv.push(i8a[ii]) ;
												}

												acept2({
													propertyName:argObj.propertyName
													,propertyValue:pv
													,digest:res.message.prop[0].edt
												}) ;
											}
										}
									] ;
									if( bSet )	EL.setPropertyValue(args[0],args[1],args[2],(new Buffer(argObj.propertyValue)),args[3]) ;
									else		EL.getPropertyValue(args[0],args[1],args[2],args[3]) ;
									return ;
								//} ;
								rjct2( 'No device found for deviceId='+dev.deviceId ) ;
							}
						}) ;
					}

					var procs = [
						{
							name:objDoc.deviceType + '.set'
							,procedure: (uuidArray,argObj) => {
								return genProcResult(true,uuidArray,argObj) ;
							}
						}
						,{
							name:objDoc.deviceType + '.get'
							,procedure: (uuidArray,argObj) => {
								return genProcResult(false,uuidArray,argObj) ;
							}
						}
					] ;
					pluginInterface.registerProcedures(procs) ;

					objDocs[eoj_hex] = objDoc ;
					onObjDocFound( objDoc ) ;
					registerDevice() ;
				}) ;
			}
		}) ;
	}) ;
}

function onELDeviceFound(res,callback){
	var device = res.device;
	var address = device.address

	// 最初の問い合わせで返答したもの（はじめてに決まっているのでUUIDを求めてから登録）
	EL.getPropertyValue( address , [0x0e,0xf0,0x01] , 0x83 , (err,res) => {
		var i8a = new Int8Array(res.message.prop[0].buffer ) ;
		i8a = Array.prototype.map.call(i8a, (en) => (en<0?256+en:en) ) ;

		var uuid = 'el'+(i8a[1]*0xffff+i8a[2]*0xff+i8a[3])+':0:' ;
		i8a.forEach((en)=>{uuid += ('0'+en.toString(16)).slice(-2);}) ;

		addrToMakerCodeColonUUIDMap[address] = uuid ;

		var ps = [] ;
		for( var ei=0;ei<device.eoj.length;++ei )
			ps.push(registerELDevice( address,device.eoj[ei] ) ) ;

		Promise.all(ps).then(callback).catch(callback);
	}) ;
}

var fs = require('fs');

function setEOJDocs(eoj_hex, onget_cb) {
  var csvpath = 'v1/plugins/'+pluginInterface.pluginPrefix+'/db/0x' + eoj_hex + '.csv';
  fs.stat(csvpath, function(err, stat) {
    if (err == null) {
      var dblines = fs.readFileSync(csvpath).toString().split(/\r\n|\r|\n/);
      dblines.shift();
      var objName = convSpaceSplittedPhraseIntoWikiName(dblines[0].substring(0, dblines[0].indexOf(',')));

      while (dblines[0].substring(0, 3) != 'EPC') {
        dblines.shift();
      }
      dblines.shift();

      var retobj = {
        protocol: 'echonetlite',
        deviceType: objName,
        epc: {}
      };

      function isMandatory(checkStr){
	if( checkStr == '-' )			return undefined ;
	else if( checkStr == 'mandatory' )	return true ;
	else if( checkStr == 'optional' )	return false ;
	return undefined ; // others
      }

      while (dblines.length > 0) {
        var l = dblines[0].trim();
        if (l.length == 0) {
          dblines.shift();
          continue;
        }
        var t = l.split(',');
        var epcName = convSpaceSplittedPhraseIntoWikiName(t[1]);
        retobj.epc[epcName] = {
		'value':parseInt(t[0])
		,'hexstr':t[0]
		,'set':isMandatory(t[t.length - 4])
		,'get':isMandatory(t[t.length - 3])
		,'announce':isMandatory(t[t.length - 2])
	};
        dblines.shift();
      }

      onget_cb(retobj);
    } else {
      console.log('File '+'0x'+eoj_hex+'.csv'+' does not exist..') ;
      onget_cb(null);
    }
  });
}

function convSpaceSplittedPhraseIntoWikiName(instr) {
  var ret = '';
  instr = instr.split(/["“”]/).join() ;
  instr = instr.split(/[\(\)]/).join(' ') ;
  instr.split(/[ \/-]/).forEach(function(wd) {
    if (wd === 'class') return;
    ret += wd.charAt(0).toUpperCase() + wd.substring(1);
  });
  return ret;
}
<commit_msg>ECHONET plugin comment removed<commit_after>//////////////////////////////////////
// Exports

var pluginInterface;

exports.init = function() {
	pluginInterface = this ;

	EL.init((err) => {
		if(err) { // An error was occurred
			pluginInterface.log('Init error:'+JSON.stringify(err)) ;
		} else {
			pluginInterface.log('Start discovery.') ;

			EL.startDiscovery((err, res) => {
				if(err) { // Error handling
					console.log(err);
				} else {
					onELDeviceFound(res) ;
				}
			});

			EL.on('data', (res) => {
				if( res.message.esv !== 'INF' )	return ;

				registerELDevice(res.device.address , res.message.seoj).then(function(re){
					var dev = re.result ;
					if( !dev || dev.doc == null ) return ;
					var epcs = dev.doc.epc ;
					for( var propertyName in epcs ){
						if( epcs[propertyName].value != res.message.prop[0].epc ) continue ;
						var pv ;
						if( res.message.prop[0].buffer == null ) pv = null ;
						else {
							pv = [] ;
							var i8a = new Int8Array(res.message.prop[0].buffer) ;
							for( var ii=0 ; ii<i8a.length ; ++ii )	pv.push(i8a[ii]) ;
						}

						pluginInterface.publish( dev.deviceType+'.'+propertyName,[dev.uuid]
							,{propertyName:propertyName,propertyValue:pv} ) ;
					}
				}) ;
			});
		}
	});
};

// When unloaded
//	if( EL == undefined ) return ;
//	EL.stopDiscovery();

//////////////////////////////////////
// ECHONET Lite Setup

var EchonetLite = require('node-echonet-lite'); // https://www.npmjs.com/package/node-echonet-lite
var EL  = new EchonetLite({'type':'lan'}) ;

var EOJs = {};
var objDocs = {} ; // eoj_hex => objDoc map

function getEOJHexFromEOJArray( eojArray ){
	var ret = eojArray[1].toString(16); // Class code
	while( ret.length<2 ) ret = '0' + ret ;
	ret = eojArray[0].toString(16)+ret; // Class group code
	while( ret.length<4 ) ret = '0' + ret ;
	return ret.toUpperCase() ;
}

var addrToMakerCodeColonUUIDMap = {} ;

function getObjKey(address,eoj_hex){
	var ehl = eoj_hex.toLowerCase() ;
	return new Promise((rslv,rjct) => {
		// まだUUIDを問い合わせたことがない、新しいノードかもしれない
		if( addrToMakerCodeColonUUIDMap[address] == undefined ){
			EL.getPropertyValue( address , [0x0e,0xf0,0x01] , 0x83 , (err,res) => {
				var i8a = new Int8Array(res.message.prop[0].buffer ) ;
				i8a = Array.prototype.map.call(i8a, (en) => (en<0?256+en:en) ) ;

				var uuid = 'el'+(i8a[1]*0xffff+i8a[2]*0xff+i8a[3])+':0:' ;
				i8a.forEach((en)=>{uuid += ('0'+en.toString(16)).slice(-2);}) ;

				addrToMakerCodeColonUUIDMap[address] = uuid ;
				rslv( 'el'+ehl.substring(0,2)+':'+ehl.substring(2,4)+':'+addrToMakerCodeColonUUIDMap[address] ) ;
			}) ;

		} else rslv( 'el'+ehl.substring(0,2)+':'+ehl.substring(2,4)+':'+addrToMakerCodeColonUUIDMap[address] ) ;
	}) ;
}

function registerELDevice( address,eojArray ){

	return new Promise((acpt,rjct) => {

		var eoj_hex = getEOJHexFromEOJArray(eojArray) ;

		getObjKey(address,eoj_hex).then((objkey)=>{
			if( EOJs[objkey] !== undefined ){ acpt({success:true,result:EOJs[objkey]}); return ; } // previously found device

			pluginInterface.log('New ECHONET Object (EOJ) found:0x'+eoj_hex) ;

			var dev = {
				uuid:objkey
				,eoj:eojArray
				,deviceType:eoj_hex
				,description:'Unknown device'
				,nickname:'Unknown@' + address + '/0x' + eoj_hex
				,address:address
			} ;
			EOJs[objkey] = dev ;	// Temporal object is set.

			function onObjDocFound(objDoc){
				// Replace some properties.
				dev.deviceType = objDoc.deviceType ;
				dev.description = null ;
				dev.nickname = objDoc.deviceType + '@' + address + '/0x' + eoj_hex ;
				dev.doc = objDoc ;
			}
			function registerDevice(){
				if(pluginInterface != undefined){
					// Check additional properties
					var propToChk = [0x81,0x8b,0x8c,0x8d,0x8e] ;
					var description = {} ;
					function getAdditionalProperty(){
						if( propToChk.length == 0 ){
							// Device registration main
							dev.description = description ;
							pluginInterface.registerDevice( dev.uuid,dev.deviceType,dev.description,dev.nickname )
							.then((re) => {
								dev.deviceId = re.deviceId;
								//pluginInterface.log('Device '+eoj_hex+' ('+dev.deviceType+') registered.') ;
								acpt({success:true,result:dev}) ;
							} ).catch(function(err){
								acpt({success:false,reason:err}) ;
							}) ;
							return ;
						}
						var prop = propToChk.shift() ;
						var prop_hex = '0x'+('0'+prop.toString(16)).slice(-2).toUpperCase() ;
						EL.getPropertyValue(address,eojArray,prop,(err,res) => {
							for( key in res.message.data){
								description[key] = res.message.data[key] ;
							} ;
							if( err != null || res.message.prop[0].buffer == null ){	// Error
								description[prop_hex] = null ;
								getAdditionalProperty() ;
							} else {
								var i8a = new Int8Array(res.message.prop[0].buffer ) ;
								i8a = Array.prototype.map.call(i8a, (en) => (en<0?256+en:en) ) ;

								description[prop_hex] = i8a ;

								setTimeout(getAdditionalProperty,500) ;
							}
						}) ;
					}
					getAdditionalProperty() ;
				} else {
					rjct({success:false,result:dev,reason:'PluginInteface is undefined.'}) ;
				}
			}

			// Potential bug: What if second devices with same eoj_hex found before objDoc is read?
			if( objDocs[eoj_hex] === null ){		// not found
				registerDevice() ;
				return ;
			} else if( objDocs[eoj_hex] !== undefined ){	// already exists. No need to register procedures.
				onObjDocFound( objDocs[eoj_hex] ) ;
				registerDevice() ;
			} else {					// search from now
				objDocs[eoj_hex] = null ;
				setEOJDocs(eoj_hex, function(objDoc) {
					if (objDoc == null) {
						console.log('Document for ' + eoj_hex + ' not found.');
						return;
					}

					function genProcResult(bSet,uuidArray,argObj){
						return new Promise(function(acept2,rjct2){

							if( uuidArray.length == 0 ){
								rjct2('UUID array cannot be empty') ;
							} else {
								if( uuidArray.length>1 )
									console.log('two or more uuids cannot be accepted') ;

								var dev = EOJs[uuidArray[0]] ;
								var args = [
									dev.address,dev.eoj,dev.doc.epc[argObj.propertyName].value
									,(err,res)=>{
										if( err )	rjct2(res) ;
										else {
											var pv ;
											if( res.message.prop[0].buffer == null ) pv = argObj.propertyValue ;
											else {
												pv = [] ;
												var i8a = new Int8Array(res.message.prop[0].buffer) ;
												for( var ii=0 ; ii<i8a.length ; ++ii )	pv.push(i8a[ii]) ;
											}

											acept2({
												propertyName:argObj.propertyName
												,propertyValue:pv
												,digest:res.message.prop[0].edt
											}) ;
										}
									}
								] ;

								//console.log('EL '+(bSet?'set':'get')+' call:'+args[0]+','+args[1]+','+args[2])

								if( bSet )	EL.setPropertyValue(args[0],args[1],args[2],(new Buffer(argObj.propertyValue)),args[3]) ;
								else		EL.getPropertyValue(args[0],args[1],args[2],args[3]) ;
							}
						}) ;
					}

					var procs = [
						{
							name:objDoc.deviceType + '.set'
							,procedure: (uuidArray,argObj) => {
								return genProcResult(true,uuidArray,argObj) ;
							}
						}
						,{
							name:objDoc.deviceType + '.get'
							,procedure: (uuidArray,argObj) => {
								return genProcResult(false,uuidArray,argObj) ;
							}
						}
					] ;
					pluginInterface.registerProcedures(procs) ;

					objDocs[eoj_hex] = objDoc ;
					onObjDocFound( objDoc ) ;
					registerDevice() ;
				}) ;
			}
		}) ;
	}) ;
}

function onELDeviceFound(res,callback){
	var device = res.device;
	var address = device.address

	// 最初の問い合わせで返答したもの（はじめてに決まっているのでUUIDを求めてから登録）
	EL.getPropertyValue( address , [0x0e,0xf0,0x01] , 0x83 , (err,res) => {
		var i8a = new Int8Array(res.message.prop[0].buffer ) ;
		i8a = Array.prototype.map.call(i8a, (en) => (en<0?256+en:en) ) ;

		var uuid = 'el'+(i8a[1]*0xffff+i8a[2]*0xff+i8a[3])+':0:' ;
		i8a.forEach((en)=>{uuid += ('0'+en.toString(16)).slice(-2);}) ;

		addrToMakerCodeColonUUIDMap[address] = uuid ;

		var ps = [] ;
		for( var ei=0;ei<device.eoj.length;++ei )
			ps.push(registerELDevice( address,device.eoj[ei] ) ) ;

		Promise.all(ps).then(callback).catch(callback);
	}) ;
}

var fs = require('fs');

function setEOJDocs(eoj_hex, onget_cb) {
  var csvpath = 'v1/plugins/'+pluginInterface.pluginPrefix+'/db/0x' + eoj_hex + '.csv';
  fs.stat(csvpath, function(err, stat) {
    if (err == null) {
      var dblines = fs.readFileSync(csvpath).toString().split(/\r\n|\r|\n/);
      dblines.shift();
      var objName = convSpaceSplittedPhraseIntoWikiName(dblines[0].substring(0, dblines[0].indexOf(',')));

      while (dblines[0].substring(0, 3) != 'EPC') {
        dblines.shift();
      }
      dblines.shift();

      var retobj = {
        protocol: 'echonetlite',
        deviceType: objName,
        epc: {}
      };

      function isMandatory(checkStr){
	if( checkStr == '-' )			return undefined ;
	else if( checkStr == 'mandatory' )	return true ;
	else if( checkStr == 'optional' )	return false ;
	return undefined ; // others
      }

      while (dblines.length > 0) {
        var l = dblines[0].trim();
        if (l.length == 0) {
          dblines.shift();
          continue;
        }
        var t = l.split(',');
        var epcName = convSpaceSplittedPhraseIntoWikiName(t[1]);
        retobj.epc[epcName] = {
		'value':parseInt(t[0])
		,'hexstr':t[0]
		,'set':isMandatory(t[t.length - 4])
		,'get':isMandatory(t[t.length - 3])
		,'announce':isMandatory(t[t.length - 2])
	};
        dblines.shift();
      }

      onget_cb(retobj);
    } else {
      console.log('File '+'0x'+eoj_hex+'.csv'+' does not exist..') ;
      onget_cb(null);
    }
  });
}

function convSpaceSplittedPhraseIntoWikiName(instr) {
  var ret = '';
  instr = instr.split(/["“”]/).join() ;
  instr = instr.split(/[\(\)]/).join(' ') ;
  instr.split(/[ \/-]/).forEach(function(wd) {
    if (wd === 'class') return;
    ret += wd.charAt(0).toUpperCase() + wd.substring(1);
  });
  return ret;
}
<|endoftext|>


<commit_before>(function () {
    var ui = glinamespace("gli.ui");
    var util = glinamespace("gli.util");
    var glc = glinamespace("gli.glConstants");

    var VertexArrayView = function (w, elementRoot) {
        var self = this;
        this.window = w;
        this.elements = {
            view: elementRoot.getElementsByClassName("window-right")[0],
            listing: elementRoot.getElementsByClassName("vertexarray-listing")[0]
        };

        this.currentVertexArray = null;
    };

    VertexArrayView.prototype.layout = function () {
    };

    function appendHistoryLine(gl, el, vertexArray, call) {
        gli.ui.appendHistoryLine(gl, el, call);

        // TODO: other custom stuff?
    }

    function generateVertexArrayHistory(gl, el, vertexArray, version) {
        var titleDiv = document.createElement("div");
        titleDiv.className = "info-title-secondary";
        titleDiv.textContent = "History";
        el.appendChild(titleDiv);

        var rootEl = document.createElement("div");
        rootEl.className = "vertexarray-history";
        el.appendChild(rootEl);

        for (var n = 0; n < version.calls.length; n++) {
            var call = version.calls[n];
            appendHistoryLine(gl, rootEl, vertexArray, call);
        }
    };

    function generateBufferValue(buffer) {
        if (buffer) {
            if (buffer.target) {
                return "[" + buffer.getName() + "]";
            }
            return "**unknown buffer";
        }
        return "null";
    }

    // Do I want the buffer at a certain state?
    function addBufferClickHandler(window, elem, buffer) {
      if (buffer && buffer.target) {
        elem.addEventListener('click', e => {
           window.showBuffer(buffer);
        });
        elem.className = elem.className + " vertexattrib-clickable";
      }
    }

    function generateVertexArrayDisplay(view, gl, el, vertexArray, version) {
        var titleDiv = document.createElement("div");
        titleDiv.className = "info-title-master";
        titleDiv.textContent = vertexArray.getName();
        el.appendChild(titleDiv);

        const table = document.createElement("table");
        table.className = "vertexarray-struct";

        let tr = document.createElement("tr");
        util.addSimpleElem(tr, "th", "index");
        util.addSimpleElem(tr, "th", "enabled");
        util.addSimpleElem(tr, "th", "size");
        util.addSimpleElem(tr, "th", "type");
        util.addSimpleElem(tr, "th", "normalized");
        util.addSimpleElem(tr, "th", "stride");
        util.addSimpleElem(tr, "th", "offset");
        util.addSimpleElem(tr, "th", "divisor");
        util.addSimpleElem(tr, "th", "buffer");
        util.addSimpleElem(tr, "th", "value");
        table.appendChild(tr);

        var defaultAttrib = {
            enabled: false,
            value: [0, 0, 0, 1],
            size: 4,
            type: gl.FLOAT,
            normalize: false,
            stride: 0,
            offset: 0,
            buffer: null,
            divisor: 0,
        };
        const attributes = version.extras.attributes;
        const maxAttributes = gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
        for (let n = 0; n < maxAttributes; ++n) {
          const attribute = attributes[n] || defaultAttrib;
          let tr = document.createElement("tr");
          tr.className = attribute.enabled ? "vertexattrib-enabled" : "vertexattrib-disabled";

          util.addSimpleElem(tr, "td", n, "vertexattrib-index");
          util.addSimpleElem(tr, "td", attribute.enabled ? "●" : "", "vertexattrib-enabled");
          util.addSimpleElem(tr, "td", attribute.size, "vertexattrib-size");
          util.addSimpleElem(tr, "td", glc.enumToString(attribute.type), "vertexattrib-type");
          util.addSimpleElem(tr, "td", attribute.normalized ? "true" : "false", "vertexattrib-normalized");
          util.addSimpleElem(tr, "td", attribute.stride, "vertexattrib-stride");
          util.addSimpleElem(tr, "td", attribute.offset, "vertexattrib-offset");
          util.addSimpleElem(tr, "td", attribute.divisor || defaultAttrib.divisor, "vertexattrib-divisor");
          const bufElem = util.addSimpleElem(tr, "td", generateBufferValue(attribute.buffer), "vertexattrib-buffer");
          addBufferClickHandler(view.window, bufElem, attribute.buffer);
          util.addSimpleElem(tr, "td", attribute.value || defaultAttrib.value, "vertexattrib-value");

          table.appendChild(tr);
        }

        el.appendChild(table);

        gli.ui.appendbr(el);
        gli.ui.appendSeparator(el);

        generateVertexArrayHistory(gl, el, vertexArray, version);
        gli.ui.appendbr(el);

        var frame = gl.ui.controller.currentFrame;
        if (frame) {
            gli.ui.appendSeparator(el);
            gli.ui.generateUsageList(gl, el, frame, vertexArray);
            gli.ui.appendbr(el);
        }

        gli.ui.appendSeparator(el);
        gli.ui.appendbr(el);
    }

    VertexArrayView.prototype.setVertexArray = function (vertexArray) {
        this.currentVertexArray = vertexArray;

        var node = this.elements.listing;
        while (node.hasChildNodes()) {
          node.removeChild(node.firstChild);
        }
        if (vertexArray) {
            var version;
            switch (this.window.activeVersion) {
                case null:
                    version = vertexArray.currentVersion;
                    break;
                case "current":
                    var frame = this.window.controller.currentFrame;
                    if (frame) {
                        version = frame.findResourceVersion(vertexArray);
                    }
                    version = version || vertexArray.currentVersion; // Fallback to live
                    break;
            }

            // Setup user preview options if not defined
            var lastDrawState = version.lastDrawState;
            if (!vertexArray.previewOptions && lastDrawState) {
                var elementArrayBufferArray = null;
                if (lastDrawState.elementArrayBuffer) {
                    elementArrayBufferArray = [lastDrawState.elementArrayBuffer, null];
                    // TODO: pick the right version of the ELEMENT_ARRAY_BUFFER
                    elementArrayBufferArray[1] = elementArrayBufferArray[0].currentVersion;
                }
            }

            generateVertexArrayDisplay(this, this.window.context, this.elements.listing, vertexArray, version);
        }

        this.elements.listing.scrollTop = 0;
    };

    ui.VertexArrayView = VertexArrayView;
})();
<commit_msg>make VertexArrayView use ui.appendElement and show ELEMENT_ARRAY_BUFFER<commit_after>(function () {
    var ui = glinamespace("gli.ui");
    var util = glinamespace("gli.util");
    var glc = glinamespace("gli.glConstants");

    var VertexArrayView = function (w, elementRoot) {
        var self = this;
        this.window = w;
        this.elements = {
            view: elementRoot.getElementsByClassName("window-right")[0],
            listing: elementRoot.getElementsByClassName("vertexarray-listing")[0]
        };

        this.currentVertexArray = null;
    };

    VertexArrayView.prototype.layout = function () {
    };

    function appendHistoryLine(gl, el, vertexArray, call) {
        gli.ui.appendHistoryLine(gl, el, call);

        // TODO: other custom stuff?
    }

    function generateVertexArrayHistory(gl, el, vertexArray, version) {
        var titleDiv = document.createElement("div");
        titleDiv.className = "info-title-secondary";
        titleDiv.textContent = "History";
        el.appendChild(titleDiv);

        var rootEl = document.createElement("div");
        rootEl.className = "vertexarray-history";
        el.appendChild(rootEl);

        for (var n = 0; n < version.calls.length; n++) {
            var call = version.calls[n];
            appendHistoryLine(gl, rootEl, vertexArray, call);
        }
    };

    function generateBufferValue(buffer) {
        if (buffer) {
            if (buffer.target) {
                return "[" + buffer.getName() + "]";
            }
            return "**unknown buffer";
        }
        return "null";
    }

    // Do I want the buffer at a certain state?
    function addBufferClickHandler(window, elem, buffer) {
      if (buffer && buffer.target) {
        elem.addEventListener('click', e => {
           window.showBuffer(buffer);
        });
        elem.className = elem.className + " vertexattrib-clickable";
      }
    }

    function generateVertexArrayDisplay(view, gl, el, vertexArray, version) {
        var titleDiv = document.createElement("div");
        titleDiv.className = "info-title-master";
        titleDiv.textContent = vertexArray.getName();
        el.appendChild(titleDiv);

        {
            const table = document.createElement("table");
            table.className = "info-parameters";

            const tr = document.createElement("tr");
            tr.className = "info-parameter-row";
            gli.ui.appendElement(tr, "td", "ELEMENT_ARRAY_BUFFER");
            const bufElem = gli.ui.appendElement(tr, "td", generateBufferValue(version.extras.elementBuffer.buffer), "vertexattrib-buffer");
            addBufferClickHandler(view.window, bufElem, version.extras.elementBuffer.buffer);

            table.appendChild(tr);
            el.appendChild(table);
        }

        gli.ui.appendbr(el);

        {
            const table = document.createElement("table");
            table.className = "vertexarray-struct";

            let tr = document.createElement("tr");
            gli.ui.appendElement(tr, "th", "index");
            gli.ui.appendElement(tr, "th", "enabled");
            gli.ui.appendElement(tr, "th", "size");
            gli.ui.appendElement(tr, "th", "type");
            gli.ui.appendElement(tr, "th", "normalized");
            gli.ui.appendElement(tr, "th", "stride");
            gli.ui.appendElement(tr, "th", "offset");
            gli.ui.appendElement(tr, "th", "divisor");
            gli.ui.appendElement(tr, "th", "buffer");
            gli.ui.appendElement(tr, "th", "value");
            table.appendChild(tr);

            const defaultAttrib = {
                enabled: false,
                value: [0, 0, 0, 1],
                size: 4,
                type: gl.FLOAT,
                normalize: false,
                stride: 0,
                offset: 0,
                buffer: null,
                divisor: 0,
            };
            const attributes = version.extras.attributes;
            const maxAttributes = gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
            for (let n = 0; n < maxAttributes; ++n) {
              const attribute = attributes[n] || defaultAttrib;
              let tr = document.createElement("tr");
              tr.className = attribute.enabled ? "vertexattrib-enabled" : "vertexattrib-disabled";

              gli.ui.appendElement(tr, "td", n, "vertexattrib-index");
              gli.ui.appendElement(tr, "td", attribute.enabled ? "●" : "", "vertexattrib-enabled");
              gli.ui.appendElement(tr, "td", attribute.size, "vertexattrib-size");
              gli.ui.appendElement(tr, "td", glc.enumToString(attribute.type), "vertexattrib-type");
              gli.ui.appendElement(tr, "td", attribute.normalized ? "true" : "false", "vertexattrib-normalized");
              gli.ui.appendElement(tr, "td", attribute.stride, "vertexattrib-stride");
              gli.ui.appendElement(tr, "td", attribute.offset, "vertexattrib-offset");
              gli.ui.appendElement(tr, "td", attribute.divisor || defaultAttrib.divisor, "vertexattrib-divisor");
              const bufElem = gli.ui.appendElement(tr, "td", generateBufferValue(attribute.buffer), "vertexattrib-buffer");
              addBufferClickHandler(view.window, bufElem, attribute.buffer);
              gli.ui.appendElement(tr, "td", attribute.value || defaultAttrib.value, "vertexattrib-value");

              table.appendChild(tr);
            }

            el.appendChild(table);
        }

        gli.ui.appendbr(el);
        gli.ui.appendSeparator(el);

        generateVertexArrayHistory(gl, el, vertexArray, version);
        gli.ui.appendbr(el);

        var frame = gl.ui.controller.currentFrame;
        if (frame) {
            gli.ui.appendSeparator(el);
            gli.ui.generateUsageList(gl, el, frame, vertexArray);
            gli.ui.appendbr(el);
        }

        gli.ui.appendSeparator(el);
        gli.ui.appendbr(el);
    }

    VertexArrayView.prototype.setVertexArray = function (vertexArray) {
        this.currentVertexArray = vertexArray;

        var node = this.elements.listing;
        while (node.hasChildNodes()) {
          node.removeChild(node.firstChild);
        }
        if (vertexArray) {
            var version;
            switch (this.window.activeVersion) {
                case null:
                    version = vertexArray.currentVersion;
                    break;
                case "current":
                    var frame = this.window.controller.currentFrame;
                    if (frame) {
                        version = frame.findResourceVersion(vertexArray);
                    }
                    version = version || vertexArray.currentVersion; // Fallback to live
                    break;
            }

            // Setup user preview options if not defined
            var lastDrawState = version.lastDrawState;
            if (!vertexArray.previewOptions && lastDrawState) {
                var elementArrayBufferArray = null;
                if (lastDrawState.elementArrayBuffer) {
                    elementArrayBufferArray = [lastDrawState.elementArrayBuffer, null];
                    // TODO: pick the right version of the ELEMENT_ARRAY_BUFFER
                    elementArrayBufferArray[1] = elementArrayBufferArray[0].currentVersion;
                }
            }

            generateVertexArrayDisplay(this, this.window.context, this.elements.listing, vertexArray, version);
        }

        this.elements.listing.scrollTop = 0;
    };

    ui.VertexArrayView = VertexArrayView;
})();
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * The examples provided by Facebook are for non-commercial testing and
 * evaluation purposes only.
 *
 * Facebook reserves all rights not expressly granted.
 *
 * 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 NON INFRINGEMENT. IN NO EVENT SHALL
 * FACEBOOK 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.  *
 *
 * @providesModule SwipeableRow
 * @flow
 */
'use strict';

const Animated = require('Animated');
const PanResponder = require('PanResponder');
const I18nManager = require('I18nManager');
const React = require('React');
const StyleSheet = require('StyleSheet');
const TimerMixin = require('react-timer-mixin');
const View = require('View');

const {PropTypes} = React;

const emptyFunction = require('fbjs/lib/emptyFunction');

const IS_RTL = I18nManager.isRTL;

// NOTE: Eventually convert these consts to an input object of configurations

// Position of the left of the swipable item when closed
const CLOSED_LEFT_POSITION = 0;
// Minimum swipe distance before we recognize it as such
const HORIZONTAL_SWIPE_DISTANCE_THRESHOLD = 10;
// Minimum swipe speed before we fully animate the user's action (open/close)
const HORIZONTAL_FULL_SWIPE_SPEED_THRESHOLD = 0.3;
// Factor to divide by to get slow speed; i.e. 4 means 1/4 of full speed
const SLOW_SPEED_SWIPE_FACTOR = 4;
// Time, in milliseconds, of how long the animated swipe should be
const SWIPE_DURATION = 300;

/**
 * On SwipeableListView mount, the 1st item will bounce to show users it's
 * possible to swipe
 */
const ON_MOUNT_BOUNCE_DELAY = 700;
const ON_MOUNT_BOUNCE_DURATION = 400;

// Distance left of closed position to bounce back when right-swiping from closed
const RIGHT_SWIPE_BOUNCE_BACK_DISTANCE = 30;
const RIGHT_SWIPE_BOUNCE_BACK_DURATION = 300;
/**
 * Max distance of right swipe to allow (right swipes do functionally nothing).
 * Must be multiplied by SLOW_SPEED_SWIPE_FACTOR because gestureState.dx tracks
 * how far the finger swipes, and not the actual animation distance.
*/
const RIGHT_SWIPE_THRESHOLD = 30 * SLOW_SPEED_SWIPE_FACTOR;

/**
 * Creates a swipable row that allows taps on the main item and a custom View
 * on the item hidden behind the row. Typically this should be used in
 * conjunction with SwipeableListView for additional functionality, but can be
 * used in a normal ListView. See the renderRow for SwipeableListView to see how
 * to use this component separately.
 */
const SwipeableRow = React.createClass({
  _panResponder: {},
  _previousLeft: CLOSED_LEFT_POSITION,

  mixins: [TimerMixin],

  propTypes: {
    children: PropTypes.any,
    isOpen: PropTypes.bool,
    maxSwipeDistance: PropTypes.number.isRequired,
    onOpen: PropTypes.func.isRequired,
    onSwipeEnd: PropTypes.func.isRequired,
    onSwipeStart: PropTypes.func.isRequired,
    // Should bounce the row on mount
    shouldBounceOnMount: PropTypes.bool,
    /**
     * A ReactElement that is unveiled when the user swipes
     */
    slideoutView: PropTypes.node.isRequired,
    /**
     * The minimum swipe distance required before fully animating the swipe. If
     * the user swipes less than this distance, the item will return to its
     * previous (open/close) position.
     */
    swipeThreshold: PropTypes.number.isRequired,
  },

  getInitialState(): Object {
    return {
      currentLeft: new Animated.Value(this._previousLeft),
      /**
       * In order to render component A beneath component B, A must be rendered
       * before B. However, this will cause "flickering", aka we see A briefly
       * then B. To counter this, _isSwipeableViewRendered flag is used to set
       * component A to be transparent until component B is loaded.
       */
      isSwipeableViewRendered: false,
      rowHeight: (null: ?number),
    };
  },

  getDefaultProps(): Object {
    return {
      isOpen: false,
      maxSwipeDistance: 0,
      onOpen: emptyFunction,
      onSwipeEnd: emptyFunction,
      onSwipeStart: emptyFunction,
      swipeThreshold: 30,
    };
  },

  componentWillMount(): void {
    this._panResponder = PanResponder.create({
      onMoveShouldSetPanResponderCapture: this._handleMoveShouldSetPanResponderCapture,
      onPanResponderGrant: this._handlePanResponderGrant,
      onPanResponderMove: this._handlePanResponderMove,
      onPanResponderRelease: this._handlePanResponderEnd,
      onPanResponderTerminationRequest: this._onPanResponderTerminationRequest,
      onPanResponderTerminate: this._handlePanResponderEnd,
    });
  },

  componentDidMount(): void {
    if (this.props.shouldBounceOnMount) {
      /**
       * Do the on mount bounce after a delay because if we animate when other
       * components are loading, the animation will be laggy
       */
      this.setTimeout(() => {
        this._animateBounceBack(ON_MOUNT_BOUNCE_DURATION);
      }, ON_MOUNT_BOUNCE_DELAY);
    }
  },

  componentWillReceiveProps(nextProps: Object): void {
    /**
     * We do not need an "animateOpen(noCallback)" because this animation is
     * handled internally by this component.
     */
    if (this.props.isOpen && !nextProps.isOpen) {
      this._animateToClosedPosition();
    }
  },

  shouldComponentUpdate(nextProps: Object, nextState: Object): boolean {
    if (this.props.shouldBounceOnMount && !nextProps.shouldBounceOnMount) {
      // No need to rerender if SwipeableListView is disabling the bounce flag
      return false;
    }

    return true;
  },

  render(): React.Element<any> {
    // The view hidden behind the main view
    let slideOutView;
    if (this.state.isSwipeableViewRendered) {
      slideOutView = (
        <View style={[
          styles.slideOutContainer,
          {height: this.state.rowHeight},
          ]}>
          {this.props.slideoutView}
        </View>
      );
    }

    // The swipeable item
    const swipeableView = (
      <Animated.View
        onLayout={this._onSwipeableViewLayout}
        style={[
          styles.swipeableContainer,
          {
            transform: [{translateX: this.state.currentLeft}],
          },
        ]}>
        {this.props.children}
      </Animated.View>
    );

    return (
      <View
        {...this._panResponder.panHandlers}>
        {slideOutView}
        {swipeableView}
      </View>
    );
  },

  _onSwipeableViewLayout(event: Object): void {
    this.setState({
      isSwipeableViewRendered: true,
      rowHeight: event.nativeEvent.layout.height,
    });
  },

  _handleMoveShouldSetPanResponderCapture(
    event: Object,
    gestureState: Object,
  ): boolean {
    // Decides whether a swipe is responded to by this component or its child
    return gestureState.dy < 10 && this._isValidSwipe(gestureState);
  },

  _handlePanResponderGrant(event: Object, gestureState: Object): void {

  },

  _handlePanResponderMove(event: Object, gestureState: Object): void {
    if (this._isSwipingExcessivelyRightFromClosedPosition(gestureState)) {
      return;
    }

    this.props.onSwipeStart();

    if (this._isSwipingRightFromClosed(gestureState)) {
      this._swipeSlowSpeed(gestureState);
    } else {
      this._swipeFullSpeed(gestureState);
    }
  },

  _isSwipingRightFromClosed(gestureState: Object): boolean {
    const gestureStateDx = IS_RTL ? -gestureState.dx : gestureState.dx;
    return this._previousLeft === CLOSED_LEFT_POSITION && gestureStateDx > 0;
  },

  _swipeFullSpeed(gestureState: Object): void {
    this.state.currentLeft.setValue(this._previousLeft + gestureState.dx);
  },

  _swipeSlowSpeed(gestureState: Object): void {
    this.state.currentLeft.setValue(
      this._previousLeft + gestureState.dx / SLOW_SPEED_SWIPE_FACTOR,
    );
  },

  _isSwipingExcessivelyRightFromClosedPosition(gestureState: Object): boolean {
    /**
     * We want to allow a BIT of right swipe, to allow users to know that
     * swiping is available, but swiping right does not do anything
     * functionally.
     */
    const gestureStateDx = IS_RTL ? -gestureState.dx : gestureState.dx;
    return (
      this._isSwipingRightFromClosed(gestureState) &&
      gestureStateDx > RIGHT_SWIPE_THRESHOLD
    );
  },

  _onPanResponderTerminationRequest(
    event: Object,
    gestureState: Object,
  ): boolean {
    return false;
  },

  _animateTo(
    toValue: number,
    duration: number = SWIPE_DURATION,
    callback: Function = emptyFunction,
  ): void {
    Animated.timing(
      this.state.currentLeft,
      {
        duration,
        toValue,
      },
    ).start(() => {
      this._previousLeft = toValue;
      callback();
    });
  },

  _animateToOpenPosition(): void {
    const maxSwipeDistance = IS_RTL ? -this.props.maxSwipeDistance : this.props.maxSwipeDistance;
    this._animateTo(-maxSwipeDistance);
  },

  _animateToOpenPositionWith(
    speed: number,
    distMoved: number,
  ): void {
    /**
     * Ensure the speed is at least the set speed threshold to prevent a slow
     * swiping animation
     */
    speed = (
      speed > HORIZONTAL_FULL_SWIPE_SPEED_THRESHOLD ?
      speed :
      HORIZONTAL_FULL_SWIPE_SPEED_THRESHOLD
    );
    /**
     * Calculate the duration the row should take to swipe the remaining distance
     * at the same speed the user swiped (or the speed threshold)
     */
    const duration = Math.abs((this.props.maxSwipeDistance - Math.abs(distMoved)) / speed);
    const maxSwipeDistance = IS_RTL ? -this.props.maxSwipeDistance : this.props.maxSwipeDistance;
    this._animateTo(-maxSwipeDistance, duration);
  },

  _animateToClosedPosition(duration: number = SWIPE_DURATION): void {
    this._animateTo(CLOSED_LEFT_POSITION, duration);
  },

  _animateToClosedPositionDuringBounce(): void {
    this._animateToClosedPosition(RIGHT_SWIPE_BOUNCE_BACK_DURATION);
  },

  _animateBounceBack(duration: number): void {
    /**
     * When swiping right, we want to bounce back past closed position on release
     * so users know they should swipe right to get content.
     */
    const swipeBounceBackDistance = IS_RTL ?
      -RIGHT_SWIPE_BOUNCE_BACK_DISTANCE :
      RIGHT_SWIPE_BOUNCE_BACK_DISTANCE;
    this._animateTo(
      -swipeBounceBackDistance,
      duration,
      this._animateToClosedPositionDuringBounce,
    );
  },

  // Ignore swipes due to user's finger moving slightly when tapping
  _isValidSwipe(gestureState: Object): boolean {
    return Math.abs(gestureState.dx) > HORIZONTAL_SWIPE_DISTANCE_THRESHOLD;
  },

  _shouldAnimateRemainder(gestureState: Object): boolean {
    /**
     * If user has swiped past a certain distance, animate the rest of the way
     * if they let go
     */
    return (
      Math.abs(gestureState.dx) > this.props.swipeThreshold ||
      gestureState.vx > HORIZONTAL_FULL_SWIPE_SPEED_THRESHOLD
    );
  },

  _handlePanResponderEnd(event: Object, gestureState: Object): void {
    const horizontalDistance = IS_RTL ? -gestureState.dx : gestureState.dx;
    if (this._isSwipingRightFromClosed(gestureState)) {
      this.props.onOpen();
      this._animateBounceBack(RIGHT_SWIPE_BOUNCE_BACK_DURATION);
    } else if (this._shouldAnimateRemainder(gestureState)) {
      if (horizontalDistance < 0) {
        // Swiped left
        this.props.onOpen();
        this._animateToOpenPositionWith(gestureState.vx, horizontalDistance);
      } else {
        // Swiped right
        this._animateToClosedPosition();
      }
    } else {
      if (this._previousLeft === CLOSED_LEFT_POSITION) {
        this._animateToClosedPosition();
      } else {
        this._animateToOpenPosition();
      }
    }

    this.props.onSwipeEnd();
  },
});

const styles = StyleSheet.create({
  slideOutContainer: {
    bottom: 0,
    left: 0,
    position: 'absolute',
    right: 0,
    top: 0,
  },
  swipeableContainer: {
    flex: 1,
  },
});

module.exports = SwipeableRow;
<commit_msg>Fix Scrollblocking on Android<commit_after>/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * The examples provided by Facebook are for non-commercial testing and
 * evaluation purposes only.
 *
 * Facebook reserves all rights not expressly granted.
 *
 * 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 NON INFRINGEMENT. IN NO EVENT SHALL
 * FACEBOOK 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.  *
 *
 * @providesModule SwipeableRow
 * @flow
 */
'use strict';

const Animated = require('Animated');
const PanResponder = require('PanResponder');
const I18nManager = require('I18nManager');
const React = require('React');
const StyleSheet = require('StyleSheet');
const TimerMixin = require('react-timer-mixin');
const View = require('View');

const {PropTypes} = React;

const emptyFunction = require('fbjs/lib/emptyFunction');

const IS_RTL = I18nManager.isRTL;

// NOTE: Eventually convert these consts to an input object of configurations

// Position of the left of the swipable item when closed
const CLOSED_LEFT_POSITION = 0;
// Minimum swipe distance before we recognize it as such
const HORIZONTAL_SWIPE_DISTANCE_THRESHOLD = 10;
// Minimum swipe speed before we fully animate the user's action (open/close)
const HORIZONTAL_FULL_SWIPE_SPEED_THRESHOLD = 0.3;
// Factor to divide by to get slow speed; i.e. 4 means 1/4 of full speed
const SLOW_SPEED_SWIPE_FACTOR = 4;
// Time, in milliseconds, of how long the animated swipe should be
const SWIPE_DURATION = 300;

/**
 * On SwipeableListView mount, the 1st item will bounce to show users it's
 * possible to swipe
 */
const ON_MOUNT_BOUNCE_DELAY = 700;
const ON_MOUNT_BOUNCE_DURATION = 400;

// Distance left of closed position to bounce back when right-swiping from closed
const RIGHT_SWIPE_BOUNCE_BACK_DISTANCE = 30;
const RIGHT_SWIPE_BOUNCE_BACK_DURATION = 300;
/**
 * Max distance of right swipe to allow (right swipes do functionally nothing).
 * Must be multiplied by SLOW_SPEED_SWIPE_FACTOR because gestureState.dx tracks
 * how far the finger swipes, and not the actual animation distance.
*/
const RIGHT_SWIPE_THRESHOLD = 30 * SLOW_SPEED_SWIPE_FACTOR;

/**
 * Creates a swipable row that allows taps on the main item and a custom View
 * on the item hidden behind the row. Typically this should be used in
 * conjunction with SwipeableListView for additional functionality, but can be
 * used in a normal ListView. See the renderRow for SwipeableListView to see how
 * to use this component separately.
 */
const SwipeableRow = React.createClass({
  _panResponder: {},
  _previousLeft: CLOSED_LEFT_POSITION,

  mixins: [TimerMixin],

  propTypes: {
    children: PropTypes.any,
    isOpen: PropTypes.bool,
    maxSwipeDistance: PropTypes.number.isRequired,
    onOpen: PropTypes.func.isRequired,
    onSwipeEnd: PropTypes.func.isRequired,
    onSwipeStart: PropTypes.func.isRequired,
    // Should bounce the row on mount
    shouldBounceOnMount: PropTypes.bool,
    /**
     * A ReactElement that is unveiled when the user swipes
     */
    slideoutView: PropTypes.node.isRequired,
    /**
     * The minimum swipe distance required before fully animating the swipe. If
     * the user swipes less than this distance, the item will return to its
     * previous (open/close) position.
     */
    swipeThreshold: PropTypes.number.isRequired,
  },

  getInitialState(): Object {
    return {
      currentLeft: new Animated.Value(this._previousLeft),
      /**
       * In order to render component A beneath component B, A must be rendered
       * before B. However, this will cause "flickering", aka we see A briefly
       * then B. To counter this, _isSwipeableViewRendered flag is used to set
       * component A to be transparent until component B is loaded.
       */
      isSwipeableViewRendered: false,
      rowHeight: (null: ?number),
    };
  },

  getDefaultProps(): Object {
    return {
      isOpen: false,
      maxSwipeDistance: 0,
      onOpen: emptyFunction,
      onSwipeEnd: emptyFunction,
      onSwipeStart: emptyFunction,
      swipeThreshold: 30,
    };
  },

  componentWillMount(): void {
    this._panResponder = PanResponder.create({
      onMoveShouldSetPanResponderCapture: this._handleMoveShouldSetPanResponderCapture,
      onPanResponderGrant: this._handlePanResponderGrant,
      onPanResponderMove: this._handlePanResponderMove,
      onPanResponderRelease: this._handlePanResponderEnd,
      onPanResponderTerminationRequest: this._onPanResponderTerminationRequest,
      onPanResponderTerminate: this._handlePanResponderEnd,
      onShouldBlockNativeResponder: (event, gestureState) => false,
    });
  },

  componentDidMount(): void {
    if (this.props.shouldBounceOnMount) {
      /**
       * Do the on mount bounce after a delay because if we animate when other
       * components are loading, the animation will be laggy
       */
      this.setTimeout(() => {
        this._animateBounceBack(ON_MOUNT_BOUNCE_DURATION);
      }, ON_MOUNT_BOUNCE_DELAY);
    }
  },

  componentWillReceiveProps(nextProps: Object): void {
    /**
     * We do not need an "animateOpen(noCallback)" because this animation is
     * handled internally by this component.
     */
    if (this.props.isOpen && !nextProps.isOpen) {
      this._animateToClosedPosition();
    }
  },

  shouldComponentUpdate(nextProps: Object, nextState: Object): boolean {
    if (this.props.shouldBounceOnMount && !nextProps.shouldBounceOnMount) {
      // No need to rerender if SwipeableListView is disabling the bounce flag
      return false;
    }

    return true;
  },

  render(): React.Element<any> {
    // The view hidden behind the main view
    let slideOutView;
    if (this.state.isSwipeableViewRendered) {
      slideOutView = (
        <View style={[
          styles.slideOutContainer,
          {height: this.state.rowHeight},
          ]}>
          {this.props.slideoutView}
        </View>
      );
    }

    // The swipeable item
    const swipeableView = (
      <Animated.View
        onLayout={this._onSwipeableViewLayout}
        style={[
          styles.swipeableContainer,
          {
            transform: [{translateX: this.state.currentLeft}],
          },
        ]}>
        {this.props.children}
      </Animated.View>
    );

    return (
      <View
        {...this._panResponder.panHandlers}>
        {slideOutView}
        {swipeableView}
      </View>
    );
  },

  _onSwipeableViewLayout(event: Object): void {
    this.setState({
      isSwipeableViewRendered: true,
      rowHeight: event.nativeEvent.layout.height,
    });
  },

  _handleMoveShouldSetPanResponderCapture(
    event: Object,
    gestureState: Object,
  ): boolean {
    // Decides whether a swipe is responded to by this component or its child
    return gestureState.dy < 10 && this._isValidSwipe(gestureState);
  },

  _handlePanResponderGrant(event: Object, gestureState: Object): void {

  },

  _handlePanResponderMove(event: Object, gestureState: Object): void {
    if (this._isSwipingExcessivelyRightFromClosedPosition(gestureState)) {
      return;
    }

    this.props.onSwipeStart();

    if (this._isSwipingRightFromClosed(gestureState)) {
      this._swipeSlowSpeed(gestureState);
    } else {
      this._swipeFullSpeed(gestureState);
    }
  },

  _isSwipingRightFromClosed(gestureState: Object): boolean {
    const gestureStateDx = IS_RTL ? -gestureState.dx : gestureState.dx;
    return this._previousLeft === CLOSED_LEFT_POSITION && gestureStateDx > 0;
  },

  _swipeFullSpeed(gestureState: Object): void {
    this.state.currentLeft.setValue(this._previousLeft + gestureState.dx);
  },

  _swipeSlowSpeed(gestureState: Object): void {
    this.state.currentLeft.setValue(
      this._previousLeft + gestureState.dx / SLOW_SPEED_SWIPE_FACTOR,
    );
  },

  _isSwipingExcessivelyRightFromClosedPosition(gestureState: Object): boolean {
    /**
     * We want to allow a BIT of right swipe, to allow users to know that
     * swiping is available, but swiping right does not do anything
     * functionally.
     */
    const gestureStateDx = IS_RTL ? -gestureState.dx : gestureState.dx;
    return (
      this._isSwipingRightFromClosed(gestureState) &&
      gestureStateDx > RIGHT_SWIPE_THRESHOLD
    );
  },

  _onPanResponderTerminationRequest(
    event: Object,
    gestureState: Object,
  ): boolean {
    return false;
  },

  _animateTo(
    toValue: number,
    duration: number = SWIPE_DURATION,
    callback: Function = emptyFunction,
  ): void {
    Animated.timing(
      this.state.currentLeft,
      {
        duration,
        toValue,
      },
    ).start(() => {
      this._previousLeft = toValue;
      callback();
    });
  },

  _animateToOpenPosition(): void {
    const maxSwipeDistance = IS_RTL ? -this.props.maxSwipeDistance : this.props.maxSwipeDistance;
    this._animateTo(-maxSwipeDistance);
  },

  _animateToOpenPositionWith(
    speed: number,
    distMoved: number,
  ): void {
    /**
     * Ensure the speed is at least the set speed threshold to prevent a slow
     * swiping animation
     */
    speed = (
      speed > HORIZONTAL_FULL_SWIPE_SPEED_THRESHOLD ?
      speed :
      HORIZONTAL_FULL_SWIPE_SPEED_THRESHOLD
    );
    /**
     * Calculate the duration the row should take to swipe the remaining distance
     * at the same speed the user swiped (or the speed threshold)
     */
    const duration = Math.abs((this.props.maxSwipeDistance - Math.abs(distMoved)) / speed);
    const maxSwipeDistance = IS_RTL ? -this.props.maxSwipeDistance : this.props.maxSwipeDistance;
    this._animateTo(-maxSwipeDistance, duration);
  },

  _animateToClosedPosition(duration: number = SWIPE_DURATION): void {
    this._animateTo(CLOSED_LEFT_POSITION, duration);
  },

  _animateToClosedPositionDuringBounce(): void {
    this._animateToClosedPosition(RIGHT_SWIPE_BOUNCE_BACK_DURATION);
  },

  _animateBounceBack(duration: number): void {
    /**
     * When swiping right, we want to bounce back past closed position on release
     * so users know they should swipe right to get content.
     */
    const swipeBounceBackDistance = IS_RTL ?
      -RIGHT_SWIPE_BOUNCE_BACK_DISTANCE :
      RIGHT_SWIPE_BOUNCE_BACK_DISTANCE;
    this._animateTo(
      -swipeBounceBackDistance,
      duration,
      this._animateToClosedPositionDuringBounce,
    );
  },

  // Ignore swipes due to user's finger moving slightly when tapping
  _isValidSwipe(gestureState: Object): boolean {
    return Math.abs(gestureState.dx) > HORIZONTAL_SWIPE_DISTANCE_THRESHOLD;
  },

  _shouldAnimateRemainder(gestureState: Object): boolean {
    /**
     * If user has swiped past a certain distance, animate the rest of the way
     * if they let go
     */
    return (
      Math.abs(gestureState.dx) > this.props.swipeThreshold ||
      gestureState.vx > HORIZONTAL_FULL_SWIPE_SPEED_THRESHOLD
    );
  },

  _handlePanResponderEnd(event: Object, gestureState: Object): void {
    const horizontalDistance = IS_RTL ? -gestureState.dx : gestureState.dx;
    if (this._isSwipingRightFromClosed(gestureState)) {
      this.props.onOpen();
      this._animateBounceBack(RIGHT_SWIPE_BOUNCE_BACK_DURATION);
    } else if (this._shouldAnimateRemainder(gestureState)) {
      if (horizontalDistance < 0) {
        // Swiped left
        this.props.onOpen();
        this._animateToOpenPositionWith(gestureState.vx, horizontalDistance);
      } else {
        // Swiped right
        this._animateToClosedPosition();
      }
    } else {
      if (this._previousLeft === CLOSED_LEFT_POSITION) {
        this._animateToClosedPosition();
      } else {
        this._animateToOpenPosition();
      }
    }

    this.props.onSwipeEnd();
  },
});

const styles = StyleSheet.create({
  slideOutContainer: {
    bottom: 0,
    left: 0,
    position: 'absolute',
    right: 0,
    top: 0,
  },
  swipeableContainer: {
    flex: 1,
  },
});

module.exports = SwipeableRow;
<|endoftext|>


<commit_before>Seed({
		name : "Mold.Lib.Component",
		dna : "class",
		include : [
			"Mold.Lib.Directive",
			"Mold.Lib.Loader",
			"Mold.Lib.Event"
		]
	},
	function(seed){

		var _seed = seed,
			_that = this,
			_loader = new Mold.Lib.Loader();

		Mold.mixing(this, new Mold.Lib.Event(this));

		_loader.on("ready", function(){
			_that.trigger("files.loaded")
		});

		_loader.on("error", function(){
			_that.trigger("files.error")
		});

		_loader.on("process", function(e){
			_that.trigger("files.loading", e.data);
		});

		var _timer = false;

		this.publics = {
			directive : function(directive){
				directive.seed = _seed;
				Mold.Lib.Directive.add(directive, document);
				new Mold.Lib.Event(document).on("DOMNodeInserted", function(e){
					window.clearTimeout(_timer);
					_timer = window.setTimeout(function(){
						Mold.Lib.Directive.add(directive, document);
					}, 50);
				});

				if(directive.at === "attribute"){
					new Mold.Lib.Event(document).on("attribute.modified", function(e){
						if(e.data.attr === directive.name){
							Mold.Lib.Directive.add(directive, document);
						}
					});
				}

			},
			files : function(file){
				if(file && file.length > 0){
					_loader.append(file);
					if(!_loader.isLoading()){
						_loader.load();
					}
				}
			}
		}
	}
)<commit_msg>watching dom changes moved to Directives<commit_after>Seed({
		name : "Mold.Lib.Component",
		dna : "class",
		include : [
			"Mold.Lib.Directive",
			"Mold.Lib.Loader",
			"Mold.Lib.Event",
			"Mold.Lib.Observer"
		]
	},
	function(seed){

		var _seed = seed,
			_that = this,
			_loader = new Mold.Lib.Loader();

		Mold.mixing(this, new Mold.Lib.Event(this));

		_loader.on("ready", function(){
			_that.trigger("files.loaded")
		});

		_loader.on("error", function(){
			_that.trigger("files.error")
		});

		_loader.on("process", function(e){
			_that.trigger("files.loading", e.data);
		});

		var _timer = false;

		this.publics = {
			directive : function(directive){
				directive.seed = _seed;
				Mold.Lib.Directive.add(directive, document);

				if(directive.at === "attribute"){
					new Mold.Lib.Event(document).on("attribute.modified", function(e){
						if(e.data.attr === directive.name){
							//Mold.Lib.Directive.add(directive, document);
						}
					});
				}

			},
			files : function(file){
				if(file && file.length > 0){
					_loader.append(file);
					if(!_loader.isLoading()){
						_loader.load();
					}
				}
			}
		}
	}
)<|endoftext|>


<commit_before>Emitter.on('theme:updateColor', (event, customColor) => {
  window.GPMTheme.updateTheme({
    foreSecondary: customColor,
  });
});

Emitter.on('theme:updateState', (event, state) => {
  if (state.state) {
    window.GPMTheme.enable();
  } else {
    window.GPMTheme.disable();
  }
});

Emitter.on('theme:updateType', (event, type) => {
  window.GPMTheme.updateTheme({
    type,
  });
});

window.wait(() => {
  window.GPMTheme.updateTheme({
    type: Settings.get('themeType', 'FULL'),
    foreSecondary: Settings.get('themeColor'),
  });
  if (Settings.get('theme')) {
    window.GPMTheme.enable();
  } else {
    window.GPMTheme.disable();
  }
});
<commit_msg>Update highlight colour (#1208)<commit_after>Emitter.on('theme:updateColor', (event, customColor) => {
  window.GPMTheme.updateTheme({
    foreSecondary: customColor,
  });
});

Emitter.on('theme:updateState', (event, state) => {
  if (state.state) {
    window.GPMTheme.enable();
  } else {
    window.GPMTheme.disable();
  }
});

Emitter.on('theme:updateType', (event, type) => {
  window.GPMTheme.updateTheme({
    type,
  });
});

window.wait(() => {
  window.GPMTheme.updateTheme({
    type: Settings.get('themeType', 'FULL'),
    backHighlight: '#1a1b1d',
    foreSecondary: Settings.get('themeColor'),
  });
  if (Settings.get('theme')) {
    window.GPMTheme.enable();
  } else {
    window.GPMTheme.disable();
  }
});
<|endoftext|>


<commit_before>function createPageChooser(id, pageType, openAtParentId) {
    var chooserElement = $('#' + id + '-chooser');
    var pageTitle = chooserElement.find('.title');
    var input = $('#' + id);
    var editLink = chooserElement.find('.edit-link');

    $('.action-choose', chooserElement).click(function() {
        var initialUrl = window.chooserUrls.pageChooser;
        if (openAtParentId) {
            initialUrl += openAtParentId + '/';
        }

        ModalWorkflow({
            url: initialUrl,
            urlParams: { page_type: pageType },
            responses: {
                pageChosen: function(pageData) {
                    input.val(pageData.id);
                    openAtParentId = pageData.parentId;
                    pageTitle.text(pageData.title);
                    chooserElement.removeClass('blank');
                    editLink.attr('href', pageData.editUrl);
                }
            }
        });
    });

    $('.action-clear', chooserElement).click(function() {
        input.val('');
        openAtParentId = null;
        chooserElement.addClass('blank');
    });
}
<commit_msg>JS changes for #1581<commit_after>function createPageChooser(id, pageTypes, openAtParentId) {
    var chooserElement = $('#' + id + '-chooser');
    var pageTitle = chooserElement.find('.title');
    var input = $('#' + id);
    var editLink = chooserElement.find('.edit-link');

    $('.action-choose', chooserElement).click(function() {
        var initialUrl = window.chooserUrls.pageChooser;
        if (openAtParentId) {
            initialUrl += openAtParentId + '/';
        }

        ModalWorkflow({
            url: initialUrl,
            urlParams: { page_types: pageTypes.join(',') },
            responses: {
                pageChosen: function(pageData) {
                    input.val(pageData.id);
                    openAtParentId = pageData.parentId;
                    pageTitle.text(pageData.title);
                    chooserElement.removeClass('blank');
                    editLink.attr('href', pageData.editUrl);
                }
            }
        });
    });

    $('.action-clear', chooserElement).click(function() {
        input.val('');
        openAtParentId = null;
        chooserElement.addClass('blank');
    });
}
<|endoftext|>