<commit_before>var numVals = {}

numVals.add = function(key, value)
{
	numVals[key] = value + "";
}

for( var i = 0; i < 10; i++ )
{
	var val = i + 48;
	numVals.add(i, val);
}

for( var i = 0; i < 26; i++ )
{
	var val = i + 65;
	var char = String.fromCharCode(val);
	numVals.add(char, val);
	numVals.add(i + 10, val);
}

function number( positive, value )
{
	this.positive = positive;
	this.value = value + "";
	this.isHex = false;
	this.toString = function()
	{
		var sign = this.positive ? "+" : "-";
		return sign + this.value;
	}
}

//note: does not handle hex.
number.prototype.biggerThan = function( otherNumber, inclusive )
{
	if( this.positive && !otherNumber.positive ){ return true; }
	else if( !this.positive && otherNumber.positive ){ return false; }
	else if( !this.positive && !otherNumber.positive )
	{
		var tempNumber = new number( true, this.value );
		var tempOtherNumber = new number( true, otherNumber.value );
		return tempOtherNumber.biggerThan(tempNumber);
	}
	else
	{
		if( this.value.length > otherNumber.value.length )
		{
			return true;
		}
		else if( this.value.length < otherNumber.value.length )
		{
			return false;
		}
		else
		{
			for(var i = 0; i < this.value.length; i++)
			{
				if( parseInt(this.value[i]) > parseInt(otherNumber.value[i]))
				{
					return true;
				}
				if( parseInt(this.value[i]) < parseInt(otherNumber.value[i]))
				{
					return false;
				}
			}

			if( inclusive == undefined || inclusive == false ){ return false; }
			if( inclusive == true ){ return true; }
		}
	}
}

number.prototype.smallerThan = function( otherNumber , inclusive)
{
	return otherNumber.biggerThan(this, inclusive);
}

number.prototype.biggerThanOrEqualTo = function( otherNumber )
{
	return this.biggerThan( otherNumber, true );
}

number.prototype.smallerThanOrEqualTo = function( otherNumber )
{
	return this.smallerThan( otherNumber, true );
}

function getPaddingString(length)
{
	var string = "";
	for(var i = 0; i < length; i++)
	{
		string += "0";
	}
	return string;
}

number.prototype.plus = function( otherNumber )
{
	if(!this.positive)
	{
		if(!otherNumber.positive)
		{
			var tempThis = new number(true, this.value);
			var tempThat = new number(true, otherNumber.value);
			var correctVal = tempThis.plus(tempThat).value;
			return new number(false, correctVal)
		}
		else
		{
			var tempThis = new number( true, this.value );
			return otherNumber.minus(this);
		}
	}
	else if(!otherNumber.positive)
	{
		var tempThat = new number( true, otherNumber.value );
		return this.minus(tempThat);
	}

	var thisLastDigit = this.value.length;
	var thatLastDigit = otherNumber.value.length;
	var carried = 0;
	var finalString = "";

	var tempThis = "0" + this.value;
	var tempOtherNumber = "0" + otherNumber.value;

	var padding = thisLastDigit - thatLastDigit;

	var paddingString = getPaddingString(Math.abs(padding));

	if(padding > 0)
	{
		tempOtherNumber = paddingString + tempOtherNumber;
	}
	else
	{
		tempThis = paddingString + tempThis;
	}

	for(var i = Math.min(thisLastDigit, thatLastDigit) + Math.abs(padding); i > -1; i--)
	{
		var newNum = tempThis.charCodeAt(i) + tempOtherNumber.charCodeAt(i) + carried - 96;
		if(newNum >= 10)
		{
			carried = ( newNum - (newNum % 10) ) / 10;
			newNum = newNum % 10;
		}
		else
		{
			carried = 0;
		}

		finalString = newNum + finalString + "";
	}

	while(finalString.length > 1 && finalString[0] == "0"){ finalString = finalString.substr(1); }

	return new number( true, finalString );
}

//todo: do this next
//note: only works for positive numbers for now.
//lies. This is not yet done.
number.prototype.minus = function( otherNumber )
{
	if(!this.positive)
	{
		if(!otherNumber.positive)
		{
			var tempThat = new number(true, otherNumber.value);
			return tempThat.plus(this);
		}
		else
		{
			return otherNumber.plus(this);
		}
	}
	else if(!otherNumber.positive)
	{
		var tempThat = new number( true, otherNumber.value );
		return this.plus(tempThat);
	}

	var thisLastDigit = this.value.length;
	var thatLastDigit = otherNumber.value.length;
	var carried = 0;
	var finalString = "";

	var tempThis = "0" + this.value;
	var tempOtherNumber = "0" + otherNumber.value;

	var positive = this.biggerThan(otherNumber, true);

	if(positive)
	{
		var padding = thisLastDigit - thatLastDigit;
		var paddingString = getPaddingString(Math.abs(padding));
		tempOtherNumber = paddingString + tempOtherNumber;

		for(var i = Math.min(thisLastDigit, thatLastDigit) + Math.abs(padding); i > -1; i--)
		{
			var newNum = tempThis.charCodeAt(i) - tempOtherNumber.charCodeAt(i) - carried;
			if(newNum < 0 )
			{
				carried = 1;
				newNum = (newNum + 10) % 10;
			}
			else
			{
				carried = 0;
			}

			finalString = newNum + finalString + "";
		}

		while(finalString.length > 1 && finalString[0] == "0"){ finalString = finalString.substr(1); }
		return new number( true, finalString );
	}
	else
	{
		var correctValue = otherNumber.minus(this).value;
		return new number( false, correctValue );
	}
}

//note: note implemented. Use newton-raphson division algorithm?
number.prototype.divideBy = function(divisor)
{
	if(divisor.value == "0")
	{
		alert("You cannot divide by 0!!!");
	}

	alert("not implemented!");

	return { quotient: this, remainder: 5 };
}

//note: returns array of numbers.
function splitInHalf(x)
{
	var result = [];
	result.push( new number( x.positive,  x.value.substr(0, Math.ceil( x.value.length / 2 ) - 1) ));
	result.push( new number( x.positive,  x.value.substr( Math.ceil( x.value.length / 2 ) - 1, 0) ));

	return result;
}

function getChars(x, char)
{
	var n = x;
	result = "";
	while(n > 0){
		result += char;
		n--;
	}
	return result;
}

//note: this needs to be more accurate for x >> y and x << y, or will cause rounding errors.
function longMultiply(x, y)
{
	var positive = x.positive == y.positive;
	return new number(positive , parseInt(x.value) * parseInt(y.value) );
}

number.prototype.timesTenToThe = function(x)
{
	var tempThis = this;
	tempThis.value += getChars(x, "0");
	return tempThis;
}

function karatsuba(x, y)
{
	x_length = x.value.length;
	y_length = y.value.length;

	if(x_length == 1 || y_length == 1)
	{
		return longMultiply(x, y);
	}

	var xSplit = splitInHalf(x);
	var ySplit = splitInHalf(y);

	var a = karatsuba(xSplit[0], ySplit[0]).timesTenToThe(2 * Math.floor(x_length / 2));
	var c = karatsuba(xSplit[1], ySplit[1]);
	var b = karatsuba(xSplit[0].plus(xSplit[1]).value, ySplit[0].plus(ySplit[1]).value).minus(a).minus(c).timesTenToThe(Math.floor(x_length / 2));

	return a.plus(c).plus(b);
}

<commit_msg>Update number.js<commit_after>var numVals = {}

numVals.add = function(key, value)
{
	numVals[key] = value + "";
}

for( var i = 0; i < 10; i++ )
{
	var val = i + 48;
	numVals.add(i, val);
}

for( var i = 0; i < 26; i++ )
{
	var val = i + 65;
	var char = String.fromCharCode(val);
	numVals.add(char, val);
	numVals.add(i + 10, val);
}

function number( positive, value )
{
	this.positive = positive;
	this.value = value + "";
	this.isHex = false;
	this.toString = function()
	{
		var sign = this.positive ? "+" : "-";
		return sign + this.value;
	}
}

//note: does not handle hex.
number.prototype.biggerThan = function( otherNumber, inclusive )
{
	if( this.positive && !otherNumber.positive ){ return true; }
	else if( !this.positive && otherNumber.positive ){ return false; }
	else if( !this.positive && !otherNumber.positive )
	{
		var tempNumber = new number( true, this.value );
		var tempOtherNumber = new number( true, otherNumber.value );
		return tempOtherNumber.biggerThan(tempNumber);
	}
	else
	{
		if( this.value.length > otherNumber.value.length )
		{
			return true;
		}
		else if( this.value.length < otherNumber.value.length )
		{
			return false;
		}
		else
		{
			for(var i = 0; i < this.value.length; i++)
			{
				if( parseInt(this.value[i]) > parseInt(otherNumber.value[i]))
				{
					return true;
				}
				if( parseInt(this.value[i]) < parseInt(otherNumber.value[i]))
				{
					return false;
				}
			}

			if( inclusive == undefined || inclusive == false ){ return false; }
			if( inclusive == true ){ return true; }
		}
	}
}

number.prototype.smallerThan = function( otherNumber , inclusive)
{
	return otherNumber.biggerThan(this, inclusive);
}

number.prototype.biggerThanOrEqualTo = function( otherNumber )
{
	return this.biggerThan( otherNumber, true );
}

number.prototype.smallerThanOrEqualTo = function( otherNumber )
{
	return this.smallerThan( otherNumber, true );
}

function getPaddingString(length)
{
	var string = "";
	for(var i = 0; i < length; i++)
	{
		string += "0";
	}
	return string;
}

number.prototype.plus = function( otherNumber )
{
	if(!this.positive)
	{
		if(!otherNumber.positive)
		{
			var tempThis = new number(true, this.value);
			var tempThat = new number(true, otherNumber.value);
			var correctVal = tempThis.plus(tempThat).value;
			return new number(false, correctVal)
		}
		else
		{
			var tempThis = new number( true, this.value );
			return otherNumber.minus(this);
		}
	}
	else if(!otherNumber.positive)
	{
		var tempThat = new number( true, otherNumber.value );
		return this.minus(tempThat);
	}

	var thisLastDigit = this.value.length;
	var thatLastDigit = otherNumber.value.length;
	var carried = 0;
	var finalString = "";

	var tempThis = "0" + this.value;
	var tempOtherNumber = "0" + otherNumber.value;

	var padding = thisLastDigit - thatLastDigit;

	var paddingString = getPaddingString(Math.abs(padding));

	if(padding > 0)
	{
		tempOtherNumber = paddingString + tempOtherNumber;
	}
	else
	{
		tempThis = paddingString + tempThis;
	}

	for(var i = Math.min(thisLastDigit, thatLastDigit) + Math.abs(padding); i > -1; i--)
	{
		var newNum = tempThis.charCodeAt(i) + tempOtherNumber.charCodeAt(i) + carried - 96;
		if(newNum >= 10)
		{
			carried = ( newNum - (newNum % 10) ) / 10;
			newNum = newNum % 10;
		}
		else
		{
			carried = 0;
		}

		finalString = newNum + finalString + "";
	}

	while(finalString.length > 1 && finalString[0] == "0"){ finalString = finalString.substr(1); }

	return new number( true, finalString );
}

