<commit_before>import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import LoadingSpinner from '../loadingSpinner';
import cx from 'classnames';
import theme from './theme.css';

class Button extends PureComponent {
  static propTypes = {
    children: PropTypes.node,
    className: PropTypes.string,
    level: PropTypes.oneOf(['outline', 'primary', 'secondary', 'destructive']),
    disabled: PropTypes.bool,
    fullWidth: PropTypes.bool,
    href: PropTypes.string,
    icon: PropTypes.element,
    iconPlacement: PropTypes.oneOf(['left', 'right']),
    inverse: PropTypes.bool,
    label: PropTypes.string,
    onMouseLeave: PropTypes.func,
    onMouseUp: PropTypes.func,
    processing: PropTypes.bool,
    size: PropTypes.oneOf(['small', 'medium', 'large']),
    type: PropTypes.string,
  };

  static defaultProps = {
    className: '',
    fullWidth: false,
    level: 'secondary',
    iconPlacement: 'left',
    inverse: false,
    processing: false,
    size: 'medium',
    type: 'button',
  };

  getSpinnerColor() {
    const { inverse, level } = this.props;

    return level === 'secondary' || (level === 'outline' && !inverse) ? 'teal' : 'white';
  }

  handleMouseUp = event => {
    this.buttonNode.blur();
    if (this.props.onMouseUp) {
      this.props.onMouseUp(event);
    }
  };

  handleMouseLeave = event => {
    this.buttonNode.blur();
    if (this.props.onMouseLeave) {
      this.props.onMouseLeave(event);
    }
  };

  render() {
    const {
      children,
      className,
      level,
      disabled,
      fullWidth,
      href,
      icon,
      iconPlacement,
      inverse,
      label,
      size,
      type,
      processing,
      ...others
    } = this.props;

    const element = href ? 'a' : 'button';

    const classNames = cx(
      theme['button'],
      theme[level],
      {
        [theme['icon-only']]: !label && !children,
        [theme['inverse']]: inverse && level === 'outline',
        [theme['is-full-width']]: fullWidth,
        [theme['processing']]: processing,
        [theme[size]]: theme[size],
      },
      className,
    );

    const props = {
      ...others,
      href,
      ref: node => {
        this.buttonNode = node;
      },
      className: classNames,
      disabled,
      onMouseUp: this.handleMouseUp,
      onMouseLeave: this.handleMouseLeave,
      type: !href ? type : null,
      'data-teamleader-ui': 'button',
    };

    return React.createElement(
      element,
      props,
      icon && iconPlacement === 'left' ? icon : null,
      label || children ? (

          {label}
          {children}

      ) : null,
      icon && iconPlacement === 'right' ? icon : null,
      processing ? (
        <LoadingSpinner
          className={theme['spinner']}
          color={this.getSpinnerColor()}
          size={size === 'small' ? 'small' : 'medium'}
        />
      ) : null,
    );
  }
}

export default Button;
export { Button };
<commit_msg>Button: add propType descriptions<commit_after>import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import LoadingSpinner from '../loadingSpinner';
import cx from 'classnames';
import theme from './theme.css';

class Button extends PureComponent {
  static propTypes = {
    /** The content to display inside the button. */
    children: PropTypes.node,
    /** A class name for the button to give custom styles. */
    className: PropTypes.string,
    /** Determines which kind of button to be rendered. */
    level: PropTypes.oneOf(['outline', 'primary', 'secondary', 'destructive']),
    /** If true, component will be disabled. */
    disabled: PropTypes.bool,
    /** If true, component will take the full width available. */
    fullWidth: PropTypes.bool,
    /** If set, button will be rendered as an anchor element. */
    href: PropTypes.string,
    /** The icon displayed inside the button. */
    icon: PropTypes.element,
    /** The position of the icon inside the button. */
    iconPlacement: PropTypes.oneOf(['left', 'right']),
    /** If true, component will be rendered in inverse mode. */
    inverse: PropTypes.bool,
    /** The textual label displayed inside the button. */
    label: PropTypes.string,
    /** Callback function that is fired when mouse leaves the component. */
    onMouseLeave: PropTypes.func,
    /** Callback function that is fired when the mouse button is released. */
    onMouseUp: PropTypes.func,
    /** If true, component will show a loading spinner instead of label or children. */
    processing: PropTypes.bool,
    /** Size of the button. */
    size: PropTypes.oneOf(['small', 'medium', 'large']),
    /** Type of the button element. */
    type: PropTypes.string,
  };

  static defaultProps = {
    className: '',
    fullWidth: false,
    level: 'secondary',
    iconPlacement: 'left',
    inverse: false,
    processing: false,
    size: 'medium',
    type: 'button',
  };

  getSpinnerColor() {
    const { inverse, level } = this.props;

    return level === 'secondary' || (level === 'outline' && !inverse) ? 'teal' : 'white';
  }

  handleMouseUp = event => {
    this.buttonNode.blur();
    if (this.props.onMouseUp) {
      this.props.onMouseUp(event);
    }
  };

  handleMouseLeave = event => {
    this.buttonNode.blur();
    if (this.props.onMouseLeave) {
      this.props.onMouseLeave(event);
    }
  };

  render() {
    const {
      children,
      className,
      level,
      disabled,
      fullWidth,
      href,
      icon,
      iconPlacement,
      inverse,
      label,
      size,
      type,
      processing,
      ...others
    } = this.props;

    const element = href ? 'a' : 'button';

    const classNames = cx(
      theme['button'],
      theme[level],
      {
        [theme['icon-only']]: !label && !children,
        [theme['inverse']]: inverse && level === 'outline',
        [theme['is-full-width']]: fullWidth,
        [theme['processing']]: processing,
        [theme[size]]: theme[size],
      },
      className,
    );

    const props = {
      ...others,
      href,
      ref: node => {
        this.buttonNode = node;
      },
      className: classNames,
      disabled,
      onMouseUp: this.handleMouseUp,
      onMouseLeave: this.handleMouseLeave,
      type: !href ? type : null,
      'data-teamleader-ui': 'button',
    };

    return React.createElement(
      element,
      props,
      icon && iconPlacement === 'left' ? icon : null,
      label || children ? (

          {label}
          {children}

      ) : null,
      icon && iconPlacement === 'right' ? icon : null,
      processing ? (
        <LoadingSpinner
          className={theme['spinner']}
          color={this.getSpinnerColor()}
          size={size === 'small' ? 'small' : 'medium'}
        />
      ) : null,
    );
  }
}

export default Button;
export { Button };
<|endoftext|>


<commit_before>
App.ItemsCollection = Backbone.Collection.extend({

  model: App.ItemModel,

  setSelectedCategory: function(category) {

    _.each(this.models, function(model) {

      if ( _.contains(model.get('categories'), category) ) {

        model.set('selected', true);

      } else {

        model.set('selected', false);

      }

    });

  },

  getSelectedModels: function() {

    var selectedCollection = [];

    _.each(this.models, function(model) {

      if ( model.get('selected') ) {

        selectedCollection.push(model);

      }

    });

    return selectedCollection;

  },

  getUniqueCategories: function() {

    var _this = this,
        _categories = [];

    _.each(_this.models, function(model) {

      _.each(model.get('categories'), function(category) {

        _categories.push(category);

      });

    });
    // end create an array of all categories in the collection

    // return an object with the unique categories sorted alphabetically
    return {
      'categories': (_.uniq(_categories)).sort()
    };

  }

});

// both grid and sorter_grid modules reference itemsCollection, so make instance here to have one global version (vs instantiating in both the module definitions, which doesn't seem super dry)
App.itemsCollection = new App.ItemsCollection(App.Data);

// create unique categories instance
App.uniqueCategoriesObject = App.itemsCollection.getUniqueCategories();
<commit_msg>improved comments regarding App.itemsCollection.getUniqueCategories method<commit_after>
App.ItemsCollection = Backbone.Collection.extend({

  model: App.ItemModel,

  setSelectedCategory: function(category) {

    _.each(this.models, function(model) {

      if ( _.contains(model.get('categories'), category) ) {

        model.set('selected', true);

      } else {

        model.set('selected', false);

      }

    });

  },

  getSelectedModels: function() {

    var selectedCollection = [];

    _.each(this.models, function(model) {

      if ( model.get('selected') ) {

        selectedCollection.push(model);

      }

    });

    return selectedCollection;

  },

  getUniqueCategories: function() {

    var _this = this,
        _categories = [];

    _.each(_this.models, function(model) {

      _.each(model.get('categories'), function(category) {

        _categories.push(category);

      });

    });
    // end create an array of all categories in the collection

    // return an object with the unique categories sorted alphabetically
    return {
      'categories': (_.uniq(_categories)).sort()
    };

  }

});

