<commit_before>/*
 * Tests the basics of successful loading the nodes into Node-RED
 */

"use strict";

var EventEmitter = require('events'),
    sinon = require('sinon'),
    embeddedStart = require('node-red-embedded-start');
var test = require('tape'),
    fixture = require('./fixture');

const FLOWS_TIMEOUT = 5000;

var before = test;
var RED;
var nodes = {};
var testFlow;

const SENSOR_NODE = 'wirelesstag';
const ALL_NODE = 'wirelesstag-all';
const CONFIG_NODE = 'wirelesstag-config';
const NODE_TYPES = [CONFIG_NODE, SENSOR_NODE, ALL_NODE];

test.onFinish(function() {

    let cloud = nodes[CONFIG_NODE];
    if (cloud && cloud.platform && cloud.platform.connecting) {
        cloud.platform.once('connect', () => fixture.closeUp(RED, testFlow, nodes));
    } else {
        fixture.closeUp(RED, testFlow, nodes);
    }
});

before('can create and initialize Node-RED runtime', function(t) {
    t.plan(3);

    RED = fixture.setup();
    t.ok(RED, 'instantiates Node-RED runtime');

    RED.start().then((result) => {
        t.pass('starts Node-RED runtime');
        return embeddedStart(RED, FLOWS_TIMEOUT, result);
    }).then(() => t.pass("flows started")).catch(fixture.failAndEnd(t));
});

test('our nodes are registered', function(t) {
    t.plan(9);
    NODE_TYPES.forEach((nt) => {
        let Node = RED.nodes.getType(nt);
        t.ok(Node, nt + ' is registered');
        t.equal(typeof Node,
                'function',
                'type is (constructor) function');
        t.ok(Node.prototype instanceof EventEmitter,
             'type inherits from event emitter');
    });
    t.end();
});

test('our nodes can be created and added to a flow', function(t) {
    t.plan(9);

    nodes[CONFIG_NODE] = fixture.createNodeDescriptor(RED, CONFIG_NODE);
    for (let nt of NODE_TYPES) {
        if (nt === CONFIG_NODE) continue;
        nodes[nt] = fixture.createNodeDescriptor(RED, nt, nodes[CONFIG_NODE].id);
    }
    let flow = {
        label: "Test Flow",
        nodes: [nodes[SENSOR_NODE], nodes[ALL_NODE]],
        configs: [nodes[CONFIG_NODE]]
    };
    RED.nodes.addFlow(flow).then((id) => {
        t.ok(id, 'successfully created flow with our nodes');
        testFlow = id;
        return RED.nodes.getFlow(testFlow);
    }).then((result) => {
        t.equal(result.id, testFlow, 'can retrieve the created flow');
        t.equal(result.nodes.length, 2, 'new flow has 2 nodes');
        t.equal(result.configs.length, 1, 'new flow has 1 config node');

        t.equal(result.configs[0].type, 'wirelesstag-config',
                'config node is of our type');
        nodes[result.configs[0].type] = result.configs[0];

        for (let i = 0; i < result.nodes.length; i++) {
            nodes[result.nodes[i].type] = result.nodes[i];
            t.equal(result.nodes[i].cloud,
                    result.configs[0].id,
                    `node ${i} references correct cloud config`);
            t.equal(result.nodes[i].type.indexOf('wirelesstag'), 0,
                    `node ${i} is of one of the types we define`);
        }
    }).then(t.end).catch(fixture.failAndEnd(t));
});

test('once added to a flow nodes can be retrieved from runtime', function(t) {
    t.plan(16);

    for (let nt in nodes) {
        if (! nodes[nt].id) {
            t.skip('missing node object for ' + nt);
            continue;
        }
        let n = RED.nodes.getNode(nodes[nt].id);
        t.ok(n, `can obtain ${nt} node object from RED runtime`);
        if (! n) {
            t.skip('cannot test retrieved node object');
            continue;
        }
        t.equal(n.id, nodes[nt].id, 'it has matching ID');
        t.equal(n.type, nodes[nt].type, 'it has matching type');
        if (nt !== CONFIG_NODE) {
            t.equal(typeof n.debug, "function", 'has debug() log method');
            t.ok(n.config, 'has config property with value');
        }
        nodes[nt] = n;
        let Node = RED.nodes.getType(nt);
        t.ok(n instanceof Node, 'it is instance of constructor');
    }
    t.end();
});

test('connects to cloud and starts IO for our nodes', function(t) {
    t.plan(44);

    let nodeTypes = [SENSOR_NODE, ALL_NODE];
    let startIOspies = {}, sendSpies = {};
    nodeTypes.forEach((nt) => {
        startIOspies[nt] = sinon.spy(nodes[nt], "startIO");
        sendSpies[nt] = sinon.spy(nodes[nt], "send");
    });

    function testMsg(msg, tag, tt) {
        tt.equal(typeof msg.tagManager, 'object', 'message has tagManager object');
        ['mac', 'name'].forEach((k) => {
            tt.equal(msg.tagManager[k],
                     tag.wirelessTagManager[k],
                     `property "${k}" of msg.tagManager is correct`);
        });
        tt.equal(typeof msg.tag, 'object', 'message has tag object');
        ['name', 'uuid', 'slaveId'].forEach((k) => {
            tt.equal(msg.tag[k], tag[k], `property "${k}" of msg.tag is correct`);
        });
        let sensor = tag[msg.payload.sensor + 'Sensor'];
        tt.ok(sensor, 'payload references valid sensor of the tag');
        tt.equal(msg.payload.reading, sensor.reading,
                 'payload gives correct reading');
        tt.equal(msg.payload.eventState, sensor.eventState,
                 'payload gives correct event state');
        tt.equal(msg.payload.armed, sensor.isArmed(),
                 'payload gives correct armed state');
        let topicParts = msg.topic.split("/");
        tt.equal(topicParts.length, 3,
                 'topic consists of 3 elements separated by "/"');
        tt.equal(topicParts[0], tag.wirelessTagManager.mac,
                 'first element is tag manager');
        tt.equal(topicParts[1], tag.slaveId.toString(),
                 'second element is tag (slave ID)');
        tt.equal(topicParts[2], sensor.sensorType,
                 'third element is sensor type');
    }

    function onConnect(platform) {
        t.pass('is connected to cloud');
        nodeTypes.forEach((nt) => {
            t.ok(startIOspies[nt].calledOnce, 'startIO() called for ' + nt);
            t.ok(sendSpies[nt].notCalled, 'send() not called for ' + nt);
        });
        platform.discoverTags().then((tags) => {
            t.ok(tags.length > 0, 'have one or more tags to test');
            let tag = tags[0];
            let sensorNode = nodes[SENSOR_NODE];
            sensorNode.config.tagmanager = tag.wirelessTagManager.mac;
            sensorNode.config.tag = tag.uuid;
            sensorNode.config.sensor = "temp"; // every tag should have temp
            return sensorNode.findTag().then((foundTag) => {
                t.ok(foundTag, 'can find tag through sensor node');
                t.equal(foundTag.uuid, tag.uuid, 'finds the correct tag');
                return foundTag.discoverSensors().then((sensors) => {
                    t.equal(sensors.length, foundTag.eachSensor().length,
                            'finds each of the tag\'s sensors');
                    sensorNode.startIO();
                    return nodes[ALL_NODE].sendData(tag);
                });
            });
        }).then((tag) => {
            t.ok(tag, 'sends data for ' + ALL_NODE);
            t.equal(sendSpies[SENSOR_NODE].callCount, 1,
                    'one message sent for ' + SENSOR_NODE);
            t.equal(sendSpies[ALL_NODE].callCount, tag.eachSensor().length,
                    'one message sent for each sensor for ' + ALL_NODE);
            t.comment(`message properties for ${SENSOR_NODE}:`);
            let msg = sendSpies[SENSOR_NODE].args[0][0];
            testMsg(msg, tag, t);
            t.comment(`message properties for ${ALL_NODE}, temp sensor:`);
            msg = sendSpies[ALL_NODE].args.map((args) => args[0]
            ).filter((msgArg) => msgArg.payload.sensor === "temp");
            testMsg(msg[0], tag, t);
        }).then(() => {
            // cleanup spies right here
            nodeTypes.forEach((nt) => {
                nodes[nt].startIO.restore();
                nodes[nt].send.restore();
            });
            t.pass('done with IO test teardown');
        }).catch(fixture.failAndEnd(t));
    }

    let platform = nodes[CONFIG_NODE].platform;
    t.ok(platform, 'config node has platform object');
    if (platform.connecting) {
        platform.on('connect', onConnect);
    } else {
        platform.isConnected().then((connected) => {
            if (connected) return onConnect(platform);
            t.fail('is not connected nor connecting');
        }).catch(fixture.failAndEnd(t));
    }
});

