<commit_before>var Uber = {
	accessToken: localStorage.getItem('accessToken') || '',
	refreshToken: localStorage.getItem('refreshToken') || '',
	locations: JSON.parse(localStorage.getItem('locations')) || [],
	products: [],

	init: function() {
		Uber.refresh();
	},

	sendError: {
		product: function(error) {
			appMessageQueue.clear();
			appMessageQueue.send({type:TYPE.PRODUCT, method:METHOD.ERROR, name:error});
		},
		location: function(error) {
			appMessageQueue.clear();
			appMessageQueue.send({type:TYPE.LOCATON, method:METHOD.ERROR, name:error});
		}
	},

	error: function(error) {
		appMessageQueue.clear();
		appMessageQueue.send({type:TYPE.ERROR, name:error});
	},

	sendLocations: function() {
		appMessageQueue.send({type:TYPE.LOCATION, method:METHOD.SIZE, index:Uber.locations.length});
		for (var i = 0; i < Uber.locations.length; i++) {
			var name = Uber.locations[i].name.substring(0,12);
			appMessageQueue.send({type:TYPE.LOCATION, method:METHOD.DATA, index:i, name:name, estimate:''});
		}
	},

	sendProducts: function() {
		appMessageQueue.send({type:TYPE.PRODUCT, method:METHOD.SIZE, index:Uber.products.length});
		for (var i = 0; i < Uber.products.length; i++) {
			var name = Uber.products[i].display_name.substring(0,12);
			var estimate = Math.ceil(Uber.products[i].estimate / 60) + ' min';
			var surge = (Uber.products[i].surge_multiplier > 1) ? 'Surge pricing: ' + Uber.products[i].surge_multiplier + 'x' : '';
			var resource = resources[name] || resources.uberX;
			appMessageQueue.send({type:TYPE.PRODUCT, method:METHOD.DATA, index:i, name:name, estimate:estimate, surge:surge, resource:resource-1});
		}
	},

	checkProducts: function(latitude, longitude) {
		Uber.sendError.product('Uber is not yet available at this location.');
		Uber.api.products(latitude, longitude, function(xhr) {
			var res = JSON.parse(xhr.responseText);
			console.log(JSON.stringify(res));
			if (res.products && res.products.length > 0) {
				Uber.sendError.product('All nearby cars are full but one should free up soon. Please try again in a few minutes.');
			}
		});
	},

	getPriceEstimates: function(productIndex) {
		navigator.geolocation.getCurrentPosition(function(pos) {
			var latitude = pos.coords.latitude || 0;
			var longitude = pos.coords.longitude || 0;
			var i = 0;
			if (!Uber.accessToken) {
				Uber.locations.forEach(function(location) {
					var index = i++;
					var url = 'https://ineal.me/pebble/ultra/api/estimates/price?start_latitude=' + latitude + '&start_longitude=' + longitude + '&end_latitude=' + location.lat + '&end_longitude=' + location.lng;
					var xhr = new XMLHttpRequest();
					xhr.open('GET', url, true);
					xhr.onload = function() {
						var res = JSON.parse(xhr.responseText);
						console.log(JSON.stringify(res));
						if (!res.prices || !res.prices.length) return;
						var name = location.name.substring(0,12);
						var estimate = '';
						res.prices.forEach(function(price) {
							if (Uber.products[productIndex].product_id == price.product_id) {
								estimate = price.estimate;
							}
						});
						appMessageQueue.send({type:TYPE.LOCATION, method:METHOD.DATA, index:index, name:name, estimate:estimate});
					};
					xhr.send(null);
				});
				return;
			}
			Uber.locations.forEach(function(location) {
				var index = i++;
				Uber.api.priceEstimates(latitude, longitude, location.lat, location.lng, function(xhr) {
					var res = JSON.parse(xhr.responseText);
					console.log(JSON.stringify(res));
					if (xhr.status === 401) {
						return Uber.refreshAccessToken(Uber.getPriceEstimates);
					}
					if (res.code && res.code == 'distance_exceeded') {
						return appMessageQueue.send({type:TYPE.LOCATION, method:METHOD.DATA, index:index, name:location.name.substring(0,12), estimate:'N/A'});
					}
					if (!res.prices || !res.prices.length) return;
					var name = location.name.substring(0,12);
					var estimate = '';
					res.prices.forEach(function(price) {
						if (Uber.products[productIndex].product_id == price.product_id) {
							estimate = price.estimate;
						}
					});
					appMessageQueue.send({type:TYPE.LOCATION, method:METHOD.DATA, index:index, name:name, estimate:estimate});
				});
			});
		}, function(err) { Uber.sendError.location('Failed to get geolocation!'); }, { timeout: 10000 });
	},

	refresh: function() {
		Uber.sendError.product('Trying to get Geolocation...');
		navigator.geolocation.getCurrentPosition(function(pos) {
			var latitude = pos.coords.latitude || 0;
			var longitude = pos.coords.longitude || 0;
			Uber.sendError.product('Requesting estimated pick up times...');
			if (!Uber.accessToken) {
				var url = 'https://ineal.me/pebble/ultra/api/estimates/time?latitude=' + latitude + '&longitude=' + longitude;
				var xhr = new XMLHttpRequest();
				xhr.open('GET', url, true);
				xhr.onload = function() {
					var res = JSON.parse(xhr.responseText);
					console.log(JSON.stringify(res));
					if (!res.times || !res.times.length) {
						return Uber.sendError.product('Uber is not yet available at this location or all cars are full at the moment. Please try again in a few minutes.');
					}
					Uber.products = res.times;
					Uber.sendProducts();
				};
				xhr.onerror = function() { Uber.sendError.product('Connection error!'); };
				xhr.ontimeout = function() { Uber.sendError.product('Connection to Uber API timed out!'); };
				xhr.timeout = 30000;
				xhr.send(null);
				return;
			}
			Uber.api.timeEstimates(latitude, longitude, function(xhr) {
				var res = JSON.parse(xhr.responseText);
				console.log(JSON.stringify(res));
				if (xhr.status === 401) {
					return Uber.refreshAccessToken(Uber.refresh);
				}
				if (!res.times) return;
				if (!res.times.length) {
					return Uber.checkProducts(latitude, longitude);
				}
				Uber.products = res.times;
				Uber.sendError.product('Checking for surge pricing...');
				Uber.api.priceEstimates(latitude, longitude, latitude, longitude, function(xhr2) {
					var res2 = JSON.parse(xhr2.responseText);
					console.log(JSON.stringify(res2));
					if (res2.prices && res2.prices.length > 0) {
						res2.prices.forEach(function(price) {
							for (var i = 0; i < Uber.products.length; i++) {
								if (Uber.products[i].product_id == price.product_id) {
									Uber.products[i].surge_multiplier = price.surge_multiplier;
								}
							}
						});
					}
					Uber.sendProducts();
				}, function(err) {
					Uber.sendProducts();
				});
			}, Uber.sendError.product);
		}, function(err) { Uber.sendError.product('Failed to get geolocation!'); }, { timeout: 10000 });
	},

	api: {
		products: function(latitude, longitude, cb, fb) {
			Uber.api.makeRequest('GET', '/v1/products', serialize({latitude:latitude, longitude:longitude}), cb, fb);
		},

		timeEstimates: function(latitude, longitude, cb, fb) {
			Uber.api.makeRequest('GET', '/v1/estimates/time', serialize({start_latitude:latitude, start_longitude:longitude}), cb, fb);
		},

		priceEstimates: function(start_lat, start_lng, end_lat, end_lng, cb, fb) {
			Uber.api.makeRequest('GET', '/v1/estimates/price', serialize({start_latitude:start_lat, start_longitude:start_lng, end_latitude:end_lat, end_longitude:end_lng}), cb, fb);
		},

		makeRequest: function(method, endpoint, data, cb, fb) {
			if (!Uber.accessToken) return Uber.sendError.product('Please log in to your Uber account on the Pebble mobile app.');
			var url = 'https://api.uber.com' + endpoint;
			if (method == 'GET' && data) {
				url += '?' + data;
				data = null;
			}
			console.log(method + ' ' + url + ' ' + data);
			var xhr = new XMLHttpRequest();
			xhr.setRequestHeader('Authorization', 'Bearer ' + Uber.accessToken);
			xhr.open(method, url, true);
			xhr.onload = function() { cb(xhr); };
			xhr.onerror = function() { fb('Connection error!'); };
			xhr.ontimeout = function() { fb('Connection to Uber API timed out!'); };
			xhr.timeout = 30000;
			xhr.send(data);
		}
	},

	handleAppMessage: function(e) {
		console.log('AppMessage received: ' + JSON.stringify(e.payload));
		if (!e.payload.method) return;
		switch (e.payload.method) {
			case METHOD.REQUESTPRODUCTS:
				Uber.refresh();
				break;
			case METHOD.REQUESTLOCATIONS:
				Uber.sendLocations();
				Uber.getPriceEstimates(e.payload.index);
				break;
			case METHOD.REQUESTPRICE:
				Uber.getPriceEstimates(e.payload.index);
				break;
		}
	},

	refreshAccessToken: function(cb) {
		var xhr = new XMLHttpRequest();
		xhr.open('GET', 'https://ineal.me/pebble/ultra/api/refresh?token=' + Uber.refreshToken, true);
		xhr.onload = function() {
			var res = JSON.parse(xhr.responseText);
			console.log('new tokens: ' + JSON.stringify(res));
			if (res.access_token) {
				Uber.accessToken = res.access_token;
				localStorage.setItem('accessToken', Uber.accessToken);
			}
			if (res.refresh_token) {
				Uber.refreshToken = res.refresh_token;
				localStorage.setItem('refreshToken', Uber.refreshToken);
			}
			if (cb && typeof(cb) === 'function') {
				cb();
			}
		};
		xhr.send(null);
	},

	showConfiguration: function() {
		var data = {locations: Uber.locations};
		Pebble.openURL('https://ineal.me/pebble/ultra/configuration/?data=' + encodeURIComponent(JSON.stringify(data)));
	},

	handleConfiguration: function(e) {
		console.log('configuration received: ' + JSON.stringify(e));
		if (!e.response) return;
		var data = JSON.parse(decodeURIComponent(e.response));
		if (data.keys) {
			if (data.keys.access_token) {
				Uber.accessToken = data.keys.access_token;
				localStorage.setItem('accessToken', Uber.accessToken);
			}
			if (data.keys.refresh_token) {
				Uber.refreshToken = data.keys.refresh_token;
				localStorage.setItem('refreshToken', Uber.refreshToken);
			}
			Uber.refresh();
		}
		if (data.locations) {
			Uber.locations = data.locations;
			localStorage.setItem('locations', JSON.stringify(Uber.locations));
			Uber.sendLocations();
		}
	}
};