// both grid and sorter_grid modules reference itemsCollection, so make instance here to have one global version (vs instantiating in both the module definitions, which doesn't seem super dry)
App.itemsCollection = new App.ItemsCollection(App.Data);

// create global unique categories instance, used in grid_sorter module & route_controller
App.uniqueCategoriesObject = App.itemsCollection.getUniqueCategories();
<|endoftext|>


<commit_before>var fs=require('fs');
var async=require('../../../async');
var constants= require('../../../constants.js')
var envVariables=require('../../../envVariables.js')
var Reading=require('../../../server/models/reading.js')

module.exports = function(socket){
  socket.on('connect',function(){
    fs.readFile(constants.MAIN_ID_PATH,'utf8',function(err,data){
      if(err){
        throw err;
      }
      constants.MAIN_ID=data;
      socket.emit('identification', {mainID: constants.MAIN_ID}, function(err,res){
        if(err){
          throw err;
        }
        if(res.status){
          if(res.new){
            fs.writeFile(constants.MAIN_ID_PATH, res.mainID, 'utf8', function(err){
              if(err){
                throw err;
              }
              constants.MAIN_ID=res.id;
              socket.disconnect();
            });
          }
          else{
            envVariables.serverConnectionStatus=true;
            Reading.find({},function(err,docs){
              if(err){
                throw err;
              }
              var i=0;
              var j=0;
              async.whilst(i<docs.length
                async.whilst(i==j,
                  socket.emit('rReading',docs[i],function(err,res){
                    if(err){
                      throw err;
                    }
                    if(res.status){
                      Reading.findById(docs[i]._id,function(err,doc){
                        if(err){
                          throw err;
                        }
                        doc.remove(function(err,res){
                          if(err){
                            throw err;
                          }
                        });
                      });
                    i++;
                    }
                  });
               );
               j++;
              );
            });
          }
        }
        else{
          if(res.error){
            console.log(res.error);
          }
          socket.disconnect();
        }
      });
    });
  });
  socket.on('disconnect',function(){
    envVariables.serverConnectionStatus=false;
  });
}
<commit_msg>Update connection.js<commit_after>var fs=require('fs');
var async=require('../../../async');
var constants= require('../../../constants.js')
var envVariables=require('../../../envVariables.js')
var Reading=require('../../../server/models/reading.js')

module.exports = function(socket){
  socket.on('connect',function(){
    fs.readFile(constants.MAIN_ID_PATH,'utf8',function(err,data){
      if(err){
        throw err;
      }
      constants.MAIN_ID=data;
      socket.emit('identification', {mainID: constants.MAIN_ID}, function(err,res){
        if(err){
          throw err;
        }
        if(res.status){
          if(res.new){
            fs.writeFile(constants.MAIN_ID_PATH, res.mainID, 'utf8', function(err){
              if(err){
                throw err;
              }
              constants.MAIN_ID=res.id;
              socket.disconnect();
            });
          }
          else{
            envVariables.serverConnectionStatus=true;
            Reading.find({},function(err,docs){
              if(err){
                throw err;
              }
              var i=0;
              var j=0;
              async.whilst(i<docs.length,
                async.whilst(i==j,
                  socket.emit('rReading',docs[i],function(err,res){
                    if(err){
                      throw err;
                    }
                    if(res.status){
                      Reading.findById(docs[i]._id,function(err,doc){
                        if(err){
                          throw err;
                        }
                        doc.remove(function(err,res){
                          if(err){
                            throw err;
                          }
                        });
                      });
                    i++;
                    }
                  });
               );
               j++;
              );
            });
          }
        }
        else{
          if(res.error){
            console.log(res.error);
          }
          socket.disconnect();
        }
      });
    });
  });
  socket.on('disconnect',function(){
    envVariables.serverConnectionStatus=false;
  });
}
<|endoftext|>


<commit_before>export { PadInfoCalculator };

import * as ValueDesc from "../../Unpacker/ValueDesc.js";

/**
 * According to input image size and depthwise convolution parameters, calculate the padding information of the depthwise convolution.
 *
 * @member {number} inputHeight           Input image height.
 * @member {number} inputWidth            Input image width.
 * @member {number} inputChannelCount     Input image channel count.
 * @member {number} AvgMax_Or_ChannelMultiplier   Depthwise operation. (ValueDesc.AvgMax_Or_ChannelMultiplier)
 * @member {number} filterHeight          The height of the depthwise convolution's filter.
 * @member {number} filterWidth           The width of the depthwise convolution's filter.
 * @member {number} stridesPad            The strides and padding of (depthwise) convolution. (ValueDesc.StridesPad)
 *
 * @member {number} channelMultiplier     The channel multiplier of the depthwise operation (according to AvgMax_Or_ChannelMultiplier).
 * @member {number} filterSize            The size of the depthwise convolution's filter. (= filterHeight * filterWidth)
 *
 * @member {number} dilationHeight        The depthwise filters's dilation across height dimension.
 * @member {number} dilationWidth         The depthwise filters's dilation across width dimension.
 *
 * @member {number} effectFilterHeight    The effect height of the depthwise convolution's filter including the dilationHeight.
 * @member {number} effectFilterWidth     The effect width of the depthwise convolution's filter including the dilationWidth.
 * @member {number} effectFilterSize      The effect size of the depthwise convolution's filter. (= effectFilterHeight * effectFilterWidth)
 *
 * @member {number} strides               The strides along the image's height and width dimension (according to stridesPad).
 * @member {number} stridesHeight         The strides along the image's height dimension (according to stridesPad).
 * @member {number} stridesWidth          The strides along the image's width dimension (according to stridesPad).

 * @member {number} pad                   The padding along the image's height and width dimension (according to stridesPad).
 * @member {number} padHeight             The padding along the input image's height dimension.
 * @member {number} padHeightTop          The padding along the input image's height dimension at the top.
 * @member {number} padHeightBottom       The padding along the input image's height dimension at the bottom.
 * @member {number} padWidth              The padding along the input image's width dimension.
 * @member {number} padWidthLeft          The padding along the input image's width dimension at the left.
 * @member {number} padWidthRight         The padding along the input image's width dimension at the right.
 *
 * @member {number} outputHeight          Output image height.
 * @member {number} outputWidth           Output image width.
 * @member {number} outputChannelCount    Output image channel count.
 * @member {number} outputElementCount    Output image elements count (= ( outputHeight * outputWidth * outputChannelCount ) ).
 */