test('is resilient to platform disconnect when not closing', function(t) {
    if (! process.env.TEST_ALL) {
        t.skip('set TEST_ALL environment to true to enable resilience test');
        return t.end();
    }
    t.plan(10);

    const RECOVERY_TIMEOUT = 60 * 6 * 1000; // 6 minutes
    let confNodeDesc = fixture.createNodeDescriptor(RED, CONFIG_NODE);
    let allNodeDesc = fixture.createNodeDescriptor(RED, ALL_NODE, confNodeDesc.id);
    let nflow = {
        label: "Test2 Flow",
        nodes: [allNodeDesc],
        configs: [confNodeDesc]
    };
    let sendSpy;

    function testResilience(platform) {
        RED.nodes.addFlow(nflow).then((flowid) => {
            t.ok(flowid, 'successfully created test flow with our nodes');
            t.notEqual(flowid, testFlow, 'created flow has its own ID');
            return new Promise((resolve) => {
                setTimeout(() => resolve(RED.nodes.removeFlow(flowid)), 1500);
            });
        }).then(() => {
            t.pass('successfully removed (stopped) new flow again');
            return new Promise((resolve) => {
                setTimeout(() => resolve(platform.isSignedIn()), 500);
            });
        }).then((signedIn) => {
            t.equal(signedIn, false, 'platform is now signed out');
            let nodeObj = RED.nodes.getNode(nodes[ALL_NODE].id);
            t.ok(nodeObj, `retrieved instance of node ${nodeObj.id} for testing recovery`);
            let confNode = RED.nodes.getNode(nodeObj.config.cloud);
            if (! confNode) {
                t.fail(`failed to retrieve config node for ${nodeObj.id}`);
            } else if (confNode.platform === undefined) {
                t.fail(`config node for ${nodeObj.id} does not have platform instance`);
            } else {
                confNode.platform.once('connect', () => {
                    t.pass(`platform instance of ${confNode.id} signed in again`);
                });
            }
            sendSpy = sinon.spy(nodeObj, "send");
            return new Promise((resolve, reject) => {
                let timer = setTimeout(reject.bind(null, nodeObj), RECOVERY_TIMEOUT);
                nodeObj.send = function(msg) {
                    nodeObj.send = sendSpy;
                    nodeObj.send(msg);
                    clearTimeout(timer);
                    t.ok(msg, 'remaining flow recovered to continue sending data');
                    resolve(nodeObj);
                };
            });
        }).then((nodeObj) => {
            setTimeout(() => {
                nodeObj.send.restore();
                let topics = new Set();
                sendSpy.args.map((args) => topics.add(args[0].topic));
                t.equal(topics.size, sendSpy.callCount, 'sent each topic (= sensor) only once');
            }, 100);
            return nodeObj;
        }).catch((nodeObj) => {
            if (sendSpy) {
                nodeObj.send = sendSpy;
                nodeObj.send.restore();
            }
            t.fail('remaining flow failed to continue receiving data');
            t.end();
        });
    }

    let platform = nodes[CONFIG_NODE].platform;
    t.ok(platform, 'config node has platform object');
    platform.isSignedIn().then((signedIn) => {
        t.equal(signedIn, true, "platform is connected");
        return testResilience(platform);
    }).catch(fixture.failAndEnd(t));
});
<commit_msg>Adds test for node ID identity<commit_after>/*
 * Tests the basics of successful loading the nodes into Node-RED
 */

"use strict";

var EventEmitter = require('events'),
    sinon = require('sinon'),
    embeddedStart = require('node-red-embedded-start');
var test = require('tape'),
    fixture = require('./fixture');

const FLOWS_TIMEOUT = 5000;

var before = test;
var RED;
var nodes = {};
var testFlow;

const SENSOR_NODE = 'wirelesstag';
const ALL_NODE = 'wirelesstag-all';
const CONFIG_NODE = 'wirelesstag-config';
const NODE_TYPES = [CONFIG_NODE, SENSOR_NODE, ALL_NODE];

test.onFinish(function() {

    let cloud = nodes[CONFIG_NODE];
    if (cloud && cloud.platform && cloud.platform.connecting) {
        cloud.platform.once('connect', () => fixture.closeUp(RED, testFlow, nodes));
    } else {
        fixture.closeUp(RED, testFlow, nodes);
    }
});

before('can create and initialize Node-RED runtime', function(t) {
    t.plan(3);

    RED = fixture.setup();
    t.ok(RED, 'instantiates Node-RED runtime');

    RED.start().then((result) => {
        t.pass('starts Node-RED runtime');
        return embeddedStart(RED, FLOWS_TIMEOUT, result);
    }).then(() => t.pass("flows started")).catch(fixture.failAndEnd(t));
});

test('our nodes are registered', function(t) {
    t.plan(9);
    NODE_TYPES.forEach((nt) => {
        let Node = RED.nodes.getType(nt);
        t.ok(Node, nt + ' is registered');
        t.equal(typeof Node,
                'function',
                'type is (constructor) function');
        t.ok(Node.prototype instanceof EventEmitter,
             'type inherits from event emitter');
    });
    t.end();
});

test('our nodes can be created and added to a flow', function(t) {
    t.plan(11);

    nodes[CONFIG_NODE] = fixture.createNodeDescriptor(RED, CONFIG_NODE);
    for (let nt of NODE_TYPES) {
        if (nt === CONFIG_NODE) continue;
        nodes[nt] = fixture.createNodeDescriptor(RED, nt, nodes[CONFIG_NODE].id);
    }
    let flow = {
        label: "Test Flow",
        nodes: [nodes[SENSOR_NODE], nodes[ALL_NODE]],
        configs: [nodes[CONFIG_NODE]]
    };
    RED.nodes.addFlow(flow).then((id) => {
        t.ok(id, 'successfully created flow with our nodes');
        testFlow = id;
        return RED.nodes.getFlow(testFlow);
    }).then((result) => {
        t.equal(result.id, testFlow, 'can retrieve the created flow');
        t.equal(result.nodes.length, 2, 'new flow has 2 nodes');
        t.equal(result.configs.length, 1, 'new flow has 1 config node');

        t.equal(result.configs[0].type, 'wirelesstag-config',
                'config node is of our type');
        nodes[result.configs[0].type] = result.configs[0];

        for (let i = 0; i < result.nodes.length; i++) {
            t.equal(result.nodes[i].id, nodes[result.nodes[i].type].id,
                    `node ${i} has correct ID`);
            nodes[result.nodes[i].type] = result.nodes[i];
            t.equal(result.nodes[i].cloud,
                    result.configs[0].id,
                    `node ${i} references correct cloud config`);
            t.equal(result.nodes[i].type.indexOf('wirelesstag'), 0,
                    `node ${i} is of one of the types we define`);
        }
    }).then(t.end).catch(fixture.failAndEnd(t));
});

test('once added to a flow nodes can be retrieved from runtime', function(t) {
    t.plan(16);

    for (let nt in nodes) {
        if (! nodes[nt].id) {
            t.skip('missing node object for ' + nt);
            continue;
        }
        let n = RED.nodes.getNode(nodes[nt].id);
        t.ok(n, `can obtain ${nt} node object from RED runtime`);
        if (! n) {
            t.skip('cannot test retrieved node object');
            continue;
        }
        t.equal(n.id, nodes[nt].id, 'it has matching ID');
        t.equal(n.type, nodes[nt].type, 'it has matching type');
        if (nt !== CONFIG_NODE) {
            t.equal(typeof n.debug, "function", 'has debug() log method');
            t.ok(n.config, 'has config property with value');
        }
        nodes[nt] = n;
        let Node = RED.nodes.getType(nt);
        t.ok(n instanceof Node, 'it is instance of constructor');
    }
    t.end();
});

test('connects to cloud and starts IO for our nodes', function(t) {
    t.plan(44);

    let nodeTypes = [SENSOR_NODE, ALL_NODE];
    let startIOspies = {}, sendSpies = {};
    nodeTypes.forEach((nt) => {
        startIOspies[nt] = sinon.spy(nodes[nt], "startIO");
        sendSpies[nt] = sinon.spy(nodes[nt], "send");
    });

    function testMsg(msg, tag, tt) {
        tt.equal(typeof msg.tagManager, 'object', 'message has tagManager object');
        ['mac', 'name'].forEach((k) => {
            tt.equal(msg.tagManager[k],
                     tag.wirelessTagManager[k],
                     `property "${k}" of msg.tagManager is correct`);
        });
        tt.equal(typeof msg.tag, 'object', 'message has tag object');
        ['name', 'uuid', 'slaveId'].forEach((k) => {
            tt.equal(msg.tag[k], tag[k], `property "${k}" of msg.tag is correct`);
        });
        let sensor = tag[msg.payload.sensor + 'Sensor'];
        tt.ok(sensor, 'payload references valid sensor of the tag');
        tt.equal(msg.payload.reading, sensor.reading,
                 'payload gives correct reading');
        tt.equal(msg.payload.eventState, sensor.eventState,
                 'payload gives correct event state');
        tt.equal(msg.payload.armed, sensor.isArmed(),
                 'payload gives correct armed state');
        let topicParts = msg.topic.split("/");
        tt.equal(topicParts.length, 3,
                 'topic consists of 3 elements separated by "/"');
        tt.equal(topicParts[0], tag.wirelessTagManager.mac,
                 'first element is tag manager');
        tt.equal(topicParts[1], tag.slaveId.toString(),
                 'second element is tag (slave ID)');
        tt.equal(topicParts[2], sensor.sensorType,
                 'third element is sensor type');
    }

    function onConnect(platform) {
        t.pass('is connected to cloud');
        nodeTypes.forEach((nt) => {
            t.ok(startIOspies[nt].calledOnce, 'startIO() called for ' + nt);
            t.ok(sendSpies[nt].notCalled, 'send() not called for ' + nt);
        });
        platform.discoverTags().then((tags) => {
            tags = tags.filter((tag) => tag.isPhysicalTag());
            t.ok(tags.length > 0, 'have one or more tags to test');
            let tag = tags[0];
            let sensorNode = nodes[SENSOR_NODE];
            sensorNode.config.tagmanager = tag.wirelessTagManager.mac;
            sensorNode.config.tag = tag.uuid;
            sensorNode.config.sensor = "temp"; // every tag should have temp
            return sensorNode.findTag().then((foundTag) => {
                t.ok(foundTag, 'can find tag through sensor node');
                t.equal(foundTag.uuid, tag.uuid, 'finds the correct tag');
                return foundTag.discoverSensors().then((sensors) => {
                    t.equal(sensors.length, foundTag.eachSensor().length,
                            'finds each of the tag\'s sensors');
                    sensorNode.startIO();
                    return nodes[ALL_NODE].sendData(tag);
                });
            });
        }).then((tag) => {
            t.ok(tag, 'sends data for ' + ALL_NODE);
            t.equal(sendSpies[SENSOR_NODE].callCount, 1,
                    'one message sent for ' + SENSOR_NODE);
            t.equal(sendSpies[ALL_NODE].callCount, tag.eachSensor().length,
                    'one message sent for each sensor for ' + ALL_NODE);
            t.comment(`message properties for ${SENSOR_NODE}:`);
            let msg = sendSpies[SENSOR_NODE].args[0][0];
            testMsg(msg, tag, t);
            t.comment(`message properties for ${ALL_NODE}, temp sensor:`);
            msg = sendSpies[ALL_NODE].args.map((args) => args[0]
            ).filter((msgArg) => msgArg.payload.sensor === "temp");
            testMsg(msg[0], tag, t);
        }).then(() => {
            // cleanup spies right here
            nodeTypes.forEach((nt) => {
                nodes[nt].startIO.restore();
                nodes[nt].send.restore();
            });
            t.pass('done with IO test teardown');
        }).catch(fixture.failAndEnd(t));
    }

    let platform = nodes[CONFIG_NODE].platform;
    t.ok(platform, 'config node has platform object');
    if (platform.connecting) {
        platform.on('connect', onConnect);
    } else {
        platform.isConnected().then((connected) => {
            if (connected) return onConnect(platform);
            t.fail('is not connected nor connecting');
        }).catch(fixture.failAndEnd(t));
    }
});

