<commit_before>var gulp = require('gulp'),
    sass = require('gulp-sass'),
    rename = require('gulp-rename'),
    cssnano = require('gulp-cssnano'),
    kss = require('kss'),
    sourcemaps = require('gulp-sourcemaps'),
    scsslint = require('gulp-scss-lint'),
    gitVersion = require('gulp-gitversion');

var paths = {
    scss: './assets/sass/style.scss',
    output: './build/'
};

gulp.task('lint', function () {
    return gulp.src(['./assets/sass/**/*.scss', '!./assets/sass/vendor/**/*.scss'])
        .pipe(scsslint({
            'config': '.scss-lint.yml',
            'reporterOutputFormat': 'Checkstyle',
            'filePipeOutput': 'scssReport.xml'
        }))
        .pipe(gulp.dest(
            (typeof process.env.CIRCLE_TEST_REPORTS != 'undefined') ?
                process.env.CIRCLE_TEST_REPORTS : paths.output))
        .pipe(scsslint.failReporter('E'))
});

gulp.task('styles', function () {
    return gulp.src(paths.scss)
        .pipe(sass().on('error', sass.logError))
        .pipe(gitVersion())
        .pipe(gulp.dest(paths.output));
});

gulp.task('styles.min', function () {
    return gulp.src(paths.scss)
        .pipe(sourcemaps.init())
        .pipe(sass().on('error', sass.logError))
        .pipe(cssnano())
        .pipe(gitVersion())
        .pipe(rename({
            suffix: '.min'
        }))
        .pipe(sourcemaps.write('.'))
        .pipe(gulp.dest(paths.output));
});
gulp.task('examples', function () {
    return gulp.src('examples/*').pipe(gulp.dest(paths.output + "/examples"));
});
gulp.task('styleguide', function () {
    return kss({
        source: 'assets/sass',
        css: 'style.css',
        destination: paths.output,
        homepage: '../../README.md',
        builder: 'kss-builder'
    });
});

gulp.task('default', function () {
    gulp.start('styles');
});

gulp.task('build', function () {
    gulp.start(['lint', 'styles', 'styles.min', 'examples', 'styleguide']);
});

gulp.task('watch', function () {
    gulp.watch(paths.scss, ['styles']);
});

gulp.task('build.watch', function () {
    gulp.watch(paths.scss, ['build']);
});
<commit_msg>Switch kss and examples as site root for deployed URL<commit_after>var gulp = require('gulp'),
    sass = require('gulp-sass'),
    rename = require('gulp-rename'),
    cssnano = require('gulp-cssnano'),
    kss = require('kss'),
    sourcemaps = require('gulp-sourcemaps'),
    scsslint = require('gulp-scss-lint'),
    gitVersion = require('gulp-gitversion');

var paths = {
    scss: './assets/sass/style.scss',
    output: './build/'
};

gulp.task('lint', function () {
    return gulp.src(['./assets/sass/**/*.scss', '!./assets/sass/vendor/**/*.scss'])
        .pipe(scsslint({
            'config': '.scss-lint.yml',
            'reporterOutputFormat': 'Checkstyle',
            'filePipeOutput': 'scssReport.xml'
        }))
        .pipe(gulp.dest(
            (typeof process.env.CIRCLE_TEST_REPORTS != 'undefined') ?
                process.env.CIRCLE_TEST_REPORTS : paths.output))
        .pipe(scsslint.failReporter('E'))
});

gulp.task('styles', function () {
    return gulp.src(paths.scss)
        .pipe(sourcemaps.init())
        .pipe(sass().on('error', sass.logError))
        .pipe(gitVersion())
        .pipe(sourcemaps.write('.'))
        .pipe(gulp.dest(paths.output));
});

gulp.task('styles.min', function () {
    return gulp.src(paths.scss)
        .pipe(sourcemaps.init())
        .pipe(sass().on('error', sass.logError))
        .pipe(cssnano())
        .pipe(gitVersion())
        .pipe(rename({
            suffix: '.min'
        }))
        .pipe(sourcemaps.write('.'))
        .pipe(gulp.dest(paths.output));
});
gulp.task('examples', function () {
    return gulp.src('examples/*').pipe(gulp.dest(paths.output));
});
gulp.task('styleguide', function () {
    return kss({
        source: 'assets/sass',
        css: 'style.css',
        destination: paths.output+'/kss',
        homepage: '../../README.md',
        builder: 'kss-builder'
    });
});

gulp.task('default', function () {
    gulp.start('styles');
});

gulp.task('build', function () {
    gulp.start(['lint', 'styles', 'styles.min', 'examples', 'styleguide']);
});

gulp.task('watch', function () {
    gulp.watch(paths.scss, ['styles']);
});

gulp.task('build.watch', function () {
    gulp.watch(paths.scss, ['build']);
});
<|endoftext|>


<commit_before>var htmlparser = require('./vendor/htmlparser2')
var entities = require('./vendor/entities')
var React = require('react-native')
var reactNativeGrid = require('react-native-grid');
var {
  LinkingIOS,
  StyleSheet,
  Text,
  View,
  Image,
  PixelRatio,
} = React

var LINE_BREAK = '\n'
var PARAGRAPH_BREAK = ''
var BULLET = '\u2022 '
var CIRCLEBULLET = '\u29BF '
var WHITEBULLET = '\u25E6 '
var TRIANGLEBULLET = '\u2023 '

function htmlToElement(rawHtml, opts, done) {
  function domToElement(dom, parent) {
    if (!dom) return null

    return dom.map((node, index, list) => {
      if (opts.customRenderer) {
        var rendered = opts.customRenderer(node, index, list)
        if (rendered || rendered === null) return rendered
      }

      if (node.type == 'text') {
        if(node.data == "\n")
          return (<Text key={index} style={{fontSize: 8}}>{node.data}</Text>);

        return (
          <Text key={index} style={parent ? opts.styles[parent.name] : null}>
            {entities.decodeHTML(node.data)}
          </Text>
        )
      }

      if (node.type == 'tag') {
        var linkPressHandler = null
        if (node.name == 'a' && node.attribs && node.attribs.href) {
          linkPressHandler = () => opts.linkHandler(entities.decodeHTML(node.attribs.href))
        }

        if (node.name == 'img') {
          return(
            <Image
              key={node.attribs.alt}
              style={{width: 400, height: 400}} //need to get correct image size
              resizeMode={Image.resizeMode.contain}
              source={{uri: node.attribs.src}} />
          )
        }

        var preListString = "";
        if(node.name == 'li') {
          //go back up tree to find how many 'ul' tags there are
          //where number of UL tags = indentation level

          var tempNode = node;
          var numIndentationLevels = 0;
          while(tempNode.parent != null) {
            if(tempNode.parent.name == 'ul') {
              numIndentationLevels = numIndentationLevels + 1;
              preListString = "    " + preListString;
            }

            tempNode = tempNode.parent;
          }

          switch(numIndentationLevels) {
            case 4:
              preListString = preListString + TRIANGLEBULLET;
              break;
            case 3:
              preListString = preListString + CIRCLEBULLET;
              break;
            case 2:
              preListString = preListString + WHITEBULLET;
              break;
            default:
              preListString = preListString + BULLET;
          }
        }

        if(node.name == 'p') {
          //This is the place to get non-image Component types into the ScrollView
          if(node.children[0].name == 'img') {
            return(
             <View  key={index}
                    onPress={linkPressHandler}
                    style={{backgroundColor: '#ccffe6'}} >
              {domToElement(node.children, node)}
            </View>
            )
          }
        }

        if(node.name == 'hr')
          return( <View style={ opts.styles.hr } /> )

        return (
          <Text key={index}
                onPress={linkPressHandler}
                style={{backgroundColor: '#ffe5e5'}} >
            {node.name == 'pre' ? LINE_BREAK : null}
            {node.name == 'li' ? preListString : null}
            {domToElement(node.children, node)}
            {node.name == 'br' ? LINE_BREAK : null}
           </Text>
        )
      }
    })
  }

  var handler = new htmlparser.DomHandler(function (err, dom) {
    if (err) done(err)
    done(null, domToElement(dom))
  })
  var parser = new htmlparser.Parser(handler)
  parser.write(rawHtml)
  parser.done()
}

var HTMLView = React.createClass({
  mixins: [
    React.addons.PureRenderMixin,
  ],
  getDefaultProps() {
    return {
      onLinkPress: LinkingIOS.openURL,
    }
  },
  getInitialState() {
    return {
      element: null,
    }
  },
  componentWillReceiveProps() {
    if (this.state.element) return
    this.startHtmlRender()
  },
  componentDidMount() {
    this.startHtmlRender()
  },
  startHtmlRender() {
    if (!this.props.value) return
    if (this.renderingHtml) return

    var opts = {
      linkHandler: this.props.onLinkPress,
      styles: Object.assign({}, baseStyles, this.props.stylesheet),
      customRenderer: this.props.renderNode,
    }

    this.renderingHtml = true
    htmlToElement(this.props.value, opts, (err, element) => {
      this.renderingHtml = false

      if (err) return (this.props.onError || console.error)(err)

      if (this.isMounted()) this.setState({element})
    })
  },
  render() {
    if (this.state.element) {
      return <View children={this.state.element} />
    }
    return <View />
  }
})

var boldStyle = {fontWeight: '500'}
var italicStyle = {fontStyle: 'italic'}
var codeStyle = {fontFamily: 'Menlo'}

var baseStyles = StyleSheet.create({
  b: boldStyle,
  strong: boldStyle,
  i: italicStyle,
  em: italicStyle,
  pre: codeStyle,
  code: codeStyle,
  a: {
    fontWeight: '500',
    color: '#007AFF',
  },
  hr: {
    backgroundColor: 'rgba(0, 0, 0, 0.2)',
    height: 1 / PixelRatio.get(),
    marginLeft: 0,
    marginRight: 0,
  }
})

module.exports = HTMLView
<commit_msg>implemented rudimentary html tables<commit_after>var htmlparser = require('./vendor/htmlparser2')
var entities = require('./vendor/entities')
var React = require('react-native')
var reactNativeGrid = require('react-native-grid');
var { Grid,Col } = reactNativeGrid;

var {
  LinkingIOS,
  StyleSheet,
  Text,
  View,
  Image,
  PixelRatio,
} = React

var LINE_BREAK = '\n'
var PARAGRAPH_BREAK = ''
var BULLET = '\u2022 '
var CIRCLEBULLET = '\u29BF '
var WHITEBULLET = '\u25E6 '
var TRIANGLEBULLET = '\u2023 '