let PadInfoCalculator = ( Base = Object ) => class extends Base {

  constructor( inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad ) {
    super();
    this.set( inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad );
  }

  /**
   *
   */
  set( inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad ) {
    this.inputHeight = inputHeight;
    this.inputWidth = inputWidth;
    this.inputChannelCount = inputChannelCount;
    this.AvgMax_Or_ChannelMultiplier = AvgMax_Or_ChannelMultiplier;
    this.filterHeight = filterHeight;
    this.filterWidth = filterWidth;
    this.stridesPad = stridesPad;

//!!! ...unfinished... (2021/03/17) What about ( filterHeight <= 0 ) or ( filterWidth <= 0 )?

    this.channelMultiplier = AvgMax_Or_ChannelMultiplier;
    if (   ( ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.AVG === AvgMax_Or_ChannelMultiplier )
        || ( ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.MAX === AvgMax_Or_ChannelMultiplier ) ) {
      this.channelMultiplier = 1;
    }

    this.filterSize = this.filterHeight * this.filterWidth;

    // Strides and Padding.
    switch ( stridesPad ) {
      case ValueDesc.StridesPad.Singleton.Ids.STRIDES_1_PAD_VALID: this.strides = 1; this.pad = "valid"; break; // (0)
      default:
      case ValueDesc.StridesPad.Singleton.Ids.STRIDES_1_PAD_SAME:  this.strides = 1; this.pad = "same";  break; // (1)
      case ValueDesc.StridesPad.Singleton.Ids.STRIDES_2_PAD_SAME:  this.strides = 2; this.pad = "same";  break; // (2)
      case ValueDesc.StridesPad.Singleton.Ids.STRIDES_2_PAD_VALID: this.strides = 2; this.pad = "valid"; break; // (3)
    }

    // Assume strides width equals strides height.
    this.stridesHeight = this.strides;
    this.stridesWidth = this.strides;

    // Currently, we can only handle dilation = 1.
    this.dilationHeight = 1;
    this.dilationWidth = 1;

    // Effect filter size (includes dilation).
    this.effectFilterHeight = this.dilationHeight * ( this.filterHeight - 1 ) + 1;
    this.effectFilterWidth =  this.dilationWidth  * ( this.filterWidth  - 1 ) + 1;
    this.effectFilterSize = this.effectFilterHeight * this.effectFilterWidth;

    // (The following codes for output image height and width and padding calculation are copied from
    // https://github.com/tensorflow/tfjs/blob/tfjs-v3.8.0/tfjs-core/src/ops/conv_util.ts)
    {
      // Determine output image height and width without padding.
      if ( this.pad == "valid" ) {
        this.outputHeight = Math.ceil( ( inputHeight - this.effectFilterHeight + 1 ) / this.stridesHeight );
        this.outputWidth =  Math.ceil( ( inputWidth  - this.effectFilterWidth  + 1 ) / this.stridesWidth  );

        this.padHeight = this.padHeightTop = this.padHeightBottom = this.padWidth = this.padWidthLeft = this.padWidthRight = 0;

      // Determine output image height and width with padding around the input image height and width.
      } else if ( this.pad == "same" ) {
        this.outputHeight = Math.ceil( inputHeight / this.stridesHeight );
        this.outputWidth =  Math.ceil( inputWidth  / this.stridesWidth  );

        this.padHeight = Math.max( 0, ( this.outputHeight - 1 ) * this.stridesHeight + this.effectFilterHeight - inputHeight );
        this.padWidth =  Math.max( 0, ( this.outputWidth  - 1 ) * this.stridesWidth  + this.effectFilterWidth  - inputWidth  );

        this.padHeightTop =    Math.floor( this.padHeight / 2 );
        this.padHeightBottom = this.padHeight - this.padHeightTop;
        this.padWidthLeft =    Math.floor( this.padWidth /  2 );
        this.padWidthRight =   this.padWidth  - this.padWidthLeft;
      }
    }

    this.outputChannelCount = inputChannelCount * this.channelMultiplier;
    this.outputElementCount = ( this.outputHeight * this.outputWidth * this.outputChannelCount );
  }

  /**
   * @return {boolean}
   *   If the ( height, width ) of this depthwise operation output is the same as its input, return true.
   */
  is_Output_Same_HeightWidth_As_Input() {

    // If this depthwise operation does not existed, the output will have the same ( height, width ) as input.
    // In fact, they are the same one in this case.
    if ( ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.NONE === this.AvgMax_Or_ChannelMultiplier )
      return true;

    if ( this.strides != 1 )
      return false; // If strides is not 1, it is impossible to output same ( height, width ) as input.

    if ( this.pad == "same" )
      return true; // If ( strides is 1 ) and ( pad is "same" ), the output will have the same ( height, width ) as input.

    // Or, although ( strides is 1 ) and ( pad is "valid" ) but ( filter size is 1x1 ), the output will have the same ( height, width ) as input.
    if ( ( this.pad == "valid" ) && ( this.filterHeight == 1 ) && ( this.filterWidth == 1 ) )
      return true;

    return false;
  }

  /**
   * If both effectFilterValue and otherFilterValue are the same as ( 1 / ( filterHeight * filter Width ) ), the result filter
   * will have the same effect as average pooling.
   *
   *
   * @param {number} centerFilterValue
   *   The filter value used for the center input pixel of the depthwise convolution. For pass-through, it is usually 1.
   *
   * @param {number} surroundingFilterValue
   *   The filter value used for the surrounding input pixel of the depthwise convolution. For pass-through, it is usually 0.
   *
   * @return {number[]}
   *   Return the depthwise convolution filters which could pass the input to output unchangely.
   */
  generate_PassThrough_FiltersArray( centerFilterValue, surroundingFilterValue ) {

    if (   ( ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.AVG === this.AvgMax_Or_ChannelMultiplier )
        || ( ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.MAX === this.AvgMax_Or_ChannelMultiplier ) ) {
      return null; // The depthwise filter of AVG pooling and MAX pooling can not be manipulated.
    }

    // Make up a depthwise convolution filter.
    let depthwiseFiltersArray = new Array( this.filterHeight * this.filterWidth * this.inputChannelCount * this.channelMultiplier );

    // There is only one position (inside the effect depthwise filter) uses centerFilterValue.
    // All other positions of the filter should be surroundingFilterValue.
    //
    // Note: It is not always just at center of filter according to the filter shape.
    //
    let oneEffectFilterY = this.padHeightTop;
    let oneEffectFilterX = this.padWidthLeft;

    // Note: Unfortunately, this may not work for ( dilation > 1 ) because the non-zero-filter-value might be just at the dilation
    //       position which does not exist in a filter. So, only ( dilation == 1 ) is supported.

//!!! ...unfinished... (2021/12/26) Since these for-loop are in correct order, the filterIndex could just begin at zero and then
// be increased one by one (i.e. without using multiplication).
//
    let filterIndex = 0; // The index in the filter weights array.

    for ( let filterY = 0, effectFilterY = 0; filterY < this.filterHeight; ++filterY ) {
      for ( let dilationFilterY = 0; dilationFilterY < this.dilationHeight; ++dilationFilterY, ++effectFilterY ) {

//!!! (2022/05/26 Remarked) Replaced by filterIndex increment.
//        let filterIndexBaseX = ( filterY * this.filterWidth );

        for ( let filterX = 0, effectFilterX = 0; filterX < this.filterWidth; ++filterX ) {
          for ( let dilationFilterX = 0; dilationFilterX < this.dilationWidth; ++dilationFilterX, ++effectFilterX ) {

            // The filter's dilation part can not be manipulated. (They are always zero.)
            if ( ( 0 != dilationFilterY ) || ( 0 != dilationFilterX ) )
              continue;

//!!! (2022/05/26 Remarked) Replaced by filterIndex increment.
//            let filterIndexBaseC = ( ( filterIndexBaseX + filterX ) * this.outputChannelCount );

            for ( let inChannel = 0; inChannel < this.inputChannelCount; ++inChannel ) {

//!!! (2022/05/26 Remarked) Replaced by filterIndex increment.
//              let filterIndexBaseSubC = filterIndexBaseC + ( inChannel * this.channelMultiplier );

              for ( let outChannelSub = 0; outChannelSub < this.channelMultiplier; ++outChannelSub ) {

//!!! (2022/05/26 Remarked) Replaced by filterIndex increment.
//                let filterIndex = filterIndexBaseSubC + outChannelSub;

                if ( ( effectFilterY == oneEffectFilterY ) && ( effectFilterX == oneEffectFilterX ) ) {
                  depthwiseFiltersArray[ filterIndex ] = centerFilterValue;
                } else {
                  depthwiseFiltersArray[ filterIndex ] = surroundingFilterValue;
                }

                ++filterIndex;
              }
            }
          }
        }
      }
    }

    return depthwiseFiltersArray;
  }

}

<commit_msg>Update Depthwise_PadInfoCalculator.js<commit_after>export { PadInfoCalculator };

import * as ValueDesc from "../../Unpacker/ValueDesc.js";

/**
 * According to input image size and depthwise convolution parameters, calculate the padding information of the depthwise convolution.
 *
 * @member {number} inputHeight           Input image height.
 * @member {number} inputWidth            Input image width.
 * @member {number} inputChannelCount     Input image channel count.
 * @member {number} AvgMax_Or_ChannelMultiplier   Depthwise operation. (ValueDesc.AvgMax_Or_ChannelMultiplier)
 * @member {number} filterHeight          The height of the depthwise convolution's filter.
 * @member {number} filterWidth           The width of the depthwise convolution's filter.
 * @member {number} stridesPad            The strides and padding of (depthwise) convolution. (ValueDesc.StridesPad)
 *
 * @member {number} channelMultiplier     The channel multiplier of the depthwise operation (according to AvgMax_Or_ChannelMultiplier).
 * @member {number} filterSize            The size of the depthwise convolution's filter. (= filterHeight * filterWidth)
 *
 * @member {number} dilationHeight        The depthwise filters's dilation across height dimension.
 * @member {number} dilationWidth         The depthwise filters's dilation across width dimension.
 *
 * @member {number} effectFilterHeight    The effect height of the depthwise convolution's filter including the dilationHeight.
 * @member {number} effectFilterWidth     The effect width of the depthwise convolution's filter including the dilationWidth.
 * @member {number} effectFilterSize      The effect size of the depthwise convolution's filter. (= effectFilterHeight * effectFilterWidth)
 *
 * @member {number} strides               The strides along the image's height and width dimension (according to stridesPad).
 * @member {number} stridesHeight         The strides along the image's height dimension (according to stridesPad).
 * @member {number} stridesWidth          The strides along the image's width dimension (according to stridesPad).

 * @member {number} pad                   The padding along the image's height and width dimension (according to stridesPad).
 * @member {number} padHeight             The padding along the input image's height dimension.
 * @member {number} padHeightTop          The padding along the input image's height dimension at the top.
 * @member {number} padHeightBottom       The padding along the input image's height dimension at the bottom.
 * @member {number} padWidth              The padding along the input image's width dimension.
 * @member {number} padWidthLeft          The padding along the input image's width dimension at the left.
 * @member {number} padWidthRight         The padding along the input image's width dimension at the right.
 *
 * @member {number} outputHeight          Output image height.
 * @member {number} outputWidth           Output image width.
 * @member {number} outputChannelCount    Output image channel count.
 * @member {number} outputElementCount    Output image elements count (= ( outputHeight * outputWidth * outputChannelCount ) ).
 */
