<commit_before>import React from 'react';
import ReactDOM from 'react-dom';
import { mount, shallow } from 'enzyme';
import _ from 'lodash';
import TextareaInput, { TextareaHelper, ErrorTextContainer, charLimitExceeded, CharLimitExceededError, determineCharCounterTextValue, CharCounterText, CharCounterErrorText } from './TextareaInput';

describe('TextareaInput', () => {

  it('componentDidMount should set value on state if there is a value prop', () => {
    const text = 'Has text here';
    const wrapper = mount(<TextareaInput name="test" value={text} />);

    expect(wrapper.state().value).toBe(text);
  });

  it('focused func sets focused state to true', () => {
    const wrapper = shallow(<TextareaInput name="test" />);

    expect(wrapper.state().focused).toBeFalsy();

    wrapper.instance().focused();
    expect(wrapper.state().focused).toBeTruthy();
  });

  it('blurred func sets focused state to false', () => {
    const wrapper = shallow(<TextareaInput name="test" />);

    wrapper.setState({
      focused: true
    });
    expect(wrapper.state().focused).toBeTruthy();

    wrapper.instance().blurred();
    expect(wrapper.state().focused).toBeFalsy();
  });

  it('renderHelperText should return null if component is collapsed, has no value, and has no error', () => {
    const wrapper = shallow(<TextareaInput name="test" collapsed/>);

    const helperText = wrapper.instance().renderHelperText();

    expect(helperText).toBeFalsy();
  });

  it('renderErrorText should return HelperTextContainer if there is an error', () => {
    const error = 'some error';
    const wrapper = shallow(<TextareaInput name="test" error={error}/>);

    const errorText = wrapper.instance().renderErrorText();

    expect(errorText).toEqual(<ErrorTextContainer charLimit={0} error={error} localError="" />);
  });

  it('renderErrorText should return HelperTextContainer if there is a charLimit and the value is greater than the charLimit, even if no error is present', () => {
    const localError = CharLimitExceededError;
    const value = 'hello world';
    const charLimit = value.length - 1;
    const wrapper = shallow(<TextareaInput name="test" charLimit={charLimit} value={value}/>);

    const errorText = wrapper.instance().renderErrorText();

    expect(errorText).toEqual(<ErrorTextContainer localError={localError} error='' charLimit={charLimit} />);
  });

  it('renderHelperText should return TextareaHelper if component is not collapsed.', () => {
    const helperTextString = 'help me!';
    const wrapper = shallow(<TextareaInput name="test" helper={helperTextString}/>);

    const helperText = wrapper.instance().renderHelperText();

    expect(helperText).toEqual(<TextareaHelper hasCharLimit={false} helper={helperTextString} />);
  });

  it('determineCharCounterTextValue should return counter string if the charLimit is greater than 0', () => {
    const charLimit = 100;
    const inputValue = 'hello';

    const charCounterStr = determineCharCounterTextValue(charLimit, inputValue);

    expect(charCounterStr).toBe(`${_.size(inputValue)} / ${charLimit}`);
  });

  it('determineCharCounterTextValue should return null if charLimit is 0', () => {
    expect(determineCharCounterTextValue(0, 'hello')).toBe(null);
  });

  it('renderCharCounterText should return CharCounterText if charLimit greater than 0 and char limit not reached', () => {
    const charLimit = 100;
    const inputValue = 'hello world';
    const wrapper = shallow(<TextareaInput name="test" value={inputValue} charLimit={charLimit}/>);

    const charCounter = wrapper.instance().renderCharCounterText();
    const charCounterStr = determineCharCounterTextValue(charLimit, inputValue);

    expect(charCounter).toEqual(<CharCounterText>{charCounterStr}</CharCounterText>)
  });

  it('renderCharCounterText should return null if charLimit is 0', () => {
    const inputValue = 'hello world';
    const wrapper = shallow(<TextareaInput name="test" value={inputValue}/>);

    const charCounter = wrapper.instance().renderCharCounterText();

    expect(charCounter).toBe(null);
  });

  it('renderCharCounterText should return CharCounterText if charLimit greater than 0 and char limit has been passed', () => {
    const charLimit = 10;
    const inputValue = 'hello world, there is some text';
    const wrapper = shallow(<TextareaInput name="test" value={inputValue} charLimit={charLimit}/>);

    const charCounter = wrapper.instance().renderCharCounterText();
    const charCounterStr = determineCharCounterTextValue(charLimit, inputValue);

    expect(charCounter).toEqual(<CharCounterErrorText>{charCounterStr}</CharCounterErrorText>)
  });

  it('renderCharCounterText should return null if charLimit is 0', () => {
    const inputValue = 'hello world, there is some text';
    const wrapper = shallow(<TextareaInput name="test" value={inputValue}/>);

    const charCounter = wrapper.instance().renderCharCounterText();

    expect(charCounter).toBe(null);
  });

  it('focusOnTextarea should set focused state to true', () => {
    const wrapper = mount(<TextareaInput name="test" />);

    expect(wrapper.state().focused).toBeFalsy();

    wrapper.instance().focusOnTextarea();
    setTimeout(() => {
      expect(wrapper.state().focused).toBeTruthy();
    }, 0);
  });

  it('onContainerMouseDown sets cancelBlur to true on state', () => {
    const wrapper = mount(<TextareaInput name="test" />);

    expect(wrapper.state().cancelBlur).toBeFalsy();

    wrapper.instance().focusOnTextarea();

    wrapper.instance().cancelBlur();
    expect(wrapper.state().cancelBlur).toBeTruthy();
  });

  it('onContainerMouseUp sets cancelBlur to true on state', () => {
    const wrapper = mount(<TextareaInput name="test" />);

    wrapper.instance().focusOnTextarea();

    wrapper.instance().cancelBlur();
    expect(wrapper.state().cancelBlur).toBeTruthy();

    wrapper.instance().removeCancelBlur();
    expect(wrapper.state().cancelBlur).toBeFalsy();
  });

  it('onChange should set the state with the new value from the input', () => {
    jest.useFakeTimers();
    const onChangePropSyp = jest.fn();
    const preventSpy = jest.fn();
    const newValue = 'Some text';
    const event = {
      preventDefault: preventSpy,
      target: {
        value: newValue
      }
    };

    const wrapper = mount(<TextareaInput name="test" onChange={onChangePropSyp} />);

    expect(wrapper.state().value).toBeFalsy();

    wrapper.instance().onChange(event);

    setTimeout(() => {
      expect(preventSpy).toHaveBeenCalled();
      expect(wrapper.state().value).toEqual(newValue);
      expect(onChangePropSyp).toHaveBeenCalledWith(newValue);
    }, 0);

    jest.runAllTimers();

  });

  it('placeholder prop should set a placeholder on the textarea element', () => {
    const placeholder = 'Hello, I am a placeholder';
    const wrapper = mount(<TextareaInput name="test" placeholder={placeholder} />)
    const instance = wrapper.instance();
    const dom = ReactDOM.findDOMNode(instance);
    const textarea = dom.querySelector('textarea');
    expect(textarea.placeholder).toBe(placeholder);
  });
});

describe('charLimitExceeded', () => {
  it(`should return ${false} if there is no charLimit`, () => {
    expect(charLimitExceeded(0, 'hello world')).toBe(false);
  });

  it(`should return ${false} if the string length is equal to the charLimit`, () => {
    const str = 'hello world';
    const charLimit = str.length;
    expect(charLimitExceeded(charLimit, str)).toBe(false)
  });

  it(`should return ${true} if the string length is greater than the charLimit`, () => {
    const str = 'hello world';
    const charLimit = str.length - 1;
    expect(charLimitExceeded(charLimit, str)).toBe(true);
  });
});
<commit_msg>use mount instead of shallow to fix tests<commit_after>import React from 'react';
import ReactDOM from 'react-dom';
import { mount, shallow } from 'enzyme';
import _ from 'lodash';
import TextareaInput, { TextareaHelper, ErrorTextContainer, charLimitExceeded, CharLimitExceededError, determineCharCounterTextValue, CharCounterText, CharCounterErrorText } from './TextareaInput';