test('is resilient to platform disconnect when not closing', function(t) {
    if (! process.env.TEST_ALL) {
        t.skip('set TEST_ALL environment to true to enable resilience test');
        return t.end();
    }
    t.plan(10);

    const RECOVERY_TIMEOUT = 60 * 6 * 1000; // 6 minutes
    let confNodeDesc = fixture.createNodeDescriptor(RED, CONFIG_NODE);
    let allNodeDesc = fixture.createNodeDescriptor(RED, ALL_NODE, confNodeDesc.id);
    let nflow = {
        label: "Test2 Flow",
        nodes: [allNodeDesc],
        configs: [confNodeDesc]
    };
    let sendSpy;

    function testResilience(platform) {
        RED.nodes.addFlow(nflow).then((flowid) => {
            t.ok(flowid, 'successfully created test flow with our nodes');
            t.notEqual(flowid, testFlow, 'created flow has its own ID');
            return new Promise((resolve) => {
                setTimeout(() => resolve(RED.nodes.removeFlow(flowid)), 1500);
            });
        }).then(() => {
            t.pass('successfully removed (stopped) new flow again');
            return new Promise((resolve) => {
                setTimeout(() => resolve(platform.isSignedIn()), 500);
            });
        }).then((signedIn) => {
            t.equal(signedIn, false, 'platform is now signed out');
            let nodeObj = RED.nodes.getNode(nodes[ALL_NODE].id);
            t.ok(nodeObj, `retrieved instance of node ${nodeObj.id} for testing recovery`);
            let confNode = RED.nodes.getNode(nodeObj.config.cloud);
            if (! confNode) {
                t.fail(`failed to retrieve config node for ${nodeObj.id}`);
            } else if (confNode.platform === undefined) {
                t.fail(`config node for ${nodeObj.id} does not have platform instance`);
            } else {
                confNode.platform.once('connect', () => {
                    t.pass(`platform instance of ${confNode.id} signed in again`);
                });
            }
            sendSpy = sinon.spy(nodeObj, "send");
            return new Promise((resolve, reject) => {
                let timer = setTimeout(reject.bind(null, nodeObj), RECOVERY_TIMEOUT);
                nodeObj.send = function(msg) {
                    nodeObj.send = sendSpy;
                    nodeObj.send(msg);
                    clearTimeout(timer);
                    t.ok(msg, 'remaining flow recovered to continue sending data');
                    resolve(nodeObj);
                };
            });
        }).then((nodeObj) => {
            setTimeout(() => {
                nodeObj.send.restore();
                let topics = new Set();
                sendSpy.args.map((args) => topics.add(args[0].topic));
                t.equal(topics.size, sendSpy.callCount, 'sent each topic (= sensor) only once');
            }, 100);
            return nodeObj;
        }).catch((nodeObj) => {
            if (sendSpy) {
                nodeObj.send = sendSpy;
                nodeObj.send.restore();
            }
            t.fail('remaining flow failed to continue receiving data');
            t.end();
        });
    }

    let platform = nodes[CONFIG_NODE].platform;
    t.ok(platform, 'config node has platform object');
    platform.isSignedIn().then((signedIn) => {
        t.equal(signedIn, true, "platform is connected");
        return testResilience(platform);
    }).catch(fixture.failAndEnd(t));
});
<|endoftext|>


<commit_before>/* @flow */
'use strict';

import React from 'react';
import NativeBaseComponent from '../Base/NativeBaseComponent';
import computeProps from '../../Utils/computeProps';

import Ionicons from 'react-native-vector-icons/Ionicons';
import Entypo from 'react-native-vector-icons/Entypo';
import FontAwesome from 'react-native-vector-icons/FontAwesome';
import Foundation from 'react-native-vector-icons/Foundation';
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
import Octicons from 'react-native-vector-icons/Octicons';
import Zocial from 'react-native-vector-icons/Zocial';

export default class IconNB extends NativeBaseComponent {

    propTypes: {
        style : React.PropTypes.object
    }

    contextTypes: {
      theme: React.PropTypes.object
    }

    componentWillMount() {
      switch(this.getTheme().iconFamily) {
        case 'Ionicons':
            this.Icon = Ionicons;
            break;
        case 'Entypo':
            this.Icon = Entypo;
            break;
        case 'FontAwesome':
            this.Icon = FontAwesome;
            break;
        case 'Foundation':
            this.Icon = Foundation;
            break;
        case 'MaterialIcons':
            this.Icon = MaterialIcons;
            break;
        case 'Octicons':
            this.Icon = Octicons;
            break;
        case 'Zocial':
            this.Icon = Zocial;
            break;
        default:
            this.Icon = Ionicons;
      }
    }

    static getImageSource = FontAwesome.getImageSource

    getInitialStyle() {
        return {
            icon: {
                fontSize: this.getTheme().iconFontSize,
                color: this.getContextForegroundColor()
            }
        }
    }
    prepareRootProps() {
        var defaultProps = {
            style: this.getInitialStyle().icon
        };

        return computeProps(this.props, defaultProps);

    }

    render() {
        return(
            <this.Icon {...this.prepareRootProps()}/>
        );
    }
}
<commit_msg>Fixed indentation<commit_after>/* @flow */
'use strict';

import React from 'react';
import NativeBaseComponent from '../Base/NativeBaseComponent';
import computeProps from '../../Utils/computeProps';

import Ionicons from 'react-native-vector-icons/Ionicons';
import Entypo from 'react-native-vector-icons/Entypo';
import FontAwesome from 'react-native-vector-icons/FontAwesome';
import Foundation from 'react-native-vector-icons/Foundation';
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
import Octicons from 'react-native-vector-icons/Octicons';
import Zocial from 'react-native-vector-icons/Zocial';

export default class IconNB extends NativeBaseComponent {

    propTypes: {
        style : React.PropTypes.object
    }

    contextTypes: {
        theme: React.PropTypes.object
    }

    componentWillMount() {
        switch(this.getTheme().iconFamily) {
            case 'Ionicons':
                this.Icon = Ionicons;
                break;
            case 'Entypo':
                this.Icon = Entypo;
                break;
            case 'FontAwesome':
                this.Icon = FontAwesome;
                break;
            case 'Foundation':
                this.Icon = Foundation;
                break;
            case 'MaterialIcons':
                this.Icon = MaterialIcons;
                break;
            case 'Octicons':
                this.Icon = Octicons;
                break;
            case 'Zocial':
                this.Icon = Zocial;
                break;
            default:
                this.Icon = Ionicons;
        }
    }

    static getImageSource = FontAwesome.getImageSource

    getInitialStyle() {
        return {
            icon: {
                fontSize: this.getTheme().iconFontSize,
                color: this.getContextForegroundColor()
            }
        }
    }
    prepareRootProps() {
        var defaultProps = {
            style: this.getInitialStyle().icon
        };

        return computeProps(this.props, defaultProps);

    }

    render() {
        return(
            <this.Icon {...this.prepareRootProps()}/>
        );
    }
}
<|endoftext|>


