<commit_before>#!/usr/bin/env node
/*
 * Copyright (c) 2015, Daliworks. All rights reserved.
 *
 * Reproduction and/or distribution in source and binary forms
 * without the written consent of Daliworks, Inc. is prohibited.
 *
 */
'use strict';

var _ = require('lodash'),
    util = require('util'),
    events = require('events'),
    logger = require('log4js').getLogger('Sensor');

logger.setLevel('INFO');

// for GrovePi
var GrovePi = require('node-grovepi').GrovePi,
    Board = GrovePi.board,
    AnalogSensor = GrovePi.sensors.base.Analog,
    DigitalSensor = GrovePi.sensors.base.Digital,
    UltrasonicDigitalSensor = GrovePi.sensors.UltrasonicDigital,
    DHTDigitalSensor = GrovePi.sensors.DHTDigital,
    LightAnalogSensor = GrovePi.sensors.LightAnalog;

var ADC = 5; // 5V
var GROVE_VCC = 5; // 5V
var ULTRASONIC_VALID_DURATION = 1000;

function RotaryAngleAnalogSensor(pin) {
  this.preDegree = 0;
  AnalogSensor.apply(this, Array.prototype.slice.call(arguments));
  this.board.pinMode(this.pin, this.board.INPUT);
}
RotaryAngleAnalogSensor.FULL_ANGLE = 300;
RotaryAngleAnalogSensor.prototype = new AnalogSensor();
RotaryAngleAnalogSensor.prototype.read = function() {
  var res = AnalogSensor.prototype.read.call(this);
  var sensorValue = parseInt(res);

  if (isNaN(sensorValue)) {
    return this.preDegree;
  }

  var voltage = sensorValue * ADC / 1023;
  var degree = Math.round(voltage * RotaryAngleAnalogSensor.FULL_ANGLE / GROVE_VCC);

  this.preDegree = degree;
  return degree;
};

function SoundAnalogSensor(pin) {
  AnalogSensor.apply(this, Array.prototype.slice.call(arguments));
  this.board.pinMode(this.pin, this.board.INPUT);
}
SoundAnalogSensor.prototype = new AnalogSensor();

function VibrationAnalogSensor(pin) {
  AnalogSensor.apply(this, Array.prototype.slice.call(arguments));
  this.board.pinMode(this.pin, this.board.INPUT);
}
VibrationAnalogSensor.prototype = new AnalogSensor();

function OnOffDigitalSensor(pin) {
  DigitalSensor.apply(this, Array.prototype.slice.call(arguments));
  this.board.pinMode(this.pin, this.board.OUTPUT);
}
OnOffDigitalSensor.prototype = new DigitalSensor();

function ButtonDigitalSensor(pin) {
  DigitalSensor.apply(this, Array.prototype.slice.call(arguments));
  this.board.pinMode(this.pin, this.board.INPUT);
}
ButtonDigitalSensor.prototype = new DigitalSensor();
ButtonDigitalSensor.prototype.read = function() {
  var res = DigitalSensor.prototype.read.call(this);
  var buttonState = (res && res.length > 0)? res[0] : 0;
  return buttonState;
};

// GrovePiSensors
function GrovePiSensors() {
  var self = this;
  self.sensors = {};
}
util.inherits(GrovePiSensors, events.EventEmitter);

GrovePiSensors.prototype.init = function (sensorNames) {
  var self = this,
      board;

  logger.info('init', sensorNames);

  _.each(sensorNames, function(sensorName) {
    self.sensors[sensorName] = {};
  });

  board = new Board({
    debug: true,
    onError: function(err) {
      logger.error('Something wrong just happened', err);
    },
    onInit: function(res) {
      if (res) {

        logger.info('GrovePi Version :: ' + board.version());

        // Analog
        self.sensors.sound.instance = new SoundAnalogSensor(0);
        self.sensors.light.instance = new LightAnalogSensor(1);
        self.sensors.rotary.instance = new RotaryAngleAnalogSensor(2);
        //self.sensors.vibration.instance = new VibrationAnalogSensor(2);

        // Digital
        self.sensors.buzzer.instance = new OnOffDigitalSensor(2);
        self.sensors.led.instance = new OnOffDigitalSensor(3);
        self.sensors.relay.instance = new OnOffDigitalSensor(4);
        self.sensors.button.instance = new ButtonDigitalSensor(5);
        self.sensors.ultrasonic.instance = new UltrasonicDigitalSensor(6);
        self.sensors.temperature.instance = self.sensors.humidity.instance
          = new DHTDigitalSensor(7, DHTDigitalSensor.VERSION.DHT11, DHTDigitalSensor.CELSIUS);

        console.log('Ultrasonic Digital Sensor (start watch)');
        /*
        self.sensors.ultrasonic.instance.on('change', function (value) {
          var ultrasonicState;
          if (value < 20) {
            ultrasonicState = 1;
          } else {
            ultrasonicState = 0;
          }

          if (!_.isUndefined(self.sensors.ultrasonic.preValue) &&
              self.sensors.ultrasonic.preValue === ultrasonicState) {
            return;
          }

          if (!self.sensors.ultrasonic.timer) {
            if (ultrasonicState) {
              logger.info('send event', 'ultrasonic', 1, value);
              self.emit('event', 'ultrasonic', 1);

              self.sensors.ultrasonic.timer = setTimeout(function () {
                if (!self.sensors.ultrasonic.preValue) {
                  logger.info('send event', 'ultrasonic', 0);
                  self.emit('event', 'ultrasonic', 0);
                }
                self.sensors.ultrasonic.timer = null;
              }, 15 * 1000);
            } else {
              if (self.sensors.ultrasonic.preValue !== ultrasonicState) {
                logger.info('send event', 'ultrasonic', 0, value);
                self.emit('event', 'ultrasonic', 0);
              }
            }
          }
          self.sensors.ultrasonic.preValue = ultrasonicState;
        });
        */
        self.sensors.ultrasonic.instance.on('change', function (value) {
          var ultrasonicValue;

          if (value === false || value === 'false' || value === 65535) {
            return;
          }

          if (value < 20) {
            ultrasonicValue = 1;
          } else {
            ultrasonicValue = 0;
          }

          logger.debug('onChange', 'ultrasonic', ultrasonicValue, value);

          if (!_.isUndefined(self.sensors.ultrasonic.preValue) &&
            self.sensors.ultrasonic.preValue !== ultrasonicValue)
          {
            logger.debug('onChange', 'ultrasonic', 'prev !== curr', ultrasonicValue);
            if (self.sensors.ultrasonic.timer) {
              logger.debug('onChange', 'ultrasonic', 'clearTimeout', ultrasonicValue);
              clearTimeout(self.sensors.ultrasonic.timer);
              self.sensors.ultrasonic.timer = null;
            } else {
              logger.debug('onChange', 'ultrasonic', 'setTimeout', ultrasonicValue);
              self.sensors.ultrasonic.timer = setTimeout(function () {
                logger.debug('send event', 'ultrasonic', self.sensors.ultrasonic.preValue);
                self.emit('event', 'ultrasonic', self.sensors.ultrasonic.preValue);
                self.sensors.ultrasonic.timer = null;
              }, ULTRASONIC_VALID_DURATION);
            }
          } else {
            logger.debug('onChange', 'ultrasonic', 'prev === curr', ultrasonicValue);
          }

          self.sensors.ultrasonic.preValue = ultrasonicValue;
        });
        self.sensors.ultrasonic.instance.watch(100);

        console.log('Button Digital Sensor (start watch)');
        self.sensors.button.instance.on('change', function(buttonState) {

          if (!_.isUndefined(self.sensors.button.preValue) &&
              self.sensors.button.preValue === buttonState) {
            return;
          }

          if (!self.sensors.button.timer) {
            if (buttonState) {
              logger.info('send event', 'button', 1);
              self.emit('event', 'button', 1);

              self.sensors.button.timer = setTimeout(function () {
                if (!self.sensors.button.preValue) {
                  logger.info('send event', 'button', 0);
                  self.emit('event', 'button', 0);
                }
                self.sensors.button.timer = null;
              }, 15 * 1000);
            } else {
              if (self.sensors.button.preValue !== buttonState) {
                logger.info('send event', 'button', 0);
                self.emit('event', 'button', 0);
              }
            }
          }
          self.sensors.button.preValue = buttonState;
        });
        self.sensors.button.instance.watch(10);
      }
    }
  });

  board.init();

  self.emit('ready');
};

GrovePiSensors.prototype.getData = function (name) {
  var self = this;
  var value, rtn;

  if (name === 'temperature') {
    value = self.sensors[name].instance.read()[0];
  } else if (name === 'humidity') {
    value = self.sensors[name].instance.read()[1];
  } else {
    value = self.sensors[name].instance.read();
  }
  rtn = { value: value, status: 'on' };

  logger.info('getData', name, rtn);
  return rtn;
};

GrovePiSensors.prototype.getStatus = function (name) {
  var rtn = { status: 'on' };
  logger.info('getStatus', name, rtn);

  return rtn;
};

GrovePiSensors.prototype.doCommand = function (name, cmd, options) {
  var self = this;
  var command;

  logger.info('doCommand', name, cmd, options);

  if (cmd === 'on') {
    command = 1;
    if (options && options.duration) {
      clearTimeout(self.sensors[name].timer);
      self.sensors[name].timer = setTimeout(function () {
        self.sensors[name].instance.write(0); // off
      }, options.duration);
    }
  } else { // 'off'
    command = 0;
  }

  self.sensors[name].instance.write(command);
};

module.exports = new GrovePiSensors();
<commit_msg>support blink command<commit_after>#!/usr/bin/env node
/*
 * Copyright (c) 2015, Daliworks. All rights reserved.
 *
 * Reproduction and/or distribution in source and binary forms
 * without the written consent of Daliworks, Inc. is prohibited.
 *
 */
'use strict';

var _ = require('lodash'),
    util = require('util'),
    events = require('events'),
    logger = require('log4js').getLogger('Sensor');

logger.setLevel('INFO');

// for GrovePi
var GrovePi = require('node-grovepi').GrovePi,
    Board = GrovePi.board,
    AnalogSensor = GrovePi.sensors.base.Analog,
    DigitalSensor = GrovePi.sensors.base.Digital,
    UltrasonicDigitalSensor = GrovePi.sensors.UltrasonicDigital,
    DHTDigitalSensor = GrovePi.sensors.DHTDigital,
    LightAnalogSensor = GrovePi.sensors.LightAnalog;

var ADC = 5; // 5V
var GROVE_VCC = 5; // 5V
var ULTRASONIC_VALID_DURATION = 1000;

function RotaryAngleAnalogSensor(pin) {
  this.preDegree = 0;
  AnalogSensor.apply(this, Array.prototype.slice.call(arguments));
  this.board.pinMode(this.pin, this.board.INPUT);
}
RotaryAngleAnalogSensor.FULL_ANGLE = 300;
RotaryAngleAnalogSensor.prototype = new AnalogSensor();
RotaryAngleAnalogSensor.prototype.read = function() {
  var res = AnalogSensor.prototype.read.call(this);
  var sensorValue = parseInt(res);

  if (isNaN(sensorValue)) {
    return this.preDegree;
  }

  var voltage = sensorValue * ADC / 1023;
  var degree = Math.round(voltage * RotaryAngleAnalogSensor.FULL_ANGLE / GROVE_VCC);

  this.preDegree = degree;
  return degree;
};

function SoundAnalogSensor(pin) {
  AnalogSensor.apply(this, Array.prototype.slice.call(arguments));
  this.board.pinMode(this.pin, this.board.INPUT);
}
SoundAnalogSensor.prototype = new AnalogSensor();