describe('TextareaInput', () => {

  it('componentDidMount should set value on state if there is a value prop', () => {
    const text = 'Has text here';
    const wrapper = mount(<TextareaInput name="test" value={text} />);

    expect(wrapper.state().value).toBe(text);
  });

  it('focused func sets focused state to true', () => {
    const wrapper = shallow(<TextareaInput name="test" />);

    expect(wrapper.state().focused).toBeFalsy();

    wrapper.instance().focused();
    expect(wrapper.state().focused).toBeTruthy();
  });

  it('blurred func sets focused state to false', () => {
    const wrapper = shallow(<TextareaInput name="test" />);

    wrapper.setState({
      focused: true
    });
    expect(wrapper.state().focused).toBeTruthy();

    wrapper.instance().blurred();
    expect(wrapper.state().focused).toBeFalsy();
  });

  it('renderHelperText should return null if component is collapsed, has no value, and has no error', () => {
    const wrapper = shallow(<TextareaInput name="test" collapsed/>);

    const helperText = wrapper.instance().renderHelperText();

    expect(helperText).toBeFalsy();
  });

  it('renderErrorText should return HelperTextContainer if there is an error', () => {
    const error = 'some error';
    const wrapper = shallow(<TextareaInput name="test" error={error}/>);

    const errorText = wrapper.instance().renderErrorText();

    expect(errorText).toEqual(<ErrorTextContainer charLimit={0} error={error} localError="" />);
  });

  it('renderErrorText should return HelperTextContainer if there is a charLimit and the value is greater than the charLimit, even if no error is present', () => {
    const localError = CharLimitExceededError;
    const value = 'hello world';
    const charLimit = value.length - 1;
    const wrapper = mount(<TextareaInput name="test" charLimit={charLimit} value={value}/>);

    const errorText = wrapper.instance().renderErrorText();

    expect(errorText).toEqual(<ErrorTextContainer localError={localError} error='' charLimit={charLimit} />);
  });

  it('renderHelperText should return TextareaHelper if component is not collapsed.', () => {
    const helperTextString = 'help me!';
    const wrapper = shallow(<TextareaInput name="test" helper={helperTextString}/>);

    const helperText = wrapper.instance().renderHelperText();

    expect(helperText).toEqual(<TextareaHelper hasCharLimit={false} helper={helperTextString} />);
  });

  it('determineCharCounterTextValue should return counter string if the charLimit is greater than 0', () => {
    const charLimit = 100;
    const inputValue = 'hello';

    const charCounterStr = determineCharCounterTextValue(charLimit, inputValue);

    expect(charCounterStr).toBe(`${_.size(inputValue)} / ${charLimit}`);
  });

  it('determineCharCounterTextValue should return null if charLimit is 0', () => {
    expect(determineCharCounterTextValue(0, 'hello')).toBe(null);
  });

  it('renderCharCounterText should return CharCounterText if charLimit greater than 0 and char limit not reached', () => {
    const charLimit = 100;
    const inputValue = 'hello world';
    const wrapper = mount(<TextareaInput name="test" value={inputValue} charLimit={charLimit}/>);

    const charCounter = wrapper.instance().renderCharCounterText();
    const charCounterStr = determineCharCounterTextValue(charLimit, inputValue);

    expect(charCounter).toEqual(<CharCounterText>{charCounterStr}</CharCounterText>)
  });

  it('renderCharCounterText should return null if charLimit is 0', () => {
    const inputValue = 'hello world';
    const wrapper = shallow(<TextareaInput name="test" value={inputValue}/>);

    const charCounter = wrapper.instance().renderCharCounterText();

    expect(charCounter).toBe(null);
  });

  it('renderCharCounterText should return CharCounterText if charLimit greater than 0 and char limit has been passed', () => {
    const charLimit = 10;
    const inputValue = 'hello world, there is some text';
    const wrapper = mount(<TextareaInput name="test" value={inputValue} charLimit={charLimit}/>);

    const charCounter = wrapper.instance().renderCharCounterText();
    const charCounterStr = determineCharCounterTextValue(charLimit, inputValue);

    expect(charCounter).toEqual(<CharCounterErrorText>{charCounterStr}</CharCounterErrorText>)
  });

  it('renderCharCounterText should return null if charLimit is 0', () => {
    const inputValue = 'hello world, there is some text';
    const wrapper = shallow(<TextareaInput name="test" value={inputValue}/>);

    const charCounter = wrapper.instance().renderCharCounterText();

    expect(charCounter).toBe(null);
  });

  it('focusOnTextarea should set focused state to true', () => {
    const wrapper = mount(<TextareaInput name="test" />);

    expect(wrapper.state().focused).toBeFalsy();

    wrapper.instance().focusOnTextarea();
    setTimeout(() => {
      expect(wrapper.state().focused).toBeTruthy();
    }, 0);
  });

  it('onContainerMouseDown sets cancelBlur to true on state', () => {
    const wrapper = mount(<TextareaInput name="test" />);

    expect(wrapper.state().cancelBlur).toBeFalsy();

    wrapper.instance().focusOnTextarea();

    wrapper.instance().cancelBlur();
    expect(wrapper.state().cancelBlur).toBeTruthy();
  });

  it('onContainerMouseUp sets cancelBlur to true on state', () => {
    const wrapper = mount(<TextareaInput name="test" />);

    wrapper.instance().focusOnTextarea();

    wrapper.instance().cancelBlur();
    expect(wrapper.state().cancelBlur).toBeTruthy();

    wrapper.instance().removeCancelBlur();
    expect(wrapper.state().cancelBlur).toBeFalsy();
  });

  it('onChange should set the state with the new value from the input', () => {
    jest.useFakeTimers();
    const onChangePropSyp = jest.fn();
    const preventSpy = jest.fn();
    const newValue = 'Some text';
    const event = {
      preventDefault: preventSpy,
      target: {
        value: newValue
      }
    };

    const wrapper = mount(<TextareaInput name="test" onChange={onChangePropSyp} />);

    expect(wrapper.state().value).toBeFalsy();

    wrapper.instance().onChange(event);

    setTimeout(() => {
      expect(preventSpy).toHaveBeenCalled();
      expect(wrapper.state().value).toEqual(newValue);
      expect(onChangePropSyp).toHaveBeenCalledWith(newValue);
    }, 0);

    jest.runAllTimers();

  });

  it('placeholder prop should set a placeholder on the textarea element', () => {
    const placeholder = 'Hello, I am a placeholder';
    const wrapper = mount(<TextareaInput name="test" placeholder={placeholder} />)
    const instance = wrapper.instance();
    const dom = ReactDOM.findDOMNode(instance);
    const textarea = dom.querySelector('textarea');
    expect(textarea.placeholder).toBe(placeholder);
  });
});

describe('charLimitExceeded', () => {
  it(`should return ${false} if there is no charLimit`, () => {
    expect(charLimitExceeded(0, 'hello world')).toBe(false);
  });

  it(`should return ${false} if the string length is equal to the charLimit`, () => {
    const str = 'hello world';
    const charLimit = str.length;
    expect(charLimitExceeded(charLimit, str)).toBe(false)
  });

  it(`should return ${true} if the string length is greater than the charLimit`, () => {
    const str = 'hello world';
    const charLimit = str.length - 1;
    expect(charLimitExceeded(charLimit, str)).toBe(true);
  });
});
<|endoftext|>