function htmlToElement(rawHtml, opts, done) {
  function domToElement(dom, parent) {
    if (!dom) return null

    return dom.map((node, index, list) => {
      if (opts.customRenderer) {
        var rendered = opts.customRenderer(node, index, list)
        if (rendered || rendered === null) return rendered
      }

      if (node.type == 'text') {
        if(node.data == "\n")
          return (<Text key={index} style={{fontSize: 8}}>{node.data}</Text>);

        return (
          <Text key={index} style={parent ? opts.styles[parent.name] : null}>
            {entities.decodeHTML(node.data)}
          </Text>
        )
      }

      if (node.type == 'tag') {
        var linkPressHandler = null
        if (node.name == 'a' && node.attribs && node.attribs.href) {
          linkPressHandler = () => opts.linkHandler(entities.decodeHTML(node.attribs.href))
        }

        if(node.name == 'table') {
          //find number of rows

          var thead = node.children.filter((child) => {return child.name === 'thead'});
          thead = thead[0];
          var tbody = node.children.filter((child) => {return child.name === 'tbody'});
          tbody = tbody[0];

          //find number of columns
          console.log(thead);
          var tr = thead.children.filter((child) => {
            if(child.type === 'tag')
              return child.name === 'tr';
          });
          tr = tr[0];

          var th = tr.children.filter((child) => {
            if(child.type === 'tag')
              return child.name === 'th';
          });

          var numColumns = th.length;
          var colSpan = 24 / numColumns; // react-native-grid arbitrarily defines 100% width to be 24 'units'

          console.log("numColumns: " + numColumns);
          console.log("colSpan: " + colSpan);

          var finalCells = [];

          th.map((thItem) => {
            finalCells.push({
              children: thItem.children,
              align: thItem.attribs.style,
              index: finalCells.length,
              weight: 'bold',
            })
          })

          var tr = tbody.children.filter((child) => {
            if(child.type === 'tag')
              return child.name === 'tr';
          })

          tr.map((trItem) => {
            trItem.children.map((child) => {
              if(child.type === 'tag')
                if(child.name === 'td') {
                  finalCells.push({
                    children: child.children,
                    align: child.attribs.style,
                    index: finalCells.length,
                    weight: 'normal',
                  })
                }
            })
          })

          return (
            <Grid key={index}
                  style={{paddingLeft: 50, paddingRight: 50}} >
              {finalCells.map((cell) => {
                var textAlignString = 'left';

                if(cell.align !== undefined) {
                  textAlignString = cell.align.split(':');
                  textAlignString = textAlignString.slice(textAlignString.length - 1, textAlignString.length);
                  textAlignString = textAlignString[0];
                }

                var borderTopWidth = null;
                if(cell.index < numColumns)
                  borderTopWidth = 0;

                return(
                  <Col  key={cell.text + String(cell.index)}
                        span={colSpan}
                        style={{borderTopWidth: borderTopWidth, borderBottomWidth: 1,
                                borderColor: 'gray',
                                padding: 5 }}>
                    <Text style={{textAlign: textAlignString,
                                  fontWeight: cell.weight}} >
                      {domToElement(cell.children, cell)}
                    </Text>
                  </Col>
                );
              })}
            </Grid>
          )
        }

        if (node.name == 'img') {
          return(
            <Image
              key={node.attribs.alt}
              style={{width: 400, height: 400}} //need to get correct image size
              resizeMode={Image.resizeMode.contain}
              source={{uri: node.attribs.src}} />
          )
        }

        var preListString = "";
        if(node.name == 'li') {
          //go back up tree to find how many 'ul' tags there are
          //where number of UL tags = indentation level

          var tempNode = node;
          var numIndentationLevels = 0;
          while(tempNode.parent != null) {
            if(tempNode.parent.name == 'ul') {
              numIndentationLevels = numIndentationLevels + 1;
              preListString = "    " + preListString;
            }

            tempNode = tempNode.parent;
          }

          switch(numIndentationLevels) {
            case 4:
              preListString = preListString + TRIANGLEBULLET;
              break;
            case 3:
              preListString = preListString + CIRCLEBULLET;
              break;
            case 2:
              preListString = preListString + WHITEBULLET;
              break;
            default:
              preListString = preListString + BULLET;
          }
        }

        if(node.name == 'p') {
          //This is the place to get non-image Component types into the ScrollView
          if(node.children[0].name == 'img') {
            return(
             <View  key={index}
                    onPress={linkPressHandler}
                    style={{backgroundColor: '#ccffe6'}} >
              {domToElement(node.children, node)}
            </View>
            )
          }
        }

        if(node.name == 'hr')
          return( <View style={ opts.styles.hr } /> )

        return (
          <Text key={index}
                onPress={linkPressHandler}
                style={{backgroundColor: '#ffe5e5'}} >
            {node.name == 'pre' ? LINE_BREAK : null}
            {node.name == 'li' ? preListString : null}
            {domToElement(node.children, node)}
            {node.name == 'br' ? LINE_BREAK : null}
           </Text>
        )
      }
    })
  }

  var handler = new htmlparser.DomHandler(function (err, dom) {
    if (err) done(err)
    done(null, domToElement(dom))
  })
  var parser = new htmlparser.Parser(handler)
  parser.write(rawHtml)
  parser.done()
}

var HTMLView = React.createClass({
  mixins: [
    React.addons.PureRenderMixin,
  ],
  getDefaultProps() {
    return {
      onLinkPress: LinkingIOS.openURL,
    }
  },
  getInitialState() {
    return {
      element: null,
    }
  },
  componentWillReceiveProps() {
    if (this.state.element) return
    this.startHtmlRender()
  },
  componentDidMount() {
    this.startHtmlRender()
  },
  startHtmlRender() {
    if (!this.props.value) return
    if (this.renderingHtml) return

    var opts = {
      linkHandler: this.props.onLinkPress,
      styles: Object.assign({}, baseStyles, this.props.stylesheet),
      customRenderer: this.props.renderNode,
    }

    this.renderingHtml = true
    htmlToElement(this.props.value, opts, (err, element) => {
      this.renderingHtml = false

      if (err) return (this.props.onError || console.error)(err)

      if (this.isMounted()) this.setState({element})
    })
  },
  render() {
    if (this.state.element) {
      return <View children={this.state.element} />
    }
    return <View />
  }
})

var boldStyle = {fontWeight: '500'}
var italicStyle = {fontStyle: 'italic'}
var codeStyle = {fontFamily: 'Menlo'}

var baseStyles = StyleSheet.create({
  b: boldStyle,
  strong: boldStyle,
  i: italicStyle,
  em: italicStyle,
  pre: codeStyle,
  code: codeStyle,
  a: {
    fontWeight: '500',
    color: '#007AFF',
  },
  hr: {
    backgroundColor: 'rgba(0, 0, 0, 0.2)',
    height: 1 / PixelRatio.get(),
    marginLeft: 0,
    marginRight: 0,
  }
})

module.exports = HTMLView
<|endoftext|>