Pebble.addEventListener('ready', Uber.init);
Pebble.addEventListener('appmessage', Uber.handleAppMessage);
Pebble.addEventListener('showConfiguration', Uber.showConfiguration);
Pebble.addEventListener('webviewclosed', Uber.handleConfiguration);
<commit_msg>Show a message if no locations are added.<commit_after>var Uber = {
	accessToken: localStorage.getItem('accessToken') || '',
	refreshToken: localStorage.getItem('refreshToken') || '',
	locations: JSON.parse(localStorage.getItem('locations')) || [],
	products: [],

	init: function() {
		Uber.refresh();
	},

	sendError: {
		product: function(error) {
			appMessageQueue.clear();
			appMessageQueue.send({type:TYPE.PRODUCT, method:METHOD.ERROR, name:error});
		},
		location: function(error) {
			appMessageQueue.clear();
			appMessageQueue.send({type:TYPE.LOCATION, method:METHOD.ERROR, name:error});
		}
	},

	error: function(error) {
		appMessageQueue.clear();
		appMessageQueue.send({type:TYPE.ERROR, name:error});
	},

	sendLocations: function() {
		if (!Uber.locations.length) {
			return Uber.sendError.location('No locations found. Use the app settings in the Pebble mobile app to add favorite locations.');
		}
		appMessageQueue.send({type:TYPE.LOCATION, method:METHOD.SIZE, index:Uber.locations.length});
		for (var i = 0; i < Uber.locations.length; i++) {
			var name = Uber.locations[i].name.substring(0,12);
			appMessageQueue.send({type:TYPE.LOCATION, method:METHOD.DATA, index:i, name:name, estimate:''});
		}
	},

	sendProducts: function() {
		appMessageQueue.send({type:TYPE.PRODUCT, method:METHOD.SIZE, index:Uber.products.length});
		for (var i = 0; i < Uber.products.length; i++) {
			var name = Uber.products[i].display_name.substring(0,12);
			var estimate = Math.ceil(Uber.products[i].estimate / 60) + ' min';
			var surge = (Uber.products[i].surge_multiplier > 1) ? 'Surge pricing: ' + Uber.products[i].surge_multiplier + 'x' : '';
			var resource = resources[name] || resources.uberX;
			appMessageQueue.send({type:TYPE.PRODUCT, method:METHOD.DATA, index:i, name:name, estimate:estimate, surge:surge, resource:resource-1});
		}
	},

	checkProducts: function(latitude, longitude) {
		Uber.sendError.product('Uber is not yet available at this location.');
		Uber.api.products(latitude, longitude, function(xhr) {
			var res = JSON.parse(xhr.responseText);
			console.log(JSON.stringify(res));
			if (res.products && res.products.length > 0) {
				Uber.sendError.product('All nearby cars are full but one should free up soon. Please try again in a few minutes.');
			}
		});
	},

	getPriceEstimates: function(productIndex) {
		navigator.geolocation.getCurrentPosition(function(pos) {
			var latitude = pos.coords.latitude || 0;
			var longitude = pos.coords.longitude || 0;
			var i = 0;
			if (!Uber.accessToken) {
				Uber.locations.forEach(function(location) {
					var index = i++;
					var url = 'https://ineal.me/pebble/ultra/api/estimates/price?start_latitude=' + latitude + '&start_longitude=' + longitude + '&end_latitude=' + location.lat + '&end_longitude=' + location.lng;
					var xhr = new XMLHttpRequest();
					xhr.open('GET', url, true);
					xhr.onload = function() {
						var res = JSON.parse(xhr.responseText);
						console.log(JSON.stringify(res));
						if (!res.prices || !res.prices.length) return;
						var name = location.name.substring(0,12);
						var estimate = '';
						res.prices.forEach(function(price) {
							if (Uber.products[productIndex].product_id == price.product_id) {
								estimate = price.estimate;
							}
						});
						appMessageQueue.send({type:TYPE.LOCATION, method:METHOD.DATA, index:index, name:name, estimate:estimate});
					};
					xhr.send(null);
				});
				return;
			}
			Uber.locations.forEach(function(location) {
				var index = i++;
				Uber.api.priceEstimates(latitude, longitude, location.lat, location.lng, function(xhr) {
					var res = JSON.parse(xhr.responseText);
					console.log(JSON.stringify(res));
					if (xhr.status === 401) {
						return Uber.refreshAccessToken(Uber.getPriceEstimates);
					}
					if (res.code && res.code == 'distance_exceeded') {
						return appMessageQueue.send({type:TYPE.LOCATION, method:METHOD.DATA, index:index, name:location.name.substring(0,12), estimate:'N/A'});
					}
					if (!res.prices || !res.prices.length) return;
					var name = location.name.substring(0,12);
					var estimate = '';
					res.prices.forEach(function(price) {
						if (Uber.products[productIndex].product_id == price.product_id) {
							estimate = price.estimate;
						}
					});
					appMessageQueue.send({type:TYPE.LOCATION, method:METHOD.DATA, index:index, name:name, estimate:estimate});
				});
			});
		}, function(err) { Uber.sendError.location('Failed to get geolocation!'); }, { timeout: 10000 });
	},

	refresh: function() {
		Uber.sendError.product('Trying to get Geolocation...');
		navigator.geolocation.getCurrentPosition(function(pos) {
			var latitude = pos.coords.latitude || 0;
			var longitude = pos.coords.longitude || 0;
			Uber.sendError.product('Requesting estimated pick up times...');
			if (!Uber.accessToken) {
				var url = 'https://ineal.me/pebble/ultra/api/estimates/time?latitude=' + latitude + '&longitude=' + longitude;
				var xhr = new XMLHttpRequest();
				xhr.open('GET', url, true);
				xhr.onload = function() {
					var res = JSON.parse(xhr.responseText);
					console.log(JSON.stringify(res));
					if (!res.times || !res.times.length) {
						return Uber.sendError.product('Uber is not yet available at this location or all cars are full at the moment. Please try again in a few minutes.');
					}
					Uber.products = res.times;
					Uber.sendProducts();
				};
				xhr.onerror = function() { Uber.sendError.product('Connection error!'); };
				xhr.ontimeout = function() { Uber.sendError.product('Connection to Uber API timed out!'); };
				xhr.timeout = 30000;
				xhr.send(null);
				return;
			}
			Uber.api.timeEstimates(latitude, longitude, function(xhr) {
				var res = JSON.parse(xhr.responseText);
				console.log(JSON.stringify(res));
				if (xhr.status === 401) {
					return Uber.refreshAccessToken(Uber.refresh);
				}
				if (!res.times) return;
				if (!res.times.length) {
					return Uber.checkProducts(latitude, longitude);
				}
				Uber.products = res.times;
				Uber.sendError.product('Checking for surge pricing...');
				Uber.api.priceEstimates(latitude, longitude, latitude, longitude, function(xhr2) {
					var res2 = JSON.parse(xhr2.responseText);
					console.log(JSON.stringify(res2));
					if (res2.prices && res2.prices.length > 0) {
						res2.prices.forEach(function(price) {
							for (var i = 0; i < Uber.products.length; i++) {
								if (Uber.products[i].product_id == price.product_id) {
									Uber.products[i].surge_multiplier = price.surge_multiplier;
								}
							}
						});
					}
					Uber.sendProducts();
				}, function(err) {
					Uber.sendProducts();
				});
			}, Uber.sendError.product);
		}, function(err) { Uber.sendError.product('Failed to get geolocation!'); }, { timeout: 10000 });
	},

	api: {
		products: function(latitude, longitude, cb, fb) {
			Uber.api.makeRequest('GET', '/v1/products', serialize({latitude:latitude, longitude:longitude}), cb, fb);
		},

		timeEstimates: function(latitude, longitude, cb, fb) {
			Uber.api.makeRequest('GET', '/v1/estimates/time', serialize({start_latitude:latitude, start_longitude:longitude}), cb, fb);
		},

		priceEstimates: function(start_lat, start_lng, end_lat, end_lng, cb, fb) {
			Uber.api.makeRequest('GET', '/v1/estimates/price', serialize({start_latitude:start_lat, start_longitude:start_lng, end_latitude:end_lat, end_longitude:end_lng}), cb, fb);
		},

		makeRequest: function(method, endpoint, data, cb, fb) {
			if (!Uber.accessToken) return Uber.sendError.product('Please log in to your Uber account on the Pebble mobile app.');
			var url = 'https://api.uber.com' + endpoint;
			if (method == 'GET' && data) {
				url += '?' + data;
				data = null;
			}
			console.log(method + ' ' + url + ' ' + data);
			var xhr = new XMLHttpRequest();
			xhr.setRequestHeader('Authorization', 'Bearer ' + Uber.accessToken);
			xhr.open(method, url, true);
			xhr.onload = function() { cb(xhr); };
			xhr.onerror = function() { fb('Connection error!'); };
			xhr.ontimeout = function() { fb('Connection to Uber API timed out!'); };
			xhr.timeout = 30000;
			xhr.send(data);
		}
	},

	handleAppMessage: function(e) {
		console.log('AppMessage received: ' + JSON.stringify(e.payload));
		if (!e.payload.method) return;
		switch (e.payload.method) {
			case METHOD.REQUESTPRODUCTS:
				Uber.refresh();
				break;
			case METHOD.REQUESTLOCATIONS:
				Uber.sendLocations();
				Uber.getPriceEstimates(e.payload.index);
				break;
			case METHOD.REQUESTPRICE:
				Uber.getPriceEstimates(e.payload.index);
				break;
		}
	},

	refreshAccessToken: function(cb) {
		var xhr = new XMLHttpRequest();
		xhr.open('GET', 'https://ineal.me/pebble/ultra/api/refresh?token=' + Uber.refreshToken, true);
		xhr.onload = function() {
			var res = JSON.parse(xhr.responseText);
			console.log('new tokens: ' + JSON.stringify(res));
			if (res.access_token) {
				Uber.accessToken = res.access_token;
				localStorage.setItem('accessToken', Uber.accessToken);
			}
			if (res.refresh_token) {
				Uber.refreshToken = res.refresh_token;
				localStorage.setItem('refreshToken', Uber.refreshToken);
			}
			if (cb && typeof(cb) === 'function') {
				cb();
			}
		};
		xhr.send(null);
	},

	showConfiguration: function() {
		var data = {locations: Uber.locations};
		Pebble.openURL('https://ineal.me/pebble/ultra/configuration/?data=' + encodeURIComponent(JSON.stringify(data)));
	},

	handleConfiguration: function(e) {
		console.log('configuration received: ' + JSON.stringify(e));
		if (!e.response) return;
		var data = JSON.parse(decodeURIComponent(e.response));
		if (data.keys) {
			if (data.keys.access_token) {
				Uber.accessToken = data.keys.access_token;
				localStorage.setItem('accessToken', Uber.accessToken);
			}
			if (data.keys.refresh_token) {
				Uber.refreshToken = data.keys.refresh_token;
				localStorage.setItem('refreshToken', Uber.refreshToken);
			}
			Uber.refresh();
		}
		if (data.locations) {
			Uber.locations = data.locations;
			localStorage.setItem('locations', JSON.stringify(Uber.locations));
			Uber.sendLocations();
		}
	}
};

Pebble.addEventListener('ready', Uber.init);
Pebble.addEventListener('appmessage', Uber.handleAppMessage);
Pebble.addEventListener('showConfiguration', Uber.showConfiguration);
Pebble.addEventListener('webviewclosed', Uber.handleConfiguration);
<|endoftext|>


<commit_before>import { useEffect } from 'react'
import { createBot } from 'botui'
import { createRoot } from 'react-dom/client'

import { BotUI, BotUIMessageList, BotUIAction, useBotUI } from '../src'

const myBot = createBot()
const CustomCheck = () => {
  const bot = useBotUI()
  console.log('bot', bot)

  return <>
    <BotUIMessageList />
    <BotUIAction />
  </>
}

const App = () => {

  useEffect(() => {
    myBot.wait({ waitTime: 1000 })
    // .then(() => myBot.action.set({ options: [{ label: 'Moin', value: 'moin' }, { label: 'Umer', value: 'umer' }] }, { input: 'select' }))
    // .then((data) => myBot.message.add({ text: `nice to meet you ${data.text}` }))
      .then(() => myBot.message.add({ text: 'hello, enter a repo' }))
      .then(() => myBot.action.set({ placeholder: 'repo' }, { input: 'text' }))
      .then(data => {
        fetch(`https://api.github.com/repos/${data.value}`)
          .then(res => res.json())
          .then(res => {
            myBot.next({ count: res.stargazers_count })
          })

        return myBot.wait()
      })
      .then(data => myBot.message.add({ text: `it has ${data.count} ⭐`}))
  }, [])

  return

    <BotUI bot={myBot}>
      <CustomCheck />
    </BotUI>

}