//todo: do this next
//note: only works for positive numbers for now.
//lies. This is not yet done.
number.prototype.minus = function( otherNumber )
{
	if(!this.positive)
	{
		if(!otherNumber.positive)
		{
			var tempThat = new number(true, otherNumber.value);
			return tempThat.plus(this);
		}
		else
		{
			return otherNumber.plus(this);
		}
	}
	else if(!otherNumber.positive)
	{
		var tempThat = new number( true, otherNumber.value );
		return this.plus(tempThat);
	}

	var thisLastDigit = this.value.length;
	var thatLastDigit = otherNumber.value.length;
	var carried = 0;
	var finalString = "";

	var tempThis = "0" + this.value;
	var tempOtherNumber = "0" + otherNumber.value;

	var positive = this.biggerThan(otherNumber, true);

	if(positive)
	{
		var padding = thisLastDigit - thatLastDigit;
		var paddingString = getPaddingString(Math.abs(padding));
		tempOtherNumber = paddingString + tempOtherNumber;

		for(var i = Math.min(thisLastDigit, thatLastDigit) + Math.abs(padding); i > -1; i--)
		{
			var newNum = tempThis.charCodeAt(i) - tempOtherNumber.charCodeAt(i) - carried;
			if(newNum < 0 )
			{
				carried = 1;
				newNum = (newNum + 10) % 10;
			}
			else
			{
				carried = 0;
			}

			finalString = newNum + finalString + "";
		}

		while(finalString.length > 1 && finalString[0] == "0"){ finalString = finalString.substr(1); }
		return new number( true, finalString );
	}
	else
	{
		var correctValue = otherNumber.minus(this).value;
		return new number( false, correctValue );
	}
}

//note: note implemented. Use newton-raphson division algorithm?
number.prototype.divideBy = function(divisor)
{
	if(divisor.value == "0")
	{
		alert("You cannot divide by 0!!!");
	}

	alert("not implemented!");

	return { quotient: this, remainder: 5 };
}

//note: returns array of numbers.
function splitInHalf(x)
{
	var result = [];
	result.push( new number( x.positive,  x.value.substr(0, Math.ceil( x.value.length / 2 )) ));
	result.push( new number( x.positive,  x.value.substr( Math.ceil( x.value.length / 2 ), x.value.length -  Math.ceil(x.value.length / 2)) ));

	return result;
}

function getChars(x, char)
{
	var n = x;
	result = "";
	while(n > 0){
		result += char;
		n--;
	}
	return result;
}

//note: this needs to be more accurate for x >> y and x << y, or will cause rounding errors.
function longMultiply(x, y)
{
	var positive = x.positive == y.positive;
	return new number(positive , parseInt(x.value) * parseInt(y.value) );
}

number.prototype.timesTenToThe = function(x)
{
	var tempThis = this;
	tempThis.value += getChars(x, "0");
	return tempThis;
}

function karatsuba(x, y)
{
	x_length = x.value.length;
	y_length = y.value.length;

	if(x_length == 1 || y_length == 1)
	{
		return longMultiply(x, y);
	}

	var xSplit = splitInHalf(x);
	var ySplit = splitInHalf(y);

	var a = karatsuba(xSplit[0], ySplit[0]).timesTenToThe(2 * Math.floor(x_length / 2));
	var c = karatsuba(xSplit[1], ySplit[1]);
	var b = karatsuba(xSplit[0].plus(xSplit[1]).value, ySplit[0].plus(ySplit[1]).value).minus(a).minus(c).timesTenToThe(Math.floor(x_length / 2));

	return a.plus(c).plus(b);
}

<|endoftext|>


<commit_before>export {
  getFile, putFile
} from 'blockstack-storage'
<commit_msg>document storage methods we expose #223<commit_after>export {
  /**
   * Retrives the specified file from the app's data store.
   * @param {String} path - the path to the file to read
   * @returns {Promise} that resolves to the raw data in the file
   * or rejects with an error
   */
  getFile
} from 'blockstack-storage'

export {
  /**
   * Stores the data provided in the file specified.
   * @param {String} path - the path to store the data in
   * @param {String|Buffer} contents - the data to store in the file
   * @return {Promise} that resolves if the operation succeed and rejects
   * if it failed
   */
  putFile
} from 'blockstack-storage'
<|endoftext|>


<commit_before>/* @flow */

import { getOrSetLocalGaiaHubConnection, getFullReadUrl, GaiaHubConfig,
         connectToGaiaHub, uploadToGaiaHub, generateAppIndexFilePath,
         BLOCKSTACK_GAIA_HUB_LABEL, APP_INDEX_FILE_NAME } from './hub'

import { encryptECIES, decryptECIES } from '../encryption'
import { loadUserData } from '../auth'
import { getPublicKeyFromPrivate } from '../keys'
import { lookupProfile } from '../profiles'

const APP_INDEX_FILES_KEY = 'files'

/**
 * Retrieves the specified file from the app's data store.
 * @param {String} path - the path to the file to read
 * @param {Object} [options=null] - options object
 * @param {Boolean} [options.decrypt=false] - try to decrypt the data with the app private key
 * @param {String} options.user - the Blockstack ID to lookup for multi-player storage
 * @param {String} options.app - the app to lookup for multi-player storage
 * @param {String} [options.zoneFileLookupURL=http://localhost:6270/v1/names/] - the blockstack
 * core endpoint url to use for zonefile lookup
 * @returns {Promise} that resolves to the raw data in the file
 * or rejects with an error
 */
export function getFile(path: string, options?: {decrypt?: boolean, user?: string, app?: string,
  zoneFileLookupURL?: string}) {
  const defaults = {
    decrypt: false,
    user: null,
    app: null,
    zoneFileLookupURL: 'http://localhost:6270/v1/names/'
  }

  const opt = Object.assign({}, defaults, options)

  return getOrSetLocalGaiaHubConnection()
    .then((gaiaHubConfig) => {
      if (opt.user && opt.app) {
        return this.getUserAppFileUrl(path, opt.user, opt.app, opt.zoneFileLookupURL)
      } else {
        return getFullReadUrl(path, gaiaHubConfig)
      }
    })
    .then((readUrl) => new Promise((resolve, reject) => {
      if (!readUrl) {
        reject(null)
      } else {
        resolve(readUrl)
      }
    }))
    .then((readUrl) => {
      return fetch(readUrl)
    })
    .then((response) => {
      if (response.status !== 200) {
        if (response.status === 404) {
          console.log(`getFile ${path} returned 404, returning null`)
          return null
        } else {
          throw new Error(`getFile ${path} failed with HTTP status ${response.status}`)
        }
      }
      const contentType = response.headers.get('Content-Type')
      if (contentType === null || opt.decrypt ||
          contentType.startsWith('text') ||
          contentType === 'application/json') {
        return response.text()
      } else {
        return response.arrayBuffer()
      }
    })
    .then((storedContents) => {
      if (opt.decrypt && storedContents !== null) {
        const privateKey = loadUserData().appPrivateKey
        const cipherObject = JSON.parse(storedContents)
        return decryptECIES(privateKey, cipherObject)
      } else {
        return storedContents
      }
    })
}

/**
 * Stores the data provided in the app's data store to to the file specified.
 * @param {String} path - the path to store the data in
 * @param {String|Buffer} content - the data to store in the file
 * @param {Object} [options=null]- options object
 * @param {Boolean} [options.encrypt=false] - encrypt the data with the app private key
 * @param {Boolean} [options.public=false] - make the file discoverable by adding it
 * to the app index
 * @return {Promise} that resolves if the operation succeed and rejects
 * if it failed
 */
export function putFile(path: string, content: string | Buffer, options?: {encrypt?: boolean,
  public?: boolean}) {
  const defaults = {
    encrypt: false,
    public: false
  }

  const opt = Object.assign({}, defaults, options)

  let contentType = 'text/plain'
  if (typeof(content) !== 'string') {
    contentType = 'application/octet-stream'
  }
  if (opt.encrypt) {
    const privateKey = loadUserData().appPrivateKey
    const publicKey = getPublicKeyFromPrivate(privateKey)
    const cipherObject = encryptECIES(publicKey, content)
    content = JSON.stringify(cipherObject)
    contentType = 'application/json'
  }
  return getOrSetLocalGaiaHubConnection()
    .then((gaiaHubConfig) => uploadToGaiaHub(path, content, gaiaHubConfig, contentType))
    .then((publicUrl) => {
      if (opt.public) {
        return this.addToAppIndex(path, publicUrl)
      } else {
        return publicUrl
      }
    })
}

/**
 * Get the the read URL of a stored file
 * @param {String} path - the file path to retrieve read URL for
 * @returns {Promise} That resolves to the read URL of the specified file
 */
export function getFileReadUrl(path: string) {
  return getOrSetLocalGaiaHubConnection()
    .then((gaiaHubConfig) => getFullReadUrl(path, gaiaHubConfig))
}

/**
 * Adds the public URL of a file to the app index file under the `files` property
 * @param {String} path - the file path
 * @param {String} publicUrl - the public URL of the file
 * @returns {Promise} That resolves to the public URL of the specified file
 */
export function addToAppIndex(path: string, publicUrl: string) {
  return this.getAppIndexFile(path)
    .then((appIndexJSON) => {
      let newAppIndex = {}
      if (!appIndexJSON) {
        newAppIndex = { [APP_INDEX_FILES_KEY]: { [path]: publicUrl } }
      } else {
        const appIndex = JSON.parse(appIndexJSON)
        const newFiles = Object.assign({}, appIndex.files, { [path]: publicUrl })

        newAppIndex = appIndex
        newAppIndex[APP_INDEX_FILES_KEY] = newFiles
      }
      return this.putAppIndexFile(JSON.stringify(newAppIndex))
    })
    .then(() => publicUrl)
}

/**
 * Get the app index file URL
 * @param {String} gaiaHubUrl - the gaia hub url to generate index file url for
 * @param {String} appPrivateKey - the app private key used to generate the app address
 * @returns {Promise} That resolves to the URL of the app index file
 * or rejects if it fails
 */
export function getAppIndexFileUrl(gaiaHubUrl: string, appPrivateKey: string) {
  return generateAppIndexFilePath(gaiaHubUrl, appPrivateKey)
}

/**
 * Retrieves the app index file from the app's data store.
 * @returns {Promise} that resolves to the raw data in the file
 * or rejects with an error
 */
export function getAppIndexFile() {
  return this.getFile(APP_INDEX_FILE_NAME)
}

/**
 * Stores the app index file which enables multi-reader storage. This file
 * will be publicly visible in the user's profile data if the `appIndex`
 * scope was requested during authentication.
 * @param {String|Buffer} content - the data to store in the file
 * @return {Promise} that resolves if the operation succeed and rejects
 * if it failed
 */
export function putAppIndexFile(content: string | Buffer) {
  return this.putFile(APP_INDEX_FILE_NAME, content)
}

/**
 * Fetch the app index file for the specified user and app.
 * @param {String} name - The blockstack ID of the user to look up
 * @param {String} appOrigin - The app origin
 * @param {string} [zoneFileLookupURL=http://localhost:6270/v1/names/] The URL
 * to use for zonefile lookup
 * @return {Promise} that resolves to the raw data in the file
 * or rejects with an error
 */
export function getUserAppIndex(name: string, appOrigin: string, zoneFileLookupURL: string = 'http://localhost:6270/v1/names/') {
  return lookupProfile(name, zoneFileLookupURL)
    .then(profile => {
      if (profile.hasOwnProperty('apps')) {
        if (profile.apps.hasOwnProperty(appOrigin)) {
          const appIndexFileURL = profile.apps[appOrigin]
          return appIndexFileURL
        } else {
          return null
        }
      } else {
        return null
      }
    })
    .then((appIndexFileURL) => fetch(appIndexFileURL))
    .then((response) => {
      if (response.status !== 200) {
        if (response.status === 404) {
          console.log(`getUserAppIndex ${response.url} returned 404, returning null`)
          return null
        } else {
          throw new Error(`getUserAppIndex ${response.url} failed with status ${response.status}`)
        }
      }
      const contentType = response.headers.get('Content-Type')
      if (contentType === null ||
          contentType.startsWith('text') ||
          contentType === 'application/json') {
        return response.text()
      } else {
        return response.arrayBuffer()
      }
    })
}

