<commit_before>import React from 'react';
import { FormattedMessage } from 'react-intl';
import { sortBy } from 'lodash';

import PermissionSets from './permissions/PermissionSets';
import PatronGroupsSettings from './PatronGroupsSettings';
import AddressTypesSettings from './AddressTypesSettings';
import ProfilePictureSettings from './ProfilePictureSettings';
import OwnerSettings from './OwnerSettings';
import FeeFineSettings from './FeeFineSettings';
import WaiveSettings from './WaiveSettings';
import PaymentSettings from './PaymentSettings';
import CommentRequiredSettings from './CommentRequiredSettings';
import RefundReasonsSettings from './RefundReasonsSettings';
import TransferAccountsSettings from './TransferAccountsSettings';
import CustomFieldsSettingsPane from './CustomFieldsSettings';
import ConditionsSettings from './ConditionsSettings';
import LimitsSettings from './LimitsSettings';
import DepartmentsSettings from './DepartmentsSettings';
import BlockTemplates from './patronBlocks/BlockTemplates';
import TransferCriteriaSettings from './TransferCriteriaSettings';

const settingsGeneral = [
  {
    route: 'perms',
    label: <FormattedMessage id="ui-users.settings.permissionSet" />,
    component: PermissionSets,
    perm: 'ui-users.settings.permsets',
  },
  {
    route: 'groups',
    label: <FormattedMessage id="ui-users.settings.patronGroups" />,
    component: PatronGroupsSettings,
    perm: 'ui-users.settings.usergroups',
  },
  {
    route: 'addresstypes',
    label: <FormattedMessage id="ui-users.settings.addressTypes" />,
    component: AddressTypesSettings,
    perm: 'ui-users.settings.addresstypes',
  },
  {
    route: 'departments',
    label: <FormattedMessage id="ui-users.settings.departments" />,
    component: DepartmentsSettings,
    perm: 'ui-users.settings.departments.view'
  },
  {
    route: 'profilepictures',
    label: <FormattedMessage id="ui-users.settings.profilePictures" />,
    component: ProfilePictureSettings,
    perm: 'ui-users.settings.profilePictures'
  },
  {
    route: 'custom-fields',
    label: <FormattedMessage id="ui-users.settings.customFields" />,
    component: CustomFieldsSettingsPane,
    perm: 'ui-users.settings.customfields.view',
  }
];

const settingsFeefines = [
  {
    route: 'owners',
    label: <FormattedMessage id="ui-users.settings.owners" />,
    component: OwnerSettings,
    perm: 'ui-users.settings.owners',
  },
  {
    route: 'feefinestable',
    label: <FormattedMessage id="ui-users.settings.manualCharges" />,
    component: FeeFineSettings,
    perm: 'ui-users.settings.feefines',
  },
  {
    route: 'waivereasons',
    label: <FormattedMessage id="ui-users.settings.waiveReasons" />,
    component: WaiveSettings,
    perm: 'ui-users.settings.waives',
  },
  {
    route: 'payments',
    label: <FormattedMessage id="ui-users.settings.paymentMethods" />,
    component: PaymentSettings,
    perm: 'ui-users.settings.payments',
  },
  {
    route: 'refunds',
    label: <FormattedMessage id="ui-users.settings.refundReasons" />,
    component: RefundReasonsSettings,
    perm: 'ui-users.settings.refunds',
  },
  {
    route: 'comments',
    label: <FormattedMessage id="ui-users.settings.commentRequired" />,
    component: CommentRequiredSettings,
    perm: 'ui-users.settings.comments',
  },
  {
    route: 'transfers',
    label: <FormattedMessage id="ui-users.settings.transferAccounts" />,
    component: TransferAccountsSettings,
    perm: 'ui-users.settings.transfers',
  },
  {
    route: 'transfer-criteria',
    label: <FormattedMessage id="ui-users.settings.transferCriteria" />,
    component: TransferCriteriaSettings,
    perm: 'ui-plugin-bursar-export.bursar-exports.all',
  },
];

const settingsPatronBlocks = [
  {
    route: 'conditions',
    label: <FormattedMessage id="ui-users.settings.conditions" />,
    component: ConditionsSettings,
    perm: 'ui-users.settings.conditions',
  },
  {
    route: 'limits',
    label: <FormattedMessage id="ui-users.settings.limits" />,
    component: LimitsSettings,
    perm: 'ui-users.settings.limits',
  },
  {
    route: 'manual-block-templates',
    label: <FormattedMessage id="ui-users.settings.manualBlockTemplates" />,
    component: BlockTemplates,
    perm: 'ui-users.settings.patron-block-templates',
    interface: 'feesfines',
  },
];