<commit_before>;
(function(mag, document, undefined) {

  'use strict'

  var privates = {},
    module = mag.mod,
    render = mag.render,
    fill = mag.fill,
    topModule,
    watch = mag.watch;

  mag.running = false

  // REMOVE THIS? why is this here?
  privates.init = function(container, data) {
    // if (!(this instanceof privates.init)) return new privates.init(container, data)
    // var instance = this
  }
  var redrawing = false
  privates.redraw = function() {
    if (redrawing) {
      // do we ever get here?
      // necessary?
      return
    }
    redrawing = true
    render.redraw(module || render.module || {}, fill, watch)
    redrawing = false
  }

  privates.withProp = function(prop, withAttrCallback) {
    return function(e) {
      e = e || event;
      var currentTarget = e.currentTarget || this;
      withAttrCallback(prop in currentTarget ? currentTarget[prop] : currentTarget.getAttribute(prop))
    }
  }

  privates.prop = function(store) {
    var prop = function() {
      if (arguments.length) {
        store = arguments[0]
        privates.redraw()
      }
      return store
    }

    prop.type = 'fun'

    prop.toJSON = function() {
      // return a copy
      return store
    }

    return prop
  }

  var unloaders = []
  privates.module = function(domElementId, moduleObject, props) {

    var index = render.roots.indexOf(domElementId)

    // clear cache if exists
    if (props && !props.retain) render.clear(index, domElementId, fill)
    // create new index on roots
    if (index < 0) index = render.roots.length;

    //unloaders that exists?

    var isPrevented = false;
    var event = {
      preventDefault: function() {
        isPrevented = true
      }
    };
    for (var i = 0, unloader; unloader = unloaders[i]; i++) {
      unloader.handler(event)
      unloader.controller.onunload = null
    }
    if (isPrevented) {
      for (var i = 0, unloader; unloader = unloaders[i]; i++) unloader.controller.onunload = unloader.handler
    } else unloaders = []

    if (isPrevented) return

    //DOM
    var element = document.getElementById(domElementId)
    if (!element) return Error('invalid node')

    // var parentElement = element.parentNode
    // var tempEle = document.createElement("span")
    // parentElement.replaceChild(tempEle, element)

    render.roots[index] = element.id

    //MODULE
    if (!moduleObject.view) return Error('module requires a view')

    var mod = module.submodule(moduleObject, [props || {}])

    //var currentModule = topModule = mod = mod || {}
    var controller = new mod.controller

    //this conditional ensures only the last recursive module call is applied
    // REALLY necessary?
    //if (currentModule === topModule) {

    module.controllers[index] = controller
    if (controller.onunload) unloaders.push({
      controller: controller,
      handler: controller.onunload
    })

    module.modules[index] = mod
    module.elements[index] = element

    // }

    //INTERPOLATIONS
    privates.redraw()

    //DOM
    // parentElement.replaceChild(element, tempEle)

    // call controller unloaders ?
    // check if was in previous and now not for the same node
    // add to fill.js

    // console.log(self.running)
    // // call onload if present in controller
    if (controller.onload && !mag.running) render.callOnload(module)
    return {
      _html: element.innerHTML
    }
    // return instance ?
    // module.controllers[index]
  }

  var interfaces = function(method) {
    return function() {
      return privates[method].apply(this, arguments)
    }
  }

  var api = interfaces('init')

  api['module'] = interfaces('module')
  api['prop'] = interfaces('prop')
  api['redraw'] = interfaces('redraw')
  api['withProp'] = interfaces('withProp')

  for (var k in api) mag[k] = api[k]

})(window.mag || {}, document)
<commit_msg>Proxy support added<commit_after>;
(function(mag, document, undefined) {

  'use strict'

  var privates = {},
    module = mag.mod,
    render = mag.render,
    fill = mag.fill,
    topModule,
    watch = mag.watch;

  mag.running = false

  // REMOVE THIS? why is this here?
  privates.init = function(container, data) {
    // if (!(this instanceof privates.init)) return new privates.init(container, data)
    // var instance = this
  }
  var redrawing = false
  privates.redraw = function() {
    if (redrawing) {
      // do we ever get here?
      // necessary?
      return
    }
    redrawing = true
    render.redraw(module || render.module || {}, fill, watch)
    redrawing = false
  }

  privates.withProp = function(prop, withAttrCallback) {
    return function(e) {
      e = e || event;
      var currentTarget = e.currentTarget || this;
      withAttrCallback(prop in currentTarget ? currentTarget[prop] : currentTarget.getAttribute(prop))
    }
  }

  privates.prop = function(store) {
    var prop = function() {
      if (arguments.length) {
        store = arguments[0]
        privates.redraw()
      }
      return store
    }

    prop.type = 'fun'

    prop.toJSON = function() {
      // return a copy
      return store
    }

    return prop
  }

  var unloaders = []
    privates.module = function(domElementId, moduleObject, props) {

      var index = render.roots.indexOf(domElementId)

      // clear cache if exists
      if (props && !props.retain) render.clear(index, domElementId, fill)
      // create new index on roots
      if (index < 0) index = render.roots.length;

      //unloaders that exists?

      var isPrevented = false;
      var event = {
        preventDefault: function() {
          isPrevented = true
        }
      };
      for (var i = 0, unloader; unloader = unloaders[i]; i++) {
        unloader.handler(event)
        unloader.controller.onunload = null
      }
      if (isPrevented) {
        for (var i = 0, unloader; unloader = unloaders[i]; i++) unloader.controller.onunload = unloader.handler
      } else unloaders = []

      if (isPrevented) return

      //DOM
      var element = document.getElementById(domElementId)
      if (!element) return Error('invalid node')

      // var parentElement = element.parentNode
      // var tempEle = document.createElement("span")
      // parentElement.replaceChild(tempEle, element)

      render.roots[index] = element.id

      //MODULE
      if (!moduleObject.view) return Error('module requires a view')

      var mod = module.submodule(moduleObject, [props || {}])

      //var currentModule = topModule = mod = mod || {}
      var controller = new mod.controller

      // FireFox only
      if (typeof Proxy !== 'undefined') {

        controller = new Proxy(controller, {
          get: function(target, prop) {
          if (target[prop] === undefined && ['watchers', 'toJSON', 'called', 'onload', 'onunload'].indexOf(prop) === -1) {
            var a = fill.find(element, prop), v
            if (a[0]) {
              if (a[0].value && a[0].value.length > 0)
                v= a[0].value
              if (a[0].innerText && a[0].innerText.length > 0)
                v= a[0].innerText
              if (a[0].innerHTML && a[0].innerHTML.length > 0)
                v= a[0].innerHTML
            }
            return v
          }
            return target[prop]
          }
        })
      }

      //this conditional ensures only the last recursive module call is applied
      // REALLY necessary?
      //if (currentModule === topModule) {

      module.controllers[index] = controller
      if (controller.onunload) unloaders.push({
        controller: controller,
        handler: controller.onunload
      })

      module.modules[index] = mod
      module.elements[index] = element

      // }

      //INTERPOLATIONS
      privates.redraw()

      //DOM
      // parentElement.replaceChild(element, tempEle)

      // call controller unloaders ?
      // check if was in previous and now not for the same node
      // add to fill.js

      // console.log(self.running)
      // // call onload if present in controller
      if (controller.onload && !mag.running) render.callOnload(module)
      return {
        _html: element.innerHTML
      }
      // return instance ?
      // module.controllers[index]
    }

  var interfaces = function(method) {
    return function() {
      return privates[method].apply(this, arguments)
    }
  }

  var api = interfaces('init')

  api['module'] = interfaces('module')
  api['prop'] = interfaces('prop')
  api['redraw'] = interfaces('redraw')
  api['withProp'] = interfaces('withProp')

  for (var k in api) mag[k] = api[k]

})(window.mag || {}, document)
<|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.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");
      process.emit('effect', {name:'tone'});
      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>journal not trash<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 timestamp =JSON.stringify(new Date()).slice(1,-2);

  var cmd = [
             'mkdir',
             '-p',
             [process.env.HOME,'tmp',timestamp].join('/'),
             '&&',
             'mv',
             [process.env.HOME,'tmp','?.wav'].join('/'),
             [process.env.HOME,'tmp',timestamp].join('/'),
             '',
             ].join(' ');
  console.error("TRASH: %s", cmd);
  var rm = piphone.mods.cp.spawn('bash', ['-c',cmd]);
  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");
      process.emit('effect', {name:'tone'});
      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|>


<commit_before>/*
 * Copyright 2014 - 2015 Björn Ricks <bjoern.ricks@gmail.com>
 *
 */