function VibrationAnalogSensor(pin) {
  AnalogSensor.apply(this, Array.prototype.slice.call(arguments));
  this.board.pinMode(this.pin, this.board.INPUT);
}
VibrationAnalogSensor.prototype = new AnalogSensor();

function OnOffDigitalSensor(pin) {
  DigitalSensor.apply(this, Array.prototype.slice.call(arguments));
  this.board.pinMode(this.pin, this.board.OUTPUT);
}
OnOffDigitalSensor.prototype = new DigitalSensor();

function ButtonDigitalSensor(pin) {
  DigitalSensor.apply(this, Array.prototype.slice.call(arguments));
  this.board.pinMode(this.pin, this.board.INPUT);
}
ButtonDigitalSensor.prototype = new DigitalSensor();
ButtonDigitalSensor.prototype.read = function() {
  var res = DigitalSensor.prototype.read.call(this);
  var buttonState = (res && res.length > 0)? res[0] : 0;
  return buttonState;
};

// GrovePiSensors
function GrovePiSensors() {
  var self = this;
  self.sensors = {};
}
util.inherits(GrovePiSensors, events.EventEmitter);

GrovePiSensors.prototype.init = function (sensorNames) {
  var self = this,
      board;

  logger.info('init', sensorNames);

  _.each(sensorNames, function(sensorName) {
    self.sensors[sensorName] = {};
  });

  board = new Board({
    debug: true,
    onError: function(err) {
      logger.error('Something wrong just happened', err);
    },
    onInit: function(res) {
      if (res) {

        logger.info('GrovePi Version :: ' + board.version());

        // Analog
        self.sensors.sound.instance = new SoundAnalogSensor(0);
        self.sensors.light.instance = new LightAnalogSensor(1);
        self.sensors.rotary.instance = new RotaryAngleAnalogSensor(2);
        //self.sensors.vibration.instance = new VibrationAnalogSensor(2);

        // Digital
        self.sensors.buzzer.instance = new OnOffDigitalSensor(2);
        self.sensors.led.instance = new OnOffDigitalSensor(3);
        self.sensors.relay.instance = new OnOffDigitalSensor(4);
        self.sensors.button.instance = new ButtonDigitalSensor(5);
        self.sensors.ultrasonic.instance = new UltrasonicDigitalSensor(6);
        self.sensors.temperature.instance = self.sensors.humidity.instance
          = new DHTDigitalSensor(7, DHTDigitalSensor.VERSION.DHT11, DHTDigitalSensor.CELSIUS);

        console.log('Ultrasonic Digital Sensor (start watch)');
        /*
        self.sensors.ultrasonic.instance.on('change', function (value) {
          var ultrasonicState;
          if (value < 20) {
            ultrasonicState = 1;
          } else {
            ultrasonicState = 0;
          }

          if (!_.isUndefined(self.sensors.ultrasonic.preValue) &&
              self.sensors.ultrasonic.preValue === ultrasonicState) {
            return;
          }

          if (!self.sensors.ultrasonic.timer) {
            if (ultrasonicState) {
              logger.info('send event', 'ultrasonic', 1, value);
              self.emit('event', 'ultrasonic', 1);

              self.sensors.ultrasonic.timer = setTimeout(function () {
                if (!self.sensors.ultrasonic.preValue) {
                  logger.info('send event', 'ultrasonic', 0);
                  self.emit('event', 'ultrasonic', 0);
                }
                self.sensors.ultrasonic.timer = null;
              }, 15 * 1000);
            } else {
              if (self.sensors.ultrasonic.preValue !== ultrasonicState) {
                logger.info('send event', 'ultrasonic', 0, value);
                self.emit('event', 'ultrasonic', 0);
              }
            }
          }
          self.sensors.ultrasonic.preValue = ultrasonicState;
        });
        */
        self.sensors.ultrasonic.instance.on('change', function (value) {
          var ultrasonicValue;

          if (value === false || value === 'false' || value === 65535) {
            return;
          }

          if (value < 20) {
            ultrasonicValue = 1;
          } else {
            ultrasonicValue = 0;
          }

          logger.debug('onChange', 'ultrasonic', ultrasonicValue, value);

          if (!_.isUndefined(self.sensors.ultrasonic.preValue) &&
            self.sensors.ultrasonic.preValue !== ultrasonicValue)
          {
            logger.debug('onChange', 'ultrasonic', 'prev !== curr', ultrasonicValue);
            if (self.sensors.ultrasonic.timer) {
              logger.debug('onChange', 'ultrasonic', 'clearTimeout', ultrasonicValue);
              clearTimeout(self.sensors.ultrasonic.timer);
              self.sensors.ultrasonic.timer = null;
            } else {
              logger.debug('onChange', 'ultrasonic', 'setTimeout', ultrasonicValue);
              self.sensors.ultrasonic.timer = setTimeout(function () {
                logger.debug('send event', 'ultrasonic', self.sensors.ultrasonic.preValue);
                self.emit('event', 'ultrasonic', self.sensors.ultrasonic.preValue);
                self.sensors.ultrasonic.timer = null;
              }, ULTRASONIC_VALID_DURATION);
            }
          } else {
            logger.debug('onChange', 'ultrasonic', 'prev === curr', ultrasonicValue);
          }

          self.sensors.ultrasonic.preValue = ultrasonicValue;
        });
        self.sensors.ultrasonic.instance.watch(100);

        console.log('Button Digital Sensor (start watch)');
        self.sensors.button.instance.on('change', function(buttonState) {

          if (!_.isUndefined(self.sensors.button.preValue) &&
              self.sensors.button.preValue === buttonState) {
            return;
          }

          if (!self.sensors.button.timer) {
            if (buttonState) {
              logger.info('send event', 'button', 1);
              self.emit('event', 'button', 1);

              self.sensors.button.timer = setTimeout(function () {
                if (!self.sensors.button.preValue) {
                  logger.info('send event', 'button', 0);
                  self.emit('event', 'button', 0);
                }
                self.sensors.button.timer = null;
              }, 15 * 1000);
            } else {
              if (self.sensors.button.preValue !== buttonState) {
                logger.info('send event', 'button', 0);
                self.emit('event', 'button', 0);
              }
            }
          }
          self.sensors.button.preValue = buttonState;
        });
        self.sensors.button.instance.watch(10);
      }
    }
  });

  board.init();

  self.emit('ready');
};

GrovePiSensors.prototype.getData = function (name) {
  var self = this;
  var value, rtn;

  if (name === 'temperature') {
    value = self.sensors[name].instance.read()[0];
  } else if (name === 'humidity') {
    value = self.sensors[name].instance.read()[1];
  } else {
    value = self.sensors[name].instance.read();
  }
  rtn = { value: value, status: 'on' };

  logger.info('getData', name, rtn);
  return rtn;
};

GrovePiSensors.prototype.getStatus = function (name) {
  var rtn = { status: 'on' };
  logger.info('getStatus', name, rtn);

  return rtn;
};

GrovePiSensors.prototype.doCommand = function (name, cmd, options) {
  var self = this;
  var command;

  logger.info('doCommand', name, cmd, options);

  if (cmd === 'on') {
    command = 1;
    if (options && options.duration) {
      clearTimeout(self.sensors[name].timer);
      self.sensors[name].timer = setTimeout(function () {
        self.sensors[name].instance.write(0); // off
      }, options.duration);
    }
  } else if (cmd === 'blink') {
    if (self.sensors[name].blinkTimer) {
      clearTimeout(self.sensors[name].blinkTimer);
    }

    self.sensors[name].blinkTtimer = setInterval(function () {
      self.sensors[name].instance.write(!self.sensors[name].instance.read()[0]);
    }, options.interval || 1000);

    if (options.duration) {
      self.sensors[name].offTimer = setTimeout(function() {
        clearTimeout(self.sensors[name].blinkTimer);
        self.blinkTimer = null;
        self.offTimer = null;
        self.sensors[name].instance.write(0);
      }, options.duration || 10000);
    }
    return;
  } else { // 'off'
    command = 0;
  }

  self.sensors[name].instance.write(command);
};

module.exports = new GrovePiSensors();
<|endoftext|>


<commit_before>import React, { Component } from 'react';

class SearchRoute extends Component {

	componentWillMount() {
		document.title = 'Daily Note - 검색';
	}

	render() {
		return (

				SearchRoute 페이지입니다.

		);
	}
};

export default SearchRoute;<commit_msg>검색페이지 일부 수정정<commit_after>import React, { Component } from 'react';
import { Dimmer, Loader } from 'semantic-ui-react';

import SearchInput from 'components/SearchInput';

import * as api from 'helpers/WebApi/note';

class SearchRoute extends Component {
	state = {
		fetch: false
	}

	componentWillMount() {
		document.title = 'Daily Note - 검색';
	}

	handleSearch = async (value) => {
		this.setState({ fetch: true });

		const result = await api.searchNotes(value);

		window.setTimeout(() => this.setState({ fetch: false }), 3000);
	}

	render() {
		const { handleSearch, state: { fetch } } = this;
		return (

				찾고자 하는 검색어 입력 :
				<SearchInput placeholder="찾고 싶은 장부의 내용들을 검색해보세요!" icon onSearch={handleSearch}/>
				{ fetch && <Dimmer active><Loader>Data Loading...</Loader></Dimmer> }

		);
	}
};

export default SearchRoute;<|endoftext|>


<commit_before>import React from 'react';

import SprkToggle from './SprkToggle';
import { action } from '@storybook/addon-actions';

export default {
  title: 'Components|Toggle',
  component: SprkToggle,
  parameters: { jest: ['SprkToggle'] },
};

export const defaultStory = () => (
  <SprkToggle title="My Disclaimer" analyticsString="toggle-1">

      This is an example of disclaimer content.
      The aria-expanded="true" attribute will be viewable
      in the DOM on the toggle link when
      this content is shown. When this content is hidden the
      aria-expanded attribute will have
      the value of false. This helps accessibilty devices in
      understanding that the link is a control for expandable content.

  </SprkToggle>

);

defaultStory.story = {
  name: 'Default',
};
<commit_msg>removed extra import<commit_after>import React from 'react';
import SprkToggle from './SprkToggle';

export default {
  title: 'Components|Toggle',
  component: SprkToggle,
  parameters: { jest: ['SprkToggle'] },
};

export const defaultStory = () => (
  <SprkToggle title="My Disclaimer" analyticsString="toggle-1">

      This is an example of disclaimer content.
      The aria-expanded="true" attribute will be viewable
      in the DOM on the toggle link when
      this content is shown. When this content is hidden the
      aria-expanded attribute will have
      the value of false. This helps accessibilty devices in
      understanding that the link is a control for expandable content.

  </SprkToggle>

);

defaultStory.story = {
  name: 'Default',
};
<|endoftext|>


<commit_before>//Assorted utility functions (extract and include as needed)

function randomNumber(min, max){
	var rnd = Math.floor((Math.random()*((max+1)-min))+min);
	return rnd;
}
<commit_msg>added extendNamespace method<commit_after>//Assorted utility functions (extract and include as needed)

function randomNumber(min, max){
	var rnd = Math.floor((Math.random()*((max+1)-min))+min);
	return rnd;
}

function extendNamespace(ns, nsStr){
	var bits = nsStr.split('.');
	var parent = ns;
	var key;
	var bL = bits.length;
	for(var i=0;i<bL;i++){
		key = bits[i];
		if(typeof(parent[key]) == 'undefined'){
			parent[key] = {};
		}
		parent = parent[key];
	}
	return parent;
}
<|endoftext|>


<commit_before>/*jslint node: true */
"use strict";
var bluebird = require("bluebird");
var request = bluebird.promisify(require("request").defaults({jar: true}));

