<commit_before>import { useEffect, useReducer } from 'react';
import moment from 'moment';
import unionBy from 'lodash.unionby';
import { UIDatabase } from '../database/index';
import {
  getAuthorizationHeader,
  getPatientRequestUrl,
  getServerURL,
  processPatientResponse,
} from '../sync/lookupApiUtils';
import { DATE_FORMAT } from '../utilities/constants';
import { useFetch } from './useFetch';
import { useThrottled } from './useThrottled';

const RNFetch = fetch;
const BATCH_SIZE = 50;

const initialState = (initialValue = []) => ({
  data: initialValue,
  loading: false,
  error: false,
  searchedWithNoResults: false,
  gettingMore: false,
  limit: 50,
  offset: 0,
});

const reducer = (state, action) => {
  const { type } = action;

  switch (type) {
    case 'fetch_more': {
      const { limit, offset } = state;
      const newOffset = offset + limit;

      return { ...state, offset: newOffset };
    }

    case 'fetch_success': {
      const { payload } = action;
      const { data } = payload;

      return { ...state, data: data ?? [], loading: false, searchedWithNoResults: false };
    }
    case 'fetch_start': {
      const { loading } = state;

      if (loading) return state;

      return { ...state, loading: true, data: [], searchedWithNoResults: false };
    }
    case 'fetch_no_results': {
      return { ...state, data: [], searchedWithNoResults: true, loading: false };
    }

    case 'fetch_error': {
      const { payload } = action;
      const { error } = payload;

      return { ...state, error, loading: false, searchedWithNoResults: false };
    }

    case 'getting_more_patients': {
      return { ...state, gettingMore: true };
    }

    case 'add_more_patients': {
      const { payload } = action;
      const { data } = payload;

      const { offset, data: oldData } = state;
      const newData = unionBy(data, oldData, 'id');
      const newOffset = offset + BATCH_SIZE;

      return { ...state, data: newData, offset: newOffset, gettingMore: false };
    }

    case 'clear': {
      return initialState();
    }
    default: {
      return state;
    }
  }
};

/**
 * Hook to help a component to be able to find patients in the local database or through the
 * patient lookup API.
 *
 * Merges the states such that there is a single state value for 'data', 'loading' etc, rather than
 * having to track multiple.
 */
export const useLocalAndRemotePatients = (initialValue = []) => {
  const [
    { data, loading, searchedWithNoResults, error, limit, offset, gettingMore },
    dispatch,
  ] = useReducer(reducer, initialValue, initialState);

  const { fetch, refresh, isLoading, response, error: fetchError } = useFetch(getServerURL());

  // If response is empty, we are not loading, and there is no error,
  // then we have tried to fetch and had no results.
  // Has fetched guards against the initial state of not loading and a response being empty.
  useEffect(() => {
    const noResults = fetchError?.message === 'No records found';
    if (noResults) {
      dispatch({ type: 'fetch_no_results' });
    }
  }, [fetchError]);

  // When isLoading is set as true, sync this with our merged state.
  useEffect(() => {
    if (isLoading) dispatch({ type: 'fetch_start' });
  }, [isLoading]);

  // When response changes and we have a new response, assign this as our set of data, merging
  // with our combined state.
  useEffect(() => {
    if (response) dispatch({ type: 'fetch_success', payload: { data: response } });
  }, [response]);

  // Synchronizing this error with our merged state.
  useEffect(() => {
    if (fetchError && fetchError.message !== 'No records found') {
      dispatch({ type: 'fetch_error', payload: { error: fetchError } });
    }
  }, [fetchError]);

  const onPressSearchOnline = searchParams => {
    const paramsWithLimits = { ...searchParams, limit, offset };

    refresh();

    fetch(
      getPatientRequestUrl(paramsWithLimits),
      { headers: { authorization: getAuthorizationHeader() } },
      { responseHandler: processPatientResponse }
    );
    dispatch({ type: 'fetch_more' });
  };

  const getMorePatients = async searchParams => {
    if (!response) return;

    dispatch({ type: 'getting_more_patients' });
    const paramsWithLimits = { ...searchParams, limit, offset };

    // Use RNFetch as the fetch returned from `useFetch` is coupled with it's state in a specific
    // implementation, which we want to change by appending to the result, rather than replacing.
    const getMoreResponse = await RNFetch(
      `${getServerURL()}${getPatientRequestUrl(paramsWithLimits)}`,
      {
        headers: { authorization: getAuthorizationHeader() },
      }
    );

    dispatch({
      type: 'add_more_patients',
      payload: {
        data: processPatientResponse({ ...getMoreResponse, json: await getMoreResponse.json() }),
      },
    });
  };

  const getLocalPatients = searchParameters => {
    refresh();

    const { lastName, firstName, dateOfBirth } = searchParameters;

    if (!(lastName || firstName || dateOfBirth)) {
      return dispatch({ type: 'clear' });
    }
    dispatch({ type: 'fetch_start' });

    const query = 'lastName BEGINSWITH[c] $0 AND firstName BEGINSWITH[c] $1';
    let patients = UIDatabase.objects('Patient').filtered(query, lastName, firstName);

    if (dateOfBirth) {
      const dob = moment(dateOfBirth, DATE_FORMAT.DD_MM_YYYY, null, true);
      if (!dob.isValid()) {
        return dispatch({ type: 'fetch_no_results' });
      }

      const dayOfDOB = dob.startOf('day').toDate();
      const dayAfterDOB = dob.endOf('day').toDate();

      patients = patients.filtered('dateOfBirth >= $0 AND dateOfBirth < $1', dayOfDOB, dayAfterDOB);
    }

    if (patients.length) {
      return dispatch({
        type: 'fetch_success',
        payload: {
          data: patients.sorted('lastName'),
        },
      });
    }

    return dispatch({ type: 'fetch_no_results' });
  };

  // Throttle the get local patients such that the method will only be called 1/2 a second
  // after the last invocation, so when typing there is not constant queries happening each
  // press as there are potentially 100,00's of thousands of patients.
  const throttledGetLocalPatients = useThrottled(getLocalPatients, 500, [], {
    leading: false,
    trailing: true,
  });

  const throttledGetMorePatients = useThrottled(getMorePatients, 1000, [limit, offset, response], {
    loading: false,
    trailing: true,
  });

  // getLocalPatients is throttled- ensure that the fetch_start action is still dispatched
  // on the first invocation so the loading state is changed.
  const getLocalPatientsWrapper = searchParameters => {
    dispatch({ type: 'fetch_start' });
    throttledGetLocalPatients(searchParameters);
  };

  return [
    { data, loading, searchedWithNoResults, error, gettingMore },
    onPressSearchOnline,
    getLocalPatientsWrapper,
    throttledGetMorePatients,
  ];
};
<commit_msg>Add handling of reaching the end of the list of patients<commit_after>import { useEffect, useReducer } from 'react';
import moment from 'moment';
import unionBy from 'lodash.unionby';
import { UIDatabase } from '../database/index';
import {
  getAuthorizationHeader,
  getPatientRequestUrl,
  getServerURL,
  processPatientResponse,
} from '../sync/lookupApiUtils';
import { DATE_FORMAT } from '../utilities/constants';
import { useFetch } from './useFetch';
import { useThrottled } from './useThrottled';

const RNFetch = fetch;
const BATCH_SIZE = 50;

const initialState = (initialValue = []) => ({
  data: initialValue,
  loading: false,
  error: false,
  searchedWithNoResults: false,
  gettingMore: false,
  noMore: true,
  limit: 5,
  offset: 0,
});

const reducer = (state, action) => {
  const { type } = action;

  switch (type) {
    case 'fetch_more': {
      const { limit, offset } = state;
      const newOffset = offset + limit;

      return { ...state, offset: newOffset };
    }

    case 'fetch_success': {
      const { payload } = action;
      const { data } = payload;

      return { ...state, data: data ?? [], loading: false, searchedWithNoResults: false };
    }
    case 'fetch_start': {
      const { loading } = state;

      if (loading) return state;

      return { ...state, loading: true, data: [], searchedWithNoResults: false, noMore: false };
    }
    case 'fetch_no_results': {
      return { ...state, data: [], searchedWithNoResults: true, loading: false };
    }

    case 'fetch_error': {
      const { payload } = action;
      const { error } = payload;

      return { ...state, error, loading: false, searchedWithNoResults: false };
    }

    case 'getting_more_patients': {
      return { ...state, gettingMore: true };
    }

    case 'add_more_patients': {
      const { payload } = action;
      const { data } = payload;

      const { offset, data: oldData } = state;
      const newData = unionBy(data, oldData, 'id');
      const newOffset = offset + BATCH_SIZE;

      return { ...state, data: newData, offset: newOffset, gettingMore: false };
    }

    case 'no_more_patients': {
      return { ...state, gettingMore: false, noMore: true };
    }

    case 'clear': {
      return initialState();
    }
    default: {
      return state;
    }
  }
};

/**
 * Hook to help a component to be able to find patients in the local database or through the
 * patient lookup API.
 *
 * Merges the states such that there is a single state value for 'data', 'loading' etc, rather than
 * having to track multiple.
 */
export const useLocalAndRemotePatients = (initialValue = []) => {
  const [
    { data, loading, searchedWithNoResults, error, limit, offset, gettingMore },
    dispatch,
  ] = useReducer(reducer, initialValue, initialState);

  const { fetch, refresh, isLoading, response, error: fetchError, noMore } = useFetch(
    getServerURL()
  );

  // If response is empty, we are not loading, and there is no error,
  // then we have tried to fetch and had no results.
  // Has fetched guards against the initial state of not loading and a response being empty.
  useEffect(() => {
    const noResults = fetchError?.message === 'No records found';
    if (noResults) {
      dispatch({ type: 'fetch_no_results' });
    }
  }, [fetchError]);

  // When isLoading is set as true, sync this with our merged state.
  useEffect(() => {
    if (isLoading) dispatch({ type: 'fetch_start' });
  }, [isLoading]);

  // When response changes and we have a new response, assign this as our set of data, merging
  // with our combined state.
  useEffect(() => {
    if (response) dispatch({ type: 'fetch_success', payload: { data: response } });
  }, [response]);

  // Synchronizing this error with our merged state.
  useEffect(() => {
    if (fetchError && fetchError.message !== 'No records found') {
      dispatch({ type: 'fetch_error', payload: { error: fetchError } });
    }
  }, [fetchError]);

  const onPressSearchOnline = searchParams => {
    const paramsWithLimits = { ...searchParams, limit, offset };

    refresh();

    fetch(
      getPatientRequestUrl(paramsWithLimits),
      { headers: { authorization: getAuthorizationHeader() } },
      { responseHandler: processPatientResponse }
    );
    dispatch({ type: 'fetch_more' });
  };

  const getMorePatients = async searchParams => {
    if (!response && !noMore) return;

    dispatch({ type: 'getting_more_patients' });
    const paramsWithLimits = { ...searchParams, limit, offset };

    // Use RNFetch as the fetch returned from `useFetch` is coupled with it's state in a specific
    // implementation, which we want to change by appending to the result, rather than replacing.
    const getMoreResponse = await RNFetch(
      `${getServerURL()}${getPatientRequestUrl(paramsWithLimits)}`,
      {
        headers: { authorization: getAuthorizationHeader() },
      }
    );

    try {
      const morePatients = processPatientResponse({
        ...getMoreResponse,
        json: await getMoreResponse.json(),
      });
      dispatch({
        type: 'add_more_patients',
        payload: {
          data: morePatients,
        },
      });
    } catch {
      dispatch({ type: 'no_more_patients' });
    }
  };

  const getLocalPatients = searchParameters => {
    refresh();

    const { lastName, firstName, dateOfBirth } = searchParameters;

    if (!(lastName || firstName || dateOfBirth)) {
      return dispatch({ type: 'clear' });
    }
    dispatch({ type: 'fetch_start' });

    const query = 'lastName BEGINSWITH[c] $0 AND firstName BEGINSWITH[c] $1';
    let patients = UIDatabase.objects('Patient').filtered(query, lastName, firstName);

    if (dateOfBirth) {
      const dob = moment(dateOfBirth, DATE_FORMAT.DD_MM_YYYY, null, true);
      if (!dob.isValid()) {
        return dispatch({ type: 'fetch_no_results' });
      }

      const dayOfDOB = dob.startOf('day').toDate();
      const dayAfterDOB = dob.endOf('day').toDate();

      patients = patients.filtered('dateOfBirth >= $0 AND dateOfBirth < $1', dayOfDOB, dayAfterDOB);
    }

    if (patients.length) {
      return dispatch({
        type: 'fetch_success',
        payload: {
          data: patients.sorted('lastName'),
        },
      });
    }

    return dispatch({ type: 'fetch_no_results' });
  };

  // Throttle the get local patients such that the method will only be called 1/2 a second
  // after the last invocation, so when typing there is not constant queries happening each
  // press as there are potentially 100,00's of thousands of patients.
  const throttledGetLocalPatients = useThrottled(getLocalPatients, 500, [], {
    leading: false,
    trailing: true,
  });

  const throttledGetMorePatients = useThrottled(
    getMorePatients,
    1000,
    [limit, offset, response, noMore],
    {
      loading: false,
      trailing: true,
    }
  );

  // getLocalPatients is throttled- ensure that the fetch_start action is still dispatched
  // on the first invocation so the loading state is changed.
  const getLocalPatientsWrapper = searchParameters => {
    dispatch({ type: 'fetch_start' });
    throttledGetLocalPatients(searchParameters);
  };

  return [
    { data, loading, searchedWithNoResults, error, gettingMore },
    onPressSearchOnline,
    getLocalPatientsWrapper,
    throttledGetMorePatients,
  ];
};
<|endoftext|>