(function() {
    'use strict';

    angular.module('angTutApp',
        ['ngRoute', 'ngMaterial']
    )
    .config(app_config)
    .factory('conf', conf)
    .factory('examples', examples)
    .factory('menu', menu)
    .directive('hl', hl)
    .directive('atHljs', at_hljs)
    .controller('HomeCtrl', home_ctrl)
    .controller('SectionCtrl', section_ctrl)
    .controller('ExampleDetailCtrl', example_detail_ctrl);

    app_config.$inject = ['$routeProvider'];

    function app_config($routeProvider) {
        $routeProvider.
        when('/', {
                templateUrl: 'home.html'
        }).
        when('/examples/:exampleNumber', {
                templateUrl: 'example-view.html',
                controller: 'ExampleDetailCtrl'
        }).
        when('/sections/:sectionNumber', {
                templateUrl: 'section-view.html',
                controller: 'SectionCtrl'
        }).
        otherwise({
                redirectTo: '/'
        });
    }

    function conf() {
        var service = {
            examplesPath: 'examples/',
        };
        return service;
    }

    examples.$inject = ['$http'];

    function examples($http) {
        function getData(callback) {
            $http({
                method: 'GET',
                url: 'examples.json',
                cache: true
            }).success(callback);
        }
        return {
            list: getData,
            find: function(exampleNumber, callback) {
                getData(function(data) {
                    var index = parseInt(exampleNumber);
                    angular.forEach(data, function(section, key) {
                        angular.forEach(section.examples, function(example, key) {
                            if (example.id == index) {
                                callback(example);
                            }
                        });
                    });
                });
            },
            findSection: function(sectionNumber, callback) {
                getData(function(data) {
                    var index = parseInt(sectionNumber);
                    angular.forEach(data, function(section, key) {
                        if (section.id == index) {
                            callback(section);
                        }
                    });
                });
            },
            findSectionFromExample: function(exampleNumber, callback) {
                getData(function(data) {
                    var index = parseInt(exampleNumber);
                    angular.forEach(data, function(section, key) {
                        angular.forEach(section.examples, function(example, key) {
                            if (example.id == index) {
                                callback(section);
                            }
                        });
                    });
                });
            },
        };
    }

    function menu() {
        var openedsection = null;
        var service = {
            selectSection: function(section) {
                openedsection = section;
            },
            toggleSelectSection: function(section) {
                openedsection = (openedsection === section ? null : section);
            },
            isSectionSelected: function(section) {
                return openedsection !== null && openedsection.id === section.id;
            },
            getSectionName: function() {
                if (!openedsection) {
                    return "";
                }
                return openedsection.name;
            },
            setSelectSection: function(section) {
                openedsection = section;
            }
        };
        return service;
    }

    home_ctrl.$inject = ['$scope', 'examples', 'menu'];

    function home_ctrl($scope, examples, menu) {
        $scope.menu = menu;
        examples.list(function(examples) {
            $scope.examples = examples;
        });
    }

    example_detail_ctrl.$inject = ['$scope', '$routeParams', '$http', '$sce', 'conf',
        'examples', 'menu'];

    function example_detail_ctrl($scope, $routeParams, $http, $sce, conf, examples, menu) {
        $scope.menu = menu;
        examples.find($routeParams.exampleNumber, function(example) {
            $scope.example = example;
            var examplePath = conf.examplesPath + example.id + '/';
            $scope.runUrl = examplePath + 'index.html';
            $scope.readme = examplePath + 'readme.html';
        });
    }

    section_ctrl = ['$scope', '$routeParams', '$http', '$sce', 'conf', 'examples', 'menu'];

    function section_ctrl($scope, $routeParams, $http, $sce, conf, examples, menu) {
        $scope.menu = menu;
        examples.findSection($routeParams.sectionNumber, function(section) {
            $scope.section = section;
            $scope.readme = conf.examplesPath + 'section' + section.id + '.html';
        });
    }

    at_hljs.$inject = ['conf'];

    function at_hljs(conf) {
        return {
            scope: { file: '=', example: '=' },
            restrict: 'A',
            controller: function($scope, $http) {
                var path = [
                    conf.examplesPath,
                    $scope.example.id,
                    $scope.file
                ].join('/');
                $http.get(path).success(function(data) {
                    if (typeof(data) === 'object') {
                        // un-parse auto-parsed JSON files for presentation as text
                        data = JSON.stringify(data, null, 2);
                    } else {
                        // Remove trailing newlines from code presentation
                        data = data.trim();
                    }
                    $scope.content = data;
                });
            },
            link: function(scope, element, attrs) {
                var contentParent = angular.element(
                    '

'
                );
                element.append(contentParent);
                scope.$watch('content', function(data) {
                    if (data) {
                        var codeElement = contentParent.find('code');
                        var highlightedCode = hljs.highlightAuto(data);
                        codeElement.append(
                            highlightedCode.value).addClass('highlight');
                    }
                });
            }
        };
    }

    function hl() {
        var directive = {
            restrict: 'A',
            link: link,
        };
        return directive;

        function link(scope, element, attrs) {
            var d = element.html();
            var contentParent = angular.element(
                '

'
            );
            var codeElement = contentParent.find('code');

            element.empty();
            element.append(contentParent);

            var highlightedCode = hljs.highlightAuto(d);
            console.log(highlightedCode);
            codeElement.html(highlightedCode.value).addClass('highlight');
        }
    }
})();
<commit_msg>Set the current selected section in example controller<commit_after>/*
 * Copyright 2014 - 2015 Björn Ricks <bjoern.ricks@gmail.com>
 *
 */
(function() {
    'use strict';

    angular.module('angTutApp',
        ['ngRoute', 'ngMaterial']
    )
    .config(app_config)
    .factory('conf', conf)
    .factory('examples', examples)
    .factory('menu', menu)
    .directive('hl', hl)
    .directive('atHljs', at_hljs)
    .controller('HomeCtrl', home_ctrl)
    .controller('SectionCtrl', section_ctrl)
    .controller('ExampleDetailCtrl', example_detail_ctrl);

    app_config.$inject = ['$routeProvider'];

    function app_config($routeProvider) {
        $routeProvider.
        when('/', {
                templateUrl: 'home.html'
        }).
        when('/examples/:exampleNumber', {
                templateUrl: 'example-view.html',
                controller: 'ExampleDetailCtrl'
        }).
        when('/sections/:sectionNumber', {
                templateUrl: 'section-view.html',
                controller: 'SectionCtrl'
        }).
        otherwise({
                redirectTo: '/'
        });
    }

    function conf() {
        var service = {
            examplesPath: 'examples/',
        };
        return service;
    }

    examples.$inject = ['$http'];

    function examples($http) {
        function getData(callback) {
            $http({
                method: 'GET',
                url: 'examples.json',
                cache: true
            }).success(callback);
        }
        return {
            list: getData,
            find: function(exampleNumber, callback) {
                getData(function(data) {
                    var index = parseInt(exampleNumber);
                    angular.forEach(data, function(section, key) {
                        angular.forEach(section.examples, function(example, key) {
                            if (example.id == index) {
                                callback(example);
                            }
                        });
                    });
                });
            },
            findSection: function(sectionNumber, callback) {
                getData(function(data) {
                    var index = parseInt(sectionNumber);
                    angular.forEach(data, function(section, key) {
                        if (section.id == index) {
                            callback(section);
                        }
                    });
                });
            },
            findSectionFromExample: function(exampleNumber, callback) {
                getData(function(data) {
                    var index = parseInt(exampleNumber);
                    angular.forEach(data, function(section, key) {
                        angular.forEach(section.examples, function(example, key) {
                            if (example.id == index) {
                                callback(section);
                            }
                        });
                    });
                });
            },
        };
    }

    function menu() {
        var openedsection = null;
        var service = {
            selectSection: function(section) {
                openedsection = section;
            },
            toggleSelectSection: function(section) {
                openedsection = (openedsection === section ? null : section);
            },
            isSectionSelected: function(section) {
                return openedsection !== null && openedsection.id === section.id;
            },
            getSectionName: function() {
                if (!openedsection) {
                    return "";
                }
                return openedsection.name;
            },
            setSelectSection: function(section) {
                openedsection = section;
            }
        };
        return service;
    }

    home_ctrl.$inject = ['$scope', 'examples', 'menu'];

    function home_ctrl($scope, examples, menu) {
        $scope.menu = menu;
        examples.list(function(examples) {
            $scope.examples = examples;
        });
    }

    example_detail_ctrl.$inject = ['$scope', '$routeParams', '$http', '$sce', 'conf',
        'examples', 'menu'];

    function example_detail_ctrl($scope, $routeParams, $http, $sce, conf, examples, menu) {
        $scope.menu = menu;
        examples.find($routeParams.exampleNumber, function(example) {
            $scope.example = example;
            var examplePath = conf.examplesPath + example.id + '/';
            $scope.runUrl = examplePath + 'index.html';
            $scope.readme = examplePath + 'readme.html';
        });

        examples.findSectionFromExample($routeParams.exampleNumber, function(section) {
            menu.setSelectSection(section);
        });
    }

    section_ctrl = ['$scope', '$routeParams', '$http', '$sce', 'conf', 'examples', 'menu'];

    function section_ctrl($scope, $routeParams, $http, $sce, conf, examples, menu) {
        $scope.menu = menu;
        examples.findSection($routeParams.sectionNumber, function(section) {
            $scope.section = section;
            $scope.readme = conf.examplesPath + 'section' + section.id + '.html';
        });
    }

    at_hljs.$inject = ['conf'];

    function at_hljs(conf) {
        return {
            scope: { file: '=', example: '=' },
            restrict: 'A',
            controller: function($scope, $http) {
                var path = [
                    conf.examplesPath,
                    $scope.example.id,
                    $scope.file
                ].join('/');
                $http.get(path).success(function(data) {
                    if (typeof(data) === 'object') {
                        // un-parse auto-parsed JSON files for presentation as text
                        data = JSON.stringify(data, null, 2);
                    } else {
                        // Remove trailing newlines from code presentation
                        data = data.trim();
                    }
                    $scope.content = data;
                });
            },
            link: function(scope, element, attrs) {
                var contentParent = angular.element(
                    '

'
                );
                element.append(contentParent);
                scope.$watch('content', function(data) {
                    if (data) {
                        var codeElement = contentParent.find('code');
                        var highlightedCode = hljs.highlightAuto(data);
                        codeElement.append(
                            highlightedCode.value).addClass('highlight');
                    }
                });
            }
        };
    }

    function hl() {
        var directive = {
            restrict: 'A',
            link: link,
        };
        return directive;

        function link(scope, element, attrs) {
            var d = element.html();
            var contentParent = angular.element(
                '

'
            );
            var codeElement = contentParent.find('code');

            element.empty();
            element.append(contentParent);

            var highlightedCode = hljs.highlightAuto(d);
            console.log(highlightedCode);
            codeElement.html(highlightedCode.value).addClass('highlight');
        }
    }
})();
<|endoftext|>