function get(url, jar, qs) {
  // I'm still confused about this
  if(typeof qs === "object") {
    for(var prop in qs) {
      if(qs.hasOwnProperty(prop) && typeof qs[prop] === "object") {
        qs[prop] = JSON.stringify(qs[prop]);
      }
    }
  }
  var op = {
    headers: {
      'Content-Type' : 'application/x-www-form-urlencoded',
      'Referer' : 'https://www.facebook.com/',
      'Host' : url.replace('https://', '').split("/")[0],
      'Origin' : 'https://www.facebook.com',
      'User-Agent' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/600.3.18 (KHTML, like Gecko) Version/8.0.3 Safari/600.3.18',
      'Connection' : 'keep-alive',
    },
    timeout: 60000,
    qs: qs,
    url: url,
    method: "GET",
    jar: jar,
    gzip: true
  };

  return request(op).then(function(res) {return res[0];});
}

function post(url, jar, form) {
  var op = {
    headers: {
      'Content-Type' : 'application/x-www-form-urlencoded',
      'Referer' : 'https://www.facebook.com/',
      'Origin' : 'https://www.facebook.com',
      'Host' : url.replace('https://', '').split("/")[0],
      'User-Agent' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/600.3.18 (KHTML, like Gecko) Version/8.0.3 Safari/600.3.18',
      'Connection' : 'keep-alive',
    },
    timeout: 60000,
    url: url,
    method: "POST",
    form: form,
    jar: jar,
    gzip: true
  };

  return request(op).then(function(res) {return res[0];});
}

function padZeros(val, len) {
    val = String(val);
    len = len || 2;
    while (val.length < len) val = "0" + val;
    return val;
}

function generateMessageID(clientID) {
  var k = Date.now();
  var l = Math.floor(Math.random() * 4294967295);
  var m = clientID;
  return ("<" + k + ":" + l + "-" + m + "@mail.projektitan.com>");
}

function getGUID() {
  /** @type {number} */
  var sectionLength = Date.now();
  /** @type {string} */
  var id = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
    /** @type {number} */
    var r = Math.floor((sectionLength + Math.random() * 16) % 16);
    /** @type {number} */
    sectionLength = Math.floor(sectionLength / 16);
    /** @type {string} */
    var _guid = (c == "x" ? r : r & 7 | 8).toString(16);
    return _guid;
  });
  return id;
}

function formatMessage(m) {
  var originalMessage = m.message ? m.message : m;

  var obj = {
    type: "message",
    sender_name: originalMessage.sender_name,
    sender_id: originalMessage.sender_fbid,
    participant_names: (originalMessage.group_thread_info ? originalMessage.group_thread_info.participant_names : [originalMessage.sender_name.split(' ')[0]]),
    participant_ids: (originalMessage.group_thread_info ? originalMessage.group_thread_info.participant_ids : [originalMessage.sender_fbid]),
    body: originalMessage.body,
    thread_id: originalMessage.tid && originalMessage.tid.split(".")[0] === "id" ? originalMessage.tid.split('.')[1] : originalMessage.other_user_fbid,
    location: originalMessage.coordinates ? originalMessage.coordinates : null
  };

  if (originalMessage.attachments){
    for (var i = 0; i < originalMessage.attachments.length; i++){
      if (originalMessage.attachments[i].attach_type === "sticker"){
        obj.type = "sticker";
        delete obj.body;
        obj.sticker_id = originalMessage.attachments[i].metadata.stickerID;
        obj.sticker_url = originalMessage.attachments[i].url;
        break;
      }
    }
  }

  if(m.type === "pages_messaging") obj.pageId = m.realtime_viewer_fbid;

  return obj;
}

function formatEvent(m) {
  return {
    type: "event",
    thread_id: m.thread_fbid,
    log_message_type: m.log_message_type,
    log_message_data: m.log_message_data,
    log_message_body: m.log_message_body,
    author: m.author.split(":")[1]
  };
}

function getFrom(str, startToken, endToken) {
  var start = str.indexOf(startToken) + startToken.length;
  if(start < startToken.length) return "";

  var lastHalf = str.substring(start);
  var end = lastHalf.indexOf(endToken);
  return lastHalf.substring(0, end);
}

function makeParsable(html) {
  return html.replace(/for\s*\(\s*;\s*;\s*\)\s*;\s*/, "");
}

function arrToForm(form) {
  return arrayToObject(form, function(v) {return v.name;}, function(v) {return v.val;});
}

function arrayToObject(arr, getKey, getValue) {
  return arr.reduce(function(acc, val) {
    acc[getKey(val)] = getValue(val);
    return acc;
  }, {});
}

function getSignatureId(){
  return Math.floor(Math.random() * 2147483648).toString(16);
}

function genTimestampRelative() {
  var d = new Date();
  return d.getHours() + ":" + padZeros(d.getMinutes());
}

function makeMergeWithDefaults(html, userId) {
  var reqCounter = 1;
  var fb_dtsg = getFrom(html, "name=\"fb_dtsg\" value=\"", "\"");
  var ttstamp = "";
  for (var i = 0; i < fb_dtsg.length; i++) {
    ttstamp += fb_dtsg.charCodeAt(i);
  }
  ttstamp += '2';

  return function (obj) {
    var newObj = {
      __user: userId,
      __req: (reqCounter++).toString(36),
      __rev: getFrom(html, "revision\":",","),
      __a: 1,
      fb_dtsg: fb_dtsg,
      ttstamp: ttstamp,
    };

    if(!obj) return newObj;

    for(var prop in obj) {
      if(obj.hasOwnProperty(prop)) {
        newObj[prop] = obj[prop];
      }
    }

    return newObj;
  };
}

function parseResponse(res) {
  return bluebird.try(function() {
    return JSON.parse(makeParsable(res.body));
  });
}

function saveCookies(jar) {
  return function(res) {
    var cookies = res.headers['set-cookie'] || [];
    cookies.map(function (c) {
      jar.setCookie(c, "https://www.facebook.com");
    });
    return res;
  };
}

var NUM_TO_MONTH = [
  'Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'
];
var NUM_TO_DAY = [
  'Sun','Mon','Tue','Wed','Thu','Fri','Sat'
];
function formatDate(date) {
  var d = date.getUTCDate(); d = d >= 10 ? d : '0'+d;
  var h = date.getUTCHours(); h = h >= 10 ? h : '0'+h;
  var m = date.getUTCMinutes(); m = m >= 10 ? m : '0'+m;
  var s = date.getUTCSeconds(); s = s >= 10 ? s : '0'+s;
  return NUM_TO_DAY[date.getUTCDay()] + ', ' +
    d+' '+ NUM_TO_MONTH[date.getUTCMonth()] +' '+ date.getUTCFullYear() +' '+
    h+':'+m+':'+s+' GMT';
}
function formatCookie(arr) {
  return arr[0]+"="+arr[1]+"; " + (arr[2] !== 0 ? "expires=" + formatDate(new Date(arr[2])) + "; " : "") + "path=" + arr[3] + ";";
}

module.exports = {
  get: get,
  post: post,
  generateMessageID: generateMessageID,
  getGUID: getGUID,
  formatMessage: formatMessage,
  getFrom: getFrom,
  makeParsable: makeParsable,
  arrToForm: arrToForm,
  getSignatureId: getSignatureId,
  getJar: request.jar,
  genTimestampRelative: genTimestampRelative,
  makeMergeWithDefaults: makeMergeWithDefaults,
  formatEvent: formatEvent,
  parseResponse: parseResponse,
  saveCookies: saveCookies,
  formatCookie: formatCookie
};
<commit_msg>Added way to get file attachment<commit_after>/*jslint node: true */
"use strict";
var bluebird = require("bluebird");
var request = bluebird.promisify(require("request").defaults({jar: true}));

function get(url, jar, qs) {
  // I'm still confused about this
  if(typeof qs === "object") {
    for(var prop in qs) {
      if(qs.hasOwnProperty(prop) && typeof qs[prop] === "object") {
        qs[prop] = JSON.stringify(qs[prop]);
      }
    }
  }
  var op = {
    headers: {
      'Content-Type' : 'application/x-www-form-urlencoded',
      'Referer' : 'https://www.facebook.com/',
      'Host' : url.replace('https://', '').split("/")[0],
      'Origin' : 'https://www.facebook.com',
      'User-Agent' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/600.3.18 (KHTML, like Gecko) Version/8.0.3 Safari/600.3.18',
      'Connection' : 'keep-alive',
    },
    timeout: 60000,
    qs: qs,
    url: url,
    method: "GET",
    jar: jar,
    gzip: true
  };

  return request(op).then(function(res) {return res[0];});
}

function post(url, jar, form) {
  var op = {
    headers: {
      'Content-Type' : 'application/x-www-form-urlencoded',
      'Referer' : 'https://www.facebook.com/',
      'Origin' : 'https://www.facebook.com',
      'Host' : url.replace('https://', '').split("/")[0],
      'User-Agent' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/600.3.18 (KHTML, like Gecko) Version/8.0.3 Safari/600.3.18',
      'Connection' : 'keep-alive',
    },
    timeout: 60000,
    url: url,
    method: "POST",
    form: form,
    jar: jar,
    gzip: true
  };

  return request(op).then(function(res) {return res[0];});
}

function padZeros(val, len) {
    val = String(val);
    len = len || 2;
    while (val.length < len) val = "0" + val;
    return val;
}

function generateMessageID(clientID) {
  var k = Date.now();
  var l = Math.floor(Math.random() * 4294967295);
  var m = clientID;
  return ("<" + k + ":" + l + "-" + m + "@mail.projektitan.com>");
}

function getGUID() {
  /** @type {number} */
  var sectionLength = Date.now();
  /** @type {string} */
  var id = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
    /** @type {number} */
    var r = Math.floor((sectionLength + Math.random() * 16) % 16);
    /** @type {number} */
    sectionLength = Math.floor(sectionLength / 16);
    /** @type {string} */
    var _guid = (c == "x" ? r : r & 7 | 8).toString(16);
    return _guid;
  });
  return id;
}

function formatMessage(m) {
  var originalMessage = m.message ? m.message : m;

  var obj = {
    type: "message",
    sender_name: originalMessage.sender_name,
    sender_id: originalMessage.sender_fbid,
    participant_names: (originalMessage.group_thread_info ? originalMessage.group_thread_info.participant_names : [originalMessage.sender_name.split(' ')[0]]),
    participant_ids: (originalMessage.group_thread_info ? originalMessage.group_thread_info.participant_ids : [originalMessage.sender_fbid]),
    body: originalMessage.body,
    thread_id: originalMessage.tid && originalMessage.tid.split(".")[0] === "id" ? originalMessage.tid.split('.')[1] : originalMessage.other_user_fbid,
    location: originalMessage.coordinates ? originalMessage.coordinates : null
  };

  if (originalMessage.attachments){
    for (var i = 0; i < originalMessage.attachments.length; i++){
      if (originalMessage.attachments[i].attach_type === "sticker"){
        obj.type = "sticker";
        delete obj.body;
        obj.sticker_id = originalMessage.attachments[i].metadata.stickerID;
        obj.sticker_url = originalMessage.attachments[i].url;
        break;
      }
      if (originalMessage.attachments[i].attach_type === "file"){
        obj.type = "file";
        delete obj.body;
        obj.name = originalMessage.attachments[i].name;
        obj.file_url = originalMessage.attachments[i].url;
        break;
      }
    }
  }

  if(m.type === "pages_messaging") obj.pageId = m.realtime_viewer_fbid;

  return obj;
}

function formatEvent(m) {
  return {
    type: "event",
    thread_id: m.thread_fbid,
    log_message_type: m.log_message_type,
    log_message_data: m.log_message_data,
    log_message_body: m.log_message_body,
    author: m.author.split(":")[1]
  };
}