<commit_before>'use strict';

/**
 * simplecached: client.
 * (C) 2013 Alex Fernández.
 */

// requires
require('prototypes');
var net = require('net');
var Log = require('log');
var testing = require('testing');
var serverLib = require('./server.js');

// globals
var log = new Log('info');

// constants
var DEFAULT_PORT = 11311;

/**
 * Client for simplecached.
 */
exports.Client = function(options, callback)
{
	// self-reference
	var self = this;

	// attributes
	var client;
	var pending;

	// init
	init();

	/**
	 * Init the connection.
	 */
	function init()
	{
		options = options || {};
		var parsedPort = parseInt(options, 10);
		var params = {
			port: parsedPort || options.port || DEFAULT_PORT,
			host: 'localhost',
		};
		client = net.connect(params, callback);
		client.on('data', receiveData);
		client.on('error', receiveError);
		client.on('end', end);
	}

	/**
	 * Receive some data.
	 */
	function receiveData(data)
	{
		var message = String(data).trim();
		log.debug('Received %s', message);
		if (!pending)
		{
			log.error('No pending function for message %s', message);
			return;
		}
		var callback = pending;
		pending = null;
		callback(null, message);
	}

	/**
	 * Receive a connection error.
	 */
	function receiveError(error)
	{
		if (!pending)
		{
			log.error('No pending function for error %s', error);
			return;
		}
		pending(error);
		pending = null;
	}

	/**
	 * End the connection.
	 */
	function end()
	{
		log.debug('Connection finished');
		if (pending)
		{
			pending(null);
		}
	}

	/**
	 * Get a value from the remote simplecached.
	 */
	self.get = function(key, callback)
	{
		client.write('get ' + key + '\r\n');
		pending = function(error, result)
		{
			if (error)
			{
				return callback(error);
			}
			if (result == 'END')
			{
				return callback(null);
			}
			if (!result.startsWith('VALUE '))
			{
				log.error('Invalid line %s', result);
				callback('Invalid result');
			}
			var value = result.substringFrom('VALUE ');
			callback(null, value);
		};
	};

	/**
	 * Set a value in the remote simplecached.
	 */
	self.set = function(key, value, callback)
	{
		client.write('set ' + key + ' ' + value + '\r\n');
		pending = function(error, result)
		{
			return callback(error, result == 'STORED');
		};
	};

	/**
	 * Delete a value in the remote simplecached.
	 */
	self.delete = function(key, callback)
	{
		client.write('delete ' + key + '\r\n');
		pending = function(error, result)
		{
			return callback(error, result == 'DELETED');
		};
	};

	/**
	 * Close the connection.
	 */
	self.close = function(callback)
	{
		pending = callback;
		client.end();
	};
};

/**
 * Test the client.
 */
function testClient(callback)
{
	var port = 11312;
	var server = new serverLib.Server(port, function(error)
	{
		testing.check(error, 'Error creating server', callback);
		var client = new exports.Client(port, function(error)
		{
			testing.check(error, 'Error creating client', callback);
			var key = 'test' + Math.random();
			var value = 'naïve value';
			client.set(key, value, function(error, result)
			{
				testing.check(error, 'Error setting value', callback);
				testing.assertEquals(result, true, 'Invalid set', callback);
				client.get(key, function(error, result)
				{
					testing.check(error, 'Error getting value', callback);
					testing.assertEquals(result, value, 'Different value', callback);
					client.delete(key, function(error, result)
					{
						testing.check(error, 'Error deleting value', callback);
						testing.assertEquals(result, true, 'Invalid set', callback);
						client.close(function(error)
						{
							testing.check(error, 'Error closing client', callback);
							server.close(function(error)
							{
								testing.check(error, 'Error closing server', callback);
								testing.success(callback);
							});
						});
					});
				});
			});
		});
	});
}

/**
 * Run all tests.
 */
exports.test = function(callback)
{
	testing.run([ testClient ], callback);
};

// run tests if invoked directly
if (__filename == process.argv[1])
{
	exports.test(testing.show);
}

<commit_msg>Change localhost /lib/client.js to set as default but can read variable. To connect to an other server in localnetwork (teacher server, student client) lines affected 20 and 46<commit_after>'use strict';

/**
 * simplecached: client.
 * (C) 2013 Alex Fernández.
 */

// requires
require('prototypes');
var net = require('net');
var Log = require('log');
var testing = require('testing');
var serverLib = require('./server.js');

// globals
var log = new Log('info');

// constants
var DEFAULT_PORT = 11311;
var DEFAULT_HOST = 'localhost';

/**
 * Client for simplecached.
 */
exports.Client = function(options, callback)
{
	// self-reference
	var self = this;

	// attributes
	var client;
	var pending;

	// init
	init();

	/**
	 * Init the connection.
	 */
	function init()
	{
		options = options || {};
		var parsedPort = parseInt(options, 10);
		var params = {
			port: parsedPort || options.port || DEFAULT_PORT,
			host: options.host || DEFAULT_HOST,
		};
		client = net.connect(params, callback);
		client.on('data', receiveData);
		client.on('error', receiveError);
		client.on('end', end);
	}

	/**
	 * Receive some data.
	 */
	function receiveData(data)
	{
		var message = String(data).trim();
		log.debug('Received %s', message);
		if (!pending)
		{
			log.error('No pending function for message %s', message);
			return;
		}
		var callback = pending;
		pending = null;
		callback(null, message);
	}

	/**
	 * Receive a connection error.
	 */
	function receiveError(error)
	{
		if (!pending)
		{
			log.error('No pending function for error %s', error);
			return;
		}
		pending(error);
		pending = null;
	}

	/**
	 * End the connection.
	 */
	function end()
	{
		log.debug('Connection finished');
		if (pending)
		{
			pending(null);
		}
	}

	/**
	 * Get a value from the remote simplecached.
	 */
	self.get = function(key, callback)
	{
		client.write('get ' + key + '\r\n');
		pending = function(error, result)
		{
			if (error)
			{
				return callback(error);
			}
			if (result == 'END')
			{
				return callback(null);
			}
			if (!result.startsWith('VALUE '))
			{
				log.error('Invalid line %s', result);
				callback('Invalid result');
			}
			var value = result.substringFrom('VALUE ');
			callback(null, value);
		};
	};

	/**
	 * Set a value in the remote simplecached.
	 */
	self.set = function(key, value, callback)
	{
		client.write('set ' + key + ' ' + value + '\r\n');
		pending = function(error, result)
		{
			return callback(error, result == 'STORED');
		};
	};

	/**
	 * Delete a value in the remote simplecached.
	 */
	self.delete = function(key, callback)
	{
		client.write('delete ' + key + '\r\n');
		pending = function(error, result)
		{
			return callback(error, result == 'DELETED');
		};
	};

	/**
	 * Close the connection.
	 */
	self.close = function(callback)
	{
		pending = callback;
		client.end();
	};
};

/**
 * Test the client.
 */
function testClient(callback)
{
	var port = 11312;
	var server = new serverLib.Server(port, function(error)
	{
		testing.check(error, 'Error creating server', callback);
		var client = new exports.Client(port, function(error)
		{
			testing.check(error, 'Error creating client', callback);
			var key = 'test' + Math.random();
			var value = 'naïve value';
			client.set(key, value, function(error, result)
			{
				testing.check(error, 'Error setting value', callback);
				testing.assertEquals(result, true, 'Invalid set', callback);
				client.get(key, function(error, result)
				{
					testing.check(error, 'Error getting value', callback);
					testing.assertEquals(result, value, 'Different value', callback);
					client.delete(key, function(error, result)
					{
						testing.check(error, 'Error deleting value', callback);
						testing.assertEquals(result, true, 'Invalid set', callback);
						client.close(function(error)
						{
							testing.check(error, 'Error closing client', callback);
							server.close(function(error)
							{
								testing.check(error, 'Error closing server', callback);
								testing.success(callback);
							});
						});
					});
				});
			});
		});
	});
}

/**
 * Run all tests.
 */
exports.test = function(callback)
{
	testing.run([ testClient ], callback);
};

// run tests if invoked directly
if (__filename == process.argv[1])
{
	exports.test(testing.show);
}

<|endoftext|>


<commit_before>/*global require, define, test, expect, strictEqual, location */

if (typeof require === 'function' && require.config) {
    require.config({
        baseUrl: '../lib',
        paths: {
            //Path relative to baseUrl
            'BBT': '../BBT'
        },
        shim: {
            'underscore': {
                exports: '_'
            }
        }
    });

    //Override if in "dist" mode
    if (location.href.indexOf('-dist') !== -1) {
        //Set location of BBT to the dist location
        require.config({
            paths: {
                'BBT': '../dist/BBT.min'
            }
        });
    }
}

(function (root, factory) {
    'use strict';

    if (typeof define === 'function' && define.amd) {
        // AMD.
        define(['BBT', 'jquery'], factory);
    } else {
        // Browser globals
        factory(root.BBT, root.jQuery);
    }
}(this, function (BBT, $) {
    'use strict';

    test('version test', function () {
        expect(1);
        strictEqual(BBT.version,
            '0.0.1, jQuery version is: ' + $.fn.jquery,
            'Version concatenated');
    });

    test('conversion test', function () {
        expect(1);
        strictEqual(BBT.convert('Harry & Sally'),
            'Harry & Sally',
            'Ampersand converted');
    });

    var histo = new BBT.H1("h","h",10,0,200);
    histo.Fill(123.4);
    histo.Fill(113.4);
    histo.Fill(129.4);
    histo.Fill(23.4);
    histo.Fill(33.6);
    histo.Fill(3.98);
    histo.Fill(12);
    histo.Fill(7);

    //var xaxishisto = new H1.fXaxis("xaxishisto", "Eje x en [u]");

    histo.Draw();

}));
<commit_msg>Little changes in workarea/BBT-tests.js<commit_after>/*global require, define, test, expect, strictEqual, location */

if (typeof require === 'function' && require.config) {
    require.config({
        baseUrl: '../lib',
        paths: {
            //Path relative to baseUrl
            'BBT': '../BBT'
        },
        shim: {
            'underscore': {
                exports: '_'
            }
        }
    });

    //Override if in "dist" mode
    if (location.href.indexOf('-dist') !== -1) {
        //Set location of BBT to the dist location
        require.config({
            paths: {
                'BBT': '../dist/BBT.min'
            }
        });
    }
}

(function (root, factory) {
    'use strict';

    if (typeof define === 'function' && define.amd) {
        // AMD.
        define(['BBT', 'jquery'], factory);
    } else {
        // Browser globals
        factory(root.BBT, root.jQuery);
    }
}(this, function (BBT, $) {
    'use strict';

    test('version test', function () {
        expect(1);
        strictEqual(BBT.version,
            '0.0.1, jQuery version is: ' + $.fn.jquery,
            'Version concatenated');
    });

    test('conversion test', function () {
        expect(1);
        strictEqual(BBT.convert('Harry & Sally'),
            'Harry & Sally',
            'Ampersand converted');
    });

    var histo = new BBT.H1("h","h",10,0,200);
    histo.Fill(123.4);
    histo.Fill(113.4);
    histo.Fill(129.4);
    histo.Fill(23.4);
    histo.Fill(33.6);
    histo.Fill(3.98);
    histo.Fill(12);
    histo.Fill(7);

    //var xaxishisto = new H1.fXaxis("xaxishisto", "Eje x en [u]");

    histo.Draw();

}));
<|endoftext|>


<commit_before>/*
  Aras.HTML5 provides a HTML5 client library to build Aras Innovator Applications

  Copyright (C) 2015 Processwall Limited.

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published
  by the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU Affero General Public License for more details.

  You should have received a copy of the GNU Affero General Public License
  along with this program.  If not, see http://opensource.org/licenses/AGPL-3.0.

  Company: Processwall Limited
  Address: The Winnowing House, Mill Lane, Askham Richard, York, YO23 3NW, United Kingdom
  Tel:     +44 113 815 3440
  Email:   support@processwall.com
*/