let PadInfoCalculator = ( Base = Object ) => class extends Base {

  constructor( inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad ) {
    super();
    this.set( inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad );
  }

  /**
   *
   */
  set( inputHeight, inputWidth, inputChannelCount, AvgMax_Or_ChannelMultiplier, filterHeight, filterWidth, stridesPad ) {
    this.inputHeight = inputHeight;
    this.inputWidth = inputWidth;
    this.inputChannelCount = inputChannelCount;
    this.AvgMax_Or_ChannelMultiplier = AvgMax_Or_ChannelMultiplier;
    this.filterHeight = filterHeight;
    this.filterWidth = filterWidth;
    this.stridesPad = stridesPad;

//!!! ...unfinished... (2021/03/17) What about ( filterHeight <= 0 ) or ( filterWidth <= 0 )?

    this.channelMultiplier = AvgMax_Or_ChannelMultiplier;
    if (   ( ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.AVG === AvgMax_Or_ChannelMultiplier )
        || ( ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.MAX === AvgMax_Or_ChannelMultiplier ) ) {
      this.channelMultiplier = 1;
    }

    this.filterSize = this.filterHeight * this.filterWidth;

    // Strides and Padding.
    switch ( stridesPad ) {
      case ValueDesc.StridesPad.Singleton.Ids.STRIDES_1_PAD_VALID: this.strides = 1; this.pad = "valid"; break; // (0)
      default:
      case ValueDesc.StridesPad.Singleton.Ids.STRIDES_1_PAD_SAME:  this.strides = 1; this.pad = "same";  break; // (1)
      case ValueDesc.StridesPad.Singleton.Ids.STRIDES_2_PAD_SAME:  this.strides = 2; this.pad = "same";  break; // (2)
      case ValueDesc.StridesPad.Singleton.Ids.STRIDES_2_PAD_VALID: this.strides = 2; this.pad = "valid"; break; // (3)
    }

    // Assume strides width equals strides height.
    this.stridesHeight = this.strides;
    this.stridesWidth = this.strides;

    // Currently, we can only handle dilation = 1.
    this.dilationHeight = 1;
    this.dilationWidth = 1;

    // Effect filter size (includes dilation).
    this.effectFilterHeight = this.dilationHeight * ( this.filterHeight - 1 ) + 1;
    this.effectFilterWidth =  this.dilationWidth  * ( this.filterWidth  - 1 ) + 1;
    this.effectFilterSize = this.effectFilterHeight * this.effectFilterWidth;

    // (The following codes for output image height and width and padding calculation are copied from
    // https://github.com/tensorflow/tfjs/blob/tfjs-v3.8.0/tfjs-core/src/ops/conv_util.ts)
    {
      // Determine output image height and width without padding.
      if ( this.pad == "valid" ) {
        this.outputHeight = Math.ceil( ( inputHeight - this.effectFilterHeight + 1 ) / this.stridesHeight );
        this.outputWidth =  Math.ceil( ( inputWidth  - this.effectFilterWidth  + 1 ) / this.stridesWidth  );

        this.padHeight = this.padHeightTop = this.padHeightBottom = this.padWidth = this.padWidthLeft = this.padWidthRight = 0;

      // Determine output image height and width with padding around the input image height and width.
      } else if ( this.pad == "same" ) {
        this.outputHeight = Math.ceil( inputHeight / this.stridesHeight );
        this.outputWidth =  Math.ceil( inputWidth  / this.stridesWidth  );

        this.padHeight = Math.max( 0, ( this.outputHeight - 1 ) * this.stridesHeight + this.effectFilterHeight - inputHeight );
        this.padWidth =  Math.max( 0, ( this.outputWidth  - 1 ) * this.stridesWidth  + this.effectFilterWidth  - inputWidth  );

        this.padHeightTop =    Math.floor( this.padHeight / 2 );
        this.padHeightBottom = this.padHeight - this.padHeightTop;
        this.padWidthLeft =    Math.floor( this.padWidth /  2 );
        this.padWidthRight =   this.padWidth  - this.padWidthLeft;
      }
    }

    this.outputChannelCount = inputChannelCount * this.channelMultiplier;
    this.outputElementCount = ( this.outputHeight * this.outputWidth * this.outputChannelCount );
  }

  /**
   * @return {boolean}
   *   If the ( height, width ) of this depthwise operation output is the same as its input, return true.
   */
  is_Output_Same_HeightWidth_As_Input() {

    // If this depthwise operation does not existed, the output will have the same ( height, width ) as input.
    // In fact, they are the same one in this case.
    if ( ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.NONE === this.AvgMax_Or_ChannelMultiplier )
      return true;

    if ( this.strides != 1 )
      return false; // If strides is not 1, it is impossible to output same ( height, width ) as input.

    if ( this.pad == "same" )
      return true; // If ( strides is 1 ) and ( pad is "same" ), the output will have the same ( height, width ) as input.

    // Or, although ( strides is 1 ) and ( pad is "valid" ) but ( filter size is 1x1 ), the output will have the same ( height, width ) as input.
    if ( ( this.pad == "valid" ) && ( this.filterHeight == 1 ) && ( this.filterWidth == 1 ) )
      return true;

    return false;
  }

  /**
   * If both effectFilterValue and otherFilterValue are the same as ( 1 / ( filterHeight * filter Width ) ), the result filter
   * will have the same effect as average pooling.
   *
   *
   * @param {number} effectFilterValue
   *   The filter value used for the effect input pixel of the depthwise convolution. For pass-through, it is usually 1.
   * Note: It is not always just at center of filter according to the filter shape and paddding.
   *
   * @param {number} surroundingFilterValue
   *   The filter value used for the surrounding input pixel of the depthwise convolution. For pass-through, it is usually 0.
   *
   * @return {number[]}
   *   Return the depthwise convolution filters which could pass the input to output unchangely.
   */
  generate_PassThrough_FiltersArray( effectFilterValue, surroundingFilterValue ) {

    if (   ( ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.AVG === this.AvgMax_Or_ChannelMultiplier )
        || ( ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.MAX === this.AvgMax_Or_ChannelMultiplier ) ) {
      return null; // The depthwise filter of AVG pooling and MAX pooling can not be manipulated.
    }

    // Make up a depthwise convolution filter.
    let depthwiseFiltersArray = new Array( this.filterHeight * this.filterWidth * this.inputChannelCount * this.channelMultiplier );

    // There is only one position (inside the effect depthwise filter) uses effectFilterValue.
    // All other positions of the filter should be surroundingFilterValue.
    //
    let oneEffectFilterY = this.padHeightTop;
    let oneEffectFilterX = this.padWidthLeft;

    // Note: Unfortunately, this may not work for ( dilation > 1 ) because the non-zero-filter-value might be just at the dilation
    //       position which does not exist in a filter. So, only ( dilation == 1 ) is supported.

//!!! ...unfinished... (2021/12/26) Since these for-loop are in correct order, the filterIndex could just begin at zero and then
// be increased one by one (i.e. without using multiplication).
//
    let filterIndex = 0; // The index in the filter weights array.

    for ( let filterY = 0, effectFilterY = 0; filterY < this.filterHeight; ++filterY ) {
      for ( let dilationFilterY = 0; dilationFilterY < this.dilationHeight; ++dilationFilterY, ++effectFilterY ) {

//!!! (2022/05/26 Remarked) Replaced by filterIndex increment.
//        let filterIndexBaseX = ( filterY * this.filterWidth );

        for ( let filterX = 0, effectFilterX = 0; filterX < this.filterWidth; ++filterX ) {
          for ( let dilationFilterX = 0; dilationFilterX < this.dilationWidth; ++dilationFilterX, ++effectFilterX ) {

            // The filter's dilation part can not be manipulated. (They are always zero.)
            if ( ( 0 != dilationFilterY ) || ( 0 != dilationFilterX ) )
              continue;

//!!! (2022/05/26 Remarked) Replaced by filterIndex increment.
//            let filterIndexBaseC = ( ( filterIndexBaseX + filterX ) * this.outputChannelCount );

            for ( let inChannel = 0; inChannel < this.inputChannelCount; ++inChannel ) {

//!!! (2022/05/26 Remarked) Replaced by filterIndex increment.
//              let filterIndexBaseSubC = filterIndexBaseC + ( inChannel * this.channelMultiplier );

              for ( let outChannelSub = 0; outChannelSub < this.channelMultiplier; ++outChannelSub ) {

//!!! (2022/05/26 Remarked) Replaced by filterIndex increment.
//                let filterIndex = filterIndexBaseSubC + outChannelSub;

                if ( ( effectFilterY == oneEffectFilterY ) && ( effectFilterX == oneEffectFilterX ) ) {
                  depthwiseFiltersArray[ filterIndex ] = effectFilterValue;
                } else {
                  depthwiseFiltersArray[ filterIndex ] = surroundingFilterValue;
                }

                ++filterIndex;
              }
            }
          }
        }
      }
    }

    return depthwiseFiltersArray;
  }

}