<commit_before>(function (factory) {

    if (typeof define === "function" && define.amd) {
        define('bforms-extensions', ['jquery'], factory);
    } else {
        factory(window.jQuery);
    }

})(function ($, undefined) {

    //#region parse form
    $.fn.parseForm = function (prefix) {

        // don't change the element sent, we allow jq objects or selectors too
        var $elem = $(this);

        // object to be returned and where all the data goes
        var data = {};

        if ($elem.length > 0) {

            // NORMAL INPUT FIELDS

            // input and select fields
            var input = $elem.find('input[type!="radio"], input[type="radio"]:checked, select, textarea, .checkBoxList-done');

            for (var key in input) {
                if (!isNaN(key)) {

                    var jqEl = $(input[key]);

                    if (jqEl.data('noparse') === true || (jqEl.prev().data('noparse') && jqEl.prev().prop('name') == jqEl.prop('name') === true))
                        continue;

                    //custom value provider?
                    if (jqEl.hasClass('checkBoxList-done')) {
                        $.extend(true, data, jqEl.bsParseCheckList());
                    } else {

                        var name = jqEl.data('formname') || jqEl.attr('name');
                        if (prefix && name) {
                            name = name.replace(prefix, "");
                        }
                        var value = jqEl.data('select2') != null ? jqEl.select2('val') : jqEl.val();

                        if ('undefined' !== typeof (name)) {

                            if (jqEl.attr('type') === 'checkbox') {
                                // checkbox
                                value = jqEl.is(':checked');
                                data[name] = value;

                            } else if ('object' !== typeof (value)) {

                                // normal input
                                if ('undefined' === typeof (data[name]))
                                    data[name] = value;

                            } else if (value !== null) {

                                // multiselect
                                for (k in value) {
                                    data[name + '[' + k + ']'] = value[k];
                                }
                            }
                        }
                    }
                }
            }

            // files
            $elem.find('input[type="file"]').each(function (k, el) {

                var files = el.files;
                if (files != undefined) {
                    var name = el.name;
                    if (files.length > 1) {
                        for (var i = 0; i < files.length; i++) {
                            data[name + '[' + i + ']'] = files[i];
                        }
                    } else if (files.length == 1) {
                        data[name] = files[0];
                    }
                }
            });

        }

        return data;
    };
    //#endregion

    //#region $.fn.resetForm
    $.fn.bsResetForm = function (focus, ignore, triggerChange) {

        $(this).find('input:not(.hasDatepicker, .hasRangepicker, ' + ignore + '), textarea:not(' + ignore + ')').each(function () {
            switch (this.type) {
                case 'password':
                case 'select-multiple':
                case 'select-one':
                case 'text':
                case 'textarea:':
                    if ($(this).hasClass("tag_counter")) {
                        $(this).val('0');
                    } else {
                        $(this).val('');
                        if (triggerChange === true) {
                            $(this).trigger('change');
                        }
                    }
                    break;
                case 'checkbox':
                    if ($(this).hasClass("checkBox-done")) {
                        $(this).attr("checked", $(this).data("initialvalue"));
                        $(this).trigger("change");
                    } else {
                        $(this).attr('checked', false);
                    }
                    break;
                case 'radio':
                    this.checked = false;
                    break;
                case 'range':
                    $(this).val(0);
                    break;
                case 'file':
                    $(this).val('');
                    break;
                case 'hidden':
                    if (typeof $(this).data('select2') !== 'undefined') {
                        $(this).select2('val', '');
                    }
            }
        });
        //#endregion

        //#region select2
        $(this).find('select' + ':not(' + ignore + ')').each(function () {
            var thisObj = $(this);
            thisObj.trigger("change");
            if (thisObj.data('initialvalue')) {
                if (thisObj.data('select2') != null) {
                    thisObj.select2('val', thisObj.data('initialvalue'));
                } else {
                    thisObj.val(thisObj.data('initialvalue'));
                }
            }
            else {
                if (thisObj.data('select2') != null) {
                    thisObj.select2('val', '');
                } else {
                    thisObj.val('');
                }
            }
            //if (thisObj.hasClass("custom_select"))
        });
        //#endregion

        //#region radio buttons
        $(this).find(".bs-radio-list").each(function () {
            if ($(this).data("initialvalue") != undefined) {
                $(this).bsRadioButtonsListUpdateSelf($(this).data("initialvalue"));
            }
        });
        //#endregion

        //#region datePicker
        $(this).find('.hasDatepicker').each(function () {
            $(this).bsDatepicker('resetValue');
        });
        //#endregion

        //#region rangePicker
        $(this).find('.hasRangepicker').each(function () {
            $(this).bsDateRange('resetValue');
        });
        //#endregion

        //#region radioButtonsList
        $(this).find('.radioButtonsList-done').bsResetRadioButtons();
        //#endregion

        if (focus !== false)
            $(this).find("input:first").focus();

        var $form = $([]);

        if (($(this)).is('form')) {
            $form = $(this);
        } else {
            $form = $(this).find('form');
        }

        if ($form.length) {
            $form.each(function() {
                var validator = $(this).data('validator');
                if (typeof validator !== "undefined" && typeof validator.resetForm === "function") {
                    validator.resetForm();
                }
            });
        }

        return $(this);
    };
    //#endregion

});<commit_msg>bsResetForm<commit_after>(function (factory) {

    if (typeof define === "function" && define.amd) {
        define('bforms-extensions', ['jquery'], factory);
    } else {
        factory(window.jQuery);
    }

})(function ($, undefined) {

    //#region parse form
    $.fn.parseForm = function (prefix) {

        // don't change the element sent, we allow jq objects or selectors too
        var $elem = $(this);

        // object to be returned and where all the data goes
        var data = {};

        if ($elem.length > 0) {

            // NORMAL INPUT FIELDS

            // input and select fields
            var input = $elem.find('input[type!="radio"], input[type="radio"]:checked, select, textarea, .checkBoxList-done');

            for (var key in input) {
                if (!isNaN(key)) {

                    var jqEl = $(input[key]);

                    if (jqEl.data('noparse') === true || (jqEl.prev().data('noparse') && jqEl.prev().prop('name') == jqEl.prop('name') === true))
                        continue;

                    //custom value provider?
                    if (jqEl.hasClass('checkBoxList-done')) {
                        $.extend(true, data, jqEl.bsParseCheckList());
                    } else {

                        var name = jqEl.data('formname') || jqEl.attr('name');
                        if (prefix && name) {
                            name = name.replace(prefix, "");
                        }
                        var value = jqEl.data('select2') != null ? jqEl.select2('val') : jqEl.val();

                        if ('undefined' !== typeof (name)) {

                            if (jqEl.attr('type') === 'checkbox') {
                                // checkbox
                                value = jqEl.is(':checked');
                                data[name] = value;

                            } else if ('object' !== typeof (value)) {

                                // normal input
                                if ('undefined' === typeof (data[name]))
                                    data[name] = value;

                            } else if (value !== null) {

                                // multiselect
                                for (k in value) {
                                    data[name + '[' + k + ']'] = value[k];
                                }
                            }
                        }
                    }
                }
            }

            // files
            $elem.find('input[type="file"]').each(function (k, el) {

                var files = el.files;
                if (files != undefined) {
                    var name = el.name;
                    if (files.length > 1) {
                        for (var i = 0; i < files.length; i++) {
                            data[name + '[' + i + ']'] = files[i];
                        }
                    } else if (files.length == 1) {
                        data[name] = files[0];
                    }
                }
            });

        }

        return data;
    };
    //#endregion

    //#region $.fn.bsResetForm
    $.fn.bsResetForm = function (focus, ignore, triggerChange) {

        $(this).find('input:not(.hasDatepicker, .hasRangepicker, ' + ignore + '), textarea:not(' + ignore + ')').each(function () {
            switch (this.type) {
                case 'password':
                case 'select-multiple':
                case 'select-one':
                case 'text':
                case 'textarea:':
                    if ($(this).hasClass("tag_counter")) {
                        $(this).val('0');
                    } else {
                        $(this).val('');
                        if (triggerChange === true) {
                            $(this).trigger('change');
                        }
                    }
                    break;
                case 'checkbox':
                    if ($(this).hasClass("checkBox-done")) {
                        $(this).attr("checked", $(this).data("initialvalue"));
                        $(this).trigger("change");
                    } else {
                        $(this).attr('checked', false);
                    }
                    break;
                case 'radio':
                    this.checked = false;
                    break;
                case 'range':
                    $(this).val(0);
                    break;
                case 'file':
                    $(this).val('');
                    break;
                case 'hidden':
                    if (typeof $(this).data('select2') !== 'undefined') {
                        $(this).select2('val', '');
                    }
            }
        });
        //#endregion

        //#region select2
        $(this).find('select' + ':not(' + ignore + ')').each(function () {
            var thisObj = $(this);
            if (thisObj.data('initialvalue')) {
                if (thisObj.data('select2') != null) {
                    thisObj.select2('val', thisObj.data('initialvalue'));
                } else {
                    thisObj.val(thisObj.data('initialvalue'));
                }
            }
            else {
                if (thisObj.data('select2') != null) {
                    thisObj.select2('val', '');
                } else {
                    thisObj.val('');
                }
            }

            thisObj.trigger('change');
        });
        //#endregion

        //#region radio buttons
        $(this).find(".bs-radio-list").each(function () {
            if ($(this).data("initialvalue") != undefined) {
                $(this).bsRadioButtonsListUpdateSelf($(this).data("initialvalue"));
            }
        });
        //#endregion

        //#region datePicker
        $(this).find('.hasDatepicker').each(function () {
            $(this).bsDatepicker('resetValue');
        });
        //#endregion

        //#region rangePicker
        $(this).find('.hasRangepicker').each(function () {
            $(this).bsDateRange('resetValue');
        });
        //#endregion

        //#region radioButtonsList
        $(this).find('.radioButtonsList-done').bsResetRadioButtons();
        //#endregion

        if (focus !== false)
            $(this).find("input:first").focus();

        var $form = $([]);

        if (($(this)).is('form')) {
            $form = $(this);
        } else {
            $form = $(this).find('form');
        }

        if ($form.length) {
            $form.each(function() {
                var validator = $(this).data('validator');
                if (typeof validator !== "undefined" && typeof validator.resetForm === "function") {
                    validator.resetForm();
                }
            });
        }

        return $(this);
    };
    //#endregion

});<|endoftext|>