const containerElement = document.getElementById('botui')
const root = createRoot(containerElement)
root.render(<App />)
<commit_msg>prettier<commit_after>import { useEffect } from 'react'
import { createBot } from 'botui'
import { createRoot } from 'react-dom/client'

import { BotUI, BotUIMessageList, BotUIAction } from '../src'

const myBot = createBot()
const CustomCheck = () => {
  return (
    <>
      <BotUIMessageList />
      <BotUIAction />
    </>
  )
}

const App = () => {
  useEffect(() => {
    myBot
      .wait({ waitTime: 1000 })
      // .then(() =>
      //   myBot.action.set(
      //     {
      //       options: [
      //         { label: 'John', value: 'john' },
      //         { label: 'Jane', value: 'jane' },
      //       ],
      //     },
      //     { input: 'select' }
      //   )
      // )
      // .then((data) =>
      //   myBot.message.add({ text: `nice to meet you ${data.text}` })
      // )
      .then(() => myBot.message.add({ text: 'hello, enter a repo' }))
      .then(() => myBot.action.set({ placeholder: 'repo' }, { input: 'text' }))
      .then((data) => {
        fetch(`https://api.github.com/repos/${data.value}`)
          .then((res) => res.json())
          .then((res) => {
            myBot.next({ count: res.stargazers_count })
          })

        return myBot.wait()
      })
      .then((data) => myBot.message.add({ text: `it has ${data.count} ⭐` }))
  }, [])

  return (

      <BotUI bot={myBot}>
        <CustomCheck />
      </BotUI>

  )
}

const containerElement = document.getElementById('botui')
const root = createRoot(containerElement)
root.render(<App />)
<|endoftext|>


<commit_before>
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { ipcRenderer } from 'electron';
import clazz from 'classname';

import classes from './style.css';
import Emoji from './Emoji';

export default class MessageInput extends Component {
    static propTypes = {
        me: PropTypes.object,
        sendMessage: PropTypes.func.isRequired,
        showMessage: PropTypes.func.isRequired,
        user: PropTypes.array.isRequired,
        confirmSendImage: PropTypes.func.isRequired,
        process: PropTypes.func.isRequired,
    };

    static defaultProps = {
        me: {},
    };

    canisend() {
        var user = this.props.user;

        if (this.blocking) {
            return false;
        }

        if (user.length === 1
            && user.slice(-1).pop().UserName === this.props.me.UserName) {
            this.props.showMessage('Can\'t send message to yourself.');
            return false;
        }

        return true;
    }

    // Prevent duplicate message
    blocking = false;

    async handleEnter(e) {
        var message = this.refs.input.value.trim();
        var user = this.props.user;
        var batch = user.length > 1;

        if (!this.canisend()
            || !message
            || e.charCode !== 13) return;

        this.blocking = true;

        // You can not send message to yourself
        await Promise.all(
            user.filter(e => e.UserName !== this.props.me.UserName).map(async e => {
                let res = await this.props.sendMessage(e, {
                    content: message,
                    type: 1,
                }, true);

                this.refs.input.value = '';

                if (!res) {
                    await this.props.showMessage(batch ? `Send message to ${e.NickName} is failed!` : 'Failed to send message.');
                }

                return true;
            })
        );
        this.blocking = false;
    }

    state = {
        showEmoji: false
    };

    toggleEmoji(show = !this.state.showEmoji) {
        this.setState({
            showEmoji: show,
        });
    }

    writeEmoji(emoji) {
        var input = this.refs.input;

        input.value += `[${emoji}]`;
        input.focus();
    }

    async batchProcess(file) {
        var message;
        var batch = this.props.user.length > 1;
        var receiver = this.props.user.filter(e => e.UserName !== this.props.me.UserName);
        var showMessage = this.props.showMessage;

        if (this.canisend() === false) {
            return;
        }

        for (let user of receiver) {
            if (message) {
                await this.props.sendMessage(user, message, true)
                    .catch(ex => showMessage(`Send message to ${user.NickName} is failed!`));
                continue;
            }

            // Do not repeat upload file, forward the message to another user
            message = await this.props.process(file, user);

            if (message === false) {
                if (batch) {
                    showMessage(`Send message to ${user.NickName} is failed!`);
                    continue;
                }
                // In batch mode just show the failed message
                showMessage('Failed to send image.');
            }
        }
    }

    async handlePaste(e) {
        var args = ipcRenderer.sendSync('file-paste');

        if (args.hasImage && this.canisend()) {
            e.preventDefault();

            if ((await this.props.confirmSendImage(args.filename)) === false) {
                return;
            }

            let parts = [
                new window.Blob([new window.Uint8Array(args.raw.data)], { type: 'image/png' })
            ];
            let file = new window.File(parts, args.filename, {
                lastModified: new Date(),
                type: 'image/png'
            });

            this.batchProcess(file);
        }
    }

    render() {
        var canisend = !!this.props.user.length;

        return (

                    You should choice a contact at first.

                <input
                    id="messageInput"
                    onPaste={e => this.handlePaste(e)}
                    onKeyPress={e => this.handleEnter(e)}
                    placeholder="Type something to send..."
                    readOnly={!canisend}
                    ref="input"
                    type="text" />

                     canisend && this.refs.uploader.click()} />
                     canisend && this.toggleEmoji(true)}
                        style={{
                            color: 'red',
                        }} />

                    <input
                        onChange={e => {
                            this.batchProcess(e.target.files[0]);
                            e.target.value = '';
                        }}
                        ref="uploader"
                        style={{
                            display: 'none',
                        }}
                        type="file" />
                    <Emoji
                        close={e => setTimeout(() => this.toggleEmoji(false), 100)}
                        output={emoji => this.writeEmoji(emoji)}
                        show={this.state.showEmoji} />

        );
    }
}
<commit_msg>Fix #93<commit_after>
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { ipcRenderer } from 'electron';
import clazz from 'classname';

import classes from './style.css';
import Emoji from './Emoji';

export default class MessageInput extends Component {
    static propTypes = {
        me: PropTypes.object,
        sendMessage: PropTypes.func.isRequired,
        showMessage: PropTypes.func.isRequired,
        user: PropTypes.array.isRequired,
        confirmSendImage: PropTypes.func.isRequired,
        process: PropTypes.func.isRequired,
    };

    static defaultProps = {
        me: {},
    };

    canisend() {
        var user = this.props.user;

        if (this.blocking) {
            return false;
        }

        if (
            true
            && user.length === 1
            && user.slice(-1).pop().UserName === this.props.me.UserName
        ) {
            this.props.showMessage('Can\'t send message to yourself.');
            return false;
        }

        return true;
    }

    // Prevent duplicate message
    blocking = false;

    async handleEnter(e) {
        var message = this.refs.input.value.trim();
        var user = this.props.user;
        var batch = user.length > 1;

        if (
            false
            || !this.canisend()
            || !message
            || e.charCode !== 13
        ) return;

        this.blocking = true;

        // You can not send message to yourself
        await Promise.all(
            user.filter(e => e.UserName !== this.props.me.UserName).map(
                async e => {
                    let res = await this.props.sendMessage(
                        e,
                        {
                            content: message,
                            type: 1,
                        },
                        true
                    );

                    this.refs.input.value = '';

                    if (!res) {
                        await this.props.showMessage(batch ? `Send message to ${e.NickName} is failed!` : 'Failed to send message.');
                    }

                    return true;
                }
            )
        );
        this.blocking = false;
    }

    state = {
        showEmoji: false
    };

    toggleEmoji(show = !this.state.showEmoji) {
        this.setState({ showEmoji: show });
    }

    writeEmoji(emoji) {
        var input = this.refs.input;

        input.value += `[${emoji}]`;
        input.focus();
    }

    async batchProcess(file) {
        var message;
        var batch = this.props.user.length > 1;
        var receiver = this.props.user.filter(e => e.UserName !== this.props.me.UserName);
        var showMessage = this.props.showMessage;

        if (this.canisend() === false) {
            return;
        }

        for (let user of receiver) {
            if (message) {
                await this.props.sendMessage(user, message, true)
                    .catch(ex => showMessage(`Send message to ${user.NickName} is failed!`));
                continue;
            }

            // Do not repeat upload file, forward the message to another user
            message = await this.props.process(file, user);

            if (message === false) {
                if (batch) {
                    showMessage(`Send message to ${user.NickName} is failed!`);
                    continue;
                }
                // In batch mode just show the failed message
                showMessage('Failed to send image.');
            }
        }
    }

    async handlePaste(e) {
        var args = ipcRenderer.sendSync('file-paste');

        if (args.hasImage && this.canisend()) {
            e.preventDefault();

            if ((await this.props.confirmSendImage(args.filename)) === false) {
                return;
            }

            let parts = [
                new window.Blob([new window.Uint8Array(args.raw.data)], { type: 'image/png' })
            ];
            let file = new window.File(parts, args.filename, {
                lastModified: new Date(),
                type: 'image/png'
            });

            this.batchProcess(file);
        }
    }

    componentWillReceiveProps(nextProps) {
        var input = this.refs.input;

        // When user has changed clear the input
        if (
            true
            && input
            && input.value
            && this.props.user.map(e => e.UserName).join() !== nextProps.user.map(e => e.UserName).join()
        ) {
            input.value = '';
        }
    }

    render() {
        var canisend = !!this.props.user.length;

        return (

                    You should choice a contact at first.

                <input
                    id="messageInput"
                    ref="input"
                    type="text"
                    placeholder="Type something to send..."
                    readOnly={!canisend}
                    onPaste={e => this.handlePaste(e)}
                    onKeyPress={e => this.handleEnter(e)}
                />

                     canisend && this.refs.uploader.click()}
                    />

                     canisend && this.toggleEmoji(true)}
                        style={{
                            color: 'red',
                        }}
                    />

                    <input
                        onChange={e => {
                            this.batchProcess(e.target.files[0]);
                            e.target.value = '';
                        }}
                        ref="uploader"
                        style={{
                            display: 'none',
                        }}
                        type="file"
                    />

                    <Emoji
                        close={e => setTimeout(() => this.toggleEmoji(false), 100)}
                        output={emoji => this.writeEmoji(emoji)}
                        show={this.state.showEmoji}
                    />

        );
    }
}
<|endoftext|>