<|endoftext|>


<commit_before>/**
 * Generate ACII art from image or video
 *
 * @module   Schwartz
 * @version  1.9
 *
 * @author   Zaytsev Alexandr
 *
 * @example
 *    // Exapmple with image
 *    var
 *        body    = document.getElementsByTagName('body')[0],
 *        outDiv  = document.createElement('div'),
 *        schwartz = new Schwartz({
 *            detail: 60
 *        });
 *    // end of vars
 *
 *    body.appendChild(outDiv);
 *    schwartz.generateFromImage('/path/to/img.jpg');
 *
 *
 *    // Example with video
 *    var
 *        body    = document.getElementsByTagName('body')[0],
 *        video   = document.getElementsByTagName('video')[0],
 *        outDiv  = document.createElement('div'),
 *
 *        schwartz = new Schwartz({
 *            inverse: true,
 *            detail: 60
 *        });
 *    // end of vars
 *
 *
 *    body.appendChild(outDiv);
 *    schwartz.generateFromVideo(video);
 */
!function( window, module ) {
    'use strict';

    var
        Schwartz = module();
        // end of vars

    // AMD
    if ( typeof define === 'function' && define.amd ) {
        define('Schwartz', [], function() {
            return Schwartz;
        });

    // YM Modules
    } else if ( typeof modules !== 'undefined' && typeof modules.define === 'function' ) {
        modules.define('Schwartz', [], function( provide ) {
            provide(Schwartz);
        });

    // Browser global
    } else {
        window.Schwartz = Schwartz;
    }
}( this, function() {
    'use strict';

    var
        Schwartz = (function() {
            var
                /**
                 * @const
                 */
                DEFAULT_DETAIL = 50,

                DEFAULT_INVERSE = false,

                /**
                 * After parse image callback
                 *
                 * @memberOf    module:Schwartz~Schwartz
                 * @callback    parseImageCallback
                 *
                 * @param       {Object[][]}  res       Art strings by line
                 * @param       {Number}      res.r     Red channel average
                 * @param       {Number}      res.g     Green channel average
                 * @param       {Number}      res.b     Blue channel average
                 * @param       {Number}      res.a     Alpha channel average
                 * @param       {Number}      res.c     Composite average
                 * @param       {Number}      res.sym   Symbol
                 */

                /**
                 * @memberOf     module:Schwartz~
                 *
                 * @constructs   Schwartz
                 *
                 * @param        {Object}               [options]
                 * @param        {Boolean}              [options.inverse=false]
                 * @param        {Number}               [options.detail=50]
                 * @param        {parseImageCallback}   [options.render]
                 */
                Schwartz = function Schwartz( options ) {
                    // enforces new
                    if ( !( this instanceof Schwartz ) ) {
                        return new Schwartz(options);
                    }

                    // constructor body
                    this.canvas        = document.createElement('canvas');
                    this.imgObj        = new Image();
                    this.c             = this.canvas.getContext('2d');

                    this.inverse       = options.inverse || DEFAULT_INVERSE;
                    this.callback      = options.render || false;

                    /**
                     * Default symbols
                     *
                     * @memberOf    module:Schwartz~Schwartz
                     * @member      loadImage
                     * @private
                     *
                     * @type        {Array}
                     */
                    this.charSet = [' ', '.', ',', ':', ';', '*', '|', '~', 'I', '1', '?', '7', '>', 'Y', 'F', '4', 'V', '#', '2', '9', '6', '8', '%', 'N', 'B', 'Q', 'M', '@', 'W'];

                    if ( options.detail ) {
                        this.setDetail(options.detail);
                    } else {
                        this.detail = DEFAULT_DETAIL;
                    }

                    this.charSet = ( this.inverse ) ? this.charSet : this.charSet.reverse();
                },

                /**
                 * ==============================
                 * ========PRIVATE METHODS=======
                 * ==============================
                 */

                /**
                 * Validate numbers
                 *
                 * @memberOf    module:Schwartz~Schwartz
                 * @method      isNumber
                 * @private
                 *
                 * @param       {*}         n
                 *
                 * @return      {Boolean}
                 */
                isNumber = function( n ) {
                    return !isNaN(parseFloat(n)) && isFinite(n);
                },

                /**
                 * After load image callback
                 *
                 * @memberOf    module:Schwartz~Schwartz
                 * @callback    loadImageCallback
                 *
                 * @param       {Object}    imageData
                 */

                /**
                 * @memberOf    module:Schwartz~Schwartz
                 * @method      loadImage
                 * @private
                 *
                 * @this        {Schwartz}
                 *
                 * @param       {String}                src
                 * @param       {loadImageCallback}     callback
                 */
                loadImage = function( src, callback ) {
                    var
                        self = this,

                        imageLoaded = function( ev ) {
                            var
                                imgW = self.imgObj.width,
                                imgH = self.imgObj.height,
                                img = ev.target,
                                imageData;
                            // end of vars

                            self.canvas.width = imgW;
                            self.canvas.height = imgH;

                            self.c.drawImage(img, 0, 0);
                            imageData = self.c.getImageData(0, 0, imgW, imgH);
                            callback.call(self, imageData);
                        };
                        // end of vars

                        this.imgObj.onload = imageLoaded;
                        this.imgObj.crossOrigin = 'use-credentials';
                        this.imgObj.src = src;
                },

                /**
                 * @memberOf    module:Schwartz~Schwartz
                 * @method      getSymbol
                 * @private
                 *
                 * @this        {Schwartz}
                 *
                 * @param       {Number}    val     Gray scale value
                 */
                getSymbol = function( val ) {
                    var
                        asciival;
                    // end of vars

                    // console.info(val);

                    if ( isNaN(val) ) {
                        asciival = this.charSet[this.charSet.length-1];
                    } else {
                        asciival = this.charSet[(this.charSet.length * val / 256 ) | 0];
                    }

                    return asciival;
                },

                /**
                 * @memberOf    module:Schwartz~Schwartz
                 * @method      getPixelGroupAvg
                 * @private
                 *
                 * @this        {Schwartz}
                 *
                 * @param       {Object}    options
                 * @param       {Object}    options.pixels              Canvas image data
                 * @param       {Number}    options.x                   Inital x position on the canvas
                 * @param       {Number}    options.y                   Inital y position on the canvas
                 * @param       {Number}    options.stepX
                 * @param       {Number}    options.stepY
                 *
                 * @return      {Object}    res
                 * @return      {Number}    res.r                       Red channel average
                 * @return      {Number}    res.g                       Green channel average
                 * @return      {Number}    res.b                       Blue channel average
                 * @return      {Number}    res.a                       Alpha channel average
                 * @return      {Number}    res.c                       Composite average
                 */
                getPixelGroupAvg = function( options ) {
                    var
                        imgPixels = options.pixels,
                        initX     = options.x,
                        initY     = options.y,
                        stepX     = options.stepX,
                        stepY     = options.stepY,

                        count     = 0,
                        sumR      = 0,
                        sumG      = 0,
                        sumB      = 0,
                        sumA      = 0,
                        sumC      = 0,

                        r, g, b, a, c,

                        x, y,
                        index;
                    // end of vars

                    for ( x = initX; x < initX + stepX; x++ ) {
                        for ( y = initY; y < initY + stepY; y++ ) {
                            index = (y * 4) * imgPixels.width + x * 4;

                            r = imgPixels.data[index];
                            g = imgPixels.data[index + 1];
                            b = imgPixels.data[index + 2];
                            a = imgPixels.data[index + 3];
                            c = (imgPixels.data[index] + imgPixels.data[index + 1] + imgPixels.data[index + 2]) / 3;

                            if ( !isNaN(r) && !isNaN(g) && !isNaN(b) && !isNaN(a) && !isNaN(c) ) {
                                sumR += r;
                                sumG += g;
                                sumB += b;
                                sumA += a;
                                sumC += c;
                                count++;
                            }
                        }
                    }

                    return {
                        r: (sumR/count) | 0,
                        g: (sumG/count) | 0,
                        b: (sumB/count) | 0,
                        a: (sumA/count) | 0,
                        c: (sumC/count) | 0
                    };
                },

                /**
                 * @memberOf    module:Schwartz~Schwartz
                 * @method      parseImage
                 * @private
                 *
                 * @this        {Schwartz}
                 *
                 * @param       {Object}    imageData   Canvas image data
                 */
                parseImage = function( imageData ) {
                    var
                        imgW = imageData.width,
                        imgH = imageData.height,
                        art  = [],

                        strChar, str, symbol,
                        p, x, y, avg;
                    //end of vars

                    this.dimX = Math.round((imgW * 100 / this.detail)/200);
                    this.dimY = Math.round((imgH * 100 / this.detail)/200);

                    this.dimX = ( this.dimX < 1) ? 1 : this.dimX;
                    this.dimY = ( this.dimY < 1) ? 1 : this.dimY;

                    for ( y = 0; y < imgH; ) {
                        str     = [];
                        strChar = '';

                        for ( x = 0; x < imgW; ) {
                            avg = getPixelGroupAvg({
                                pixels: imageData,
                                x: x,
                                y: y,
                                stepX: this.dimX,
                                stepY: this.dimY
                            });

                            symbol  = getSymbol.call(this, avg.c);
                            strChar += symbol;
                            avg.sym = symbol;

                            str.push(avg)

                            x += this.dimX;
                        }

                        art.push(str);

                        y += this.dimY;
                    }

                    if ( typeof this.callback !== 'undefined' && typeof this.callback === 'function' ) {
                        this.callback(art);
                    }
                },

                /**
                 * @memberOf    module:Schwartz~Schwartz
                 * @method      videoTimerCallback
                 * @private
                 *
                 * @this        {Schwartz}
                 */
                videoTimerCallback = function() {
                    var
                        videoWidth  = this.video.width,
                        videoHeight = this.video.height,
                        self        = this,
                        imageData;
                    // end of vars

                    if ( this.video.paused || this.video.ended ) {
                        return;
                    }

                    this.c.drawImage(this.video, 0, 0, videoWidth, videoHeight);
                    imageData = this.c.getImageData(0, 0, videoWidth, videoHeight);
                    parseImage.call(this, imageData);

                    setTimeout(function () {
                        videoTimerCallback.call(self);
                    }, 0);
                };
            // end of vars

            /**
             * ==============================
             * ========PUBLIC METHODS========
             * ==============================
             */

            /**
             * @memberOf    module:Schwartz~Schwartz
             * @method      setDetail
             * @public
             *
             * @param       {Number}    n   Level of detail
             */
            Schwartz.prototype.setDetail = function( n ) {
                var
                    newDetail;
                // end of vars

                // validate number
                if ( isNumber(n) ) {
                    newDetail = parseInt(n, 10);

                    this.detail = ( newDetail < 1 ) ? 1 : ( newDetail > 100 ) ? 100 : newDetail;
                } else {
                    this.detail = DEFAULT_DETAIL;
                }

                return;
            };

            /**
             * @memberOf    module:Schwartz~Schwartz
             * @method      setCharSet
             * @public
             *
             * @param       {String}    str
             */
            Schwartz.prototype.setCharSet = function( str ) {
                var
                    // create local canvas
                    canvas = document.createElement('canvas'),
                    c      = canvas.getContext('2d'),
                    all    = [],

                    i, avg, imgPixels;
                // end of vars

                canvas.width  = 10;
                canvas.height = 20;
                c.fillStyle   = '#000';
                c.font        = '18px monospace';
                this.charSet  = [];

                for ( i = 0; i < str.length; i++ ) {
                    // reset canvas
                    canvas.width = canvas.width;

                    c.fillText(str[i], 0, 18);
                    imgPixels = c.getImageData(0, 0, canvas.width, canvas.height);

                    avg = getPixelGroupAvg({
                        pixels: imgPixels,
                        x: 0,
                        y: 0,
                        stepX: canvas.width,
                        stepY: canvas.height
                    });

                    all[avg.a] = str[i];
                }

                for ( i = 0; i < all.length; i++ ) {
                    if ( all[i] ) {
                        this.charSet.push(all[i]);
                    }
                }

                this.charSet = ( this.inverse ) ? this.charSet : this.charSet.reverse();
            };

            /**
             * @memberOf    module:Schwartz~Schwartz
             * @method      loadImg
             * @public
             *
             * @param       {String} src    Image source URI
             */
            Schwartz.prototype.generateFromImage = function( src ) {
                loadImage.call(this, src, parseImage);
            };

            /**
             * Inverse image
             *
             * @memberOf    module:Schwartz~Schwartz
             * @method      inverseImage
             * @public
             */
            Schwartz.prototype.inverseImage = function() {
                this.charSet.reverse();
            };

            /**
             * @memberOf    module:Schwartz~Schwartz
             * @method      loadImg
             * @public
             *
             * @param       {DOM Element}   video    Video DOM element
             */
            Schwartz.prototype.generateFromVideo = function( video ) {
                var
                    self = this;
                // end of vars

                this.video = video;

                video.addEventListener('play', function() {
                    videoTimerCallback.call(self);
                }, false);
            };

            return Schwartz;
        }());
    // end of vars

    return Schwartz;
});
<commit_msg>Video demensions bug fix.<commit_after>/**
 * Generate ACII art from image or video
 *
 * @module   Schwartz
 * @version  1.9.1
 *
 * @author   Zaytsev Alexandr
 *
 * @example
 *    // Exapmple with image
 *    var
 *        body    = document.getElementsByTagName('body')[0],
 *        outDiv  = document.createElement('div'),
 *        schwartz = new Schwartz({
 *            detail: 60
 *        });
 *    // end of vars
 *
 *    body.appendChild(outDiv);
 *    schwartz.generateFromImage('/path/to/img.jpg');
 *
 *
 *    // Example with video
 *    var
 *        body    = document.getElementsByTagName('body')[0],
 *        video   = document.getElementsByTagName('video')[0],
 *        outDiv  = document.createElement('div'),
 *
 *        schwartz = new Schwartz({
 *            inverse: true,
 *            detail: 60
 *        });
 *    // end of vars
 *
 *
 *    body.appendChild(outDiv);
 *    schwartz.generateFromVideo(video);
 */