<commit_before>/**
 * @file CascaderSelect component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import Dropdown from '../Dropdown';
import CascaderList from '../CascaderList';
import Theme from '../Theme';
import Tip from '../Tip';

import SelectMode from '../_statics/SelectMode';
import HorizontalDirection from '../_statics/HorizontalDirection';
import Position from '../_statics/Position';

import Util from '../_vendors/Util';
import Event from '../_vendors/Event';
import CascaderCalculation from '../_vendors/CascaderCalculation';
import ComponentUtil from '../_vendors/ComponentUtil';

class CascaderSelect extends Component {

    static SelectMode = SelectMode;
    static ExpandDirection = HorizontalDirection;
    static Theme = Theme;
    static Position = Position;
    static TipPosition = Position;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            value: props.value,
            popupVisible: false,
            path: props.selectMode === SelectMode.SINGLE_SELECT ?
                CascaderCalculation.calPath(props.value, props.data, props) : undefined
        };

    }

    /**
     * public
     */
    startRipple = (e, props) => {
        this.refs.dropdown && this.refs.dropdown.startRipple(e, props);
    };

    /**
     * public
     */
    endRipple = () => {
        this.refs.dropdown && this.refs.dropdown.endRipple();
    };

    /**
     * public
     */
    triggerRipple = (e, props) => {
        this.refs.dropdown && this.refs.dropdown.triggerRipple(e, props);
    };

    /**
     * public
     */
    resetPopupPosition = () => {
        this.refs.dropdown && this.refs.dropdown.resetPosition();
    };

    /**
     * public
     */
    openPopup = () => {
        this.refs.dropdown && this.refs.dropdown.openPopup();
    };

    /**
     * public
     */
    closePopup = () => {
        this.refs.dropdown && this.refs.dropdown.closePopup();
    };

    getTriggerValue = (props = this.props) => {

        const {data, selectMode, placeholder, triggerRenderer, renderer, displayField, valueField} = props,
            {value} = this.state,
            path = CascaderCalculation.calPath(value, data, props),
            isMultiSelect = selectMode === SelectMode.MULTI_SELECT;

        if (triggerRenderer) {
            return typeof triggerRenderer === 'function' ? triggerRenderer(value, path) : triggerRenderer;
        }

        if (!value) {
            return placeholder;
        }

        if (isMultiSelect) {
            return value.length > 0 ?
                value.length + ' selected'
                :
                placeholder;
        }

        let result = [];

        function addNode(node, i) {
            result.push(
                renderer ?

                        {renderer(node, path && path.slice(0, i + 1))}

                    :
                    Util.getTextByDisplayField(node, displayField, valueField)
            );
        }

        if (path) {
            for (let i = 0, len = path.length; i < len; i++) {

                i > 0 && result.push(

                );

                addNode(path[i].node, i);

            }
        }

        return result;

    };

    handlePathChange = () => {
        this.refs.dropdown.resetPopupPosition();
    };

    handleNodeSelect = (node, path) => {

        const {onNodeSelect} = this.props;
        onNodeSelect && onNodeSelect(node, path);

        if (this.props.selectMode !== SelectMode.SINGLE_SELECT) {
            return;
        }

        this.setState({
            path
        });

    };

    handleChange = value => {

        const {autoClose} = this.props;
        if (autoClose) {
            this.closePopup();
        }

        this.setState({
            value
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(value);
        });

    };

    handlePopupClosed = e => {
        this.setState({
            popupVisible: false
        }, () => {
            const {onClosePopup} = this.props;
            onClosePopup && onClosePopup(e);
        });
    };

    static getDerivedStateFromProps(props, state) {
        return {
            prevProps: props,
            value: ComponentUtil.getDerivedState(props, state, 'value')
        };
    }

    render() {

        const {

                className, triggerClassName, popupClassName, style, name, popupTheme, listWidth, data, renderer,
                selectTheme, selectMode, expandDirection, valueField, displayField, descriptionField, triggerRenderer,
                isSelectRecursive, allowCollapse, onNodeDeselect, popupChildren, onNodeClick,
                collapsedIconCls, expandedIconCls, radioUncheckedIconCls, radioCheckedIconCls,
                checkboxUncheckedIconCls, checkboxCheckedIconCls, checkboxIndeterminateIconCls,

                ...restProps

            } = this.props,
            {value, popupVisible} = this.state,

            wrapperClassName = classNames('cascader-select', {
                [className]: className
            }),

            selectTriggerClassName = classNames({
                activated: popupVisible,
                empty: !triggerRenderer && !value,
                [triggerClassName]: triggerClassName
            }),
            selectPopupClassName = classNames('cascader-select-popup', {
                [popupClassName]: popupClassName
            });

        return (

                {
                    name ?
                        <input type="hidden"
                               name={name}
                               value={Util.getValueByValueField(value, valueField, displayField)}/>
                        :
                        null
                }

                <Dropdown {...restProps}
                          ref="dropdown"
                          triggerClassName={selectTriggerClassName}
                          popupClassName={selectPopupClassName}
                          popupTheme={popupTheme}
                          autoPopupWidth={false}
                          triggerValue={this.getTriggerValue()}
                          onClosePopup={this.handlePopupClosed}>

 Event.wheelHandler(e, this.props)}>

                        <CascaderList className="cascader-select-list"
                                      theme={popupTheme}
                                      selectTheme={selectTheme}
                                      expandDirection={expandDirection}
                                      listWidth={listWidth}
                                      selectMode={selectMode}
                                      data={data}
                                      value={value}
                                      valueField={valueField}
                                      displayField={displayField}
                                      descriptionField={descriptionField}
                                      isSelectRecursive={isSelectRecursive}
                                      allowCollapse={allowCollapse}
                                      collapsedIconCls={collapsedIconCls}
                                      expandedIconCls={expandedIconCls}
                                      radioUncheckedIconCls={radioUncheckedIconCls}
                                      radioCheckedIconCls={radioCheckedIconCls}
                                      checkboxUncheckedIconCls={checkboxUncheckedIconCls}
                                      checkboxCheckedIconCls={checkboxCheckedIconCls}
                                      checkboxIndeterminateIconCls={checkboxIndeterminateIconCls}
                                      renderer={renderer}
                                      onNodeClick={onNodeClick}
                                      onNodeSelect={this.handleNodeSelect}
                                      onNodeDeselect={onNodeDeselect}
                                      onChange={this.handleChange}
                                      onPathChange={this.handlePathChange}/>

                    {popupChildren}

                </Dropdown>

        );

    }
}

CascaderSelect.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * The CSS class name of the trigger element.
     */
    triggerClassName: PropTypes.string,

    /**
     * The class name of the popup element.
     */
    popupClassName: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * Override the styles of the popup element.
     */
    popupStyle: PropTypes.object,

    /**
     * The theme.
     */
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The theme.
     */
    popupTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    position: PropTypes.oneOf(Util.enumerateValue(Position)),

    listWidth: PropTypes.number,

    /**
     * The theme of the tree node select radio or checkbox.
     */
    selectTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The mode of tree node.
     */
    selectMode: PropTypes.oneOf(Util.enumerateValue(SelectMode)),

    /**
     * The direction of expansion.
     */
    expandDirection: PropTypes.oneOf(Util.enumerateValue(HorizontalDirection)),

    /**
     * The name of the dropDownSelect.
     */
    name: PropTypes.string,

    /**
     * The value of the dropDownSelect.
     */
    value: PropTypes.any,

    /**
     * The placeholder of the dropDownSelect.
     */
    placeholder: PropTypes.string,

    title: PropTypes.string,
    tip: PropTypes.string,
    tipPosition: PropTypes.oneOf(Util.enumerateValue(Position)),
    triggerValue: PropTypes.string,
    rightIconCls: PropTypes.string,

    /**
     * The options data.
     */
    data: PropTypes.arrayOf(PropTypes.shape({

        /**
         * The CSS class name of the tree node.
         */
        className: PropTypes.string,

        /**
         * Override the styles of the tree node.
         */
        style: PropTypes.object,

        /**
         * The theme of the tree button.
         */
        theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

        /**
         * The text value of the tree button.Type can be string or number.
         */
        value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

        /**
         * The tree node's display text. Type can be string, number or bool.
         */
        text: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

        /**
         * The desc value of the tree node. Type can be string or number.
         */
        desc: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

        /**
         * If true,the tree node will be disabled.
         */
        disabled: PropTypes.bool,

        /**
         * If true,the button will be have loading effect.
         */
        isLoading: PropTypes.bool,

        /**
         * Use this property to display an icon. It will display on the left.
         */
        iconCls: PropTypes.string,

        /**
         * Use this property to display an icon. It will display on the right.
         */
        rightIconCls: PropTypes.string,

        /**
         * The message of tip.
         */
        tip: PropTypes.string,

        /**
         * The position of tip.
         */
        tipPosition: PropTypes.oneOf(Util.enumerateValue(Tip.Position)),

        children: PropTypes.array,

        /**
         * You can create a complicated renderer callback instead of value and desc prop.
         */
        itemRenderer: PropTypes.func,

        /**
         * Callback function fired when a tree node touch-tapped.
         */
        onClick: PropTypes.func

    })),

    /**
     * The invalid message of dropDownSelect.
     */
    invalidMsg: PropTypes.string,

    /**
     * If true,the dropDownSelect will be disabled.
     */
    disabled: PropTypes.bool,

    /**
     * If true, the tree will be at loading status.
     */
    isLoading: PropTypes.bool,

    /**
     * The value field name in data. (default: "value")
     */
    valueField: PropTypes.string,

    /**
     * The display field name in data. (default: "text")
     */
    displayField: PropTypes.string,

    /**
     * The description field name in data. (default: "desc")
     */
    descriptionField: PropTypes.string,

    /**
     * The message of the dropDownSelect.
     */
    infoMsg: PropTypes.string,

    /**
     * Use this function to format the options's text.
     */
    textFormat: PropTypes.func,

    /**
     * If true,the drop-down box automatically closed after selection.
     */
    autoClose: PropTypes.bool,

    shouldPreventContainerScroll: PropTypes.bool,
    isSelectRecursive: PropTypes.bool,
    allowCollapse: PropTypes.bool,
    collapsedIconCls: PropTypes.string,
    expandedIconCls: PropTypes.string,
    radioUncheckedIconCls: PropTypes.string,
    radioCheckedIconCls: PropTypes.string,
    checkboxUncheckedIconCls: PropTypes.string,
    checkboxCheckedIconCls: PropTypes.string,
    checkboxIndeterminateIconCls: PropTypes.string,

    popupChildren: PropTypes.any,

    resetPopPositionWait: PropTypes.number,

    renderer: PropTypes.func,

    triggerRenderer: PropTypes.oneOfType([PropTypes.number, PropTypes.string, PropTypes.func]),

    /**
     * Callback function fired when the button is touch-tapped.
     */
    onNodeClick: PropTypes.func,

    /**
     * Callback function fired when the tree node selected.
     */
    onNodeSelect: PropTypes.func,

    /**
     * Callback function fired when the tree node deselected.
     */
    onNodeDeselect: PropTypes.func,

    /**
     * Callback function fired when the popup is closed.
     */
    onClosePopup: PropTypes.func,

    /**
     * Callback function fired when a menu item is selected.
     */
    onChange: PropTypes.func,

    onWheel: PropTypes.func,
    onFocus: PropTypes.func,
    onBlur: PropTypes.func,
    onMouseOver: PropTypes.func,
    onMouseOut: PropTypes.func

};