<commit_before>/**
# T5.zoomable(view, params)
This mixin is used to make an object support integer zoom levels which are
implemented when the view scales
*/
function zoomable(view, params) {
    params = COG.extend({
        initial: 1,
        min: 0,
        max: null,
        zoomAnimation: COG.easing('quad.out')
    }, params);

    // initialise variables
    var zoomLevel = params.initial;

    /* internal functions */

    function handleDoubleTap(evt, absXY, relXY) {
        if (view.scalable()) {

        } // if
    } // handleDoubleTap

    function handleScale(evt, scaleAmount, zoomXY) {
        var zoomChange = Math.log(scaleAmount) / Math.LN2;

        // cancel any current animations
        // TODO: review if there is a better place to do this
        COG.endTweens(function(tweenInstance) {
            return tweenInstance.cancelOnInteract;
        });

        evt.cancel = ! setZoomLevel(zoomLevel + zoomChange, zoomXY);
        if (! evt.cancel) {
            view.updateOffset(zoomXY.x * scaleAmount, zoomXY.y * scaleAmount);
        } // if
    } // handleScale

    /* exports */

    /**
    ### getZoomLevel()
    Get the current zoom level for the map
    */
    function getZoomLevel() {
        return zoomLevel;
    } // getZoomLevel

    /**
    ### setZoomLevel(value)
    Update the map's zoom level to the specified zoom level
    */
    function setZoomLevel(value, zoomXY) {
        if (value && (zoomLevel !== value)) {
            // trigger the zoom level change
            var zoomOK = view.triggerAll('zoomLevelChange', value, zoomXY);

            // update the zoom level
            if (zoomOK) {
                zoomLevel = value;
            } // if

            return zoomOK;
        } // if
    } // setZoomLevel

    // apply the mixin
    COG.extend(view, {
        getZoomLevel: getZoomLevel,
        setZoomLevel: setZoomLevel
    });

    // handle scale events
    view.bind('scale', handleScale);
};
<commit_msg>Tweaks to zoom handling<commit_after>/**
# T5.zoomable(view, params)
This mixin is used to make an object support integer zoom levels which are
implemented when the view scales
*/
function zoomable(view, params) {
    params = COG.extend({
        initial: 1,
        min: 0,
        max: null
    }, params);

    // initialise variables
    var zoomLevel = params.initial;

    /* internal functions */

    function handleScale(evt, scaleAmount, zoomXY) {
        var zoomChange = Math.log(scaleAmount) / Math.LN2;

        // cancel any current animations
        // TODO: review if there is a better place to do this
        COG.endTweens(function(tweenInstance) {
            return tweenInstance.cancelOnInteract;
        });

        evt.cancel = ! setZoomLevel(zoomLevel + zoomChange, zoomXY);
        if (! evt.cancel) {
            view.updateOffset(zoomXY.x * scaleAmount, zoomXY.y * scaleAmount);
        } // if
    } // handleScale

    /* exports */

    /**
    ### getZoomLevel()
    Get the current zoom level for the map
    */
    function getZoomLevel() {
        return zoomLevel;
    } // getZoomLevel

    /**
    ### setZoomLevel(value)
    Update the map's zoom level to the specified zoom level
    */
    function setZoomLevel(value, zoomXY) {
        if (value && (zoomLevel !== value)) {
            // trigger the zoom level change
            var zoomOK = view.triggerAll('zoomLevelChange', value, zoomXY);

            // update the zoom level
            if (zoomOK) {
                zoomLevel = value;
            } // if

            return zoomOK;
        } // if
    } // setZoomLevel

    // apply the mixin
    COG.extend(view, {
        getZoomLevel: getZoomLevel,
        setZoomLevel: setZoomLevel
    });

    // handle scale events
    view.bind('scale', handleScale);
};
<|endoftext|>


<commit_before>import React from 'react';
import ReactDOM from 'react-dom';
import './global.css'
import './index.css'
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { square, cube } from './features/treeShake';

ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();

cube(5)

console.log('\nindex.js\n', {
  process: process,
  'process.env': process.env,
})
<commit_msg>yhtml5-scripts: update demo<commit_after>import React from 'react';
import ReactDOM from 'react-dom';
import './global.css'
import './index.css'
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { square, cube } from './features/treeShake';
// const { square, cube } = require('./features/treeShake');

ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();

cube(5)

console.log('\nindex.js\n', {
  process: process,
  'process.env': process.env,
})
<|endoftext|>


<commit_before>
function NotificationViewModel(category, message) {
  var self = this;
  self.CATEGORY = category;
  assert(category != "balances" && category != "debits" && category != "credits",
    "Trying to notify on a category that we don't notify on: " + category);
  self.WHEN = new Date().getTime();
  self.ICON_CLASS =  NotificationViewModel.calcIconClass(category);
  self.COLOR_CLASS =  NotificationViewModel.calcColorClass(category);
  self.MESSAGE = message;
  self.MESSAGE_TEXT = NotificationViewModel.calcText(category, message);

}
NotificationViewModel.calcIconClass = function(category) {
  /*
   * Possible categories:
   * user: Misc user notification (not critical)
   * alert: Something to alert the user to at a notification level
   * security: Security-related notification
   *
   * Beyond this, any one of the valid message category types:
   * credits, debits, orders, bets, broadcasts, etc
   */
  if(category == 'user') return 'fa-user';
  if(category == 'alert') return 'fa-exclamation';
  if(category == 'security') return 'fa-shield';
  return ENTITY_ICONS[category] ? ENTITY_ICONS[category] : 'fa-question';
}
NotificationViewModel.calcColorClass = function(category) {
  if(category == 'user') return 'bg-color-lighten';
  if(category == 'alert') return 'bg-color-redLight';
  if(category == 'security') return 'bg-color-redLight';
  return ENTITY_NOTO_COLORS[category] ? ENTITY_NOTO_COLORS[category] : 'bg-color-white';
}
NotificationViewModel.calcText = function(category, message) {
  //Run through this function only once for each notification -- when the notification is initially received (and
  // before the underlying wallet/asset/whatever state has been modified, so that we can compare new to existing states
  // -- if we need to -- to be able to see what changed, which is important in the case of asset issuances, for instance)
  var desc = "";

  if(category == "sends") {
    if(WALLET.getAddressObj(message['source']) && WALLET.getAddressObj(message['destination'])) {
      desc = "You transferred <Am>" + smartFormat(normalizeQuantity(message['quantity'], message['_divisible']))
        + "</Am> <As>" + message['asset'] + "</As> from <Ad>" + getAddressLabel(message['source'])
        + "</Ad> to <Ad>" + getAddressLabel(message['destination']) + "</Ad>";
    } else if(WALLET.getAddressObj(message['source'])) { //we sent funds
      desc = "You sent <Am>" + smartFormat(normalizeQuantity(message['quantity'], message['_divisible']))
        + "</Am> <As>" + message['asset'] + "</As> from <Ad>" + getAddressLabel(message['source'])
        + "</Ad> to address <Ad>" +  getAddressLabel(message['destination']) + "</Ad>";
    } else if(WALLET.getAddressObj(message['destination'])) { //we received funds
      desc = "You received <Am>"
        + smartFormat(normalizeQuantity(message['quantity'], message['_divisible'])) + "</Am> <As>" + message['asset']
        + "</As> from <Ad>" +  getAddressLabel(message['source'])
        + "</Ad> to your address <Ad>" +  getAddressLabel(message['destination']) + "</Ad>";
    }
  } else if(category == "btcpays" && (WALLET.getAddressObj(message['source']) || WALLET.getAddressObj(message['destination']))) {
    desc = "BTCPay from <Ad>" + getAddressLabel(message['source']) + "</Ad> to <Ad>" + getAddressLabel(message['destination'])
      + "</Ad> for <Am>" + smartFormat(normalizeQuantity(message['btc_amount'])) + "</Am> <As>BTC</As>";
  } else if(category == "burns" && WALLET.getAddressObj(message['source'])) {
    desc = "Your address <Ad>" + getAddressLabel(message['source']) + "</Ad> has burned <Am>" + smartFormat(normalizeQuantity(message['burned']))
      + "</Am> <As>BTC</As> for <Am>" + smartFormat(normalizeQuantity(message['earned'])) + "</Am> <As>XCP</As>";
  } else if(category == "cancels" && WALLET.getAddressObj(message['source'])) {
    desc = "Order/Bid ID " + message['tx_index'] + " for your address <Ad>" + getAddressLabel(message['source']) + "</Ad> was cancelled";
  } else if(category == "callbacks" || category == "dividend") {
    //See if any of our addresses own any of the specified asset, and if so, notify them of the callback or dividend
    // NOTE that counterpartyd has automatically already adusted the balances of all asset holders...we just need to notify
    var addressesWithAsset = WALLET.getAddressesWithAsset(message['asset']);
    if(!addressesWithAsset.length) return;
    if(category == "callbacks") {
      desc = "<As>XCP</As> balance adjusted on your address(es) <Ad>" + addressesWithAsset.join(', ')
        + "</Ad> due to <Am>" + (parseFloat(message['fraction']) * 100).toString()
        + "%</Am> callback option being exercised for token <As>" + message['asset'] + "</As>";
    } else {
      desc = "<As>" + message['dividend_asset'] + "</As> balance adjusted on your address(es) <Ad>" + addressesWithAsset.join(', ')
        + "</Ad> due to <Am>" + message['quantity_per_unit'] + "</Am> distribution being issued for token <As>" + message['asset'] + "</As>";
    }
  } else if(category == 'issuances') {
    var addresses = WALLET.getAddressesList();
    var assetObj = null;
    var addressesWithAsset = WALLET.getAddressesWithAsset(message['asset']);
    if(addressesWithAsset.length)
      assetObj = WALLET.getAddressObj(addressesWithAsset[0]).getAssetObj(message['asset']);

    if(message['transfer']) {
      //Detect transfers, whether we currently have the object in our wallet or not (as it could be
      // a transfer FROM an address outside of our wallet)
      if(addresses.indexOf(message['source'])!=-1 || addresses.indexOf(message['issuer'])!=-1) {
        desc = "Token <As>" + message['asset'] + "</As> was transferred from <Ad>"
          + getLinkForCPData('address', message['source'], getAddressLabel(message['source'])) + "</Ad> to <Ad>"
          + getLinkForCPData('address', message['issuer'], getAddressLabel(message['issuer'])) + "</Ad>";
      }
    } else if(assetObj) { //the address is in the wallet
      //Detect everything else besides transfers, which we only care to see if the asset is listed in one of the wallet addresses
      if(message['locked']) {
        assert(!assetObj.locked());
        desc = "Token <As>" + message['asset'] + "</As> was locked against additional issuance";
      } else if(message['description'] != assetObj.description()) {
        desc = "Token <As>" + message['asset'] + "</As> had its description changed from " + assetObj.description()
          + " to " + message['description'] + "";
      } else {
        var additionalQuantity = message['quantity'] - assetObj.rawSupply();
        if(additionalQuantity) {
          desc = "Additional <Am>" + smartFormat(normalizeQuantity(additionalQuantity, assetObj.DIVISIBLE))
            + "</Am> units issued for token <As>" + message['asset'] + "</As>";
        } else {
          //this is not a transfer, but it is not in our wallet as well we can assume it's an issuance of a totally new asset
          desc = "Token <As>" + message['asset'] + "</As> was issued with an initial quantity of <Am>"
            + smartFormat(normalizeQuantity(message['quantity'], message['divisible'])) + "</Am> units";
        }
      }
    }
  } else if(category == "orders" && WALLET.getAddressObj(message['source'])) {
    desc = "Your order to buy <Am>" + smartFormat(normalizeQuantity(message['get_quantity'], message['_get_asset_divisible']))
      + "</Am> <As>" + message['get_asset'] + "</As> from <Ad>" + getAddressLabel(message['source'])
      + "</Ad> in exchange for <Am>" + smartFormat(normalizeQuantity(message['give_quantity'], message['_give_asset_divisible']))
      + "</Am> <As>" + message['give_asset'] + "</As> is active";
  } else if(category == "order_matches" && (WALLET.getAddressObj(message['tx0_address']) || WALLET.getAddressObj(message['tx1_address']))) {
    desc = "Order matched between <Ad>"
      + getAddressLabel(message['tx0_address']) + "</Ad> (gave <Am>"
      + smartFormat(normalizeQuantity(message['forward_quantity'], message['_forward_asset_divisible'])) + "</Ad> <As>" + message['forward_asset'] + "</As>) and <Ad>"
      + getAddressLabel(message['tx1_address']) + "</Ad> (gave <Am>"
      + smartFormat(normalizeQuantity(message['backward_quantity'], message['_backward_asset_divisible'])) + "</Ad> <As>" + message['backward_asset'] + "</As>)";
  } else if(category == "order_expirations" && WALLET.getAddressObj(message['source'])) {
    desc = "Your order ID " + message['order_index'] + " from address <Ad>" + getAddressLabel(message['source']) + "</Ad> has expired";
  } else if(category == "order_match_expirations") {
    if(WALLET.getAddressObj(message['tx0_address']) && WALLET.getAddressObj(message['tx1_address'])) {
      desc = "An order match between your addresses <Ad>" + getAddressLabel(message['tx0_address'])
        + "</Ad> and <Ad>" + getAddressLabel(message['tx1_address']) + "</Ad> has expired";
    } else if(WALLET.getAddressObj(message['tx0_address'])) {
      desc = "An order match between your address <Ad>" + getAddressLabel(message['tx0_address'])
        + "</Ad> and address <Ad>" + getAddressLabel(message['tx1_address']) + "</Ad> has expired";
    } else if(WALLET.getAddressObj(message['tx1_address'])) {
      desc = "An order match between your address <Ad>" + getAddressLabel(message['tx1_address'])
        + "</Ad> and address <Ad>" + getAddressLabel(message['tx0_address']) + "</Ad> has expired";
    }
  } else if(category == "broadcasts" && WALLET.getAddressObj(message['source'])) {
    if(message['locked']) {
      desc = "You have locked the feed at address <Ad>" + getAddressLabel(message['source']) + "</Ad>";
    } else {
      desc = "You have broadcast value <Am>" + message['value'] + "</Am> from address <Ad>" + getAddressLabel(message['source']) + "</Ad>";
    }
  } else if(category == "bets" && WALLET.getAddressObj(message['source'])) {
    desc = "You bet <Am>" + smartFormat(normalizeQuantity(message['wager_quantity'])) + "</Am> <As>XCP</As> on the feed @"
      + " <Ad>" + getAddressLabel(message['source']) + "</Ad>";
  } else if(category == "bet_matches" && (WALLET.getAddressObj(message['tx0_address']) || WALLET.getAddressObj(message['tx1_address']))) {
    desc = "Bet @ feed <Ad>" + getAddressLabel(message['source']) + "</Ad> matched between <Ad>"
      + getAddressLabel(message['tx0_address']) + "</Ad> (gave <Am>"
      + smartFormat(normalizeQuantity(message['forward_quantity'])) + "</Ad> <As>XCP</As>) and <Ad>"
      + getAddressLabel(message['tx1_address']) + "</Ad> (gave <Am>"
      + smartFormat(normalizeQuantity(message['backward_quantity'])) + "</Ad> <As>XCP</As>)";
  } else if(category == "bet_expirations" && WALLET.getAddressObj(message['source'])) {
    desc = "Your bet ID " + message['bet_index'] + " from address <Ad>" + getAddressLabel(message['source']) + "</Ad> has expired";
  } else if(category == "bet_match_expirations") {
    if(WALLET.getAddressObj(message['tx0_address']) && WALLET.getAddressObj(message['tx1_address'])) {
      desc = "A bet match between your addresses <Ad>" + getAddressLabel(message['tx0_address'])
        + "</Ad> and <Ad>" + getAddressLabel(message['tx1_address']) + "</Ad> has expired";
    } else if(WALLET.getAddressObj(message['tx0_address'])) {
      desc = "A bet match between your address <Ad>" + getAddressLabel(message['tx0_address'])
        + "</Ad> and address <Ad>" + getAddressLabel(message['tx1_address']) + "</Ad> has expired";
    } else if(WALLET.getAddressObj(message['tx1_address'])) {
      desc = "A bet match between your address <Ad>" + getAddressLabel(message['tx1_address'])
        + "</Ad> and address <Ad>" + getAddressLabel(message['tx0_address']) + "</Ad> has expired";
    }
  }

  if(desc) {
    desc = desc.replace(/<Am>/g, '').replace(/<\/Am>/g, '');
    desc = desc.replace(/<Ad>/g, '').replace(/<\/Ad>/g, '');
    desc = desc.replace(/<As>/g, '').replace(/<\/As>/g, '');
  }
  return desc;
}