!function( window, module ) {
    'use strict';

    var
        Schwartz = module();
        // end of vars

    // AMD
    if ( typeof define === 'function' && define.amd ) {
        define('Schwartz', [], function() {
            return Schwartz;
        });

    // YM Modules
    } else if ( typeof modules !== 'undefined' && typeof modules.define === 'function' ) {
        modules.define('Schwartz', [], function( provide ) {
            provide(Schwartz);
        });

    // Browser global
    } else {
        window.Schwartz = Schwartz;
    }
}( this, function() {
    'use strict';

    var
        Schwartz = (function() {
            var
                /**
                 * @const
                 */
                DEFAULT_DETAIL = 50,

                DEFAULT_INVERSE = false,

                /**
                 * After parse image callback
                 *
                 * @memberOf    module:Schwartz~Schwartz
                 * @callback    parseImageCallback
                 *
                 * @param       {Object[][]}  res       Art strings by line
                 * @param       {Number}      res.r     Red channel average
                 * @param       {Number}      res.g     Green channel average
                 * @param       {Number}      res.b     Blue channel average
                 * @param       {Number}      res.a     Alpha channel average
                 * @param       {Number}      res.c     Composite average
                 * @param       {Number}      res.sym   Symbol
                 */

                /**
                 * @memberOf     module:Schwartz~
                 *
                 * @constructs   Schwartz
                 *
                 * @param        {Object}               [options]
                 * @param        {Boolean}              [options.inverse=false]
                 * @param        {Number}               [options.detail=50]
                 * @param        {parseImageCallback}   [options.render]
                 */
                Schwartz = function Schwartz( options ) {
                    // enforces new
                    if ( !( this instanceof Schwartz ) ) {
                        return new Schwartz(options);
                    }

                    // constructor body
                    this.canvas        = document.createElement('canvas');
                    this.imgObj        = new Image();
                    this.c             = this.canvas.getContext('2d');

                    this.inverse       = options.inverse || DEFAULT_INVERSE;
                    this.callback      = options.render || false;

                    /**
                     * Default symbols
                     *
                     * @memberOf    module:Schwartz~Schwartz
                     * @member      loadImage
                     * @private
                     *
                     * @type        {Array}
                     */
                    this.charSet = [' ', '.', ',', ':', ';', '*', '|', '~', 'I', '1', '?', '7', '>', 'Y', 'F', '4', 'V', '#', '2', '9', '6', '8', '%', 'N', 'B', 'Q', 'M', '@', 'W'];

                    if ( options.detail ) {
                        this.setDetail(options.detail);
                    } else {
                        this.detail = DEFAULT_DETAIL;
                    }

                    this.charSet = ( this.inverse ) ? this.charSet : this.charSet.reverse();
                },

                /**
                 * ==============================
                 * ========PRIVATE METHODS=======
                 * ==============================
                 */

                /**
                 * Validate numbers
                 *
                 * @memberOf    module:Schwartz~Schwartz
                 * @method      isNumber
                 * @private
                 *
                 * @param       {*}         n
                 *
                 * @return      {Boolean}
                 */
                isNumber = function( n ) {
                    return !isNaN(parseFloat(n)) && isFinite(n);
                },

                /**
                 * After load image callback
                 *
                 * @memberOf    module:Schwartz~Schwartz
                 * @callback    loadImageCallback
                 *
                 * @param       {Object}    imageData
                 */

                /**
                 * @memberOf    module:Schwartz~Schwartz
                 * @method      loadImage
                 * @private
                 *
                 * @this        {Schwartz}
                 *
                 * @param       {String}                src
                 * @param       {loadImageCallback}     callback
                 */
                loadImage = function( src, callback ) {
                    var
                        self = this,

                        imageLoaded = function( ev ) {
                            var
                                imgW = self.imgObj.width,
                                imgH = self.imgObj.height,
                                img  = ev.target,
                                imageData;
                            // end of vars

                            self.canvas.width  = imgW;
                            self.canvas.height = imgH;

                            self.c.drawImage(img, 0, 0);
                            imageData = self.c.getImageData(0, 0, imgW, imgH);
                            callback.call(self, imageData);
                        };
                        // end of vars

                        this.imgObj.onload = imageLoaded;
                        this.imgObj.crossOrigin = 'use-credentials';
                        this.imgObj.src = src;
                },

                /**
                 * @memberOf    module:Schwartz~Schwartz
                 * @method      getSymbol
                 * @private
                 *
                 * @this        {Schwartz}
                 *
                 * @param       {Number}    val     Gray scale value
                 */
                getSymbol = function( val ) {
                    var
                        asciival;
                    // end of vars

                    // console.info(val);

                    if ( isNaN(val) ) {
                        asciival = this.charSet[this.charSet.length-1];
                    } else {
                        asciival = this.charSet[(this.charSet.length * val / 256 ) | 0];
                    }

                    return asciival;
                },

                /**
                 * @memberOf    module:Schwartz~Schwartz
                 * @method      getPixelGroupAvg
                 * @private
                 *
                 * @this        {Schwartz}
                 *
                 * @param       {Object}    options
                 * @param       {Object}    options.pixels              Canvas image data
                 * @param       {Number}    options.x                   Inital x position on the canvas
                 * @param       {Number}    options.y                   Inital y position on the canvas
                 * @param       {Number}    options.stepX
                 * @param       {Number}    options.stepY
                 *
                 * @return      {Object}    res
                 * @return      {Number}    res.r                       Red channel average
                 * @return      {Number}    res.g                       Green channel average
                 * @return      {Number}    res.b                       Blue channel average
                 * @return      {Number}    res.a                       Alpha channel average
                 * @return      {Number}    res.c                       Composite average
                 */
                getPixelGroupAvg = function( options ) {
                    var
                        imgPixels = options.pixels,
                        initX     = options.x,
                        initY     = options.y,
                        stepX     = options.stepX,
                        stepY     = options.stepY,

                        count     = 0,
                        sumR      = 0,
                        sumG      = 0,
                        sumB      = 0,
                        sumA      = 0,
                        sumC      = 0,

                        r, g, b, a, c,

                        x, y,
                        index;
                    // end of vars

                    for ( x = initX; x < initX + stepX; x++ ) {
                        for ( y = initY; y < initY + stepY; y++ ) {
                            index = (y * 4) * imgPixels.width + x * 4;

                            r = imgPixels.data[index];
                            g = imgPixels.data[index + 1];
                            b = imgPixels.data[index + 2];
                            a = imgPixels.data[index + 3];
                            c = (imgPixels.data[index] + imgPixels.data[index + 1] + imgPixels.data[index + 2]) / 3;

                            if ( !isNaN(r) && !isNaN(g) && !isNaN(b) && !isNaN(a) && !isNaN(c) ) {
                                sumR += r;
                                sumG += g;
                                sumB += b;
                                sumA += a;
                                sumC += c;
                                count++;
                            }
                        }
                    }

                    return {
                        r: (sumR/count) | 0,
                        g: (sumG/count) | 0,
                        b: (sumB/count) | 0,
                        a: (sumA/count) | 0,
                        c: (sumC/count) | 0
                    };
                },

                /**
                 * @memberOf    module:Schwartz~Schwartz
                 * @method      parseImage
                 * @private
                 *
                 * @this        {Schwartz}
                 *
                 * @param       {Object}    imageData   Canvas image data
                 */
                parseImage = function( imageData ) {
                    var
                        imgW = imageData.width,
                        imgH = imageData.height,
                        art  = [],

                        strChar, str, symbol,
                        p, x, y, avg;
                    //end of vars

                    this.dimX = Math.round((imgW * 100 / this.detail)/200);
                    this.dimY = Math.round((imgH * 100 / this.detail)/200);

                    this.dimX = ( this.dimX < 1) ? 1 : this.dimX;
                    this.dimY = ( this.dimY < 1) ? 1 : this.dimY;

                    for ( y = 0; y < imgH; ) {
                        str     = [];
                        strChar = '';

                        for ( x = 0; x < imgW; ) {
                            avg = getPixelGroupAvg({
                                pixels: imageData,
                                x: x,
                                y: y,
                                stepX: this.dimX,
                                stepY: this.dimY
                            });

                            symbol  = getSymbol.call(this, avg.c);
                            strChar += symbol;
                            avg.sym = symbol;

                            str.push(avg)

                            x += this.dimX;
                        }

                        art.push(str);

                        y += this.dimY;
                    }

                    if ( typeof this.callback !== 'undefined' && typeof this.callback === 'function' ) {
                        this.callback(art);
                    }
                },

                /**
                 * @memberOf    module:Schwartz~Schwartz
                 * @method      videoTimerCallback
                 * @private
                 *
                 * @this        {Schwartz}
                 */
                videoTimerCallback = function() {
                    var
                        videoWidth  = this.video.width,
                        videoHeight = this.video.height,
                        self        = this,
                        imageData;
                    // end of vars

                    if ( this.video.paused || this.video.ended ) {
                        return;
                    }

                    this.canvas.width  = videoWidth;
                    this.canvas.height = videoHeight;

                    this.c.drawImage(this.video, 0, 0);
                    imageData = this.c.getImageData(0, 0, videoWidth, videoHeight);
                    parseImage.call(this, imageData);

                    setTimeout(function () {
                        videoTimerCallback.call(self);
                    }, 0);
                };
            // end of vars

            /**
             * ==============================
             * ========PUBLIC METHODS========
             * ==============================
             */

            /**
             * @memberOf    module:Schwartz~Schwartz
             * @method      setDetail
             * @public
             *
             * @param       {Number}    n   Level of detail
             */
            Schwartz.prototype.setDetail = function( n ) {
                var
                    newDetail;
                // end of vars

                // validate number
                if ( isNumber(n) ) {
                    newDetail = parseInt(n, 10);

                    this.detail = ( newDetail < 1 ) ? 1 : ( newDetail > 100 ) ? 100 : newDetail;
                } else {
                    this.detail = DEFAULT_DETAIL;
                }

                return;
            };

            /**
             * @memberOf    module:Schwartz~Schwartz
             * @method      setCharSet
             * @public
             *
             * @param       {String}    str
             */
            Schwartz.prototype.setCharSet = function( str ) {
                var
                    // create local canvas
                    canvas = document.createElement('canvas'),
                    c      = canvas.getContext('2d'),
                    all    = [],

                    i, avg, imgPixels;
                // end of vars

                canvas.width  = 10;
                canvas.height = 20;
                c.fillStyle   = '#000';
                c.font        = '18px monospace';
                this.charSet  = [];

                for ( i = 0; i < str.length; i++ ) {
                    // reset canvas
                    canvas.width = canvas.width;

                    c.fillText(str[i], 0, 18);
                    imgPixels = c.getImageData(0, 0, canvas.width, canvas.height);

                    avg = getPixelGroupAvg({
                        pixels: imgPixels,
                        x: 0,
                        y: 0,
                        stepX: canvas.width,
                        stepY: canvas.height
                    });

                    all[avg.a] = str[i];
                }

                for ( i = 0; i < all.length; i++ ) {
                    if ( all[i] ) {
                        this.charSet.push(all[i]);
                    }
                }

                this.charSet = ( this.inverse ) ? this.charSet : this.charSet.reverse();
            };

            /**
             * @memberOf    module:Schwartz~Schwartz
             * @method      loadImg
             * @public
             *
             * @param       {String} src    Image source URI
             */
            Schwartz.prototype.generateFromImage = function( src ) {
                loadImage.call(this, src, parseImage);
            };

            /**
             * Inverse image
             *
             * @memberOf    module:Schwartz~Schwartz
             * @method      inverseImage
             * @public
             */
            Schwartz.prototype.inverseImage = function() {
                this.charSet.reverse();
            };

            /**
             * @memberOf    module:Schwartz~Schwartz
             * @method      loadImg
             * @public
             *
             * @param       {DOM Element}   video    Video DOM element
             */
            Schwartz.prototype.generateFromVideo = function( video ) {
                var
                    self = this;
                // end of vars

                this.video = video;

                video.addEventListener('play', function() {
                    videoTimerCallback.call(self);
                }, false);
            };

            return Schwartz;
        }());
    // end of vars

    return Schwartz;
});
<|endoftext|>