function getFrom(str, startToken, endToken) {
  var start = str.indexOf(startToken) + startToken.length;
  if(start < startToken.length) return "";

  var lastHalf = str.substring(start);
  var end = lastHalf.indexOf(endToken);
  return lastHalf.substring(0, end);
}

function makeParsable(html) {
  return html.replace(/for\s*\(\s*;\s*;\s*\)\s*;\s*/, "");
}

function arrToForm(form) {
  return arrayToObject(form, function(v) {return v.name;}, function(v) {return v.val;});
}

function arrayToObject(arr, getKey, getValue) {
  return arr.reduce(function(acc, val) {
    acc[getKey(val)] = getValue(val);
    return acc;
  }, {});
}

function getSignatureId(){
  return Math.floor(Math.random() * 2147483648).toString(16);
}

function genTimestampRelative() {
  var d = new Date();
  return d.getHours() + ":" + padZeros(d.getMinutes());
}

function makeMergeWithDefaults(html, userId) {
  var reqCounter = 1;
  var fb_dtsg = getFrom(html, "name=\"fb_dtsg\" value=\"", "\"");
  var ttstamp = "";
  for (var i = 0; i < fb_dtsg.length; i++) {
    ttstamp += fb_dtsg.charCodeAt(i);
  }
  ttstamp += '2';

  return function (obj) {
    var newObj = {
      __user: userId,
      __req: (reqCounter++).toString(36),
      __rev: getFrom(html, "revision\":",","),
      __a: 1,
      fb_dtsg: fb_dtsg,
      ttstamp: ttstamp,
    };

    if(!obj) return newObj;

    for(var prop in obj) {
      if(obj.hasOwnProperty(prop)) {
        newObj[prop] = obj[prop];
      }
    }

    return newObj;
  };
}

function parseResponse(res) {
  return bluebird.try(function() {
    return JSON.parse(makeParsable(res.body));
  });
}

function saveCookies(jar) {
  return function(res) {
    var cookies = res.headers['set-cookie'] || [];
    cookies.map(function (c) {
      jar.setCookie(c, "https://www.facebook.com");
    });
    return res;
  };
}

var NUM_TO_MONTH = [
  'Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'
];
var NUM_TO_DAY = [
  'Sun','Mon','Tue','Wed','Thu','Fri','Sat'
];
function formatDate(date) {
  var d = date.getUTCDate(); d = d >= 10 ? d : '0'+d;
  var h = date.getUTCHours(); h = h >= 10 ? h : '0'+h;
  var m = date.getUTCMinutes(); m = m >= 10 ? m : '0'+m;
  var s = date.getUTCSeconds(); s = s >= 10 ? s : '0'+s;
  return NUM_TO_DAY[date.getUTCDay()] + ', ' +
    d+' '+ NUM_TO_MONTH[date.getUTCMonth()] +' '+ date.getUTCFullYear() +' '+
    h+':'+m+':'+s+' GMT';
}
function formatCookie(arr) {
  return arr[0]+"="+arr[1]+"; " + (arr[2] !== 0 ? "expires=" + formatDate(new Date(arr[2])) + "; " : "") + "path=" + arr[3] + ";";
}

module.exports = {
  get: get,
  post: post,
  generateMessageID: generateMessageID,
  getGUID: getGUID,
  formatMessage: formatMessage,
  getFrom: getFrom,
  makeParsable: makeParsable,
  arrToForm: arrToForm,
  getSignatureId: getSignatureId,
  getJar: request.jar,
  genTimestampRelative: genTimestampRelative,
  makeMergeWithDefaults: makeMergeWithDefaults,
  formatEvent: formatEvent,
  parseResponse: parseResponse,
  saveCookies: saveCookies,
  formatCookie: formatCookie
};
<|endoftext|>


<commit_before>var     _ = require('underscore')
,   fp    = require('functionpool')
,   fs    = require ('fs')
, ffmpeg  = require('fluent-ffmpeg')
,  mongo  = require('mongodb');

var _exists     = fs.exists     || require('path').exists;
var _existsSync = fs.existsSync || require('path').existsSync;

var   Db = mongo.Db,
    BSON = mongo.BSONPure;

var server = new mongo.Server('localhost', 27017, {auto_reconnect: true});

exports.db = db = new Db('mediadb', server, {safe: true});

exports.openDB = function (callback, populateCallback) {
    db.open(function(err, db) {
        if(!err) {
            console.log("Connected to 'mediadb' database");
            db.collection('medias', {safe:true}, function(err, collection) {
                if (err) {
                    console.log("The 'medias' collection doesn't exist. Creating it with sample data...");
                    if (populateCallback)
                        populateCallback()
                } else {
                    collection.find().toArray(function(err, items) {
                        _(items).each (function (item) {
                            exports.check_media (item, function (item) {
                                callback(item);
                            });
                        });
                    });
                }
            });
        } else {
            console.log("Could not connect:", err);
            abort();
        }
    });
}

/*--------------------------------------------------------------------------------------------------------------------*/
// Populate database with sample data -- Only used once: the first time the application is started.
// You'd typically not find this code in a real-life app, since the database would already exist.
var populateDB = function() {
    /*
    setInterval(function () {_addMedia ({ file: 'test' + Date.now(), _id: Date.now()})},
                4*1000);

    return;
    */
}

exports.sc_pool = new fp.Pool({size: 1}, function (media, callback, done) {
    var dest = './public/sc/' + media._id + '.jpg';
    console.log ('starting sc', media.file);

    if (_existsSync('./public/sc/' + media._id)) {
        console.log ('skipping screenshot of: ' + md5 + '(file already there).');
        return done(media);
    }

    var proc = new ffmpeg({source: media.file})
        .withSize('150x100')
        .onCodecData(function(metadata) {
            if (!callback)
                return;

            console.log(metadata);
            metadata._id  = media._id;
            metadata.file = media.file;
            metadata.stat = media.stat;
            callback (metadata);
        })
        .withFps(1)
        .addOption('-ss', '5')
        .onProgress(function(progress) {
            console.log(progress);
        })
        .saveToFile(dest, function(retcode, error) {
            if (! _existsSync (dest) || error)
                return done (new Error('File not created' + error));

            console.log('sc ok: ' + media._id);
            return done(media);
        });
});

exports.parse_pool = new fp.Pool({size: 1}, function (file, stat, done) {
    var spawn = require('child_process').spawn,
    md5sum    = spawn('md5sum', [file]);

    db.collection('medias', function(err, collection) {
        collection.findOne({'file': file}, function(err, item) {
            if (!err && item) {
                if (stat === item.stat) return (done(item));
                else item.stat = stat;
            } else {
                item = {file: file, stat: stat};
            }

            md5sum.stdout.on('data', function (data) {
                item._id = data.toString().split(' ')[0];
                done(item);
            });
        });
    });
});

exports.scrape_files = function (path, callback) {
  var walk      = require('walk')
    , spawn     = require('child_process').spawn
    , bs        = 10*1024*1024
    , observe   = path;

    console.log ('launched obeserver on path: ' + observe);

    /* Ok, this a bit messy, it goes like this:
       + we get the file;
       + spawn a binary to calculate md5;
       + give that to the ffmpeg process that will:
         . extract codec data;
         . take a screenshot at 5s from start;
       + when all is done and good, we _addMedia, to get it into the medias objects;
    */

    //This listens for files found
    walk.walk(observe, { followLinks: false })
    .on('file', function (root, stat, next) {
        var file = root + '/' +  stat.name;
        next();
        if (! stat.name.match(/\.(webm|mp4|flv|avi|mpeg|mpeg2|mpg|mkv|ogm|ogg)$/i)) {
            return new Error('file not a vid');
        }

        exports.parse_pool.task(file, stat, function (res, err) {
            if (err)
                return (console.error('error:', err));
            console.log ('parsed: ' + stat.name);
            exports.sc_pool.task(res, callback, function (err, res) {return res});
        });

    })
    .on('end', function () {
        console.log ("all done");
    });
}

exports.check_media = function (media, cb, arg) {
    if (!arg)
        arg = media;

    _exists (media.file, function (e) {
        if (!e)
            return;
        if (cb)
            cb(arg)
        _exists (__dirname + '/../public/sc/' + media._id + '.jpg', function (e) {
            if (!e)
                exports.sc_pool.task (media, null, function (err, res) {
                    if (err)
                        console.error (new Error("couldn't sc"));
                });
        });
    });
}
<commit_msg>WIP: merge handler for resumable. this is working quite inconsistantly.<commit_after>var     _ = require('underscore')
,   fp    = require('functionpool')
,   fs    = require ('fs')
, ffmpeg  = require('fluent-ffmpeg')
,  mongo  = require('mongodb');

var _exists     = fs.exists     || require('path').exists;
var _existsSync = fs.existsSync || require('path').existsSync;

var   Db = mongo.Db,
    BSON = mongo.BSONPure;

var server = new mongo.Server('localhost', 27017, {auto_reconnect: true});

exports.db = db = new Db('mediadb', server, {safe: true});

exports.openDB = function (callback, populateCallback) {
    db.open(function(err, db) {
        if(!err) {
            console.log("Connected to 'mediadb' database");
            db.collection('medias', {safe:true}, function(err, collection) {
                if (err) {
                    console.log("The 'medias' collection doesn't exist. Creating it with sample data...");
                    if (populateCallback)
                        populateCallback()
                } else {
                    collection.find().toArray(function(err, items) {
                        _(items).each (function (item) {
                            exports.check_media (item, function (item) {
                                callback(item);
                            });
                        });
                    });
                }
            });
        } else {
            console.log("Could not connect:", err);
            abort();
        }
    });
}

exports.merge = function (original_filename, callback) {
    var i = 1;
    var path = require ('path');
    var source_base = path.join (__dirname, '/public/uploads/incoming', '/resumable-' + original_filename + '.');

    dest = path.join (__dirname, '/public/uploads/', original_filename);
    if (_existsSync (dest))
        return;

    for (; _existsSync (source_base + i); i++)  {
        console.log ('--->', source_base + i);

        var writter = fs.createWriteStream(dest, {'flags': 'a'});
        writter.write (fs.readFileSync (source_base +i));
    };

    var id = setInterval (function () {
        console.log ('about to call merge_finish');
        exports.merge_finish(dest, id, callback)
    }, 200);

    return dest;
}

/* all this hack, is because sometimes merge_finish was called before dest could be found
   this hopes to fix it */

exports.merge_finish = function (dest, id, callback) {
    if (! _existsSync (dest))
        return;

    clearInterval (id);

    var stat = fs.statSync (dest);
    console.log ('all good, pasing it to parse', exports.parse_pool);
    exports.parse_pool.task(dest, stat, function (res, err) {
        if (err)
            return (console.error('error:', err));
        console.log ('parsed: ' + stat.name);
        exports.sc_pool.task(res, callback, function (err, res) {return res});
    });
}

/*--------------------------------------------------------------------------------------------------------------------*/
// Populate database with sample data -- Only used once: the first time the application is started.
// You'd typically not find this code in a real-life app, since the database would already exist.
var populateDB = function() {
    /*
    setInterval(function () {_addMedia ({ file: 'test' + Date.now(), _id: Date.now()})},
                4*1000);

    return;
    */
}

exports.sc_pool = new fp.Pool({size: 1}, function (media, callback, done) {
    var dest = './public/sc/' + media._id + '.jpg';
    console.log ('starting sc', media.file);

    if (_existsSync('./public/sc/' + media._id)) {
        console.log ('skipping screenshot of: ' + md5 + '(file already there).');
        return done(media);
    }

    var proc = new ffmpeg({source: media.file})
        .withSize('150x100')
        .onCodecData(function(metadata) {
            if (!callback)
                return;

            console.log(metadata);
            metadata._id  = media._id;
            metadata.file = media.file;
            metadata.stat = media.stat;
            callback (metadata);
        })
        .withFps(1)
        .addOption('-ss', '5')
        .onProgress(function(progress) {
            console.log(progress);
        })
        .saveToFile(dest, function(retcode, error) {
            if (! _existsSync (dest) || error)
                return done (new Error('File not created' + error));

            console.log('sc ok: ' + media._id);
            return done(media);
        });
});