function NotificationFeedViewModel(initialCount) {
  var self = this;

  self.entries = ko.observableArray([]);
  self.lastUpdated = ko.observable(new Date());
  self.count = ko.observable(initialCount || 0);
  self.unackedCount = ko.observable(initialCount || 0);

  self.ack = function() {
    self.unackedCount(0);
  }

  self.add = function(category, message) {
    if(category == "balances" || category == "debits" || category == "credits") return;
    //^ we don't notify on these categories (since the action is covered by other categories, such as send, which makes these redundant)

    var noto = new NotificationViewModel(category, message);
    if(!noto.MESSAGE_TEXT)
      return; //will be the case if this noto does not apply to this client or is not something this client needs to see

    self.entries.unshift(noto); //add to front of array
    self.unackedCount(self.unackedCount() + 1);
    //if the number of entries are over 40, remove the oldest one
    if(self.entries().length > 40) self.entries.pop();
    self.lastUpdated(new Date());
  }
}

/*NOTE: Any code here is only triggered the first time the page is visited. Put JS that needs to run on the
  first load and subsequent ajax page switches in the .html


<commit_before>angular.module('blocktrail.wallet')
   .controller('WalletCtrl', function($q, $log, $scope, $state, $rootScope, $interval, storageService, sdkService, Wallet,
                                      Contacts, CONFIG, settingsService, $timeout, $ionicUser, launchService) {

        $timeout(function() {
            $rootScope.hideLoadingScreen = true;
        }, 200);
        if (!$rootScope.settings.enablePolling) {
            Wallet.disablePolling();
        }

        $rootScope.getPrice = function() {
            //get a live prices update
            return $q.when(Wallet.price(false).then(function(data) {
                return $rootScope.bitcoinPrices = data;
            }));
        };

        $rootScope.getBlockHeight = function() {
            //get a live block height update (used to calculate confirmations)
            return $q.when(Wallet.blockHeight(false).then(function(data) {
                return $rootScope.blockHeight = data.height;
            }));
        };

        $rootScope.getBalance = function() {
            //get a live balance update
            return $q.when(Wallet.balance(false).then(function(balanceData) {
                if ((balanceData.balance > 0 || balanceData.uncBalance > 0) && !$ionicUser.get().isActive) {
                    $ionicUser.identify({user_id: $ionicUser.get().user_id, isActive: 1});
                    $ionicAnalytics.track('Actived', {});
                }

                $rootScope.balance = balanceData.balance;
                $rootScope.uncBalance = balanceData.uncBalance;
                return {balance: balanceData.balance, uncBalance: balanceData.uncBalance};
            }));
        };

        $rootScope.syncProfile = function() {
            //sync profile if a pending update is present
            if (!settingsService.profileSynced) {
                settingsService.$syncProfileUp();
            }
        };

        $rootScope.syncContacts = function() {
            //sync any changes to contacts
            Contacts.list()
                .catch(function(err) {
                    $log.error(err);
                })
            ;
        };

        // do initial updates then poll for changes, all with small offsets to reducing blocking / slowing down of rendering
        $timeout(function() { $rootScope.syncContacts(); }, 500);
        $timeout(function() { $rootScope.getPrice(); }, 1000);
        $timeout(function() { $rootScope.syncProfile(); }, 2000);

        var pricePolling = $interval(function() {
            $rootScope.getPrice();
        }, 20000);

        var balancePolling = $interval(function() {
            $rootScope.getBalance();
        }, 15000);

        var blockheightPolling = $interval(function() {
            $rootScope.getBlockHeight();
        }, 15500); // slight offset not to collide

        var contactSyncPolling = $interval(function() {
            $rootScope.syncContacts();
        }, 300500); // 5 min + slight offset not to collide

        var profileSyncPolling = $interval(function() {
            $rootScope.syncProfile();
        }, 301500); // 5 min + slight offset not to collide
    }
);

angular.module('blocktrail.wallet')
    .controller('WalletSummaryCtrl', function($scope, $rootScope, $state, $log, $filter, $http, $q, $timeout, Wallet, $translate, $modal) {
        $rootScope.pageTitle = 'TRANSACTIONS';
        // update balance from cache
        $scope.transactionsList         = [];   //original list of transactions
        $scope.transactionsDisplayList  = [];    //transactions with "date headers" inserted
        $scope.isFirstLoad              = true;
        $scope.canLoadMoreTransactions  = true;
        $scope.loading                  = false;
        $scope.paginationOptions = {
            from: 0,
            limit: 15
        };
        $scope.lastDateHeader = 0;      //used to keep track of the last date header added

        $scope.refreshTransactions = function() {
            $log.debug('refresh transactions...');

            //refresh transactions, block height and wallet balance
            $q.all([
                $q.when(Wallet.pollTransactions()),
                $q.when($rootScope.getBlockHeight()),
                $q.when($rootScope.getBalance())
            ]).then(function() {
                $scope.paginationOptions.from = 0;
                $scope.getTransactions($scope.paginationOptions.from, $scope.paginationOptions.limit).then(function() {
                    $scope.loading = false;
                    $scope.isFirstLoad = false;
                });
            });
        };

        $scope.getTransactions = function(from, limit) {
            $log.debug('getTransactions', from, limit);
            //get cached transactions
            return Wallet.transactions(from, limit).then(function(result) {
                $scope.transactionsList = $scope.transactionsList.concat(result);

                $scope.transactionsDisplayList = $scope.groupTransactions($scope.transactionsList);
                $scope.paginationOptions.from += result.length;
                $scope.canLoadMoreTransactions = result.length > 0;

                $log.debug("transactionsDisplayList", $scope.transactionsDisplayList, $scope.transactionsDisplayList.length);
            });
        };

        $scope.loadMoreTransactions = function() {
            $log.debug('loadMoreTransactions', $scope.loading);
            if ($scope.loading) {
                return;
            }

            $scope.loading = true;
            // may need to merge existing data set when getting more results?
            // or else check for existing entries so they aren't added more than once when grouping data
            $scope.getTransactions($scope.paginationOptions.from, $scope.paginationOptions.limit).then(function() {
                $scope.loading = false;
                $scope.isFirstLoad = false;
            });
        };

        $scope.groupTransactions = function(data) {
            $scope.lastDateHeader = 0;

            var groupedList = [];

            data.forEach(function(transaction) {
                var date = null;

                if (transaction.hash) {
                    date = new Date(transaction.time*1000);
                    date.setHours(0);
                    date.setMinutes(0);
                    date.setSeconds(0);
                    date.setMilliseconds(0);
                    date = date.valueOf();

                    //create the header object
                    if (date < $scope.lastDateHeader || $scope.lastDateHeader == 0) {
                        $scope.lastDateHeader = date;
                        var headerObj = {isHeader: true, date: date};
                        groupedList.push(headerObj);
                    }

                    //add a contact token
                    if (transaction.contact) {
                        if (!transaction.contact.lastName && transaction.contact.firstName) {
                            transaction.contactInitials = transaction.contact.firstName.substr(0, 2);
                        } else if (!transaction.contact.firstName && transaction.contact.lastName) {
                            transaction.contactInitials = transaction.contact.lastName.substr(0, 2);
                        } else if (transaction.contact.firstName && transaction.contact.lastName) {
                            transaction.contactInitials = transaction.contact.firstName.substr(0, 1) + transaction.contact.lastName.substr(0, 1);
                        }
                    }

                    if (transaction.wallet_value_change > 0) {
                        //received from anonymous
                        transaction.altDisplay = transaction.txin_other_addresses.length >= 1 && transaction.txin_other_addresses[0];
                        transaction.altDisplay = $translate.instant('TX_INFO_RECEIVED');
                    } else if (transaction.is_internal) {
                        //sent to self
                        transaction.altDisplay = $translate.instant('INTERNAL_TRANSACTION_TITLE');
                    } else {
                        //sent to anonymous
                        transaction.altDisplay = transaction.txout_other_addresses.length >= 1 && transaction.txout_other_addresses[0];
                        transaction.altDisplay = $translate.instant('TX_INFO_SENT');
                    }

                    groupedList.push(transaction);
                }
            });

            return groupedList;
        };

        $scope.showTransaction = function(transaction) {
            $modal.open({
                controller: 'WalletTxInfoCtrl',
                templateUrl: 'templates/wallet/wallet.tx-info.modal.html',
                resolve: {
                    data: function() {
                        return transaction
                    }
                }
            })
        };

        $scope.$on('new_transactions', function(event, transactions) {
            $log.debug('New Transaction have been found!!!', transactions);

            transactions.forEach(function(transaction) {
                $scope.transactionsList.unshift(transaction);
            });
            $scope.$apply(function() {
                $scope.transactionsDisplayList = $scope.groupTransactions($scope.transactionsList);
                $scope.$broadcast('scroll.infiniteScrollComplete');
            });
        });

        $scope.$on('confirmed_transactions', function(event, confirmedTxs) {
            $log.debug('Transactions have been confirmed!!!', confirmedTxs);
            //refresh the txs that have changed (just update the block heights)
            $scope.$apply(function() {
                $scope.transactionsDisplayList.forEach(function(transaction) {
                    $log.debug('checking tx: ' + transaction.hash + ' against ' + confirmedTxs.length);
                    if (!confirmedTxs.length) {
                        return;
                    }
                    confirmedTxs.forEach(function(confirmedTx, index) {
                        if (confirmedTx.hash == transaction.hash) {
                            $log.debug('found and updated!');
                            transaction.block_height = confirmedTx.block_height;
                            //remove from array to speed things up
                            delete confirmedTxs[index];
                        }
                    });
                });
            });
        });

        $scope.$on('ORPHAN', function() {
            $scope.$apply(function() {
                $log.debug('WalletCtrl.ORPHAN');

                $scope.transactionData = [];
                $scope.transactionsDisplayList = [];
                $scope.paginationOptions.from = 0;

                $scope.loadMoreTransactions();
            });
        });

        $scope.refreshTransactions();
    })
;

angular.module('blocktrail.wallet')
    .controller('WalletTxInfoCtrl', function($scope, $modalInstance, data) {
        $scope.data = data;

        $scope.dismiss = function() {
            $modalInstance.dismiss();
        }
    })
;
<commit_msg>use refreshtransactions on orphan instead of custom logic<commit_after>angular.module('blocktrail.wallet')
   .controller('WalletCtrl', function($q, $log, $scope, $state, $rootScope, $interval, storageService, sdkService, Wallet,
                                      Contacts, CONFIG, settingsService, $timeout, $ionicUser, launchService) {

        $timeout(function() {
            $rootScope.hideLoadingScreen = true;
        }, 200);
        if (!$rootScope.settings.enablePolling) {
            Wallet.disablePolling();
        }

        $rootScope.getPrice = function() {
            //get a live prices update
            return $q.when(Wallet.price(false).then(function(data) {
                return $rootScope.bitcoinPrices = data;
            }));
        };

        $rootScope.getBlockHeight = function() {
            //get a live block height update (used to calculate confirmations)
            return $q.when(Wallet.blockHeight(false).then(function(data) {
                return $rootScope.blockHeight = data.height;
            }));
        };

        $rootScope.getBalance = function() {
            //get a live balance update
            return $q.when(Wallet.balance(false).then(function(balanceData) {
                if ((balanceData.balance > 0 || balanceData.uncBalance > 0) && !$ionicUser.get().isActive) {
                    $ionicUser.identify({user_id: $ionicUser.get().user_id, isActive: 1});
                    $ionicAnalytics.track('Actived', {});
                }

                $rootScope.balance = balanceData.balance;
                $rootScope.uncBalance = balanceData.uncBalance;
                return {balance: balanceData.balance, uncBalance: balanceData.uncBalance};
            }));
        };

        $rootScope.syncProfile = function() {
            //sync profile if a pending update is present
            if (!settingsService.profileSynced) {
                settingsService.$syncProfileUp();
            }
        };

        $rootScope.syncContacts = function() {
            //sync any changes to contacts
            Contacts.list()
                .catch(function(err) {
                    $log.error(err);
                })
            ;
        };

        // do initial updates then poll for changes, all with small offsets to reducing blocking / slowing down of rendering
        $timeout(function() { $rootScope.syncContacts(); }, 500);
        $timeout(function() { $rootScope.getPrice(); }, 1000);
        $timeout(function() { $rootScope.syncProfile(); }, 2000);

        var pricePolling = $interval(function() {
            $rootScope.getPrice();
        }, 20000);

        var balancePolling = $interval(function() {
            $rootScope.getBalance();
        }, 15000);

        var blockheightPolling = $interval(function() {
            $rootScope.getBlockHeight();
        }, 15500); // slight offset not to collide

        var contactSyncPolling = $interval(function() {
            $rootScope.syncContacts();
        }, 300500); // 5 min + slight offset not to collide

        var profileSyncPolling = $interval(function() {
            $rootScope.syncProfile();
        }, 301500); // 5 min + slight offset not to collide
    }
);

angular.module('blocktrail.wallet')
    .controller('WalletSummaryCtrl', function($scope, $rootScope, $state, $log, $filter, $http, $q, $timeout, Wallet, $translate, $modal) {
        $rootScope.pageTitle = 'TRANSACTIONS';
        // update balance from cache
        $scope.transactionsList         = [];   //original list of transactions
        $scope.transactionsDisplayList  = [];    //transactions with "date headers" inserted
        $scope.isFirstLoad              = true;
        $scope.canLoadMoreTransactions  = true;
        $scope.loading                  = false;
        $scope.paginationOptions = {
            from: 0,
            limit: 15
        };
        $scope.lastDateHeader = 0;      //used to keep track of the last date header added

        $scope.refreshTransactions = function() {
            $log.debug('refresh transactions...');

            //refresh transactions, block height and wallet balance
            $q.all([
                $q.when(Wallet.pollTransactions()),
                $q.when($rootScope.getBlockHeight()),
                $q.when($rootScope.getBalance())
            ]).then(function() {
                $scope.paginationOptions.from = 0;
                $scope.getTransactions($scope.paginationOptions.from, $scope.paginationOptions.limit).then(function() {
                    $scope.loading = false;
                    $scope.isFirstLoad = false;
                });
            });
        };

        $scope.getTransactions = function(from, limit) {
            $log.debug('getTransactions', from, limit);
            //get cached transactions
            return Wallet.transactions(from, limit).then(function(result) {
                $scope.transactionsList = $scope.transactionsList.concat(result);

                $scope.transactionsDisplayList = $scope.groupTransactions($scope.transactionsList);
                $scope.paginationOptions.from += result.length;
                $scope.canLoadMoreTransactions = result.length > 0;

                $log.debug("transactionsDisplayList", $scope.transactionsDisplayList, $scope.transactionsDisplayList.length);
            });
        };

        $scope.loadMoreTransactions = function() {
            $log.debug('loadMoreTransactions', $scope.loading);
            if ($scope.loading) {
                return;
            }

            $scope.loading = true;
            // may need to merge existing data set when getting more results?
            // or else check for existing entries so they aren't added more than once when grouping data
            $scope.getTransactions($scope.paginationOptions.from, $scope.paginationOptions.limit).then(function() {
                $scope.loading = false;
                $scope.isFirstLoad = false;
            });
        };

        $scope.groupTransactions = function(data) {
            $scope.lastDateHeader = 0;

            var groupedList = [];

            data.forEach(function(transaction) {
                var date = null;

                if (transaction.hash) {
                    date = new Date(transaction.time*1000);
                    date.setHours(0);
                    date.setMinutes(0);
                    date.setSeconds(0);
                    date.setMilliseconds(0);
                    date = date.valueOf();

                    //create the header object
                    if (date < $scope.lastDateHeader || $scope.lastDateHeader == 0) {
                        $scope.lastDateHeader = date;
                        var headerObj = {isHeader: true, date: date};
                        groupedList.push(headerObj);
                    }

                    //add a contact token
                    if (transaction.contact) {
                        if (!transaction.contact.lastName && transaction.contact.firstName) {
                            transaction.contactInitials = transaction.contact.firstName.substr(0, 2);
                        } else if (!transaction.contact.firstName && transaction.contact.lastName) {
                            transaction.contactInitials = transaction.contact.lastName.substr(0, 2);
                        } else if (transaction.contact.firstName && transaction.contact.lastName) {
                            transaction.contactInitials = transaction.contact.firstName.substr(0, 1) + transaction.contact.lastName.substr(0, 1);
                        }
                    }

                    if (transaction.wallet_value_change > 0) {
                        //received from anonymous
                        transaction.altDisplay = transaction.txin_other_addresses.length >= 1 && transaction.txin_other_addresses[0];
                        transaction.altDisplay = $translate.instant('TX_INFO_RECEIVED');
                    } else if (transaction.is_internal) {
                        //sent to self
                        transaction.altDisplay = $translate.instant('INTERNAL_TRANSACTION_TITLE');
                    } else {
                        //sent to anonymous
                        transaction.altDisplay = transaction.txout_other_addresses.length >= 1 && transaction.txout_other_addresses[0];
                        transaction.altDisplay = $translate.instant('TX_INFO_SENT');
                    }

                    groupedList.push(transaction);
                }
            });

            return groupedList;
        };

        $scope.showTransaction = function(transaction) {
            $modal.open({
                controller: 'WalletTxInfoCtrl',
                templateUrl: 'templates/wallet/wallet.tx-info.modal.html',
                resolve: {
                    data: function() {
                        return transaction
                    }
                }
            })
        };

        $scope.$on('new_transactions', function(event, transactions) {
            $log.debug('New Transaction have been found!!!', transactions);

            transactions.forEach(function(transaction) {
                $scope.transactionsList.unshift(transaction);
            });
            $scope.$apply(function() {
                $scope.transactionsDisplayList = $scope.groupTransactions($scope.transactionsList);
                $scope.$broadcast('scroll.infiniteScrollComplete');
            });
        });

        $scope.$on('confirmed_transactions', function(event, confirmedTxs) {
            $log.debug('Transactions have been confirmed!!!', confirmedTxs);
            //refresh the txs that have changed (just update the block heights)
            $scope.$apply(function() {
                $scope.transactionsDisplayList.forEach(function(transaction) {
                    $log.debug('checking tx: ' + transaction.hash + ' against ' + confirmedTxs.length);
                    if (!confirmedTxs.length) {
                        return;
                    }
                    confirmedTxs.forEach(function(confirmedTx, index) {
                        if (confirmedTx.hash == transaction.hash) {
                            $log.debug('found and updated!');
                            transaction.block_height = confirmedTx.block_height;
                            //remove from array to speed things up
                            delete confirmedTxs[index];
                        }
                    });
                });
            });
        });

        $scope.$on('ORPHAN', function() {
            $timeout(function() {
                $log.debug('WalletCtrl.ORPHAN');

                $scope.refreshTransactions();
            });
        });

        $scope.refreshTransactions();
    })
;

angular.module('blocktrail.wallet')
    .controller('WalletTxInfoCtrl', function($scope, $modalInstance, data) {
        $scope.data = data;

        $scope.dismiss = function() {
            $modalInstance.dismiss();
        }
    })
;
<|endoftext|>


<commit_before>/**
 * Avalara © 2017
 * file: test/companies.spec.js
 */