<commit_before>// stuff to do as soon as document is loadded
$(function(){
    $('.ui-slider').hide()
});

// make API request immediately
$.ajax({
    url : '/moves/summary/daily',
    xhrFields : {
        withCredentials : true
    }
}).done(function(data){

    $(document).ready( function(){ // ensure document ready

        var desiredWeeklyIncreaseRate = 1.1

        var walkToday = data.walk.distance.slice(-1)[0]
        // suggest walking distance based on average distance centered around a week ago
        var suggestedWalkToday = data.walk.distance.slice(-11,-4).reduce(function(a,b){ return a + b })/7*desiredWeeklyIncreaseRate // ignores that curve will be exponential, but resulting overshoot is minimal
        var suggestedWalkTomorrow = data.walk.distance.slice(-10,-3).reduce(function(a,b){ return a + b })/7*desiredWeeklyIncreaseRate // ignores that curve will be exponential, but resulting overshoot is minimal

        $('.walked-today').text(roundForDisplay(walkToday) + ' mi')
        $('.walk-more-today').text(roundForDisplay(Math.max(suggestedWalkToday - walkToday, 0)) + ' mi')
        $('.walk-tomorrow').text(roundForDisplay(suggestedWalkTomorrow) + ' mi')

        var walkGraphData = _.pairs(_.object(data.dates, data.walk.distance))

        var suggestedWalkGraphData = [walkGraphData.slice(-2)[0], [data.dates.slice(-1)[0], suggestedWalkToday], [data.futureDates[0], suggestedWalkTomorrow]]

        var walkGraph = $.jqplot('walk-graph',  [walkGraphData, suggestedWalkGraphData],
            {
                title: 'Daily walking distance since you started using Moves',
                axes: {
                    xaxis: {
                        renderer         : $.jqplot.DateAxisRenderer,
                        tickOptions : {
                            formatString : '%d-%b-%Y'
                        },
                        min              : data.dates[0]
                    },
                    yaxis : {
                        min : 0,
                        max : Math.ceil(Math.max.apply(null, data.walk.distance))
                    }
                },
                series : [{
                    lineWidth       : 3,
                    showMarker      : false,
                    shadow          : false,
                    rendererOptions : {
                        smooth : true
                    }
                },{
                    lineWidth       : 3,
                    linePattern     : 'dotted',
                    showMarker      : false,
                    shadow          : false,
                    rendererOptions : {
                        smooth : true
                    }
                }],
                highlighter: {
                    show                : true,
                    sizeAdjust          : 7.5,
                    tooltipAxes         : 'y',
                    useAxesFormatters   : false,
                    tooltipFormatString : '%.1f'
               }
            })

        $('#date-range-slider').slider({
            range: true,
            min: 0,
            max: 100,
            values: [ 0, 100 ],
            slide: function( event, ui ) {

            }
        });
        $('.ui-slider').show()
    })

})