define([
	'dojo/_base/declare',
	'dojo/_base/lang',
	'dojo/on',
	'../Property',
	'dijit/form/NumberTextBox'
], function(declare, lang, on, Property, NumberTextBox) {

	return declare('Aras.View.Properties.Integer', [NumberTextBox, Property], {

		_viewModelValueHandle: null,

		_valueHandle: null,

		constructor: function() {

			// Set Default Contraints
			this.constraints = { pattern: '#', places: 0 };
		},

		startup: function() {
			this.inherited(arguments);

			// Call Property Startup
			this._startup();

			this._updateInteger();
		},

		destroy: function() {
			this.inherited(arguments);

			// Call Property Destroy
			this._destroy();

			if (this._viewModelValueHandle != null)
			{
				this._viewModelValueHandle.unwatch();
			}

			if (this._valueHandle != null)
			{
				this._valueHandle.unwatch();
			}
		},

		_updateInteger: function() {

			if (this.ViewModel != null)
			{
				// Set Minimum Value
				this.constraints.min = this.ViewModel.MinValue;

				// Set Maximum Value
				this.constraints.max = this.ViewModel.MaxValue;

				// Set Value from ViewModel
				this.set("value", this.ViewModel.Value);

				// Watch for changes in Control value
				if (!this._valueHandle)
				{
					this._valueHandle = this.watch('value', lang.hitch(this, function(name, oldValue, newValue) {

						if (isNaN(newValue))
						{
							this.set("value", oldValue);
						}
						else
						{
							var newnumber = Number(newValue);
							var currentnumber = Number(this.ViewModel.get('Value'));

							if (currentnumber !== newnumber)
							{
								if (!this._updateFromViewModel)
								{
									// Update ViewModel Value
									this.ViewModel.set('Value', newnumber);
									this.ViewModel.Write();
								}
							}
						}

					}));
				}

				// Watch for changes in ViewModel
				if (!this._viewModelValueHandle)
				{
					this._viewModelValueHandle = this.ViewModel.watch('Value', lang.hitch(this, function(name, oldValue, newValue) {

						// Stop ViewModel Update
						this._updateFromViewModel = true;

						// Set Value
						this.set("value", newValue);

						// Start ViewModel Update
						this._updateFromViewModel = false;
					}));
				}
			}
		},

		OnViewModelChanged: function(name, oldValue, newValue) {
			this.inherited(arguments);

			this._updateInteger();
		}
	});
});<commit_msg>Allow Integer Property to be set to NULL<commit_after>/*
  Aras.HTML5 provides a HTML5 client library to build Aras Innovator Applications

  Copyright (C) 2015 Processwall Limited.

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published
  by the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU Affero General Public License for more details.

  You should have received a copy of the GNU Affero General Public License
  along with this program.  If not, see http://opensource.org/licenses/AGPL-3.0.

  Company: Processwall Limited
  Address: The Winnowing House, Mill Lane, Askham Richard, York, YO23 3NW, United Kingdom
  Tel:     +44 113 815 3440
  Email:   support@processwall.com
*/

define([
	'dojo/_base/declare',
	'dojo/_base/lang',
	'dojo/on',
	'../Property',
	'dijit/form/NumberTextBox'
], function(declare, lang, on, Property, NumberTextBox) {

	return declare('Aras.View.Properties.Integer', [NumberTextBox, Property], {

		_viewModelValueHandle: null,

		_valueHandle: null,

		constructor: function() {

			// Set Default Contraints
			this.constraints = { pattern: '#', places: 0 };
		},

		startup: function() {
			this.inherited(arguments);

			// Call Property Startup
			this._startup();

			this._updateInteger();
		},

		destroy: function() {
			this.inherited(arguments);

			// Call Property Destroy
			this._destroy();

			if (this._viewModelValueHandle != null)
			{
				this._viewModelValueHandle.unwatch();
			}

			if (this._valueHandle != null)
			{
				this._valueHandle.unwatch();
			}
		},

		_updateInteger: function() {

			if (this.ViewModel != null)
			{
				// Set Minimum Value
				this.constraints.min = this.ViewModel.MinValue;

				// Set Maximum Value
				this.constraints.max = this.ViewModel.MaxValue;

				// Set Value from ViewModel
				this.set("value", this.ViewModel.Value);

				// Watch for changes in Control value
				if (!this._valueHandle)
				{
					this._valueHandle = this.watch('value', lang.hitch(this, function(name, oldValue, newValue) {

						if (isNaN(newValue))
						{
							this.set("value", null);
						}
						else
						{
							var newnumber = Number(newValue);
							var currentnumber = Number(this.ViewModel.get('Value'));

							if (currentnumber !== newnumber)
							{
								if (!this._updateFromViewModel)
								{
									// Update ViewModel Value
									this.ViewModel.set('Value', newnumber);
									this.ViewModel.Write();
								}
							}
						}

					}));
				}

				// Watch for changes in ViewModel
				if (!this._viewModelValueHandle)
				{
					this._viewModelValueHandle = this.ViewModel.watch('Value', lang.hitch(this, function(name, oldValue, newValue) {

						// Stop ViewModel Update
						this._updateFromViewModel = true;

						// Set Value
						this.set("value", newValue);

						// Start ViewModel Update
						this._updateFromViewModel = false;
					}));
				}
			}
		},

		OnViewModelChanged: function(name, oldValue, newValue) {
			this.inherited(arguments);

			this._updateInteger();
		}
	});
});<|endoftext|>


<commit_before>import React from "react";
import format from "date-fns/format";

export default class PostPreview extends React.Component {
  render() {
    const {entry, getAsset, widgetFor} = this.props;
    let image = getAsset(entry.getIn(["data", "image"]));

    // Bit of a nasty hack to make relative paths work as expected as a background image here
    if (image && !image.fileObj) {
      image = window.parent.location.protocol + "//" + window.parent.location.host + image;
    }

    return

            <div className="single-head-background bg-center cover" style={{
            backgroundImage: image && `url(${image})`
            }}>

                { entry.getIn(["data", "title"]) }

                { entry.getIn(["data", "subtitle"]) }

                        Here is the operating status of over 100 hotels in Puerto Rico, including the date by which they're accepting reservations.

                            Show

                                Open

                                Closed

                                All

                            Updated on:

                            { format(entry.getIn(["data", "date"]), "dddd MMM D, YY") }

                        Hotel Name

                        Status

                        Next Reservation

                        More info

                {(entry.getIn(['data', 'hotels']) || []).map((hotel, index) =>

                            {hotel.get('name')}

                            {hotel.get('open') ? 'Open' : 'Closed'}

                            {hotel.get('date')}

                            {hotel.get('phone')}

                                {hotel.get('date') == 'Booking' ? Hotel is booking new reservations. : (hotel.get('date') == 'Booking' ? Next reservation date is pending. : Next Reservation:  + hotel.get('date') )}

                                {hotel.get('phone')}

                )}

;
  }
}

<commit_msg>Hotels CMS Preview<commit_after>import React from "react";
import format from "date-fns/format";

export default class PostPreview extends React.Component {
  render() {
    const {entry, getAsset, widgetFor} = this.props;
    let image = getAsset(entry.getIn(["data", "image"]));

    // Bit of a nasty hack to make relative paths work as expected as a background image here
    if (image && !image.fileObj) {
      image = window.parent.location.protocol + "//" + window.parent.location.host + image;
    }

    return

            <div className="single-head-background bg-center cover" style={{
            backgroundImage: image && `url(${image})`
            }}>

                { entry.getIn(["data", "title"]) }

                { entry.getIn(["data", "subtitle"]) }

                        Here is the operating status of over 100 hotels in Puerto Rico, including the date by which they're accepting reservations.

                            Show

                                Open

                                Closed

                                All

                            Updated on:

                            { format(entry.getIn(["data", "date"]), "dddd MMM D, YY") }

                        Hotel Name

                        Status

                        Next Reservation

                        More info

                {(entry.getIn(['data', 'hotels']) || []).map((hotel, index) =>

                            {hotel.get('name')}

                            {hotel.get('open') ? 'Open' : 'Closed'}

                            {hotel.get('date')}

                            {hotel.get('phone')}

                                {hotel.get('date')}

                                {hotel.get('phone')}

                )}

;
  }
}

<|endoftext|>


<commit_before>'use strict';

(function(){

  angular
    .module('bitcoincom.services')
    .factory('satoshiDiceService', satoshiDiceService);

    function satoshiDiceService(
      $http
      , $interval
      , $log
      , $q
    ) {
      var privateVariable = "";

      var service = {
        // Public variables
        myVariable: "myService",

        // Public functions
        addressIsKnown: addressIsKnown,
        getBetStatus: getBetStatus,
        imageUrlForAddress: imageUrlForAddress
      };

      var STATUS_CHECK_INTERVAL = 1000; // The request sometimes takes 1.15s
      var STATUS_CHECKS_MAX = 30;
      var addresses = [
        '1Dice9GgmweQWxqdiu683E7bHfpb7MUXGd', // 1.05x
        '1Dice81SKu2S1nAzRJUbvpr5LiNTzn7MDV', // 1.12x

      ];

      return service;

      function addressIsKnown(legacyAddress) {
        // Content
        var isKnown = false;
        var knownAddress = '';
        var knownAddressCount = addresses.length;
        for(var i = 0; i < knownAddressCount; i++) {
          knownAddress = addresses[i];
          if (knownAddress === legacyAddress) {
            isKnown = true;
            break;
          }
        }
        return isKnown;
      }

      /**
       * Returns a promose. The result of which has the form: (returned by the API)
       * {
       *   "id":438983,
       *   "depositTxHash":"681e9ceaf94becf80a30041bfb82c0f4147319269f1ceb8a169f3ce2a673eb51",
       *   "vout":0,
       *   "payoutTxHash":"28e4c318f6c938faf85391ddc886d91e2e6b7ed2076469e0e4632d7de49a3496",
       *   "serverSeedHash":"90abbfdb3988cc19ab8573277caccd93a0d8858d2fab574c8a371545b08ea128",
       *   "randomSeed":"4b82c6816ad8d40fa47d702a82ecbee482c4f105c2b0abaf6eff0a01de2be216",
       *   "serverSeedTx":"2174aba545248847c004c0bbac785261d6f308d8c443850e68a968d36f0b977d",
       *   "bet":61440,
       *   "roll":24600,
       *   "win":true,
       *   "betAmount":0.0005,
       *   "payout":0.000525,
       *   "timestamp":"Dec 18, 2018 12:12:23 AM",
       *   "timestampUnix":1545091943000,
       *   "seedPublishTimestamp":"Dec 19, 2018 12:00:00 AM",
       *   "seedPublishTimestampUnix":1545177600000
       * }
       * @param {*} txid
       */
      function getBetStatus(txid) {
        var deferred = $q.defer();

        _getBetStatus(txid, deferred, 0);

        return deferred.promise;
      }

      function _getBetStatus(txid, deferred, totalIterations) {
        console.log('_getBetStatus(), totalIterations: ' + totalIterations);

        function _tryAgain() {
          if (totalIterations >= STATUS_CHECKS_MAX) {
            deferred.reject('Retries exhausted.');
          } else {
            $interval(_getBetStatus, STATUS_CHECK_INTERVAL, 1, false, txid, deferred, totalIterations + 1);
          }
        }

        var url = 'https://satoshidice.com/api/game?txid=' + txid;

        $http({
          method: 'GET',
          url:url
        }).then(
          function onSuccess(response) {
            console.log('sd onSuccess', response);
            $log.debug('Retrieved bet status', response);
            if (response.status === 200) {
              if (response.data.payload.length > 0) {
                deferred.resolve(response.data.payload[0]);
                return;
              }
            }
            _tryAgain();
          },
          function onError(response) {
            $log.error('Failed to retrieve bet status.', response);
            _tryAgain();
          }
        );
      }

      function imageUrlForAddress(legacyAddress) {
      }

    }
})();<commit_msg>All SD addresses.<commit_after>'use strict';