import Avatax from '../lib/AvaTaxClient';
import { v4 } from 'node-uuid';
import loadCreds from './helpers/load_creds';
import nock from 'nock';
import companyGetResponse from './fixtures/company_get_response';
import companiesListResponse from './fixtures/companies_list_response';

describe('Company Integration Tests', () => {
  describe('Valid company initialize request', () => {
    const clientCreds = loadCreds();
    const client = new Avatax(clientCreds).withSecurity(clientCreds);

    it('should initialize a company', () => {
      const request = {
        name: "Bob's Artisan Pottery",
        companyCode: v4().replace(/-/gi, '').substring(0, 8),
        taxpayerIdNumber: '12-3456789',
        line1: '123 Main Street',
        city: 'Irvine',
        region: 'CA',
        postalCode: '92615',
        country: 'US',
        firstName: 'Bob',
        lastName: 'Example',
        title: 'Owner',
        email: 'bob@example.org',
        phoneNumber: '714 555-2121',
        mobileNumber: '714 555-1212'
      };

      return client.companyInitialize({ model: request }).then(res => {
        expect(res).toBeDefined();
        expect(res.contacts.length).toEqual(1);
        expect(res.locations.length).toEqual(1);
        expect(res.nexus.length).toEqual(3);
      });
    });

    describe('Invalid company initialize request', () => {
      it('should return valid exception response');
    });
  });
});