<commit_before>(function(){"use strict";var Hook=function(hookEvent){var _this=this;var _cache={};var _defaultTimeout=6e4;var _hookHandlers={};var _genericHookHandler=[];var _helloHandler;var _hookValidator;var _bindSocket;var BASE=hookEvent||"_hook";var HOOK_REQUEST=BASE+":request";var HOOK_RESPONSE=BASE+":response";var HELLO_REQUEST=BASE+":hello:request";var HELLO_RESPONSE=BASE+":hello:response";var TIMEOUT=this.TIMEOUT="TIMEOUT";var INVALID_MESSAGE=this.INVALID_MESSAGE="INVALID_MESSAGE";var NO_HANDLERS=this.NO_HANDLERS="NO_HANDLERS";var NO_SOCKET_BIND=this.NO_SOCKET_BIND="NO_SOCKET_BIND";var INTERNAL_ERROR=this.INTERNAL_ERROR="INTERNAL_ERROR";var onHookResponse=function(message){var rand=message&&message.rand;if(!rand){return}if(!_cache[rand]){return}var cache=_cache[rand];if(cache.active){if(message.error){cache.reject(message.error)}else{cache.resolve(message.body)}}};var onRemoteHook=function(socket,message){var rand=message&&message.rand;return Promise.resolve().then(function(){if(message&&message.isInvalid){throw _this.INVALID_MESSAGE}if(!message){throw _this.INVALID_MESSAGE}if(!rand){throw _this.INVALID_MESSAGE}var action=message.action;if(!action){throw _this.INVALID_MESSAGE}var promise=Promise.resolve(message.body);if(!_hookHandlers[action]&&!_genericHookHandler.length){console.warn("No handler was registered for '%s' action. ignoring message",action);throw _this.NO_HANDLERS}var args=[message.body,socket];if(_hookValidator){promise=promise.then(function(body){return new Promise(function(resolve,reject){try{_hookValidator(action,body,socket,function(){var extraArgs=Array.prototype.slice.call(arguments);args=args.concat(extraArgs);resolve()})}catch(err){reject(err)}})})}var userResponse;if(_genericHookHandler.length){promise=promise.then(function(){var chain=Promise.resolve();var argsArray=[action].concat(args);_genericHookHandler.forEach(function(handler){chain=chain.then(function(){return handler.apply(null,argsArray)}).then(function(result){if(result){userResponse=result}})});return chain})}if(_hookHandlers[action]){promise=promise.then(function(){var chain=Promise.resolve();_hookHandlers[action].forEach(function(handler){chain=chain.then(function(){return handler.apply(null,args)}).then(function(result){if(result){userResponse=result}})});return chain})}return promise.then(function(result){return{body:userResponse||result}})}).catch(function(err){var error=err;if(err instanceof Error){error=_this.INTERNAL_ERROR;console.error("Error while processing RemoteHook: \n"+(err.stack||err.message||err))}return{error:error}}).then(function(response){if(rand){response.rand=rand;socket.emit(HOOK_RESPONSE,response)}})};var onRemoteHello=function(socket,message){var args=Array.prototype.slice.call(arguments);socket=args.shift();message=args.shift();var rand=message&&message.rand;var promise=Promise.resolve({body:{}});promise=promise.then(function(){if(!_helloHandler){throw NO_HANDLERS}return _helloHandler(message.body,socket)});return promise.then(function(body){return{body:body}}).catch(function(err){var error=err;if(err instanceof Error){error=_this.INTERNAL_ERROR;console.error("Error while processing RemoteHook: \n"+(err.stack||err.message||err))}return{error:error}}).then(function(response){if(rand){response.rand=rand;socket.emit(HOOK_RESPONSE,response)}})};var emit=function(socket,event,action,body,timeout){var rand;while(!rand){var temp=Math.random().toString(36).substr(2);if(!_cache[rand]){rand=temp}}var cache=_cache[rand]={active:true};return new Promise(function(resolve,reject){cache.resolve=function(data){if(cache.active){cache.active=false;clearTimeout(cache.timer);delete _cache[rand];resolve(data)}};cache.reject=function(err){if(cache.active){cache.active=false;clearTimeout(cache.timer);delete _cache[rand];reject(err)}};cache.active=true;if(timeout!==0){cache.timer=setTimeout(function(){cache.active&&cache.reject(_this.TIMEOUT)},timeout||_defaultTimeout)}var message={rand:rand,action:action,body:body};socket.emit(event,message)})};this.onHello=function(handler){if(_helloHandler){console.error("called onHello more than once")}if(!handler||typeof handler!="function"){console.warn("Invalid handler for 'onHello' method. must be a function. [%s]",typeof handler)}else{_helloHandler=handler}return _this};this.onHook=function(handler){if(_hookValidator){console.error("called onHook more than once")}else{_hookValidator=handler}return _this};this.bindSocket=function(socket){if(socket&&typeof socket=="object"){_bindSocket=socket}else{console.warn("Hook#bindSocket - Invalid socket",socket)}return _this};this.attach=function(socket){socket.on(HELLO_REQUEST,function(message){onRemoteHello(socket,message)});socket.on(HELLO_RESPONSE,onHookResponse);socket.on(HOOK_REQUEST,function(message){onRemoteHook(socket,message)});socket.on(HOOK_RESPONSE,onHookResponse)};this.on=function(action,handler){switch(action&&typeof action){case"function":_genericHookHandler.push(action);break;case"string":if(!handler||typeof handler!="function"){console.warn("An handler is must be provided when calling 'on' method. ({string} action, {function} handler)");break}if(!_hookHandlers[action]){_hookHandlers[action]=[]}_hookHandlers[action].push(handler);break;case"object":Object.keys(action).forEach(function(key){if(!action[key]||typeof action[key]!="function"){console.warn("An handler is must be provided for each action when calling 'on method'. ({string} action, {function} handler)");return}if(!_hookHandlers[key]){_hookHandlers[key]=[]}_hookHandlers[key].push(action[key])});break;default:console.warn("Called 'commands' property with invalid argument [%s] %s",typeof action,action);break}return _this};this.sendTo=function(socket,action,body,timeout){return emit(socket,HOOK_REQUEST,action,body,timeout)};this.send=function(action,body,timeout){if(!_bindSocket)return Promise.reject(NO_SOCKET_BIND);return emit(_bindSocket,HOOK_REQUEST,action,body,timeout)};this.sendHelloTo=function(socket,body){return emit(socket,HELLO_REQUEST,"hello",body)};this.sendHello=function(body){if(!_bindSocket)return Promise.reject(NO_SOCKET_BIND);return emit(_bindSocket,HELLO_REQUEST,"hello",body)}};if(typeof exports!="undefined"){if(typeof module!="undefined"&&module.exports){exports=module.exports=Hook}else{exports.Hook=Hook}}else{window.Hook=Hook}}).call(this);
<commit_msg>reminified the latest version<commit_after>(function(){"use strict";var Hook=function(hookEvent){var _this=this;var _cache={};var _defaultTimeout=6e4;var _hookHandlers={};var _genericHookHandler=[];var _helloHandler;var _hookValidator;var _bindSocket;var BASE=hookEvent||"_hook";var HOOK_REQUEST=BASE+":request";var HOOK_RESPONSE=BASE+":response";var HELLO_REQUEST=BASE+":hello:request";var HELLO_RESPONSE=BASE+":hello:response";var TIMEOUT=this.TIMEOUT="TIMEOUT";var INVALID_MESSAGE=this.INVALID_MESSAGE="INVALID_MESSAGE";var NO_HANDLERS=this.NO_HANDLERS="NO_HANDLERS";var NO_SOCKET_BIND=this.NO_SOCKET_BIND="NO_SOCKET_BIND";var INTERNAL_ERROR=this.INTERNAL_ERROR="INTERNAL_ERROR";var onHookResponse=function(message){var rand=message&&message.rand;if(!rand){return}if(!_cache[rand]){return}var cache=_cache[rand];if(cache.active){if(message.error){cache.reject(message.error)}else{cache.resolve(message.body)}}};var onRemoteHook=function(socket,message){var rand=message&&message.rand;return Promise.resolve().then(function(){if(message&&message.isInvalid){throw _this.INVALID_MESSAGE}if(!message){throw _this.INVALID_MESSAGE}if(!rand){throw _this.INVALID_MESSAGE}var action=message.action;if(!action){throw _this.INVALID_MESSAGE}var promise=Promise.resolve(message.body);if(!_hookHandlers[action]&&!_genericHookHandler.length){console.warn("No handler was registered for '%s' action. ignoring message",action);throw _this.NO_HANDLERS}var args=[message.body,socket];if(_hookValidator){promise=promise.then(function(body){return new Promise(function(resolve,reject){try{_hookValidator(action,body,socket,function(){var extraArgs=Array.prototype.slice.call(arguments);args=args.concat(extraArgs);resolve()})}catch(err){reject(err)}})})}var userResponse;if(_genericHookHandler.length){promise=promise.then(function(){var chain=Promise.resolve();var argsArray=[action].concat(args);_genericHookHandler.forEach(function(handler){chain=chain.then(function(){return handler.apply(null,argsArray)}).then(function(result){if(result){userResponse=result}})});return chain})}if(_hookHandlers[action]){promise=promise.then(function(){var chain=Promise.resolve();_hookHandlers[action].forEach(function(handler){chain=chain.then(function(){return handler.apply(null,args)}).then(function(result){if(result){userResponse=result}})});return chain})}return promise.then(function(result){return{body:userResponse||result}})}).catch(function(err){var error=err;if(err instanceof Error){error=_this.INTERNAL_ERROR;console.error("Error while processing RemoteHook: \n"+(err.stack||err.message||err))}return{error:error}}).then(function(response){if(rand){response.rand=rand;socket.emit(HOOK_RESPONSE,response)}})};var onRemoteHello=function(socket,message){var args=Array.prototype.slice.call(arguments);socket=args.shift();message=args.shift();var rand=message&&message.rand;var promise=Promise.resolve({body:{}});promise=promise.then(function(){if(!_helloHandler){throw NO_HANDLERS}return _helloHandler(message.body,socket)});return promise.then(function(body){return{body:body}}).catch(function(err){var error=err;if(err instanceof Error){error=_this.INTERNAL_ERROR;console.error("Error while processing RemoteHook: \n"+(err.stack||err.message||err))}return{error:error}}).then(function(response){if(rand){response.rand=rand;socket.emit(HOOK_RESPONSE,response)}})};var emit=function(socket,event,action,body,timeout){var rand;while(!rand){var temp=Math.random().toString(36).substr(2);if(!_cache[rand]){rand=temp}}var cache=_cache[rand]={active:true};return new Promise(function(resolve,reject){cache.resolve=function(data){if(cache.active){cache.active=false;clearTimeout(cache.timer);delete _cache[rand];resolve(data)}};cache.reject=function(err){if(cache.active){cache.active=false;clearTimeout(cache.timer);delete _cache[rand];reject(err)}};cache.active=true;if(timeout!==0){cache.timer=setTimeout(function(){cache.active&&cache.reject(_this.TIMEOUT)},timeout||_defaultTimeout)}var message={rand:rand,action:action,body:body};socket.emit(event,message)})};this.onHello=function(handler){if(_helloHandler){console.error("called onHello more than once")}if(!handler||typeof handler!="function"){console.warn("Invalid handler for 'onHello' method. must be a function. [%s]",typeof handler)}else{_helloHandler=handler}return _this};this.onHook=function(handler){if(_hookValidator){console.error("called onHook more than once")}else{_hookValidator=handler}return _this};this.bindSocket=function(socket){if(socket&&typeof socket=="object"){_bindSocket=socket}else{console.warn("Hook#bindSocket - Invalid socket",socket)}return _this};this.attach=function(socket){socket.on(HELLO_REQUEST,function(message){onRemoteHello(socket,message)});socket.on(HELLO_RESPONSE,onHookResponse);socket.on(HOOK_REQUEST,function(message){onRemoteHook(socket,message)});socket.on(HOOK_RESPONSE,onHookResponse);return _this};this.on=function(action,handler){switch(action&&typeof action){case"function":_genericHookHandler.push(action);break;case"string":if(!handler||typeof handler!="function"){console.warn("An handler is must be provided when calling 'on' method. ({string} action, {function} handler)");break}if(!_hookHandlers[action]){_hookHandlers[action]=[]}_hookHandlers[action].push(handler);break;case"object":Object.keys(action).forEach(function(key){if(!action[key]||typeof action[key]!="function"){console.warn("An handler is must be provided for each action when calling 'on method'. ({string} action, {function} handler)");return}if(!_hookHandlers[key]){_hookHandlers[key]=[]}_hookHandlers[key].push(action[key])});break;default:console.warn("Called 'commands' property with invalid argument [%s] %s",typeof action,action);break}return _this};this.sendTo=function(socket,action,body,timeout){return emit(socket,HOOK_REQUEST,action,body,timeout)};this.send=function(action,body,timeout){if(!_bindSocket)return Promise.reject(NO_SOCKET_BIND);return emit(_bindSocket,HOOK_REQUEST,action,body,timeout)};this.sendHelloTo=function(socket,body){return emit(socket,HELLO_REQUEST,"hello",body)};this.sendHello=function(body){if(!_bindSocket)return Promise.reject(NO_SOCKET_BIND);return emit(_bindSocket,HELLO_REQUEST,"hello",body)}};if(typeof exports!="undefined"){if(typeof module!="undefined"&&module.exports){exports=module.exports=Hook}else{exports.Hook=Hook}}else{window.Hook=Hook}}).call(this);
<|endoftext|>