CascaderSelect.defaultProps = {

    theme: Theme.DEFAULT,
    popupTheme: Theme.DEFAULT,
    listWidth: 200,

    selectTheme: Theme.DEFAULT,
    selectMode: SelectMode.SINGLE_SELECT,
    expandDirection: HorizontalDirection.RIGHT,

    placeholder: 'Please select ...',
    rightIconCls: 'fas fa-angle-down',
    disabled: false,
    isLoading: false,
    tipPosition: Position.BOTTOM,

    valueField: 'value',
    displayField: 'text',
    descriptionField: 'desc',

    autoClose: false,

    shouldPreventContainerScroll: true,
    isSelectRecursive: true,
    allowCollapse: true,

    resetPopPositionWait: 250

};

export default CascaderSelect;
<commit_msg>update CascaderSelect<commit_after>/**
 * @file CascaderSelect component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import Dropdown from '../Dropdown';
import CascaderList from '../CascaderList';
import Theme from '../Theme';
import Tip from '../Tip';

import SelectMode from '../_statics/SelectMode';
import HorizontalDirection from '../_statics/HorizontalDirection';
import Position from '../_statics/Position';

import Util from '../_vendors/Util';
import Event from '../_vendors/Event';
import CascaderCalculation from '../_vendors/CascaderCalculation';
import ComponentUtil from '../_vendors/ComponentUtil';

class CascaderSelect extends Component {

    static SelectMode = SelectMode;
    static ExpandDirection = HorizontalDirection;
    static Theme = Theme;
    static Position = Position;
    static TipPosition = Position;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            value: props.value,
            popupVisible: false,
            path: props.selectMode === SelectMode.SINGLE_SELECT ?
                CascaderCalculation.calPath(props.value, props.data, props) : undefined
        };

    }

    /**
     * public
     */
    startRipple = (e, props) => {
        this.refs.dropdown && this.refs.dropdown.startRipple(e, props);
    };

    /**
     * public
     */
    endRipple = () => {
        this.refs.dropdown && this.refs.dropdown.endRipple();
    };

    /**
     * public
     */
    triggerRipple = (e, props) => {
        this.refs.dropdown && this.refs.dropdown.triggerRipple(e, props);
    };

    /**
     * public
     */
    resetPopupPosition = () => {
        this.refs.dropdown && this.refs.dropdown.resetPosition();
    };

    /**
     * public
     */
    openPopup = () => {
        this.refs.dropdown && this.refs.dropdown.openPopup();
    };

    /**
     * public
     */
    closePopup = () => {
        this.refs.dropdown && this.refs.dropdown.closePopup();
    };

    getTriggerValue = (props = this.props) => {

        const {data, selectMode, placeholder, triggerRenderer, renderer, displayField, valueField} = props,
            {value} = this.state,
            path = CascaderCalculation.calPath(value, data, props),
            isMultiSelect = selectMode === SelectMode.MULTI_SELECT;

        if (triggerRenderer) {
            return typeof triggerRenderer === 'function' ? triggerRenderer(value, path) : triggerRenderer;
        }

        if (!value) {
            return placeholder;
        }

        if (isMultiSelect) {
            return value.length > 0 ?
                value.length + ' selected'
                :
                placeholder;
        }

        let result = [];

        function addNode(node, i) {
            result.push(
                renderer ?

                        {renderer(node, path && path.slice(0, i + 1))}

                    :
                    Util.getTextByDisplayField(node, displayField, valueField)
            );
        }

        if (path) {
            for (let i = 0, len = path.length; i < len; i++) {

                i > 0 && result.push(

                );

                addNode(path[i].node, i);

            }
        }

        return result;

    };

    handlePathChange = () => {
        this.refs.dropdown.resetPopupPosition();
    };

    handleNodeSelect = (node, path) => {

        const {onNodeSelect} = this.props;
        onNodeSelect && onNodeSelect(node, path);

        if (this.props.selectMode !== SelectMode.SINGLE_SELECT) {
            return;
        }

        this.setState({
            path
        });

    };

    handleChange = value => {

        const {autoClose} = this.props;
        if (autoClose) {
            this.closePopup();
        }

        this.setState({
            value
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(value);
        });

    };

    handlePopupClosed = e => {
        this.setState({
            popupVisible: false
        }, () => {
            const {onClosePopup} = this.props;
            onClosePopup && onClosePopup(e);
        });
    };

    static getDerivedStateFromProps(props, state) {
        return {
            prevProps: props,
            value: ComponentUtil.getDerivedState(props, state, 'value')
        };
    }

    render() {

        const {

                className, triggerClassName, popupClassName, style, name, popupTheme, listWidth, data, renderer,
                selectTheme, selectMode, expandDirection, valueField, displayField, descriptionField, triggerRenderer,
                isSelectRecursive, allowCollapse, onNodeDeselect, popupChildren, onNodeClick,
                collapsedIconCls, expandedIconCls, radioUncheckedIconCls, radioCheckedIconCls,
                checkboxUncheckedIconCls, checkboxCheckedIconCls, checkboxIndeterminateIconCls,

                ...restProps

            } = this.props,
            {value, popupVisible} = this.state,

            wrapperClassName = classNames('cascader-select', {
                [className]: className
            }),

            selectTriggerClassName = classNames({
                activated: popupVisible,
                empty: !triggerRenderer && !value,
                [triggerClassName]: triggerClassName
            }),
            selectPopupClassName = classNames('cascader-select-popup', {
                [popupClassName]: popupClassName
            });

        return (

                {
                    name ?
                        <input type="hidden"
                               name={name}
                               value={Util.getValueByValueField(value, valueField, displayField)}/>
                        :
                        null
                }

                <Dropdown {...restProps}
                          ref="dropdown"
                          triggerClassName={selectTriggerClassName}
                          popupClassName={selectPopupClassName}
                          popupTheme={popupTheme}
                          autoPopupWidth={false}
                          triggerValue={this.getTriggerValue()}
                          onClosePopup={this.handlePopupClosed}>

 Event.wheelHandler(e, this.props)}>

                        <CascaderList className="cascader-select-list"
                                      theme={popupTheme}
                                      selectTheme={selectTheme}
                                      expandDirection={expandDirection}
                                      listWidth={listWidth}
                                      selectMode={selectMode}
                                      data={data}
                                      value={value}
                                      valueField={valueField}
                                      displayField={displayField}
                                      descriptionField={descriptionField}
                                      isSelectRecursive={isSelectRecursive}
                                      allowCollapse={allowCollapse}
                                      collapsedIconCls={collapsedIconCls}
                                      expandedIconCls={expandedIconCls}
                                      radioUncheckedIconCls={radioUncheckedIconCls}
                                      radioCheckedIconCls={radioCheckedIconCls}
                                      checkboxUncheckedIconCls={checkboxUncheckedIconCls}
                                      checkboxCheckedIconCls={checkboxCheckedIconCls}
                                      checkboxIndeterminateIconCls={checkboxIndeterminateIconCls}
                                      renderer={renderer}
                                      onNodeClick={onNodeClick}
                                      onNodeSelect={this.handleNodeSelect}
                                      onNodeDeselect={onNodeDeselect}
                                      onChange={this.handleChange}
                                      onPathChange={this.handlePathChange}/>

                    {popupChildren}

                </Dropdown>

        );

    }
}