/**
 * Fetch the public file read url for the specified user and app.
 * @param {String} path - the path to the file to read
 * @param {String} name - The blockstack ID of the user to look up
 * @param {String} appOrigin - The app origin
 * @param {string} [zoneFileLookupURL=http://localhost:6270/v1/names/] The URL
 * to use for zonefile lookup
 * @return {Promise} that resolves to the public read url of the file
 * or rejects with an error
 */
export function getUserAppFileUrl(path: string, name: string, appOrigin: string,
  zoneFileLookupURL: string = 'http://localhost:6270/v1/names/') {
  return getUserAppIndex(name, appOrigin, zoneFileLookupURL)
    .then((appIndexFileJSON) => {
      const appIndexFile = JSON.parse(appIndexFileJSON)
      if (appIndexFile.hasOwnProperty(APP_INDEX_FILES_KEY)) {
        return appIndexFile.files[path]
      } else {
        throw new Error(`getUserAppFileUrl ${path} app index does not contain files key`)
      }
    })
}

/**
 * Deletes the specified file from the app's data store.
 * @param {String} path - the path to the file to delete
 * @returns {Promise} that resolves when the file has been removed
 * or rejects with an error
 */
export function deleteFile(path: string) {
  throw new Error(`Delete of ${path} not supported by gaia hubs`)
}

export { connectToGaiaHub, uploadToGaiaHub, BLOCKSTACK_GAIA_HUB_LABEL, GaiaHubConfig }
<commit_msg>Fix lint.<commit_after>/* @flow */

import { getOrSetLocalGaiaHubConnection, getFullReadUrl, GaiaHubConfig,
         connectToGaiaHub, uploadToGaiaHub, generateAppIndexFilePath,
         BLOCKSTACK_GAIA_HUB_LABEL, APP_INDEX_FILE_NAME } from './hub'

import { encryptECIES, decryptECIES } from '../encryption'
import { loadUserData } from '../auth'
import { getPublicKeyFromPrivate } from '../keys'
import { lookupProfile } from '../profiles'

const APP_INDEX_FILES_KEY = 'files'

/**
 * Retrieves the specified file from the app's data store.
 * @param {String} path - the path to the file to read
 * @param {Object} [options=null] - options object
 * @param {Boolean} [options.decrypt=false] - try to decrypt the data with the app private key
 * @param {String} options.user - the Blockstack ID to lookup for multi-player storage
 * @param {String} options.app - the app to lookup for multi-player storage
 * @param {String} [options.zoneFileLookupURL=http://localhost:6270/v1/names/] - the blockstack
 * core endpoint url to use for zonefile lookup
 * @returns {Promise} that resolves to the raw data in the file
 * or rejects with an error
 */
export function getFile(path: string, options?: {decrypt?: boolean, user?: string, app?: string,
  zoneFileLookupURL?: string}) {
  const defaults = {
    decrypt: false,
    user: null,
    app: null,
    zoneFileLookupURL: 'http://localhost:6270/v1/names/'
  }

  const opt = Object.assign({}, defaults, options)

  return getOrSetLocalGaiaHubConnection()
    .then((gaiaHubConfig) => {
      if (opt.user && opt.app) {
        return this.getUserAppFileUrl(path, opt.user, opt.app, opt.zoneFileLookupURL)
      } else {
        return getFullReadUrl(path, gaiaHubConfig)
      }
    })
    .then((readUrl) => new Promise((resolve, reject) => {
      if (!readUrl) {
        reject(null)
      } else {
        resolve(readUrl)
      }
    }))
    .then((readUrl) => fetch(readUrl))
    .then((response) => {
      if (response.status !== 200) {
        if (response.status === 404) {
          console.log(`getFile ${path} returned 404, returning null`)
          return null
        } else {
          throw new Error(`getFile ${path} failed with HTTP status ${response.status}`)
        }
      }
      const contentType = response.headers.get('Content-Type')
      if (contentType === null || opt.decrypt ||
          contentType.startsWith('text') ||
          contentType === 'application/json') {
        return response.text()
      } else {
        return response.arrayBuffer()
      }
    })
    .then((storedContents) => {
      if (opt.decrypt && storedContents !== null) {
        const privateKey = loadUserData().appPrivateKey
        const cipherObject = JSON.parse(storedContents)
        return decryptECIES(privateKey, cipherObject)
      } else {
        return storedContents
      }
    })
}

/**
 * Stores the data provided in the app's data store to to the file specified.
 * @param {String} path - the path to store the data in
 * @param {String|Buffer} content - the data to store in the file
 * @param {Object} [options=null]- options object
 * @param {Boolean} [options.encrypt=false] - encrypt the data with the app private key
 * @param {Boolean} [options.public=false] - make the file discoverable by adding it
 * to the app index
 * @return {Promise} that resolves if the operation succeed and rejects
 * if it failed
 */
export function putFile(path: string, content: string | Buffer, options?: {encrypt?: boolean,
  public?: boolean}) {
  const defaults = {
    encrypt: false,
    public: false
  }

  const opt = Object.assign({}, defaults, options)

  let contentType = 'text/plain'
  if (typeof(content) !== 'string') {
    contentType = 'application/octet-stream'
  }
  if (opt.encrypt) {
    const privateKey = loadUserData().appPrivateKey
    const publicKey = getPublicKeyFromPrivate(privateKey)
    const cipherObject = encryptECIES(publicKey, content)
    content = JSON.stringify(cipherObject)
    contentType = 'application/json'
  }
  return getOrSetLocalGaiaHubConnection()
    .then((gaiaHubConfig) => uploadToGaiaHub(path, content, gaiaHubConfig, contentType))
    .then((publicUrl) => {
      if (opt.public) {
        return this.addToAppIndex(path, publicUrl)
      } else {
        return publicUrl
      }
    })
}

/**
 * Get the the read URL of a stored file
 * @param {String} path - the file path to retrieve read URL for
 * @returns {Promise} That resolves to the read URL of the specified file
 */
export function getFileReadUrl(path: string) {
  return getOrSetLocalGaiaHubConnection()
    .then((gaiaHubConfig) => getFullReadUrl(path, gaiaHubConfig))
}

/**
 * Adds the public URL of a file to the app index file under the `files` property
 * @param {String} path - the file path
 * @param {String} publicUrl - the public URL of the file
 * @returns {Promise} That resolves to the public URL of the specified file
 */
export function addToAppIndex(path: string, publicUrl: string) {
  return this.getAppIndexFile(path)
    .then((appIndexJSON) => {
      let newAppIndex = {}
      if (!appIndexJSON) {
        newAppIndex = { [APP_INDEX_FILES_KEY]: { [path]: publicUrl } }
      } else {
        const appIndex = JSON.parse(appIndexJSON)
        const newFiles = Object.assign({}, appIndex.files, { [path]: publicUrl })

        newAppIndex = appIndex
        newAppIndex[APP_INDEX_FILES_KEY] = newFiles
      }
      return this.putAppIndexFile(JSON.stringify(newAppIndex))
    })
    .then(() => publicUrl)
}

/**
 * Get the app index file URL
 * @param {String} gaiaHubUrl - the gaia hub url to generate index file url for
 * @param {String} appPrivateKey - the app private key used to generate the app address
 * @returns {Promise} That resolves to the URL of the app index file
 * or rejects if it fails
 */
export function getAppIndexFileUrl(gaiaHubUrl: string, appPrivateKey: string) {
  return generateAppIndexFilePath(gaiaHubUrl, appPrivateKey)
}

/**
 * Retrieves the app index file from the app's data store.
 * @returns {Promise} that resolves to the raw data in the file
 * or rejects with an error
 */
export function getAppIndexFile() {
  return this.getFile(APP_INDEX_FILE_NAME)
}

/**
 * Stores the app index file which enables multi-reader storage. This file
 * will be publicly visible in the user's profile data if the `appIndex`
 * scope was requested during authentication.
 * @param {String|Buffer} content - the data to store in the file
 * @return {Promise} that resolves if the operation succeed and rejects
 * if it failed
 */
export function putAppIndexFile(content: string | Buffer) {
  return this.putFile(APP_INDEX_FILE_NAME, content)
}

/**
 * Fetch the app index file for the specified user and app.
 * @param {String} name - The blockstack ID of the user to look up
 * @param {String} appOrigin - The app origin
 * @param {string} [zoneFileLookupURL=http://localhost:6270/v1/names/] The URL
 * to use for zonefile lookup
 * @return {Promise} that resolves to the raw data in the file
 * or rejects with an error
 */
export function getUserAppIndex(name: string, appOrigin: string, zoneFileLookupURL: string = 'http://localhost:6270/v1/names/') {
  return lookupProfile(name, zoneFileLookupURL)
    .then(profile => {
      if (profile.hasOwnProperty('apps')) {
        if (profile.apps.hasOwnProperty(appOrigin)) {
          const appIndexFileURL = profile.apps[appOrigin]
          return appIndexFileURL
        } else {
          return null
        }
      } else {
        return null
      }
    })
    .then((appIndexFileURL) => fetch(appIndexFileURL))
    .then((response) => {
      if (response.status !== 200) {
        if (response.status === 404) {
          console.log(`getUserAppIndex ${response.url} returned 404, returning null`)
          return null
        } else {
          throw new Error(`getUserAppIndex ${response.url} failed with status ${response.status}`)
        }
      }
      const contentType = response.headers.get('Content-Type')
      if (contentType === null ||
          contentType.startsWith('text') ||
          contentType === 'application/json') {
        return response.text()
      } else {
        return response.arrayBuffer()
      }
    })
}

/**
 * Fetch the public file read url for the specified user and app.
 * @param {String} path - the path to the file to read
 * @param {String} name - The blockstack ID of the user to look up
 * @param {String} appOrigin - The app origin
 * @param {string} [zoneFileLookupURL=http://localhost:6270/v1/names/] The URL
 * to use for zonefile lookup
 * @return {Promise} that resolves to the public read url of the file
 * or rejects with an error
 */
export function getUserAppFileUrl(path: string, name: string, appOrigin: string,
  zoneFileLookupURL: string = 'http://localhost:6270/v1/names/') {
  return getUserAppIndex(name, appOrigin, zoneFileLookupURL)
    .then((appIndexFileJSON) => {
      const appIndexFile = JSON.parse(appIndexFileJSON)
      if (appIndexFile.hasOwnProperty(APP_INDEX_FILES_KEY)) {
        return appIndexFile.files[path]
      } else {
        throw new Error(`getUserAppFileUrl ${path} app index does not contain files key`)
      }
    })
}

/**
 * Deletes the specified file from the app's data store.
 * @param {String} path - the path to the file to delete
 * @returns {Promise} that resolves when the file has been removed
 * or rejects with an error
 */
export function deleteFile(path: string) {
  throw new Error(`Delete of ${path} not supported by gaia hubs`)
}

export { connectToGaiaHub, uploadToGaiaHub, BLOCKSTACK_GAIA_HUB_LABEL, GaiaHubConfig }
<|endoftext|>


<commit_before>import store from './index.js';
import getToolForElement from './getToolForElement.js';

/**
 * Adds a tool to an enabled element.
 *
 * @export @public @method
 * @name addToolForElement
 * @param {HTMLElement} element The element to add the tool to.
 * @param {import('./../base/BaseTool').BaseTool} apiTool The tool to add to the element.
 * @param {Object} [configuration] Override the default tool configuration
 */