<commit_before>var Physics = function() {
    PIXELS_PER_METER = 16;
    return function(context, gameWidth, gameHeight) {
        function newDebugDraw() {
            var debugDraw = new b2DebugDraw();
            debugDraw.SetSprite(document.getElementById("sup").getContext("2d"));
            debugDraw.SetDrawScale(PIXELS_PER_METER);
            debugDraw.SetFillAlpha(0.5);
            debugDraw.SetLineThickness(1.0);
            debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);
            return debugDraw;
        }

        var b2Vec2 = Box2D.Common.Math.b2Vec2;
        var b2BodyDef = Box2D.Dynamics.b2BodyDef;
        var b2Body = Box2D.Dynamics.b2Body;
        var b2FixtureDef = Box2D.Dynamics.b2FixtureDef;
        var b2Fixture = Box2D.Dynamics.b2Fixture;
        var b2World = Box2D.Dynamics.b2World;
        var b2MassData = Box2D.Collision.Shapes.b2MassData;
        var b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape;
        var b2CircleShape = Box2D.Collision.Shapes.b2CircleShape;
        var b2DebugDraw = Box2D.Dynamics.b2DebugDraw;

        var that = this;
        var gameWidth = gameWidth;
        var gameHeight = gameHeight;
        var turnRate = 0;
        var targetRotation = 0;
        var rotation = 0;

        Constants.get("world_rotation_rate", function(value) {
            turnRate = value;
        });

        var debugDraw = newDebugDraw();

        this.world = new b2World(newGravity(), false);
        this.world.SetDebugDraw(debugDraw);

        newWorld();

        this.newBlock = function(stage) {
            var fd                 = new b2FixtureDef;
            fd.shape               = new b2PolygonShape();
            fd.shape.SetAsBox(1,1);

            var bodyDef                  = new b2BodyDef();
            bodyDef.type                 = b2Body.b2_dynamicBody;
            bodyDef.position.x           = 150/PIXELS_PER_METER;
            bodyDef.position.y           = 150/PIXELS_PER_METER;
            var body = that.world.CreateBody(bodyDef);
            body.CreateFixture(fd);

            var e = new Entity();
            console.log(Sprites.block);
            Constants.get("block_image", function(value) {
                var beeTexture = PIXI.Texture.fromImage(value, true);
                var beeSprite = new PIXI.Sprite(beeTexture);

                beeSprite.anchor.x = 0.5;
                beeSprite.anchor.y = 0.5;

                e.addComponent(SpriteComponent(stage, beeSprite));
                e.addComponent(PhysicsComponent(body));
                console.log("here");
                World.add(e);
            });
        }

        this.draw = function() {
            that.world.DrawDebugData();
        }

        this.update = function() {
            if (Math.abs(targetRotation - rotation) > turnRate) {
                function sign(x) { return x ? x < 0 ? -1 : 1 : 0; }
                direction = sign(targetRotation - rotation);
                console.log(direction);
                rotation += direction * turnRate;
                that.world.SetGravity(newGravity());
            }

            that.world.Step(3,3);
        }

        this.getRotation = function() {
            return rotation;
        }

        this.setTargetRotation = function(rotation) {
            targetRotation = rotation;
        }

        function newGravity() {
            var magnitude = PIXELS_PER_METER*1;
            var xComponent = magnitude * Math.sin(rotation *  (Math.PI/180));
            var yComponent = magnitude * Math.cos(rotation * (Math.PI/180));
            return new b2Vec2(xComponent, yComponent);
        }

        function addWall(x, y, width, height) {
            //floor
            var floorDef                 = new b2FixtureDef;
            floorDef.shape               = new b2PolygonShape();
            floorDef.shape.SetAsBox(width/2, height/2);

            var floorBodyDef                  = new b2BodyDef();
            floorBodyDef.type                 = b2Body.b2_staticBody;
            floorBodyDef.position.x           = x/PIXELS_PER_METER;
            floorBodyDef.position.y           = y/PIXELS_PER_METER;
            that.world.CreateBody(floorBodyDef).CreateFixture(floorDef);
        }

        function newWorld() {
            addWall(gameWidth/2, 0, 10000, 3);
            addWall(gameWidth/2, gameHeight, 10000, 3);
            addWall(0, gameHeight/2, 3, 100000);
            addWall(gameWidth, gameHeight/2, 3, 100000);
        }

    }
}();
<commit_msg>Fix physics<commit_after>var Physics = function() {
    PIXELS_PER_METER = 16;
    return function(context, gameWidth, gameHeight) {
        function newDebugDraw() {
            var debugDraw = new b2DebugDraw();
            debugDraw.SetSprite(document.getElementById("sup").getContext("2d"));
            debugDraw.SetDrawScale(PIXELS_PER_METER);
            debugDraw.SetFillAlpha(0.5);
            debugDraw.SetLineThickness(1.0);
            debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);
            return debugDraw;
        }

        var b2Vec2 = Box2D.Common.Math.b2Vec2;
        var b2BodyDef = Box2D.Dynamics.b2BodyDef;
        var b2Body = Box2D.Dynamics.b2Body;
        var b2FixtureDef = Box2D.Dynamics.b2FixtureDef;
        var b2Fixture = Box2D.Dynamics.b2Fixture;
        var b2World = Box2D.Dynamics.b2World;
        var b2MassData = Box2D.Collision.Shapes.b2MassData;
        var b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape;
        var b2CircleShape = Box2D.Collision.Shapes.b2CircleShape;
        var b2DebugDraw = Box2D.Dynamics.b2DebugDraw;

        var that = this;
        var gameWidth = gameWidth;
        var gameHeight = gameHeight;
        var turnRate = 0;
        var targetRotation = 0;
        var rotation = 0;

        Constants.get("world_rotation_rate", function(value) {
            turnRate = value;
        });

        var debugDraw = newDebugDraw();

        this.world = new b2World(newGravity(), false);
        this.world.SetDebugDraw(debugDraw);

        newWorld();

        this.newBlock = function(stage) {
            var fd                 = new b2FixtureDef;
            fd.shape               = new b2PolygonShape();
            fd.shape.SetAsBox(1,1);

            var bodyDef                  = new b2BodyDef();
            bodyDef.type                 = b2Body.b2_dynamicBody;
            bodyDef.position.x           = 150/PIXELS_PER_METER;
            bodyDef.position.y           = 150/PIXELS_PER_METER;
            var body = that.world.CreateBody(bodyDef);
            body.CreateFixture(fd);

            var e = new Entity();
            Constants.get("block_image", function(value) {
                var beeTexture = PIXI.Texture.fromImage(value, true);
                var beeSprite = new PIXI.Sprite(beeTexture);

                beeSprite.anchor.x = 0.5;
                beeSprite.anchor.y = 0.5;

                e.addComponent(SpriteComponent(stage, beeSprite));
                e.addComponent(PhysicsComponent(body));
                console.log("here");
                World.add(e);
            });
        }

        this.draw = function() {
            that.world.DrawDebugData();
        }

        this.update = function() {
            if (Math.abs(targetRotation - rotation) > turnRate) {
                function sign(x) { return x ? x < 0 ? -1 : 1 : 0; }
                direction = sign(targetRotation - rotation);
                console.log(direction);
                rotation += direction * turnRate;
                that.world.SetGravity(newGravity());
            }

            that.world.Step(3,3);
        }

        this.getRotation = function() {
            return rotation;
        }

        this.setTargetRotation = function(rotation) {
            targetRotation = rotation;
        }

        function newGravity() {
            var magnitude = PIXELS_PER_METER*1;
            var xComponent = magnitude * Math.sin(rotation *  (Math.PI/180));
            var yComponent = magnitude * Math.cos(rotation * (Math.PI/180));
            return new b2Vec2(xComponent, yComponent);
        }

        function addWall(x, y, width, height) {
            //floor
            var floorDef                 = new b2FixtureDef;
            floorDef.shape               = new b2PolygonShape();
            floorDef.shape.SetAsBox(width/2, height/2);

            var floorBodyDef                  = new b2BodyDef();
            floorBodyDef.type                 = b2Body.b2_staticBody;
            floorBodyDef.position.x           = x/PIXELS_PER_METER;
            floorBodyDef.position.y           = y/PIXELS_PER_METER;
            that.world.CreateBody(floorBodyDef).CreateFixture(floorDef);
        }

        function newWorld() {
            addWall(gameWidth/2, 0, 10000, 3);
            addWall(gameWidth/2, gameHeight, 10000, 3);
            addWall(0, gameHeight/2, 3, 100000);
            addWall(gameWidth, gameHeight/2, 3, 100000);
        }

    }
}();
<|endoftext|>