<commit_before>var config = require(__dirname + "/config.js"),
    db = require(__dirname + "/database.js"),
    md5 = require(__dirname + "/md5.js"),
    utils = require(__dirname + "/utils.js"),
    mongodb = require("mongodb"),
    objectid = mongodb.BSONPure.ObjectID,
    datetime = require(__dirname + "/datetime.js"),
    errorcodes = require(__dirname + "/errorcodes.js").errorcodes;

var leaderboards = module.exports = {

    /**
     * Lists scores from a leaderboard table
     * @param options:  table, url, highest, mode, page, perpage, filters ass. array, friendslist,
     * @param callback function (error, errorcode, numscores, scores)
     */
    list:function (options, callback) {

        // defaults
        if(!options.page) {
            options.page = 1;
        }

        if(!options.perpage) {
            options.perpage = 20;
        }

        if(!options.highest && !options.lowest) {
            options.highest = true;
        }

        var query = {

            filter: {
                publickey: options.publickey,
                table: options.table
            },

            limit: options.perpage,
            skip: (options.page - 1) * options.perpage,
            sort: {},
            cache: true,
            cachetime: 120
        };

        // per-source leaderboards, originally this
        // was to separate websites but you could use it as
        // any additional string filter with a database index
        if(options.source) {
            query.filter.source = utils.baseurl(options.source);
        }

        // filters for custom fields
        for(var x in options.filters) {
            query.filter["fields." + x] = options.filters[x];
        }

        // filtering for friends, maximum 100 and we can't tell what is
        // the 100 important ones so that has to be determined on the
        // client side
        if(options.friendslist) {
            if(options.friendslist.length > 100) {
                options.friendslist.length = 100;
            }

            query.filter.playerid = { $in: options.friendslist };
        }

        if(options.playerid) {
            query.filter.playerid = options.playerid;
        }

        // date mode
        switch(options.mode) {
            case "today":
                query.filter.date = {"$gte": datetime.now - (24 * 60 * 60)};
                break;

           case "last7days":
               query.filter.date = {"$gte": (datetime.now - (7 * 24 * 60 * 60))};
                break;

            case "last30days":
                query.filter.date = {"$gte": (datetime.now - (30 * 24 * 60 * 60))};
                break;
        }

        // sorting
        if(options.mode == "newest") {
            query.sort = { date: -1 };
        } else {
            query.sort = { points: options.highest || !options.lowest ? -1 : 1 };
        }

        // the scores
        db.playtomic.leaderboard_scores.getAndCount(query, function(error, scores, numscores){

            if(error) {
                //console.log(JSON.stringify(query));
                callback("unable to load scores: " + error + " (api.leaderboards.list:104)", errorcodes.GeneralError);
                return;
            }

            // clean up scores
            if(!scores) {
                scores = [];
            }

            callback(null, errorcodes.NoError, numscores, clean(scores, query.skip));
        });
    },

    /**
     * Saves a score
     * @param options: url, name, points, auth, playerid, table, highest, allowduplicates, customfields ass. array
     * @param callback function(error, errorcode)
     */
    save: function(options, callback) {

        // defaults
        if(!options.source) {
            options.source = "";
        }

        if(!options.name) {
            callback("no name (" + options.name + ")", errorcodes.InvalidName);
            return;
        }

        if(!options.table) {
            callback("no table name (" + options.table + ")", errorcodes.InvalidName);
            return;
        }

        if(!options.highest && !options.lowest) {
            //console.log("assuming highest");
            options.highest = true;
        }

        // small cleanup
        var score = {};

        // fields that just aren't relevant, by doing it this way it's easier to extend because you can
        // just add more fields directly in your game and they will end up in your scores and returned
        // to your game
        var exclude = ["allowduplicates", "highest", "lowest", "numfields", "section", "action",
                        "ip", "date", "url", "rank", "points", "page", "perpage", "global", "filters"];

        for(var x in options) {
            if(exclude.indexOf(x) > -1) {
                continue;
            }

            score[x] = options[x];
        }

        score.hash = md5(options.ip + "." +
                         options.table + "." +
                         options.name + "." +
                         options.playerid + "." +
                         (options.highest || false) + "." +
                         (options.lowest || false) + "." +
                         options.source);
        score.points = options.points;
        score.date = datetime.now;

        // check bans

        // insert
        if(options.hasOwnProperty("allowduplicates") && options.allowduplicates) {

            console.log("*** DUPLICATES ALLOWED");

            db.playtomic.leaderboard_scores.insert({doc: score, safe: true}, function(error, item) {

                if(error) {
                    callback("unable to insert score: " + error + " (api.leaderboards.save:192)", errorcodes.GeneralError);
                    return;
                }

                callback(null, errorcodes.NoError);
            });

            return;
        }

        // update if it's better or worse
		console.log("*** CHECKING FOR DUPLICATES");

        // check for duplicates, by default we will assume highest unless
        // lowest is explicitly specified
        var dupequery = {
            filter: {
                hash: score.hash
            },
            limit: 1,
            cache: false,
            sort: options.highest ? {score : -1 } : {score: 1 }
        };

        db.playtomic.leaderboard_scores.get(dupequery, function(error, items) {

            // no duplicates
            if(items.length == 0) {

                db.playtomic.leaderboard_scores.insert({doc: score}, function(error, item) {

                    if(error) {
                        callback("unable to insert score: " + error + " (api.leaderboards.save:212)", errorcodes.GeneralError);
                        return;
                    }

                    callback(null, errorcodes.NoError);
                });

                return;
            }

            // check if the new score is higher or lower
            var dupe = items[0];

            if((dupe.points <= score.points && options.highest) || (dupe.points >= score.points && options.lowest)) {

				score._id = dupe._id;

                var query = {
                    filter: { _id: dupe._id },
                    doc: score
                };

                db.playtomic.leaderboard_scores.update(query, function(error, item) {

                    if(error) {
                        callback("unable to update score: " + error + " (api.leaderboards.save:240)", errorcodes.GeneralError);
                        return;
                    }

                    callback(null, errorcodes.NoError);
                });
            } else {
                //console.log("rejecting");
                callback(null, errorcodes.NotBestScore);
            }
        });
    },

    saveAndList: function(options, callback) {

        leaderboards.save(options, function(error, errorcode) {

            if(error) {
                callback(error + " (api.leaderboards.saveAndList:232)", errorcode);
                return;
            }

            if(options.playerid && options.excludeplayerid) {
                delete(options.playerid);
                delete(options.excludeplayerid);
            }

            // get scores before or after
            var query = {
                filter: {
                    publickey: options.publickey,
                    table: options.table
                },
                sort: options.highest ? {score : -1 } : {score: 1 },
                cache: true,
                cachetime: 120
            };

            if(options.highest || !options.lowest) {
                query.filter.points = {"$gte": options.points};
            } else {
                query.filter.points = {"$lte": options.points};
            }

            for(var x in options.fields) {
                query.filter["fields." + x] = options.fields[x];
            }

            if(options.friendslist) {
                if(options.friendslist.length > 100) {
                    options.friendslist.length = 100;
                }

                query.filter.playerid = { $in: options.friendslist }

            }

            var serrorcode = errorcode;

            db.playtomic.leaderboard_scores.count(query, function(error, numscores) {

                if(error) {
                    callback(error + " (api.leaderboards.saveAndList:276)", errorcode);
                    return;
                }

                var page = Math.floor(numscores / options.perpage);
                var rank = page * options.perpage + 1;

                leaderboards.list(options, function(error, errorcode, numscores, scores) {

                    if(error) {
                        callback(error + " (api.leaderboards.saveAndList:293)", errorcode);
                        return;
                    }

                    if(serrorcode > 0) {
                        errorcode = serrorcode;
                    }

                    // clean up scores
                    if(!scores) {
                        scores = [];
                    }

                    callback(null, errorcode, numscores, clean(scores, rank));
                })
            });
        });
    }
};