exports.parse_pool = new fp.Pool({size: 1}, function (file, stat, done) {
    var spawn = require('child_process').spawn,
    md5sum    = spawn('md5sum', [file]);

    db.collection('medias', function(err, collection) {
        collection.findOne({'file': file}, function(err, item) {
            if (!err && item) {
                if (stat === item.stat) return (done(item));
                else item.stat = stat;
            } else {
                item = {file: file, stat: stat};
            }

            md5sum.stdout.on('data', function (data) {
                item._id = data.toString().split(' ')[0];
                done(item);
            });
        });
    });
});

exports.scrape_files = function (path, callback) {
  var walk      = require('walk')
    , spawn     = require('child_process').spawn
    , bs        = 10*1024*1024
    , observe   = path;

    console.log ('launched obeserver on path: ' + observe);

    /* Ok, this a bit messy, it goes like this:
       + we get the file;
       + spawn a binary to calculate md5;
       + give that to the ffmpeg process that will:
         . extract codec data;
         . take a screenshot at 5s from start;
       + when all is done and good, we _addMedia, to get it into the medias objects;
    */

    //This listens for files found
    walk.walk(observe, { followLinks: false })
    .on('file', function (root, stat, next) {
        var file = root + '/' +  stat.name;
        next();
        if (! stat.name.match(/\.(webm|mp4|flv|avi|mpeg|mpeg2|mpg|mkv|ogm|ogg)$/i)) {
            return new Error('file not a vid');
        }

        exports.parse_pool.task(file, stat, function (res, err) {
            if (err)
                return (console.error('error:', err));
            console.log ('parsed: ' + stat.name);
            exports.sc_pool.task(res, callback, function (err, res) {return res});
        });

    })
    .on('end', function () {
        console.log ("all done");
    });
}

exports.check_media = function (media, cb, arg) {
    if (!arg)
        arg = media;

    _exists (media.file, function (e) {
        if (!e)
            return;
        if (cb)
            cb(arg)
        _exists (__dirname + '/../public/sc/' + media._id + '.jpg', function (e) {
            if (!e)
                exports.sc_pool.task (media, null, function (err, res) {
                    if (err)
                        console.error (new Error("couldn't sc"));
                });
        });
    });
}
<|endoftext|>


<commit_before>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

/* eslint-disable import/prefer-default-export */
import { parsePositiveInteger, newSortDataBy } from '../../../utilities';

/**
 * Immutably clears the current focus
 * @param {object} currState  the copy of state you want affected
 * @return {object}           New state with no cell focused
 */
const clearFocus = state => {
  const { dataState, currentFocusedRowKey } = state;
  if (!currentFocusedRowKey) {
    return state;
  }

  const newDataState = new Map(dataState);
  const currentRowState = newDataState.get(currentFocusedRowKey);
  newDataState.set(currentFocusedRowKey, {
    ...currentRowState,
    focusedColumn: null,
  });

  return { ...state, dataState: newDataState, currentFocusedRowKey: null };
};

/**
 * Immutably sets the current focus to the cell identified by `rowKey` and `columnKey`
 * @param {object} state  The copy of state to affect
 * @param {string} rowKey     The key of the row the cell to focus is in
 * @param {string} columnKey  The key of the column the cell to focus is in
 * @return {object}           A new object with a cell focused
 */
const setFocus = (state, rowKey, columnKey) => {
  const { dataState, currentFocusedRowKey } = state;
  const newDataState = new Map(dataState);

  // Clear previous focus if in a different row
  if (currentFocusedRowKey && rowKey !== currentFocusedRowKey) {
    const currentRowState = newDataState.get(currentFocusedRowKey);
    newDataState.set(currentFocusedRowKey, {
      ...currentRowState,
      focusedColumn: null,
    });
  }

  // Update focusedColumn in specified row
  const nextRowState = newDataState.get(rowKey);
  newDataState.set(rowKey, {
    ...nextRowState,
    focusedColumn: columnKey,
  });

  return {
    ...state,
    dataState: newDataState,
    currentFocusedRowKey: rowKey,
  };
};

/**
 *
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 */
export const filterData = (state, action) => {
  const { backingData, filterDataKeys, sortBy, isAscending } = state;
  const { searchTerm } = action;

  const columnKeyToDataType = {
    itemCode: 'string',
    itemName: 'string',
    availableQuantity: 'number',
    totalQuantity: 'number',
  };

  const queryString = filterDataKeys
    .map(filterTerm => `${filterTerm} BEGINSWITH[c]  $0`)
    .join(' OR ');

  const newData = newSortDataBy(
    backingData.filtered(queryString, searchTerm).slice(),
    sortBy,
    columnKeyToDataType[sortBy],
    isAscending
  );
  return { ...state, data: newData };
};

export const editTotalQuantity = (state, action) => {
  const { value, rowKey } = action;
  const { data, database, dataState, keyExtractor } = state;

  const objectToEdit = data.find(row => keyExtractor(row) === rowKey);

  database.write(() => {
    objectToEdit.setTotalQuantity(database, parsePositiveInteger(Number(value)));
    database.save('TransactionItem', objectToEdit);
  });

  // Change object reference of row in `dataState` to trigger rerender of that row.
  // Realm object reference in `data` can't be affected in any tidy manner.
  const newDataState = new Map(dataState);
  const nextRowState = newDataState.get(rowKey);
  newDataState.set(rowKey, {
    ...nextRowState,
  });

  return {
    ...state,
    dataState: newDataState,
  };
};

/**
 * Focus the next appropriate cell after the current cell provided in action
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 */
export const focusNextCell = (state, action) => {
  const { data, columns, keyExtractor } = state;
  const { rowKey, columnKey } = action;

  // Handle finding next cell to focus
  let nextEditableColKey;
  const currentColIndex = columns.findIndex(col => col.key === columnKey);
  for (let index = currentColIndex + 1; index < columns.length; index++) {
    if (columns[index].type === 'editable') {
      nextEditableColKey = columns[index].key;
      break;
    }
  }

  if (nextEditableColKey) {
    // Focus next editable cell in row
    return setFocus(state, rowKey, nextEditableColKey);
  }

  // Attempt moving focus to next row
  const nextRowIndex = data.findIndex(row => keyExtractor(row) === rowKey) + 1;

  if (nextRowIndex < data.length) {
    // Focus first editable cell in next row
    const nextRowKey = keyExtractor(data[nextRowIndex]);
    const firstEditableColKey = columns.find(col => col.type === 'editable').key;
    return setFocus(state, nextRowKey, firstEditableColKey);
  }

  // We were on the last row and last column, so unfocus current cell
  return clearFocus(state);
};

/**
 * Sets the provided row in action as selected
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * Action: { type: 'selectRow', rowKey }
 */
export const selectRow = (state, action) => {
  const { dataState } = state;
  const { rowKey } = action;
  const newDataState = new Map(dataState);

  const rowState = newDataState.get(rowKey);
  newDataState.set(rowKey, {
    ...rowState,
    isSelected: true,
  });

  return { ...state, dataState: newDataState };
};

/**
 * Removes the provided row in action from being
 * selected.
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * Action: { type: 'deselectRow', rowKey }
 */
export const deselectRow = (state, action) => {
  const { dataState } = state;
  const { rowKey } = action;
  const newDataState = new Map(dataState);

  const rowState = newDataState.get(rowKey);
  newDataState.set(rowKey, {
    ...rowState,
    isSelected: false,
  });

  return { ...state, dataState: newDataState };
};

/**
 * Removes all rows from being selected.
 * @param {Object} state  The current state
 * Action: { type: 'deselectAll' }
 */
export const deselectAll = state => {
  const { dataState } = state;
  const newDataState = new Map(dataState);

  // eslint-disable-next-line no-restricted-syntax
  for (const [rowKey, rowState] of newDataState.entries()) {
    if (rowState.isSelected) {
      newDataState.set(rowKey, {
        ...rowState,
        isSelected: false,
      });
    }
  }
  return { ...state, dataState: newDataState };
};

/**
 * Focuses the cell provided in the action passed
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * action: { type: 'focusCell', rowKey, columnKey }
 */
export const focusCell = (state, action) => {
  // Clear any existing focus and focus cell specified in action
  const { rowKey, columnKey } = action;
  return setFocus(state, rowKey, columnKey);
};

/**
 * Sorts the current set of data by the provided
 * key and direction in action.
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * Action: { type: 'sortData', sortBy }
 * sortBy: String key of the field of the objects to sort by.
 */
export const sortData = (state, action) => {
  const { data, isAscending, sortBy } = state;
  const { sortBy: newSortBy } = action;
  const columnKeyToDataType = {
    itemCode: 'string',
    itemName: 'string',
    availableQuantity: 'number',
    totalQuantity: 'number',
  };

  // If the new sortBy is the same as the sortBy in state, then invert isAscending
  // that was set by the last sortBy action. Otherwise, default to true.
  const newIsAscending = newSortBy === sortBy ? !isAscending : true;

  const newData = newSortDataBy(data, newSortBy, columnKeyToDataType[newSortBy], newIsAscending);
  return { ...state, data: newData, sortBy: newSortBy, isAscending: newIsAscending };
};

/**
 * Opens a basic modal which does not require any other value
 * to be set upon opening. i.e. text editors
 *
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * Action: { type: 'openBasicModal', modalKey }
 */
export const openBasicModal = (state, action) => {
  const { modalKey } = action;
  return { ...state, modalIsOpen: true, modalKey };
};

/**
 * Sets the modal open state to false, closing any
 * modal that is open.
 *
 * @param {Object} state  The current state
 * Action: { type: 'closeBasicModal' }
 */
export const closeBasicModal = state => ({ ...state, modalIsOpen: false, modalKey: '' });
<commit_msg>Add  addMasterListItems reducer case<commit_after>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

/* eslint-disable import/prefer-default-export */
import { parsePositiveInteger, newSortDataBy } from '../../../utilities';

/**
 * Immutably clears the current focus
 * @param {object} currState  the copy of state you want affected
 * @return {object}           New state with no cell focused
 */
const clearFocus = state => {
  const { dataState, currentFocusedRowKey } = state;
  if (!currentFocusedRowKey) {
    return state;
  }

  const newDataState = new Map(dataState);
  const currentRowState = newDataState.get(currentFocusedRowKey);
  newDataState.set(currentFocusedRowKey, {
    ...currentRowState,
    focusedColumn: null,
  });

  return { ...state, dataState: newDataState, currentFocusedRowKey: null };
};

/**
 * Immutably sets the current focus to the cell identified by `rowKey` and `columnKey`
 * @param {object} state  The copy of state to affect
 * @param {string} rowKey     The key of the row the cell to focus is in
 * @param {string} columnKey  The key of the column the cell to focus is in
 * @return {object}           A new object with a cell focused
 */
const setFocus = (state, rowKey, columnKey) => {
  const { dataState, currentFocusedRowKey } = state;
  const newDataState = new Map(dataState);

  // Clear previous focus if in a different row
  if (currentFocusedRowKey && rowKey !== currentFocusedRowKey) {
    const currentRowState = newDataState.get(currentFocusedRowKey);
    newDataState.set(currentFocusedRowKey, {
      ...currentRowState,
      focusedColumn: null,
    });
  }

  // Update focusedColumn in specified row
  const nextRowState = newDataState.get(rowKey);
  newDataState.set(rowKey, {
    ...nextRowState,
    focusedColumn: columnKey,
  });

  return {
    ...state,
    dataState: newDataState,
    currentFocusedRowKey: rowKey,
  };
};