describe('Company Unit Tests', () => {
  const clientCreds = loadCreds();
  const baseUrl = 'https://sandbox-rest.avatax.com';
  const client = new Avatax(clientCreds).withSecurity(clientCreds);

  afterEach(() => {
    nock.cleanAll();
  });

  describe('Get company by id', () => {
    const id = 12345;

    beforeEach(() => {
      nock(baseUrl)
        .get(`/api/v2/companies/${id}`)
        .reply(200, companyGetResponse);
    });

    it('should return single company', () => {
      return client.getCompany({ id }).then(res => {
        expect(res).toEqual(companyGetResponse);
      });
    });
  });

  describe('Listing companies for account', () => {
    beforeEach(() => {
      nock(baseUrl).get(`/api/v2/companies`).reply(200, companiesListResponse);
    });

    it('should return list of companies', () => {
      return client.queryCompanies().then(res => {
        expect(res).toEqual(companiesListResponse);
      });
    });
  });
});
<commit_msg>test update<commit_after>/**
 * Avalara © 2017
 * file: test/companies.spec.js
 */

import Avatax from '../lib/AvaTaxClient';
import { v4 } from 'node-uuid';
import loadCreds from './helpers/load_creds';
import nock from 'nock';
import companyGetResponse from './fixtures/company_get_response';
import companiesListResponse from './fixtures/companies_list_response';

describe('Company Integration Tests', () => {
  describe('Valid company initialize request', () => {
    const clientCreds = loadCreds();
    const client = new Avatax(clientCreds).withSecurity(clientCreds);

    it('should initialize a company', () => {
      const request = {
        name: "Bob's Artisan Pottery",
        companyCode: v4().replace(/-/gi, '').substring(0, 8),
        taxpayerIdNumber: '12-3456789',
        line1: '123 Main Street',
        city: 'Irvine',
        region: 'CA',
        postalCode: '92615',
        country: 'US',
        firstName: 'Bob',
        lastName: 'Example',
        title: 'Owner',
        email: 'bob@example.org',
        phoneNumber: '714 555-2121',
        mobileNumber: '714 555-1212'
      };

      return client.companyInitialize({ model: request }).then(res => {
        expect(res).toBeDefined();
        expect(res.contacts.length).toEqual(1);
        expect(res.locations.length).toEqual(1);
        expect(res.nexus.length).toEqual(2);
      });
    });

    describe('Invalid company initialize request', () => {
      it('should return valid exception response');
    });
  });
});

describe('Company Unit Tests', () => {
  const clientCreds = loadCreds();
  const baseUrl = 'https://sandbox-rest.avatax.com';
  const client = new Avatax(clientCreds).withSecurity(clientCreds);

  afterEach(() => {
    nock.cleanAll();
  });

  describe('Get company by id', () => {
    const id = 12345;

    beforeEach(() => {
      nock(baseUrl)
        .get(`/api/v2/companies/${id}`)
        .reply(200, companyGetResponse);
    });

    it('should return single company', () => {
      return client.getCompany({ id }).then(res => {
        expect(res).toEqual(companyGetResponse);
      });
    });
  });

  describe('Listing companies for account', () => {
    beforeEach(() => {
      nock(baseUrl).get(`/api/v2/companies`).reply(200, companiesListResponse);
    });

    it('should return list of companies', () => {
      return client.queryCompanies().then(res => {
        expect(res).toEqual(companiesListResponse);
      });
    });
  });
});
<|endoftext|>


<commit_before>import {isTargetType,hasAnyNullOrUndefined} from './util';
import 'reflect-metadata';
const JSON_META_DATA_KEY = 'JsonProperty';

var DecoratorMetaData = (function () {
    function DecoratorMetaData(name, clazz) {
        this.name = name;
        this.clazz = clazz;
    }
    return DecoratorMetaData;
}());

function JsonProperty(metadata) {
    let decoratorMetaData;
    if (isTargetType(metadata, 'string')) {
        decoratorMetaData = new DecoratorMetaData(metadata);
    }
    else if (isTargetType(metadata, 'object')) {
        decoratorMetaData = metadata;
    }
    else {
        throw new Error('meta data in Json property is undefined. meta data: ' + metadata);
    }
    return Reflect.metadata(JSON_META_DATA_KEY, decoratorMetaData);
}

function getJsonProperty(target, propertyKey) {
    return Reflect.getMetadata(JSON_META_DATA_KEY, target, propertyKey);

}

function mapFromJson(decoratorMetadata, instance, json, key) {
    /**
     * if decorator name is not found, use target property key as decorator name. It means mapping it directly
     */
    let decoratorName = decoratorMetadata.name || key;
    let innerJson = json ? json[decoratorName] : undefined;
    if (innerJson !== undefined && innerJson !== null) {
        if (decoratorMetadata.clazz && innerJson.constructor === Array) {
            let metadata = getJsonProperty(instance, key);
            if (metadata && metadata.clazz) {
                if (innerJson) {
                    return innerJson.map(
                        (item) => MapperEntity(metadata.clazz, item)
                    );
                }
                return;
            } else {
                return innerJson;
            }
        }
        if (decoratorMetadata.clazz&&typeof innerJson==='object') {
            return MapperEntity(decoratorMetadata.clazz, innerJson);
        }
    }
    return json ? json[decoratorName] : undefined;
}
function MapperEntity(Clazz,json) {
    if (hasAnyNullOrUndefined(Clazz, json)) {
        return void 0;
    }
    /**
     * Prevent non-json continue
     */
    if (!isTargetType(json, 'object')) {
        return void 0;
    }

    /**
     * init root class to contain json
     */

    let instance = new Clazz();

    Object.keys(instance).forEach((key) => {
        /**
         * get decoratorMetaData, structure: { name?:string, clazz?:{ new():T } }
         */
        let decoratorMetaData = getJsonProperty(instance, key);
        /**
         * pass value to instance
         */
        instance[key] = decoratorMetaData ? mapFromJson(decoratorMetaData, instance, json, key) : json[key];
    });

    return instance;
}
module.exports={
    MapperEntity,
    JsonProperty
}<commit_msg>feature : 增加默认值<commit_after>import {isTargetType,hasAnyNullOrUndefined} from './util';
import 'reflect-metadata';
const JSON_META_DATA_KEY = 'JsonProperty';

var DecoratorMetaData = (function () {
    function DecoratorMetaData(name, clazz) {
        this.name = name;
        this.clazz = clazz;
    }
    return DecoratorMetaData;
}());

function JsonProperty(metadata) {
    let decoratorMetaData;
    if (isTargetType(metadata, 'string')) {
        decoratorMetaData = new DecoratorMetaData(metadata);
    }
    else if (isTargetType(metadata, 'object')) {
        decoratorMetaData = metadata;
    }
    else {
        throw new Error('meta data in Json property is undefined. meta data: ' + metadata);
    }
    return Reflect.metadata(JSON_META_DATA_KEY, decoratorMetaData);
}

function getJsonProperty(target, propertyKey) {
    return Reflect.getMetadata(JSON_META_DATA_KEY, target, propertyKey);

}

function mapFromJson(decoratorMetadata, instance, json, key) {
    /**
     * if decorator name is not found, use target property key as decorator name. It means mapping it directly
     */
    let decoratorName = decoratorMetadata.name || key;
    let innerJson = json ? json[decoratorName] : instance[key];
    if (innerJson !== undefined && innerJson !== null) {
        if (decoratorMetadata.clazz && innerJson.constructor === Array) {
            let metadata = getJsonProperty(instance, key);
            if (metadata && metadata.clazz) {
                if (innerJson) {
                    return innerJson.map(
                        (item) => MapperEntity(metadata.clazz, item)
                    );
                }
                return;
            } else {
                return innerJson;
            }
        }
        if (decoratorMetadata.clazz&&typeof innerJson==='object') {
            return MapperEntity(decoratorMetadata.clazz, innerJson);
        }
    }
    return json ? json[decoratorName] : undefined;
}
function MapperEntity(Clazz,json) {
    if (hasAnyNullOrUndefined(Clazz, json)) {
        return void 0;
    }
    /**
     * Prevent non-json continue
     */
    if (!isTargetType(json, 'object')) {
        return void 0;
    }

    /**
     * init root class to contain json
     */

    let instance = new Clazz();

    Object.keys(instance).forEach((key) => {
        /**
         * get decoratorMetaData, structure: { name?:string, clazz?:{ new():T } }
         */
        let decoratorMetaData = getJsonProperty(instance, key);
        //console.log(decoratorMetaData)
        /**
         * pass value to instance
         */
        instance[key] = decoratorMetaData ? mapFromJson(decoratorMetaData, instance, json, key) : (json[key]||instance[key]);
    });

    return instance;
}
module.exports={
    MapperEntity,
    JsonProperty
}<|endoftext|>


<commit_before>'use strict';

angular.module('copayApp.controllers').controller('preferencesBwsUrlController',
  function($scope, configService, isMobile, isCordova, go,  applicationService ) {
    this.isSafari = isMobile.Safari();
    this.isCordova = isCordova;
    this.error = null;
    this.success = null;

    var config = configService.getSync();

    this.bwsurl = config.bws.url;

    this.save = function() {
      var opts = {
        bws: {
          url: this.bwsurl,
        }
      };

      configService.set(opts, function(err) {
        if (err) console.log(err);
        $scope.$emit('Local/BWSUpdated');
        applicationService.restart();
      });
    };

  });
<commit_msg>add devel aliases for BWS urls<commit_after>'use strict';

angular.module('copayApp.controllers').controller('preferencesBwsUrlController',
  function($scope,$log, configService, isMobile, isCordova, go,  applicationService ) {
    this.isSafari = isMobile.Safari();
    this.isCordova = isCordova;
    this.error = null;
    this.success = null;

    var config = configService.getSync();

    this.bwsurl = config.bws.url;

    this.save = function() {

      var bws;
      switch (this.bwsurl) {
        case 'prod':
        case 'production':
          bws = 'https://bws.bitpay.com/bws/api'
          break;
        case 'sta':
        case 'staging':
          bws = 'https://bws-staging.b-pay.net/bws/api'
          break;
        case 'loc':
        case 'local':
          bws = 'http://localhost:3232/bws/api'
          break;
      };
      if (bws) {
        $log.info('Using BWS URL Alias to ' + bws);
        this.bwsurl = bws;
      }

      var opts = {
        bws: {
          url: this.bwsurl,
        }
      };

      configService.set(opts, function(err) {
        if (err) console.log(err);
        $scope.$emit('Local/BWSUpdated');
        applicationService.restart();
      });
    };

  });