const addToolForElement = function (element, apiTool, configuration) {
  // Instantiating the tool here makes it harder to accidentally add
  // The same tool (by reference) for multiple elements (which would reassign the tool
  // To a new element).
  const tool = new apiTool(configuration.name);

  const toolAlreadyAddedToElement = getToolForElement(element, tool.name);

  if (toolAlreadyAddedToElement) {
    console.warn(`${tool.name} has already been added to the target element`);

    return;
  }

  tool.element = element;
  store.state.tools.push(tool);
};

/**
 * Adds a tool to all enabled element.
 *
 * @export @public @method
 * @name addTool
 * @param {import('./../base/BaseTool').BaseTool} apiTool The tool to add to each element.
 * @param {object} [configuration] Override the default tool configuration
 */
const addTool = function (apiTool, configuration) {
  _addToolGlobally(apiTool, configuration);
  store.state.enabledElements.forEach((element) => {
    addToolForElement(element, apiTool, configuration);
  });
};

/**
 * Adds tool with matching name from globally registered tools.
 * Requires `globalToolSyncEnabled` to be set to true
 *
 * @private @method
 * @name addToolGlobally
 * @param {import('./../base/BaseTool').BaseTool} apiTool
 * @param {object} [configuration] Override the default tool configuration
 */
const _addToolGlobally = function (apiTool, configuration) {
  if (!store.modules.globalConfiguration.state.globalToolSyncEnabled) {
    return;
  }

  const tool = new apiTool(configuration.name);
  const toolAlreadyAddedGlobally =
    store.state.globalTools[tool.name] !== undefined;

  if (toolAlreadyAddedGlobally) {
    console.warn(`${tool.name} has already been added globally`);

    return;
  }

  store.state.globalTools[tool.name] = {
    tool: apiTool,
    configuration
  };
};

export { addTool, addToolForElement };
<commit_msg>Pass in entire configuration object; not just name<commit_after>import store from './index.js';
import getToolForElement from './getToolForElement.js';

/**
 * Adds a tool to an enabled element.
 *
 * @export @public @method
 * @name addToolForElement
 * @param {HTMLElement} element The element to add the tool to.
 * @param {import('./../base/BaseTool').BaseTool} apiTool The tool to add to the element.
 * @param {Object} [configuration] Override the default tool configuration
 */
const addToolForElement = function (element, apiTool, configuration) {
  // Instantiating the tool here makes it harder to accidentally add
  // The same tool (by reference) for multiple elements (which would reassign the tool
  // To a new element).
  const tool = new apiTool(configuration);
  const toolAlreadyAddedToElement = getToolForElement(element, tool.name);

  if (toolAlreadyAddedToElement) {
    console.warn(`${tool.name} has already been added to the target element`);

    return;
  }

  tool.element = element;
  store.state.tools.push(tool);
};

/**
 * Adds a tool to all enabled element.
 *
 * @export @public @method
 * @name addTool
 * @param {import('./../base/BaseTool').BaseTool} apiTool The tool to add to each element.
 * @param {object} [configuration] Override the default tool configuration
 */
const addTool = function (apiTool, configuration) {
  _addToolGlobally(apiTool, configuration);
  store.state.enabledElements.forEach((element) => {
    addToolForElement(element, apiTool, configuration);
  });
};

/**
 * Adds tool with matching name from globally registered tools.
 * Requires `globalToolSyncEnabled` to be set to true
 *
 * @private @method
 * @name addToolGlobally
 * @param {import('./../base/BaseTool').BaseTool} apiTool
 * @param {object} [configuration] Override the default tool configuration
 */
const _addToolGlobally = function (apiTool, configuration) {
  if (!store.modules.globalConfiguration.state.globalToolSyncEnabled) {
    return;
  }

  const tool = new apiTool(configuration);
  const toolAlreadyAddedGlobally =
    store.state.globalTools[tool.name] !== undefined;

  if (toolAlreadyAddedGlobally) {
    console.warn(`${tool.name} has already been added globally`);

    return;
  }

  store.state.globalTools[tool.name] = {
    tool: apiTool,
    configuration
  };
};

export { addTool, addToolForElement };
<|endoftext|>


<commit_before>// @flow
import React from 'react'

import { storiesOf } from '@storybook/react'
import { action } from '@storybook/addon-actions'
import { linkTo } from '@storybook/addon-links'

import { muiTheme } from 'storybook-addon-material-ui'

import '../injectGlobal'

import ButtonBar from '../components/BottomBar'
import LoadingIndicator from '../components/LoadingIndicator'
import StoryCell from '../components/StoryCell'
import type {
  Screen,
  Story,
  Article,
  ArticleById,
  Blog,
  BlogById,
  System,
} from '../types'

const story: Story = {
  id: 1,
  title: '櫻子｢名探偵櫻子ちゃんだ！｣',
  tagList: ['ゆるゆり'],
  firstPostedAt: '2016-02-16T07:55:00.000Z',
  articles: [75603, 97311],
}
const articles: ArticleById = {
  '75603': {
    id: 75603,
    url: 'http://ayamevip.com/archives/46843668.html',
    postedAt: '2016-02-16T07:55:00.000Z',
    blog: 3,
  },
  '97311': {
    id: 97311,
    url: 'http://blog.livedoor.jp/mode_ss/archives/52009474.html',
    postedAt: '2016-02-17T16:00:00.000Z',
    blog: 5,
  },
}

const blogs: BlogById = {
  '3': {
    id: 3,
    title: 'あやめ速報',
  },
  '5': {
    id: 5,
    title: 'えすえすmode',
  },
}

storiesOf('StoryCell', module)
  .addDecorator(muiTheme())
  .add('normal', () => (
    <StoryCell story={story} articles={articles} blogs={blogs} />
  ))

const screens: Screen[] = [
  {
    id: 1,
    type: 'new',
    page: 1,
    loaded: false,
  },
  {
    id: 2,
    type: 'search',
    tag: 'ゆるゆり',
    q: '京子',
    page: 1,
    loaded: false,
  },
]

const system: System = {
  loaded: 0,
  selectedTab: 1,
}

storiesOf('ButtonBar', module)
  .addDecorator(muiTheme())
  .add('normal', () => (
    <ButtonBar
      screens={screens}
      switchTab={id => {
        console.log(`switch to ${id}`)
      }}
      system={system}
    />
  ))

storiesOf('LoadingIndicator', module)
  .addDecorator(muiTheme())
  .add('normal', () => <LoadingIndicator />)
<commit_msg>Storybook Phone frame<commit_after>// @flow
import React from 'react'
import styled from 'styled-components'

import { storiesOf } from '@storybook/react'
import { action } from '@storybook/addon-actions'
import { linkTo } from '@storybook/addon-links'

import { muiTheme } from 'storybook-addon-material-ui'

import '../injectGlobal'

import ButtonBar from '../components/BottomBar'
import LoadingIndicator from '../components/LoadingIndicator'
import StoryCell from '../components/StoryCell'
import type {
  Screen,
  Story,
  Article,
  ArticleById,
  Blog,
  BlogById,
  System,
} from '../types'

const story: Story = {
  id: 1,
  title: '櫻子｢名探偵櫻子ちゃんだ！｣',
  tagList: ['ゆるゆり'],
  firstPostedAt: '2016-02-16T07:55:00.000Z',
  articles: [75603, 97311],
}
const articles: ArticleById = {
  '75603': {
    id: 75603,
    url: 'http://ayamevip.com/archives/46843668.html',
    postedAt: '2016-02-16T07:55:00.000Z',
    blog: 3,
  },
  '97311': {
    id: 97311,
    url: 'http://blog.livedoor.jp/mode_ss/archives/52009474.html',
    postedAt: '2016-02-17T16:00:00.000Z',
    blog: 5,
  },
}

const blogs: BlogById = {
  '3': {
    id: 3,
    title: 'あやめ速報',
  },
  '5': {
    id: 5,
    title: 'えすえすmode',
  },
}

const PhoneWrap = styled.div`
  width: 375px;
  height: 667px;
  margin: 10px;
  box-shadow: 0px 0px 10px 0px;
`

storiesOf('StoryCell', module)
  .addDecorator(muiTheme())
  .add('normal', () => (
    <PhoneWrap>
      <StoryCell story={story} articles={articles} blogs={blogs} />
    </PhoneWrap>
  ))

const screens: Screen[] = [
  {
    id: 1,
    type: 'new',
    page: 1,
    loaded: false,
  },
  {
    id: 2,
    type: 'search',
    tag: 'ゆるゆり',
    q: '京子',
    page: 1,
    loaded: false,
  },
]

const system: System = {
  loaded: 0,
  selectedTab: 1,
}

storiesOf('ButtonBar', module)
  .addDecorator(muiTheme())
  .add('normal', () => (
    <PhoneWrap>
      <ButtonBar
        screens={screens}
        switchTab={id => {
          console.log(`switch to ${id}`)
        }}
        system={system}
      />
    </PhoneWrap>
  ))

storiesOf('LoadingIndicator', module)
  .addDecorator(muiTheme())
  .add('normal', () => (
    <PhoneWrap>
      <LoadingIndicator />
    </PhoneWrap>
  ))
<|endoftext|>


<commit_before>function notNull(x)  { return x != null; }
function wrap(x)     { return '(' + x + ')';}

function maybeQuote(value) {
    if (typeof value === 'string') {
        return '"' + value + '"';
    }
    return value;
}

function lookUp(key) {
    if (key[0] === '$') {
        // keys prefixed with $ are special properties in the context object (not feature properties)
        return 'context[\'' + key.substring(1) + '\']';
    }
    else if (key.indexOf('.') > -1) {
        // dot notation indicates a nested feature property
        return `context.feature.properties${key.split('.').map(k => '[\'' + k + '\']').join('')}`;
    }
    // single-level feature property
    return 'context.feature.properties[\'' + key + '\']';
}

function nullValue(/*key, value*/) {
    return ' true ';
}

function propertyEqual(key, value) {
    return wrap(maybeQuote(value) + ' === ' + lookUp(key));
}

function propertyOr(key, values) {
    const arr = '[' + values.map(maybeQuote).join(',') + ']';
    return wrap(`${arr}.indexOf(${lookUp(key)}) > -1`);
}

function printNested(values, joiner) {
    return wrap(values.filter(notNull).map(function (x) {
        return wrap(x.join(' && '));
    }).join(' ' + joiner + ' '));
}

function any(_, values, options) {
    return (values && values.length > 0) ? printNested(values.map(function(v) { return parseFilter(v, options); }), '||') : 'true';
}

function all(_, values, options) {
    return (values && values.length > 0) ? printNested(values.map(function(v) { return parseFilter(v, options); }), '&&') : 'true';
}

function not(key, value, options) {
    return '!' + wrap(parseFilter(value, options).join(' && '));
}

function none(key, values, options) {
    return '!' + wrap(any(null, values, options));
}

function propertyMatchesBoolean(key, value) {
    return wrap(lookUp(key) + (value ? ' != ' : ' == ')  + 'null');
}

function rangeMatch(key, values, options) {
    var expressions = [];
    var transform = options && (typeof options.rangeTransform === 'function') && options.rangeTransform;

    if (values.max) {
        var max = transform ? transform(values.max) : values.max;
        expressions.push('' + lookUp(key) + ' < ' + max);
    }

    if (values.min) {
        var min = transform ? min = transform(values.min) : values.min;
        expressions.push('' + lookUp(key) + ' >= ' + min);
    }

    return wrap(expressions.join(' && '));
}