CascaderSelect.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * The CSS class name of the trigger element.
     */
    triggerClassName: PropTypes.string,

    /**
     * The class name of the popup element.
     */
    popupClassName: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * Override the styles of the popup element.
     */
    popupStyle: PropTypes.object,

    /**
     * The theme.
     */
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The theme.
     */
    popupTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    position: PropTypes.oneOf(Util.enumerateValue(Position)),

    listWidth: PropTypes.number,

    /**
     * The theme of the tree node select radio or checkbox.
     */
    selectTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The mode of tree node.
     */
    selectMode: PropTypes.oneOf(Util.enumerateValue(SelectMode)),

    /**
     * The direction of expansion.
     */
    expandDirection: PropTypes.oneOf(Util.enumerateValue(HorizontalDirection)),

    /**
     * The name of the dropDownSelect.
     */
    name: PropTypes.string,

    /**
     * The value of the dropDownSelect.
     */
    value: PropTypes.any,

    /**
     * The placeholder of the dropDownSelect.
     */
    placeholder: PropTypes.string,

    title: PropTypes.string,
    tip: PropTypes.string,
    tipPosition: PropTypes.oneOf(Util.enumerateValue(Position)),
    triggerValue: PropTypes.string,
    rightIconCls: PropTypes.string,

    /**
     * The options data.
     */
    data: PropTypes.arrayOf(PropTypes.shape({

        /**
         * The CSS class name of the tree node.
         */
        className: PropTypes.string,

        /**
         * Override the styles of the tree node.
         */
        style: PropTypes.object,

        /**
         * The theme of the tree button.
         */
        theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

        /**
         * The text value of the tree button.Type can be string or number.
         */
        value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

        /**
         * The tree node's display text. Type can be string, number or bool.
         */
        text: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

        /**
         * The desc value of the tree node. Type can be string or number.
         */
        desc: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

        /**
         * If true,the tree node will be disabled.
         */
        disabled: PropTypes.bool,

        /**
         * If true,the button will be have loading effect.
         */
        isLoading: PropTypes.bool,

        /**
         * Use this property to display an icon. It will display on the left.
         */
        iconCls: PropTypes.string,

        /**
         * Use this property to display an icon. It will display on the right.
         */
        rightIconCls: PropTypes.string,

        /**
         * The message of tip.
         */
        tip: PropTypes.string,

        /**
         * The position of tip.
         */
        tipPosition: PropTypes.oneOf(Util.enumerateValue(Tip.Position)),

        children: PropTypes.array,

        /**
         * You can create a complicated renderer callback instead of value and desc prop.
         */
        itemRenderer: PropTypes.func,

        /**
         * Callback function fired when a tree node touch-tapped.
         */
        onClick: PropTypes.func

    })),

    /**
     * The invalid message of dropDownSelect.
     */
    invalidMsg: PropTypes.string,

    /**
     * If true,the dropDownSelect will be disabled.
     */
    disabled: PropTypes.bool,

    /**
     * If true, the tree will be at loading status.
     */
    isLoading: PropTypes.bool,

    /**
     * The value field name in data. (default: "value")
     */
    valueField: PropTypes.string,

    /**
     * The display field name in data. (default: "text")
     */
    displayField: PropTypes.string,

    /**
     * The description field name in data. (default: "desc")
     */
    descriptionField: PropTypes.string,

    /**
     * The message of the dropDownSelect.
     */
    infoMsg: PropTypes.string,

    /**
     * Use this function to format the options's text.
     */
    textFormat: PropTypes.func,

    /**
     * If true,the drop-down box automatically closed after selection.
     */
    autoClose: PropTypes.bool,

    isSelectRecursive: PropTypes.bool,
    allowCollapse: PropTypes.bool,
    collapsedIconCls: PropTypes.string,
    expandedIconCls: PropTypes.string,
    radioUncheckedIconCls: PropTypes.string,
    radioCheckedIconCls: PropTypes.string,
    checkboxUncheckedIconCls: PropTypes.string,
    checkboxCheckedIconCls: PropTypes.string,
    checkboxIndeterminateIconCls: PropTypes.string,

    popupChildren: PropTypes.any,

    resetPopPositionWait: PropTypes.number,

    renderer: PropTypes.func,

    triggerRenderer: PropTypes.oneOfType([PropTypes.number, PropTypes.string, PropTypes.func]),

    /**
     * Callback function fired when the button is touch-tapped.
     */
    onNodeClick: PropTypes.func,

    /**
     * Callback function fired when the tree node selected.
     */
    onNodeSelect: PropTypes.func,

    /**
     * Callback function fired when the tree node deselected.
     */
    onNodeDeselect: PropTypes.func,

    /**
     * Callback function fired when the popup is closed.
     */
    onClosePopup: PropTypes.func,

    /**
     * Callback function fired when a menu item is selected.
     */
    onChange: PropTypes.func,

    onWheel: PropTypes.func,
    onFocus: PropTypes.func,
    onBlur: PropTypes.func,
    onMouseOver: PropTypes.func,
    onMouseOut: PropTypes.func

};

CascaderSelect.defaultProps = {

    theme: Theme.DEFAULT,
    popupTheme: Theme.DEFAULT,
    listWidth: 200,

    selectTheme: Theme.DEFAULT,
    selectMode: SelectMode.SINGLE_SELECT,
    expandDirection: HorizontalDirection.RIGHT,

    placeholder: 'Please select ...',
    rightIconCls: 'fas fa-angle-down',
    disabled: false,
    isLoading: false,
    tipPosition: Position.BOTTOM,

    valueField: 'value',
    displayField: 'text',
    descriptionField: 'desc',

    autoClose: false,

    isSelectRecursive: true,
    allowCollapse: true,

    resetPopPositionWait: 250

};

export default CascaderSelect;
<|endoftext|>


<commit_before>

if (Meteor.isClient) {
  // counter starts at 0
  Session.setDefault('counter', 0);

  Template.hello.helpers({
    counter: function () {
      return Session.get('counter');
    }
  });

  Template.hello.events({
    'click button': function () {
      // increment the counter when button is clicked
      Session.set('counter', Session.get('counter') + 1);
    }
  });

  Accounts.ui.config({
    passwordSignupFields: 'USERNAME_AND_EMAIL'
  });

}

if (Meteor.isServer) {
  Meteor.startup(function () {
    schools.insert(schoolsData.data);
    nonCompliance.insert(nonComplianceData.data);
  });
}
<commit_msg>removed hello template helpers<commit_after>

if (Meteor.isClient) {
  // counter starts at 0
  Session.setDefault('counter', 0);

  Accounts.ui.config({
    passwordSignupFields: 'USERNAME_AND_EMAIL'
  });

}

if (Meteor.isServer) {
  Meteor.startup(function () {
    schools.insert(schoolsData.data);
    nonCompliance.insert(nonComplianceData.data);
  });
}
<|endoftext|>


<commit_before>/*
 * blueimp Gallery Vimeo Video Factory JS 1.2.0
 * https://github.com/blueimp/Gallery
 *
 * Copyright 2013, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * http://www.opensource.org/licenses/MIT
 */

/* global define, window, document, location, $f */

(function (factory) {
    'use strict';
    if (typeof define === 'function' && define.amd) {
        // Register as an anonymous AMD module:
        define([
            './blueimp-helper',
            './blueimp-gallery-video'
        ], factory);
    } else {
        // Browser globals:
        factory(
            window.blueimp.helper || window.jQuery,
            window.blueimp.Gallery
        );
    }
}(function ($, Gallery) {
    'use strict';

    if (!window.postMessage) {
        return Gallery;
    }

    $.extend(Gallery.prototype.options, {
        // The list object property (or data attribute) with the Vimeo video id:
        vimeoVideoIdProperty: 'vimeo',
        // The URL for the Vimeo video player, can be extended with custom parameters:
        // https://developer.vimeo.com/player/embedding
        vimeoPlayerUrl: '//player.vimeo.com/video/VIDEO_ID?api=1&player_id=PLAYER_ID',
        // The prefix for the Vimeo video player ID:
        vimeoPlayerIdPrefix: 'vimeo-player-',
        // Require a click on the native Vimeo player for the initial playback:
        vimeoClickToPlay: true
    });

    var textFactory = Gallery.prototype.textFactory || Gallery.prototype.imageFactory,
        VimeoPlayer = function (url, videoId, playerId, clickToPlay) {
            this.url = url;
            this.videoId = videoId;
            this.playerId = playerId;
            this.clickToPlay = clickToPlay;
            this.element = document.createElement('div');
            this.listeners = {};
        },
        counter = 0;

    $.extend(VimeoPlayer.prototype, {

        canPlayType: function () {
            return true;
        },

        on: function (type, func) {
            this.listeners[type] = func;
            return this;
        },

        loadAPI: function () {
            var that = this,
                apiUrl = '//' + (location.protocol === 'https' ? 'secure-' : '') +
                    'a.vimeocdn.com/js/froogaloop2.min.js',
                scriptTags = document.getElementsByTagName('script'),
                i = scriptTags.length,
                scriptTag,
                called,
                callback = function () {
                    if (!called && that.playOnReady) {
                        that.play();
                    }
                    called = true;
                };
            while (i) {
                i -= 1;
                if (scriptTags[i].src === apiUrl) {
                    scriptTag = scriptTags[i];
                    break;
                }
            }
            if (!scriptTag) {
                scriptTag = document.createElement('script');
                scriptTag.src = apiUrl;
            }
            $(scriptTag).on('load', callback);
            scriptTags[0].parentNode.insertBefore(scriptTag, scriptTags[0]);
            // Fix for cached scripts on IE 8:
            if (/loaded|complete/.test(scriptTag.readyState)) {
                callback();
            }
        },

        onReady: function () {
            var that = this;
            this.ready = true;
            this.player.addEvent('play', function () {
                that.hasPlayed = true;
                that.onPlaying();
            });
            this.player.addEvent('pause', function () {
                that.onPause();
            });
            this.player.addEvent('finish', function () {
                that.onPause();
            });
            if (this.playOnReady) {
                this.play();
            }
        },

        onPlaying: function () {
            if (this.playStatus < 2) {
                this.listeners.playing();
                this.playStatus = 2;
            }
        },

        onPause: function () {
            this.listeners.pause();
            delete this.playStatus;
        },

        insertIframe: function () {
            var iframe = document.createElement('iframe');
            iframe.src = this.url
                .replace('VIDEO_ID', this.videoId)
                .replace('PLAYER_ID', this.playerId);
            iframe.id = this.playerId;
            this.element.parentNode.replaceChild(iframe, this.element);
            this.element = iframe;
        },

        play: function () {
            var that = this;
            if (!this.playStatus) {
                this.listeners.play();
                this.playStatus = 1;
            }
            if (this.ready) {
                if (!this.hasPlayed && (this.clickToPlay || (window.navigator &&
                        /iP(hone|od|ad)/.test(window.navigator.platform)))) {
                    // Manually trigger the playing callback if clickToPlay
                    // is enabled and to workaround a limitation in iOS,
                    // which requires synchronous user interaction to start
                    // the video playback:
                    this.onPlaying();
                } else {
                    this.player.api('play');
                }
            } else {
                this.playOnReady = true;
                if (!window.$f) {
                    this.loadAPI();
                } else if (!this.player) {
                    this.insertIframe();
                    this.player = $f(this.element);
                    this.player.addEvent('ready', function () {
                        that.onReady();
                    });
                }
            }
        },

        pause: function () {
            if (this.ready) {
                this.player.api('pause');
            } else if (this.playStatus) {
                delete this.playOnReady;
                this.listeners.pause();
                delete this.playStatus;
            }
        }

    });

    $.extend(Gallery.prototype, {

        VimeoPlayer: VimeoPlayer,

        textFactory: function (obj, callback) {
            var options = this.options,
                videoId = this.getItemProperty(obj, options.vimeoVideoIdProperty);
            if (videoId) {
                if (this.getItemProperty(obj, options.urlProperty) === undefined) {
                    obj[options.urlProperty] = '//vimeo.com/' + videoId;
                }
                counter += 1;
                return this.videoFactory(
                    obj,
                    callback,
                    new VimeoPlayer(
                        options.vimeoPlayerUrl,
                        videoId,
                        options.vimeoPlayerIdPrefix + counter,
                        options.vimeoClickToPlay
                    )
                );
            }
            return textFactory.call(this, obj, callback);
        }

    });

    return Gallery;
}));
<commit_msg>Delete blueimp-gallery-vimeo.js<commit_after><|endoftext|>