/**
 *
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 */
export const filterData = (state, action) => {
  const { backingData, filterDataKeys, sortBy, isAscending } = state;
  const { searchTerm } = action;

  const columnKeyToDataType = {
    itemCode: 'string',
    itemName: 'string',
    availableQuantity: 'number',
    totalQuantity: 'number',
  };

  const queryString = filterDataKeys
    .map(filterTerm => `${filterTerm} BEGINSWITH[c]  $0`)
    .join(' OR ');

  const newData = newSortDataBy(
    backingData.filtered(queryString, searchTerm).slice(),
    sortBy,
    columnKeyToDataType[sortBy],
    isAscending
  );
  return { ...state, data: newData };
};

export const editTotalQuantity = (state, action) => {
  const { value, rowKey } = action;
  const { data, database, dataState, keyExtractor } = state;

  const objectToEdit = data.find(row => keyExtractor(row) === rowKey);

  database.write(() => {
    objectToEdit.setTotalQuantity(database, parsePositiveInteger(Number(value)));
    database.save('TransactionItem', objectToEdit);
  });

  // Change object reference of row in `dataState` to trigger rerender of that row.
  // Realm object reference in `data` can't be affected in any tidy manner.
  const newDataState = new Map(dataState);
  const nextRowState = newDataState.get(rowKey);
  newDataState.set(rowKey, {
    ...nextRowState,
  });

  return {
    ...state,
    dataState: newDataState,
  };
};

/**
 * Focus the next appropriate cell after the current cell provided in action
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 */
export const focusNextCell = (state, action) => {
  const { data, columns, keyExtractor } = state;
  const { rowKey, columnKey } = action;

  // Handle finding next cell to focus
  let nextEditableColKey;
  const currentColIndex = columns.findIndex(col => col.key === columnKey);
  for (let index = currentColIndex + 1; index < columns.length; index++) {
    if (columns[index].type === 'editable') {
      nextEditableColKey = columns[index].key;
      break;
    }
  }

  if (nextEditableColKey) {
    // Focus next editable cell in row
    return setFocus(state, rowKey, nextEditableColKey);
  }

  // Attempt moving focus to next row
  const nextRowIndex = data.findIndex(row => keyExtractor(row) === rowKey) + 1;

  if (nextRowIndex < data.length) {
    // Focus first editable cell in next row
    const nextRowKey = keyExtractor(data[nextRowIndex]);
    const firstEditableColKey = columns.find(col => col.type === 'editable').key;
    return setFocus(state, nextRowKey, firstEditableColKey);
  }

  // We were on the last row and last column, so unfocus current cell
  return clearFocus(state);
};

/**
 * Sets the provided row in action as selected
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * Action: { type: 'selectRow', rowKey }
 */
export const selectRow = (state, action) => {
  const { dataState } = state;
  const { rowKey } = action;
  const newDataState = new Map(dataState);

  const rowState = newDataState.get(rowKey);
  newDataState.set(rowKey, {
    ...rowState,
    isSelected: true,
  });

  return { ...state, dataState: newDataState };
};

/**
 * Removes the provided row in action from being
 * selected.
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * Action: { type: 'deselectRow', rowKey }
 */
export const deselectRow = (state, action) => {
  const { dataState } = state;
  const { rowKey } = action;
  const newDataState = new Map(dataState);

  const rowState = newDataState.get(rowKey);
  newDataState.set(rowKey, {
    ...rowState,
    isSelected: false,
  });

  return { ...state, dataState: newDataState };
};

/**
 * Removes all rows from being selected.
 * @param {Object} state  The current state
 * Action: { type: 'deselectAll' }
 */
export const deselectAll = state => {
  const { dataState } = state;
  const newDataState = new Map(dataState);

  // eslint-disable-next-line no-restricted-syntax
  for (const [rowKey, rowState] of newDataState.entries()) {
    if (rowState.isSelected) {
      newDataState.set(rowKey, {
        ...rowState,
        isSelected: false,
      });
    }
  }
  return { ...state, dataState: newDataState };
};

/**
 * Focuses the cell provided in the action passed
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * action: { type: 'focusCell', rowKey, columnKey }
 */
export const focusCell = (state, action) => {
  // Clear any existing focus and focus cell specified in action
  const { rowKey, columnKey } = action;
  return setFocus(state, rowKey, columnKey);
};

/**
 * Sorts the current set of data by the provided
 * key and direction in action.
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * Action: { type: 'sortData', sortBy }
 * sortBy: String key of the field of the objects to sort by.
 */
export const sortData = (state, action) => {
  const { data, isAscending, sortBy } = state;
  const { sortBy: newSortBy } = action;
  const columnKeyToDataType = {
    itemCode: 'string',
    itemName: 'string',
    availableQuantity: 'number',
    totalQuantity: 'number',
  };

  // If the new sortBy is the same as the sortBy in state, then invert isAscending
  // that was set by the last sortBy action. Otherwise, default to true.
  const newIsAscending = newSortBy === sortBy ? !isAscending : true;

  const newData = newSortDataBy(data, newSortBy, columnKeyToDataType[newSortBy], newIsAscending);
  return { ...state, data: newData, sortBy: newSortBy, isAscending: newIsAscending };
};

/**
 * Opens a basic modal which does not require any other value
 * to be set upon opening. i.e. text editors
 *
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * Action: { type: 'openBasicModal', modalKey }
 */
export const openBasicModal = (state, action) => {
  const { modalKey } = action;
  return { ...state, modalIsOpen: true, modalKey };
};

/**
 * Sets the modal open state to false, closing any
 * modal that is open.
 *
 * @param {Object} state  The current state
 * Action: { type: 'closeBasicModal' }
 */
export const closeBasicModal = state => ({ ...state, modalIsOpen: false, modalKey: '' });

/**
 * Adds all items from a master list to the pageObject held
 * in state - either a Requisition or Transaction.
 * @param {Object} state  The current state
 * @param {Object} action The action to act upon
 * Action: { type: 'addMasterListItems', objectType }
 */
export const addMasterListItems = (state, action) => {
  const { pageObject, database, backingData } = state;
  const { objectType } = action;

  database.write(() => {
    pageObject.addItemsFromMasterList(database);
    database.save(objectType, pageObject);
  });

  const newData = backingData.slice();

  return {
    ...state,
    data: newData,
  };
};
<|endoftext|>


<commit_before>if (Ti.Platform.name == 'android')
{
	Titanium.UI.currentWindow.backgroundColor = '#4e5c4d';
}
else
{
	Titanium.UI.currentWindow.backgroundColor = '#aebcad';
}

// create table view data object
var data = [
	{title:'Basic', hasChild:true, test:'../examples/table_view_basic.js', header:'Simple Table API'},
	{title:'Performance', hasChild:true, test:'../examples/table_view_perf.js'},
	{title:'Custom Row Data', hasChild:true, test:'../examples/table_view_custom_rowdata.js'},
	{title:'Headers', hasChild:true, test:'../examples/table_view_headers.js'},
	{title:'Footers', hasChild:true, test:'../examples/table_view_footers.js'},
	{title:'Table API Basic', hasChild:true, test:'../examples/table_view_api_basic.js', header:'New Programmatic API'},
	{title:'Table Custom Row Header', hasChild:true, test:'../examples/table_view_api_custom_rowheader.js'},
	{title:'Table Section Header', hasChild:true, test:'../examples/table_view_section_header.js'},
	{title:'Table Empty Dataset (Create)', hasChild:true, test:'../examples/table_view_api_emptydata.js'},
	{title:'Append Row', hasChild:true, test:'../examples/table_view_row_append.js'},
	{title:'Delete Row', hasChild:true, test:'../examples/table_view_row_delete.js'},
	{title:'Insert Row', hasChild:true, test:'../examples/table_view_row_insert.js'},
	{title:'Update Row', hasChild:true, test:'../examples/table_view_row_update.js'},
	{title:'Set Row Data', hasChild:true, test:'../examples/table_view_set.js'},
	{title:'Remove all rows', hasChild:true, test:'../examples/table_view_removeall.js'},
	{title:'Empty Table View', hasChild:true, test:'../examples/table_view_empty.js'},
	{title:'Table Auto Height', hasChild:true, test:'../examples/table_view_api_auto_height.js'},
	{title:'Refresh Table View', hasChild:true, test:'../examples/table_view_refresh.js'},
	{title:'Composite (Partial Size)', hasChild:true, test:'../examples/table_view_composite.js'},
	{title:'Table View (Layout)', hasChild:true, test:'../examples/table_view_layout.js'},
	{title:'Table View (Layout 2)', hasChild:true, test:'../examples/table_view_layout_2.js'},
	{title:'Table View (Transparency)', hasChild:true, test:'../examples/table_view_layout_trans.js'}
];

// add iphone specific tests
if (Titanium.Platform.name == 'iPhone OS')
{
	// these are mostly working in android but require minor fixes

	data.push({title:'Table View (Layout 3)', hasChild:true, test:'../examples/table_view_layout_3.js'});
	data.push({title:'Table View (Layout 4)', hasChild:true, test:'../examples/table_view_layout_4.js'});
	data.push({title:'Table Custom Header', hasChild:true, test:'../examples/table_view_api_custom_header.js'});
	data.push({title:'Table Custom Footer', hasChild:true, test:'../examples/table_view_api_custom_footer.js'});
	data.push({title:'Table with Controls', hasChild:true, test:'../examples/table_view_api_controls.js'});
	data.push({title:'Table with Controls 2', hasChild:true, test:'../examples/table_view_controls_2.js'});
	data.push({title:'Update Row Objects', hasChild:true, test:'../examples/table_view_update_row_objects.js'});
	data.push({title:'Table View w/Text Field', hasChild:true, test:'../examples/table_view_textfield.js'});
	data.push({title:'Headers with Filter', hasChild:true, test:'../examples/table_view_headers_filter.js'});
	data.push({title:'Table View Options', hasChild:true, test:'../examples/table_view_options.js'});
	data.push({title:'Table with Remote Images', hasChild:true, test:'../examples/table_view_api_remote_images.js'});
	data.push({title:'Table with Remote Images 2', hasChild:true, test:'../examples/table_view_remote_images_2.js'});
	data.push({title:'Table Custom Cell Selection', hasChild:true, test:'../examples/table_view_cell_selection.js'});
	data.push({title:'Table Search', hasChild:true, test:'../examples/table_view_api_search.js'});
	data.push({title:'Grouped w/BG Image', hasChild:true, test:'../examples/table_view_grouped_bg_image.js'});
	data.push({title:'Delete Mode', hasChild:true, test:'../examples/table_view_delete.js'});
	data.push({title:'Delete Mode (2)', hasChild:true, test:'../examples/table_view_delete_2.js'});
	data.push({title:'Delete Mode (3)', hasChild:true, test:'../examples/table_view_delete_3.js'});
	data.push({title:'Move Mode', hasChild:true, test:'../examples/table_view_move.js'});
	data.push({title:'Table Grouped View', hasChild:true, test:'../examples/table_view_api_grouped.js'});
	data.push({title:'Table Empty AppendRow', hasChild:true, test:'../examples/table_view_api_empty_append.js'});
	data.push({title:'Selectable Rows', hasChild:true, test:'../examples/table_view_selectable.js'});
	data.push({title:'Events', hasChild:true, test:'../examples/table_view_events.js'});
	data.push({title:'Touch Events', hasChild:true, test:'../examples/table_view_touch.js'});
	data.push({title:'Edit and Move', hasChild:true, test:'../examples/table_view_edit_and_move.js'});
	data.push({title:'No Scrolling', hasChild:true, test:'../examples/table_view_noscroll.js'});
	data.push({title:'Pull to Refresh', hasChild:true, test:'../examples/table_view_pull_to_refresh.js'});
	data.push({title:'Dynamic Scrolling', hasChild:true, test:'../examples/table_view_dynamic_scroll.js'});
}
// create table view
var tableViewOptions = {
		data:data,
		style:Titanium.UI.iPhone.TableViewStyle.GROUPED,
		headerTitle:'TableView examples and test cases',
		footerTitle:"Wow. That was cool!",
		backgroundColor:'transparent',
		rowBackgroundColor:'white'
	};