<commit_before>(function (w) {
	var retries = 4;
	var schedule;
	var default_options = {
		negativerisk_detailed: false,
		negativerisk_yesrisk: false,
		negativerisk_maxprice: 97,
		negativerisk_refresh: 10,
		negativerisk_great: 5,
	};

	const COLORS = {
		no: 'red',
		yes: 'green'
	};

	const STATUSES = {
		unknown: {
			icon: '❓',
			bgcolor: '#777',
			label: 'Unknown',
			instruction: "Don't Buy",
			lowLabel: 'Lowest Potential Loss',
			highLabel: 'Highest Potential Loss'
		},
		awful: {
			icon: '💩',
			bgcolor: '#bdb58e',
			label: 'Awful',
			instruction: "Don't Buy",
			lowLabel: 'Lowest Potential Loss',
			highLabel: 'Highest Potential Loss'
		},
		bad: {
			icon: '❌',
			bgcolor: '#fdd',
			label: 'Bad',
			instruction: "Don't Buy",
			lowLabel: 'Lowest Potential Loss',
			highLabel: 'Highest Potential Loss'
		},
		mixed: {
			icon: '😐',
			bgcolor: 'white',
			label: 'Mixed',
			instruction: "Don't Buy",
			lowLabel: 'Lowest Potential Loss',
			highLabel: 'Highest Potential Gain'
		},
		good: {
			icon: '✅',
			bgcolor: '#93d591',
			label: 'Good',
			instruction: "Buy",
			lowLabel: 'Lowest Potential Gain',
			highLabel: 'Highest Potential Gain'
		},
		great: {
			icon: '🤑',
			bgcolor: '#6dd56a',
			label: 'Great',
			instruction: "Buy",
			lowLabel: 'Lowest Potential Gain',
			highLabel: 'Highest Potential Gain'
		}
	};

	function scheduleUpdate(wait) {
		if (schedule) {
			clearTimeout(schedule);
			schedule = null;
		}
		schedule = (function(time) {
			return setTimeout(update, time);
		})(wait);
	}

	function scheduleRefresh() {
		retries = 4;
		scheduleUpdate(2000);
	};

	function assertInjectionComplete(callback) {
		chrome.storage.sync.get(default_options || {}, function(options) {
			var headerAnnotation = $('#headerAnnotation');
			var detailedAnnotation = $('#detailedAnnotation');

			if (options.negativerisk_detailed) {
				//headerAnnotation.remove();
				if (!detailedAnnotation || detailedAnnotation.length === 0) {
					//$('#contractList div.panel.panel-default.activity').parent().before('

');
					$('#contractList div.panel.panel-default.activity').parent().after('

');
				}
			} else {
				detailedAnnotation.remove();
			}
			var sharesHeader = $('#contractListTable .sharesHeader');
			if (!headerAnnotation || headerAnnotation.length === 0) {
				sharesHeader.find('a.glyphicons').after(' ');
			}

			var refreshElement = $('#contractListTable .sharesHeader a.glyphicons.refresh');
			if (refreshElement && refreshElement.length > 0) {
				var exists = false;
				var events = $._data(refreshElement[0], 'events');
				if (events && events.click) {
					$.each(events.click, function(index, ev) {
						if (ev.handler === scheduleRefresh) {
							exists = true;
						}
					});
				}

				if (exists) {
					console.log('refresh is already captured');
				} else {
					console.log('capturing refresh button');
					refreshElement.click(scheduleRefresh);
				}
			}

			if (callback) { callback(options); }
		});
	}

	function updateAnnotation(info, callback) {
		chrome.storage.sync.get(default_options || {}, function(options) {
			if (!options.negativerisk_yesrisk && info.type === 'yes') {
				console.log('Skipping "Yes" negative risk update.');
				$('#headerYes').html('');
				$('#detailedYes').html('');
			} else {
				info.options = options;
				if (options.negativerisk_detailed) {
					updateDetailedAnnotation(info);
				}
				updateHeaderAnnotation(info);
			}
			if (callback) { callback(options); }
		});
	}

	function colorize(type, text) {
		return '' + text + '';
	}

	function updateHeaderAnnotation(info) {
		//console.log('updateHeaderAnnotation:',info);
		assertInjectionComplete(function() {
			var typeDisplay = info.type.charAt(0).toUpperCase() + info.type.slice(1).toLowerCase();
			var el = $('#header' + typeDisplay);

			el.html(' ' + typeDisplay + ': ' + info.status.icon + ' ');

			var mouseover = info.status.instruction + ' ' + typeDisplay + '\n';
			mouseover += info.status.lowLabel + ': ' + info.low + '%\n';
			mouseover += info.status.highLabel + ': ' + info.high + '%';

			el.attr('title', mouseover);
		});
	}

	function updateDetailedAnnotation(info) {
		//console.log('updateDetailedAnnotation:',info);
		assertInjectionComplete(function() {
			var typeDisplay = info.type.charAt(0).toUpperCase() + info.type.slice(1).toLowerCase();
			var el = $('#detailed' + typeDisplay);

			var detail = '
' +
				'
' +
					'' + info.status.icon + ' ' + info.status.instruction + ' ' + colorize(info.type, typeDisplay) + '	' +
					'' + info.status.lowLabel + ':' + '	' +
					'' + info.low + '%	' +
				'
' +
				'
' +
					'' + info.status.highLabel + ':' + '	' +
					'' + info.high + '%	' +
				'
' +
			'
';

			el.html(detail);
		});
	}

	function update(callback) {
		chrome.storage.sync.get(default_options || {}, function(options) {
			calculate(options.negativerisk_maxprice, options.negativerisk_great);
			if (callback) { callback(options); }
		});
	}

	function calculate(max, great) {
		console.log('Calculating negative risk.  Max acceptable price: ' + max + '¢');

		var yeses = [];
		var nos = [];

		var rows = $('#contractListTable tr');
		rows.each(function(index, el) {
			var up = $(el).find('span.sharesUp').first();
			var down = $(el).find('span.sharesDown').first();
			var price;

			var upa = $(up).find('a.sharesUp');
			if (upa && upa.length) {
				price = parseInt(upa.text().split('¢')[0],10);
			} else {
				price = parseInt(up.text().split('¢')[0],10);
			}
			if (!isNaN(price) && price <= max) {
				yeses.push(price);
				console.log('yes: ' + price);
			} else if (upa.text() === 'None') {
				console.log('yes: 100 (none)');
				yeses.push(100.0);
			} else {
				console.log('yes did not match: ' + price);
			}

			var downa = $(down).find('a.sharesDown');
			if (downa && downa.length) {
				price = parseInt(downa.text().split('¢')[0],10);
			} else {
				price = parseInt(down.text().split('¢')[0],10);
			}
			if (!isNaN(price) && price <= max) {
				nos.push(price);
				console.log('no: ' + price);
			} else {
				console.log('no did not match: ' + price);
			}

		});

		var yesList = yeses.map(function(item) {
			return item / 100.0;
		});
		var noList = nos.map(function(item) {
			return item / 100.0;
		});

		//console.log('YESes: ' , yesList);
		//console.log('NOs: ', noList);

		if (retries === 0) {
			console.log('Out of retries.  Giving up.');
			return;
		}

		if (noList.length === 0 && yesList.length === 0) {
			console.log('Not finished loading, retrying in 2 seconds.');
			scheduleUpdate(2000);
			retries--;
			return;
		}

		if (noList.length < 2) {
			updateAnnotation({
				type: 'no',
				status: STATUSES.unknown,
				note: 'Too few markets ' + max + '¢ or under to calculate negative risk.'
			});
		}
		if (yesList.length < 2) {
			updateAnnotation({
				type: 'yes',
				status: STATUSES.unknown,
				note: 'Too few markets ' + max + '¢ or under to calculate negative risk.'
			});
		}

		if (noList.length < 2 && yesList.length < 2) {
			scheduleUpdate(2000);
			return;
		}

		var i, status;
		var highestPotentialPercent = null;
		var lowestPotentialPercent = null;

		///// PROCESS YES /////

		// Iterate through each entry in the linked market and add them up.  This is the total
		// spent.  Then iterate again, subtracting each entry from $1 to determine the profit
		// and apply the 10% fee to it, to calculate gain.

		var totalSpent = yesList.reduce(function(prev, current) {
			return prev + current;
		});
		console.log('Yes total spent: $' + totalSpent);
		for (i=0, len=yesList.length; i < len; i++) {
			var entry = yesList[i];
			var spent = totalSpent - entry;
			var profit = ((1-entry)*.9) - spent;

			var percentGainOrLoss = profit * 100.0;
			if (percentGainOrLoss == -0) {
				percentGainOrLoss = 0.00;
			}

			console.log("Yes " + (i+1) + "(" + yesList[i] + "):" + percentGainOrLoss + '%');

			if (highestPotentialPercent == null) {
				highestPotentialPercent = percentGainOrLoss;
			} else if (percentGainOrLoss > highestPotentialPercent) {
				highestPotentialPercent = percentGainOrLoss;
			}

			if (lowestPotentialPercent == null) {
				lowestPotentialPercent = percentGainOrLoss;
			} else if (percentGainOrLoss < lowestPotentialPercent) {
				lowestPotentialPercent = percentGainOrLoss;
			}
		}

		status = STATUSES.unknown;

		if (lowestPotentialPercent >= great) {
			status = STATUSES.great;
		} else if (lowestPotentialPercent > 0) {
			status = STATUSES.good;
		} else if (highestPotentialPercent < 0) {
			status = STATUSES.bad;
		} else {
			status = STATUSES.mixed;
		}

		updateAnnotation({
			type: 'yes',
			status: status,
			low: lowestPotentialPercent.toFixed(2),
			high: highestPotentialPercent.toFixed(2)
		});

		///// PROCESS NO /////

		highestPotentialPercent = null;
		lowestPotentialPercent = null;

		// Iterate through each entry in the linked market; this value will be treated as
		// the "yes" (no payout) entry when adding up potential gains.
		for (i=0, len=noList.length; i < len; i++) {
			var total = 0;

			// Iterate through each entry, adding up the potential gain, skipping the "i"
			// value to simulate the "yes" not paying out.
			for (j = 0; j < len; j++) {
				if (i !== j) {
					// add the current value to the potential total, taking out 10% for PredictIt
					total += (1-noList[j])*.9;
				}
			}

			// subtract the amount you would spend on the "yes" that would be lost
			var realTotal = total - noList[i];

			// calculate a percentage
			var percentGainOrLoss = realTotal*100.0;
			if (percentGainOrLoss == -0) {
				percentGainOrLoss = 0.00;
			}

			console.log("No " + (i+1) + "(" + noList[i] + "):" + percentGainOrLoss + "%");

			if (highestPotentialPercent == null) {
				//console.log('setting highest to ' + percentGainOrLoss);
				highestPotentialPercent = percentGainOrLoss;
			} else if (percentGainOrLoss > highestPotentialPercent) {
				//console.log('replacing current highest (' + highestPotentialPercent + ') with ' + percentGainOrLoss)
				highestPotentialPercent = percentGainOrLoss;
			}

			if (lowestPotentialPercent == null) {
				//console.log('setting lowest to ' + percentGainOrLoss);
				lowestPotentialPercent = percentGainOrLoss;
			} else if (percentGainOrLoss < lowestPotentialPercent) {
				//console.log('replacing current lowest (' + lowestPotentialPercent + ') with ' + percentGainOrLoss)
				lowestPotentialPercent = percentGainOrLoss;
			}
		}

		status = STATUSES.unknown;

		if (lowestPotentialPercent >= great) {
			status = STATUSES.great;
		} else if (lowestPotentialPercent > 0) {
			status = STATUSES.good;
		} else if (highestPotentialPercent < 0) {
			status = STATUSES.bad;
		} else {
			status = STATUSES.mixed;
		}

		updateAnnotation({
			type: 'no',
			status: status,
			low: lowestPotentialPercent.toFixed(2),
			high: highestPotentialPercent.toFixed(2)
		});

		chrome.storage.sync.get(default_options || {}, function(options) {
			var refreshTime = parseInt(options.negativerisk_refresh, 10);
			if (!refreshTime || isNaN(refreshTime)) {
				refreshTime = 10;
			}
			console.log('Refreshing again in ' + refreshTime + ' seconds.');
			scheduleUpdate(refreshTime * 1000);
		});
	}

	scheduleUpdate(2000);
})(window);<commit_msg>also show "average" gain/loss<commit_after>(function (w) {
	var retries = 4;
	var schedule;
	var default_options = {
		negativerisk_detailed: false,
		negativerisk_yesrisk: false,
		negativerisk_maxprice: 97,
		negativerisk_refresh: 10,
		negativerisk_great: 5,
	};

	const COLORS = {
		no: 'red',
		yes: 'green'
	};

	const STATUSES = {
		unknown: {
			icon: '❓',
			bgcolor: '#777',
			label: 'Unknown',
			instruction: "Don't Buy",
			lowLabel: 'Lowest Potential Loss',
			highLabel: 'Highest Potential Loss'
		},
		awful: {
			icon: '💩',
			bgcolor: '#bdb58e',
			label: 'Awful',
			instruction: "Don't Buy",
			lowLabel: 'Lowest Potential Loss',
			highLabel: 'Highest Potential Loss'
		},
		bad: {
			icon: '❌',
			bgcolor: '#fdd',
			label: 'Bad',
			instruction: "Don't Buy",
			lowLabel: 'Lowest Potential Loss',
			highLabel: 'Highest Potential Loss'
		},
		mixed: {
			icon: '😐',
			bgcolor: 'white',
			label: 'Mixed',
			instruction: "Don't Buy",
			lowLabel: 'Lowest Potential Loss',
			highLabel: 'Highest Potential Gain'
		},
		good: {
			icon: '✅',
			bgcolor: '#93d591',
			label: 'Good',
			instruction: "Buy",
			lowLabel: 'Lowest Potential Gain',
			highLabel: 'Highest Potential Gain'
		},
		great: {
			icon: '🤑',
			bgcolor: '#6dd56a',
			label: 'Great',
			instruction: "Buy",
			lowLabel: 'Lowest Potential Gain',
			highLabel: 'Highest Potential Gain'
		}
	};

	function scheduleUpdate(wait) {
		if (schedule) {
			clearTimeout(schedule);
			schedule = null;
		}
		schedule = (function(time) {
			return setTimeout(update, time);
		})(wait);
	}

	function scheduleRefresh() {
		retries = 4;
		scheduleUpdate(2000);
	};

	function assertInjectionComplete(callback) {
		chrome.storage.sync.get(default_options || {}, function(options) {
			var headerAnnotation = $('#headerAnnotation');
			var detailedAnnotation = $('#detailedAnnotation');

			if (options.negativerisk_detailed) {
				//headerAnnotation.remove();
				if (!detailedAnnotation || detailedAnnotation.length === 0) {
					//$('#contractList div.panel.panel-default.activity').parent().before('

');
					$('#contractList div.panel.panel-default.activity').parent().after('

');
				}
			} else {
				detailedAnnotation.remove();
			}
			var sharesHeader = $('#contractListTable .sharesHeader');
			if (!headerAnnotation || headerAnnotation.length === 0) {
				sharesHeader.find('a.glyphicons').after(' ');
			}

			var refreshElement = $('#contractListTable .sharesHeader a.glyphicons.refresh');
			if (refreshElement && refreshElement.length > 0) {
				var exists = false;
				var events = $._data(refreshElement[0], 'events');
				if (events && events.click) {
					$.each(events.click, function(index, ev) {
						if (ev.handler === scheduleRefresh) {
							exists = true;
						}
					});
				}

				if (exists) {
					console.log('refresh is already captured');
				} else {
					console.log('capturing refresh button');
					refreshElement.click(scheduleRefresh);
				}
			}

			if (callback) { callback(options); }
		});
	}

	function updateAnnotation(info, callback) {
		chrome.storage.sync.get(default_options || {}, function(options) {
			if (!options.negativerisk_yesrisk && info.type === 'yes') {
				console.log('Skipping "Yes" negative risk update.');
				$('#headerYes').html('');
				$('#detailedYes').html('');
			} else {
				info.options = options;
				if (options.negativerisk_detailed) {
					updateDetailedAnnotation(info);
				}
				updateHeaderAnnotation(info);
			}
			if (callback) { callback(options); }
		});
	}

	function colorize(type, text) {
		return '' + text + '';
	}

	function updateHeaderAnnotation(info) {
		//console.log('updateHeaderAnnotation:',info);
		assertInjectionComplete(function() {
			var typeDisplay = info.type.charAt(0).toUpperCase() + info.type.slice(1).toLowerCase();
			var el = $('#header' + typeDisplay);

			el.html(' ' + typeDisplay + ': ' + info.status.icon + ' ');

			var mouseover = info.status.instruction + ' ' + typeDisplay + '\n';
			mouseover += info.status.lowLabel + ': ' + info.low + '%\n';
			mouseover += info.status.highLabel + ': ' + info.high + '%\n';
			mouseover += 'Average: ' + info.average + '%';

			el.attr('title', mouseover);
		});
	}

	function updateDetailedAnnotation(info) {
		//console.log('updateDetailedAnnotation:',info);
		assertInjectionComplete(function() {
			var typeDisplay = info.type.charAt(0).toUpperCase() + info.type.slice(1).toLowerCase();
			var el = $('#detailed' + typeDisplay);

			var detail = '
' +
				'
' +
					'' + info.status.icon + ' ' + info.status.instruction + ' ' + colorize(info.type, typeDisplay) + '	' +
					'' + info.status.lowLabel + ':' + '	' +
					'' + info.low + '%	' +
				'
' +
				'
' +
					'' + info.status.highLabel + ':' + '	' +
					'' + info.high + '%	' +
				'
' +
				'
' +
					'Average:' + '	' +
					'' + info.average + '%	' +
				'
' +
			'
';

			el.html(detail);
		});
	}

	function update(callback) {
		chrome.storage.sync.get(default_options || {}, function(options) {
			calculate(options.negativerisk_maxprice, options.negativerisk_great);
			if (callback) { callback(options); }
		});
	}

	function calculate(max, great) {
		console.log('Calculating negative risk.  Max acceptable price: ' + max + '¢');

		var yeses = [];
		var nos = [];

		var rows = $('#contractListTable tr');
		rows.each(function(index, el) {
			var up = $(el).find('span.sharesUp').first();
			var down = $(el).find('span.sharesDown').first();
			var price;

			var upa = $(up).find('a.sharesUp');
			if (upa && upa.length) {
				price = parseInt(upa.text().split('¢')[0],10);
			} else {
				price = parseInt(up.text().split('¢')[0],10);
			}
			if (!isNaN(price) && price <= max) {
				yeses.push(price);
				console.log('yes: ' + price);
			} else if (upa.text() === 'None') {
				console.log('yes: 100 (none)');
				yeses.push(100.0);
			} else {
				console.log('yes did not match: ' + price);
			}

			var downa = $(down).find('a.sharesDown');
			if (downa && downa.length) {
				price = parseInt(downa.text().split('¢')[0],10);
			} else {
				price = parseInt(down.text().split('¢')[0],10);
			}
			if (!isNaN(price) && price <= max) {
				nos.push(price);
				console.log('no: ' + price);
			} else {
				console.log('no did not match: ' + price);
			}

		});

		var yesList = yeses.map(function(item) {
			return item / 100.0;
		});
		var noList = nos.map(function(item) {
			return item / 100.0;
		});

		//console.log('YESes: ' , yesList);
		//console.log('NOs: ', noList);

		if (retries === 0) {
			console.log('Out of retries.  Giving up.');
			return;
		}

		if (noList.length === 0 && yesList.length === 0) {
			console.log('Not finished loading, retrying in 2 seconds.');
			scheduleUpdate(2000);
			retries--;
			return;
		}

		if (noList.length < 2) {
			updateAnnotation({
				type: 'no',
				status: STATUSES.unknown,
				note: 'Too few markets ' + max + '¢ or under to calculate negative risk.'
			});
		}
		if (yesList.length < 2) {
			updateAnnotation({
				type: 'yes',
				status: STATUSES.unknown,
				note: 'Too few markets ' + max + '¢ or under to calculate negative risk.'
			});
		}

		if (noList.length < 2 && yesList.length < 2) {
			scheduleUpdate(2000);
			return;
		}

		var i, status, average;
		var highestPotentialPercent = null;
		var lowestPotentialPercent = null;
		var totalPercent = null;

		///// PROCESS YES /////

		// Iterate through each entry in the linked market and add them up.  This is the total
		// spent.  Then iterate again, subtracting each entry from $1 to determine the profit
		// and apply the 10% fee to it, to calculate gain.

		var totalSpent = yesList.reduce(function(prev, current) {
			return prev + current;
		});
		console.log('Yes total spent: $' + totalSpent);
		for (i=0, len=yesList.length; i < len; i++) {
			var entry = yesList[i];
			var spent = totalSpent - entry;
			var profit = ((1-entry)*.9) - spent;

			var percentGainOrLoss = profit * 100.0;
			if (percentGainOrLoss == -0) {
				percentGainOrLoss = 0.00;
			}

			console.log("Yes " + (i+1) + "(" + yesList[i] + "):" + percentGainOrLoss + '%');

			if (totalPercent == null) {
				totalPercent = percentGainOrLoss;
			} else {
				totalPercent += percentGainOrLoss;
			}

			if (highestPotentialPercent == null) {
				highestPotentialPercent = percentGainOrLoss;
			} else if (percentGainOrLoss > highestPotentialPercent) {
				highestPotentialPercent = percentGainOrLoss;
			}

			if (lowestPotentialPercent == null) {
				lowestPotentialPercent = percentGainOrLoss;
			} else if (percentGainOrLoss < lowestPotentialPercent) {
				lowestPotentialPercent = percentGainOrLoss;
			}
		}

		status = STATUSES.unknown;

		if (lowestPotentialPercent >= great) {
			status = STATUSES.great;
		} else if (lowestPotentialPercent > 0) {
			status = STATUSES.good;
		} else if (highestPotentialPercent < 0) {
			status = STATUSES.bad;
		} else {
			status = STATUSES.mixed;
		}

		average = (totalPercent / yeses.length).toFixed(2);
		console.log('Yes average: ' + average + '%');

		updateAnnotation({
			type: 'yes',
			status: status,
			average: average,
			low: lowestPotentialPercent.toFixed(2),
			high: highestPotentialPercent.toFixed(2)
		});

		///// PROCESS NO /////

		highestPotentialPercent = null;
		lowestPotentialPercent = null;
		totalPercent = null;

		// Iterate through each entry in the linked market; this value will be treated as
		// the "yes" (no payout) entry when adding up potential gains.
		for (i=0, len=noList.length; i < len; i++) {
			var total = 0;

			// Iterate through each entry, adding up the potential gain, skipping the "i"
			// value to simulate the "yes" not paying out.
			for (j = 0; j < len; j++) {
				if (i !== j) {
					// add the current value to the potential total, taking out 10% for PredictIt
					total += (1-noList[j])*.9;
				}
			}

			// subtract the amount you would spend on the "yes" that would be lost
			var realTotal = total - noList[i];

			// calculate a percentage
			var percentGainOrLoss = realTotal*100.0;
			if (percentGainOrLoss == -0) {
				percentGainOrLoss = 0.00;
			}

			console.log("No " + (i+1) + "(" + noList[i] + "):" + percentGainOrLoss + "%");

			if (totalPercent == null) {
				totalPercent = percentGainOrLoss;
			} else {
				totalPercent += percentGainOrLoss;
			}

			if (highestPotentialPercent == null) {
				//console.log('setting highest to ' + percentGainOrLoss);
				highestPotentialPercent = percentGainOrLoss;
			} else if (percentGainOrLoss > highestPotentialPercent) {
				//console.log('replacing current highest (' + highestPotentialPercent + ') with ' + percentGainOrLoss)
				highestPotentialPercent = percentGainOrLoss;
			}

			if (lowestPotentialPercent == null) {
				//console.log('setting lowest to ' + percentGainOrLoss);
				lowestPotentialPercent = percentGainOrLoss;
			} else if (percentGainOrLoss < lowestPotentialPercent) {
				//console.log('replacing current lowest (' + lowestPotentialPercent + ') with ' + percentGainOrLoss)
				lowestPotentialPercent = percentGainOrLoss;
			}
		}

		status = STATUSES.unknown;

		if (lowestPotentialPercent >= great) {
			status = STATUSES.great;
		} else if (lowestPotentialPercent > 0) {
			status = STATUSES.good;
		} else if (highestPotentialPercent < 0) {
			status = STATUSES.bad;
		} else {
			status = STATUSES.mixed;
		}

		average = (totalPercent / yeses.length).toFixed(2);
		console.log('No average: ' + average + '%');

		updateAnnotation({
			type: 'no',
			status: status,
			average: average,
			low: lowestPotentialPercent.toFixed(2),
			high: highestPotentialPercent.toFixed(2)
		});

		chrome.storage.sync.get(default_options || {}, function(options) {
			var refreshTime = parseInt(options.negativerisk_refresh, 10);
			if (!refreshTime || isNaN(refreshTime)) {
				refreshTime = 10;
			}
			console.log('Refreshing again in ' + refreshTime + ' seconds.');
			scheduleUpdate(refreshTime * 1000);
		});
	}

	scheduleUpdate(2000);
})(window);<|endoftext|>


<commit_before>#!/usr/bin/env node

var piphone = {};

piphone.mods = {};
piphone.mods.cp = require('child_process');
piphone.mods.gpiobutton = require('gpiobutton');
piphone.mods.fs = require('fs');

piphone.digits = {
  '1': '.*',
  '2': '[abc]',
  '3': '[def]',
  '4': '[ghi]',
  '5': '[jkl]',
  '6': '[mno]',
  '7': '[pqrs]',
  '8': '[tuv]',
  '9': '[wxyz]',
  '0': ' ',
};

piphone.closings = [
  "goodbye",
  "farewell",
  "see you later",
  "leka nosht",
  "ciao"
];

piphone.state = {};
piphone.state.mode = "";
piphone.state.sofar = [];

piphone.effects = {
  beep: 'Answering_Machine_Beep-Mike_Koenig-SoundBible.com-1804176620.wav',
  blip: 'Robot_blip-Marianne_Gagnon-SoundBible.com-120342607.wav',
  tone: 'Short_Dial_Tone-Mike_Koenig-SoundBible.com-1911037576.wav',
  modem: 'Dial_Up_Modem-ezwa-SoundBible.com-909377495.wav',
  flush: 'Flushing_The_Toilet-Grzegorz_Adam_Hankie-SoundBible.com-399247839.wav',
  uhoh: 'Uh_Oh_Baby-Mike_Koenig-SoundBible.com-1858856676.wav',
};

piphone.root = require.resolve('./piphone.js').split('/').slice(0,-1).join("/");

piphone.dev = {};
piphone.dev.hook = new piphone.mods.gpiobutton.button({name:'hook', gpiono:22, DOWN:1});
piphone.dev.dial = new piphone.mods.gpiobutton.button({name:'dial', gpiono:27, longTimeout: 2500});
piphone.dev.rotary = new piphone.mods.gpiobutton.button({name:'rotary', gpiono:17, interval:30, DOWN:1});
//piphone.dev.onoff = new piphone.mods.gpiobutton.button({name:'switch', gpiono: 18});

piphone.dev.hook.on('buttondown', function() {
  process.emit('clear_code');
  if (piphone.mike) {
    piphone.mike.kill();
  }
});
piphone.dev.hook.on('longpress', function() {
  process.emit('mpc', {cmd:['pause']});
});

piphone.dev.dial.on('longpress', function() {
  console.log("LONG!");
  process.emit('setmode', {from:piphone.state.mode, to:'-'});
});

piphone.dev.rotary.on('multipress', function(spec) {
  var digit = Math.ceil(spec.count) % 10;
  //piphone.state.sofar.push(piphone.state.mode);
  piphone.state.sofar.push(digit);

  process.emit('code');

  switch (piphone.state.mode) {
    case '-':
      process.emit('setmode', {from:piphone.state.mode, to:''});
      break;
    case '*':
      if (piphone.state.sofar.length >= 3) {
        process.emit('setmode', {from:piphone.state.mode, to:''});
        process.emit('clear_code');
      }
      break;
    default:
  }
});
piphone.dev.rotary.on('buttonpress', function(spec) {
  piphone.dev.rotary.emit('multipress', spec);
});

process.on('code', function(spec) {
  var code = piphone.state.sofar.join("");

  console.log("CODE %j", {code:code, state:piphone.state});

  if (piphone.state.sofar[0] === 0) {
    process.emit('rotary_query', {rquery:piphone.state.sofar.slice(1)});
    return;
  }

  switch (code) {
    case '1':
      process.emit("volume", {volume:80});
      process.emit("mpc", {cmd:'play'});
      piphone.state.sofar.shift();
      break;
    case '2':
      process.emit("volume", {volume:100});
      process.emit("mpc", {cmd:'play'});
      piphone.state.sofar.shift();
      break;
    case '3':
      process.emit("mpc", {cmd:'next'});
      piphone.state.sofar.shift();
      break;
    case '4':
      process.emit("mpc", {cmd:'prev'});
      piphone.state.sofar.shift();
      break;
    case '5':
      process.emit("tts", {text:['number','5']});
      process.emit("mpcq", {query:['guapo']});
      piphone.state.sofar.shift();
      break;
    case '6':
      process.emit("tts", {text:['number','6']});
      process.emit("mpcq", {query:['belafonte','matilda']});
      piphone.state.sofar.shift();
      break;
    case '7':
      process.emit("tts", {text:['number','7']});
      process.emit("mpcq", {query:['susanna','tanyas']});
      piphone.state.sofar.shift();
      break;
    case '8':
      process.emit("tts", {text:['number','8']});
      process.emit("mpcq", {query:['puff']});
      piphone.state.sofar.shift();
      break;
    case '9':
      process.emit("tts", {text:['number','9']});
      process.emit("mpcq", {query:['lilly']});
      piphone.state.sofar.shift();
      break;
    case '-1':
      process.emit('clear_code');
      process.emit('setmode', {from:piphone.state.mode, to:'*'});
      piphone.state.sofar.push("*");
      break;
    case '-2':
      process.emit('clear_code');
      process.emit('setmode', {from:piphone.state.mode, to:'#'});
      piphone.state.sofar.push("#");
      break;
    case '-3':
    case '-4':
    case '-5':
    case '-6':
    case '-7':
    case '-8':
    case '-9':
      process.emit('mpc', {cmd:['pause']});
      process.emit('mike', {id:piphone.state.sofar.pop()});
      process.emit('clear_code');
      break;
    case '-0':
      process.emit('setmode', {from:piphone.state.mode, to:''});
      process.emit('clear_code');
      process.emit('clear_recs');
      break;
    case "*60":
      process.emit('mpc', {cmd:['single', 'on']});
      process.emit('audible_status');
      break;
    case "*80":
      process.emit('mpc', {cmd:['single', 'off']});
      process.emit('audible_status');
      break;
    case "*61":
      process.emit('mpc', {cmd:['random', 'on']});
      process.emit('audible_status');
      break;
    case "*81":
      process.emit('mpc', {cmd:['random', 'off']});
      process.emit('audible_status');
      break;
    case "*65":
      process.emit('audible_trackid');
      break;
    case "*66":
      process.emit('mpc', {cmd:['repeat', 'on']});
      process.emit('audible_status');
      break;
    case "*86":
      process.emit('mpc', {cmd:['repeat', 'off']});
      process.emit('audible_status');
      break;
    case '*78':
      process.emit('shutdown_request');
      break;
  }
});

process.on('rotary_query', function(spec) {
  if (spec.rquery.length === 0) {
    return;
  }

  spec.regex = spec.rquery.map(function(digit) { return piphone.digits[digit]; }).join('');
  console.error("RQUERY %j", spec);

  var mpcq = piphone.mods.cp.exec(['mpc_query', spec.regex].join(' '), function(code, out, err) {

    if (out.length === 0) {
      process.emit('effect', {name:'beep'});
      return;
    }

    var lines = out.split(/\n/);
    console.error("MPCQ %j", {code:code,out:out.slice(0,100),err:err,lines:lines.slice(0,10)});
    if (lines.length === 2) {
      process.emit('mpc', {cmd:['play',lines[0]]});
      return;
    }

    //process.emit('effect', {name:'uhoh'});
    //process.emit('tts', {text:[lines.length]});

  });
});

process.on('clear_code', function(spec) {
  piphone.state.sofar = [];
});

process.on('clear_recs', function(spec) {
  var rm = piphone.mods.cp.spawn('bash', ['-c',['rm',[process.env.HOME,'tmp','?.wav'].join('/')].join(' ')]);
  rm.stdout.pipe(process.stdout);
  rm.stderr.pipe(process.stderr);
  rm.on('exit', function() {
    process.emit('effect', {name:'flush'});
  });
});

process.on('setmode', function(spec) {
  var oldmode = piphone.state.mode;
  piphone.state.mode = spec.to;
  process.emit('newmode', spec);
});

process.on('newmode', function(spec) {
  if (spec.to === '-') {
    process.emit('effect', {name:'blip'});
    piphone.state.sofar.push('-');
  }
});
process.on('newmode', function(spec) {
  if (spec.to === '*') {
    process.emit('effect', {name:'modem'});
    //piphone.state.sofar.push('*');
  }
});
process.on('newmode', function(spec) {
  if (spec.to === '#') {
    process.emit('effect', {name:'tone'});
    //piphone.state.sofar.push('#');
  }
});

process.on('shutdown_request', function() {
  process.emit('mpc', {cmd:['stop']});
  process.emit("tts", {text:['goodbye cruel world']});
  setInterval(function() {
    var draw = Math.floor(Math.random()*piphone.closings.length);
    process.emit("tts", {text:[piphone.closings[draw]]});
  }, 2500);
  setTimeout(function() {
    piphone.mods.cp.exec('shutdown -h now', function(err, stdout, stderr) {
      console.error("SHUTDOWN: %j", {err:err,stdout:stdout,stderr:stderr});
    });
  }, 6000);
});

process.on('mpc', function(spec) {
  piphone.mods.cp.exec(['mpc'].concat(spec.cmd).join(" "), function(err, stdout, stderr) {
    console.error("%s: %j", spec.cmd, {err:err,stdout:stdout,stderr:stderr});
  });
});
process.on('volume', function(spec) {
  piphone.mods.cp.exec(['mpc','volume',spec.volume].join(" "), function(err, stdout, stderr) {
    console.error("VOL: %j", {vol:spec.volume,err:err,stdout:stdout,stderr:stderr});
  });
});

process.on('tts', function(spec) {
  var tts = piphone.mods.cp.spawn('/usr/bin/tts', spec.text);
  tts.stdout.pipe(process.stdout);
  tts.stderr.pipe(process.stderr);
  //tts.stdin.write(spec.text.concat(['\n']).join(" "));;
  //tts.stdin.end();
});

process.on('effect', function(spec) {
  var name = spec.name;
  var filename = piphone.effects[name];
  var path = [piphone.root,'wav',filename].join("/");
  console.error("PATH: %s -> %s %j", name, filename, path);
  var aplay = piphone.mods.cp.spawn('aplay', [path]);
  aplay.stdout.pipe(process.stdout);
  aplay.stderr.pipe(process.stderr);
});

process.on('audible_trackid', function(spec) {
  var stat = piphone.mods.cp.spawn('bash', ['-c', 'mpc current | tts']);

  stat.stdout.pipe(process.stderr);
  stat.stderr.pipe(process.stderr);
  //tts.stdin.write(spec.text.concat(['\n']).join(" "));;
  //tts.stdin.end();
});

process.on('audible_status', function(spec) {
  var stat = piphone.mods.cp.spawn('bash', ['-c', 'mpc | tail -1 | tts']);

  stat.stdout.pipe(process.stderr);
  stat.stderr.pipe(process.stderr);
  //tts.stdin.write(spec.text.concat(['\n']).join(" "));;
  //tts.stdin.end();
});

process.on('mike', function(spec) {
  if (piphone.mike) { return; }
  var mikeid = spec.id;
  var mikefile = [process.env.HOME, 'tmp', [mikeid,'wav'].join(".")].join('/');

  piphone.mods.fs.exists(mikefile, function(exists) {
    if (exists) {
      var play = piphone.cp.spawn('aplay', [mikefile]);
      play.stdout.pipe(process.stdout);
      play.stderr.pipe(process.stderr);
      return;
    }

    process.emit('effect', {name: 'beep'});
    var cmd = [
      //'(',
      'exec',
      'arecord',
      '-Dplug:usb',
      '--format=S16_LE',
      '--duration=120',
      mikefile
    ].join(" ");
    piphone.mike = piphone.mods.cp.spawn('bash', ['-c', cmd]);
    piphone.mike.stdout.pipe(process.stdout);
    piphone.mike.stderr.pipe(process.stderr);
    piphone.mike.on('exit', function() {
      console.error("DROPMIKE");
      delete piphone.mike;
    });

  });

  return;

  var cmd = [
      //'(',
      'aplay',
      mikefile,
      //')',
      '||',
      'exec',
      'arecord',
      '-Dplug:usb',
      '--format=S16_LE',
      '--duration=120',
      mikefile
      ].join(" ");
  console.error("MIKE: %s", cmd);
  piphone.mike = piphone.mods.cp.spawn('bash', ['-c', cmd]);
  piphone.mike.stdout.pipe(process.stdout);
  piphone.mike.stderr.pipe(process.stderr);
  piphone.mike.on('exit', function() {
    console.error("DROPMIKE");
    delete piphone.mike;
  });

});

process.on('mpcq', function(spec) {
    //process.emit('tts', {text:spec.query});
    var mpc = piphone.mods.cp.exec(['/usr/bin/mpc_query',spec.query.join(".*")].concat([
      '|',
      'xargs mpc play'
    ]).join(" "), function(err,stdout,stderr) {
    //console.error("DEMAND: %j", {err:err,stdout:stdout,stderr:stderr});
  });
});

process.emit('tts', {text:['hello', 'world']});
<commit_msg>bugfix<commit_after>#!/usr/bin/env node

var piphone = {};

piphone.mods = {};
piphone.mods.cp = require('child_process');
piphone.mods.gpiobutton = require('gpiobutton');
piphone.mods.fs = require('fs');

piphone.digits = {
  '1': '.*',
  '2': '[abc]',
  '3': '[def]',
  '4': '[ghi]',
  '5': '[jkl]',
  '6': '[mno]',
  '7': '[pqrs]',
  '8': '[tuv]',
  '9': '[wxyz]',
  '0': ' ',
};

piphone.closings = [
  "goodbye",
  "farewell",
  "see you later",
  "leka nosht",
  "ciao"
];

piphone.state = {};
piphone.state.mode = "";
piphone.state.sofar = [];

piphone.effects = {
  beep: 'Answering_Machine_Beep-Mike_Koenig-SoundBible.com-1804176620.wav',
  blip: 'Robot_blip-Marianne_Gagnon-SoundBible.com-120342607.wav',
  tone: 'Short_Dial_Tone-Mike_Koenig-SoundBible.com-1911037576.wav',
  modem: 'Dial_Up_Modem-ezwa-SoundBible.com-909377495.wav',
  flush: 'Flushing_The_Toilet-Grzegorz_Adam_Hankie-SoundBible.com-399247839.wav',
  uhoh: 'Uh_Oh_Baby-Mike_Koenig-SoundBible.com-1858856676.wav',
};

piphone.root = require.resolve('./piphone.js').split('/').slice(0,-1).join("/");

piphone.dev = {};
piphone.dev.hook = new piphone.mods.gpiobutton.button({name:'hook', gpiono:22, DOWN:1});
piphone.dev.dial = new piphone.mods.gpiobutton.button({name:'dial', gpiono:27, longTimeout: 2500});
piphone.dev.rotary = new piphone.mods.gpiobutton.button({name:'rotary', gpiono:17, interval:30, DOWN:1});
//piphone.dev.onoff = new piphone.mods.gpiobutton.button({name:'switch', gpiono: 18});

piphone.dev.hook.on('buttondown', function() {
  process.emit('clear_code');
  if (piphone.mike) {
    piphone.mike.kill();
  }
});
piphone.dev.hook.on('longpress', function() {
  process.emit('mpc', {cmd:['pause']});
});

piphone.dev.dial.on('longpress', function() {
  console.log("LONG!");
  process.emit('setmode', {from:piphone.state.mode, to:'-'});
});

piphone.dev.rotary.on('multipress', function(spec) {
  var digit = Math.ceil(spec.count) % 10;
  //piphone.state.sofar.push(piphone.state.mode);
  piphone.state.sofar.push(digit);

  process.emit('code');

  switch (piphone.state.mode) {
    case '-':
      process.emit('setmode', {from:piphone.state.mode, to:''});
      break;
    case '*':
      if (piphone.state.sofar.length >= 3) {
        process.emit('setmode', {from:piphone.state.mode, to:''});
        process.emit('clear_code');
      }
      break;
    default:
  }
});
piphone.dev.rotary.on('buttonpress', function(spec) {
  piphone.dev.rotary.emit('multipress', spec);
});

process.on('code', function(spec) {
  var code = piphone.state.sofar.join("");

  console.log("CODE %j", {code:code, state:piphone.state});

  if (piphone.state.sofar[0] === 0) {
    process.emit('rotary_query', {rquery:piphone.state.sofar.slice(1)});
    return;
  }

  switch (code) {
    case '1':
      process.emit("volume", {volume:80});
      process.emit("mpc", {cmd:'play'});
      piphone.state.sofar.shift();
      break;
    case '2':
      process.emit("volume", {volume:100});
      process.emit("mpc", {cmd:'play'});
      piphone.state.sofar.shift();
      break;
    case '3':
      process.emit("mpc", {cmd:'next'});
      piphone.state.sofar.shift();
      break;
    case '4':
      process.emit("mpc", {cmd:'prev'});
      piphone.state.sofar.shift();
      break;
    case '5':
      process.emit("tts", {text:['number','5']});
      process.emit("mpcq", {query:['guapo']});
      piphone.state.sofar.shift();
      break;
    case '6':
      process.emit("tts", {text:['number','6']});
      process.emit("mpcq", {query:['belafonte','matilda']});
      piphone.state.sofar.shift();
      break;
    case '7':
      process.emit("tts", {text:['number','7']});
      process.emit("mpcq", {query:['susanna','tanyas']});
      piphone.state.sofar.shift();
      break;
    case '8':
      process.emit("tts", {text:['number','8']});
      process.emit("mpcq", {query:['puff']});
      piphone.state.sofar.shift();
      break;
    case '9':
      process.emit("tts", {text:['number','9']});
      process.emit("mpcq", {query:['lilly']});
      piphone.state.sofar.shift();
      break;
    case '-1':
      process.emit('clear_code');
      process.emit('setmode', {from:piphone.state.mode, to:'*'});
      piphone.state.sofar.push("*");
      break;
    case '-2':
      process.emit('clear_code');
      process.emit('setmode', {from:piphone.state.mode, to:'#'});
      piphone.state.sofar.push("#");
      break;
    case '-3':
    case '-4':
    case '-5':
    case '-6':
    case '-7':
    case '-8':
    case '-9':
      process.emit('mpc', {cmd:['pause']});
      process.emit('mike', {id:piphone.state.sofar.pop()});
      process.emit('clear_code');
      break;
    case '-0':
      process.emit('setmode', {from:piphone.state.mode, to:''});
      process.emit('clear_code');
      process.emit('clear_recs');
      break;
    case "*60":
      process.emit('mpc', {cmd:['single', 'on']});
      process.emit('audible_status');
      break;
    case "*80":
      process.emit('mpc', {cmd:['single', 'off']});
      process.emit('audible_status');
      break;
    case "*61":
      process.emit('mpc', {cmd:['random', 'on']});
      process.emit('audible_status');
      break;
    case "*81":
      process.emit('mpc', {cmd:['random', 'off']});
      process.emit('audible_status');
      break;
    case "*65":
      process.emit('audible_trackid');
      break;
    case "*66":
      process.emit('mpc', {cmd:['repeat', 'on']});
      process.emit('audible_status');
      break;
    case "*86":
      process.emit('mpc', {cmd:['repeat', 'off']});
      process.emit('audible_status');
      break;
    case '*78':
      process.emit('shutdown_request');
      break;
  }
});

process.on('rotary_query', function(spec) {
  if (spec.rquery.length === 0) {
    return;
  }

  spec.regex = spec.rquery.map(function(digit) { return piphone.digits[digit]; }).join('');
  console.error("RQUERY %j", spec);

  var mpcq = piphone.mods.cp.exec(['mpc_query', spec.regex].join(' '), function(code, out, err) {

    if (out.length === 0) {
      process.emit('effect', {name:'beep'});
      return;
    }

    var lines = out.split(/\n/);
    console.error("MPCQ %j", {code:code,out:out.slice(0,100),err:err,lines:lines.slice(0,10)});
    if (lines.length === 2) {
      process.emit('mpc', {cmd:['play',lines[0]]});
      return;
    }

    //process.emit('effect', {name:'uhoh'});
    //process.emit('tts', {text:[lines.length]});

  });
});

process.on('clear_code', function(spec) {
  piphone.state.sofar = [];
});

process.on('clear_recs', function(spec) {
  var rm = piphone.mods.cp.spawn('bash', ['-c',['rm',[process.env.HOME,'tmp','?.wav'].join('/')].join(' ')]);
  rm.stdout.pipe(process.stdout);
  rm.stderr.pipe(process.stderr);
  rm.on('exit', function() {
    process.emit('effect', {name:'flush'});
  });
});

process.on('setmode', function(spec) {
  var oldmode = piphone.state.mode;
  piphone.state.mode = spec.to;
  process.emit('newmode', spec);
});

process.on('newmode', function(spec) {
  if (spec.to === '-') {
    process.emit('effect', {name:'blip'});
    piphone.state.sofar.push('-');
  }
});
process.on('newmode', function(spec) {
  if (spec.to === '*') {
    process.emit('effect', {name:'modem'});
    //piphone.state.sofar.push('*');
  }
});
process.on('newmode', function(spec) {
  if (spec.to === '#') {
    process.emit('effect', {name:'tone'});
    //piphone.state.sofar.push('#');
  }
});

process.on('shutdown_request', function() {
  process.emit('mpc', {cmd:['stop']});
  process.emit("tts", {text:['goodbye cruel world']});
  setInterval(function() {
    var draw = Math.floor(Math.random()*piphone.closings.length);
    process.emit("tts", {text:[piphone.closings[draw]]});
  }, 2500);
  setTimeout(function() {
    piphone.mods.cp.exec('shutdown -h now', function(err, stdout, stderr) {
      console.error("SHUTDOWN: %j", {err:err,stdout:stdout,stderr:stderr});
    });
  }, 6000);
});

process.on('mpc', function(spec) {
  piphone.mods.cp.exec(['mpc'].concat(spec.cmd).join(" "), function(err, stdout, stderr) {
    console.error("%s: %j", spec.cmd, {err:err,stdout:stdout,stderr:stderr});
  });
});
process.on('volume', function(spec) {
  piphone.mods.cp.exec(['mpc','volume',spec.volume].join(" "), function(err, stdout, stderr) {
    console.error("VOL: %j", {vol:spec.volume,err:err,stdout:stdout,stderr:stderr});
  });
});

process.on('tts', function(spec) {
  var tts = piphone.mods.cp.spawn('/usr/bin/tts', spec.text);
  tts.stdout.pipe(process.stdout);
  tts.stderr.pipe(process.stderr);
  //tts.stdin.write(spec.text.concat(['\n']).join(" "));;
  //tts.stdin.end();
});

process.on('effect', function(spec) {
  var name = spec.name;
  var filename = piphone.effects[name];
  var path = [piphone.root,'wav',filename].join("/");
  console.error("PATH: %s -> %s %j", name, filename, path);
  var aplay = piphone.mods.cp.spawn('aplay', [path]);
  aplay.stdout.pipe(process.stdout);
  aplay.stderr.pipe(process.stderr);
});

process.on('audible_trackid', function(spec) {
  var stat = piphone.mods.cp.spawn('bash', ['-c', 'mpc current | tts']);

  stat.stdout.pipe(process.stderr);
  stat.stderr.pipe(process.stderr);
  //tts.stdin.write(spec.text.concat(['\n']).join(" "));;
  //tts.stdin.end();
});

process.on('audible_status', function(spec) {
  var stat = piphone.mods.cp.spawn('bash', ['-c', 'mpc | tail -1 | tts']);

  stat.stdout.pipe(process.stderr);
  stat.stderr.pipe(process.stderr);
  //tts.stdin.write(spec.text.concat(['\n']).join(" "));;
  //tts.stdin.end();
});

process.on('mike', function(spec) {
  if (piphone.mike) { return; }
  var mikeid = spec.id;
  var mikefile = [process.env.HOME, 'tmp', [mikeid,'wav'].join(".")].join('/');

  piphone.mods.fs.exists(mikefile, function(exists) {
    if (exists) {
      var play = piphone.mods.cp.spawn('aplay', [mikefile]);
      play.stdout.pipe(process.stdout);
      play.stderr.pipe(process.stderr);
      return;
    }

    process.emit('effect', {name: 'beep'});
    var cmd = [
      //'(',
      'exec',
      'arecord',
      '-Dplug:usb',
      '--format=S16_LE',
      '--duration=120',
      mikefile
    ].join(" ");
    piphone.mike = piphone.mods.cp.spawn('bash', ['-c', cmd]);
    piphone.mike.stdout.pipe(process.stdout);
    piphone.mike.stderr.pipe(process.stderr);
    piphone.mike.on('exit', function() {
      console.error("DROPMIKE");
      delete piphone.mike;
    });

  });

  return;

  var cmd = [
      //'(',
      'aplay',
      mikefile,
      //')',
      '||',
      'exec',
      'arecord',
      '-Dplug:usb',
      '--format=S16_LE',
      '--duration=120',
      mikefile
      ].join(" ");
  console.error("MIKE: %s", cmd);
  piphone.mike = piphone.mods.cp.spawn('bash', ['-c', cmd]);
  piphone.mike.stdout.pipe(process.stdout);
  piphone.mike.stderr.pipe(process.stderr);
  piphone.mike.on('exit', function() {
    console.error("DROPMIKE");
    delete piphone.mike;
  });

});

process.on('mpcq', function(spec) {
    //process.emit('tts', {text:spec.query});
    var mpc = piphone.mods.cp.exec(['/usr/bin/mpc_query',spec.query.join(".*")].concat([
      '|',
      'xargs mpc play'
    ]).join(" "), function(err,stdout,stderr) {
    //console.error("DEMAND: %j", {err:err,stdout:stdout,stderr:stderr});
  });
});

process.emit('tts', {text:['hello', 'world']});
<|endoftext|>