function parseFilter(filter, options) {
    var filterAST = [];

    // Function filter
    if (typeof filter === 'function') {
        return [wrap(filter.toString() + '(context)')];
    }
    // Array filter, implicit 'any'
    else if (Array.isArray(filter)) {
        return [any(null, filter, options)];
    }
    // Null filter object
    else if (filter == null) {
        return ['true'];
    }

    // Object filter, e.g. implicit 'all'
    var keys = Object.keys(filter);
    for (var k=0; k < keys.length; k++) {
        var key = keys[k];

        var value = filter[key],
            type  = typeof value;
        if (type === 'string' || type === 'number') {
            filterAST.push(propertyEqual(key, value));
        } else if (type === 'boolean') {
            filterAST.push(propertyMatchesBoolean(key, value));
        } else if (key === 'not') {
            filterAST.push(not(key, value, options));
        } else if (key === 'any') {
            filterAST.push(any(key, value, options));
        } else if (key === 'all') {
            filterAST.push(all(key, value, options));
        } else if (key === 'none') {
            filterAST.push(none(key, value, options));
        } else if (Array.isArray(value)) {
            filterAST.push(propertyOr(key, value));
        } else if (type === 'object' && value != null) {
            if (value.max || value.min) {
                filterAST.push(rangeMatch(key, value, options));
            }
        } else if (value == null) {
            filterAST.push(nullValue(key, value));
        } else {
            throw new Error('Unknown Query syntax: ' + value);
        }
    }

    return keys.length === 0 ? ['true'] : filterAST;
}

function filterToString(filterAST) {
    return wrap(filterAST.join(' && '));
}

export function buildFilter(filter, options) {
    if (filter == null) { return function () { return true; }; }
    // jshint evil: true
    return new Function('context', 'return ' + filterToString(parseFilter(filter, options)) + ';');
}
<commit_msg>use '\.' to escape dot notation on feature properties in filters (unescaped '.' will be interpreted as a nested feature property)<commit_after>function notNull(x)  { return x != null; }
function wrap(x)     { return '(' + x + ')';}

function maybeQuote(value) {
    if (typeof value === 'string') {
        return '"' + value + '"';
    }
    return value;
}

function lookUp(key) {
    if (key[0] === '$') {
        // keys prefixed with $ are special properties in the context object (not feature properties)
        return 'context[\'' + key.substring(1) + '\']';
    }
    else if (key.indexOf('.') > -1) {
        if (key.indexOf('\\.') === -1) { // no escaped dot notation
            // un-escaped dot notation indicates a nested feature property
            return `context.feature.properties${key.split('.').map(k => '[\'' + k + '\']').join('')}`;
        }
        else { // mixed escaped/unescaped dot notation
            // escaped dot notation will be interpreted as a single-level feature property with dots in the name
            // this splits on unescaped dots, which requires a temporary swap of escaped and unescaped dots
            let keys = key
                .replace(/\\\./g, '__TANGRAM_SEPARATOR__')
                .split('.')
                .map(s => s.replace(/__TANGRAM_SEPARATOR__/g, '.'));
            return `context.feature.properties${keys.map(k => '[\'' + k + '\']').join('')}`;
        }
    }
    // single-level feature property
    return 'context.feature.properties[\'' + key + '\']';
}

function nullValue(/*key, value*/) {
    return ' true ';
}

function propertyEqual(key, value) {
    return wrap(maybeQuote(value) + ' === ' + lookUp(key));
}

function propertyOr(key, values) {
    const arr = '[' + values.map(maybeQuote).join(',') + ']';
    return wrap(`${arr}.indexOf(${lookUp(key)}) > -1`);
}

function printNested(values, joiner) {
    return wrap(values.filter(notNull).map(function (x) {
        return wrap(x.join(' && '));
    }).join(' ' + joiner + ' '));
}

function any(_, values, options) {
    return (values && values.length > 0) ? printNested(values.map(function(v) { return parseFilter(v, options); }), '||') : 'true';
}

function all(_, values, options) {
    return (values && values.length > 0) ? printNested(values.map(function(v) { return parseFilter(v, options); }), '&&') : 'true';
}

function not(key, value, options) {
    return '!' + wrap(parseFilter(value, options).join(' && '));
}

function none(key, values, options) {
    return '!' + wrap(any(null, values, options));
}

function propertyMatchesBoolean(key, value) {
    return wrap(lookUp(key) + (value ? ' != ' : ' == ')  + 'null');
}

function rangeMatch(key, values, options) {
    var expressions = [];
    var transform = options && (typeof options.rangeTransform === 'function') && options.rangeTransform;

    if (values.max) {
        var max = transform ? transform(values.max) : values.max;
        expressions.push('' + lookUp(key) + ' < ' + max);
    }

    if (values.min) {
        var min = transform ? min = transform(values.min) : values.min;
        expressions.push('' + lookUp(key) + ' >= ' + min);
    }

    return wrap(expressions.join(' && '));
}

function parseFilter(filter, options) {
    var filterAST = [];

    // Function filter
    if (typeof filter === 'function') {
        return [wrap(filter.toString() + '(context)')];
    }
    // Array filter, implicit 'any'
    else if (Array.isArray(filter)) {
        return [any(null, filter, options)];
    }
    // Null filter object
    else if (filter == null) {
        return ['true'];
    }

    // Object filter, e.g. implicit 'all'
    var keys = Object.keys(filter);
    for (var k=0; k < keys.length; k++) {
        var key = keys[k];

        var value = filter[key],
            type  = typeof value;
        if (type === 'string' || type === 'number') {
            filterAST.push(propertyEqual(key, value));
        } else if (type === 'boolean') {
            filterAST.push(propertyMatchesBoolean(key, value));
        } else if (key === 'not') {
            filterAST.push(not(key, value, options));
        } else if (key === 'any') {
            filterAST.push(any(key, value, options));
        } else if (key === 'all') {
            filterAST.push(all(key, value, options));
        } else if (key === 'none') {
            filterAST.push(none(key, value, options));
        } else if (Array.isArray(value)) {
            filterAST.push(propertyOr(key, value));
        } else if (type === 'object' && value != null) {
            if (value.max || value.min) {
                filterAST.push(rangeMatch(key, value, options));
            }
        } else if (value == null) {
            filterAST.push(nullValue(key, value));
        } else {
            throw new Error('Unknown Query syntax: ' + value);
        }
    }

    return keys.length === 0 ? ['true'] : filterAST;
}

function filterToString(filterAST) {
    return wrap(filterAST.join(' && '));
}

export function buildFilter(filter, options) {
    if (filter == null) { return function () { return true; }; }
    // jshint evil: true
    return new Function('context', 'return ' + filterToString(parseFilter(filter, options)) + ';');
}
<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('path');
var semver = require('semver');

var name = 'remotedev-server';
var startFlag = '/* ' + name + ' start */';
var endFlag = '/* ' + name + ' end */';
var serverFlags = {
  'react-native': {
    '0.0.1': '    _server(argv, config, resolve, reject);',
    '0.31.0': "  runServer(args, config, () => console.log('\\nReact packager ready.\\n'));"
  },
  'react-native-desktop': {
    '0.0.1': '    _server(argv, config, resolve, reject);'
  }
};

function getModuleVersion(modulePath) {
  return JSON.parse(
    fs.readFileSync(
      path.join(modulePath, 'package.json'),
      'utf-8'
    )
  ).version;
}

function getServerFlag(moduleName, version) {
  var flags = serverFlags[moduleName || 'react-native'];
  var versions = Object.keys(flags);
  var flag;
  for (var i = 0; i < versions.length; i++) {
    if (semver.gte(version, versions[i])) {
      flag = flags[versions[i]];
    }
  }
  return flag;
}

exports.dir = 'local-cli/server';
exports.file = 'server.js';
exports.fullPath = path.join(exports.dir, exports.file);

exports.inject = function(modulePath, options, moduleName) {
  var filePath = path.join(modulePath, exports.fullPath);
  if (!fs.existsSync(filePath)) return false;

  var serverFlag = getServerFlag(
    moduleName,
    getModuleVersion(modulePath)
  );
  var code = [
    startFlag,
    '    require("' + name + '")(' + JSON.stringify(options) + ')',
    '      .then(_remotedev =>',
    '        _remotedev.on("ready", () => {',
    '          if (!_remotedev.portAlreadyUsed) console.log("-".repeat(80));',
    '      ' + serverFlag,
    '        })',
    '      );',
    endFlag,
  ].join('\n');

  var serverCode = fs.readFileSync(filePath, 'utf-8');
  var start = serverCode.indexOf(startFlag);  // already injected ?
  var end = serverCode.indexOf(endFlag) + endFlag.length;
  if (start === -1) {
    start = serverCode.indexOf(serverFlag);
    end = start + serverFlag.length;
  }
  fs.writeFileSync(
    filePath,
    serverCode.substr(0, start) + code + serverCode.substr(end, serverCode.length)
  );
  return true;
};