(function(){

  angular
    .module('bitcoincom.services')
    .factory('satoshiDiceService', satoshiDiceService);

    function satoshiDiceService(
      $http
      , $interval
      , $log
      , $q
    ) {
      var privateVariable = "";

      var service = {
        // Public variables
        myVariable: "myService",

        // Public functions
        addressIsKnown: addressIsKnown,
        getBetStatus: getBetStatus,
        imageUrlForAddress: imageUrlForAddress
      };

      var STATUS_CHECK_INTERVAL = 1000; // The request sometimes takes 1.15s
      var STATUS_CHECKS_MAX = 30;
      var addresses = [
        '1Dice9GgmweQWxqdiu683E7bHfpb7MUXGd', //    1.05x
        '1Dice81SKu2S1nAzRJUbvpr5LiNTzn7MDV', //    1.12x
        '1Dice7v1M3me7dJGtTX6cqPggwGoRADVQJ', //    1.31x
        '1Dice7JNVnvzyaenNyNcACuNnRVjt7jBrC', //    1.58x
        '1Dice5ycHmxDHUFVkdKGgrwsDDK1mPES3U', //    1.98x
        '1Dice2wTatMqebSPsbG4gKgT3HfHznsHWi', //    2.63x
        '1Dice1wBBY22stCobuE1LJxHX5FNZ7U97N', //    3.95x
        '1Dice1cF41TGRLoCTbtN33DSdPtTujzUzx', //    7.91x
        '1Dice1FZk6Ls5LKhnGMCLq47tg1DFG763e', //   15.83x
        '1Dice115YcjDrPM9gXFW8iFV9S3j9MtERm', //   63.35x
        '1DiceoejxZdTrYwu3FMP2Ldew91jq9L2u'   // 1013.74x
      ];

      return service;

      function addressIsKnown(legacyAddress) {
        // Content
        var isKnown = false;
        var knownAddress = '';
        var knownAddressCount = addresses.length;
        for(var i = 0; i < knownAddressCount; i++) {
          knownAddress = addresses[i];
          if (knownAddress === legacyAddress) {
            isKnown = true;
            break;
          }
        }
        return isKnown;
      }

      /**
       * Returns a promose. The result of which has the form: (returned by the API)
       * {
       *   "id":438983,
       *   "depositTxHash":"681e9ceaf94becf80a30041bfb82c0f4147319269f1ceb8a169f3ce2a673eb51",
       *   "vout":0,
       *   "payoutTxHash":"28e4c318f6c938faf85391ddc886d91e2e6b7ed2076469e0e4632d7de49a3496",
       *   "serverSeedHash":"90abbfdb3988cc19ab8573277caccd93a0d8858d2fab574c8a371545b08ea128",
       *   "randomSeed":"4b82c6816ad8d40fa47d702a82ecbee482c4f105c2b0abaf6eff0a01de2be216",
       *   "serverSeedTx":"2174aba545248847c004c0bbac785261d6f308d8c443850e68a968d36f0b977d",
       *   "bet":61440,
       *   "roll":24600,
       *   "win":true,
       *   "betAmount":0.0005,
       *   "payout":0.000525,
       *   "timestamp":"Dec 18, 2018 12:12:23 AM",
       *   "timestampUnix":1545091943000,
       *   "seedPublishTimestamp":"Dec 19, 2018 12:00:00 AM",
       *   "seedPublishTimestampUnix":1545177600000
       * }
       * @param {*} txid
       */
      function getBetStatus(txid) {
        var deferred = $q.defer();
        _getBetStatus(txid, deferred, 0);
        return deferred.promise;
      }

      function _getBetStatus(txid, deferred, totalIterations) {
        console.log('_getBetStatus(), totalIterations: ' + totalIterations);

        function _tryAgain() {
          if (totalIterations >= STATUS_CHECKS_MAX) {
            deferred.reject('Retries exhausted.');
          } else {
            $interval(_getBetStatus, STATUS_CHECK_INTERVAL, 1, false, txid, deferred, totalIterations + 1);
          }
        }

        var url = 'https://satoshidice.com/api/game?txid=' + txid;

        $http({
          method: 'GET',
          url:url
        }).then(
          function onSuccess(response) {
            console.log('sd onSuccess', response);
            $log.debug('Retrieved bet status', response);
            if (response.status === 200) {
              if (response.data.payload.length > 0) {
                deferred.resolve(response.data.payload[0]);
                return;
              }
            }
            _tryAgain();
          },
          function onError(response) {
            $log.error('Failed to retrieve bet status.', response);
            _tryAgain();
          }
        );
      }

      function imageUrlForAddress(legacyAddress) {
      }

    }
})();<|endoftext|>


<commit_before>// http://processors.wiki.ti.com/index.php/SensorTag_User_Guide

var IR_TEMPERATURE_UUID                     = 'f000aa0004514000b000000000000000';
var HUMIDITY_UUID                           = 'f000aa2004514000b000000000000000';
var BAROMETRIC_PRESSURE_UUID                = 'f000aa4004514000b000000000000000';
var SIMPLE_KEY_UUID                         = 'ffe0';
var CONN_PARAMS_UUID                        = 'f000ccc004514000b000000000000000';
var OAD_UUID                                = 'f000ffc004514000b000000000000000';

var IR_TEMPERATURE_CONFIG_UUID              = 'f000aa0204514000b000000000000000';
var IR_TEMPERATURE_DATA_UUID                = 'f000aa0104514000b000000000000000';
var IR_TEMPERATURE_PERIOD_UUID              = 'f000aa0304514000b000000000000000';
var IR_TEMPERATURE_PERIOD_UUID              = 'f000aa0304514000b000000000000000';

var HUMIDITY_CONFIG_UUID                    = 'f000aa2204514000b000000000000000';
var HUMIDITY_DATA_UUID                      = 'f000aa2104514000b000000000000000';
var HUMIDITY_PERIOD_UUID                    = 'f000aa2304514000b000000000000000';

var BAROMETRIC_PRESSURE_CONFIG_UUID         = 'f000aa4204514000b000000000000000';
var BAROMETRIC_PRESSURE_DATA_UUID           = 'f000aa4104514000b000000000000000';
var BAROMETRIC_PRESSURE_PERIOD_UUID         = 'f000aa4404514000b000000000000000';

var SIMPLE_KEY_DATA_UUID                    = 'ffe1';

var CONN_PARAMS_READ_NOTIFY_UUID            = 'f000ccc104514000b000000000000000';
var CONNECTION_PARAMS_WRITE_UUID            = 'f000ccc204514000b000000000000000';

var OAD_IMG_IDENTIFY_UUID                   = 'f000ffc104514000b000000000000000';
var OAD_IMG_BLOCK_UUID                      = 'f000ffc204514000b000000000000000';

function SensorTagCommon() {
  this.onIrTemperatureChangeBinded      = this.onIrTemperatureChange.bind(this);
  this.onHumidityChangeBinded           = this.onHumidityChange.bind(this);
  this.onBarometricPressureChangeBinded = this.onBarometricPressureChange.bind(this);
  this.onSimpleKeyChangeBinded          = this.onSimpleKeyChange.bind(this);
}

SensorTagCommon.prototype.toString = function() {
  return JSON.stringify({
    uuid: this.uuid,
    type: this.type
  });
};

SensorTagCommon.prototype.writePeriodCharacteristic = function(serviceUuid, characteristicUuid, period, callback) {
  period /= 10; // input is scaled by units of 10ms

  if (period < 10) {
    period = 10;
  } else if (period > 255) {
    period = 255;
  }

  this.writeUInt8Characteristic(serviceUuid, characteristicUuid, period, callback);
};

SensorTagCommon.prototype.enableConfigCharacteristic = function(serviceUuid, characteristicUuid, callback) {
  this.writeUInt8Characteristic(serviceUuid, characteristicUuid, 0x01, callback);
};

SensorTagCommon.prototype.disableConfigCharacteristic = function(serviceUuid, characteristicUuid, callback) {
  this.writeUInt8Characteristic(serviceUuid, characteristicUuid, 0x00, callback);
};

SensorTagCommon.prototype.setIrTemperaturePeriod = function(period, callback) {
  this.writePeriodCharacteristic(IR_TEMPERATURE_UUID, IR_TEMPERATURE_PERIOD_UUID, period, callback);
};

SensorTagCommon.prototype.enableIrTemperature = function(callback) {
  this.enableConfigCharacteristic(IR_TEMPERATURE_UUID, IR_TEMPERATURE_CONFIG_UUID, callback);
};

SensorTagCommon.prototype.disableIrTemperature = function(callback) {
  this.disableConfigCharacteristic(IR_TEMPERATURE_UUID, IR_TEMPERATURE_CONFIG_UUID, callback);
};

SensorTagCommon.prototype.readIrTemperature = function(callback) {
  this.readDataCharacteristic(IR_TEMPERATURE_UUID, IR_TEMPERATURE_DATA_UUID, function(error, data) {
    if (error) {
      return callback(error);
    }

    this.convertIrTemperatureData(data, function(objectTemperature, ambientTemperature) {
      callback(null, objectTemperature, ambientTemperature);
    }.bind(this));
  }.bind(this));
};

SensorTagCommon.prototype.onIrTemperatureChange = function(data) {
  this.convertIrTemperatureData(data, function(objectTemperature, ambientTemperature) {
    this.emit('irTemperatureChange', objectTemperature, ambientTemperature);
  }.bind(this));
};

SensorTagCommon.prototype.notifyIrTemperature = function(callback) {
  this.notifyCharacteristic(IR_TEMPERATURE_UUID, IR_TEMPERATURE_DATA_UUID, true, this.onIrTemperatureChangeBinded, callback);
};

SensorTagCommon.prototype.unnotifyIrTemperature = function(callback) {
  this.notifyCharacteristic(IR_TEMPERATURE_UUID, IR_TEMPERATURE_DATA_UUID, false, this.onIrTemperatureChangeBinded, callback);
};

SensorTagCommon.prototype.setIrTemperaturePeriod = function(period, callback) {
  this.writePeriodCharacteristic(IR_TEMPERATURE_UUID, IR_TEMPERATURE_PERIOD_UUID, period, callback);
};

SensorTagCommon.prototype.setHumidityPeriod = function(period, callback) {
  this.writePeriodCharacteristic(HUMIDITY_UUID, HUMIDITY_PERIOD_UUID, period, callback);
};

SensorTagCommon.prototype.enableHumidity = function(callback) {
  this.enableConfigCharacteristic(HUMIDITY_UUID, HUMIDITY_CONFIG_UUID, callback);
};

SensorTagCommon.prototype.disableHumidity = function(callback) {
  this.disableConfigCharacteristic(HUMIDITY_UUID, HUMIDITY_CONFIG_UUID, callback);
};

SensorTagCommon.prototype.readHumidity = function(callback) {
  this.readDataCharacteristic(HUMIDITY_UUID, HUMIDITY_DATA_UUID, function(error, data) {
    if (error) {
      return callback(error);
    }

    this.convertHumidityData(data, function(temperature, humidity) {
      callback(null, temperature, humidity);
    });
  }.bind(this));
};

SensorTagCommon.prototype.onHumidityChange = function(data) {
  this.convertHumidityData(data, function(temperature, humidity) {
    this.emit('humidityChange', temperature, humidity);
  }.bind(this));
};

SensorTagCommon.prototype.notifyHumidity = function(callback) {
  this.notifyCharacteristic(HUMIDITY_UUID, HUMIDITY_DATA_UUID, true, this.onHumidityChangeBinded, callback);
};

SensorTagCommon.prototype.unnotifyHumidity = function(callback) {
  this.notifyCharacteristic(HUMIDITY_UUID, HUMIDITY_DATA_UUID, false, this.onHumidityChangeBinded, callback);
};

SensorTagCommon.prototype.setBarometricPressurePeriod = function(period, callback) {
  this.writePeriodCharacteristic(BAROMETRIC_PRESSURE_UUID, BAROMETRIC_PRESSURE_PERIOD_UUID, period, callback);
};

SensorTagCommon.prototype.disableBarometricPressure = function(callback) {
  this.disableConfigCharacteristic(BAROMETRIC_PRESSURE_UUID, BAROMETRIC_PRESSURE_CONFIG_UUID, callback);
};

SensorTagCommon.prototype.readBarometricPressure = function(callback) {
  this.readDataCharacteristic(BAROMETRIC_PRESSURE_UUID, BAROMETRIC_PRESSURE_DATA_UUID, function(error, data) {
    if (error) {
      return callback(error);
    }

    this.convertBarometricPressureData(data, function(pressure) {
      callback(null, pressure);
    }.bind(this));
  }.bind(this));
};

SensorTagCommon.prototype.onBarometricPressureChange = function(data) {
  this.convertBarometricPressureData(data, function(pressure) {
    this.emit('barometricPressureChange', pressure);
  }.bind(this));
};

SensorTagCommon.prototype.notifyBarometricPressure = function(callback) {
  this.notifyCharacteristic(BAROMETRIC_PRESSURE_UUID, BAROMETRIC_PRESSURE_DATA_UUID, true, this.onBarometricPressureChangeBinded, callback);
};

SensorTagCommon.prototype.unnotifyBarometricPressure = function(callback) {
  this.notifyCharacteristic(BAROMETRIC_PRESSURE_UUID, BAROMETRIC_PRESSURE_DATA_UUID, false, this.onBarometricPressureChangeBinded, callback);
};