<|endoftext|>


<commit_before>/**
 * This file is mainly copied from the default BlockMover component, there are some small differences.
 * The arrows' labels are changed and are switched. Also, dragging is enabled even if the element is the only block.
 **/

/**
 * External dependencies
 */
import { first, partial, castArray } from 'lodash';
import classnames from 'classnames';

/**
 * WordPress dependencies
 */
import { __ } from '@wordpress/i18n';
import { IconButton } from '@wordpress/components';
import { getBlockType } from '@wordpress/blocks';
import { Component } from '@wordpress/element';
import { withSelect, withDispatch } from '@wordpress/data';
import { withInstanceId, compose } from '@wordpress/compose';

/**
 * Internal dependencies
 */
import { upArrow, downArrow, dragHandle } from './icons';
import { IconDragHandle } from './drag-handle';
import IgnoreNestedEvents from './ignore-nested-events';
import './edit.css';

export class BlockMover extends Component {
	constructor() {
		super( ...arguments );
		this.state = {
			isFocused: false,
		};
		this.onFocus = this.onFocus.bind( this );
		this.onBlur = this.onBlur.bind( this );
	}

	onFocus() {
		this.setState( {
			isFocused: true,
		} );
	}

	onBlur() {
		this.setState( {
			isFocused: false,
		} );
	}

	render() {
		const { bringForward, sendBackward, isFirst, isLast, isDraggable, onDragStart, onDragEnd, clientIds, blockElementId, instanceId } = this.props;
		const { isFocused } = this.state;

		// We emulate a disabled state because forcefully applying the `disabled`
		// attribute on the button while it has focus causes the screen to change
		// to an unfocused state (body as active element) without firing blur on,
		// the rendering parent, leaving it unable to react to focus out.
		return (
			<IgnoreNestedEvents childHandledEvents={ [ 'onDragStart', 'onMouseDown' ] }>

					<IconButton
						className="editor-block-mover__control block-editor-block-mover__control"
						onClick={ isFirst ? null : bringForward }
						icon={ upArrow }
						label={ __( 'Bring Forward', 'amp' ) }
						aria-describedby={ `editor-block-mover__up-description-${ instanceId }` }
						aria-disabled={ isFirst }
						onFocus={ this.onFocus }
						onBlur={ this.onBlur }
					/>
					<IconDragHandle
						className="editor-block-mover__control block-editor-block-mover__control"
						icon={ dragHandle }
						clientId={ clientIds }
						blockElementId={ blockElementId }
						isVisible={ isDraggable }
						onDragStart={ onDragStart }
						onDragEnd={ onDragEnd }
					/>
					<IconButton
						className="editor-block-mover__control block-editor-block-mover__control"
						onClick={ isLast ? null : sendBackward }
						icon={ downArrow }
						label={ __( 'Send Backward', 'amp' ) }
						aria-describedby={ `editor-block-mover__down-description-${ instanceId }` }
						aria-disabled={ isLast }
						onFocus={ this.onFocus }
						onBlur={ this.onBlur }
					/>

			</IgnoreNestedEvents>
		);
	}
}

export default compose(
	withSelect( ( select, { clientIds } ) => {
		const { getBlock, getBlockIndex, getTemplateLock, getBlockRootClientId } = select( 'core/block-editor' );
		const firstClientId = first( castArray( clientIds ) );
		const block = getBlock( firstClientId );
		const rootClientId = getBlockRootClientId( first( castArray( clientIds ) ) );

		return {
			firstIndex: getBlockIndex( firstClientId, rootClientId ),
			blockType: block ? getBlockType( block.name ) : null,
			isLocked: getTemplateLock( rootClientId ) === 'all',
			rootClientId,
		};
	} ),
	withDispatch( ( dispatch, { clientIds, rootClientId } ) => {
		const { moveBlocksDown, moveBlocksUp } = dispatch( 'core/block-editor' );
		return {
			bringForward: partial( moveBlocksDown, clientIds, rootClientId ),
			sendBackward: partial( moveBlocksUp, clientIds, rootClientId ),
		};
	} ),
	withInstanceId,
)( BlockMover );
<commit_msg>Clear selected block after rotation to make result immediately visible<commit_after>/**
 * This file is mainly copied from the default BlockMover component, there are some small differences.
 * The arrows' labels are changed and are switched. Also, dragging is enabled even if the element is the only block.
 **/

/**
 * External dependencies
 */
import { first, partial, castArray } from 'lodash';
import classnames from 'classnames';

/**
 * WordPress dependencies
 */
import { __ } from '@wordpress/i18n';
import { IconButton } from '@wordpress/components';
import { getBlockType } from '@wordpress/blocks';
import { Component } from '@wordpress/element';
import { withSelect, withDispatch } from '@wordpress/data';
import { withInstanceId, compose } from '@wordpress/compose';

/**
 * Internal dependencies
 */
import { upArrow, downArrow, dragHandle } from './icons';
import { IconDragHandle } from './drag-handle';
import IgnoreNestedEvents from './ignore-nested-events';
import './edit.css';

export class BlockMover extends Component {
	constructor() {
		super( ...arguments );
		this.state = {
			isFocused: false,
		};
		this.onFocus = this.onFocus.bind( this );
		this.onBlur = this.onBlur.bind( this );
	}

	onFocus() {
		this.setState( {
			isFocused: true,
		} );
	}

	onBlur() {
		this.setState( {
			isFocused: false,
		} );
	}

	render() {
		const { bringForward, sendBackward, isFirst, isLast, isDraggable, onDragStart, onDragEnd, clientIds, blockElementId, instanceId } = this.props;
		const { isFocused } = this.state;

		// We emulate a disabled state because forcefully applying the `disabled`
		// attribute on the button while it has focus causes the screen to change
		// to an unfocused state (body as active element) without firing blur on,
		// the rendering parent, leaving it unable to react to focus out.
		return (
			<IgnoreNestedEvents childHandledEvents={ [ 'onDragStart', 'onMouseDown' ] }>

					<IconButton
						className="editor-block-mover__control block-editor-block-mover__control"
						onClick={ isFirst ? null : bringForward }
						icon={ upArrow }
						label={ __( 'Bring Forward', 'amp' ) }
						aria-describedby={ `editor-block-mover__up-description-${ instanceId }` }
						aria-disabled={ isFirst }
						onFocus={ this.onFocus }
						onBlur={ this.onBlur }
					/>
					<IconDragHandle
						className="editor-block-mover__control block-editor-block-mover__control"
						icon={ dragHandle }
						clientId={ clientIds }
						blockElementId={ blockElementId }
						isVisible={ isDraggable }
						onDragStart={ onDragStart }
						onDragEnd={ onDragEnd }
					/>
					<IconButton
						className="editor-block-mover__control block-editor-block-mover__control"
						onClick={ isLast ? null : sendBackward }
						icon={ downArrow }
						label={ __( 'Send Backward', 'amp' ) }
						aria-describedby={ `editor-block-mover__down-description-${ instanceId }` }
						aria-disabled={ isLast }
						onFocus={ this.onFocus }
						onBlur={ this.onBlur }
					/>

			</IgnoreNestedEvents>
		);
	}
}

export default compose(
	withSelect( ( select, { clientIds } ) => {
		const { getBlock, getBlockIndex, getTemplateLock, getBlockRootClientId } = select( 'core/block-editor' );
		const firstClientId = first( castArray( clientIds ) );
		const block = getBlock( firstClientId );
		const rootClientId = getBlockRootClientId( first( castArray( clientIds ) ) );

		return {
			firstIndex: getBlockIndex( firstClientId, rootClientId ),
			blockType: block ? getBlockType( block.name ) : null,
			isLocked: getTemplateLock( rootClientId ) === 'all',
			rootClientId,
		};
	} ),
	withDispatch( ( dispatch, { clientIds, rootClientId } ) => {
		const { moveBlocksDown, moveBlocksUp, clearSelectedBlock } = dispatch( 'core/block-editor' );

		return {
			onDragEnd: clearSelectedBlock,
			bringForward: partial( moveBlocksDown, clientIds, rootClientId ),
			sendBackward: partial( moveBlocksUp, clientIds, rootClientId ),
		};
	} ),
	withInstanceId,
)( BlockMover );
<|endoftext|>


<commit_before>(angular => {
    'use strict';
    /**
     * @ngdoc function
     * @name tubularNoCacheInterceptor
     * @description
     *
     * Implement a httpInterceptor to prevent browser caching
     *
     * @constructor
     * @returns {Object} A httpInterceptor
     */
    angular.module('tubular.services')
        .factory('tubularNoCacheInterceptor', [function () {

            return {
                request: (config) => {
                    if (config.method === 'GET' &&
                    config.url.indexOf('.htm') === -1 &&
                    config.url.indexOf('blob:') === -1 &&
                    config.url.indexOf('noCache=') === -1) {
                        const separator = config.url.indexOf('?') === -1 ? '?' : '&';
                        config.url = `${config.url + separator  }noCache=${  new Date().getTime()}`;
                    }

                    return config;
                }
            };
        }]);
})(angular);<commit_msg>No cache refactor (#100)<commit_after>(angular => {
    'use strict';
    /**
     * @ngdoc function
     * @name tubularNoCacheInterceptor
     * @description
     *
     * Implement a httpInterceptor to prevent browser caching
     *
     * @constructor
     * @returns {Object} A httpInterceptor
     */
    angular.module('tubular.services')
        .factory('tubularNoCacheInterceptor', [function () {

            return {
                request: (config) => {

                    if (config.method !== 'GET') {
                        return config;
                    }

                    // patterns to escape: .htm | blob: | noCache=
                    const matchesEscapePatterns = (/\.htm|blob:|noCache\=/.test(config.url));

                    if (!matchesEscapePatterns) {
                        const separator = config.url.indexOf('?') === -1 ? '?' : '&';
                        config.url = `${config.url + separator}noCache=${new Date().getTime()}`;
                    }

                    return config;
                }
            };
        }]);
})(angular);<|endoftext|>


<commit_before>"use strict";
const EmptyValue = require('./EmptyValue');
const Value = require('../Value');

class UndefinedValue extends EmptyValue {
	toNative() { return undefined; }
	get jsTypeName() { return "undefined"; }
	*tripleEquals(other, realm) {
		return other instanceof UndefinedValue ? Value.true : Value.false;
	}

	*add(other) { return this.fromNative(undefined + other.toNative()); }

	*asString() {
		return "undefined";
	}

	*toPrimitiveValue(preferedType) { return this; }
	*toNumberValue() { return Value.nan; }
	*toStringValue() { Value.fromNative('undefined'); }

	get debugString() { return "undefined"; }
}

module.exports = UndefinedValue;
<commit_msg>Actually return this thing.<commit_after>"use strict";
const EmptyValue = require('./EmptyValue');
const Value = require('../Value');

class UndefinedValue extends EmptyValue {
	toNative() { return undefined; }
	get jsTypeName() { return "undefined"; }
	*tripleEquals(other, realm) {
		return other instanceof UndefinedValue ? Value.true : Value.false;
	}

	*add(other) { return this.fromNative(undefined + other.toNative()); }

	*asString() {
		return "undefined";
	}

	*toPrimitiveValue(preferedType) { return this; }
	*toNumberValue() { return Value.nan; }
	*toStringValue() { return Value.fromNative('undefined'); }

	get debugString() { return "undefined"; }
}

module.exports = UndefinedValue;
<|endoftext|>