<commit_before>var button;
var input;
var radius;
var label;
var names;
var isGenerated = false;
var spinnerAngle;
var spinnerSpeed = 0;
var selected = "";
var hasSpun = false;

function setup() {
  createCanvas(windowWidth, windowHeight);
  radius = height/2 - 20;

  label = createP('Enter Names:');
  label.position(25, 25);

  input = createInput('');
  input.position(25, 60);

  button = createButton('Generate');
  button.position(25, 85);
  button.mousePressed(generate);

  spinnerAngle = 0;

  noStroke();
  generate();
}

function draw() {
  if(isGenerated) {
    background(255);
    drawCircle();
    drawSpinner();
    fill(0);
  }

  if(mouseIsPressed && mouseX > width - 20) {
    spinnerAngle++;
  }
}

function generate() {
  var n = input.value().split(", ");
  if(n.length > 2) {
    if(n.length%2 == 1) {
      n.push("Spin Again");
    }
    names = n;
    isGenerated = true;
    drawCircle();
    drawSpinner();
  }
}

function drawCircle() {
  var num = names.length;
  ellipse(width/2, height/2, radius*2, radius*2);
  //Draw Segments
  for(var i = 0; i < num; i++) {
    if(i%2 == 0)
      fill(200, 0, 0);
    else
      fill(0, 0, 0);
    var rad1 = radians(360*i/num);
    var rad2 = radians(360*(i+1)/num);
    arc(width/2, height/2, radius*2, radius*2, rad1, rad2);
  }

  //Write Names
  textAlign(CENTER);
  textSize(32);
  for(var i = 0; i < num; i++) {
    var rad3 = radians((360*(i+1)/num + 360*i/num)/2 + 3);
    push();
    var transX = width/2 + (radius*2/3) * cos(rad3);
    var transY = height/2 + (radius*2/3) * sin(rad3);
    translate(transX, transY);
    rotate(rad3);
    if(i == selected && hasSpun)
      fill(0, 255, 0);
    else
      fill(255);
    text(names[i], 0,  0);
    pop();
  }
}

function drawSpinner() {
  push();
  fill(255, 255, 0);
  translate(width/2, height/2);
  rotate(radians(spinnerAngle));
  beginShape();
  vertex(-25, -15);
  vertex(-25, 15);
  vertex(200, 0);
  vertex(200, 0);
  endShape(CLOSE);
  fill(100);
  ellipse(0, 0, radius/25, radius/25);
  pop();
  var distance = sqrt(sq(mouseX - width/2) + sq(mouseY - height/2))
  if(mouseIsPressed && distance < radius) {
    if(isGenerated) {
      if(spinnerSpeed == 0) {
        hasSpun = true;
        selected = -1;
        spinnerSpeed = 20;
      }
    }
  } else {
    if(spinnerSpeed > 0)
      spinnerSpeed -= .1;
    else
      spinnerSpeed = 0;
  }
  spinnerAngle += spinnerSpeed;
  spinnerAngle = spinnerAngle%360
  if(spinnerSpeed == 0) {
    var num = names.length;
    var segmentSize = 360/num;
    var angle = spinnerAngle%360;
    var index = floor(angle/segmentSize);
    selected = index;
  }
}

function keyPressed() {
  if(key == ' ' && selected != -1 && names.length > 2) {
    names.splice(selected, 1);

    if(names[names.length-1] == "Spin Again") {
      names.splice(names.length-1, 1);
    } else {
      names.push("Spin Again");
    }

    selected = -1;
    hasSpun = false;
  }
}
<commit_msg>fix comma separation<commit_after>var button;
var input;
var radius;
var label;
var names;
var isGenerated = false;
var spinnerAngle;
var spinnerSpeed = 0;
var selected = "";
var hasSpun = false;

function setup() {
  createCanvas(windowWidth, windowHeight);
  radius = height/2 - 20;

  label = createP('Enter Names:');
  label.position(25, 25);

  input = createInput('');
  input.position(25, 60);

  button = createButton('Generate');
  button.position(25, 85);
  button.mousePressed(generate);

  spinnerAngle = 0;

  noStroke();
  generate();
}

function draw() {
  if(isGenerated) {
    background(255);
    drawCircle();
    drawSpinner();
    fill(0);
  }
}

function generate() {
  var n = input.value().split(",");
  for(var i = 0; i < n.length; i++) {
    if(n[i].charAt(0) == ' ')
      n[i] = n[i].slice(1);
    if(n[i].charAt(n[i].length-1) == ' ')
      n[i] = n[i].slice(0, -1);
  }
  if(n.length > 2) {
    if(n.length%2 == 1) {
      n.push("Spin Again");
    }
    names = n;
    isGenerated = true;
    drawCircle();
    drawSpinner();
  }
}

function drawCircle() {
  var num = names.length;
  ellipse(width/2, height/2, radius*2, radius*2);
  //Draw Segments
  for(var i = 0; i < num; i++) {
    if(i%2 == 0)
      fill(200, 0, 0);
    else
      fill(0, 0, 0);
    var rad1 = radians(360*i/num);
    var rad2 = radians(360*(i+1)/num);
    arc(width/2, height/2, radius*2, radius*2, rad1, rad2);
  }

  //Write Names
  textAlign(CENTER);
  textSize(32);
  for(var i = 0; i < num; i++) {
    var rad3 = radians((360*(i+1)/num + 360*i/num)/2 + 3);
    push();
    var transX = width/2 + (radius*2/3) * cos(rad3);
    var transY = height/2 + (radius*2/3) * sin(rad3);
    translate(transX, transY);
    rotate(rad3);
    if(i == selected && hasSpun)
      fill(0, 255, 0);
    else
      fill(255);
    text(names[i], 0,  0);
    pop();
  }
}

function drawSpinner() {
  push();
  fill(255, 255, 0);
  translate(width/2, height/2);
  rotate(radians(spinnerAngle));
  beginShape();
  vertex(-25, -15);
  vertex(-25, 15);
  vertex(200, 0);
  vertex(200, 0);
  endShape(CLOSE);
  fill(100);
  ellipse(0, 0, radius/25, radius/25);
  pop();
  var distance = sqrt(sq(mouseX - width/2) + sq(mouseY - height/2))
  if(mouseIsPressed && distance < radius) {
    if(isGenerated) {
      if(spinnerSpeed == 0) {
        hasSpun = true;
        selected = -1;
        spinnerSpeed = 20;
      }
    }
  } else {
    if(spinnerSpeed > 0)
      spinnerSpeed -= .1;
    else
      spinnerSpeed = 0;
  }
  spinnerAngle += spinnerSpeed;
  spinnerAngle = spinnerAngle%360
  if(spinnerSpeed == 0) {
    var num = names.length;
    var segmentSize = 360/num;
    var angle = spinnerAngle%360;
    var index = floor(angle/segmentSize);
    selected = index;
  }
}

function keyPressed() {
  if(key == ' ' && selected != -1 && names.length > 2) {
    names.splice(selected, 1);

    if(names[names.length-1] == "Spin Again") {
      names.splice(names.length-1, 1);
    } else {
      names.push("Spin Again");
    }

    selected = -1;
    hasSpun = false;
  }
}
<|endoftext|>