SensorTagCommon.prototype.onSimpleKeyChange = function(data) {
  this.convertSimpleKeyData(data, function(left, right) {
    this.emit('simpleKeyChange', left, right);
  }.bind(this));
};

SensorTagCommon.prototype.convertSimpleKeyData = function(data, callback) {
  var b = data.readUInt8(0);

  var left = (b & 0x2) ? true : false;
  var right = (b & 0x1) ? true : false;

  callback(left, right);
};

SensorTagCommon.prototype.notifySimpleKey = function(callback) {
  this.notifyCharacteristic(SIMPLE_KEY_UUID, SIMPLE_KEY_DATA_UUID, true, this.onSimpleKeyChangeBinded, callback);
};

SensorTagCommon.prototype.unnotifySimpleKey = function(callback) {
  this.notifyCharacteristic(SIMPLE_KEY_UUID, SIMPLE_KEY_DATA_UUID, false, this.onSimpleKeyChangeBinded, callback);
};

SensorTagCommon.prototype.setConnectionParameters = function(connection_interval, supervision_timeout, callback) {
  var buf = new Buffer(8);
  buf.writeUInt16LE(connection_interval, 0);
  buf.writeUInt16LE(connection_interval, 2);
  buf.writeUInt16LE(0, 4);
  buf.writeUInt16LE(supervision_timeout, 6);
  this.notifyCharacteristic(CONN_PARAMS_UUID, CONN_PARAMS_READ_NOTIFY_UUID, true, onConnParamsChange.bind(this),
    function() {
      this.writeDataCharacteristic(CONN_PARAMS_UUID, CONNECTION_PARAMS_WRITE_UUID, buf, null);
    }.bind(this));

  function onConnParamsChange(data) {
    this.notifyCharacteristic(CONN_PARAMS_UUID, CONN_PARAMS_READ_NOTIFY_UUID, true, onConnParamsChange, callback);
  }

}

module.exports = SensorTagCommon;
<commit_msg>begin adding functions for img block/identify chars<commit_after>// http://processors.wiki.ti.com/index.php/SensorTag_User_Guide

var IR_TEMPERATURE_UUID                     = 'f000aa0004514000b000000000000000';
var HUMIDITY_UUID                           = 'f000aa2004514000b000000000000000';
var BAROMETRIC_PRESSURE_UUID                = 'f000aa4004514000b000000000000000';
var SIMPLE_KEY_UUID                         = 'ffe0';
var CONN_PARAMS_UUID                        = 'f000ccc004514000b000000000000000';
var OAD_UUID                                = 'f000ffc004514000b000000000000000';

var IR_TEMPERATURE_CONFIG_UUID              = 'f000aa0204514000b000000000000000';
var IR_TEMPERATURE_DATA_UUID                = 'f000aa0104514000b000000000000000';
var IR_TEMPERATURE_PERIOD_UUID              = 'f000aa0304514000b000000000000000';
var IR_TEMPERATURE_PERIOD_UUID              = 'f000aa0304514000b000000000000000';

var HUMIDITY_CONFIG_UUID                    = 'f000aa2204514000b000000000000000';
var HUMIDITY_DATA_UUID                      = 'f000aa2104514000b000000000000000';
var HUMIDITY_PERIOD_UUID                    = 'f000aa2304514000b000000000000000';

var BAROMETRIC_PRESSURE_CONFIG_UUID         = 'f000aa4204514000b000000000000000';
var BAROMETRIC_PRESSURE_DATA_UUID           = 'f000aa4104514000b000000000000000';
var BAROMETRIC_PRESSURE_PERIOD_UUID         = 'f000aa4404514000b000000000000000';

var SIMPLE_KEY_DATA_UUID                    = 'ffe1';

var CONN_PARAMS_READ_NOTIFY_UUID            = 'f000ccc104514000b000000000000000';
var CONNECTION_PARAMS_WRITE_UUID            = 'f000ccc204514000b000000000000000';

var OAD_IMG_IDENTIFY_UUID                   = 'f000ffc104514000b000000000000000';
var OAD_IMG_BLOCK_UUID                      = 'f000ffc204514000b000000000000000';

function SensorTagCommon() {
  this.onIrTemperatureChangeBinded      = this.onIrTemperatureChange.bind(this);
  this.onHumidityChangeBinded           = this.onHumidityChange.bind(this);
  this.onBarometricPressureChangeBinded = this.onBarometricPressureChange.bind(this);
  this.onSimpleKeyChangeBinded          = this.onSimpleKeyChange.bind(this);
}

SensorTagCommon.prototype.toString = function() {
  return JSON.stringify({
    uuid: this.uuid,
    type: this.type
  });
};

SensorTagCommon.prototype.writePeriodCharacteristic = function(serviceUuid, characteristicUuid, period, callback) {
  period /= 10; // input is scaled by units of 10ms

  if (period < 10) {
    period = 10;
  } else if (period > 255) {
    period = 255;
  }

  this.writeUInt8Characteristic(serviceUuid, characteristicUuid, period, callback);
};

SensorTagCommon.prototype.enableConfigCharacteristic = function(serviceUuid, characteristicUuid, callback) {
  this.writeUInt8Characteristic(serviceUuid, characteristicUuid, 0x01, callback);
};

SensorTagCommon.prototype.disableConfigCharacteristic = function(serviceUuid, characteristicUuid, callback) {
  this.writeUInt8Characteristic(serviceUuid, characteristicUuid, 0x00, callback);
};

SensorTagCommon.prototype.setIrTemperaturePeriod = function(period, callback) {
  this.writePeriodCharacteristic(IR_TEMPERATURE_UUID, IR_TEMPERATURE_PERIOD_UUID, period, callback);
};

SensorTagCommon.prototype.enableIrTemperature = function(callback) {
  this.enableConfigCharacteristic(IR_TEMPERATURE_UUID, IR_TEMPERATURE_CONFIG_UUID, callback);
};

SensorTagCommon.prototype.disableIrTemperature = function(callback) {
  this.disableConfigCharacteristic(IR_TEMPERATURE_UUID, IR_TEMPERATURE_CONFIG_UUID, callback);
};

SensorTagCommon.prototype.readIrTemperature = function(callback) {
  this.readDataCharacteristic(IR_TEMPERATURE_UUID, IR_TEMPERATURE_DATA_UUID, function(error, data) {
    if (error) {
      return callback(error);
    }

    this.convertIrTemperatureData(data, function(objectTemperature, ambientTemperature) {
      callback(null, objectTemperature, ambientTemperature);
    }.bind(this));
  }.bind(this));
};

SensorTagCommon.prototype.onIrTemperatureChange = function(data) {
  this.convertIrTemperatureData(data, function(objectTemperature, ambientTemperature) {
    this.emit('irTemperatureChange', objectTemperature, ambientTemperature);
  }.bind(this));
};

SensorTagCommon.prototype.notifyIrTemperature = function(callback) {
  this.notifyCharacteristic(IR_TEMPERATURE_UUID, IR_TEMPERATURE_DATA_UUID, true, this.onIrTemperatureChangeBinded, callback);
};

SensorTagCommon.prototype.unnotifyIrTemperature = function(callback) {
  this.notifyCharacteristic(IR_TEMPERATURE_UUID, IR_TEMPERATURE_DATA_UUID, false, this.onIrTemperatureChangeBinded, callback);
};

SensorTagCommon.prototype.setIrTemperaturePeriod = function(period, callback) {
  this.writePeriodCharacteristic(IR_TEMPERATURE_UUID, IR_TEMPERATURE_PERIOD_UUID, period, callback);
};

SensorTagCommon.prototype.setHumidityPeriod = function(period, callback) {
  this.writePeriodCharacteristic(HUMIDITY_UUID, HUMIDITY_PERIOD_UUID, period, callback);
};

SensorTagCommon.prototype.enableHumidity = function(callback) {
  this.enableConfigCharacteristic(HUMIDITY_UUID, HUMIDITY_CONFIG_UUID, callback);
};

SensorTagCommon.prototype.disableHumidity = function(callback) {
  this.disableConfigCharacteristic(HUMIDITY_UUID, HUMIDITY_CONFIG_UUID, callback);
};

SensorTagCommon.prototype.readHumidity = function(callback) {
  this.readDataCharacteristic(HUMIDITY_UUID, HUMIDITY_DATA_UUID, function(error, data) {
    if (error) {
      return callback(error);
    }

    this.convertHumidityData(data, function(temperature, humidity) {
      callback(null, temperature, humidity);
    });
  }.bind(this));
};

SensorTagCommon.prototype.onHumidityChange = function(data) {
  this.convertHumidityData(data, function(temperature, humidity) {
    this.emit('humidityChange', temperature, humidity);
  }.bind(this));
};

SensorTagCommon.prototype.notifyHumidity = function(callback) {
  this.notifyCharacteristic(HUMIDITY_UUID, HUMIDITY_DATA_UUID, true, this.onHumidityChangeBinded, callback);
};

SensorTagCommon.prototype.unnotifyHumidity = function(callback) {
  this.notifyCharacteristic(HUMIDITY_UUID, HUMIDITY_DATA_UUID, false, this.onHumidityChangeBinded, callback);
};

SensorTagCommon.prototype.setBarometricPressurePeriod = function(period, callback) {
  this.writePeriodCharacteristic(BAROMETRIC_PRESSURE_UUID, BAROMETRIC_PRESSURE_PERIOD_UUID, period, callback);
};

SensorTagCommon.prototype.disableBarometricPressure = function(callback) {
  this.disableConfigCharacteristic(BAROMETRIC_PRESSURE_UUID, BAROMETRIC_PRESSURE_CONFIG_UUID, callback);
};

SensorTagCommon.prototype.readBarometricPressure = function(callback) {
  this.readDataCharacteristic(BAROMETRIC_PRESSURE_UUID, BAROMETRIC_PRESSURE_DATA_UUID, function(error, data) {
    if (error) {
      return callback(error);
    }

    this.convertBarometricPressureData(data, function(pressure) {
      callback(null, pressure);
    }.bind(this));
  }.bind(this));
};

SensorTagCommon.prototype.onBarometricPressureChange = function(data) {
  this.convertBarometricPressureData(data, function(pressure) {
    this.emit('barometricPressureChange', pressure);
  }.bind(this));
};

SensorTagCommon.prototype.notifyBarometricPressure = function(callback) {
  this.notifyCharacteristic(BAROMETRIC_PRESSURE_UUID, BAROMETRIC_PRESSURE_DATA_UUID, true, this.onBarometricPressureChangeBinded, callback);
};

SensorTagCommon.prototype.unnotifyBarometricPressure = function(callback) {
  this.notifyCharacteristic(BAROMETRIC_PRESSURE_UUID, BAROMETRIC_PRESSURE_DATA_UUID, false, this.onBarometricPressureChangeBinded, callback);
};

SensorTagCommon.prototype.onSimpleKeyChange = function(data) {
  this.convertSimpleKeyData(data, function(left, right) {
    this.emit('simpleKeyChange', left, right);
  }.bind(this));
};

SensorTagCommon.prototype.convertSimpleKeyData = function(data, callback) {
  var b = data.readUInt8(0);

  var left = (b & 0x2) ? true : false;
  var right = (b & 0x1) ? true : false;

  callback(left, right);
};

SensorTagCommon.prototype.notifySimpleKey = function(callback) {
  this.notifyCharacteristic(SIMPLE_KEY_UUID, SIMPLE_KEY_DATA_UUID, true, this.onSimpleKeyChangeBinded, callback);
};

SensorTagCommon.prototype.unnotifySimpleKey = function(callback) {
  this.notifyCharacteristic(SIMPLE_KEY_UUID, SIMPLE_KEY_DATA_UUID, false, this.onSimpleKeyChangeBinded, callback);
};

SensorTagCommon.prototype.setConnectionParameters = function(connection_interval, supervision_timeout, callback) {
  var buf = new Buffer(8);
  buf.writeUInt16LE(connection_interval, 0);
  buf.writeUInt16LE(connection_interval, 2);
  buf.writeUInt16LE(0, 4);
  buf.writeUInt16LE(supervision_timeout, 6);
  this.notifyCharacteristic(CONN_PARAMS_UUID, CONN_PARAMS_READ_NOTIFY_UUID, true, onConnParamsChange.bind(this),
    function() {
      this.writeDataCharacteristic(CONN_PARAMS_UUID, CONNECTION_PARAMS_WRITE_UUID, buf, null);
    }.bind(this));

  function onConnParamsChange(data) {
    this.notifyCharacteristic(CONN_PARAMS_UUID, CONN_PARAMS_READ_NOTIFY_UUID, true, onConnParamsChange, callback);
  }

};