exports.revert = function(modulePath, moduleName) {
  var filePath = path.join(modulePath, exports.fullPath);
  if (!fs.existsSync(filePath)) return false;

  var serverFlag = getServerFlag(
    moduleName,
    getModuleVersion(modulePath)
  );
  var serverCode = fs.readFileSync(filePath, 'utf-8');
  var start = serverCode.indexOf(startFlag); // already injected ?
  var end = serverCode.indexOf(endFlag) + endFlag.length;
  if (start !== -1) {
    fs.writeFileSync(
      filePath,
      serverCode.substr(0, start) + serverFlag + serverCode.substr(end, serverCode.length)
    );
  }
  return true;
};<commit_msg>Support RN >= 0.44.0-rc.0 for injectserver (#38)<commit_after>var fs = require('fs');
var path = require('path');
var semver = require('semver');

var name = 'remotedev-server';
var startFlag = '/* ' + name + ' start */';
var endFlag = '/* ' + name + ' end */';
var serverFlags = {
  'react-native': {
    '0.0.1': '    _server(argv, config, resolve, reject);',
    '0.31.0': "  runServer(args, config, () => console.log('\\nReact packager ready.\\n'));",
    '0.44.0-rc.0': '  runServer(args, config, startedCallback, readyCallback);'
  },
  'react-native-desktop': {
    '0.0.1': '    _server(argv, config, resolve, reject);'
  }
};

function getModuleVersion(modulePath) {
  return JSON.parse(
    fs.readFileSync(
      path.join(modulePath, 'package.json'),
      'utf-8'
    )
  ).version;
}

function getServerFlag(moduleName, version) {
  var flags = serverFlags[moduleName || 'react-native'];
  var versions = Object.keys(flags);
  var flag;
  for (var i = 0; i < versions.length; i++) {
    if (semver.gte(version, versions[i])) {
      flag = flags[versions[i]];
    }
  }
  return flag;
}

exports.dir = 'local-cli/server';
exports.file = 'server.js';
exports.fullPath = path.join(exports.dir, exports.file);

exports.inject = function(modulePath, options, moduleName) {
  var filePath = path.join(modulePath, exports.fullPath);
  if (!fs.existsSync(filePath)) return false;

  var serverFlag = getServerFlag(
    moduleName,
    getModuleVersion(modulePath)
  );
  var code = [
    startFlag,
    '    require("' + name + '")(' + JSON.stringify(options) + ')',
    '      .then(_remotedev =>',
    '        _remotedev.on("ready", () => {',
    '          if (!_remotedev.portAlreadyUsed) console.log("-".repeat(80));',
    '      ' + serverFlag,
    '        })',
    '      );',
    endFlag,
  ].join('\n');

  var serverCode = fs.readFileSync(filePath, 'utf-8');
  var start = serverCode.indexOf(startFlag);  // already injected ?
  var end = serverCode.indexOf(endFlag) + endFlag.length;
  if (start === -1) {
    start = serverCode.indexOf(serverFlag);
    end = start + serverFlag.length;
  }
  fs.writeFileSync(
    filePath,
    serverCode.substr(0, start) + code + serverCode.substr(end, serverCode.length)
  );
  return true;
};

exports.revert = function(modulePath, moduleName) {
  var filePath = path.join(modulePath, exports.fullPath);
  if (!fs.existsSync(filePath)) return false;

  var serverFlag = getServerFlag(
    moduleName,
    getModuleVersion(modulePath)
  );
  var serverCode = fs.readFileSync(filePath, 'utf-8');
  var start = serverCode.indexOf(startFlag); // already injected ?
  var end = serverCode.indexOf(endFlag) + endFlag.length;
  if (start !== -1) {
    fs.writeFileSync(
      filePath,
      serverCode.substr(0, start) + serverFlag + serverCode.substr(end, serverCode.length)
    );
  }
  return true;
};
<|endoftext|>


<commit_before>'use strict'
const slapp = require('../../slackSetup.js').slapp
const getRadius = require('../../radius.js').getRadius
const tinyurl = require('tinyurl');
const routeFuncs = require('./routesIndex.js')

//db imports
const firebase    = require('../../firebaseSetup.js'),
      db = firebase.db,
      snaps = firebase.snaps,
      tags = firebase.tags,
      users = firebase.users

let attachmentArr = []

let callback = function(picUrl,snap,i){
  let snapAttach={
    title: `${snap.description}`,
    image_url: `${picUrl}`,
    text: `${snap.address}`,
    footer:`Jaywalk`
  }
  attachmentArr.push(snapAttach)
  if(i==4){
    msg.respont({
        text: '',
        attachments:[{
          color: 'warning',
          callback_id: "relaventAsk_callback"
          author_name:"Jaywalk",
          author_link:"http://www.jaywalk.me",
          fields: attachmentArr
        }]
    }) //end msg.say
    .route('relaventAsk', (msg,state),60)
  }
}

function snapsByGeo (lat,lng, msg, state){
    //firebase search by snap lat (start at bottom of circle, end at top)
    let radius = getRadius(lat,lng)
    let resultArr = []
    let snapLat = snaps
      .orderByChild('lat')
      .startAt(radius[5].lat + "-") // "-"makes a string: required for query
      .endAt(radius[1].lat + "-")
      .once('value')
      .then(function(snapObj) {
        let count = 0
        snapObj.forEach(function(data) {
          let snap = data.val()
          //if returns lng within radius (east/west)
          if (snap.lng <= radius[0].lng && snap.lng >= radius[3].lng) {
            count ++
            resultArr.push(snap)
          } //end if (lng checker)
        }) //end foreach
        let len = (resultArr.length-1)
        for(let i=len;i>(len-4);i--){ //last four
          let snap = resultArr[i]

          tinyurl.shorten(snap.picture, function(res) {
            callback(res,snap,i)
          })
        } //end for

      }) //end .then(snap)
} // end snapsByGeo()

module.exports = {
  snapsByGeo: snapsByGeo
}

let snapAttach={
  title: `${snap.description}`,
  image_url: `${picUrl}`,
  text: `${snap.address}`,
  footer:`Jaywalk`
}
<commit_msg>dressing up snap response again<commit_after>'use strict'
const slapp = require('../../slackSetup.js').slapp
const getRadius = require('../../radius.js').getRadius
const tinyurl = require('tinyurl');
const routeFuncs = require('./routesIndex.js')

//db imports
const firebase    = require('../../firebaseSetup.js'),
      db = firebase.db,
      snaps = firebase.snaps,
      tags = firebase.tags,
      users = firebase.users

let attachmentArr = []

let callback = function(picUrl,snap,i){
  let snapAttach={
    title: `${snap.description}`,
    image_url: `${picUrl}`,
    text: `${snap.address}`,
    footer:`Jaywalk`
  }
  attachmentArr.push(snapAttach)
  if(i==4){
    msg.respont({
        text: '',
        attachments:[{
          color: 'warning',
          callback_id: "relaventAsk_callback",
          author_name:"Jaywalk",
          author_link:"http://www.jaywalk.me",
          fields: attachmentArr
        }]
    }) //end msg.say
    .route('relaventAsk', (msg,state),60)
  }
}

function snapsByGeo (lat,lng, msg, state){
    //firebase search by snap lat (start at bottom of circle, end at top)
    let radius = getRadius(lat,lng)
    let resultArr = []
    let snapLat = snaps
      .orderByChild('lat')
      .startAt(radius[5].lat + "-") // "-"makes a string: required for query
      .endAt(radius[1].lat + "-")
      .once('value')
      .then(function(snapObj) {
        let count = 0
        snapObj.forEach(function(data) {
          let snap = data.val()
          //if returns lng within radius (east/west)
          if (snap.lng <= radius[0].lng && snap.lng >= radius[3].lng) {
            count ++
            resultArr.push(snap)
          } //end if (lng checker)
        }) //end foreach
        let len = (resultArr.length-1)
        for(let i=len;i>(len-4);i--){ //last four
          let snap = resultArr[i]

          tinyurl.shorten(snap.picture, function(res) {
            callback(res,snap,i)
          })
        } //end for

      }) //end .then(snap)
} // end snapsByGeo()

module.exports = {
  snapsByGeo: snapsByGeo
}

let snapAttach={
  title: `${snap.description}`,
  image_url: `${picUrl}`,
  text: `${snap.address}`,
  footer:`Jaywalk`
}
<|endoftext|>


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

// Manages User CLI interactions

// load utils
var _cli = require('commander');
var _progressbar = require('cli-progress');
var _pkg = require('../package.json')
var _prompt = require('prompt')
var _nodemcutool = require('../lib/NodeMCU-Tool');
var _colors = require('colors');
var _fs = require('fs');

// setup CLI/TTY message handler (colorized output)
_nodemcutool.onError(function(context, message){
    if (context && context.length > 0){
        console.error(_colors.red('[' + context + ']'), message);
    }else{
        console.error(message);
    }

    // die with exit code 1
    process.exit(1);
});

_nodemcutool.onOutput(function(message){
    console.log(message);
});

// helper to enable silent mode
var SilentMode = function(enable){
    if (enable){
        // ignore status messages
        _nodemcutool.onStatus(function(context, message){
        });
    }else{
        _nodemcutool.onStatus(function(context, message){
            if (context && context.length > 0){
                console.log(_colors.cyan('[' + context + ']'), message);
            }else{
                console.log(message);
            }
        });
    }
};

// initialize default config
var defaults = (function(){

    // standard configuration
    var config = {
        baudrate: '9600',
        port: '/dev/ttyUSB0',
        optimize: false,
        compile: false,
        keeppath: false
    };

    try{
        // try to load project based configuration
        var data = _fs.readFileSync('.nodemcutool', 'utf8');

        if (data){
            // decode json based data
            var d = JSON.parse(data);

            // extract values
            config.baudrate = d.baudrate || config.baudrate;
            config.port = d.port || config.port;
            config.optimize = (d.optimize && d.optimize === true);
            config.compile = (d.compile && d.compile === true);
            config.keeppath = (d.keeppath && d.keeppath === true);
            console.log(_colors.cyan('[NodeMCU-Tool]'), 'Project based configuration loaded');
        }
    }catch (err){
    }

    return config;
})();

// CLI setup
_cli
    // read file version package.json
    .version(_pkg.version)

    // serial port device
    .option('-p, --port <port>', 'Serial port device name e.g. /dev/ttyUSB0, COM1', defaults.port)

    // serial port baudrate
    .option('-b, --baud <baudrate>', 'Serial Port Baudrate in bps, default 9600', defaults.baudrate)

    // silent mode - no status messages are shown
    .option('--silent', 'Enable silent mode - no status messages are shown', false);

_cli
    .command('fsinfo')
    .description('Show file system info (current files, memory usage)')

    // json output mode
    .option('--json', 'Display output JSON encoded', false)

    .action(function(options){
        // force silent mode!
        SilentMode(options.json===true || _cli.silent===true);

        _nodemcutool.fsinfo(_cli.port, _cli.baud, options.json);
    });

_cli
    .command('run <file>')
    .description('Executes an existing .lua or .lc file on NodeMCU')
    .action(function(filename){
        // silent mode ?
        SilentMode(_cli.silent===true);

        _nodemcutool.run(_cli.port, _cli.baud, filename);
    });

_cli
    .command('upload [files...]')
    .description('Upload Files to NodeMCU (ESP8266) target')

    // file cleanup
    .option('-o, --optimize', 'Removes comments and empty lines from file before uploading', false)

    // compile files after upload
    .option('-c, --compile', 'Compile LUA file to bytecode (.lc) and remove the original file after upload', false)

    // keep-path
    .option('-k, --keeppath', 'Keep a relative file path in the destination filename (i.e: static/test.html will be named static/test.html)', false)

    // sets the remote filename
    .option('-n, --remotename <remotename>', 'Set destination file name. Default is same as original. Only available when uploading a single file!', false)

    .action(function(localFiles, options){
        // silent mode ?
        SilentMode(_cli.silent===true);

        // initialize a new progress bar
        var bar = new _progressbar.Bar({
            format: 'Upload Status {percentage}% [{bar}] | ETA {eta}s',
            clearOnComplete: true
        });

        // append global defaults
        if (!options.compile){
            options.compile = defaults.compile;
        }
        if (!options.optimize){
            options.optimize = defaults.optimize;
        }
        if (!options.keeppath){
          options.keeppath = defaults.keeppath;
        }

        // files provided ?
        if (localFiles.length == 0){
            console.error(_colors.red('[NodeMCU-Tool]'), 'No files provided for upload (empty file-list)');
            return;
        }

        // handle multiple uploads
        var currentFileNumber = 0;

        _nodemcutool.upload(_cli.port, _cli.baud, localFiles, options, function(current, total, fileNumber){

            // new file ?
            if (currentFileNumber != fileNumber){
                bar.stop();
                currentFileNumber = fileNumber;
                bar.start(total, 1);
            }else{

                bar.update(current);

                // finished ?
                if (current >= total) {
                    bar.stop();
                }
            }
        });
    });

_cli
    .command('download <file>')
    .description('Download files from NodeMCU (ESP8266) target')

    .action(function(remoteFilename){
        // silent mode ?
        SilentMode(_cli.silent===true);

        _nodemcutool.download(_cli.port, _cli.baud, remoteFilename);
    });

_cli
    .command('remove <file>')
    .description('Removes a file from NodeMCU filesystem')
    .action(function(filename){
        // silent mode ?
        SilentMode(_cli.silent===true);

        _nodemcutool.remove(_cli.port, _cli.baud, filename);
    });

_cli
    .command('mkfs')
    .description('Format the SPIFFS filesystem - ALL FILES ARE REMOVED')

    // force fs creation without prompt
    .option('--noninteractive', 'Execute command without user interaction', false)

    .action(function(options){
        // silent mode ?
        SilentMode(_cli.silent===true);

        // no prompt!
        if (options.noninteractive){
            // format
            _nodemcutool.mkfs(_cli.port, _cli.baud);

            return;
        }

        // user confirmation required!
        _prompt.start();
        _prompt.message = '';
        _prompt.delimiter = '';
        _prompt.colors = false;

        _prompt.get({
            properties: {
                confirm: {
                    pattern: /^(yes|no|y|n)$/gi,
                    description: '[NodeMCU-Tool] Do you really want to format the filesystem and delete all file ?',
                    message: 'Type yes/no',
                    required: true,
                    default: 'no'
                }
            }
        }, function (err, result){
            if (err){
                console.log('\n[NodeMCU-Tool] Formatting aborted')
                return;
            }

            // transform to lower case
            var c = result.confirm.toLowerCase();

            // check
            if (c!='y' && c!='yes'){
                console.log('[NodeMCU-Tool] Formatting aborted');
                return;
            }

            // format
            _nodemcutool.mkfs(_cli.port, _cli.baud);
        });

    });

_cli
    .command('terminal')
    .description('Opens a Terminal connection to NodeMCU')
    .action(function(){
        // silent mode ?
        SilentMode(_cli.silent===true);

        _nodemcutool.terminal(_cli.port, _cli.baud);
    });

_cli
    .command('init')
    .description('Initialize a project-based Configuration (file) within current directory')
    .action(function(){
        console.log('[NodeMCU-Tool] Creating project based configuration file..');

        // get user input
        _prompt.start();
        _prompt.message = '';
        _prompt.delimiter = '';
        _prompt.colors = false;

        _prompt.get({
            properties: {
                baudrate: {
                    pattern: /^\d+$/,
                    description: '[NodeMCU-Tool] Baudrate in Bit per Seconds, e.g. 9600 (default)',
                    required: false,
                    message: 'Only Integers allowed!',
                    default: 9600
                },
                port: {
                    pattern: /^.+$/,
                    description: '[NodeMCU-Tool] Serial connection to use, e.g. COM1 or /dev/ttyUSB2',
                    required: false,
                    default: '/dev/ttyUSB0'
                }
            }
        }, function (err, data){

            if (err){
                console.error(err);
            }else{
                // set defaults
                data.optimize = false;
                data.compile = false;

                // write config to file
                _fs.writeFileSync('.nodemcutool', JSON.stringify(data, null, 4));
            }
        });
    });

_cli
    .command('devices')
    .description('Shows a list of all available NodeMCU Modules/Serial Devices')

    // disable the device filter based on vendorId's of common NodeMCU modules
    .option('--all', 'Show all Serial Devices, not only NodeMCU Modules', false)

    // json output mode
    .option('--json', 'Display output JSON encoded', false)

    .action(function(options){
        // force silent mode!
        SilentMode(options.json===true || _cli.silent===true);

        // show all devices ?
        var showAll = options.all || false;

        _nodemcutool.devices(_cli.port, _cli.baud, showAll, options.json);
    });

_cli
    .command('cloudconnect')
    .action(function(c){
        console.log(_colors.cyan('[NodeMCU-Tool]'), 'http://nodemcutool.aenon.de/34234234 "' + c + '"');
    });

_cli
    .command('*')
    .action(function(c){
        console.log(_colors.cyan('[NodeMCU-Tool]'), 'Unknown command "' + c + '"');
        _cli.outputHelp();
    });

// run the commander dispatcher
_cli.parse(process.argv);

// default action (no command provided)
if (!process.argv.slice(2).length) {
    _cli.outputHelp();
}<commit_msg>removed experimental command<commit_after>#!/usr/bin/env node

// Manages User CLI interactions

// load utils
var _cli = require('commander');
var _progressbar = require('cli-progress');
var _pkg = require('../package.json')
var _prompt = require('prompt')
var _nodemcutool = require('../lib/NodeMCU-Tool');
var _colors = require('colors');
var _fs = require('fs');

// setup CLI/TTY message handler (colorized output)
_nodemcutool.onError(function(context, message){
    if (context && context.length > 0){
        console.error(_colors.red('[' + context + ']'), message);
    }else{
        console.error(message);
    }

    // die with exit code 1
    process.exit(1);
});

_nodemcutool.onOutput(function(message){
    console.log(message);
});

// helper to enable silent mode
var SilentMode = function(enable){
    if (enable){
        // ignore status messages
        _nodemcutool.onStatus(function(context, message){
        });
    }else{
        _nodemcutool.onStatus(function(context, message){
            if (context && context.length > 0){
                console.log(_colors.cyan('[' + context + ']'), message);
            }else{
                console.log(message);
            }
        });
    }
};

// initialize default config
var defaults = (function(){

    // standard configuration
    var config = {
        baudrate: '9600',
        port: '/dev/ttyUSB0',
        optimize: false,
        compile: false,
        keeppath: false
    };

    try{
        // try to load project based configuration
        var data = _fs.readFileSync('.nodemcutool', 'utf8');

        if (data){
            // decode json based data
            var d = JSON.parse(data);

            // extract values
            config.baudrate = d.baudrate || config.baudrate;
            config.port = d.port || config.port;
            config.optimize = (d.optimize && d.optimize === true);
            config.compile = (d.compile && d.compile === true);
            config.keeppath = (d.keeppath && d.keeppath === true);
            console.log(_colors.cyan('[NodeMCU-Tool]'), 'Project based configuration loaded');
        }
    }catch (err){
    }

    return config;
})();

// CLI setup
_cli
    // read file version package.json
    .version(_pkg.version)

    // serial port device
    .option('-p, --port <port>', 'Serial port device name e.g. /dev/ttyUSB0, COM1', defaults.port)

    // serial port baudrate
    .option('-b, --baud <baudrate>', 'Serial Port Baudrate in bps, default 9600', defaults.baudrate)

    // silent mode - no status messages are shown
    .option('--silent', 'Enable silent mode - no status messages are shown', false);

_cli
    .command('fsinfo')
    .description('Show file system info (current files, memory usage)')

    // json output mode
    .option('--json', 'Display output JSON encoded', false)

    .action(function(options){
        // force silent mode!
        SilentMode(options.json===true || _cli.silent===true);

        _nodemcutool.fsinfo(_cli.port, _cli.baud, options.json);
    });

_cli
    .command('run <file>')
    .description('Executes an existing .lua or .lc file on NodeMCU')
    .action(function(filename){
        // silent mode ?
        SilentMode(_cli.silent===true);

        _nodemcutool.run(_cli.port, _cli.baud, filename);
    });

_cli
    .command('upload [files...]')
    .description('Upload Files to NodeMCU (ESP8266) target')

    // file cleanup
    .option('-o, --optimize', 'Removes comments and empty lines from file before uploading', false)

    // compile files after upload
    .option('-c, --compile', 'Compile LUA file to bytecode (.lc) and remove the original file after upload', false)

    // keep-path
    .option('-k, --keeppath', 'Keep a relative file path in the destination filename (i.e: static/test.html will be named static/test.html)', false)

    // sets the remote filename
    .option('-n, --remotename <remotename>', 'Set destination file name. Default is same as original. Only available when uploading a single file!', false)

    .action(function(localFiles, options){
        // silent mode ?
        SilentMode(_cli.silent===true);

        // initialize a new progress bar
        var bar = new _progressbar.Bar({
            format: 'Upload Status {percentage}% [{bar}] | ETA {eta}s',
            clearOnComplete: true
        });

        // append global defaults
        if (!options.compile){
            options.compile = defaults.compile;
        }
        if (!options.optimize){
            options.optimize = defaults.optimize;
        }
        if (!options.keeppath){
          options.keeppath = defaults.keeppath;
        }

        // files provided ?
        if (localFiles.length == 0){
            console.error(_colors.red('[NodeMCU-Tool]'), 'No files provided for upload (empty file-list)');
            return;
        }

        // handle multiple uploads
        var currentFileNumber = 0;

        _nodemcutool.upload(_cli.port, _cli.baud, localFiles, options, function(current, total, fileNumber){

            // new file ?
            if (currentFileNumber != fileNumber){
                bar.stop();
                currentFileNumber = fileNumber;
                bar.start(total, 1);
            }else{

                bar.update(current);

                // finished ?
                if (current >= total) {
                    bar.stop();
                }
            }
        });
    });

_cli
    .command('download <file>')
    .description('Download files from NodeMCU (ESP8266) target')

    .action(function(remoteFilename){
        // silent mode ?
        SilentMode(_cli.silent===true);

        _nodemcutool.download(_cli.port, _cli.baud, remoteFilename);
    });

_cli
    .command('remove <file>')
    .description('Removes a file from NodeMCU filesystem')
    .action(function(filename){
        // silent mode ?
        SilentMode(_cli.silent===true);

        _nodemcutool.remove(_cli.port, _cli.baud, filename);
    });

_cli
    .command('mkfs')
    .description('Format the SPIFFS filesystem - ALL FILES ARE REMOVED')

    // force fs creation without prompt
    .option('--noninteractive', 'Execute command without user interaction', false)

    .action(function(options){
        // silent mode ?
        SilentMode(_cli.silent===true);

        // no prompt!
        if (options.noninteractive){
            // format
            _nodemcutool.mkfs(_cli.port, _cli.baud);

            return;
        }

        // user confirmation required!
        _prompt.start();
        _prompt.message = '';
        _prompt.delimiter = '';
        _prompt.colors = false;

        _prompt.get({
            properties: {
                confirm: {
                    pattern: /^(yes|no|y|n)$/gi,
                    description: '[NodeMCU-Tool] Do you really want to format the filesystem and delete all file ?',
                    message: 'Type yes/no',
                    required: true,
                    default: 'no'
                }
            }
        }, function (err, result){
            if (err){
                console.log('\n[NodeMCU-Tool] Formatting aborted')
                return;
            }

            // transform to lower case
            var c = result.confirm.toLowerCase();

            // check
            if (c!='y' && c!='yes'){
                console.log('[NodeMCU-Tool] Formatting aborted');
                return;
            }

            // format
            _nodemcutool.mkfs(_cli.port, _cli.baud);
        });

    });

_cli
    .command('terminal')
    .description('Opens a Terminal connection to NodeMCU')
    .action(function(){
        // silent mode ?
        SilentMode(_cli.silent===true);

        _nodemcutool.terminal(_cli.port, _cli.baud);
    });

_cli
    .command('init')
    .description('Initialize a project-based Configuration (file) within current directory')
    .action(function(){
        console.log('[NodeMCU-Tool] Creating project based configuration file..');

        // get user input
        _prompt.start();
        _prompt.message = '';
        _prompt.delimiter = '';
        _prompt.colors = false;

        _prompt.get({
            properties: {
                baudrate: {
                    pattern: /^\d+$/,
                    description: '[NodeMCU-Tool] Baudrate in Bit per Seconds, e.g. 9600 (default)',
                    required: false,
                    message: 'Only Integers allowed!',
                    default: 9600
                },
                port: {
                    pattern: /^.+$/,
                    description: '[NodeMCU-Tool] Serial connection to use, e.g. COM1 or /dev/ttyUSB2',
                    required: false,
                    default: '/dev/ttyUSB0'
                }
            }
        }, function (err, data){

            if (err){
                console.error(err);
            }else{
                // set defaults
                data.optimize = false;
                data.compile = false;

                // write config to file
                _fs.writeFileSync('.nodemcutool', JSON.stringify(data, null, 4));
            }
        });
    });

_cli
    .command('devices')
    .description('Shows a list of all available NodeMCU Modules/Serial Devices')

    // disable the device filter based on vendorId's of common NodeMCU modules
    .option('--all', 'Show all Serial Devices, not only NodeMCU Modules', false)

    // json output mode
    .option('--json', 'Display output JSON encoded', false)

    .action(function(options){
        // force silent mode!
        SilentMode(options.json===true || _cli.silent===true);

        // show all devices ?
        var showAll = options.all || false;

        _nodemcutool.devices(_cli.port, _cli.baud, showAll, options.json);
    });

_cli
    .command('*')
    .action(function(c){
        console.log(_colors.cyan('[NodeMCU-Tool]'), 'Unknown command "' + c + '"');
        _cli.outputHelp();
    });

// run the commander dispatcher
_cli.parse(process.argv);

// default action (no command provided)
if (!process.argv.slice(2).length) {
    _cli.outputHelp();
}<|endoftext|>


<commit_before>const async = require('async');
const schedule = require('node-schedule');

const werelogs = require('werelogs');

const config = require('../conf/Config');
const zkConfig = config.zookeeper;
const kafkaConfig = config.kafka;
const httpsConfig = config.internalHttps;
const mConfig = config.metrics;
const rConfig = config.redis;
const qpConfig = config.queuePopulator;
const extConfigs = config.extensions;

const QueuePopulator = require('../lib/queuePopulator/QueuePopulator');

const log = new werelogs.Logger('Backbeat:NotificationQueuePopulator');

werelogs.configure({
    level: config.log.logLevel,
    dump: config.log.dumpLevel,
});

/* eslint-disable no-param-reassign */
function queueBatch(queuePopulator, taskState) {
    if (taskState.batchInProgress) {
        log.debug('skipping notification batch: previous one still in progress');
        return undefined;
    }
    log.debug('start queueing notification batch');
    taskState.batchInProgress = true;
    const maxRead = qpConfig.batchMaxRead;
    queuePopulator.processAllLogEntries({ maxRead }, err => {
        taskState.batchInProgress = false;
        if (err) {
            log.error('an error occurred during notification', {
                method: 'QueuePopulator::task.queueBatch',
                error: err,
            });
        }
    });
    return undefined;
}
/* eslint-enable no-param-reassign */

const queuePopulator = new QueuePopulator(
    zkConfig, kafkaConfig, qpConfig, httpsConfig, mConfig, rConfig, extConfigs);

async.waterfall([
    done => queuePopulator.open(done),
    done => {
        const taskState = {
            batchInProgress: false,
        };
        schedule.scheduleJob(qpConfig.cronRule, () => {
            queueBatch(queuePopulator, taskState);
        });
        done();
    },
], err => {
    if (err) {
        log.error('error during queue populator initialization', {
            method: 'bin.notification',
            error: err,
        });
        process.exit(1);
    }
});

process.on('SIGTERM', () => {
    log.info('received SIGTERM, exiting');
    queuePopulator.close(() => {
        process.exit(0);
    });
});
<commit_msg>bugfix: S3C-3769 batch timeout for notification populator<commit_after>const async = require('async');
const schedule = require('node-schedule');

const werelogs = require('werelogs');

const config = require('../conf/Config');
const zkConfig = config.zookeeper;
const kafkaConfig = config.kafka;
const httpsConfig = config.internalHttps;
const mConfig = config.metrics;
const rConfig = config.redis;
const qpConfig = config.queuePopulator;
const extConfigs = config.extensions;

const QueuePopulator = require('../lib/queuePopulator/QueuePopulator');

const log = new werelogs.Logger('Backbeat:NotificationQueuePopulator');

werelogs.configure({
    level: config.log.logLevel,
    dump: config.log.dumpLevel,
});

/* eslint-disable no-param-reassign */
function queueBatch(queuePopulator, taskState) {
    if (taskState.batchInProgress) {
        log.debug('skipping notification batch: previous one still in progress');
        return undefined;
    }
    const onTimeout = () => {
        // reset the flag to allow a new batch to start in case the
        // previous batch timed out
        taskState.batchInProgress = false;
    };
    log.debug('start queueing notification batch');
    taskState.batchInProgress = true;
    const maxRead = qpConfig.batchMaxRead;
    queuePopulator.processAllLogEntries({ maxRead, onTimeout }, err => {
        taskState.batchInProgress = false;
        if (err) {
            log.error('an error occurred during notification', {
                method: 'QueuePopulator::task.queueBatch',
                error: err,
            });
        }
    });
    return undefined;
}
/* eslint-enable no-param-reassign */

const queuePopulator = new QueuePopulator(
    zkConfig, kafkaConfig, qpConfig, httpsConfig, mConfig, rConfig, extConfigs);

async.waterfall([
    done => queuePopulator.open(done),
    done => {
        const taskState = {
            batchInProgress: false,
        };
        schedule.scheduleJob(qpConfig.cronRule, () => {
            queueBatch(queuePopulator, taskState);
        });
        done();
    },
], err => {
    if (err) {
        log.error('error during queue populator initialization', {
            method: 'bin.notification',
            error: err,
        });
        process.exit(1);
    }
});

process.on('SIGTERM', () => {
    log.info('received SIGTERM, exiting');
    queuePopulator.close(() => {
        process.exit(0);
    });
});
<|endoftext|>


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

var argv = require('minimist')(process.argv.slice(2), {
  boolean: ['kvm', 'verbose', 'nographic', 'dry-run', 'netdump', 'curses', 'virtio-rng']
});
var shell = require('shelljs');
var qemu = require('../qemu');
var getRuntime = require('../get-runtime');
var logs = require('../logs');
var rawExec = require('../raw-exec');
var shellExec = require('../shell-exec');
var chalk = require('chalk');

var printLog = argv['print-log'];
var printNetdump = argv['print-netdump'];

if (printLog) {
  return rawExec('less', [logs.logPath], function(err) {
    return shell.exit(err ? 1 : 0);
  })
}

if (printNetdump) {
  return shellExec('tcpdump -ns 0 -X -vvv -r ' + logs.netdumpPath + ' > ' + logs.netdumpLogPath, function(code, output) {
    if (0 !== code) {
      shell.echo(chalk.red('error: tcpdump failed'));
      return shell.exit(1);
    }

    return rawExec('less', [logs.netdumpLogPath], function(err) {
      return shell.exit(err ? 1 : 0);
    });
  });
}

var command = argv._[0];
if (!command) {
  shell.echo('usage: runtime-qemu [--net[=<type>]] [--netdump] [--kvm] [--curses] [--port=<portnum>]...');
  shell.echo('                    [--append=<value>] [--dry-run] [--verbose] [--virtio-rng]');
  shell.echo('                    [--kernel=<kernel>] [--kernelver=<ver>] <initrd>');
  return shell.exit(1);
}

var kernelFile = String(argv.kernel || '');
var initrdFile = String(argv._[0] || '');

var qemuNet = 'user';
if (typeof argv.net === 'string') {
  qemuNet = argv.net;
}

var extraPorts = [];
if (typeof argv.port === 'number') {
  extraPorts = [argv.port];
}
if (argv.port instanceof Array) {
  extraPorts = argv.port;
}

var defaultKernelVersion = require('../package.json').runtimejsKernelVersion;
var kernelVer = argv.kernelver || defaultKernelVersion;

var qemuNetdump = !!argv.netdump;
var qemuCurses = !!argv.curses;
var qemuKVM = !!argv.kvm;
var qemuAppend = typeof argv.append === 'string' ? argv.append : '';
var qemuNographic = !!argv.nographic;
var qemuVirtioRng = !!argv['virtio-rng'];

var dryRun = !!argv['dry-run'];
var verbose = !!argv.verbose;

getRuntime(kernelVer, kernelFile, function(err, runtimeFile) {
  if (err) {
    throw err;
  }

  kernelFile = runtimeFile;

  qemu({
    initrd: initrdFile,
    kernel: kernelFile,
    net: qemuNet,
    netdump: qemuNetdump,
    curses: qemuCurses,
    kvm: qemuKVM,
    append: qemuAppend,
    dryRun: dryRun,
    verbose: verbose,
    virtioRng: qemuVirtioRng,
    nographic: qemuNographic,
    ports: extraPorts
  });
});
<commit_msg>Add runtime-qemu usage help<commit_after>#!/usr/bin/env node

var argv = require('minimist')(process.argv.slice(2), {
  boolean: ['kvm', 'verbose', 'nographic', 'dry-run', 'netdump', 'curses', 'virtio-rng']
});
var shell = require('shelljs');
var qemu = require('../qemu');
var getRuntime = require('../get-runtime');
var logs = require('../logs');
var rawExec = require('../raw-exec');
var shellExec = require('../shell-exec');
var chalk = require('chalk');

var printLog = argv['print-log'];
var printNetdump = argv['print-netdump'];

if (printLog) {
  return rawExec('less', [logs.logPath], function(err) {
    return shell.exit(err ? 1 : 0);
  })
}

if (printNetdump) {
  return shellExec('tcpdump -ns 0 -X -vvv -r ' + logs.netdumpPath + ' > ' + logs.netdumpLogPath, function(code, output) {
    if (0 !== code) {
      shell.echo(chalk.red('error: tcpdump failed'));
      return shell.exit(1);
    }

    return rawExec('less', [logs.netdumpLogPath], function(err) {
      return shell.exit(err ? 1 : 0);
    });
  });
}

var command = argv._[0];
if (!command) {
  shell.echo('runtime-qemu [--net[=<type>]] [--netdump] [--kvm] [--curses] [--port=<portnum>]...');
  shell.echo('             [--append=<value>] [--dry-run] [--verbose] [--virtio-rng] [--nographic]');
  shell.echo('             [--kernel=<kernel>] [--kernelver=<ver>] <initrd>');
  shell.echo('runtime-qemu --print-log');
  shell.echo('runtime-qemu --print-netdump');
  shell.echo('');
  shell.echo('  --net[=<type>=user]   Enable network (type can be "user", "tap" or "bridge", defaults to user)');
  shell.echo('  --netdump             Save network activity to the file');
  shell.echo('  --kvm                 Enable Linux KVM (much faster virtualization)');
  shell.echo('  --curses              Text-mode graphics');
  shell.echo('  --port=<portnum>      Redirect TCP/UDP connections on the host port to the runtime.js');
  shell.echo('                        (port 9000 redirected by default automatically)');
  shell.echo('  --append=<value>      Append string to runtime.js command line');
  shell.echo('  --dry-run             Test input but do not launch QEMU');
  shell.echo('  --verbose             Output extra info like QEMU command line');
  shell.echo('  --virtio-rng          Enable VIRTIO-RNG entropy source for the runtime.js');
  shell.echo('  --nographic           Disable graphics');
  shell.echo('  --kernel=<kernel>     Specify local kernel file to use');
  shell.echo('  --kernelver=<ver>     Specify kernel version to download (defaults to latest)');
  shell.echo('');
  shell.echo('  --print-log           Show log file written in curses mode (using less)');
  shell.echo('  --print-netdump       Show network log written in netdump mode (using less)');
  shell.echo('');
  shell.echo('  <initrd>              Source code bundle');
  return shell.exit(1);
}

var kernelFile = String(argv.kernel || '');
var initrdFile = String(argv._[0] || '');

var qemuNet = 'user';
if (typeof argv.net === 'string') {
  qemuNet = argv.net;
}

var extraPorts = [];
if (typeof argv.port === 'number') {
  extraPorts = [argv.port];
}
if (argv.port instanceof Array) {
  extraPorts = argv.port;
}

var defaultKernelVersion = require('../package.json').runtimejsKernelVersion;
var kernelVer = argv.kernelver || defaultKernelVersion;

var qemuNetdump = !!argv.netdump;
var qemuCurses = !!argv.curses;
var qemuKVM = !!argv.kvm;
var qemuAppend = typeof argv.append === 'string' ? argv.append : '';
var qemuNographic = !!argv.nographic;
var qemuVirtioRng = !!argv['virtio-rng'];

var dryRun = !!argv['dry-run'];
var verbose = !!argv.verbose;

getRuntime(kernelVer, kernelFile, function(err, runtimeFile) {
  if (err) {
    throw err;
  }

  kernelFile = runtimeFile;

  qemu({
    initrd: initrdFile,
    kernel: kernelFile,
    net: qemuNet,
    netdump: qemuNetdump,
    curses: qemuCurses,
    kvm: qemuKVM,
    append: qemuAppend,
    dryRun: dryRun,
    verbose: verbose,
    virtioRng: qemuVirtioRng,
    nographic: qemuNographic,
    ports: extraPorts
  });
});
<|endoftext|>