export default [
  {
    label: <FormattedMessage id="ui-users.settings.general" />,
    pages: sortBy(settingsGeneral, ['label']),
  },
  {
    label: <FormattedMessage id="ui-users.settings.feefine" />,
    pages: sortBy(settingsFeefines, ['label']),
    interface: 'feesfines',
  },
  {
    label: <FormattedMessage id="ui-users.settings.patronBlocks" />,
    pages: sortBy(settingsPatronBlocks, ['label']),
    interface: 'circulation',
  },
];
<commit_msg>Revert "Revert "Removed Profile Picture settings (#1797)" (#1804)" (#1823)<commit_after>import React from 'react';
import { FormattedMessage } from 'react-intl';
import { sortBy } from 'lodash';

import PermissionSets from './permissions/PermissionSets';
import PatronGroupsSettings from './PatronGroupsSettings';
import AddressTypesSettings from './AddressTypesSettings';
import OwnerSettings from './OwnerSettings';
import FeeFineSettings from './FeeFineSettings';
import WaiveSettings from './WaiveSettings';
import PaymentSettings from './PaymentSettings';
import CommentRequiredSettings from './CommentRequiredSettings';
import RefundReasonsSettings from './RefundReasonsSettings';
import TransferAccountsSettings from './TransferAccountsSettings';
import CustomFieldsSettingsPane from './CustomFieldsSettings';
import ConditionsSettings from './ConditionsSettings';
import LimitsSettings from './LimitsSettings';
import DepartmentsSettings from './DepartmentsSettings';
import BlockTemplates from './patronBlocks/BlockTemplates';
import TransferCriteriaSettings from './TransferCriteriaSettings';

const settingsGeneral = [
  {
    route: 'perms',
    label: <FormattedMessage id="ui-users.settings.permissionSet" />,
    component: PermissionSets,
    perm: 'ui-users.settings.permsets',
  },
  {
    route: 'groups',
    label: <FormattedMessage id="ui-users.settings.patronGroups" />,
    component: PatronGroupsSettings,
    perm: 'ui-users.settings.usergroups',
  },
  {
    route: 'addresstypes',
    label: <FormattedMessage id="ui-users.settings.addressTypes" />,
    component: AddressTypesSettings,
    perm: 'ui-users.settings.addresstypes',
  },
  {
    route: 'departments',
    label: <FormattedMessage id="ui-users.settings.departments" />,
    component: DepartmentsSettings,
    perm: 'ui-users.settings.departments.view'
  },
  //   Profile pictures are currently unsupported in Folio and the existence of this setting has
  //   confused implementers. Commenting out for now as opposed to deleting it so the existing
  //   files and components aren't orphaned.
  //   {
  //     route: 'profilepictures',
  //     label: <FormattedMessage id="ui-users.settings.profilePictures" />,
  //     component: ProfilePictureSettings,
  //     perm: 'ui-users.settings.profilePictures'
  //   },
  {
    route: 'custom-fields',
    label: <FormattedMessage id="ui-users.settings.customFields" />,
    component: CustomFieldsSettingsPane,
    perm: 'ui-users.settings.customfields.view',
  }
];

const settingsFeefines = [
  {
    route: 'owners',
    label: <FormattedMessage id="ui-users.settings.owners" />,
    component: OwnerSettings,
    perm: 'ui-users.settings.owners',
  },
  {
    route: 'feefinestable',
    label: <FormattedMessage id="ui-users.settings.manualCharges" />,
    component: FeeFineSettings,
    perm: 'ui-users.settings.feefines',
  },
  {
    route: 'waivereasons',
    label: <FormattedMessage id="ui-users.settings.waiveReasons" />,
    component: WaiveSettings,
    perm: 'ui-users.settings.waives',
  },
  {
    route: 'payments',
    label: <FormattedMessage id="ui-users.settings.paymentMethods" />,
    component: PaymentSettings,
    perm: 'ui-users.settings.payments',
  },
  {
    route: 'refunds',
    label: <FormattedMessage id="ui-users.settings.refundReasons" />,
    component: RefundReasonsSettings,
    perm: 'ui-users.settings.refunds',
  },
  {
    route: 'comments',
    label: <FormattedMessage id="ui-users.settings.commentRequired" />,
    component: CommentRequiredSettings,
    perm: 'ui-users.settings.comments',
  },
  {
    route: 'transfers',
    label: <FormattedMessage id="ui-users.settings.transferAccounts" />,
    component: TransferAccountsSettings,
    perm: 'ui-users.settings.transfers',
  },
  {
    route: 'transfer-criteria',
    label: <FormattedMessage id="ui-users.settings.transferCriteria" />,
    component: TransferCriteriaSettings,
    perm: 'ui-plugin-bursar-export.bursar-exports.all',
  },
];

const settingsPatronBlocks = [
  {
    route: 'conditions',
    label: <FormattedMessage id="ui-users.settings.conditions" />,
    component: ConditionsSettings,
    perm: 'ui-users.settings.conditions',
  },
  {
    route: 'limits',
    label: <FormattedMessage id="ui-users.settings.limits" />,
    component: LimitsSettings,
    perm: 'ui-users.settings.limits',
  },
  {
    route: 'manual-block-templates',
    label: <FormattedMessage id="ui-users.settings.manualBlockTemplates" />,
    component: BlockTemplates,
    perm: 'ui-users.settings.patron-block-templates',
    interface: 'feesfines',
  },
];

export default [
  {
    label: <FormattedMessage id="ui-users.settings.general" />,
    pages: sortBy(settingsGeneral, ['label']),
  },
  {
    label: <FormattedMessage id="ui-users.settings.feefine" />,
    pages: sortBy(settingsFeefines, ['label']),
    interface: 'feesfines',
  },
  {
    label: <FormattedMessage id="ui-users.settings.patronBlocks" />,
    pages: sortBy(settingsPatronBlocks, ['label']),
    interface: 'circulation',
  },
];
<|endoftext|>


<commit_before>import {identity} from "./util.js";

//Export math constants
export const pi = Math.PI;
export const tau = 2 * Math.PI;
export const epsilon = Number.EPSILON;

//Restrict the value to be in the [min, max] range
export function clamp (value, min, max) {
    if (value < min) {
        return min;
    }
    else if (value > max) {
        return max;
    }
    //Default: return the value
    return value;
}

//Generate an array with an algorithmic progression
//Based on https://docs.python.org/3/library/stdtypes.html#range
export function range (start, end, step) {
    //Check for no start value
    if (typeof start !== "number") {
        return [];
    }
    //Check for no end value
    if (typeof end !== "number") {
        end = start;
        start = 0;
        // Same as calling return range(0, start, 1);
    }
    //Check if start < end and if start is not negative
    if (0 <= start && start < end) {
        //Check if step is not defined or if step is a negative or zero number
        if (typeof step !== "number" || step <= 0) {
            step = 1;
        }
        //Calculate the number of elements of the new array
        let count = Math.floor((end - start) / step);
        return Array(count).fill().map(function (value, index) {
            return start + (index * step);
        });
    }
    else {
        //Start or end values not valid, return an empty array
        return [];
    }
}

//Returns the q-quantile value of a given SORTED list of numbers
//https://en.wikipedia.org/wiki/Quantile
//https://en.wikipedia.org/wiki/Quantile#Estimating_the_quantiles_of_a_population
export function quantile (q, values, valueOf) {
    //Check for undefined valueOf method
    if (typeof valueOf !== "function") {
        valueOf = identity; //Save it as a function that returns the same value
    }
    //Check for no values
    if (values.length === 0) {
        return null;
    }
    //Check for negative values of q or for more than 2 values in the list
    if (q <= 0 || values.length < 2) {
        return valueOf(values[0]);
    }
    //Check for numbers of q >= 1
    if (q >= 1) {
        return valueOf(values[values.length -1]);
    }
    //Calculate tue quantile
    let h = (values.length - 1) * q;
    let rh = Math.floor(h);
    return valueOf(values[rh]) + (valueOf(values[rh + 1]) - valueOf(values[rh])) * (h - rh);
}

//Calculate the hypotenuse without danger of overflow in taking the square root
// hypo = sqrt(max*max + min*min)
// hypo = sqrt(max*max*(1 + min*min/(max*max)))
// hypo = max*sqrt(1 + min*min/(max*max))
//Inspired in from https://www.johndcook.com/blog/2010/06/02/whats-so-hard-about-finding-a-hypotenuse/
export function hypo (x, y) {
    let max = Math.max(Math.abs(x), Math.abs(y));
    let min = Math.min(Math.abs(x), Math.abs(y));
    let division = min / max;
    return max * Math.sqrt(1 + division*division);
}

//Get the X coordinate of the conversion from polar to cartesian
export function polarToCartesianX (radius, angle, center) {
    return center + radius * Math.cos(angle);
}

//Get the Y coordinate of the conversion from polar to cartesian
export function polarToCartesianY (radius, angle, center) {
    return center + radius * Math.sin(angle);
}

//Convert polar to cartesian
export function polarToCartesian(radius, angle, centerX, centerY) {
    //Return the conversion to cartesian coordinates
    return {
        "x": polarToCartesianX(radius, angle, centerX),
        "y": polarToCartesianY(radius, angle, centerY)
    };
}

//Convert cartesian to polar
//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2
export function cartesianToPolar(x, y, centerX, centerY) {
    //Check for no center point provided
    if (typeof centerX !== "number" || typeof centerY !== "number") {
        centerX = 0; centerY = 0;
    }
    //Return the polar coordinates
    return {
        "radius": hypo(x - center.x, y - center.y),
        "angle": Math.atan2(y - center.y, x - center.x)
    };
}

//Format size prefixes
let formatSizePrefix = ["", "K", "M", "G", "T", "P"];

//Format a number
export function format (value, digits) {
    //Check for no digits provided
    if (typeof digits !== "number") {
        digits = 2;
    }
    //Get the size prefix index
    let i = parseInt(Math.floor(Math.log(value) / Math.log(1000)));
    //Return the formated value
    return (value / Math.pow(1000, i)).toFixed(digits) + "" + formatSizePrefix[i];
}

//Returns a random number between `min` and `max` (not included)
//If this function is called only with one argumet, it returns a random number between `0` and that number.
export function random (min, max) {
    if (typeof min !== "number") {
        return Math.random(); //Return a random number between 0 and 1
    }
    else if (typeof max !== "number") {
        return Math.random() * min; //Use min as the max value
    }
    //Default
    return min + Math.random()*(max - min);
}

//Nice number for Heckbert algorithm
export function niceNumber (x, round) {
    let exp = Math.floor(Math.log10(x));
    let f = x / Math.pow(10, exp);
    let nf = 0;
    if (round === true) {
        nf = (f < 1.5) ? 1 : ((f < 3) ? 2 : ((f < 7) ? 5 : 10));
    }
    else  {
        nf = (f <= 1) ? 1 : ((f <= 2) ? 2 : ((f <= 5) ? 5 : 10));
    }
    //Return the nice number
    return nf * Math.pow(10, exp);
}

//Generate values in the provided range using the Heckbert algorithm
export function ticks (start, end, n, tight) {
    //Check for the same range values
    if (start === end) {
        return [start];
    }
    //Check if end < start --> call this method with the reversed arguments
    if (end < start) {
        return ticks(end, start, n, tight);
    }
    let range = niceNumber(end - start, false);
    let step = niceNumber(range / (n - 1), true); //Ticks separation
    let ticksStart = Math.floor(start / step) * step; //Ticks start
    let ticksEnd = Math.ceil(end / step) * step; //Ticks end
    let nfrac = Math.max(-Math.floor(Math.log10(step)), 0); //number of fractional digits to show;
    let ticksValues = []; //Output ticks values
    for (let value = ticksStart; value <= ticksEnd; value = value + step) {
        ticksValues.push(parseFloat(value.toFixed(8)));
    }
    //Check for tight option --> remove ticks outside of the [start, end] interval
    //and add start and end values
    if (typeof tight === "boolean" && tight === true) {
        ticksValues.filter(function (value) {
            return start < value && value < end;
        });
        //Insert start and end values
        ticksValues.unshift(start); //Insert start as the first item
        ticksValues.push(end); //Insert end as the last item
    }
    //Return ticks values
    return ticksValues;
}

//Sort an array of objects
export function sort (list, keys, order) {
    return list.sort(function (a, b) {
        for(let i = 0; i < keys.length; i++) {
            let key = keys[i]; //Get list field
            if (typeof a[key] === "undefined" || typeof b[key] === "undefined") {
                continue; //Undefined value
            }
            //Check for the same value
            if (a[key] === b[key]) {
                continue; //Use next field
            }
            let isNum = !isNaN(+a[key] - +b[key]);
            let aValue = (isNum === true) ? +a[key] : a[key].toLowerCase();
            let bValue = (isNum === true) ? +b[key] : b[key].toLowerCase();
            if (aValue < bValue) {
                return (order[i] === "asc") ? -1 : 1;
            }
            else if (aValue > bValue) {
                return (order[i] === "asc") ? 1 : -1;
            }
        }
        return 0; //Default --> return 0
    });
}

//Find all divisors of a number
export function divisors (n) {
    let result = []; //List with all divisors
    let m = Math.ceil(n / 2);
    for (let i = 1; i <= m; i++) {
        if (n % i === 0) {
            result.push([i, n / i]);
        }
    }
    //Return list of divisors
    return result;
}

//Logaritmic function
export function log (x, base) {
    return Math.log(x) / Math.log(base);
}

//Check if a number is integer
export function isInteger (value) {
    return typeof value === "number" && isFinite(value) && Math.floor(value) === value;
}

//Check if a number is natural
export function isNatural (value) {
    return value >= 0 && isInteger(value);
}

//Parse a nth number
let nthValue = function (sign, value) {
    if (typeof value === "undefined") {
        return 0; //No value to parse
    }
    else if (value === "") {
        value = 1; //It matches, but there is no value --> use 1
    }
    //Default --> parse the value and the sign
    return (sign === "-") ? ((-1) * parseInt(value)) : parseInt(value);
};

//Parse a nth expression (an + b)
let nthExpr = /^(?:([+\-]?)\s*(\d*)n)?\s*(?:([+\-]?)\s*(\d+))?$/; //To check for an expression
export function nthParse (expr) {
    expr = expr.trim().toLowerCase();
    let match = null;
    if (expr === "*") {
        return [1, 0]; //Catch all
    }
    else if (expr === "even") {
        return [2, 0]; //Return for even nth checks
    }
    else if (expr === "odd") {
        return [2, 1]; //Return for odd nth checks
    }
    else if (expr === "n") {
        return [1, 0]; //Return a catch-all
    }
    else if ((match = expr.match(nthExpr)) !== null) {
        return [nthValue(match[1], match[2]), nthValue(match[3], match[4])];
    }
    //Default --> not valid expression
    throw new SyntaxError(`Invalid nth expression ('${expr}')`);
}

//Check a nth expression
export function nthCheck (value, attr) {
    let a = attr[0], b = attr[1];
    //If a === 0 --> only check the b value
    if (a === 0) {
        return b === value;
    }
    //Default --> check if (value - b) / a is a positive integer
    return isNatural((value - b) / a);
}

//Parse + check a nth expression
export function nth (value, expr) {
    return nthCheck(value, nthParse(expr));
}

//Binary search tree
let BinarySearchTreeHandler = function () {
    this.root = null; //Initial node
};

//Prototype
BinarySearchTreeHandler.prototype = {
    //Add a new node to the tree
    "add": function (value) {
        let node = {"value": value, "left": null, "right": null};
        if (this.root === null) {
            this.root = node;
            return true; //Node has been inserted
        }
        //Search in the tree
        let current = this.root;
        while (true) {
            //Check if there is no
            if (node.value.end < current.value.start) {
                if (current.left === null) {
                    current.left = node;
                    return true;
                }
                //Now check the left node
                current = current.left;
            }
            else if (current.value.end < node.value.start) {
                if (current.right === null) {
                    current.right = node;
                    return true;
                }
                //Now check the right side
                current = current.right;
            }
            else {
                //This node intersects the current node --> reject
                return false;
            }
        }
    }
};

//Create a new binary search tree
export function binarySearchTree () {
    return new BinarySearchTreeHandler();
}

<commit_msg>math: fixed minor bug generating ticks values<commit_after>import {identity} from "./util.js";

//Export math constants
export const pi = Math.PI;
export const tau = 2 * Math.PI;
export const epsilon = Number.EPSILON;

//Restrict the value to be in the [min, max] range
export function clamp (value, min, max) {
    if (value < min) {
        return min;
    }
    else if (value > max) {
        return max;
    }
    //Default: return the value
    return value;
}

//Generate an array with an algorithmic progression
//Based on https://docs.python.org/3/library/stdtypes.html#range
export function range (start, end, step) {
    //Check for no start value
    if (typeof start !== "number") {
        return [];
    }
    //Check for no end value
    if (typeof end !== "number") {
        end = start;
        start = 0;
        // Same as calling return range(0, start, 1);
    }
    //Check if start < end and if start is not negative
    if (0 <= start && start < end) {
        //Check if step is not defined or if step is a negative or zero number
        if (typeof step !== "number" || step <= 0) {
            step = 1;
        }
        //Calculate the number of elements of the new array
        let count = Math.floor((end - start) / step);
        return Array(count).fill().map(function (value, index) {
            return start + (index * step);
        });
    }
    else {
        //Start or end values not valid, return an empty array
        return [];
    }
}

//Returns the q-quantile value of a given SORTED list of numbers
//https://en.wikipedia.org/wiki/Quantile
//https://en.wikipedia.org/wiki/Quantile#Estimating_the_quantiles_of_a_population
export function quantile (q, values, valueOf) {
    //Check for undefined valueOf method
    if (typeof valueOf !== "function") {
        valueOf = identity; //Save it as a function that returns the same value
    }
    //Check for no values
    if (values.length === 0) {
        return null;
    }
    //Check for negative values of q or for more than 2 values in the list
    if (q <= 0 || values.length < 2) {
        return valueOf(values[0]);
    }
    //Check for numbers of q >= 1
    if (q >= 1) {
        return valueOf(values[values.length -1]);
    }
    //Calculate tue quantile
    let h = (values.length - 1) * q;
    let rh = Math.floor(h);
    return valueOf(values[rh]) + (valueOf(values[rh + 1]) - valueOf(values[rh])) * (h - rh);
}

//Calculate the hypotenuse without danger of overflow in taking the square root
// hypo = sqrt(max*max + min*min)
// hypo = sqrt(max*max*(1 + min*min/(max*max)))
// hypo = max*sqrt(1 + min*min/(max*max))
//Inspired in from https://www.johndcook.com/blog/2010/06/02/whats-so-hard-about-finding-a-hypotenuse/
export function hypo (x, y) {
    let max = Math.max(Math.abs(x), Math.abs(y));
    let min = Math.min(Math.abs(x), Math.abs(y));
    let division = min / max;
    return max * Math.sqrt(1 + division*division);
}

//Get the X coordinate of the conversion from polar to cartesian
export function polarToCartesianX (radius, angle, center) {
    return center + radius * Math.cos(angle);
}

//Get the Y coordinate of the conversion from polar to cartesian
export function polarToCartesianY (radius, angle, center) {
    return center + radius * Math.sin(angle);
}

//Convert polar to cartesian
export function polarToCartesian(radius, angle, centerX, centerY) {
    //Return the conversion to cartesian coordinates
    return {
        "x": polarToCartesianX(radius, angle, centerX),
        "y": polarToCartesianY(radius, angle, centerY)
    };
}

//Convert cartesian to polar
//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2
export function cartesianToPolar(x, y, centerX, centerY) {
    //Check for no center point provided
    if (typeof centerX !== "number" || typeof centerY !== "number") {
        centerX = 0; centerY = 0;
    }
    //Return the polar coordinates
    return {
        "radius": hypo(x - center.x, y - center.y),
        "angle": Math.atan2(y - center.y, x - center.x)
    };
}

//Format size prefixes
let formatSizePrefix = ["", "K", "M", "G", "T", "P"];

//Format a number
export function format (value, digits) {
    //Check for no digits provided
    if (typeof digits !== "number") {
        digits = 2;
    }
    //Get the size prefix index
    let i = parseInt(Math.floor(Math.log(value) / Math.log(1000)));
    //Return the formated value
    return (value / Math.pow(1000, i)).toFixed(digits) + "" + formatSizePrefix[i];
}

//Returns a random number between `min` and `max` (not included)
//If this function is called only with one argumet, it returns a random number between `0` and that number.
export function random (min, max) {
    if (typeof min !== "number") {
        return Math.random(); //Return a random number between 0 and 1
    }
    else if (typeof max !== "number") {
        return Math.random() * min; //Use min as the max value
    }
    //Default
    return min + Math.random()*(max - min);
}

//Nice number for Heckbert algorithm
export function niceNumber (x, round) {
    let exp = Math.floor(Math.log10(x));
    let f = x / Math.pow(10, exp);
    let nf = 0;
    if (round === true) {
        nf = (f < 1.5) ? 1 : ((f < 3) ? 2 : ((f < 7) ? 5 : 10));
    }
    else  {
        nf = (f <= 1) ? 1 : ((f <= 2) ? 2 : ((f <= 5) ? 5 : 10));
    }
    //Return the nice number
    return nf * Math.pow(10, exp);
}

//Generate values in the provided range using the Heckbert algorithm
export function ticks (start, end, n, tight) {
    //Check for the same range values
    if (start === end) {
        return [start];
    }
    //Check if end < start --> call this method with the reversed arguments
    if (end < start) {
        return ticks(end, start, n, tight);
    }
    let range = niceNumber(end - start, false);
    let step = niceNumber(range / (n - 1), true); //Ticks separation
    let ticksStart = Math.floor(start / step) * step; //Ticks start
    let ticksEnd = Math.ceil(end / step) * step; //Ticks end
    let nfrac = Math.max(-Math.floor(Math.log10(step)), 0); //number of fractional digits to show;
    let ticksValues = []; //Output ticks values
    for (let value = ticksStart; value <= ticksEnd; value = value + step) {
        ticksValues.push(parseFloat(value.toFixed(8)));
    }
    //Check for tight option --> remove ticks outside of the [start, end] interval
    //and add start and end values
    if (typeof tight === "boolean" && tight === true) {
        ticksValues = ticksValues.filter(function (value) {
            return start < value && value < end;
        });
        //Insert start and end values
        ticksValues.unshift(start); //Insert start as the first item
        ticksValues.push(end); //Insert end as the last item
    }
    //Return ticks values
    return ticksValues;
}

//Sort an array of objects
export function sort (list, keys, order) {
    return list.sort(function (a, b) {
        for(let i = 0; i < keys.length; i++) {
            let key = keys[i]; //Get list field
            if (typeof a[key] === "undefined" || typeof b[key] === "undefined") {
                continue; //Undefined value
            }
            //Check for the same value
            if (a[key] === b[key]) {
                continue; //Use next field
            }
            let isNum = !isNaN(+a[key] - +b[key]);
            let aValue = (isNum === true) ? +a[key] : a[key].toLowerCase();
            let bValue = (isNum === true) ? +b[key] : b[key].toLowerCase();
            if (aValue < bValue) {
                return (order[i] === "asc") ? -1 : 1;
            }
            else if (aValue > bValue) {
                return (order[i] === "asc") ? 1 : -1;
            }
        }
        return 0; //Default --> return 0
    });
}

//Find all divisors of a number
export function divisors (n) {
    let result = []; //List with all divisors
    let m = Math.ceil(n / 2);
    for (let i = 1; i <= m; i++) {
        if (n % i === 0) {
            result.push([i, n / i]);
        }
    }
    //Return list of divisors
    return result;
}

//Logaritmic function
export function log (x, base) {
    return Math.log(x) / Math.log(base);
}

//Check if a number is integer
export function isInteger (value) {
    return typeof value === "number" && isFinite(value) && Math.floor(value) === value;
}

//Check if a number is natural
export function isNatural (value) {
    return value >= 0 && isInteger(value);
}

//Parse a nth number
let nthValue = function (sign, value) {
    if (typeof value === "undefined") {
        return 0; //No value to parse
    }
    else if (value === "") {
        value = 1; //It matches, but there is no value --> use 1
    }
    //Default --> parse the value and the sign
    return (sign === "-") ? ((-1) * parseInt(value)) : parseInt(value);
};

//Parse a nth expression (an + b)
let nthExpr = /^(?:([+\-]?)\s*(\d*)n)?\s*(?:([+\-]?)\s*(\d+))?$/; //To check for an expression
export function nthParse (expr) {
    expr = expr.trim().toLowerCase();
    let match = null;
    if (expr === "*") {
        return [1, 0]; //Catch all
    }
    else if (expr === "even") {
        return [2, 0]; //Return for even nth checks
    }
    else if (expr === "odd") {
        return [2, 1]; //Return for odd nth checks
    }
    else if (expr === "n") {
        return [1, 0]; //Return a catch-all
    }
    else if ((match = expr.match(nthExpr)) !== null) {
        return [nthValue(match[1], match[2]), nthValue(match[3], match[4])];
    }
    //Default --> not valid expression
    throw new SyntaxError(`Invalid nth expression ('${expr}')`);
}

//Check a nth expression
export function nthCheck (value, attr) {
    let a = attr[0], b = attr[1];
    //If a === 0 --> only check the b value
    if (a === 0) {
        return b === value;
    }
    //Default --> check if (value - b) / a is a positive integer
    return isNatural((value - b) / a);
}

//Parse + check a nth expression
export function nth (value, expr) {
    return nthCheck(value, nthParse(expr));
}

//Binary search tree
let BinarySearchTreeHandler = function () {
    this.root = null; //Initial node
};

//Prototype
BinarySearchTreeHandler.prototype = {
    //Add a new node to the tree
    "add": function (value) {
        let node = {"value": value, "left": null, "right": null};
        if (this.root === null) {
            this.root = node;
            return true; //Node has been inserted
        }
        //Search in the tree
        let current = this.root;
        while (true) {
            //Check if there is no
            if (node.value.end < current.value.start) {
                if (current.left === null) {
                    current.left = node;
                    return true;
                }
                //Now check the left node
                current = current.left;
            }
            else if (current.value.end < node.value.start) {
                if (current.right === null) {
                    current.right = node;
                    return true;
                }
                //Now check the right side
                current = current.right;
            }
            else {
                //This node intersects the current node --> reject
                return false;
            }
        }
    }
};

//Create a new binary search tree
export function binarySearchTree () {
    return new BinarySearchTreeHandler();
}

<|endoftext|>


<commit_before>var Readable      = require('stream').Readable;
var Transform     = require('stream').Transform;
var StreamSearch  = require('streamsearch');
var util          = require('util');
var fs            = require('fs');
var Stream        = require('stream');

util.inherits(StringReader, Readable);
util.inherits(MboxStream, Transform);

function StringReader(string) {
  // setup stream reader
  if (!(this instanceof StringReader))
    return new StringReader();
  Readable.call(this);
  this.string = string;
}

StringReader.prototype._read = function(size) {
  if (this.string.length === 0) {
    this.push(null);
  }
  else {
    this.push(this.string.slice(0, size));
    this.string = this.string.slice(size);
  }
};

function MboxStream(input, opts) {
  var stream = this;

  // handle arguments
  if (input !== undefined) {
    var klass   = input.constructor.name;
    var handle  = null;

    if (klass === 'String' || klass === 'Buffer') {
      // either filename...
      if (fs.existsSync(input))
        handle = fs.createReadStream(input);
      // ...or raw input string (handled by StringReader)
      else
        handle = new StringReader(input);
    }
    else
    if (klass === 'ReadStream')
      handle = input;
    else
    if (klass === 'Object')
      opts = input;

    if (handle) {
      // set encoding
      handle.setEncoding('ascii');

      // pipe handle to ourselves
      handle.pipe(this);
    }
  }

  // output encoding
  this.encoding = (opts && opts.encoding) ? opts.encoding : 'binary';

  // setup stream transformer
  if (!(this instanceof MboxStream)) {
    return new MboxStream(opts);
  }
  Transform.call(this, opts);

  // keep track of number of messages found
  this.number_of_messages = 0;

  // done
  var chunks = [];
  this.on('finish', function() {
    if (chunks.length) {
      stream.emit('message', chunks.join(''));
    }
    this.emit('end', stream.number_of_messages);
  });

  // setup stream searcher
  this.searcher = new StreamSearch('\nFrom ');
  this.searcher.on('info', function(isMatch, chunk, start, end) {
    if (chunk) {
      // Add needle.
      chunks.push( 'From ' + chunk.toString(stream.encoding, start, end) );
    }
    if (isMatch && chunks.length) {
      stream.number_of_messages++;
      stream.emit('message', chunks.join(''));
      chunks = [];
    }
  });

  // Push a dummy \n to the search so we match the first message.
  this.searcher.push('\n');
}

MboxStream.prototype._transform = function(chunk, encoding, done) {
  // Push chunks to the searcher.
  this.searcher.push(chunk);
  done();
};

module.exports = MboxStream;
<commit_msg>Update mbox.js<commit_after>var Readable      = require('stream').Readable;
var Transform     = require('stream').Transform;
var StreamSearch  = require('streamsearch');
var util          = require('util');
var fs            = require('fs');
var Stream        = require('stream');

util.inherits(StringReader, Readable);
util.inherits(MboxStream, Transform);

function StringReader(string) {
  // setup stream reader
  if (!(this instanceof StringReader))
    return new StringReader();
  Readable.call(this);
  this.string = string;
}

StringReader.prototype._read = function(size) {
  if (this.string.length === 0) {
    this.push(null);
  }
  else {
    this.push(this.string.slice(0, size));
    this.string = this.string.slice(size);
  }
};

function MboxStream(input, opts) {
  var stream = this;

  // handle arguments
  if (input !== undefined) {
    var klass   = input.constructor.name;
    var handle  = null;

    if (klass === 'String' || klass === 'Buffer') {
      // either filename...
      if (fs.existsSync(input))
        handle = fs.createReadStream(input);
      // ...or raw input string (handled by StringReader)
      else
        handle = new StringReader(input);
    }
    else
    if (klass === 'ReadStream')
      handle = input;
    else
    if (klass === 'Object')
      opts = input;

    if (handle) {
      // set encoding
      handle.setEncoding('ascii');

      // pipe handle to ourselves
      handle.pipe(this);
    }
  }

  // output encoding
  this.encoding = (opts && opts.encoding) ? opts.encoding : 'binary';

  // setup stream transformer
  if (!(this instanceof MboxStream)) {
    return new MboxStream(opts);
  }
  Transform.call(this, opts);

  // keep track of number of messages found
  this.number_of_messages = 0;

  // done
  var chunks = [];
  this.on('finish', function() {
    if (chunks.length) {
      stream.number_of_messages++;
      stream.emit('message', chunks.join(''));
    }
    this.emit('end', stream.number_of_messages);
  });

  // setup stream searcher
  this.searcher = new StreamSearch('\nFrom ');
  this.searcher.on('info', function(isMatch, chunk, start, end) {
    if (chunk) {
      // Add needle.
      chunks.push( 'From ' + chunk.toString(stream.encoding, start, end) );
    }
    if (isMatch && chunks.length) {
      stream.number_of_messages++;
      stream.emit('message', chunks.join(''));
      chunks = [];
    }
  });

  // Push a dummy \n to the search so we match the first message.
  this.searcher.push('\n');
}

MboxStream.prototype._transform = function(chunk, encoding, done) {
  // Push chunks to the searcher.
  this.searcher.push(chunk);
  done();
};

module.exports = MboxStream;
<|endoftext|>


<commit_before>var fs = require('fs');
var http = require('http');

/**
 * DSL Namespacing
 */

var uri = require('./request.js').uri;
var request = require('./request.js').uri;
var method = require('./request.js').method;

var response = require('./response.js').response;
var text = require('./response.js').text;

var route = require('./route.js').route;
var routes = require('./route.js').routes;

var mock = function (port, file) {
  fs.readFile(file, encoding="ascii", function (err, data) {
    console.log("Starting server with configuration:");
    console.log("------------------------------------------------");
    console.log(data);
    console.log("------------------------------------------------");

    var r = eval(data);
    console.log("Starting server on port " + port);

    http.createServer(function (req, res) {
      console.log(new Date() + " -- " + req.method + " " + req.url);
      r.process(req, res);
    }).listen(port, "0.0.0.0");
  });
};

module.exports = {
  mock : mock
};<commit_msg>Namespace the status response.<commit_after>var fs = require('fs');
var http = require('http');

/**
 * DSL Namespacing
 */

var uri = require('./request.js').uri;
var request = require('./request.js').uri;
var method = require('./request.js').method;

var response = require('./response.js').response;
var text = require('./response.js').text;
var status = require('./response.js').status;

var route = require('./route.js').route;
var routes = require('./route.js').routes;

var mock = function (port, file) {
  fs.readFile(file, encoding="ascii", function (err, data) {
    console.log("Starting server with configuration:");
    console.log("------------------------------------------------");
    console.log(data);
    console.log("------------------------------------------------");

    var r = eval(data);
    console.log("Starting server on port " + port);

    http.createServer(function (req, res) {
      console.log(new Date() + " -- " + req.method + " " + req.url);
      r.process(req, res);
    }).listen(port, "0.0.0.0");
  });
};

module.exports = {
  mock : mock
};<|endoftext|>


<commit_before>( function ( angular ) {
  'use strict';

  angular.module( 'ngyn', [ 'ngynSelect2', 'ngynSelectKey',
                            'ngynRoute', 'ngynResource',
                            'ngynTimespanPicker', 'ngynServerConnection' ] );

} )( window.angular );<commit_msg>removing missed select-key dependancy<commit_after>( function ( angular ) {
  'use strict';

  angular.module( 'ngyn', [ 'ngynSelect2',
                            'ngynRoute', 'ngynResource',
                            'ngynTimespanPicker', 'ngynServerConnection' ] );

} )( window.angular );
<|endoftext|>


<commit_before>import helpers from './helpers/helpers'
const { flatten, sampleArray, createDictByProp, bindAll } = helpers
import { values } from 'underscore'
import nodes from './nodes'

const cyclesInMemory = 3

export default class Node {
	constructor(opts) {
		this.id = opts.id
		this.username = opts.username
		this.belief = opts.belief

		this._following = []
		this.memory = [[]]

		const env = {
			getNumStates: () => 1,
			getMaxNumActions: () => 3 // political, neural, nothing
		}

		const spec = { alpha: 0.01 }

		this.agent = new RL.DQNAgent(env, spec)
	}

	set following(newFollowing) {
		this._following = newFollowing
	}

	get following() {
		return this._following
	}

	getMessage() {
		let orientation = "", retweetID = null

		// this first randomness will be the part that we learn
		if(Math.random() < 0.5) {
			orientation = this.belief
			if(Math.random() < 0.5) {
				const matchingMessages = this.memory.reduce(flatten)
					.filter(msg => msg.orientation === this.belief)

				if(matchingMessages.length) {
					retweetID = sampleArray(matchingMessages).id
				}
			}
		}

		return {
			orientation,
			retweetID,
			user: this.id
		}
	}

	sendMessages(messages) {
		if(this.memory.length > cyclesInMemory) {
			this.memory.shift()
		}

		this.memory.push(messages.filter(msg =>
			this._following.includes(msg.user)))

		this.adjustFollowing()
	}

	adjustFollowing() {
		const byBeliefs = createDictByProp(
			this.memory.reduce(flatten).filter(msg =>
				msg.orientation !== this.belief && !!msg.orientation), 'orientation')

		const strongCounterOrientation = values(byBeliefs).find(d => d.length > 3)

		if(strongCounterOrientation) {
			// change your belief to match the strong counter orientation
			this.belief = strongCounterOrientation[0].orientation

			// now follow someone randomly from the strong counter orientation group
			const availableFollowees = nodes.filter(n =>
				n.belief === this.belief && !this._following.includes(n.id))
			if(availableFollowees.length) {
				this._following.push(sampleArray(availableFollowees).id)
			}
		}

		// unfollow anyone who has been political for the last 3 rounds
		if(values(byBeliefs).length) {
			const messagesByUser = createDictByProp(values(byBeliefs).reduce(flatten), 'user')
			const overpoliticalUsers = Object.keys(messagesByUser)
				.filter(k => messagesByUser[k].length === cyclesInMemory)

			if(overpoliticalUsers.length) {
				this._following.splice(
					this._following.findIndex(d =>
						d === sampleArray(overpoliticalUsers)), 1)
			}
		}
	}

	init() {
		bindAll(this, [ "getMessage", "sendMessages", "adjustFollowing" ])
	}
}
<commit_msg>change calculation for adding followers<commit_after>import helpers from './helpers/helpers'
const { flatten, sampleArray, createDictByProp, bindAll } = helpers
import { values } from 'underscore'
import nodes from './nodes'

const cyclesInMemory = 3

export default class Node {
	constructor(opts) {
		this.id = opts.id
		this.username = opts.username
		this.belief = opts.belief

		this._following = []
		this.memory = [[]]

		const env = {
			getNumStates: () => 1,
			getMaxNumActions: () => 3 // political, neural, nothing
		}

		const spec = { alpha: 0.01 }

		this.agent = new RL.DQNAgent(env, spec)
	}

	set following(newFollowing) {
		this._following = newFollowing
	}

	get following() {
		return this._following
	}

	getMessage() {
		let orientation = "", retweetID = null

		// this first randomness will be the part that we learn
		if(Math.random() < 0.5) {
			orientation = this.belief
			if(Math.random() < 0.5) {
				const matchingMessages = this.memory.reduce(flatten)
					.filter(msg => msg.orientation === this.belief)

				if(matchingMessages.length) {
					retweetID = sampleArray(matchingMessages).id
				}
			}
		}

		return {
			orientation,
			retweetID,
			user: this.id
		}
	}

	sendMessages(messages) {
		if(this.memory.length > cyclesInMemory) {
			this.memory.shift()
		}

		this.memory.push(messages.filter(msg =>
			this._following.includes(msg.user)))

		this.adjustFollowing()
	}

	adjustFollowing() {
		const byBeliefs = createDictByProp(this.memory.reduce(flatten), 'orientation')
		const agreementCount = byBeliefs[this.belief] ? byBeliefs[this.belief].length : 0.0001 // prevent div by 0

		const strongCounterOrientation = Object.keys(byBeliefs)
			.filter(d => d !== this.belief && !!d)
			.map(k => byBeliefs[k] )
			.find(d => d.length / agreementCount > 1.5)

		if(strongCounterOrientation) {
			// change your belief to match the strong counter orientation
			this.belief = strongCounterOrientation[0].orientation

			// now follow someone randomly from the strong counter orientation group
			const availableFollowees = nodes.filter(n =>
				n.belief === this.belief && !this._following.includes(n.id))
			if(availableFollowees.length) {
				this._following.push(sampleArray(availableFollowees).id)
			}
		}

		// unfollow anyone who has been political for the last 3 rounds
		if(values(byBeliefs).length) {
			const messagesByUser = createDictByProp(values(byBeliefs).reduce(flatten), 'user')
			const overpoliticalUsers = Object.keys(messagesByUser)
				.filter(k => messagesByUser[k].length === cyclesInMemory)

			if(overpoliticalUsers.length) {
				this._following.splice(
					this._following.findIndex(d =>
						d === sampleArray(overpoliticalUsers)), 1)
			}
		}
	}

	init() {
		bindAll(this, [ "getMessage", "sendMessages", "adjustFollowing" ])
	}
}
<|endoftext|>


<commit_before>#! /usr/bin/env node
'use strict';

const program = require('commander');
const exec = require('child_process').exec;
const fs = require('fs');
const __version__ = require('../package.json').version;

program
  .version(__version__)
  .usage('<command> [<files>]')

program
  .command('no-git')
  .description('Remove a file from git without removing it from the filesystem')
  .arguments('<files...>')
  .action(function(files) {
    files.map(function(file) {
      exec('git reset ' + __dirname + file, function(error, stdout, stderr) {
        if (error) console.log(error);
        if (stderr) console.log(stderr);
        // find the location of the .git folder, strip away the directory
        // and add the file path to .gitignore
      });
    });
  });

program
  .command('missed')
  .description('Stage a file or files under the most recent commit')
  .arguments('<files...>')
  .action(function(files) {
    files.map(function(file) {
      exec('git add ' + file, function(error, stdout, stderr) {
        if (error) console.log(error);
        if (stderr) console.log(stderr);
      });
    });
    exec('git commit --amend -C HEAD', function(error, stdout, stderr) {
      if (error) console.log(error);
      if (stderr) console.log(stderr);
    });
  });

program
  .command('undo')
  .description('Undo the last commit')
  .action(function() {
    exec('giddy reset HEAD~1', function(error, stdout, stderr) {
      if (error) console.log(error);
      if (stderr) console.log(stderr);
    });
  });

program.parse(process.argv);

// Output the usage information, if giddy-oops is invoked without providing a command
if (!process.argv.slice(2).length) {
  program.outputHelp();
}
<commit_msg>Call git instead of giddy<commit_after>#! /usr/bin/env node
'use strict';

const program = require('commander');
const exec = require('child_process').exec;
const fs = require('fs');
const __version__ = require('../package.json').version;

program
  .version(__version__)
  .usage('<command> [<files>]')

program
  .command('no-git')
  .description('Remove a file from git without removing it from the filesystem')
  .arguments('<files...>')
  .action(function(files) {
    files.map(function(file) {
      exec('git reset ' + __dirname + file, function(error, stdout, stderr) {
        if (error) console.log(error);
        if (stderr) console.log(stderr);
        // find the location of the .git folder, strip away the directory
        // and add the file path to .gitignore
      });
    });
  });

program
  .command('missed')
  .description('Stage a file or files under the most recent commit')
  .arguments('<files...>')
  .action(function(files) {
    files.map(function(file) {
      exec('git add ' + file, function(error, stdout, stderr) {
        if (error) console.log(error);
        if (stderr) console.log(stderr);
      });
    });
    exec('git commit --amend -C HEAD', function(error, stdout, stderr) {
      if (error) console.log(error);
      if (stderr) console.log(stderr);
    });
  });

program
  .command('undo')
  .description('Undo the last commit')
  .action(function() {
    exec('git reset HEAD~1', function(error, stdout, stderr) {
      if (error) console.log(error);
      if (stderr) console.log(stderr);
    });
  });

program.parse(process.argv);

// Output the usage information, if giddy-oops is invoked without providing a command
if (!process.argv.slice(2).length) {
  program.outputHelp();
}
<|endoftext|>


<commit_before>'use strict';

const Device = require('./device');

/**
 * Plug Device.
 *
 * TP-Link models: HS100, HS105, HS110, HS200.
 *
 * Emits events after device status is queried, such as {@link #getSysInfo} and {@link #getEmeterRealtime}.
 * @extends Device
 * @extends EventEmitter
 * @emits  Plug#power-on
 * @emits  Plug#power-off
 * @emits  Plug#power-update
 * @emits  Plug#in-use
 * @emits  Plug#not-in-use
 * @emits  Plug#in-use-update
 * @emits  Plug#emeter-realtime-update
 */
class Plug extends Device {
  /**
   * Created by {@link Client} - Do not instantiate directly.
   *
   * See {@link Device#constructor} for common options.
   * @param  {Object} options
   * @param  {Number} [options.inUseThreshold=0]
   */
  constructor (options) {
    super(options);
    if (typeof options === 'undefined') options = {};

    this.log.debug('plug.constructor()');

    this.apiModuleNamespace = {
      'system': 'system',
      'cloud': 'cnCloud',
      'schedule': 'schedule',
      'timesetting': 'time',
      'emeter': 'emeter',
      'netif': 'netif'
    };

    this.inUseThreshold = options.inUseThreshold || 0;

    this.lastState = Object.assign(this.lastState, { powerOn: null, inUse: null });

    this.emitEventsEnabled = true;
  }

  get sysInfo () {
    return super.sysInfo;
  }
  /**
   * @private
   */
  set sysInfo (sysInfo) {
    super.sysInfo = sysInfo;
    try {
      this.supportsEmeter = (sysInfo.feature.includes('ENE'));
    } catch (e) {
      this.supportsEmeter = false;
    }
    this.log.debug('[%s] plug sysInfo set', this.alias);
    this.emitEvents();
  }

  get emeterRealtime () {
    return super.emeterRealtime;
  }
  /**
   * @private
   */
  set emeterRealtime (emeterRealtime) {
    this.log.debug('[%s] plug emeterRealtime set', this.alias);
    this._emeterRealtime = emeterRealtime;
    if (this.supportsEmeter) {
      this.emitEvents();
    }
  }
  /**
   * Determines if device is in use based on cached `emeter.get_realtime` results.
   *
   * If device supports energy monitoring (HS110): `power > inUseThreshold`
   *
   * Otherwise fallback on relay state:  `relay_state === 1`
   * @return {boolean}
   */
  get inUse () {
    if (this.supportsEmeter) {
      return (this.emeterRealtime.power > this.inUseThreshold);
    }
    return (this.sysInfo.relay_state === 1);
  }

  /**
   * Same as {@link #inUse}, but requests current `emeter.get_realtime`.
   * @return {Promise<boolean, ResponseError>}
   */
  async getInUse () {
    if (this.supportsEmeter) {
      await this.getEmeterRealtime();
    } else {
      await this.getSysInfo();
    }
    return this.isUse;
  }

  /**
   * Plug's relay was turned on.
   * @event Plug#power-on
   * @property {boolean} value Relay State (`true`)
   */

  /**
   * Plug's relay was turned off.
   * @event Plug#power-off
   * @property {boolean} value Relay State (`false`)
   */

  /**
   * Plug's relay state was updated from device. Fired regardless if status was changed.
   * @event Plug#power-update
   * @property {boolean} value Relay State
   */

  /**
   * Plug's relay was turned on _or_ power draw exceeded `inUseThreshold` for HS110
   * @event Plug#in-use
   * @property {boolean} value In Use State (`true`)
   */

  /**
   * Plug's relay was turned off _or_ power draw fell below `inUseThreshold` for HS110
   * @event Plug#not-in-use
   * @property {boolean} value In Use State (`false`)
   */

   /**
   * Plug's in-use state was updated from device. Fired regardless if status was changed.
   * @event Plug#in-use-update
   * @property {boolean} value In Use State
   */

  /**
   * Plug's Energy Monitoring Details were updated from device. Fired regardless if status was changed.
   * @event Plug#emeter-realtime-update
   * @property {Object} value Energy Monitoring Details
   */

  /**
   * @private
   */
  emitEvents () {
    if (!this.emitEventsEnabled) { return; }

    const inUse = this.inUse;
    const powerOn = (this.sysInfo.relay_state === 1);

    this.log.debug('[%s] plug.emitEvents() inUse: %s powerOn: %s lastState: %j', this.alias, inUse, powerOn, this.lastState);
    if (this.lastState.inUse !== inUse) {
      this.lastState.inUse = inUse;
      if (inUse) {
        this.emit('in-use', this, inUse);
      } else {
        this.emit('not-in-use', this, inUse);
      }
    } else {
      this.emit('in-use-update', this, inUse);
    }

    if (this.lastState.powerOn !== powerOn) {
      this.lastState.powerOn = powerOn;
      if (powerOn) {
        this.emit('power-on', this, powerOn);
      } else {
        this.emit('power-off', this, powerOn);
      }
    } else {
      this.emit('power-update', this, powerOn);
    }

    if (this.supportsEmeter) {
      this.emit('emeter-realtime-update', this, this.emeterRealtime);
    }
  }

  /**
   * Requests common Plug status details in a single request.
   * - `system.get_sysinfo`
   * - `cloud.get_sysinfo`
   * - `emeter.get_realtime`
   * - `schedule.get_next_action`
   * @return {Promise<Object, Error>} parsed JSON response
   */
  async getInfo () {
    // TODO switch to sendCommand, but need to handle error for devices that don't support emeter
    let data = await this.send('{"emeter":{"get_realtime":{}},"schedule":{"get_next_action":{}},"system":{"get_sysinfo":{}},"cnCloud":{"get_info":{}}}');
    this.sysInfo = data.system.get_sysinfo;
    this.cloudInfo = data.cnCloud.get_info;
    this.emeterRealtime = data.emeter.get_realtime;
    this.scheduleNextAction = data.schedule.get_next_action;
    return {sysInfo: this.sysInfo, cloudInfo: this.cloudInfo, emeterRealtime: this.emeterRealtime, scheduleNextAction: this.scheduleNextAction};
  }

  /**
   * Get Plug relay state (on/off).
   *
   * Requests `system.get_sysinfo` and returns true if `relay_state === 1`.
   * @return {Promise<boolean, ResponseError>}
   */
  async getPowerState () {
    let sysInfo = await this.getSysInfo();
    return (sysInfo.relay_state === 1);
  }

  /**
   * Turns Plug relay on/off.
   *
   * Sends `system.set_relay_state` command.
   * @param  {boolean}  value
   * @return {Promise<boolean, ResponseError>}
   */
  async setPowerState (value) {
    this.log.debug('[%s] plug.setPowerState(%s)', this.alias, value);
    await this.sendCommand(`{"system":{"set_relay_state":{"state":${(value ? 1 : 0)}}}}`);
    this.sysInfo.relay_state = (value ? 1 : 0);
    this.emitEvents();
    return true;
  }
  /**
   * Get Away Rules.
   *
   * Requests `anti_theft.get_rules`.
   * @return {Promise<Object, ResponseError>} parsed JSON response
   */
  async getAwayRules () {
    return this.sendCommand(`{"anti_theft":{"get_rules":{}}}`);
  }
  /**
   * Get Timer Rules.
   *
   * Requests `count_down.get_rules`.
   * @return {Promise<Object, ResponseError>} parsed JSON response
   */
  async getTimerRules () {
    return this.sendCommand(`{"count_down":{"get_rules":{}}}`);
  }
  /**
   * Get Plug LED state (night mode).
   *
   * Requests `system.sys_info` and returns true if `led_off === 0`.
   * @return {Promise<boolean, ResponseError>} LED State, true === on
   */
  async getLedState () {
    let sysInfo = await this.getSysInfo();
    return (sysInfo.led_off === 0);
  }
  /**
   * Turn Plug LED on/off (night mode).
   *
   * Sends `system.set_led_off` command.
   * @param  {boolean}  value LED State, true === on
   * @return {Promise<boolean, ResponseError>}
   */
  async setLedState (value) {
    await this.sendCommand(`{"system":{"set_led_off":{"off":${(value ? 0 : 1)}}}}`);
    this.sysInfo.set_led_off = (value ? 0 : 1);
    return true;
  }
  /**
   * Blink Plug LED.
   *
   * Sends `system.set_led_off` command alternating on and off number of `times` at `rate`,
   * then sets the led to its pre-blink state.
   *
   * Note: `system.set_led_off` is particulally slow, so blink rate is not guaranteed.
   * @param  {number}  [times=5]
   * @param  {number}  [rate=1000]
   * @return {Promise<boolean, ResponseError>}
   */
  async blink (times = 5, rate = 1000) {
    let delay = (t) => { return new Promise((resolve) => { setTimeout(resolve, t); }); };

    let origLedState = await this.getLedState();
    let lastBlink = Date.now();

    let currLedState = false;
    for (var i = 0; i < times * 2; i++) {
      currLedState = !currLedState;
      lastBlink = Date.now();
      await this.setLedState(currLedState);
      let timeToWait = (rate / 2) - (Date.now() - lastBlink);
      if (timeToWait > 0) {
        await delay(timeToWait);
      }
    }
    if (currLedState !== origLedState) {
      await this.setLedState(origLedState);
    }
    return true;
  }
}

module.exports = Plug;
<commit_msg>event changes<commit_after>'use strict';

const Device = require('./device');

/**
 * Plug Device.
 *
 * TP-Link models: HS100, HS105, HS110, HS200.
 *
 * Emits events after device status is queried, such as {@link #getSysInfo} and {@link #getEmeterRealtime}.
 * @extends Device
 * @extends EventEmitter
 * @emits  Plug#power-on
 * @emits  Plug#power-off
 * @emits  Plug#power-update
 * @emits  Plug#in-use
 * @emits  Plug#not-in-use
 * @emits  Plug#in-use-update
 * @emits  Plug#emeter-realtime-update
 */
class Plug extends Device {
  /**
   * Created by {@link Client} - Do not instantiate directly.
   *
   * See {@link Device#constructor} for common options.
   * @param  {Object} options
   * @param  {Number} [options.inUseThreshold=0]
   */
  constructor (options) {
    super(options);
    if (typeof options === 'undefined') options = {};

    this.log.debug('plug.constructor()');

    this.apiModuleNamespace = {
      'system': 'system',
      'cloud': 'cnCloud',
      'schedule': 'schedule',
      'timesetting': 'time',
      'emeter': 'emeter',
      'netif': 'netif'
    };

    this.inUseThreshold = options.inUseThreshold || 0;

    this.lastState = Object.assign(this.lastState, { powerOn: null, inUse: null });

    this.emitEventsEnabled = true;
  }

  get sysInfo () {
    return super.sysInfo;
  }
  /**
   * @private
   */
  set sysInfo (sysInfo) {
    super.sysInfo = sysInfo;
    this.supportsEmeter = (sysInfo.feature && typeof sysInfo.feature === 'string' ? sysInfo.feature.includes('ENE') : false);
    this.log.debug('[%s] plug sysInfo set', this.alias);
    this.emitEvents();
  }

  get emeterRealtime () {
    return super.emeterRealtime;
  }
  /**
   * @private
   */
  set emeterRealtime (emeterRealtime) {
    this.log.debug('[%s] plug emeterRealtime set', this.alias);
    this._emeterRealtime = emeterRealtime;
    if (this.supportsEmeter) {
      this.emitEvents();
    }
  }
  /**
   * Determines if device is in use based on cached `emeter.get_realtime` results.
   *
   * If device supports energy monitoring (HS110): `power > inUseThreshold`
   *
   * Otherwise fallback on relay state:  `relay_state === 1`
   * @return {boolean}
   */
  get inUse () {
    if (this.supportsEmeter) {
      return (this.emeterRealtime.power > this.inUseThreshold);
    }
    return (this.sysInfo.relay_state === 1);
  }

  /**
   * Same as {@link #inUse}, but requests current `emeter.get_realtime`.
   * @return {Promise<boolean, ResponseError>}
   */
  async getInUse () {
    if (this.supportsEmeter) {
      await this.getEmeterRealtime();
    } else {
      await this.getSysInfo();
    }
    return this.inUse;
  }
  /**
   * Plug's relay was turned on.
   * @event Plug#power-on
   */
  /**
   * Plug's relay was turned off.
   * @event Plug#power-off
   */
  /**
   * Plug's relay state was updated from device. Fired regardless if status was changed.
   * @event Plug#power-update
   * @property {boolean} value Relay State
   */
  /**
   * Plug's relay was turned on _or_ power draw exceeded `inUseThreshold` for HS110
   * @event Plug#in-use
   */
  /**
   * Plug's relay was turned off _or_ power draw fell below `inUseThreshold` for HS110
   * @event Plug#not-in-use
   */
  /**
   * Plug's in-use state was updated from device. Fired regardless if status was changed.
   * @event Plug#in-use-update
   * @property {boolean} value In Use State
   */
  /**
   * Plug's Energy Monitoring Details were updated from device. Fired regardless if status was changed.
   * @event Plug#emeter-realtime-update
   * @property {Object} value emeterRealtime
   */
  /**
   * @private
   */
  emitEvents () {
    if (!this.emitEventsEnabled) { return; }

    const inUse = this.inUse;
    const powerOn = (this.sysInfo.relay_state === 1);

    this.log.debug('[%s] plug.emitEvents() inUse: %s powerOn: %s lastState: %j', this.alias, inUse, powerOn, this.lastState);
    if (this.lastState.inUse !== inUse) {
      this.lastState.inUse = inUse;
      if (inUse) {
        this.emit('in-use');
      } else {
        this.emit('not-in-use');
      }
    }
    this.emit('in-use-update', inUse);

    if (this.lastState.powerOn !== powerOn) {
      this.lastState.powerOn = powerOn;
      if (powerOn) {
        this.emit('power-on');
      } else {
        this.emit('power-off');
      }
    }
    this.emit('power-update', powerOn);

    // if (this.supportsEmeter) {
    //   this.emit('emeter-realtime-update', this.emeterRealtime);
    // }
  }
  /**
   * Requests common Plug status details in a single request.
   * - `system.get_sysinfo`
   * - `cloud.get_sysinfo`
   * - `emeter.get_realtime`
   * - `schedule.get_next_action`
   * @return {Promise<Object, Error>} parsed JSON response
   */
  async getInfo () {
    // TODO switch to sendCommand, but need to handle error for devices that don't support emeter
    let data = await this.send('{"emeter":{"get_realtime":{}},"schedule":{"get_next_action":{}},"system":{"get_sysinfo":{}},"cnCloud":{"get_info":{}}}');
    this.sysInfo = data.system.get_sysinfo;
    this.cloudInfo = data.cnCloud.get_info;
    this.emeterRealtime = data.emeter.get_realtime;
    this.scheduleNextAction = data.schedule.get_next_action;
    return {sysInfo: this.sysInfo, cloudInfo: this.cloudInfo, emeterRealtime: this.emeterRealtime, scheduleNextAction: this.scheduleNextAction};
  }
  /**
   * Get Plug relay state (on/off).
   *
   * Requests `system.get_sysinfo` and returns true if `relay_state === 1`.
   * @return {Promise<boolean, ResponseError>}
   */
  async getPowerState () {
    let sysInfo = await this.getSysInfo();
    return (sysInfo.relay_state === 1);
  }
  /**
   * Turns Plug relay on/off.
   *
   * Sends `system.set_relay_state` command.
   * @param  {boolean}  value
   * @return {Promise<boolean, ResponseError>}
   */
  async setPowerState (value) {
    this.log.debug('[%s] plug.setPowerState(%s)', this.alias, value);
    await this.sendCommand(`{"system":{"set_relay_state":{"state":${(value ? 1 : 0)}}}}`);
    this.sysInfo.relay_state = (value ? 1 : 0);
    this.emitEvents();
    return true;
  }
  /**
   * Get Away Rules.
   *
   * Requests `anti_theft.get_rules`.
   * @return {Promise<Object, ResponseError>} parsed JSON response
   */
  async getAwayRules () {
    return this.sendCommand(`{"anti_theft":{"get_rules":{}}}`);
  }
  /**
   * Get Timer Rules.
   *
   * Requests `count_down.get_rules`.
   * @return {Promise<Object, ResponseError>} parsed JSON response
   */
  async getTimerRules () {
    return this.sendCommand(`{"count_down":{"get_rules":{}}}`);
  }
  /**
   * Get Plug LED state (night mode).
   *
   * Requests `system.sys_info` and returns true if `led_off === 0`.
   * @return {Promise<boolean, ResponseError>} LED State, true === on
   */
  async getLedState () {
    let sysInfo = await this.getSysInfo();
    return (sysInfo.led_off === 0);
  }
  /**
   * Turn Plug LED on/off (night mode).
   *
   * Sends `system.set_led_off` command.
   * @param  {boolean}  value LED State, true === on
   * @return {Promise<boolean, ResponseError>}
   */
  async setLedState (value) {
    await this.sendCommand(`{"system":{"set_led_off":{"off":${(value ? 0 : 1)}}}}`);
    this.sysInfo.set_led_off = (value ? 0 : 1);
    return true;
  }
  /**
   * Blink Plug LED.
   *
   * Sends `system.set_led_off` command alternating on and off number of `times` at `rate`,
   * then sets the led to its pre-blink state.
   *
   * Note: `system.set_led_off` is particulally slow, so blink rate is not guaranteed.
   * @param  {number}  [times=5]
   * @param  {number}  [rate=1000]
   * @return {Promise<boolean, ResponseError>}
   */
  async blink (times = 5, rate = 1000) {
    let delay = (t) => { return new Promise((resolve) => { setTimeout(resolve, t); }); };

    let origLedState = await this.getLedState();
    let lastBlink = Date.now();

    let currLedState = false;
    for (var i = 0; i < times * 2; i++) {
      currLedState = !currLedState;
      lastBlink = Date.now();
      await this.setLedState(currLedState);
      let timeToWait = (rate / 2) - (Date.now() - lastBlink);
      if (timeToWait > 0) {
        await delay(timeToWait);
      }
    }
    if (currLedState !== origLedState) {
      await this.setLedState(origLedState);
    }
    return true;
  }
}

module.exports = Plug;
<|endoftext|>


<commit_before>var config = require('./config.js');
var log = require('./log.js');
var connection = require('./connection.js');
var net = require('net');

function pool () {
	var self = this;

	self.remote_config = config.get('remote');
	self.pool_config = config.get('pool');
	self.connections = [];
	log.info('#pool# config: enabled=${enabled}, capacity=${capacity}, expiration=${expiration}', self.pool_config);

	if(!self.pool_config.enabled) {
		return;
	}

	for(var count = 0; count < self.pool_config.capacity; count++) {
		self.inject();
	}
}
pool.prototype.inject = function () {
	var self = this;

	var new_connection = new connection();
	var remote_socket = net.connect(self.remote_config, function () {
		remote_socket.connected = true;
		self.connections.push(new_connection);
	});
	new_connection.set_remote(remote_socket);
	remote_socket.emit('create', self.remote_config);
	remote_socket.on('error', function (err) {
		log.error('#pool# remote_socket error: ${syscall} ${errno}', err);
	});
	remote_socket.on('close', function () {
		log.info('#pool# remote_socket close');
		if(self.closed) {
			return;
		}
		var index = self.connections.indexOf(remote_socket);
		if(index >= 0) {
			self.connections.splice(index, 1);
			self.inject();
		} else if (!remote_socket.connected) {
			setTimeout(function () {
				self.inject();
			}, 1000 * self.pool_config.expiration);
		}
	});
}
pool.prototype.fetch = function (client_socket) {
	var self = this;
	log.info('#pool# get a connection, total: ' + self.connections.length);

	var new_connection
	if(!self.connections.length) {
		new_connection = new connection();
		var remote_socket = net.connect(self.remote_config);
		remote_socket.emit('create', self.remote_config);
		new_connection.set_remote(remote_socket);
	} else {
		self.inject();
		new_connection = self.connections.shift();
	}
	new_connection.set_client(client_socket);
	new_connection.go();
}

module.exports = pool;<commit_msg>修改连接池添加新连接机制<commit_after>var config = require('./config.js');
var log = require('./log.js');
var connection = require('./connection.js');
var net = require('net');

function pool () {
	var self = this;

	self.remote_config = config.get('remote');
	self.pool_config = config.get('pool');
	self.connections = [];
	log.info('#pool# config: enabled=${enabled}, capacity=${capacity}, expiration=${expiration}', self.pool_config);

	if(!self.pool_config.enabled) {
		return;
	}

	for(var count = 0; count < self.pool_config.capacity; count++) {
		self.inject();
	}
}
pool.prototype.inject = function () {
	var self = this;

	var new_connection = new connection();
	self.connections.push(new_connection);
	var remote_socket = net.connect(self.remote_config, function () {
		remote_socket.connected = true;
	});
	new_connection.set_remote(remote_socket);
	remote_socket.emit('create', self.remote_config);
	remote_socket.on('error', function (err) {
		log.error('#pool# remote_socket error: ${syscall} ${errno}', err);
	});
	remote_socket.on('close', function () {
		log.info('#pool# remote_socket close');

		if (remote_socket.connected) {
			var index = self.connections.indexOf(remote_socket);
			if(index >= 0) {
				self.connections.splice(index, 1);
				self.inject();
			}
		} else {
			setTimeout(function () {
				self.inject();
			}, 1000 * self.pool_config.expiration);
		}
	});
}
pool.prototype.fetch = function (client_socket) {
	var self = this;
	log.info('#pool# get a connection, total: ' + self.connections.length);

	var new_connection;
	if(!self.connections.length) {
		new_connection = new connection();
		var remote_socket = net.connect(self.remote_config);
		new_connection.set_remote(remote_socket);
		remote_socket.emit('create', self.remote_config);
	} else {
		self.inject();
		new_connection = self.connections.shift();
	}
	new_connection.set_client(client_socket);
	new_connection.go();
}

module.exports = pool;<|endoftext|>


<commit_before>import { KinveyError, NotFoundError } from 'kinvey-javascript-sdk-core/build/errors';
import { EventEmitter } from 'events';
import { DataStore, DataStoreType } from 'kinvey-javascript-sdk-core/build/stores/datastore';
import { HttpMethod, AuthType } from 'kinvey-javascript-sdk-core/build/enums';
import { User } from 'kinvey-javascript-sdk-core/build/user';
import { NetworkRequest } from 'kinvey-javascript-sdk-core/build/requests/network';
import { Client } from 'kinvey-javascript-sdk-core/build/client';
import { Query } from 'kinvey-javascript-sdk-core/build/query';
import { isiOS, isAndroid } from './utils';
import assign from 'lodash/assign';
import url from 'url';
const pushNamespace = process.env.KINVEY_PUSH_NAMESPACE || 'push';
const notificationEvent = process.env.KINVEY_NOTIFICATION_EVENT || 'notification';
const deviceCollectionName = process.env.KINVEY_DEVICE_COLLECTION_NAME || 'kinvey_device';
const emitter = new EventEmitter();
let phonegapPush = null;

export class Push {
  static listeners() {
    return emitter.listeners(notificationEvent);
  }

  static onNotification(listener) {
    return emitter.on(notificationEvent, listener);
  }

  static onceNotification(listener) {
    return emitter.once(notificationEvent, listener);
  }

  static removeListener(listener) {
    return emitter.removeListener(notificationEvent, listener);
  }

  static removeAllListeners() {
    return emitter.removeAllListeners(notificationEvent);
  }

  static isSupported() {
    return isiOS() || isAndroid();
  }

  static register(options = {}) {
    if (!Push.isSupported()) {
      return Promise.reject(new KinveyError('Kinvey currently only supports ' +
        'push notifications on iOS and Android platforms.'));
    }

    options = assign({
      android: {
        senderID: undefined
      },
      ios: {
        alert: true,
        badge: true,
        sound: true
      },
      force: false
    }, options);

    const promise = new Promise((resolve, reject) => {
      if (typeof global.PushNotification === 'undefined') {
        return reject(new KinveyError('PhoneGap Push Notification Plugin is not installed.',
          'Please refer to http://devcenter.kinvey.com/phonegap/guides/push#ProjectSetUp for ' +
          'setting up your project.'));
      }

      phonegapPush = global.PushNotification.init(options);

      phonegapPush.on('registration', data => {
        resolve(data.registrationId);
      });

      phonegapPush.on('notification', data => {
        emitter.emit(notificationEvent, data);
      });

      phonegapPush.on('error', error => {
        reject(new KinveyError('An error occurred registering this device for push notifications.', error));
      });

      return phonegapPush;
    }).then(deviceId => {
      if (!deviceId) {
        throw new KinveyError('Unable to retrieve the device id to register this device for push notifications.');
      }

      const store = DataStore.getInstance(deviceCollectionName, DataStoreType.Sync);
      store.disableSync();
      return store.findById(deviceId).catch(error => {
        if (error instanceof NotFoundError) {
          return undefined;
        }

        throw error;
      }).then(entity => {
        if (entity && options.force !== true) {
          return entity;
        }

        const user = User.getActiveUser();
        const client = Client.sharedInstance();
        const request = new NetworkRequest({
          method: HttpMethod.POST,
          url: url.format({
            protocol: client.protocol,
            host: client.host,
            pathname: `/${pushNamespace}/${client.appKey}/register-device`
          }),
          properties: options.properties,
          authType: user ? AuthType.Session : AuthType.Master,
          data: {
            platform: global.device.platform.toLowerCase(),
            framework: 'phonegap',
            deviceId: deviceId,
            userId: user ? undefined : options.userId
          },
          timeout: options.timeout
        });
        return request.execute().then(() => store.save({ _id: deviceId, registered: true }));
      });
    });

    return promise;
  }

  static unregister(options = {}) {
    if (!Push.isSupported()) {
      return Promise.reject(new KinveyError('Kinvey currently only supports ' +
        'push notifications on iOS and Android platforms.'));
    }

    const store = DataStore.getInstance(deviceCollectionName, DataStoreType.Sync);
    store.disableSync();
    const query = new Query();
    query.equalsTo('registered', true);
    const promise = store.find(query).then(data => {
      if (data.length === 1) {
        return data[0]._id;
      }

      return undefined;
    }).then(deviceId => {
      if (!deviceId) {
        throw new KinveyError('This device has not been registered.');
      }

      const user = User.getActiveUser();
      const client = Client.sharedInstance();
      const request = new NetworkRequest({
        method: HttpMethod.POST,
        url: url.format({
          protocol: client.protocol,
          host: client.host,
          pathname: `/${pushNamespace}/${client.appKey}/unregister-device`
        }),
        properties: options.properties,
        authType: user ? AuthType.Session : AuthType.Master,
        data: {
          platform: global.device.platform.toLowerCase(),
          framework: 'phonegap',
          deviceId: deviceId,
          userId: user ? null : options.userId
        },
        timeout: options.timeout
      });
      return request.execute().then(response => store.removeById(deviceId).then(() => response.data));
    });

    return promise;
  }
}
<commit_msg>MLIBZ-1016 Fix push registration on iOS devices<commit_after>import { KinveyError, NotFoundError } from 'kinvey-javascript-sdk-core/build/errors';
import { EventEmitter } from 'events';
import { DataStore, DataStoreType } from 'kinvey-javascript-sdk-core/build/stores/datastore';
import { HttpMethod, AuthType } from 'kinvey-javascript-sdk-core/build/enums';
import { User } from 'kinvey-javascript-sdk-core/build/user';
import { NetworkRequest } from 'kinvey-javascript-sdk-core/build/requests/network';
import { Client } from 'kinvey-javascript-sdk-core/build/client';
import { Query } from 'kinvey-javascript-sdk-core/build/query';
import { isiOS, isAndroid } from './utils';
import assign from 'lodash/assign';
import url from 'url';
import bind from 'lodash/bind';
const pushNamespace = process.env.KINVEY_PUSH_NAMESPACE || 'push';
const notificationEvent = process.env.KINVEY_NOTIFICATION_EVENT || 'notification';
const deviceCollectionName = process.env.KINVEY_DEVICE_COLLECTION_NAME || 'kinvey_device';

export class Push extends EventEmitter {
  constructor() {
    super();

    this.client = Client.sharedInstance();
    this.eventListeners = {
      notificationListener: bind(this.notificationListener, this)
    };

    const pushOptions = this.client.push;
    if (pushOptions && typeof global.PushNotification === 'undefined') {
      this.phonegapPush = global.PushNotification.init(pushOptions);
      this.phonegapPush.on(notificationEvent, this.eventListeners.notificationListener);
    }
  }

  get _pathname() {
    return `/${pushNamespace}/${this.client.appKey}`;
  }

  get client() {
    return this._client;
  }

  set client(client) {
    if (!client) {
      throw new KinveyError('$kinvey.Push much have a client defined.');
    }

    this._client = client;
  }

  isSupported() {
    return isiOS() || isAndroid();
  }

  onNotification(listener) {
    return this.on(notificationEvent, listener);
  }

  onceNotification(listener) {
    return this.once(notificationEvent, listener);
  }

  notificationListener(data) {
    this.emit(notificationEvent, data);
  }

  register(options = {}) {
    if (!this.isSupported()) {
      return Promise.reject(new KinveyError('Kinvey currently only supports ' +
        'push notifications on iOS and Android platforms.'));
    }

    options = assign({
      android: {
        senderID: undefined
      },
      ios: {
        alert: true,
        badge: true,
        sound: true
      },
      force: false
    }, options);

    const promise = new Promise((resolve, reject) => {
      if (typeof global.PushNotification === 'undefined') {
        return reject(new KinveyError('PhoneGap Push Notification Plugin is not installed.',
          'Please refer to http://devcenter.kinvey.com/phonegap-v3.0/guides/push#ProjectSetUp for help with ' +
          'setting up your project.'));
      }

      if (this.phonegapPush) {
        this.phonegapPush.off(notificationEvent, this.eventListeners.notificationListener);
      }

      return global.PushNotification.hasPermission(data => {
        if (!data.isEnabled) {
          return reject(new KinveyError('Permission for push notifications has not been granted by the user.'));
        }

        this.phonegapPush = global.PushNotification.init(options);

        this.phonegapPush.on('registration', data => {
          resolve(data.registrationId);
        });

        this.phonegapPush.on('error', error => {
          reject(new KinveyError('An error occurred registering this device for push notifications.', error));
        });

        return this;
      });
    }).then(deviceId => {
      if (!deviceId) {
        throw new KinveyError('Unable to retrieve the device id to register this device for push notifications.');
      }

      const store = DataStore.getInstance(deviceCollectionName, DataStoreType.Sync);
      store.client = this.client;
      store.disableSync();
      return store.findById(deviceId).catch(error => {
        if (error instanceof NotFoundError) {
          return undefined;
        }

        throw error;
      }).then(entity => {
        if (entity && options.force !== true) {
          this.phonegapPush.on(notificationEvent, this.eventListeners.notificationListener);
          return entity;
        }

        const user = User.getActiveUser(this.client);
        const request = new NetworkRequest({
          method: HttpMethod.POST,
          url: url.format({
            protocol: this.client.protocol,
            host: this.client.host,
            pathname: `${this._pathname}/register-device`
          }),
          properties: options.properties,
          authType: user ? AuthType.Session : AuthType.Master,
          data: {
            platform: global.device.platform.toLowerCase(),
            framework: 'phonegap',
            deviceId: deviceId,
            userId: user ? undefined : options.userId
          },
          timeout: options.timeout
        });
        return request.execute().then(() => store.save({ _id: deviceId, registered: true })).then(response => {
          this.phonegapPush.on(notificationEvent, this.eventListeners.notificationListener);
          this.client.push = options;
          return response;
        });
      });
    });

    return promise;
  }

  unregister(options = {}) {
    if (!this.isSupported()) {
      return Promise.reject(new KinveyError('Kinvey currently only supports ' +
        'push notifications on iOS and Android platforms.'));
    }

    const store = DataStore.getInstance(deviceCollectionName, DataStoreType.Sync);
    store.client = this.client;
    store.disableSync();

    let promise = new Promise((resolve, reject) => {
      if (this.phonegapPush) {
        this.phonegapPush.unregister(() => {
          this.phonegapPush = null;
          resolve();
        }, () => {
          reject(new KinveyError('Unable to unregister with the PhoneGap Push Plugin.'));
        });
      }

      resolve();
    });

    promise = promise.then(() => {
      const query = new Query();
      query.equalsTo('registered', true);
      return store.find(query);
    }).then(data => {
      if (data.length === 1) {
        return data[0]._id;
      }

      return undefined;
    }).then(deviceId => {
      if (!deviceId) {
        throw new KinveyError('This device has not been registered for push notifications.');
      }

      const user = User.getActiveUser(this.client);
      const request = new NetworkRequest({
        method: HttpMethod.POST,
        url: url.format({
          protocol: this.client.protocol,
          host: this.client.host,
          pathname: `${this._pathname}/unregister-device`
        }),
        properties: options.properties,
        authType: user ? AuthType.Session : AuthType.Master,
        data: {
          platform: global.device.platform.toLowerCase(),
          framework: 'phonegap',
          deviceId: deviceId,
          userId: user ? null : options.userId
        },
        timeout: options.timeout
      });
      return request.execute().then(response => store.removeById(deviceId).then(() => {
        this.client.push = null;
        return response.data;
      }));
    });

    return promise;
  }
}
<|endoftext|>


<commit_before>var nodegit = require("nodegit");
var path = require("path");
var promisify = require("promisify-node");
var fse = promisify(require("fs-extra"));
fse.ensureDir = promisify(fse.ensureDir);

var fileName = "newFile.txt";
var fileContent = "hello world";

var repository;
var remote;
var oid;

var signature = nodegit.Signature.create("Foo bar",
  "foo@bar.com", 123456789, 60);

// Load up the repository index and make our initial commit to HEAD
nodegit.Repository.open(path.resolve(__dirname, "../.git"))
  .then(function(repoResult) {
    repository = repoResult;

    return repository.refreshIndex();
  })
  .then(function(index) {
    return index.addAll()
      .then(function() {
        return index.write();
      })
      .then(function() {
        return index.writeTree();
      });
  })
  .then(function(oidResult) {
    oid = oidResult;
    return nodegit.Reference.nameToId(repository, "HEAD");
  })
  .then(function(head) {
    return repository.getCommit(head);
  })
  .then(function(parent) {
    return repository.createCommit("HEAD", signature, signature,
      "commit 111", oid, [parent]);
  })

  // Add a new remote
  .then(function() {
    return nodegit.Remote.push(
        ["refs/heads/master:refs/heads/master"],
        {
          callbacks: {
            credentials: function(url, userName) {
              return nodegit.Cred.sshKeyFromAgent(userName);
            }
          }
        }
      );
  }).done(function() {
    console.log("Done!");
  });
<commit_msg>commit 111<commit_after>var nodegit = require("nodegit");
var path = require("path");
var promisify = require("promisify-node");
var fse = promisify(require("fs-extra"));
fse.ensureDir = promisify(fse.ensureDir);

var fileName = "newFile.txt";
var fileContent = "hello world";

var repository;
var remote;
var oid;

var signature = nodegit.Signature.create("Foo bar",
  "foo@bar.com", 123456789, 60);

// Load up the repository index and make our initial commit to HEAD
nodegit.Repository.open(path.resolve(__dirname, "../.git"))
  .then(function(repoResult) {
    repository = repoResult;

    return repository.refreshIndex();
  })
  .then(function(index) {
    return index.addAll()
      .then(function() {
        return index.write();
      })
      .then(function() {
        return index.writeTree();
      });
  })
  .then(function(oidResult) {
    oid = oidResult;
    return nodegit.Reference.nameToId(repository, "HEAD");
  })
  .then(function(head) {
    return repository.getCommit(head);
  })
  .then(function(parent) {
    return repository.createCommit("HEAD", signature, signature,
      "commit 111", oid, [parent]);
  })

  // Add a new remote
  .then(function() {
    return nodegit.Remote.create(repository, "origin",
      "git@github.com:tlqylzn/gitnode.git")
    .then(function(remoteResult) {
      remote = remoteResult;

      // Create the push object for this remote
      return nodegit.Remote.push(
        ["refs/heads/master:refs/heads/master"],
        {
          callbacks: {
            credentials: function(url, userName) {
              return nodegit.Cred.sshKeyFromAgent(userName);
            }
          }
        }
      );
    });
  }).done(function() {
    console.log("Done!");
  });
<|endoftext|>


<commit_before>import FileSaver from 'file-saver';
import LZString from 'lz-string';
import applyPolyfills from './polyfills';
import initWebAudio from './initWebAudio';

// Shim drag and drop for mobile browsers
var iosDragDropShim = { enableEnterLeave: true };
require('drag-drop-webkit-mobile');

applyPolyfills();

const availableBlockClasses = {}; // maps block class id to block class
[
  'bigben',
  'convolux',
  'egen',
  'gainer',
  'orinami',
  'remark',
  'scope',
  'thumper',
  'waverly',
].forEach(n => {
  availableBlockClasses[n] = require('./blocks/' + n + '/block.js').default; // hacky for now
});

import createRacketWithBlocks from './blocks/racket';
const availableBlockClassesIncludingRacks = Object.assign({}, availableBlockClasses);
availableBlockClassesIncludingRacks['racket'] = createRacketWithBlocks(availableBlockClasses);

// Create a Web Audio context
const audioContext = initWebAudio(window);

let rootBlockClassId;
let rootBlockInst;

function loadRootBlock(blockClassId, settings) {
  // Unload any current root block
  if (rootBlockInst) {
    rootBlockInst.outputs.audio.node.disconnect(audioContext.destination);
    if (rootBlockInst.deactivate) {
      rootBlockInst.deactivate();
    }
    document.querySelector('#root-container').innerHTML = '';
  }

  rootBlockClassId = blockClassId;

  // Instantiate the root block, which will let the user dynamically instantiate
  //  and connect up other blocks.
  rootBlockInst = new (availableBlockClassesIncludingRacks[blockClassId])(document, audioContext, settings);

  // Mount the root block's window view
  // TODO: Handle case where windowView is undefined
  document.querySelector('#root-container').appendChild(rootBlockInst.windowView);

  // Connect root audio output to context final output
  rootBlockInst.outputs.audio.node.connect(audioContext.destination);
}

function loadEmptyRack() {
  const EMPTY_RACK_SETTINGS = {"bm":{"ro":{"b":"__ro","s":{"p":{"audio":{"t":"audio"}}},"n":"Rack Outputs"},"b1":{"b":"remark","s":{"t":"WELCOME TO PLINTH!\n\nSome tips to get you started:\n- Drag blocks from the block palette on the left into the rack area on the right.\n- Press the Flip button (or space bar) to show the back view of the rack, where you can click or drag to wire connections between block ports.\n- To remove wires, toggle Delete Wires mode and click on them.\n- Click on any block in the palette for help.\n- To hear something, connect an audio generating block (such as Waverly) to the Rack Outputs block audio port.\n- Unwanted blocks can be deleted with the small X button in the back view. Once you've read this, feel free to delete this block.\n\nHappy patching!"},"n":"Remark"}},"bo":["b1","ro"],"c":[]};
  loadRootBlock('racket', EMPTY_RACK_SETTINGS);
}

const loadScreenElem = document.querySelector('#load-screen');
const loadScreenCloseButtonElem = document.querySelector('#load-screen-close-button');

const LOAD_SCREEN_FADE_TIME = 0.15;

function showLoadScreen() {
  if (rootBlockInst) {
    loadScreenCloseButtonElem.style.visibility = 'visible';
  } else {
    loadScreenCloseButtonElem.style.visibility = 'hidden';
  }

  loadScreenElem.style.transition = 'visibility 0s,opacity ' + LOAD_SCREEN_FADE_TIME + 's linear';
  loadScreenElem.style.visibility = 'visible';
  loadScreenElem.style.opacity = '1';
  loadScreenElem.style.pointerEvents = 'auto';
}

function hideLoadScreen() {
  loadScreenElem.style.transition = 'visibility 0s linear ' + LOAD_SCREEN_FADE_TIME + 's,opacity ' + LOAD_SCREEN_FADE_TIME + 's linear';
  loadScreenElem.style.visibility = 'hidden';
  loadScreenElem.style.opacity = '0';
  loadScreenElem.style.pointerEvents = 'none';
}

loadScreenCloseButtonElem.addEventListener('click', e => {
  e.preventDefault();
  hideLoadScreen();
});

document.querySelector('#new-patch-button').addEventListener('click', e => {
  e.preventDefault();
  showLoadScreen();
});

document.querySelector('#load-empty-rack-button').addEventListener('click', e => {
  e.preventDefault();
  loadEmptyRack();
  hideLoadScreen();
});

const loadPresetFileChooserElem = document.querySelector('#load-preset-file-chooser');
loadPresetFileChooserElem.addEventListener('change', e => {
  e.preventDefault();

  const files = loadPresetFileChooserElem.files;
  if (files.length < 1) {
    // No file chosen
    return;
  }
  loadPresetFileChooserElem.files = null;
  const file = files[0];
  const reader = new FileReader();
  reader.onload = (e) => {
    // TODO: various error handling
    const presetJSON = e.target.result;
    const presetObj = JSON.parse(presetJSON);
    if (presetObj.plinthPresetVersion === undefined) {
      throw new Error('Not a preset');
    }
    loadRootBlock(presetObj.b, presetObj.s);
    hideLoadScreen();
  };
  reader.readAsText(file, 'utf-8');
});

document.querySelector('#save-preset-button').addEventListener('click', e => {
  e.preventDefault();

  let settings = null;
  if (rootBlockInst.save) {
    settings = rootBlockInst.save();
  } else {
    console.log("Loaded block doesn't support saving settings");
  }

  const presetObj = {
    plinthPresetVersion: 0,
    b: rootBlockClassId,
    s: settings,
  };

  const presetJSON = JSON.stringify(presetObj);

  const uriPresetJSON = LZString.compressToEncodedURIComponent(presetJSON);
  console.log('encoded URI length:', uriPresetJSON.length);

  FileSaver.saveAs(new Blob([presetJSON], {type: "application/json;charset=utf-8"}), 'preset.json');
});
<commit_msg>Fix cross-browser bug with preset loading<commit_after>import FileSaver from 'file-saver';
import LZString from 'lz-string';
import applyPolyfills from './polyfills';
import initWebAudio from './initWebAudio';

// Shim drag and drop for mobile browsers
var iosDragDropShim = { enableEnterLeave: true };
require('drag-drop-webkit-mobile');

applyPolyfills();

const availableBlockClasses = {}; // maps block class id to block class
[
  'bigben',
  'convolux',
  'egen',
  'gainer',
  'orinami',
  'remark',
  'scope',
  'thumper',
  'waverly',
].forEach(n => {
  availableBlockClasses[n] = require('./blocks/' + n + '/block.js').default; // hacky for now
});

import createRacketWithBlocks from './blocks/racket';
const availableBlockClassesIncludingRacks = Object.assign({}, availableBlockClasses);
availableBlockClassesIncludingRacks['racket'] = createRacketWithBlocks(availableBlockClasses);

// Create a Web Audio context
const audioContext = initWebAudio(window);

let rootBlockClassId;
let rootBlockInst;

function loadRootBlock(blockClassId, settings) {
  // Unload any current root block
  if (rootBlockInst) {
    rootBlockInst.outputs.audio.node.disconnect(audioContext.destination);
    if (rootBlockInst.deactivate) {
      rootBlockInst.deactivate();
    }
    document.querySelector('#root-container').innerHTML = '';
  }

  rootBlockClassId = blockClassId;

  // Instantiate the root block, which will let the user dynamically instantiate
  //  and connect up other blocks.
  rootBlockInst = new (availableBlockClassesIncludingRacks[blockClassId])(document, audioContext, settings);

  // Mount the root block's window view
  // TODO: Handle case where windowView is undefined
  document.querySelector('#root-container').appendChild(rootBlockInst.windowView);

  // Connect root audio output to context final output
  rootBlockInst.outputs.audio.node.connect(audioContext.destination);
}

function loadEmptyRack() {
  const EMPTY_RACK_SETTINGS = {"bm":{"ro":{"b":"__ro","s":{"p":{"audio":{"t":"audio"}}},"n":"Rack Outputs"},"b1":{"b":"remark","s":{"t":"WELCOME TO PLINTH!\n\nSome tips to get you started:\n- Drag blocks from the block palette on the left into the rack area on the right.\n- Press the Flip button (or space bar) to show the back view of the rack, where you can click or drag to wire connections between block ports.\n- To remove wires, toggle Delete Wires mode and click on them.\n- Click on any block in the palette for help.\n- To hear something, connect an audio generating block (such as Waverly) to the Rack Outputs block audio port.\n- Unwanted blocks can be deleted with the small X button in the back view. Once you've read this, feel free to delete this block.\n\nHappy patching!"},"n":"Remark"}},"bo":["b1","ro"],"c":[]};
  loadRootBlock('racket', EMPTY_RACK_SETTINGS);
}

const loadScreenElem = document.querySelector('#load-screen');
const loadScreenCloseButtonElem = document.querySelector('#load-screen-close-button');

const LOAD_SCREEN_FADE_TIME = 0.15;

function showLoadScreen() {
  if (rootBlockInst) {
    loadScreenCloseButtonElem.style.visibility = 'visible';
  } else {
    loadScreenCloseButtonElem.style.visibility = 'hidden';
  }

  loadScreenElem.style.transition = 'visibility 0s,opacity ' + LOAD_SCREEN_FADE_TIME + 's linear';
  loadScreenElem.style.visibility = 'visible';
  loadScreenElem.style.opacity = '1';
  loadScreenElem.style.pointerEvents = 'auto';
}

function hideLoadScreen() {
  loadScreenElem.style.transition = 'visibility 0s linear ' + LOAD_SCREEN_FADE_TIME + 's,opacity ' + LOAD_SCREEN_FADE_TIME + 's linear';
  loadScreenElem.style.visibility = 'hidden';
  loadScreenElem.style.opacity = '0';
  loadScreenElem.style.pointerEvents = 'none';
}

loadScreenCloseButtonElem.addEventListener('click', e => {
  e.preventDefault();
  hideLoadScreen();
});

document.querySelector('#new-patch-button').addEventListener('click', e => {
  e.preventDefault();
  showLoadScreen();
});

document.querySelector('#load-empty-rack-button').addEventListener('click', e => {
  e.preventDefault();
  loadEmptyRack();
  hideLoadScreen();
});

const loadPresetFileChooserElem = document.querySelector('#load-preset-file-chooser');
loadPresetFileChooserElem.addEventListener('change', e => {
  e.preventDefault();

  const files = loadPresetFileChooserElem.files;
  if (files.length < 1) {
    // No file chosen
    return;
  }
  const file = files[0];
  const reader = new FileReader();
  reader.onload = (e) => {
    // TODO: various error handling
    const presetJSON = e.target.result;
    const presetObj = JSON.parse(presetJSON);
    if (presetObj.plinthPresetVersion === undefined) {
      throw new Error('Not a preset');
    }
    loadRootBlock(presetObj.b, presetObj.s);
    hideLoadScreen();
  };
  reader.readAsText(file, 'utf-8');
});

document.querySelector('#save-preset-button').addEventListener('click', e => {
  e.preventDefault();

  let settings = null;
  if (rootBlockInst.save) {
    settings = rootBlockInst.save();
  } else {
    console.log("Loaded block doesn't support saving settings");
  }

  const presetObj = {
    plinthPresetVersion: 0,
    b: rootBlockClassId,
    s: settings,
  };

  const presetJSON = JSON.stringify(presetObj);

  const uriPresetJSON = LZString.compressToEncodedURIComponent(presetJSON);
  console.log('encoded URI length:', uriPresetJSON.length);

  FileSaver.saveAs(new Blob([presetJSON], {type: "application/json;charset=utf-8"}), 'preset.json');
});
<|endoftext|>