SensorTagCommon.prototype.onImgIdentifyChange = function(callback) {
  this.emit('imgIdentifyChange');
};

SensorTagCommon.prototype.notifyImgIdentify = function(callback) {
  this.notifyCharacteristic(OAD_UUID, OAD_IMG_IDENTIFY_UUID, true, this.onImgIdentifyChangeBinded, callback);
};

SensorTagCommon.prototype.unnotifyImgIdentify = function(callback) {
  this.notifyCharacteristic(OAD_UUID, OAD_IMG_IDENTIFY_UUID, false, this.onImgIdentifyChangeBinded, callback);
};

SensorTagCommon.prototype.notifyImgBlock = function(callback) {
  this.notifyCharacteristic(OAD_UUID, OAD_IMG_BLOCK_UUID, true, this.onImgBlockChangeBinded, callback);
};

SensorTagCommon.prototype.unnotifyImgBlock = function(callback) {
  this.notifyCharacteristic(OAD_UUID, OAD_IMG_BLOCK_UUID, false, this.onImgBlockChangeBinded, callback);
};

module.exports = SensorTagCommon;
<|endoftext|>


<commit_before>'use strict';

const OFF = 'off';
const ERROR = 'error';
const WARN = 'warn';

const COMPLEXITY_MAX = 5;
const TABWIDTH = 2;
const NESTED_CALLBACK_MAX = 4;
const ARGUMENTS_MAX = 4;

module.exports = {
  extends: 'eslint:recommended',
  env: {
    node: true,
  },
  globals: {
    require: false,
    module: false,
  },
  parserOptions: {
    ecmaVersion: 6,
    sourceType: 'script',
    arrowFunctions: true,
    binaryLiterals: true,
    blockBindings: true,
    classes: false,
    defaultParams: true,
    destructuring: true,
    forOf: true,
    generators: false,
    modules: false,
    objectLiteralComputedProperties: true,
    objectLiteralDuplicateProperties: true,
    objectLiteralShorthandMethods: true,
    objectLiteralShorthandProperties: true,
    octalLiterals: false,
    regexUFlag: true,
    regexYFlag: true,
    restParams: true,
    spread: true,
    superInFunctions: true,
    templateStrings: true,
    unicodeCodePointEscapes: true,
  },
  rules: {
    'accessor-pairs': [
      ERROR,
      { getWithoutSet: false, setWithoutGet: true },
    ],
    'array-bracket-spacing': [
      ERROR,
      'never',
    ],
    'array-callback-return': ERROR,
    'arrow-body-style': [
      ERROR,
      'as-needed',
    ],
    'arrow-parens': [
      ERROR,
      'as-needed',
      { requireForBlockBody: true },
    ],
    'arrow-spacing': ERROR,
    'block-spacing': [
      ERROR,
      'always',
    ],
    'brace-style': OFF,
    'callback-return': [
      ERROR, [
        'callback',
        'cb',
        'next',
        'done',
      ],
    ],
    camelcase: OFF,
    'capitalized-comments': OFF,
    'class-methods-use-this': ERROR,
    'comma-dangle': [
      ERROR,
      'always-multiline',
    ],
    'comma-spacing': [
      ERROR,
      { before: false, after: true },
    ],
    'comma-style': [
      ERROR,
      'last',
    ],
    complexity: [
      WARN,
      COMPLEXITY_MAX,
    ],
    'computed-property-spacing': [
      ERROR,
      'never',
    ],
    'consistent-return': ERROR,
    'consistent-this': [
      ERROR,
      '_this',
    ],
    'constructor-super': ERROR,
    curly: ERROR,
    'default-case': ERROR,
    'dot-location': [
      ERROR,
      'property',
    ],
    'dot-notation': ERROR,
    'eol-last': ERROR,
    eqeqeq: [
      ERROR,
      'smart',
    ],
    'func-call-spacing': [
      ERROR,
      'never',
    ],
    'func-names': WARN,
    'func-name-matching': OFF,
    'func-style': [
      ERROR,
      'declaration',
      { allowArrowFunctions: true },
    ],
    'global-require': ERROR,
    'guard-for-in': ERROR,
    'handle-callback-err': ERROR,
    'id-blacklist': OFF,
    'id-length': OFF,
    'id-match': OFF,
    indent: [
      ERROR,
      TABWIDTH,
      { SwitchCase: 1 },
    ],
    'init-declarations': OFF,
    'jsx-quotes': OFF,
    'key-spacing': [
      ERROR,
      { beforeColon: false, afterColon: true },
    ],
    'line-comment-position': OFF,
    'linebreak-style': [ERROR, 'unix'],
    'lines-around-comment': OFF,
    'lines-around-directive': OFF,
    'max-len': [
      WARN,
      100,
      TABWIDTH,
      {
        ignoreComments: true,
        ignorePattern: '^\\s+import\\s.+\\sfrom\\s.+;$',
      },
    ],
    'max-lines': [
      WARN,
      { max: 1000 },
    ],
    'max-nested-callbacks': [
      ERROR,
      NESTED_CALLBACK_MAX,
    ],
    'max-params': [
      ERROR,
      ARGUMENTS_MAX,
    ],
    'max-statements-per-line': [
      WARN,
      { max: 2 },
    ],
    'multiline-ternary': OFF,
    'new-cap': [
      ERROR,
      { newIsCap: true, capIsNew: true },
    ],
    'new-parens': ERROR,
    'newline-after-var': WARN,
    'newline-before-return': OFF,
    'newline-per-chained-call': [
      WARN,
      { ignoreChainWithDepth: 2 },
    ],
    'no-alert': ERROR,
    'no-array-constructor': ERROR,
    'no-await-in-loop': ERROR,
    'no-caller': ERROR,
    'no-case-declarations': ERROR,
    'no-catch-shadow': ERROR,
    'no-class-assign': ERROR,
    'no-confusing-arrow': OFF,
    'no-cond-assign': ERROR,
    'no-console': ERROR,
    'no-const-assign': ERROR,
    'no-constant-condition': ERROR,
    'no-continue': OFF,
    'no-control-regex': ERROR,
    'no-debugger': ERROR,
    'no-delete-var': ERROR,
    'no-div-regex': ERROR,
    'no-dupe-args': ERROR,
    'no-dupe-class-members': OFF,
    'no-dupe-keys': ERROR,
    'no-duplicate-case': ERROR,
    'no-duplicate-imports': ERROR,
    'no-else-return': ERROR,
    'no-empty': ERROR,
    'no-empty-character-class': ERROR,
    'no-empty-pattern': ERROR,
    'no-global-assign': ERROR,
    'no-labels': ERROR,
    'no-eq-null': ERROR,
    'no-eval': ERROR,
    'no-ex-assign': ERROR,
    'no-extend-native': ERROR,
    'no-extra-bind': ERROR,
    'no-extra-boolean-cast': ERROR,
    'no-extra-label': OFF,
    'no-extra-parens': OFF,
    'no-extra-semi': ERROR,
    'no-fallthrough': ERROR,
    'no-floating-decimal': ERROR,
    'no-func-assign': ERROR,
    'no-implicit-coercion': OFF,
    'no-implicit-globals': OFF,
    'no-implied-eval': ERROR,
    'no-inline-comments': OFF,
    'no-inner-declarations': ERROR,
    'no-invalid-regexp': ERROR,
    'no-invalid-this': WARN,
    'no-irregular-whitespace': ERROR,
    'no-iterator': ERROR,
    'no-label-var': ERROR,
    'no-lone-blocks': ERROR,
    'no-lonely-if': ERROR,
    'no-loop-func': ERROR,
    'no-magic-numbers': [
      ERROR,
      {
        ignoreArrayIndexes: true,
        ignore: [
          -1,
          0, 1,
          100, 101, 102,
          200, 201, 202, 203, 204, 205, 206, 207, 210, 226,
          300, 301, 302, 303, 304, 305, 306, 307, 308, 310,
          400, 401, 402, 403, 404, 405, 406, 407, 408, 409,
          410, 411, 412, 413, 414, 415, 416, 417, 418,
          421, 422, 423, 424, 425, 426, 427, 428, 429,
          431, 449, 450, 451, 456, 499,
          500, 501, 502, 503, 504, 505, 506, 507, 508, 509,
          510, 511, 520,
        ],
      },
    ],
    'no-mixed-operators': [
      ERROR,
      { allowSamePrecedence: false },
    ],
    'no-mixed-requires': OFF,
    'no-mixed-spaces-and-tabs': ERROR,
    'no-multi-spaces': ERROR,
    'no-multi-str': ERROR,
    'no-multiple-empty-lines': [
      ERROR,
      { max: 2 },
    ],
    'no-native-reassign': ERROR,
    'no-negated-condition': OFF,
    'no-negated-in-lhs': ERROR,
    'no-nested-ternary': OFF,
    'no-new': WARN,
    'no-new-func': ERROR,
    'no-new-object': ERROR,
    'no-new-require': ERROR,
    'no-new-symbol': ERROR,
    'no-obj-calls': ERROR,
    'no-octal': ERROR,
    'no-octal-escape': ERROR,
    'no-param-reassign': OFF,
    'no-path-concat': ERROR,
    'no-process-env': OFF,
    'no-process-exit': OFF,
    'no-proto': ERROR,
    'no-prototype-builtins': WARN,
    'no-redeclare': ERROR,
    'no-regex-spaces': ERROR,
    'no-reserved-keys': OFF,
    'no-restricted-globals': OFF,
    'no-restricted-imports': OFF,
    'no-restricted-modules': OFF,
    'no-restricted-syntax': OFF,
    'no-restricted-properties': [
      ERROR,
      {
        object: 'describe',
        property: 'only',
        message: 'Using describe.only is not allowed',
      }, {
        object: 'it',
        property: 'only',
        message: 'Using it.only is not allowed',
      },
    ],
    'no-return-assign': ERROR,
    'no-return-await': ERROR,
    'no-script-url': ERROR,
    'no-self-assign': ERROR,
    'no-self-compare': ERROR,
    'no-sequences': ERROR,
    'no-shadow': WARN,
    'no-shadow-restricted-names': ERROR,
    'no-spaced-func': ERROR,
    'no-sparse-arrays': ERROR,
    'no-sync': OFF,
    'no-tabs': ERROR,
    'no-ternary': OFF,
    'no-template-curly-in-string': WARN,
    'no-throw-literal': ERROR,
    'no-this-before-super': ERROR,
    'no-trailing-spaces': ERROR,
    'no-undef': ERROR,
    'no-undef-init': ERROR,
    'no-undefined': ERROR,
    'no-underscore-dangle': OFF,
    'no-unexpected-multiline': ERROR,
    'no-unmodified-loop-condition': ERROR,
    'no-unneeded-ternary': ERROR,
    'no-unreachable': ERROR,
    'no-unsafe-finally': ERROR,
    'no-unsafe-negation': ERROR,
    'no-unused-expressions': ERROR,
    'no-unused-labels': OFF,
    'no-unused-vars': [
      ERROR,
      { args: 'none' },
    ],
    'no-use-before-define': [
      ERROR,
      'nofunc',
    ],
    'no-useless-call': ERROR,
    'no-useless-computed-key': ERROR,
    'no-useless-concat': ERROR,
    'no-useless-constructor': ERROR,
    'no-useless-escape': WARN,
    'no-useless-rename': WARN,
    'no-useless-return': ERROR,
    'no-void': ERROR,
    'no-warning-comments': OFF,
    'no-with': ERROR,
    'no-whitespace-before-property': WARN,
    'object-curly-newline': OFF,
    'object-curly-spacing': [
      ERROR,
      'always',
    ],
    'object-property-newline': [
      ERROR,
      { allowMultiplePropertiesPerLine: true },
    ],
    'one-var': [
      ERROR,
      'never',
    ],
    'one-var-declaration-per-line': OFF,
    'operator-assignment': [
      ERROR,
      'always',
    ],
    'operator-linebreak': [
      ERROR,
      'after',
    ],
    'padded-blocks': OFF,
    'prefer-arrow-callback': OFF,
    'prefer-destructuring': OFF,
    'prefer-numeric-literals': ERROR,
    'prefer-reflect': OFF,
    'prefer-rest-params': WARN,
    'prefer-spread': OFF,
    'prefer-template': OFF,
    'quote-props': [
      ERROR,
      'as-needed',
    ],
    quotes: [
      ERROR,
      'single',
    ],
    radix: ERROR,
    'require-await': WARN,
    'require-jsdoc': OFF,
    'require-yield': ERROR,
    'rest-spread-spacing': [
      ERROR,
      'never',
    ],
    semi: [
      ERROR,
      'always',
    ],
    'semi-spacing': [
      ERROR,
      { before: false, after: true },
    ],
    'sort-imports': OFF,
    'sort-keys': OFF,
    'sort-vars': OFF,
    'space-after-keywords': OFF,
    'space-before-blocks': [
      ERROR,
      'always',
    ],
    'space-before-function-paren': [
      ERROR,
      'never',
    ],
    'keyword-spacing': [
      'error',
      {
        before: true,
        after: true,
        overrides: {
          if: { after: false },
          for: { after: false },
          while: { after: false },
        },
      },
    ],
    'space-in-parens': [
      ERROR,
      'never',
    ],
    'space-infix-ops': ERROR,
    'space-unary-ops': ERROR,
    'spaced-comment': [
      ERROR,
      'always',
    ],
    'symbol-description': ERROR,
    'template-curly-spacing': [
      ERROR,
      'never',
    ],
    'unicode-bom': ERROR,
    'valid-jsdoc': ERROR,
    'valid-typeof': ERROR,
    'vars-on-top': ERROR,
    'wrap-iife': ERROR,
    'wrap-regex': ERROR,
    yoda: [
      ERROR,
      'always',
    ],
  },
};
<commit_msg>feat(rules): add eslint v3.15.0 rules<commit_after>'use strict';