function clean(scores, baserank) {

    for(var i=0; i<scores.length; i++) {

        var score = scores[i];

        for(var x in score) {
            if(typeof(score[x]) == "String") {
                score[x] = utils.unescape(score[x]);
            }
        }

        for(var x in score.fields) {
            if(typeof(score.fields[x]) == "String") {
                score.fields[x] = utils.unescape(score.fields[x]);
            }
        }

        score.rank = baserank + i + 1;
        score.scoreid = score._id;
        delete score._id;
        delete score.hash;
    }

    return scores;
}<commit_msg>Numerous fixes for saveAndList<commit_after>var config = require(__dirname + "/config.js"),
    db = require(__dirname + "/database.js"),
    md5 = require(__dirname + "/md5.js"),
    utils = require(__dirname + "/utils.js"),
    mongodb = require("mongodb"),
    objectid = mongodb.BSONPure.ObjectID,
    datetime = require(__dirname + "/datetime.js"),
    errorcodes = require(__dirname + "/errorcodes.js").errorcodes;

var leaderboards = module.exports = {

    /**
     * Lists scores from a leaderboard table
     * @param options:  table, url, highest, mode, page, perpage, filters ass. array, friendslist,
     * @param callback function (error, errorcode, numscores, scores)
     */
    list:function (options, callback) {

        // defaults
        if(!options.page) {
            options.page = 1;
        }

        if(!options.perpage) {
            options.perpage = 20;
        }

        if(!options.highest && !options.lowest) {
            options.highest = true;
        }

        var query = {

            filter: {
                publickey: options.publickey,
                table: options.table
            },

            limit: options.perpage,
            skip: (options.page - 1) * options.perpage,
            sort: {},
            cache: true,
            cachetime: 120
        };

        // per-source leaderboards, originally this
        // was to separate websites but you could use it as
        // any additional string filter with a database index
        if(options.source) {
            query.filter.source = utils.baseurl(options.source);
        }

        // filters for custom fields
        for(var x in options.filters) {
            query.filter["fields." + x] = options.filters[x];
        }

        // filtering for friends, maximum 100 and we can't tell what is
        // the 100 important ones so that has to be determined on the
        // client side
        if(options.friendslist) {
            if(options.friendslist.length > 100) {
                options.friendslist.length = 100;
            }

            query.filter.playerid = { $in: options.friendslist };
        }

        if(options.playerid) {
            query.filter.playerid = options.playerid;
        }

        // date mode
        switch(options.mode) {
            case "today":
                query.filter.date = {"$gte": datetime.now - (24 * 60 * 60)};
                break;

           case "last7days":
               query.filter.date = {"$gte": (datetime.now - (7 * 24 * 60 * 60))};
                break;

            case "last30days":
                query.filter.date = {"$gte": (datetime.now - (30 * 24 * 60 * 60))};
                break;
        }

        // sorting
        if(options.mode == "newest") {
            query.sort = { date: -1 };
        } else {
            query.sort = { points: options.highest || !options.lowest ? -1 : 1 };
        }

        // the scores
        db.playtomic.leaderboard_scores.getAndCount(query, function(error, scores, numscores){

            if(error) {
                callback("unable to load scores: " + error + " (api.leaderboards.list:104)", errorcodes.GeneralError);
                return;
            }

            // clean up scores
            if(!scores) {
                scores = [];
            }

            callback(null, errorcodes.NoError, numscores, clean(scores, query.skip));
        });
    },

    /**
     * Saves a score
     * @param options: url, name, points, auth, playerid, table, highest, allowduplicates, customfields ass. array
     * @param callback function(error, errorcode)
     */
    save: function(options, callback) {

        // defaults
        if(!options.source) {
            options.source = "";
        }

        if(!options.name) {
            callback("no name (" + options.name + ")", errorcodes.InvalidName);
            return;
        }

        if(!options.table) {
            callback("no table name (" + options.table + ")", errorcodes.InvalidName);
            return;
        }

        if(!options.highest && !options.lowest) {
            options.highest = true;
        }

        // small cleanup
        var score = {};

        // fields that just aren't relevant, by doing it this way it's easier to extend because you can
        // just add more fields directly in your game and they will end up in your scores and returned
        // to your game
        var exclude = ["allowduplicates", "highest", "lowest", "numfields", "section", "action",
                        "ip", "date", "url", "rank", "points", "page", "perpage", "global", "filters", "debug"];

        for(var x in options) {
            if(exclude.indexOf(x) > -1) {
                continue;
            }

            score[x] = options[x];
        }

        score.hash = md5(options.ip + "." +
                         options.table + "." +
                         options.name + "." +
                         options.playerid + "." +
                         (options.highest || false) + "." +
                         (options.lowest || false) + "." +
                         options.source);
        score.points = options.points;
        score.date = datetime.now;

        // check bans

        // insert
        if(options.hasOwnProperty("allowduplicates") && options.allowduplicates) {

            db.playtomic.leaderboard_scores.insert({doc: score, safe: true}, function(error, item) {

                if(error) {
                    callback("unable to insert score: " + error + " (api.leaderboards.save:192)", errorcodes.GeneralError);
                    return;
                }

                callback(null, errorcodes.NoError);
            });

            return;
        }

        // check for duplicates, by default we will assume highest unless
        // lowest is explicitly specified
        var dupequery = {
            filter: {
                hash: score.hash
            },
            limit: 1,
            cache: false,
            sort: options.highest ? {score : -1 } : {score: 1 }
        };

        db.playtomic.leaderboard_scores.get(dupequery, function(error, items) {

            // no duplicates
            if(items.length == 0) {

                db.playtomic.leaderboard_scores.insert({doc: score}, function(error, item) {

                    if(error) {
                        callback("unable to insert score: " + error + " (api.leaderboards.save:212)", errorcodes.GeneralError);
                        return;
                    }

                    callback(null, errorcodes.NoError);
                });

                return;
            }

            // check if the new score is higher or lower
            var dupe = items[0];

            if((dupe.points <= score.points && options.highest) || (dupe.points >= score.points && options.lowest)) {

				score._id = dupe._id;

                var query = {
                    filter: { _id: dupe._id },
                    doc: score
                };

                db.playtomic.leaderboard_scores.update(query, function(error, item) {

                    if(error) {
                        callback("unable to update score: " + error + " (api.leaderboards.save:240)", errorcodes.GeneralError);
                        return;
                    }

                    callback(null, errorcodes.NoError);
                });
            } else {
                callback(null, errorcodes.NotBestScore);
            }
        });
    },

    saveAndList: function(options, callback) {

        leaderboards.save(options, function(error, errorcode) {

            if(error) {
                callback(error + " (api.leaderboards.saveAndList:232)", errorcode);
                return;
            }

            // get scores before or after
            var query = {
                filter: {
                    publickey: options.publickey,
                    table: options.table
                },
                sort: options.highest ? {score : -1} : {score: 1},
                cache: true,
                cachetime: 120
            };

			if(options.playerid && !options.excludeplayerid) {
				query.filter.playerid = options.playerid;
			}

            if(options.highest || !options.lowest) {
                query.filter.points = {"$gte": options.points};
            } else {
                query.filter.points = {"$lte": options.points};
            }

            for(var x in options.fields) {
                query.filter["fields." + x] = options.fields[x];
            }

            if(options.friendslist) {
                if(options.friendslist.length > 100) {
                    options.friendslist.length = 100;
                }

                query.filter.playerid = { $in: options.friendslist }

            }

            var serrorcode = errorcode;

            db.playtomic.leaderboard_scores.count(query, function(error, numscores) {

                if(error) {
                    callback(error + " (api.leaderboards.saveAndList:276)", errorcode);
                    return;
                }

                var page = Math.floor(numscores / options.perpage);
                var rank = page * options.perpage;
				options.page = page + 1;

                leaderboards.list(options, function(error, errorcode, numscores, scores) {

                    if(error) {
                        callback(error + " (api.leaderboards.saveAndList:293)", errorcode);
                        return;
                    }

                    if(serrorcode > 0) {
                        errorcode = serrorcode;
                    }

                    // clean up scores
                    if(!scores) {
                        scores = [];
                    }

                    callback(null, errorcode, numscores, clean(scores, rank));
                })
            });
        });
    }
};