var tableview = Titanium.UI.createTableView(tableViewOptions);

// create table view event listener
tableview.addEventListener('click', function(e)
{
	if (e.rowData.test)
	{
		var win = Titanium.UI.createWindow({
			url:e.rowData.test,
			title:e.rowData.title
		});
		Titanium.UI.currentTab.open(win,{animated:true});
	}
});

// add table view to the window
Titanium.UI.currentWindow.add(tableview);
<commit_msg>removed non test<commit_after>if (Ti.Platform.name == 'android')
{
	Titanium.UI.currentWindow.backgroundColor = '#4e5c4d';
}
else
{
	Titanium.UI.currentWindow.backgroundColor = '#aebcad';
}

// create table view data object
var data = [
	{title:'Basic', hasChild:true, test:'../examples/table_view_basic.js', header:'Simple Table API'},
	{title:'Performance', hasChild:true, test:'../examples/table_view_perf.js'},
	{title:'Custom Row Data', hasChild:true, test:'../examples/table_view_custom_rowdata.js'},
	{title:'Headers', hasChild:true, test:'../examples/table_view_headers.js'},
	{title:'Footers', hasChild:true, test:'../examples/table_view_footers.js'},
	{title:'Table API Basic', hasChild:true, test:'../examples/table_view_api_basic.js', header:'New Programmatic API'},
	{title:'Table Custom Row Header', hasChild:true, test:'../examples/table_view_api_custom_rowheader.js'},
	{title:'Table Section Header', hasChild:true, test:'../examples/table_view_section_header.js'},
	{title:'Table Empty Dataset (Create)', hasChild:true, test:'../examples/table_view_api_emptydata.js'},
	{title:'Append Row', hasChild:true, test:'../examples/table_view_row_append.js'},
	{title:'Delete Row', hasChild:true, test:'../examples/table_view_row_delete.js'},
	{title:'Insert Row', hasChild:true, test:'../examples/table_view_row_insert.js'},
	{title:'Update Row', hasChild:true, test:'../examples/table_view_row_update.js'},
	{title:'Set Row Data', hasChild:true, test:'../examples/table_view_set.js'},
	{title:'Remove all rows', hasChild:true, test:'../examples/table_view_removeall.js'},
	{title:'Empty Table View', hasChild:true, test:'../examples/table_view_empty.js'},
	{title:'Table Auto Height', hasChild:true, test:'../examples/table_view_api_auto_height.js'},
	{title:'Refresh Table View', hasChild:true, test:'../examples/table_view_refresh.js'},
	{title:'Composite (Partial Size)', hasChild:true, test:'../examples/table_view_composite.js'},
	{title:'Table View (Layout)', hasChild:true, test:'../examples/table_view_layout.js'},
	{title:'Table View (Layout 2)', hasChild:true, test:'../examples/table_view_layout_2.js'},
];

// add iphone specific tests
if (Titanium.Platform.name == 'iPhone OS')
{
	// these are mostly working in android but require minor fixes

	data.push({title:'Table View (Layout 3)', hasChild:true, test:'../examples/table_view_layout_3.js'});
	data.push({title:'Table View (Layout 4)', hasChild:true, test:'../examples/table_view_layout_4.js'});
	data.push({title:'Table Custom Header', hasChild:true, test:'../examples/table_view_api_custom_header.js'});
	data.push({title:'Table Custom Footer', hasChild:true, test:'../examples/table_view_api_custom_footer.js'});
	data.push({title:'Table with Controls', hasChild:true, test:'../examples/table_view_api_controls.js'});
	data.push({title:'Table with Controls 2', hasChild:true, test:'../examples/table_view_controls_2.js'});
	data.push({title:'Update Row Objects', hasChild:true, test:'../examples/table_view_update_row_objects.js'});
	data.push({title:'Table View w/Text Field', hasChild:true, test:'../examples/table_view_textfield.js'});
	data.push({title:'Headers with Filter', hasChild:true, test:'../examples/table_view_headers_filter.js'});
	data.push({title:'Table View Options', hasChild:true, test:'../examples/table_view_options.js'});
	data.push({title:'Table with Remote Images', hasChild:true, test:'../examples/table_view_api_remote_images.js'});
	data.push({title:'Table with Remote Images 2', hasChild:true, test:'../examples/table_view_remote_images_2.js'});
	data.push({title:'Table Custom Cell Selection', hasChild:true, test:'../examples/table_view_cell_selection.js'});
	data.push({title:'Table Search', hasChild:true, test:'../examples/table_view_api_search.js'});
	data.push({title:'Grouped w/BG Image', hasChild:true, test:'../examples/table_view_grouped_bg_image.js'});
	data.push({title:'Delete Mode', hasChild:true, test:'../examples/table_view_delete.js'});
	data.push({title:'Delete Mode (2)', hasChild:true, test:'../examples/table_view_delete_2.js'});
	data.push({title:'Delete Mode (3)', hasChild:true, test:'../examples/table_view_delete_3.js'});
	data.push({title:'Move Mode', hasChild:true, test:'../examples/table_view_move.js'});
	data.push({title:'Table Grouped View', hasChild:true, test:'../examples/table_view_api_grouped.js'});
	data.push({title:'Table Empty AppendRow', hasChild:true, test:'../examples/table_view_api_empty_append.js'});
	data.push({title:'Selectable Rows', hasChild:true, test:'../examples/table_view_selectable.js'});
	data.push({title:'Events', hasChild:true, test:'../examples/table_view_events.js'});
	data.push({title:'Touch Events', hasChild:true, test:'../examples/table_view_touch.js'});
	data.push({title:'Edit and Move', hasChild:true, test:'../examples/table_view_edit_and_move.js'});
	data.push({title:'No Scrolling', hasChild:true, test:'../examples/table_view_noscroll.js'});
	data.push({title:'Pull to Refresh', hasChild:true, test:'../examples/table_view_pull_to_refresh.js'});
	data.push({title:'Dynamic Scrolling', hasChild:true, test:'../examples/table_view_dynamic_scroll.js'});
}
// create table view
var tableViewOptions = {
		data:data,
		style:Titanium.UI.iPhone.TableViewStyle.GROUPED,
		headerTitle:'TableView examples and test cases',
		footerTitle:"Wow. That was cool!",
		backgroundColor:'transparent',
		rowBackgroundColor:'white'
	};

var tableview = Titanium.UI.createTableView(tableViewOptions);

// create table view event listener
tableview.addEventListener('click', function(e)
{
	if (e.rowData.test)
	{
		var win = Titanium.UI.createWindow({
			url:e.rowData.test,
			title:e.rowData.title
		});
		Titanium.UI.currentTab.open(win,{animated:true});
	}
});

// add table view to the window
Titanium.UI.currentWindow.add(tableview);
<|endoftext|>


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

'use strict';

import type { Fiber } from 'ReactFiber';

var ReactInstanceMap = require('ReactInstanceMap');

var invariant = require('invariant');

var {
  HostContainer,
  HostComponent,
  HostText,
} = require('ReactTypeOfWork');

var {
  NoEffect,
  Placement,
} = require('ReactTypeOfSideEffect');

var MOUNTING = 1;
var MOUNTED = 2;
var UNMOUNTED = 3;

function isFiberMounted(fiber : Fiber) : number {
  let node = fiber;
  if (!fiber.alternate) {
    // If there is no alternate, this might be a new tree that isn't inserted
    // yet. If it is, then it will have a pending insertion effect on it.
    if ((node.effectTag & Placement) !== NoEffect) {
      return MOUNTING;
    }
    while (node.return) {
      node = node.return;
      if ((node.effectTag & Placement) !== NoEffect) {
        return MOUNTING;
      }
    }
  } else {
    while (node.return) {
      node = node.return;
    }
  }
  if (node.tag === HostContainer) {
    // TODO: Check if this was a nested HostContainer when used with
    // renderContainerIntoSubtree.
    return MOUNTED;
  }
  // If we didn't hit the root, that means that we're in an disconnected tree
  // that has been unmounted.
  return UNMOUNTED;
}
exports.isFiberMounted = isFiberMounted;

exports.isMounted = function(component : ReactComponent<any, any, any>) : boolean {
  var fiber : ?Fiber = ReactInstanceMap.get(component);
  if (!fiber) {
    return false;
  }
  return isFiberMounted(fiber) === MOUNTED;
};

exports.findCurrentHostFiber = function(parent : Fiber) : Fiber | null {
  // First check if this node itself is mounted.
  const state = isFiberMounted(parent, true);
  if (state === UNMOUNTED) {
    invariant(
      false,
      'Unable to find node on an unmounted component.'
    );
  } else if (state === MOUNTING) {
    return null;
  }

  let didTryOtherTree = false;

  // If the component doesn't have a child we first check the alternate to see
  // if it has any and if so, if those were just recently inserted.
  if (!parent.child && parent.alternate) {
    parent = parent.alternate;
  }

  // Next we'll drill down this component to find the first HostComponent/Text.
  let node : Fiber = parent;
  while (true) {
    if ((node.effectTag & Placement) !== NoEffect || !node.return) {
      // If any node along the way was deleted, or is an insertion, that means
      // that we're actually in a work in progress to update this component with
      // a different component. We need to look in the "current" fiber instead.
      if (!parent.alternate) {
        return null;
      }
      if (didTryOtherTree) {
        // Safety, to avoid an infinite loop if something goes wrong.
        throw new Error('This should never hit this infinite loop.');
      }
      didTryOtherTree = true;
      node = parent = parent.alternate;
      continue;
    }
    if (node.tag === HostComponent || node.tag === HostText) {
      return node;
    } else if (node.child) {
      // TODO: Coroutines need to visit the stateNode.
      node = node.child;
      continue;
    }
    if (node === parent) {
      return null;
    }
    while (!node.sibling) {
      if (!node.return || node.return === parent) {
        return null;
      }
      node = node.return;
    }
    node = node.sibling;
  }
  // Flow needs the return null here, but ESLint complains about it.
  // eslint-disable-next-line no-unreachable
  return null;
};