const OFF = 'off';
const ERROR = 'error';
const WARN = 'warn';

const COMPLEXITY_MAX = 5;
const TABWIDTH = 2;
const NESTED_CALLBACK_MAX = 4;
const ARGUMENTS_MAX = 4;

module.exports = {
  extends: 'eslint:recommended',
  env: {
    node: true,
  },
  globals: {
    require: false,
    module: false,
  },
  parserOptions: {
    ecmaVersion: 6,
    sourceType: 'script',
    arrowFunctions: true,
    binaryLiterals: true,
    blockBindings: true,
    classes: false,
    defaultParams: true,
    destructuring: true,
    forOf: true,
    generators: false,
    modules: false,
    objectLiteralComputedProperties: true,
    objectLiteralDuplicateProperties: true,
    objectLiteralShorthandMethods: true,
    objectLiteralShorthandProperties: true,
    octalLiterals: false,
    regexUFlag: true,
    regexYFlag: true,
    restParams: true,
    spread: true,
    superInFunctions: true,
    templateStrings: true,
    unicodeCodePointEscapes: true,
  },
  rules: {
    'accessor-pairs': [
      ERROR,
      { getWithoutSet: false, setWithoutGet: true },
    ],
    'array-bracket-spacing': [
      ERROR,
      'never',
    ],
    'array-callback-return': ERROR,
    'arrow-body-style': [
      ERROR,
      'as-needed',
    ],
    'arrow-parens': [
      ERROR,
      'as-needed',
      { requireForBlockBody: true },
    ],
    'arrow-spacing': ERROR,
    'block-spacing': [
      ERROR,
      'always',
    ],
    'brace-style': OFF,
    'callback-return': [
      ERROR, [
        'callback',
        'cb',
        'next',
        'done',
      ],
    ],
    camelcase: OFF,
    'capitalized-comments': OFF,
    'class-methods-use-this': ERROR,
    'comma-dangle': [
      ERROR,
      'always-multiline',
    ],
    'comma-spacing': [
      ERROR,
      { before: false, after: true },
    ],
    'comma-style': [
      ERROR,
      'last',
    ],
    complexity: [
      WARN,
      COMPLEXITY_MAX,
    ],
    'computed-property-spacing': [
      ERROR,
      'never',
    ],
    'consistent-return': ERROR,
    'consistent-this': [
      ERROR,
      '_this',
    ],
    'constructor-super': ERROR,
    curly: ERROR,
    'default-case': ERROR,
    'dot-location': [
      ERROR,
      'property',
    ],
    'dot-notation': ERROR,
    'eol-last': ERROR,
    eqeqeq: [
      ERROR,
      'smart',
    ],
    'func-call-spacing': [
      ERROR,
      'never',
    ],
    'func-names': WARN,
    'func-name-matching': OFF,
    'func-style': [
      ERROR,
      'declaration',
      { allowArrowFunctions: true },
    ],
    'global-require': ERROR,
    'guard-for-in': ERROR,
    'handle-callback-err': ERROR,
    'id-blacklist': OFF,
    'id-length': OFF,
    'id-match': OFF,
    indent: [
      ERROR,
      TABWIDTH,
      { SwitchCase: 1 },
    ],
    'init-declarations': OFF,
    'jsx-quotes': OFF,
    'key-spacing': [
      ERROR,
      { beforeColon: false, afterColon: true },
    ],
    'line-comment-position': OFF,
    'linebreak-style': [ERROR, 'unix'],
    'lines-around-comment': OFF,
    'lines-around-directive': OFF,
    'max-len': [
      WARN,
      100,
      TABWIDTH,
      {
        ignoreComments: true,
        ignorePattern: '^\\s+import\\s.+\\sfrom\\s.+;$',
      },
    ],
    'max-lines': [
      WARN,
      { max: 1000 },
    ],
    'max-nested-callbacks': [
      ERROR,
      NESTED_CALLBACK_MAX,
    ],
    'max-params': [
      ERROR,
      ARGUMENTS_MAX,
    ],
    'max-statements-per-line': [
      WARN,
      { max: 2 },
    ],
    'multiline-ternary': OFF,
    'new-cap': [
      ERROR,
      { newIsCap: true, capIsNew: true },
    ],
    'new-parens': ERROR,
    'newline-after-var': WARN,
    'newline-before-return': OFF,
    'newline-per-chained-call': [
      WARN,
      { ignoreChainWithDepth: 2 },
    ],
    'no-alert': ERROR,
    'no-array-constructor': ERROR,
    'no-await-in-loop': ERROR,
    'no-caller': ERROR,
    'no-case-declarations': ERROR,
    'no-catch-shadow': ERROR,
    'no-class-assign': ERROR,
    'no-confusing-arrow': OFF,
    'no-cond-assign': ERROR,
    'no-console': ERROR,
    'no-const-assign': ERROR,
    'no-constant-condition': ERROR,
    'no-continue': OFF,
    'no-control-regex': ERROR,
    'no-debugger': ERROR,
    'no-delete-var': ERROR,
    'no-div-regex': ERROR,
    'no-dupe-args': ERROR,
    'no-dupe-class-members': OFF,
    'no-dupe-keys': ERROR,
    'no-duplicate-case': ERROR,
    'no-duplicate-imports': ERROR,
    'no-else-return': ERROR,
    'no-empty': ERROR,
    'no-empty-character-class': ERROR,
    'no-empty-pattern': ERROR,
    'no-global-assign': ERROR,
    'no-labels': ERROR,
    'no-eq-null': ERROR,
    'no-eval': ERROR,
    'no-ex-assign': ERROR,
    'no-extend-native': ERROR,
    'no-extra-bind': ERROR,
    'no-extra-boolean-cast': ERROR,
    'no-extra-label': OFF,
    'no-extra-parens': OFF,
    'no-extra-semi': ERROR,
    'no-fallthrough': ERROR,
    'no-floating-decimal': ERROR,
    'no-func-assign': ERROR,
    'no-implicit-coercion': OFF,
    'no-implicit-globals': OFF,
    'no-implied-eval': ERROR,
    'no-inline-comments': OFF,
    'no-inner-declarations': ERROR,
    'no-invalid-regexp': ERROR,
    'no-invalid-this': WARN,
    'no-irregular-whitespace': ERROR,
    'no-iterator': ERROR,
    'no-label-var': ERROR,
    'no-lone-blocks': ERROR,
    'no-lonely-if': ERROR,
    'no-loop-func': ERROR,
    'no-magic-numbers': [
      ERROR,
      {
        ignoreArrayIndexes: true,
        ignore: [
          -1,
          0, 1,
          100, 101, 102,
          200, 201, 202, 203, 204, 205, 206, 207, 210, 226,
          300, 301, 302, 303, 304, 305, 306, 307, 308, 310,
          400, 401, 402, 403, 404, 405, 406, 407, 408, 409,
          410, 411, 412, 413, 414, 415, 416, 417, 418,
          421, 422, 423, 424, 425, 426, 427, 428, 429,
          431, 449, 450, 451, 456, 499,
          500, 501, 502, 503, 504, 505, 506, 507, 508, 509,
          510, 511, 520,
        ],
      },
    ],
    'no-mixed-operators': [
      ERROR,
      { allowSamePrecedence: false },
    ],
    'no-mixed-requires': OFF,
    'no-mixed-spaces-and-tabs': ERROR,
    'no-multi-spaces': ERROR,
    'no-multi-str': ERROR,
    'no-multiple-empty-lines': [
      ERROR,
      { max: 2 },
    ],
    'no-native-reassign': ERROR,
    'no-negated-condition': OFF,
    'no-negated-in-lhs': ERROR,
    'no-nested-ternary': OFF,
    'no-new': WARN,
    'no-new-func': ERROR,
    'no-new-object': ERROR,
    'no-new-require': ERROR,
    'no-new-symbol': ERROR,
    'no-obj-calls': ERROR,
    'no-octal': ERROR,
    'no-octal-escape': ERROR,
    'no-param-reassign': OFF,
    'no-path-concat': ERROR,
    'no-process-env': OFF,
    'no-process-exit': OFF,
    'no-proto': ERROR,
    'no-prototype-builtins': WARN,
    'no-redeclare': ERROR,
    'no-regex-spaces': ERROR,
    'no-reserved-keys': OFF,
    'no-restricted-globals': OFF,
    'no-restricted-imports': OFF,
    'no-restricted-modules': OFF,
    'no-restricted-syntax': OFF,
    'no-restricted-properties': [
      ERROR,
      {
        object: 'describe',
        property: 'only',
        message: 'Using describe.only is not allowed',
      }, {
        object: 'it',
        property: 'only',
        message: 'Using it.only is not allowed',
      },
    ],
    'no-return-assign': ERROR,
    'no-return-await': ERROR,
    'no-script-url': ERROR,
    'no-self-assign': ERROR,
    'no-self-compare': ERROR,
    'no-sequences': ERROR,
    'no-shadow': WARN,
    'no-shadow-restricted-names': ERROR,
    'no-spaced-func': ERROR,
    'no-sparse-arrays': ERROR,
    'no-sync': OFF,
    'no-tabs': ERROR,
    'no-ternary': OFF,
    'no-template-curly-in-string': WARN,
    'no-throw-literal': ERROR,
    'no-this-before-super': ERROR,
    'no-trailing-spaces': ERROR,
    'no-undef': ERROR,
    'no-undef-init': ERROR,
    'no-undefined': ERROR,
    'no-underscore-dangle': OFF,
    'no-unexpected-multiline': ERROR,
    'no-unmodified-loop-condition': ERROR,
    'no-unneeded-ternary': ERROR,
    'no-unreachable': ERROR,
    'no-unsafe-finally': ERROR,
    'no-unsafe-negation': ERROR,
    'no-unused-expressions': ERROR,
    'no-unused-labels': OFF,
    'no-unused-vars': [
      ERROR,
      { args: 'none' },
    ],
    'no-use-before-define': [
      ERROR,
      'nofunc',
    ],
    'no-useless-call': ERROR,
    'no-useless-computed-key': ERROR,
    'no-useless-concat': ERROR,
    'no-useless-constructor': ERROR,
    'no-useless-escape': WARN,
    'no-useless-rename': WARN,
    'no-useless-return': ERROR,
    'no-void': ERROR,
    'no-warning-comments': OFF,
    'no-with': ERROR,
    'no-whitespace-before-property': WARN,
    'object-curly-newline': OFF,
    'object-curly-spacing': [
      ERROR,
      'always',
    ],
    'object-property-newline': [
      ERROR,
      { allowMultiplePropertiesPerLine: true },
    ],
    'one-var': [
      ERROR,
      'never',
    ],
    'one-var-declaration-per-line': OFF,
    'operator-assignment': [
      ERROR,
      'always',
    ],
    'operator-linebreak': [
      ERROR,
      'after',
    ],
    'padded-blocks': OFF,
    'prefer-arrow-callback': OFF,
    'prefer-destructuring': OFF,
    'prefer-numeric-literals': ERROR,
    'prefer-reflect': OFF,
    'prefer-rest-params': WARN,
    'prefer-spread': OFF,
    'prefer-template': OFF,
    'quote-props': [
      ERROR,
      'as-needed',
    ],
    quotes: [
      ERROR,
      'single',
    ],
    radix: ERROR,
    'require-await': WARN,
    'require-jsdoc': OFF,
    'require-yield': ERROR,
    'rest-spread-spacing': [
      ERROR,
      'never',
    ],
    semi: [
      ERROR,
      'always',
    ],
    'semi-spacing': [
      ERROR,
      { before: false, after: true },
    ],
    'sort-imports': OFF,
    'sort-keys': OFF,
    'sort-vars': OFF,
    'space-after-keywords': OFF,
    'space-before-blocks': [
      ERROR,
      'always',
    ],
    'space-before-function-paren': [
      ERROR,
      'never',
    ],
    'keyword-spacing': [
      'error',
      {
        before: true,
        after: true,
        overrides: {
          if: { after: false },
          for: { after: false },
          while: { after: false },
        },
      },
    ],
    'space-in-parens': [
      ERROR,
      'never',
    ],
    'space-infix-ops': ERROR,
    'space-unary-ops': ERROR,
    'spaced-comment': [
      ERROR,
      'always',
    ],
    'symbol-description': ERROR,
    'template-curly-spacing': [
      ERROR,
      'never',
    ],
    'template-tag-spacing': OFF,
    'unicode-bom': ERROR,
    'valid-jsdoc': ERROR,
    'valid-typeof': ERROR,
    'vars-on-top': ERROR,
    'wrap-iife': ERROR,
    'wrap-regex': ERROR,
    yoda: [
      ERROR,
      'always',
    ],
  },
};
<|endoftext|>