function roundForDisplay(num){
    return Math.round(num*10)/10
}
<commit_msg>Date slider works<commit_after>// stuff to do as soon as document is loadded
$(function(){
    $('.ui-slider').hide()
});

// make API request immediately
$.ajax({
    url : '/moves/summary/daily',
    xhrFields : {
        withCredentials : true
    }
}).done(function(data){

    $(document).ready( function(){ // ensure document ready

        var desiredWeeklyIncreaseRate = 1.1

        var walkToday = data.walk.distance.slice(-1)[0]
        // suggest walking distance based on average distance centered around a week ago
        var suggestedWalkToday = data.walk.distance.slice(-11,-4).reduce(function(a,b){ return a + b })/7*desiredWeeklyIncreaseRate // ignores that curve will be exponential, but resulting overshoot is minimal
        var suggestedWalkTomorrow = data.walk.distance.slice(-10,-3).reduce(function(a,b){ return a + b })/7*desiredWeeklyIncreaseRate // ignores that curve will be exponential, but resulting overshoot is minimal

        $('.walked-today').text(roundForDisplay(walkToday) + ' mi')
        $('.walk-more-today').text(roundForDisplay(Math.max(suggestedWalkToday - walkToday, 0)) + ' mi')
        $('.walk-tomorrow').text(roundForDisplay(suggestedWalkTomorrow) + ' mi')

        var walkGraphData = _.pairs(_.object(data.dates, data.walk.distance))

        var suggestedWalkGraphData = [walkGraphData.slice(-2)[0], [data.dates.slice(-1)[0], suggestedWalkToday], [data.futureDates[0], suggestedWalkTomorrow]]

        var fullGraphDateRange = data.dates.concat(data.futureDates.slice(0,7))

        var walkGraph = $.jqplot('walk-graph',  [walkGraphData, suggestedWalkGraphData],
            {
                title: 'Daily walking distance since you started using Moves',
                axes: {
                    xaxis: {
                        renderer         : $.jqplot.DateAxisRenderer,
                        tickOptions : {
                            formatString : '%d-%b-%Y'
                        },
                        min              : data.dates[0],
                        max              : fullGraphDateRange.slice(-1)[0]
                    },
                    yaxis : {
                        min : 0,
                        max : Math.ceil(Math.max.apply(null, data.walk.distance))
                    }
                },
                series : [{
                    lineWidth       : 3,
                    showMarker      : false,
                    shadow          : false,
                    rendererOptions : {
                        smooth : true
                    }
                },{
                    lineWidth       : 3,
                    linePattern     : 'dotted',
                    showMarker      : false,
                    shadow          : false,
                    rendererOptions : {
                        smooth : true
                    }
                }],
                highlighter: {
                    show                : true,
                    sizeAdjust          : 7.5,
                    tooltipAxes         : 'y',
                    useAxesFormatters   : false,
                    tooltipFormatString : '%.1f'
               }
            })

        $('#date-range-slider').slider({
            range: true,
            min: 0,
            max: 100,
            values: [ 0, 100 ],
            slide: function( event, ui ) {
                walkGraph.replot({
                    axes: {
                        xaxis : {
                            min : data.dates[ui.values[0]],
                            max : fullGraphDateRange[Math.ceil(ui.values[1]/100*fullGraphDateRange.length)]
                        }
                    }
                });
            }
        });
        $('.ui-slider').show()
    })

})

function roundForDisplay(num){
    return Math.round(num*10)/10
}
<|endoftext|>