exports.getComponentName = function(fiber: Fiber): string {
  const type = fiber.type;
  const instance = fiber.stateNode;
  const constructor = instance && instance.constructor;
  return (
    type.displayName || (constructor && constructor.displayName) ||
    type.name || (constructor && constructor.name) ||
    'A Component'
  );
};
<commit_msg>Fix isFiberMounted exports (#8465)<commit_after>/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ReactFiberTreeReflection
 * @flow
 */

'use strict';

import type { Fiber } from 'ReactFiber';

var ReactInstanceMap = require('ReactInstanceMap');

var invariant = require('invariant');

var {
  HostContainer,
  HostComponent,
  HostText,
} = require('ReactTypeOfWork');

var {
  NoEffect,
  Placement,
} = require('ReactTypeOfSideEffect');

var MOUNTING = 1;
var MOUNTED = 2;
var UNMOUNTED = 3;

function isFiberMountedImpl(fiber : Fiber) : number {
  let node = fiber;
  if (!fiber.alternate) {
    // If there is no alternate, this might be a new tree that isn't inserted
    // yet. If it is, then it will have a pending insertion effect on it.
    if ((node.effectTag & Placement) !== NoEffect) {
      return MOUNTING;
    }
    while (node.return) {
      node = node.return;
      if ((node.effectTag & Placement) !== NoEffect) {
        return MOUNTING;
      }
    }
  } else {
    while (node.return) {
      node = node.return;
    }
  }
  if (node.tag === HostContainer) {
    // TODO: Check if this was a nested HostContainer when used with
    // renderContainerIntoSubtree.
    return MOUNTED;
  }
  // If we didn't hit the root, that means that we're in an disconnected tree
  // that has been unmounted.
  return UNMOUNTED;
}
exports.isFiberMounted = function(fiber : Fiber) : boolean {
  return isFiberMountedImpl(fiber) === MOUNTED;
};

exports.isMounted = function(component : ReactComponent<any, any, any>) : boolean {
  var fiber : ?Fiber = ReactInstanceMap.get(component);
  if (!fiber) {
    return false;
  }
  return isFiberMountedImpl(fiber) === MOUNTED;
};

exports.findCurrentHostFiber = function(parent : Fiber) : Fiber | null {
  // First check if this node itself is mounted.
  const state = isFiberMountedImpl(parent, true);
  if (state === UNMOUNTED) {
    invariant(
      false,
      'Unable to find node on an unmounted component.'
    );
  } else if (state === MOUNTING) {
    return null;
  }

  let didTryOtherTree = false;

  // If the component doesn't have a child we first check the alternate to see
  // if it has any and if so, if those were just recently inserted.
  if (!parent.child && parent.alternate) {
    parent = parent.alternate;
  }

  // Next we'll drill down this component to find the first HostComponent/Text.
  let node : Fiber = parent;
  while (true) {
    if ((node.effectTag & Placement) !== NoEffect || !node.return) {
      // If any node along the way was deleted, or is an insertion, that means
      // that we're actually in a work in progress to update this component with
      // a different component. We need to look in the "current" fiber instead.
      if (!parent.alternate) {
        return null;
      }
      if (didTryOtherTree) {
        // Safety, to avoid an infinite loop if something goes wrong.
        throw new Error('This should never hit this infinite loop.');
      }
      didTryOtherTree = true;
      node = parent = parent.alternate;
      continue;
    }
    if (node.tag === HostComponent || node.tag === HostText) {
      return node;
    } else if (node.child) {
      // TODO: Coroutines need to visit the stateNode.
      node = node.child;
      continue;
    }
    if (node === parent) {
      return null;
    }
    while (!node.sibling) {
      if (!node.return || node.return === parent) {
        return null;
      }
      node = node.return;
    }
    node = node.sibling;
  }
  // Flow needs the return null here, but ESLint complains about it.
  // eslint-disable-next-line no-unreachable
  return null;
};

exports.getComponentName = function(fiber: Fiber): string {
  const type = fiber.type;
  const instance = fiber.stateNode;
  const constructor = instance && instance.constructor;
  return (
    type.displayName || (constructor && constructor.displayName) ||
    type.name || (constructor && constructor.name) ||
    'A Component'
  );
};
<|endoftext|>


<commit_before>/* global QUnit*/

sap.ui.define([
	"sap/ui/dt/DOMUtil",
	"sap/ui/Device"
],
function(
	DOMUtil,
	Device
) {
	"use strict";

	var SCROLLBAR_WIDTH = 11;
	var RELATIVE_POS_LEFT = -35;

	QUnit.module("Given that a container is rendered with a bigger content element (for scrollbars)", {
		beforeEach: function() {
			this.oContent = jQuery("

");
			this.oContainer = jQuery("

");
			this.oContainer.append(this.oContent).appendTo("#qunit-fixture");
		},
		afterEach: function() {
			this.oContainer.remove();
		}
	}, function() {
		QUnit.test("when getOffsetFromParent is called for the content without scrolling", function(assert) {
			var oContentGeometry = DOMUtil.getGeometry(this.oContent.get(0));
			var mOffset = DOMUtil.getOffsetFromParent(oContentGeometry, this.oContainer.get(0));
			//TODO: Remove when bug in Chrome and Safari is fixed
			var iExpectedOffsetLeft = (Device.browser.safari && !Device.browser.mobile) ? RELATIVE_POS_LEFT - SCROLLBAR_WIDTH : RELATIVE_POS_LEFT;
			// in some cases (special physical devices) the offset is returend as decimal value
			// actually we need to round the offset for chrome browser on mac
			assert.strictEqual(Math.ceil(mOffset.left), iExpectedOffsetLeft, "the left offset is correct - result: " +
				Math.ceil(mOffset.left) + " / expected value: " + iExpectedOffsetLeft);
			assert.strictEqual(mOffset.top, 40, "the top offset is correct");
		});

		QUnit.test("when getOffsetFromParent is called for the content after scrolling on the container", function(assert) {
			var oContainerDomRef = this.oContainer.get(0);
			var oContentGeometry = DOMUtil.getGeometry(this.oContent.get(0));
			var iScrollValue = -50;
			var iRelativePosLeft = iScrollValue + RELATIVE_POS_LEFT;
			//TODO: Remove when bug in Chrome and Safari is fixed
			var iExpectedOffsetLeft = (Device.browser.safari && !Device.browser.mobile) ? iRelativePosLeft - SCROLLBAR_WIDTH : iRelativePosLeft;
			var iMaxScrollWidth = oContainerDomRef.scrollWidth - oContainerDomRef.clientWidth;
			iScrollValue = iMaxScrollWidth + iScrollValue;
			jQuery(this.oContainer).scrollLeftRTL(iScrollValue);
			this.oContainer.scrollTop(60);
			var mOffset = DOMUtil.getOffsetFromParent(oContentGeometry, oContainerDomRef);
			// round for offset value is actually nedded for chrome browser on mac
			assert.strictEqual(Math.ceil(mOffset.left), iExpectedOffsetLeft,
				"the left offset is correct - result: " + Math.ceil(mOffset.left) + " / expected value: " + iExpectedOffsetLeft);
			assert.strictEqual(mOffset.top, 100, "the top offset is correct");
		});
	});

	QUnit.module("getScrollLeft()", {
		beforeEach: function() {
			this.$Panel = jQuery('

').css({
				width: '100px',
				height: '100px',
				overflow: 'auto'
			}).appendTo("#qunit-fixture");

			jQuery('

').css({
				width: '200px',
				height: '200px'
			}).appendTo(this.$Panel);
		},
		afterEach: function() {
			this.$Panel.remove();
		}
	}, function () {
		QUnit.test("initial position", function (assert) {
			var iScrollLeftResult = DOMUtil.getScrollLeft(this.$Panel.get(0));
			assert.strictEqual(Math.round(iScrollLeftResult), 0);
		});
		QUnit.test("scrolled to the most left position", function (assert) {
			var iMaxScrollLeftValue = this.$Panel.get(0).scrollWidth - this.$Panel.get(0).clientWidth;
			jQuery(this.$Panel).scrollLeftRTL(0);
			var iScrollLeftResult = DOMUtil.getScrollLeft(this.$Panel.get(0));
			assert.strictEqual(Math.round(iScrollLeftResult), -iMaxScrollLeftValue);
		});
	});

	QUnit.done(function() {
		jQuery("#qunit-fixture").hide();
	});
});<commit_msg>[INTERNAL][FIX] sap.ui.dt - fixed DOMUtilRtl test<commit_after>/* global QUnit*/

sap.ui.define([
	"sap/ui/dt/DOMUtil",
	"sap/ui/Device"
],
function(
	DOMUtil,
	Device
) {
	"use strict";

	var SCROLLBAR_WIDTH = DOMUtil.getScrollbarWidth();
	var RELATIVE_POS_LEFT = -35;

	QUnit.module("Given that a container is rendered with a bigger content element (for scrollbars)", {
		beforeEach: function() {
			this.oContent = jQuery("

");
			this.oContainer = jQuery("

");
			this.oContainer.append(this.oContent).appendTo("#qunit-fixture");
		},
		afterEach: function() {
			this.oContainer.remove();
		}
	}, function() {
		QUnit.test("when getOffsetFromParent is called for the content without scrolling", function(assert) {
			var oContentGeometry = DOMUtil.getGeometry(this.oContent.get(0));
			var mOffset = DOMUtil.getOffsetFromParent(oContentGeometry, this.oContainer.get(0));
			//TODO: Remove when bug in Chrome and Safari is fixed
			var iExpectedOffsetLeft = (Device.browser.safari && !Device.browser.mobile) ? RELATIVE_POS_LEFT - SCROLLBAR_WIDTH : RELATIVE_POS_LEFT;
			// in some cases (special physical devices) the offset is returend as decimal value
			// actually we need to round the offset for chrome browser on mac
			assert.strictEqual(Math.ceil(mOffset.left), iExpectedOffsetLeft, "the left offset is correct - result: " +
				Math.ceil(mOffset.left) + " / expected value: " + iExpectedOffsetLeft);
			assert.strictEqual(mOffset.top, 40, "the top offset is correct");
		});

		QUnit.test("when getOffsetFromParent is called for the content after scrolling on the container", function(assert) {
			var oContainerDomRef = this.oContainer.get(0);
			var oContentGeometry = DOMUtil.getGeometry(this.oContent.get(0));
			var iScrollValue = -50;
			var iRelativePosLeft = iScrollValue + RELATIVE_POS_LEFT;
			//TODO: Remove when bug in Chrome and Safari is fixed
			var iExpectedOffsetLeft = (Device.browser.safari && !Device.browser.mobile) ? iRelativePosLeft - SCROLLBAR_WIDTH : iRelativePosLeft;
			var iMaxScrollWidth = oContainerDomRef.scrollWidth - oContainerDomRef.clientWidth;
			iScrollValue = iMaxScrollWidth + iScrollValue;
			jQuery(this.oContainer).scrollLeftRTL(iScrollValue);
			this.oContainer.scrollTop(60);
			var mOffset = DOMUtil.getOffsetFromParent(oContentGeometry, oContainerDomRef);
			// round for offset value is actually nedded for chrome browser on mac
			assert.strictEqual(Math.ceil(mOffset.left), iExpectedOffsetLeft,
				"the left offset is correct - result: " + Math.ceil(mOffset.left) + " / expected value: " + iExpectedOffsetLeft);
			assert.strictEqual(mOffset.top, 100, "the top offset is correct");
		});
	});

	QUnit.module("getScrollLeft()", {
		beforeEach: function() {
			this.$Panel = jQuery('

').css({
				width: '100px',
				height: '100px',
				overflow: 'auto'
			}).appendTo("#qunit-fixture");

			jQuery('

').css({
				width: '200px',
				height: '200px'
			}).appendTo(this.$Panel);
		},
		afterEach: function() {
			this.$Panel.remove();
		}
	}, function () {
		QUnit.test("initial position", function (assert) {
			var iScrollLeftResult = DOMUtil.getScrollLeft(this.$Panel.get(0));
			assert.strictEqual(Math.round(iScrollLeftResult), 0);
		});
		QUnit.test("scrolled to the most left position", function (assert) {
			var iMaxScrollLeftValue = this.$Panel.get(0).scrollWidth - this.$Panel.get(0).clientWidth;
			jQuery(this.$Panel).scrollLeftRTL(0);
			var iScrollLeftResult = DOMUtil.getScrollLeft(this.$Panel.get(0));
			assert.strictEqual(Math.round(iScrollLeftResult), -iMaxScrollLeftValue);
		});
	});

	QUnit.done(function() {
		jQuery("#qunit-fixture").hide();
	});
});<|endoftext|>