function clean(scores, baserank) {

    for(var i=0; i<scores.length; i++) {

        var score = scores[i];

        for(var x in score) {
            if(typeof(score[x]) == "String") {
                score[x] = utils.unescape(score[x]);
            }
        }

        for(var x in score.fields) {
            if(typeof(score.fields[x]) == "String") {
                score.fields[x] = utils.unescape(score.fields[x]);
            }
        }

        score.rank = baserank + i + 1;
        score.scoreid = score._id;
        delete score._id;
        delete score.hash;
    }

    return scores;
}<|endoftext|>


<commit_before>'use strict';
let git = require(`git-promise`);
let exec = require(`exec-then`);
let del = require(`del`);
let fs = require(`fs`);
let promisify = require(`es6-promisify`);
let log = require(`loglevel`);
let crypto = require(`crypto`);
let uniq = require(`lodash/array/uniq`);

let readFilePromise = promisify(fs.readFile);
let delPromise = promisify(del);
let statPromise = promisify(fs.stat);

module.exports = (cwd, {repo, verbose, crossPlatform}) => {

    let packageJsonSha1;
    let packageJsonVersion;
    log.setLevel(verbose ? `debug`: `info`);
    return readFilePromise(`${cwd}/package.json`, `utf-8`)
    .then((packageJsonContent) => {
        // replace / in hash with _ because git does not allow leading / in tags
        packageJsonSha1 = crypto.createHash(`sha1`).update(packageJsonContent).digest(`base64`).replace(/\//g, "_");
        packageJsonVersion = packageJsonContent.version;
        log.debug(`Sha1 of package.json is ${packageJsonSha1}`);
        return packageJsonSha1;
    })
    .then(() => {
        return statPromise(`${cwd}/node_modules`)
        .then(() => {
            log.debug(`Checking if remote ${repo} exists`);
            process.chdir(`${cwd}/node_modules`);
            return git(`git remote -v`)
            .then((remoteCommandOutput) => {
                if (remoteCommandOutput.indexOf(repo) !== -1) {
                    // repo is in remotes, let`s pull the required version
                    log.debug(`Remote exists, fetching from it`);
                    return git(`git fetch -t ${repo}`);
                }
                return cloneRepo();
            });
        })
        .catch(cloneRepo)
    })
    .then(() => {
        log.debug(`${repo} is in node_modules cwd, checking out ${packageJsonSha1} tag`);
        process.chdir(`${cwd}/node_modules`);
        return git(`checkout tags/${packageJsonSha1}`)
        .then(() => {
            log.debug(`Cleanup checked out commit`);
            return git(`clean -df`);
        })
        .then(() => {
            if (crossPlatform) {
                return rebuildAndIgnorePlatformSpecific();
            }
        })
        .catch(installPackagesTagAndPustToRemote);
    })
    .then(() => {
        process.chdir(`${cwd}`);
        log.info(`Node_modules are in sync with ${repo} ${packageJsonSha1}`);
    })
    .catch((error) => {
        process.chdir(`${cwd}`);
        log.info(`Failed to synchronise node_modules with ${repo}: ${error}`);
        throw error;
    });

    function cloneRepo() {
        log.debug(`Remote ${repo} is not present in ${cwd}/node_modules/.git repo`);
        log.debug(`Removing ${cwd}/node_modules cwd`);
        process.chdir(`${cwd}`);
        return delPromise([`node_modules/`])
        .then(() => {
            log.debug(`cloning ${repo}`);
            return git(`clone ${repo} node_modules`);
        })
    }

    function gitGetUntracked() {
        return git(`status --porcelain --untracked-files=all`)
        .then(result => {
            return result.split('\n').filter(line => line && line.startsWith('??')).map(line => line.substr(3));
        })
    }

    function npmRunCommand(cmd, args) {
        let loglevel = [`--log-level=${verbose ? 'warn' : 'silent'}`];
        return exec(['npm', cmd].concat(loglevel).concat(args), {verbose}, (std, deferred) => {
            deferred.resolve(std.stdout.split('\n'));
        });
    }

    function rebuildAndIgnorePlatformSpecific() {
        log.debug(`Rebuilding package in ${cwd}`);
        process.chdir(`${cwd}`);
        return npmRunCommand('rebuild')
        .then(() => {
            process.chdir(`${cwd}/node_modules`);
            return gitGetUntracked();
        })
        .then((files) => {
            let ignored = [];
            try {
                ignored = fs.readFileSync('.gitignore', {encoding: 'utf8'}).split('\n');
            } catch (e) {
                // ignore errors while reading .gitignore
            }
            ignored = ignored.concat(files);
            ignored.sort();
            ignored = uniq(ignored);
            fs.writeFileSync('.gitignore', ignored.join('\n'), {encoding: 'utf8'});
            return git(`add .gitignore`);
        })
    }

    function installPackagesTagAndPustToRemote() {
        log.debug(`Requested tag does not exist, remove everything from node_modules and do npm install`);
        return git(`checkout master`)
        .then(() => {
            return delPromise([`**`, `!.git/`])
        })
        .then(() => {
            process.chdir(`${cwd}`);
            return npmRunCommand(`install`, crossPlatform ? ['--ignore-scripts'] : []);
        })
        .then(() => {
            log.debug(`All packages installed`);
            process.chdir(`${cwd}/node_modules`);
            return git(`add .`);
        })
        .then(() => {
            if (crossPlatform) {
                return rebuildAndIgnorePlatformSpecific();
            }
        })
        .then(() => {
            return npmRunCommand(`--version`);
        })
        .then((npmVersion) => {
            process.chdir(`${cwd}/node_modules`);
            return git(`commit -a -m "sealing package.json dependencies of version ${packageJsonVersion}, using npm ${npmVersion[0]}"`);
        })
        .then(() => {
            log.debug(`Committed, adding tag`);
            return git(`tag ${packageJsonSha1}`);
        })
        .then(() => {
            log.debug(`Pushing tag ${packageJsonSha1} to ${repo}`);
            return git(`push ${repo} master --tags`);
        })
    }
};

<commit_msg>make sure that we are in node_modules before working with that repo<commit_after>'use strict';
let git = require(`git-promise`);
let exec = require(`exec-then`);
let del = require(`del`);
let fs = require(`fs`);
let promisify = require(`es6-promisify`);
let log = require(`loglevel`);
let crypto = require(`crypto`);
let uniq = require(`lodash/array/uniq`);

let readFilePromise = promisify(fs.readFile);
let delPromise = promisify(del);
let statPromise = promisify(fs.stat);

module.exports = (cwd, {repo, verbose, crossPlatform}) => {

    let packageJsonSha1;
    let packageJsonVersion;
    log.setLevel(verbose ? `debug`: `info`);
    return readFilePromise(`${cwd}/package.json`, `utf-8`)
    .then((packageJsonContent) => {
        // replace / in hash with _ because git does not allow leading / in tags
        packageJsonSha1 = crypto.createHash(`sha1`).update(packageJsonContent).digest(`base64`).replace(/\//g, "_");
        packageJsonVersion = packageJsonContent.version;
        log.debug(`Sha1 of package.json is ${packageJsonSha1}`);
        return packageJsonSha1;
    })
    .then(() => {
        return statPromise(`${cwd}/node_modules`)
        .then(() => {
            log.debug(`Checking if remote ${repo} exists`);
            process.chdir(`${cwd}/node_modules`);
            return git(`git remote -v`)
            .then((remoteCommandOutput) => {
                if (remoteCommandOutput.indexOf(repo) !== -1) {
                    // repo is in remotes, let`s pull the required version
                    log.debug(`Remote exists, fetching from it`);
                    return git(`git fetch -t ${repo}`);
                }
                return cloneRepo();
            });
        })
        .catch(cloneRepo)
    })
    .then(() => {
        log.debug(`${repo} is in node_modules cwd, checking out ${packageJsonSha1} tag`);
        process.chdir(`${cwd}/node_modules`);
        return git(`checkout tags/${packageJsonSha1}`)
        .then(() => {
            log.debug(`Cleanup checked out commit`);
            return git(`clean -df`);
        })
        .then(() => {
            if (crossPlatform) {
                return rebuildAndIgnorePlatformSpecific();
            }
        })
        .catch(installPackagesTagAndPustToRemote);
    })
    .then(() => {
        process.chdir(`${cwd}`);
        log.info(`Node_modules are in sync with ${repo} ${packageJsonSha1}`);
    })
    .catch((error) => {
        process.chdir(`${cwd}`);
        log.info(`Failed to synchronise node_modules with ${repo}: ${error}`);
        throw error;
    });

    function cloneRepo() {
        log.debug(`Remote ${repo} is not present in ${cwd}/node_modules/.git repo`);
        log.debug(`Removing ${cwd}/node_modules cwd`);
        process.chdir(`${cwd}`);
        return delPromise([`node_modules/`])
        .then(() => {
            log.debug(`cloning ${repo}`);
            return git(`clone ${repo} node_modules`);
        })
    }

    function gitGetUntracked() {
        return git(`status --porcelain --untracked-files=all`)
        .then(result => {
            return result.split('\n').filter(line => line && line.startsWith('??')).map(line => line.substr(3));
        })
    }

    function npmRunCommand(cmd, args) {
        let loglevel = [`--log-level=${verbose ? 'warn' : 'silent'}`];
        return exec(['npm', cmd].concat(loglevel).concat(args), {verbose}, (std, deferred) => {
            deferred.resolve(std.stdout.split('\n'));
        });
    }

    function rebuildAndIgnorePlatformSpecific() {
        log.debug(`Rebuilding package in ${cwd}`);
        process.chdir(`${cwd}`);
        return npmRunCommand('rebuild')
        .then(() => {
            process.chdir(`${cwd}/node_modules`);
            return gitGetUntracked();
        })
        .then((files) => {
            let ignored = [];
            try {
                ignored = fs.readFileSync('.gitignore', {encoding: 'utf8'}).split('\n');
            } catch (e) {
                // ignore errors while reading .gitignore
            }
            ignored = ignored.concat(files);
            ignored.sort();
            ignored = uniq(ignored);
            fs.writeFileSync('.gitignore', ignored.join('\n'), {encoding: 'utf8'});
            return git(`add .gitignore`);
        })
    }

    function installPackagesTagAndPustToRemote() {
        log.debug(`Requested tag does not exist, remove everything from node_modules and do npm install`);
        process.chdir(`${cwd}/node_modules`);
        return git(`checkout master`)
        .then(() => {
            return delPromise([`**`, `!.git/`])
        })
        .then(() => {
            process.chdir(`${cwd}`);
            return npmRunCommand(`install`, crossPlatform ? ['--ignore-scripts'] : []);
        })
        .then(() => {
            log.debug(`All packages installed`);
            process.chdir(`${cwd}/node_modules`);
            return git(`add .`);
        })
        .then(() => {
            if (crossPlatform) {
                return rebuildAndIgnorePlatformSpecific();
            }
        })
        .then(() => {
            return npmRunCommand(`--version`);
        })
        .then((npmVersion) => {
            process.chdir(`${cwd}/node_modules`);
            return git(`commit -a -m "sealing package.json dependencies of version ${packageJsonVersion}, using npm ${npmVersion[0]}"`);
        })
        .then(() => {
            log.debug(`Committed, adding tag`);
            return git(`tag ${packageJsonSha1}`);
        })
        .then(() => {
            log.debug(`Pushing tag ${packageJsonSha1} to ${repo}`);
            return git(`push ${repo} master --tags`);
        })
    }
};

<|endoftext|>


<commit_before>var tvmaze = require('../');

// parse options

var opt = require('node-getopt').create([
    ['',    'raw',      'include raw details in result'],
]).bindHelp().parseSystem();

var command = opt.argv[0];
var args = opt.argv.slice(1);
if (!command) {
    usageError('need command as first parameter');
}

// set options
tvmaze.options.includeRaw = opt.options.raw;

// parse command

if (command == 'h') {
    usage();
} else if (command === 's') {
    argCountCheck(1);
    tvmaze.search(args[0], printReponse);
} else if (command === 'l') {
    argCountCheck(1);
    tvmaze.episodes(args[0], printReponse);
}

function usage() {
    console.log('h              help');
    console.log('s <show>       search for show');
    console.log('l <id>         list episodes for show id');
}

function argCountCheck(expect) {
    if (args.length != expect)
        usageError('expected ' + expect + ' argument');
}

function usageError(s) {
    console.error(s);
    process.exit(1);
}

function printReponse(err, res) {
    if (err) {
        console.error(err);
        process.exit(1);
    }

    console.log(JSON.stringify(res, null, 2));
}
<commit_msg>cli command to get episode details<commit_after>var tvmaze = require('../');

// parse options

var opt = require('node-getopt').create([
    ['',    'raw',      'include raw details in result'],
]).bindHelp().parseSystem();

var command = opt.argv[0];
var args = opt.argv.slice(1);
if (!command) {
    usageError('need command as first parameter');
}

// set options
tvmaze.options.includeRaw = opt.options.raw;

// parse command

if (command == 'h') {
    usage();
} else if (command === 's') {
    argCountCheck(1);
    tvmaze.search(args[0], printReponse);
} else if (command === 'l') {
    argCountCheck(1);
    tvmaze.episodes(args[0], printReponse);
} else if (command === 'e') {
    argCountCheck(3);
    tvmaze.episodeDetail(args[0], args[1], args[2], printReponse);
}

function usage() {
    console.log('h                                  help');
    console.log('s <show>                           search for show');
    console.log('l <id>                             list episodes for show id');
    console.log('e <id> <season> <ep number>        list episodes for show id');
}

function argCountCheck(expect) {
    if (args.length != expect)
        usageError('expected ' + expect + ' argument');
}

function usageError(s) {
    console.error(s);
    process.exit(1);
}

function printReponse(err, res) {
    if (err) {
        console.error(err);
        process.exit(1);
    }

    console.log(JSON.stringify(res, null, 2));
}
<|endoftext|>