<commit_before>'use strict';

const { UndefinedOptionError } = require('./errors')
const { convert } = require('./schema')

/**
 * Create new configuration.
 * @param {Object} schema – Schema definition.
 * @param {...Object} options – User-defined options.
 * @return {Object} Immutable option object.
 * @throws {UndefinedOptionError} Options must not be undefined.
 */
function create (schema, ...options) {
  const set = Object.assign({}, ...options)
  const config = {}

  for (let key in schema) {
    let def = schema[key]
    let value = extract(set, key, def)

    if (value === undefined) {
      throw new UndefinedOptionError(key)
    }

    define(config, key, value)
  }

  return config
}

/**
 * Extract a key from a source object.
 * @param {Object} src - Source object.
 * @param {string} key - Key name.
 * @param {Object} def - Key schema definition.
 * @param {string} [def.type=string] – Option type.
 * @param {*} [def.default] – Default value.
 * @return {*} Extracted and converted value.
 */
function extract (src, key, def) {
  const type = def['type'] || 'string'
  const fallback = def['default']
  const value = src[key]

  if (value === undefined && fallback !== undefined) {
    return fallback
  }

  if (value !== undefined) {
    return convert(value, type)
  }

  return undefined
}

/**
 * Define a read-only property on an object.
 * @param {Object} obj – Target object.
 * @param {string} key – Property name.
 * @param {*} value – Property value.
 */
function define (obj, key, value) {
  Object.defineProperty(obj, key, {
    enumerable: true,
    writable: false,
    value
  })
}

/**
 * Config module.
 * @type {Object}
 * @module zenconfig/lib/config
 */
module.exports = create
<commit_msg>Improve default args handling in extract function<commit_after>'use strict';

const { UndefinedOptionError } = require('./errors')
const { convert } = require('./schema')

/**
 * Create new configuration.
 * @param {Object} schema – Schema definition.
 * @param {...Object} options – User-defined options.
 * @return {Object} Immutable option object.
 * @throws {UndefinedOptionError} Options must not be undefined.
 */
function create (schema, ...options) {
  const set = Object.assign({}, ...options)
  const config = {}

  for (let key in schema) {
    let def = schema[key]
    let value = extract(set, key, def)

    if (value === undefined) {
      throw new UndefinedOptionError(key)
    }

    define(config, key, value)
  }

  return config
}

/**
 * Extract a key from a source object.
 * @param {Object} src - Source object.
 * @param {string} key - Key name.
 * @param {Object} def - Key schema definition.
 * @param {string} [def.type=string] – Option type.
 * @param {*} [def.default] – Default value.
 * @return {*} Extracted and converted value.
 */
function extract (src, key, def = { type: 'string' }) {
  const { type, default: fallback } = def
  const value = src[key]

  if (value === undefined && fallback !== undefined) {
    return fallback
  }

  if (value !== undefined) {
    return convert(value, type)
  }

  return undefined
}

/**
 * Define a read-only property on an object.
 * @param {Object} obj – Target object.
 * @param {string} key – Property name.
 * @param {*} value – Property value.
 */
function define (obj, key, value) {
  Object.defineProperty(obj, key, {
    enumerable: true,
    writable: false,
    value
  })
}

/**
 * Config module.
 * @type {Object}
 * @module zenconfig/lib/config
 */
module.exports = create
<|endoftext|>


<commit_before>'use strict';

const packageInfo = require('../package.json');
const pluginInfo = require('../plugin.json');

const pluginId = pluginInfo.id.replace('nodebb-plugin-', '');
const NodeBB = require('./nodebb');

const Config = {};

const features = [
	{
		name: 'Gists',
		id: 'gist',
		description: 'Easily create Gists',
		icon: 'fa-github-alt',
		button: 'Create Gist',
		enabled: true,
	},
	{
		name: 'Archive',
		id: 'archive',
		description: 'View older posts',
		icon: 'fa-archive',
		button: 'View Archive',
		enabled: true,
	},
	{
		name: 'Bugs',
		id: 'bug',
		description: 'Report bugs quickly',
		icon: 'fa-bug',
		button: 'Report Bug',
		enabled: false,
	},
];

const adminDefaults = {
	toggles: {
		guestsAllowed: false,
		headerLink: false,
		features: (function () {
			const defaults = {};
			features.forEach((el) => {
				defaults[el.id] = el.enabled;
			});

			return defaults;
		}()),
	},
	limits: {
		shoutLimit: '25',
	},
	version: '',
};

const userDefaults = {
	'toggles:sound': 1,
	'toggles:notification': 1,
	'toggles:hide': 0,
	muted: '',
};

Config.plugin = {
	name: pluginInfo.name,
	id: pluginId,
	version: packageInfo.version,
	description: packageInfo.description,
	icon: 'fa-bullhorn',
};

Config.init = function (callback) {
	Config.global = new NodeBB.Settings(Config.plugin.id, Config.plugin.version, adminDefaults, () => {
		callback();
	});
};

Config.global = {};

Config.adminSockets = {
	sync: function () {
		Config.global.sync();
	},
	getDefaults: function (socket, data, callback) {
		callback(null, Config.global.createDefaultWrapper());
	},
};

Config.user = {};
Config.user.sockets = {};

Config.user.get = async function (data) {
	if (!data) {
		throw new Error('[[error:invalid-data]]');
	}

	const prefix = `${Config.plugin.id}:`;
	if (!data.settings) {
		data.settings = {};
	}

	Object.keys(userDefaults).forEach((key) => {
		const fullKey = prefix + key;
		data.settings[fullKey] = data.settings.hasOwnProperty(fullKey) ? data.settings[fullKey] : userDefaults[key];
	});

	data.settings['shoutbox:shoutLimit'] = parseInt(Config.global.get('limits.shoutLimit'), 10);
	return data;
};

// get user shoutbox settings
Config.user.load = async function (uid) {
	const settings = await NodeBB.User.getSettings(uid);
	const sbSettings = {};
	const prefix = `${Config.plugin.id}:`;
	Object.keys(userDefaults).forEach((key) => {
		const fullKey = prefix + key;
		sbSettings[fullKey] = settings.hasOwnProperty(fullKey) ? settings[fullKey] : userDefaults[key];
	});
	sbSettings['shoutbox:shoutLimit'] = parseInt(Config.global.get('limits.shoutLimit'), 10);
	return sbSettings;
};

Config.user.save = async function (hookData) {
	if (!hookData || !hookData.uid || !hookData.settings) {
		throw new Error('[[error:invalid-data]]');
	}

	Object.keys(userDefaults).forEach((key) => {
		const fullKey = `${Config.plugin.id}:${key}`;
		if (hookData.data.hasOwnProperty(fullKey)) {
			hookData.settings[fullKey] = hookData.data[fullKey];
		}
	});
	return hookData;
};

Config.user.sockets.getSettings = async function (socket) {
	if (!socket.uid) {
		throw new Error('not-logged-in');
	}
	return {
		settings: await Config.user.load(socket.uid),
	};
};

Config.user.sockets.saveSettings = async function (socket, data) {
	if (!socket.uid || !data || !data.settings) {
		throw new Error('[[error:invalid-data]]');
	}

	data.uid = socket.uid;
	await NodeBB.api.users.updateSettings(socket, data);
};

Config.getTemplateData = function () {
	const featureConfig = Config.global.get('toggles.features');
	const data = {};

	data.features = features.slice(0).map((item) => {
		item.enabled = featureConfig[item.id];
		return item;
	});

	return data;
};

module.exports = Config;
<commit_msg>fix: fix crash if filter:config.get is triggered early aka from emoji plugin .init<commit_after>'use strict';

const packageInfo = require('../package.json');
const pluginInfo = require('../plugin.json');

const pluginId = pluginInfo.id.replace('nodebb-plugin-', '');
const NodeBB = require('./nodebb');

const Config = {};

const features = [
	{
		name: 'Gists',
		id: 'gist',
		description: 'Easily create Gists',
		icon: 'fa-github-alt',
		button: 'Create Gist',
		enabled: true,
	},
	{
		name: 'Archive',
		id: 'archive',
		description: 'View older posts',
		icon: 'fa-archive',
		button: 'View Archive',
		enabled: true,
	},
	{
		name: 'Bugs',
		id: 'bug',
		description: 'Report bugs quickly',
		icon: 'fa-bug',
		button: 'Report Bug',
		enabled: false,
	},
];

const adminDefaults = {
	toggles: {
		guestsAllowed: false,
		headerLink: false,
		features: (function () {
			const defaults = {};
			features.forEach((el) => {
				defaults[el.id] = el.enabled;
			});

			return defaults;
		}()),
	},
	limits: {
		shoutLimit: '25',
	},
	version: '',
};

const userDefaults = {
	'toggles:sound': 1,
	'toggles:notification': 1,
	'toggles:hide': 0,
	muted: '',
};

Config.plugin = {
	name: pluginInfo.name,
	id: pluginId,
	version: packageInfo.version,
	description: packageInfo.description,
	icon: 'fa-bullhorn',
};

Config.init = function (callback) {
	Config.global = new NodeBB.Settings(Config.plugin.id, Config.plugin.version, adminDefaults, () => {
		callback();
	});
};

Config.global = {};

Config.adminSockets = {
	sync: function () {
		Config.global.sync();
	},
	getDefaults: function (socket, data, callback) {
		callback(null, Config.global.createDefaultWrapper());
	},
};

Config.user = {};
Config.user.sockets = {};

Config.user.get = async function (data) {
	if (!data) {
		throw new Error('[[error:invalid-data]]');
	}
	if (!Config.global.get) {
		return data;
	}
	const prefix = `${Config.plugin.id}:`;
	if (!data.settings) {
		data.settings = {};
	}

	Object.keys(userDefaults).forEach((key) => {
		const fullKey = prefix + key;
		data.settings[fullKey] = data.settings.hasOwnProperty(fullKey) ? data.settings[fullKey] : userDefaults[key];
	});

	data.settings['shoutbox:shoutLimit'] = parseInt(Config.global.get('limits.shoutLimit'), 10);
	return data;
};

// get user shoutbox settings
Config.user.load = async function (uid) {
	const settings = await NodeBB.User.getSettings(uid);
	const sbSettings = {};
	const prefix = `${Config.plugin.id}:`;
	Object.keys(userDefaults).forEach((key) => {
		const fullKey = prefix + key;
		sbSettings[fullKey] = settings.hasOwnProperty(fullKey) ? settings[fullKey] : userDefaults[key];
	});
	sbSettings['shoutbox:shoutLimit'] = parseInt(Config.global.get('limits.shoutLimit'), 10);
	return sbSettings;
};

Config.user.save = async function (hookData) {
	if (!hookData || !hookData.uid || !hookData.settings) {
		throw new Error('[[error:invalid-data]]');
	}

	Object.keys(userDefaults).forEach((key) => {
		const fullKey = `${Config.plugin.id}:${key}`;
		if (hookData.data.hasOwnProperty(fullKey)) {
			hookData.settings[fullKey] = hookData.data[fullKey];
		}
	});
	return hookData;
};

Config.user.sockets.getSettings = async function (socket) {
	if (!socket.uid) {
		throw new Error('not-logged-in');
	}
	return {
		settings: await Config.user.load(socket.uid),
	};
};

Config.user.sockets.saveSettings = async function (socket, data) {
	if (!socket.uid || !data || !data.settings) {
		throw new Error('[[error:invalid-data]]');
	}

	data.uid = socket.uid;
	await NodeBB.api.users.updateSettings(socket, data);
};

Config.getTemplateData = function () {
	const featureConfig = Config.global.get('toggles.features');
	const data = {};

	data.features = features.slice(0).map((item) => {
		item.enabled = featureConfig[item.id];
		return item;
	});

	return data;
};

module.exports = Config;
<|endoftext|>