<commit_before>angular.module('app').factory('vendorService', ['$http', 'userService', function($http, User) {

    // dummy data
    var itemList = [{
        id: 1,
        name: 'BearCom Operating LLC',
        contactPerson: {
            name: 'Jenn Delong',
            email: 'jdelong@marlinfinance.com',
            phone: '888-479-9111 Ext. 5015'
        },
        logo: {
            original: 'https://www.filepicker.io/api/file/2v44yfa8TqKt2RAAkpZB'
        },
        website: 'http://www.bearcom.com/',
        legalTerms: 'These are legal terms for vendor 1',
        businessPhone: '',
        businessAddress: {
            address1: '2424 E York St',
            address2: '',
            city: 'Philadelphia',
            state: 'PA',
            zip: '19125'
        },
        geo: {
            lat: 40.42,
            lng: -73.45
        },
        programIds: [1,3,4],
        //salesRep: User.getOneWhereIn('vendorIds', 1),
        programs: [{
            id: 1,
            displayName: 'A Custom Display Name for Program 1'
        }]
    },
    {
        id: 2,
        name: 'Protection One - West',
        contactPerson: {
            name: 'Jennifer DeLong-Giefer',
            email: 'jdelong@marlinfinance.com',
            phone: '866-687-3778'
        },
        logo: {
            original: 'https://www.filepicker.io/api/file/ZxTd0qV0QA2ek9HHSmu3'
        },
        website: 'http://www.protection1.com/',
        legalTerms: 'These are legal terms for vendor 2',
        businessPhone: '',
        businessAddress: {
            address1: '',
            address2: '',
            city: '',
            state: '',
            zip: ''
        },
        geo: {
            lat: 45,
            lng: -73
        },
        programIds: [1,3,4],
        //salesRep: User.getOneWhereIn('vendorIds', 1),
        programs: [{
            id: 1,
            displayName: 'A Custom Display Name for Program 1'
        }]
    },
    {
        id: 3,
        name: 'Gametime',
        contactPerson: {
            name: 'Warren Myers',
            email: 'wmyers@playcore.com',
            phone: ''
        },
        logo: {
            original: 'https://www.filepicker.io/api/file/EcryKofERBynAuL2txez'
        },
        website: 'http://www.gametime.com',
        legalTerms: 'These are legal terms for vendor 3',
        businessPhone: '',
        businessAddress: {
            address1: '',
            address2: '',
            city: '',
            state: '',
            zip: ''
        },
        geo: {
            lat: 39.57,
            lng: -75.10
        },
        programIds: [1,3,4],
        //salesRep: User.getOneWhereIn('vendorIds', 1),
        programs: [{
            id: 1,
            displayName: 'A Custom Display Name for Program 1'
        }]
    },
    {
        id: 4,
        name: 'Union Bank',
        contactPerson: {
            name: 'Union Bank Financing Consultant',
            email: 'ubapps@marlinfinance.com',
            phone: '877-307-6756'
        },
        logo: {
            original: 'https://www.filepicker.io/api/file/yjlOg28UROmPXyK8uadA'
        },
        website: 'https://www.unionbank.com/',
        legalTerms: 'These are legal terms for vendor 3',
        businessPhone: '',
        businessAddress: {
            address1: '',
            address2: '',
            city: '',
            state: '',
            zip: ''
        },
        geo: {
            lat: 35.11,
            lng: -101.50
        },
        programIds: [1,3,4],
        //salesRep: User.getOneWhereIn('vendorIds', 1),
        programs: [{
            id: 1,
            displayName: 'A Custom Display Name for Program 1'
        }]
    },
    {
        id: 5,
        name: 'Southeastern Equipment & Supply, Inc.',
        contactPerson: {
            name: 'Grady Martin',
            email: 'leasing@southeasternequipment.net',
            phone: '803-454-3656'
        },
        logo: {
            original: 'https://www.filepicker.io/api/file/nvC3zhiTQoSl01P7K3Br'
        },
        website: 'https://www.unionbank.com/',
        legalTerms: 'These are legal terms for vendor 3',
        businessPhone: '',
        businessAddress: {
            address1: '1919 Old Dunbar Road',
            address2: '',
            city: 'West Columbia',
            state: 'SC',
            zip: '29045'
        },
        programIds: [1,3,4],
        //salesRep: User.getOneWhereIn('vendorIds', 1),
        programs: [{
            id: 1,
            displayName: 'A Custom Display Name for Program 1'
        }]
    }];

    //initLocalStore();

    // create and expose service methods
    var exports = {};

    // get all items
    exports.getAll = function() {
        return itemList;
    };

    // get one item by id
    exports.getById = function(id) {
        var theItem = _.find(itemList, function(item) {
            return item.id == id;
        });
        return theItem ? theItem : false;
    };

    // update one item by id
    // @todo check for updating the id!
    exports.updateById = function(id, newData) {
        var theId = _.findIndex(itemList, function(item) {
            return item.id == id;
        });
        theList = _.extend(itemList[theId], newData);
        return theList;
    };

    // update one item by item
    // @note we figure out id from item
    exports.update = function(newItem) {
        var theIndex = _.findIndex(itemList, function(item) {
            return item.id == newItem.id;
        });
        theList = _.extend(itemList[theIndex], newItem);
        return theList;
    };

    // add a new item
    exports.add = function(item) {
        item.id = itemList.length + 1;
        itemList.push(item);
        return item;
    };

    // remove item by item
    exports.remove = function(item) {
        itemList.splice(itemList.indexOf(item), 1);
        return item;
    };

    /**
    * Add a vendor to a program using the id of each
    * @param int vendorId ID of the vendor
    * @param int programId ID of the program
    * @return array Updated array of programIds associated with vendor
    */
    exports.addProgramToVendor = function(programId, vendorId) {

        // gets array key for this vendor
        var theId = _.findIndex(itemList, function(item) {
            return item.id == vendorId;
        });

        console.log(itemList[theId].programIds);

        // just in case this vendor has no ids array yet!
        if(!itemList[theId].programIds) itemList[theId].programIds = [];

        itemList[theId].programIds.push(programId);

        return itemList[theId].programIds;
    };

    /**
    * Removes vendor from program the id of each
    *
    * @param int vendorId ID of the vendor
    * @param int programId ID of the program
    * @return array Updated array of programs for the vendor
    */
    exports.removeProgramFromVendor = function(programId, vendorId) {
        // gets array key for this vendor
        var theId = _.findIndex(itemList, function(item) {
            return item.id == vendorId;
        });

        itemList[theId].programIds = _.reject(itemList[theId].programIds, function(item) {
            return programId == item;
        });

        console.log(itemList[theId].programIds);

        //console.log(itemList[theId]);

        return itemList[theId].programIds;
    };

    /**
    * Reduces the itemList to those where ID is in values array
    *
    * If ID exists multiple times, will only return item one time
    *
    */
    exports.getManyWhereIn = function(values) {
        var reducedList = _.filter(itemList, function(item) {
            return _.contains(values, item.id);
        });
        return reducedList;
    };

    exports.getAllWithoutSalesReps = function() {
        var reducedList = _.filter(itemList, function(item) {
            return !User.getOneWhereIn('vendorIds', item.id);
        });
        return reducedList;
    };

    return exports;

}]);<commit_msg>Added more vendors<commit_after>angular.module('app').factory('vendorService', ['$http', 'userService', function($http, User) {

    // dummy data
    var itemList = [
        {
            "id": 1,
            "name": "BearCom Operating LLC",
            "contactPerson": {
                "name": "Jenn Delong",
                "email": "jdelong@marlinfinance.com",
                "phone": "888-479-9111 Ext. 5015"
            },
            "logo": {
                "original": "https://www.filepicker.io/api/file/2v44yfa8TqKt2RAAkpZB"
            },
            "website": "http://www.bearcom.com/",
            "legalTerms": "These are legal terms for vendor 1",
            "businessPhone": "",
            "businessAddress": {
                "address1": "2424 E York St",
                "address2": "",
                "city": "Philadelphia",
                "state": "PA",
                "zip": "19125"
            },
            "geo": {
                "lat": 40.42,
                "lng": -73.45
            },
            "programIds": [
                1,
                3,
                4
            ],
            "programs": [
                {
                    "id": 1,
                    "displayName": "A Custom Display Name for Program 1"
                }
            ]
        },
        {
            "id": 2,
            "name": "Protection One - West",
            "contactPerson": {
                "name": "Jennifer DeLong-Giefer",
                "email": "jdelong@marlinfinance.com",
                "phone": "866-687-3778"
            },
            "logo": {
                "original": "https://www.filepicker.io/api/file/ZxTd0qV0QA2ek9HHSmu3"
            },
            "website": "http://www.protection1.com/",
            "legalTerms": "These are legal terms for vendor 2",
            "businessPhone": "",
            "businessAddress": {
                "address1": "",
                "address2": "",
                "city": "",
                "state": "",
                "zip": ""
            },
            "geo": {
                "lat": 45,
                "lng": -73
            },
            "programIds": [
                1,
                3,
                4
            ],
            "programs": [
                {
                    "id": 1,
                    "displayName": "A Custom Display Name for Program 1"
                }
            ]
        },
        {
            "id": 3,
            "name": "Gametime",
            "contactPerson": {
                "name": "Warren Myers",
                "email": "wmyers@playcore.com",
                "phone": ""
            },
            "logo": {
                "original": "https://www.filepicker.io/api/file/EcryKofERBynAuL2txez"
            },
            "website": "http://www.gametime.com",
            "legalTerms": "These are legal terms for vendor 3",
            "businessPhone": "",
            "businessAddress": {
                "address1": "",
                "address2": "",
                "city": "",
                "state": "",
                "zip": ""
            },
            "geo": {
                "lat": 39.57,
                "lng": -75.1
            },
            "programIds": [
                1,
                3,
                4
            ],
            "programs": [
                {
                    "id": 1,
                    "displayName": "A Custom Display Name for Program 1"
                }
            ]
        },
        {
            "id": 4,
            "name": "Union Bank",
            "contactPerson": {
                "name": "Union Bank Financing Consultant",
                "email": "ubapps@marlinfinance.com",
                "phone": "877-307-6756"
            },
            "logo": {
                "original": "https://www.filepicker.io/api/file/yjlOg28UROmPXyK8uadA"
            },
            "website": "https://www.unionbank.com/",
            "legalTerms": "These are legal terms for vendor 3",
            "businessPhone": "",
            "businessAddress": {
                "address1": "",
                "address2": "",
                "city": "",
                "state": "",
                "zip": ""
            },
            "geo": {
                "lat": 35.11,
                "lng": -101.5
            },
            "programIds": [
                1,
                3,
                4
            ],
            "programs": [
                {
                    "id": 1,
                    "displayName": "A Custom Display Name for Program 1"
                }
            ]
        },
        {
            "id": 5,
            "name": "Southeastern Equipment & Supply, Inc.",
            "contactPerson": {
                "name": "Grady Martin",
                "email": "leasing@southeasternequipment.net",
                "phone": "803-454-3656"
            },
            "logo": {
                "original": "https://www.filepicker.io/api/file/nvC3zhiTQoSl01P7K3Br"
            },
            "website": "https://www.unionbank.com/",
            "legalTerms": "These are legal terms for vendor 3",
            "businessPhone": "",
            "businessAddress": {
                "address1": "1919 Old Dunbar Road",
                "address2": "",
                "city": "West Columbia",
                "state": "SC",
                "zip": "29045"
            },
            "programIds": [
                1,
                3,
                4
            ],
            "programs": [
                {
                    "id": 1,
                    "displayName": "A Custom Display Name for Program 1"
                }
            ]
        },
        {
            "logo": {
                "original": "https://www.filepicker.io/api/file/AtnUNRiRSQCBrV1eDES8"
            },
            "state": false,
            "name": "Arizona Business Systems Inc",
            "businessAddress": {
                "address1": "16844 E Avenue of the Fountains",
                "address2": "Suite 202",
                "city": "Fountain Hills",
                "state": "AZ",
                "zip": "85268-4202"
            },
            "contactPerson": {
                "phone": "(480)816-1222"
            },
            "businessPhone": "",
            "id": 6,
            "salesRep": false,
            "website": "http://www.absinc-sw.com/"
        },
        {
            "logo": {
                "original": "https://www.filepicker.io/api/file/zhgBhDtsRDuxgsg3Qw49"
            },
            "state": false,
            "name": "Praxair Inc",
            "businessAddress": {
                "address1": "3505 Buck Owens Blvd",
                "city": "Bakersfield",
                "zip": "93308-4919"
            },
            "contactPerson": {
                "phone": "(661)327-5336",
                "email": "tony_flores@praxair.com"
            },
            "website": "www.praxair.com",
            "id": 7,
            "salesRep": false
        },
        {
            "logo": {
                "original": "https://www.filepicker.io/api/file/ga99yLAOSnqjeoJVAie7"
            },
            "state": false,
            "name": "EZ 2000 , Inc.",
            "businessAddress": {
                "address1": "1800 Century Park E",
                "address2": "Ste 600",
                "city": "Los Angeles",
                "zip": "90067-1501"
            },
            "contactPerson": {
                "phone": "(800)273-5033",
                "email": "vpsales@ez2000software.com"
            },
            "website": "www.ez2000software.com",
            "id": 8,
            "salesRep": false
        },
        {
            "logo": {
                "original": "https://www.filepicker.io/api/file/YVnYF7JITLyjN3dWWOaQ"
            },
            "state": false,
            "name": "Meeting One",
            "businessAddress": {
                "address1": "501 S Cherry St Ste 1000",
                "city": "Denver",
                "zip": "80246-1327"
            },
            "contactPerson": {
                "phone": "(303)623-2530",
                "email": "wkim@meetingone.com"
            },
            "website": "www.meetingone.com",
            "id": 9,
            "salesRep": false
        },
        {
            "logo": {
                "original": "https://www.filepicker.io/api/file/m6PTCIGdRsSVyMYimcI2"
            },
            "state": false,
            "name": "capitol office products",
            "businessAddress": {
                "address1": "P O Box 1671",
                "city": "Daytona Beach",
                "zip": "32115-1671"
            },
            "contactPerson": {
                "phone": "(386)238-1177",
                "email": "Jshearer@capofficeproducts.com"
            },
            "website": "www.capofficeproducts.com",
            "id": 10,
            "salesRep": false
        },
        {
            "logo": {
                "original": "https://www.filepicker.io/api/file/9gaCD06IQ9u9ZaE7YDnS"
            },
            "state": false,
            "name": "Edge Business Systems LLC",
            "businessAddress": {
                "address1": "11660 Alpharetta Hwy",
                "address2": "Ste 710",
                "city": "Roswell",
                "zip": "30076"
            },
            "contactPerson": {
                "phone": "(404)228-4951"
            },
            "id": 11,
            "salesRep": false
        },
        {
            "logo": {
                "original": "https://www.filepicker.io/api/file/bokWgO5uTl6gYIpFvkye"
            },
            "state": false,
            "name": "Epay Systems",
            "businessAddress": {
                "address1": "8420 W Bryn Mawr Ave",
                "city": "Chicago",
                "zip": "60631-3479"
            },
            "contactPerson": {
                "phone": "(773)362-4803",
                "email": "desquivel@epaysystems.com"
            },
            "website": "http://www.epaysystems.com",
            "id": 12,
            "salesRep": false
        },
        {
            "logo": {
                "original": "https://www.filepicker.io/api/file/ehj73VN8RleUCWatx25K"
            },
            "state": false,
            "name": "Central Restaurant Products",
            "businessAddress": {
                "address1": "7750 Georgetown Rd.",
                "city": "Indianapolis",
                "zip": "46268"
            },
            "contactPerson": {
                "phone": "(800)222-5107",
                "email": "jeffn@centralrestaurant.com"
            },
            "website": "http://www.centralrestaurant.com",
            "id": 13,
            "salesRep": false
        },
        {
            "logo": {
                "original": "https://www.filepicker.io/api/file/B057fvRamXKKOTr9xmMw"
            },
            "state": false,
            "name": "Clark Security Products-Washington DC.",
            "businessAddress": {
                "address1": "12006 Plum Orchard Dr",
                "city": "Silver Springs",
                "zip": "20904-7857"
            },
            "contactPerson": {
                "phone": "(301)572-1901",
                "email": "tom.hightman@clarksecurity.com"
            },
            "website": "www.clarksecurity.com",
            "id": 14,
            "salesRep": false
        },
        {
            "logo": {
                "original": "https://www.filepicker.io/api/file/nkZx82FWQHWObEAvCCkR"
            },
            "state": false,
            "name": "Hendrix Business Systems Inc",
            "businessAddress": {
                "address1": "2040 Ind Cmmerce Dr Ste A",
                "address2": "2040 Independence Commerce Dr",
                "city": "Matthews",
                "zip": "28105"
            },
            "contactPerson": {
                "phone": "(704)574-4800",
                "email": "rhendrix@hendrixbusiness.com"
            },
            "website": "www.hendrixbusiness.com",
            "id": 15,
            "salesRep": false
        },
        {
            "logo": {
                "original": "https://www.filepicker.io/api/file/27BUlutGTwmUJ124XGLS"
            },
            "state": false,
            "name": "1st Run Computer Services, Inc.",
            "businessAddress": {
                "address1": "1261 Broadway Rm 508",
                "city": "New York",
                "zip": "10001-3546"
            },
            "contactPerson": {
                "phone": "(212)779-0800",
                "email": "SHawkins@1strun.com"
            },
            "website": "http://www.1strun.net",
            "id": 16,
            "salesRep": false
        },
        {
            "logo": {
                "original": "https://www.filepicker.io/api/file/SOgR7MisTbuisebKHdxg"
            },
            "state": false,
            "name": "Autozone, Inc - CORP",
            "businessAddress": {
                "address1": "P O Box 2198",
                "address2": "123 S. Front Street",
                "city": "Memphis",
                "zip": "38101-2198"
            },
            "contactPerson": {
                "phone": "(901)495-6500",
                "email": "tim.mcauliff@autozone.com"
            },
            "website": "www.autozone.com",
            "id": 17,
            "salesRep": false
        },
        {
            "logo": {
                "original": "https://www.filepicker.io/api/file/2MeQ92VoTZSNA3JwLQf0"
            },
            "state": false,
            "name": "American Typewriter & Office Equipment",
            "businessAddress": {
                "address1": "322 W Nakoma St",
                "city": "San Antonio",
                "zip": "78216-2621"
            },
            "contactPerson": {
                "phone": "(210)349-9026"
            },
            "id": 18,
            "salesRep": false,
            "website": "http://www.americantypewritertx.com/"
        },
        {
            "logo": {},
            "state": false,
            "name": "Cuda Washington Inc.",
            "businessAddress": {
                "address1": "914 164th St SE Ste 1698",
                "city": "Mill Creek",
                "zip": "98012-6385"
            },
            "contactPerson": {
                "phone": "(866)344-8144"
            },
            "id": 19,
            "salesRep": false
        },
        {
            "logo": {
                "original": "https://www.filepicker.io/api/file/aiXfeFchQxu6ua1YueUs"
            },
            "state": false,
            "name": "Advanced Mailing & Shipping Tech Inc",
            "businessAddress": {
                "address1": "2346 Market St",
                "city": "Wheeling",
                "zip": "26003"
            },
            "contactPerson": {
                "phone": "(304)232-1755",
                "email": "rthoburn@amasti.com"
            },
            "website": "http://www.amasti.com/index.html",
            "id": 20,
            "salesRep": false
        },
        {
            "logo": {
                "original": "https://www.filepicker.io/api/file/I9KHZslQoKC2teGmUoSM"
            },
            "state": false,
            "name": "Warren Southwest Refrigeration",
            "businessAddress": {
                "address1": "6423 Cunningham Rd",
                "address2": "713",
                "city": "Houston",
                "zip": "77041-4713"
            },
            "contactPerson": {
                "phone": "(713)869-6221",
                "email": "steve@warrenswrefrigeration.com"
            },
            "website": "http://www.warrensouthwestrefrigera",
            "id": 21,
            "salesRep": false
        },
        {
            "logo": {
                "original": "https://www.filepicker.io/api/file/SjFpjBZZRU6Tcfz4qrNa"
            },
            "state": false,
            "name": "Air-Care",
            "businessAddress": {
                "address1": "3868 E Post Rd",
                "city": "Las Vegas",
                "zip": "89120-3963"
            },
            "contactPerson": {
                "phone": "(800)322-9919",
                "email": "jared@air-care.com"
            },
            "website": "http://www.air-care.com",
            "id": 22,
            "salesRep": false
        },
        {
            "logo": {},
            "state": false,
            "name": "B&G Restaurant Supply Inc.",
            "businessAddress": {
                "address1": "48 Eagle St",
                "city": "Pittsfield",
                "zip": "01201-4715"
            },
            "contactPerson": {
                "phone": "(413)442-0390",
                "email": "bob@bgrestsupply.com"
            },
            "website": "www.bgrestsupply.com",
            "id": 23,
            "salesRep": false
        },
        {
            "logo": {
                "original": "https://www.filepicker.io/api/file/JnaftzTAQuST64VnRHXg"
            },
            "state": false,
            "name": "American Dish Service",
            "businessAddress": {
                "address1": "900 Blake St",
                "city": "Edwardsville",
                "zip": "66111-3820"
            },
            "contactPerson": {
                "phone": "(913)422-3700",
                "email": "djohnson@pfgc.com"
            },
            "website": "www.americandish.com",
            "id": 24,
            "salesRep": false
        }
    ];

    //initLocalStore();

    // create and expose service methods
    var exports = {};

    // get all items
    exports.getAll = function() {
        return itemList;
    };

    // get one item by id
    exports.getById = function(id) {
        var theItem = _.find(itemList, function(item) {
            return item.id == id;
        });
        return theItem ? theItem : false;
    };

    // update one item by id
    // @todo check for updating the id!
    exports.updateById = function(id, newData) {
        var theId = _.findIndex(itemList, function(item) {
            return item.id == id;
        });
        theList = _.extend(itemList[theId], newData);
        return theList;
    };

    // update one item by item
    // @note we figure out id from item
    exports.update = function(newItem) {
        var theIndex = _.findIndex(itemList, function(item) {
            return item.id == newItem.id;
        });
        theList = _.extend(itemList[theIndex], newItem);
        return theList;
    };

    // add a new item
    exports.add = function(item) {
        item.id = itemList.length + 1;
        itemList.push(item);
        return item;
    };

    // remove item by item
    exports.remove = function(item) {
        itemList.splice(itemList.indexOf(item), 1);
        return item;
    };

    /**
    * Add a vendor to a program using the id of each
    * @param int vendorId ID of the vendor
    * @param int programId ID of the program
    * @return array Updated array of programIds associated with vendor
    */
    exports.addProgramToVendor = function(programId, vendorId) {

        // gets array key for this vendor
        var theId = _.findIndex(itemList, function(item) {
            return item.id == vendorId;
        });

        console.log(itemList[theId].programIds);

        // just in case this vendor has no ids array yet!
        if(!itemList[theId].programIds) itemList[theId].programIds = [];

        itemList[theId].programIds.push(programId);

        return itemList[theId].programIds;
    };

    /**
    * Removes vendor from program the id of each
    *
    * @param int vendorId ID of the vendor
    * @param int programId ID of the program
    * @return array Updated array of programs for the vendor
    */
    exports.removeProgramFromVendor = function(programId, vendorId) {
        // gets array key for this vendor
        var theId = _.findIndex(itemList, function(item) {
            return item.id == vendorId;
        });

        itemList[theId].programIds = _.reject(itemList[theId].programIds, function(item) {
            return programId == item;
        });

        console.log(itemList[theId].programIds);

        //console.log(itemList[theId]);

        return itemList[theId].programIds;
    };

    /**
    * Reduces the itemList to those where ID is in values array
    *
    * If ID exists multiple times, will only return item one time
    *
    */
    exports.getManyWhereIn = function(values) {
        var reducedList = _.filter(itemList, function(item) {
            return _.contains(values, item.id);
        });
        return reducedList;
    };

    exports.getAllWithoutSalesReps = function() {
        var reducedList = _.filter(itemList, function(item) {
            return !User.getOneWhereIn('vendorIds', item.id);
        });
        return reducedList;
    };

    return exports;

}]);<|endoftext|>


<commit_before>import { translate as $t } from '../../helpers';
import { store, State } from '../../store';

import { AmountWell, FilteredAmountWell } from './amount-well';
import SearchComponent from './search';
import Operation from './operation';
import SyncButton from './sync-button';

import throttle from 'lodash.throttle';

// Height of an operation line (px) based on the css settings
let OPERATION_HEIGHT = setOperationHeight();

// Number of operations before / after the ones to render, for flexibility.
const OPERATION_BALLAST = 10;

// Throttling for the scroll event (ms)
const SCROLL_THROTTLING = 150;

// Number of elements
let INITIAL_SHOW_ITEMS = window.innerHeight / OPERATION_HEIGHT | 0;

// Filter functions used in amount wells.
function noFilter() {
    return true;
}
function isPositive(op) {
    return op.amount > 0;
}
function isNegative(op) {
    return op.amount < 0;
}

function setOperationHeight() {
    return window.innerWidth < 768 ? 41 : 55;
}

export default class OperationsComponent extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            account: store.getCurrentAccount(),
            operations: store.getCurrentOperations(),
            filteredOperations: [],
            firstItemShown: 0,
            lastItemShown: INITIAL_SHOW_ITEMS,
            hasFilteredOperations: false
        };
        this.listener = this._listener.bind(this);
        this.setFilteredOperations = this.setFilteredOperations.bind(this);

        this.handleScroll = throttle(this.onScroll.bind(this), SCROLL_THROTTLING);
        this.handleResize = this.handleResize.bind(this);
    }

    _listener() {
        this.setState({
            account: store.getCurrentAccount(),
            operations: store.getCurrentOperations(),
            firstItemShown: 0,
            lastItemShown: INITIAL_SHOW_ITEMS
        }, () => this.refs.search.filter());
    }

    setFilteredOperations(operations) {
        this.setState({
            filteredOperations: operations,
            hasFilteredOperations: operations.length < this.state.operations.length,
            firstItemShown: 0,
            lastItemShown: INITIAL_SHOW_ITEMS
        });
    }

    componentDidMount() {
        store.on(State.banks, this.listener);
        store.on(State.accounts, this.listener);
        store.on(State.operations, this.listener);

        window.addEventListener('scroll', this.handleScroll);
        window.addEventListener('resize', this.handleResize);
    }

    componentWillUnmount() {
        store.removeListener(State.banks, this.listener);
        store.removeListener(State.operations, this.listener);
        store.removeListener(State.accounts, this.listener);

        window.removeEventListener('scroll', this.handleScroll);
        window.removeEventListener('resize', this.handleResize);
    }

    handleResize(e) {
        e.preventDefault();
        OPERATION_HEIGHT = setOperationHeight();
        INITIAL_SHOW_ITEMS = window.innerHeight / OPERATION_HEIGHT | 0;
        this.handleScroll();
    }

    onScroll() {
        let wellH = React.findDOMNode(this.refs.wells).scrollHeight;
        let searchH = React.findDOMNode(this.refs.search).scrollHeight;
        let panelH = React.findDOMNode(this.refs.panelHeading).scrollHeight;
        let theadH = React.findDOMNode(this.refs.thead).scrollHeight;
        let fixedTopH = wellH + searchH + panelH + theadH;

        let topItemH = Math.max(window.scrollY - fixedTopH, 0);
        let bottomItemH = topItemH + window.innerHeight;

        let firstItemShown = Math.max(topItemH / OPERATION_HEIGHT - OPERATION_BALLAST | 0, 0);
        let lastItemShown = (bottomItemH / OPERATION_HEIGHT | 0) + OPERATION_BALLAST;

        this.setState({
            firstItemShown,
            lastItemShown
        });
    }

    render() {
        // Edge case: the component hasn't retrieved the account yet.
        if (this.state.account === null) {
            return
;
        }

        let bufferPreH = OPERATION_HEIGHT * this.state.firstItemShown;
        let bufferPre = <tr style={ { height: `${bufferPreH}px` } } />;

        let formatCurrency = this.state.account.formatCurrency;
        let ops = this.state.filteredOperations
                    .slice(this.state.firstItemShown, this.state.lastItemShown)
                    .map(o =>
                        <Operation key={ o.id }
                          operation={ o }
                          formatCurrency={ formatCurrency }
                        />);

        let numOps = this.state.filteredOperations.length;
        let bufferPostH = OPERATION_HEIGHT * Math.max(numOps - this.state.lastItemShown, 0);
        let bufferPost = <tr style={ { height: `${bufferPostH}px` } } />;

        let asOf = $t('client.operations.as_of');
        let lastCheckedDate = new Date(this.state.account.lastChecked).toLocaleDateString();
        let lastCheckDate = `${asOf} ${lastCheckedDate}`;

        return (

                    <AmountWell
                      size="col-xs-12 col-md-3"
                      backgroundColor="background-lightblue"
                      icon="balance-scale"
                      title={ $t('client.operations.current_balance') }
                      subtitle={ lastCheckDate }
                      operations={ this.state.operations }
                      initialAmount={ this.state.account.initialAmount }
                      filterFunction={ noFilter }
                      formatCurrency={ formatCurrency }
                    />

                    <FilteredAmountWell
                      size="col-xs-12 col-md-3"
                      backgroundColor="background-green"
                      icon="arrow-down"
                      title={ $t('client.operations.received') }
                      hasFilteredOperations={ this.state.hasFilteredOperations }
                      operations={ this.state.operations }
                      filteredOperations={ this.state.filteredOperations }
                      initialAmount={ 0 }
                      filterFunction={ isPositive }
                      formatCurrency={ formatCurrency }
                    />

                    <FilteredAmountWell
                      size="col-xs-12 col-md-3"
                      backgroundColor="background-orange"
                      icon="arrow-up"
                      title={ $t('client.operations.spent') }
                      hasFilteredOperations={ this.state.hasFilteredOperations }
                      operations={ this.state.operations }
                      filteredOperations={ this.state.filteredOperations }
                      initialAmount={ 0 }
                      filterFunction={ isNegative }
                      formatCurrency={ formatCurrency }
                    />

                    <FilteredAmountWell
                      size="col-xs-12 col-md-3"
                      backgroundColor="background-darkblue"
                      icon="database"
                      title={ $t('client.operations.saved') }
                      hasFilteredOperations={ this.state.hasFilteredOperations }
                      operations={ this.state.operations }
                      filteredOperations={ this.state.filteredOperations }
                      initialAmount={ 0 }
                      filterFunction={ noFilter }
                      formatCurrency={ formatCurrency }
                    />

                <SearchComponent
                  ref="search"
                  setFilteredOperations={ this.setFilteredOperations }
                  operations={ this.state.operations }
                />

                            { $t('client.operations.title') }

                        <SyncButton account={ this.state.account } />

                                        { $t('client.operations.column_date') }

                                        { $t('client.operations.column_type') }

                                        { $t('client.operations.column_name') }

                                        { $t('client.operations.column_amount') }

                                        { $t('client.operations.column_category') }

                                { bufferPre }
                                { ops }
                                { bufferPost }

        );
    }
}
<commit_msg>Fixes #430: Fixes small jumps in the infinite list;<commit_after>import { translate as $t } from '../../helpers';
import { store, State } from '../../store';

import { AmountWell, FilteredAmountWell } from './amount-well';
import SearchComponent from './search';
import Operation from './operation';
import SyncButton from './sync-button';

import throttle from 'lodash.throttle';

// Height of an operation line (px) based on the css settings
let OPERATION_HEIGHT = setOperationHeight();

// Number of operations before / after the ones to render, for flexibility.
const OPERATION_BALLAST = 10;

// Throttling for the scroll event (ms)
const SCROLL_THROTTLING = 150;

// Number of elements
let INITIAL_SHOW_ITEMS = window.innerHeight / OPERATION_HEIGHT | 0;

// Filter functions used in amount wells.
function noFilter() {
    return true;
}
function isPositive(op) {
    return op.amount > 0;
}
function isNegative(op) {
    return op.amount < 0;
}

function setOperationHeight() {
    // Keep in sync with style.css.
    return window.innerWidth < 768 ? 41 : 54;
}

export default class OperationsComponent extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            account: store.getCurrentAccount(),
            operations: store.getCurrentOperations(),
            filteredOperations: [],
            firstItemShown: 0,
            lastItemShown: INITIAL_SHOW_ITEMS,
            hasFilteredOperations: false
        };
        this.listener = this._listener.bind(this);
        this.setFilteredOperations = this.setFilteredOperations.bind(this);

        this.handleScroll = throttle(this.onScroll.bind(this), SCROLL_THROTTLING);
        this.handleResize = this.handleResize.bind(this);
    }

    _listener() {
        this.setState({
            account: store.getCurrentAccount(),
            operations: store.getCurrentOperations(),
            firstItemShown: 0,
            lastItemShown: INITIAL_SHOW_ITEMS
        }, () => this.refs.search.filter());
    }

    setFilteredOperations(operations) {
        this.setState({
            filteredOperations: operations,
            hasFilteredOperations: operations.length < this.state.operations.length,
            firstItemShown: 0,
            lastItemShown: INITIAL_SHOW_ITEMS
        });
    }

    componentDidMount() {
        store.on(State.banks, this.listener);
        store.on(State.accounts, this.listener);
        store.on(State.operations, this.listener);

        window.addEventListener('scroll', this.handleScroll);
        window.addEventListener('resize', this.handleResize);
    }

    componentWillUnmount() {
        store.removeListener(State.banks, this.listener);
        store.removeListener(State.operations, this.listener);
        store.removeListener(State.accounts, this.listener);

        window.removeEventListener('scroll', this.handleScroll);
        window.removeEventListener('resize', this.handleResize);
    }

    handleResize(e) {
        e.preventDefault();
        OPERATION_HEIGHT = setOperationHeight();
        INITIAL_SHOW_ITEMS = window.innerHeight / OPERATION_HEIGHT | 0;
        this.handleScroll();
    }

    onScroll() {
        let wellH = React.findDOMNode(this.refs.wells).scrollHeight;
        let searchH = React.findDOMNode(this.refs.search).scrollHeight;
        let panelH = React.findDOMNode(this.refs.panelHeading).scrollHeight;
        let theadH = React.findDOMNode(this.refs.thead).scrollHeight;
        let fixedTopH = wellH + searchH + panelH + theadH;

        let topItemH = Math.max(window.scrollY - fixedTopH, 0);
        let bottomItemH = topItemH + window.innerHeight;

        let firstItemShown = Math.max(topItemH / OPERATION_HEIGHT - OPERATION_BALLAST | 0, 0);
        let lastItemShown = (bottomItemH / OPERATION_HEIGHT | 0) + OPERATION_BALLAST;

        this.setState({
            firstItemShown,
            lastItemShown
        });
    }

    render() {
        // Edge case: the component hasn't retrieved the account yet.
        if (this.state.account === null) {
            return
;
        }

        let bufferPreH = OPERATION_HEIGHT * this.state.firstItemShown;
        let bufferPre = <tr style={ { height: `${bufferPreH}px` } } />;

        let formatCurrency = this.state.account.formatCurrency;
        let ops = this.state.filteredOperations
                    .slice(this.state.firstItemShown, this.state.lastItemShown)
                    .map(o =>
                        <Operation key={ o.id }
                          operation={ o }
                          formatCurrency={ formatCurrency }
                        />);

        let numOps = this.state.filteredOperations.length;
        let bufferPostH = OPERATION_HEIGHT * Math.max(numOps - this.state.lastItemShown, 0);
        let bufferPost = <tr style={ { height: `${bufferPostH}px` } } />;

        let asOf = $t('client.operations.as_of');
        let lastCheckedDate = new Date(this.state.account.lastChecked).toLocaleDateString();
        let lastCheckDate = `${asOf} ${lastCheckedDate}`;

        return (

                    <AmountWell
                      size="col-xs-12 col-md-3"
                      backgroundColor="background-lightblue"
                      icon="balance-scale"
                      title={ $t('client.operations.current_balance') }
                      subtitle={ lastCheckDate }
                      operations={ this.state.operations }
                      initialAmount={ this.state.account.initialAmount }
                      filterFunction={ noFilter }
                      formatCurrency={ formatCurrency }
                    />

                    <FilteredAmountWell
                      size="col-xs-12 col-md-3"
                      backgroundColor="background-green"
                      icon="arrow-down"
                      title={ $t('client.operations.received') }
                      hasFilteredOperations={ this.state.hasFilteredOperations }
                      operations={ this.state.operations }
                      filteredOperations={ this.state.filteredOperations }
                      initialAmount={ 0 }
                      filterFunction={ isPositive }
                      formatCurrency={ formatCurrency }
                    />

                    <FilteredAmountWell
                      size="col-xs-12 col-md-3"
                      backgroundColor="background-orange"
                      icon="arrow-up"
                      title={ $t('client.operations.spent') }
                      hasFilteredOperations={ this.state.hasFilteredOperations }
                      operations={ this.state.operations }
                      filteredOperations={ this.state.filteredOperations }
                      initialAmount={ 0 }
                      filterFunction={ isNegative }
                      formatCurrency={ formatCurrency }
                    />

                    <FilteredAmountWell
                      size="col-xs-12 col-md-3"
                      backgroundColor="background-darkblue"
                      icon="database"
                      title={ $t('client.operations.saved') }
                      hasFilteredOperations={ this.state.hasFilteredOperations }
                      operations={ this.state.operations }
                      filteredOperations={ this.state.filteredOperations }
                      initialAmount={ 0 }
                      filterFunction={ noFilter }
                      formatCurrency={ formatCurrency }
                    />

                <SearchComponent
                  ref="search"
                  setFilteredOperations={ this.setFilteredOperations }
                  operations={ this.state.operations }
                />

                            { $t('client.operations.title') }

                        <SyncButton account={ this.state.account } />

                                        { $t('client.operations.column_date') }

                                        { $t('client.operations.column_type') }

                                        { $t('client.operations.column_name') }

                                        { $t('client.operations.column_amount') }

                                        { $t('client.operations.column_category') }

                                { bufferPre }
                                { ops }
                                { bufferPost }

        );
    }
}
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { reduxForm } from 'redux-form';
import { createUser } from '../../actions';
import { Link } from 'react-router';

const fields = ['username', 'name', 'contactNumber', 'email', 'address1', 'address2', 'city', 'state', 'zip', 'country'];

class UserSignup extends Component {
  onSubmit(props) {
    this.props.createUser(props)
      .then(() => {
        this.context.router.push('/');
      })
  }

  render() {
    const { fields: { username, name, contactNumber, email, address1, address2, city, state, zip, country }, handleSubmit } = this.props;

    return (

          <form onSubmit={handleSubmit(this.onSubmit.bind(this))}>

Register to Donate

            Username

              <input className="input" type='text' {...username} />

            Company Name

              <input className="input" type='text' {...name} />

            Phone Number

              <input className="input" type='text' {...contactNumber} />

            Email

              <input className="input" type='text' {...email} />

            Address Line 1

              <input className="input" type='text' {...address1} />

            Address Line 2

              <input className="input" type='text' {...address2} />

            City

              <input className="input" type='text' {...city} />

            State

              <input className="input" type='text' {...state} />

            Zip

              <input className="input" type='text' {...zip} />

            Country

              <input className="input" type='text' {...country} />

              <button type="submit" className="button is-primary">Submit</button>
              <button className="button is-link">Cancel</button>

          </form>

    );
  }
}

function validate(values) {
  const errors = {};

  if (!values.title) {
    errors.title = 'Enter a title';
  }
  if (!values.categories) {
    errors.categories = 'Enter categories';
  }
  if (!values.content) {
    errors.content = 'Enter some content';
  }

  return errors;
}

// reduxForm: 1 is form config, 2nd is mapStateToProps, 3rd is mapDispatchToProps

// export default reduxForm({
//   form: 'UserSignupForm',
//   fields
// }, null, null)(UserSignup);

export default reduxForm({
  form: 'UserSignupForm',
  fields
}, null, { createUser })(UserSignup);
<commit_msg>removed comments<commit_after>import React, { Component } from 'react';
import { reduxForm } from 'redux-form';
import { createUser } from '../../actions';
import { Link } from 'react-router';

const fields = ['username', 'name', 'contactNumber', 'email', 'address1', 'address2', 'city', 'state', 'zip', 'country'];

class UserSignup extends Component {
  onSubmit(props) {
    this.props.createUser(props)
      .then(() => {
        this.context.router.push('/');
      })
  }

  render() {
    const { fields: { username, name, contactNumber, email, address1, address2, city, state, zip, country }, handleSubmit } = this.props;

    return (

          <form onSubmit={handleSubmit(this.onSubmit.bind(this))}>

Register to Donate

            Username

              <input className="input" type='text' {...username} />

            Company Name

              <input className="input" type='text' {...name} />

            Phone Number

              <input className="input" type='text' {...contactNumber} />

            Email

              <input className="input" type='text' {...email} />

            Address Line 1

              <input className="input" type='text' {...address1} />

            Address Line 2

              <input className="input" type='text' {...address2} />

            City

              <input className="input" type='text' {...city} />

            State

              <input className="input" type='text' {...state} />

            Zip

              <input className="input" type='text' {...zip} />

            Country

              <input className="input" type='text' {...country} />

              <button type="submit" className="button is-primary">Submit</button>
              <button className="button is-link">Cancel</button>

          </form>

    );
  }
}

function validate(values) {
  const errors = {};

  if (!values.title) {
    errors.title = 'Enter a title';
  }
  if (!values.categories) {
    errors.categories = 'Enter categories';
  }
  if (!values.content) {
    errors.content = 'Enter some content';
  }

  return errors;
}

// reduxForm: 1 is form config, 2nd is mapStateToProps, 3rd is mapDispatchToProps

export default reduxForm({
  form: 'UserSignupForm',
  fields
}, null, { createUser })(UserSignup);
<|endoftext|>


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

  angular.module('foundation.modal', ['foundation.core'])
    .directive('zfModal', modalDirective)
    .service('ModalFactory', ModalFactory)
  ;

  modalDirective.$inject = ['FoundationApi'];

  function modalDirective(foundationApi) {

    var directive = {
      restrict: 'EA',
      templateUrl: 'components/modal/modal.html',
      transclude: true,
      scope: true,
      replace: true,
      compile: compile
    };

    return directive;

    function compile(tElement, tAttrs, transclude) {
      var type = 'modal';

      return {
        pre: preLink,
        post: postLink
      };

      function preLink(scope, iElement, iAttrs, controller) {
          iAttrs.$set('zf-closable', type);
      }

      function postLink(scope, element, attrs) {
        var dialog = angular.element(element.children()[0]);

        scope.active = scope.active || false;
        scope.overlay = attrs.overlay === 'true' || attrs.overlayClose === 'true' ? true : false;
        scope.overlayClose = attrs.overlayClose === 'true' ? true : false;

        var animationIn = attrs.animationIn || 'fadeIn';
        var animationOut = attrs.animationOut || 'fadeOut';
        var overlayIn = 'fadeIn';
        var overlayOut = 'fadeOut';

        scope.hideOverlay = function() {
          if(scope.overlayClose) {
            scope.hide();
          }
        };

        scope.hide = function() {
          scope.active = false;
          animate();
          return;
        };

        scope.show = function() {
          scope.active = true;
          animate();
          dialog.tabIndex = -1;
          dialog[0].focus();
          return;
        };

        scope.toggle = function() {
          scope.active = !scope.active;
          animate();
          return;
        };

        init();

        //setup
        foundationApi.subscribe(attrs.id, function(msg) {
          if(msg === 'show' || msg === 'open') {
            scope.show();
          } else if (msg === 'close' || msg === 'hide') {
            scope.hide();
          } else if (msg === 'toggle') {
            scope.toggle();
          }

          if (!scope.$root.$$phase) {
            scope.$apply();
          }

          return;
        });

        function animate() {
          //animate both overlay and dialog
          if(!scope.overlay) {
            element.css('background', 'transparent');
          }

          foundationApi.animate(element, scope.active, overlayIn, overlayOut);
          foundationApi.animate(dialog, scope.active, animationIn, animationOut);
        }

        function init() {
          if(scope.active) {
            scope.show();
          }
        }
      }
    }
  }

  ModalFactory.$inject = ['$http', '$templateCache', '$rootScope', '$compile', '$timeout', 'FoundationApi'];

  function ModalFactory($http, $templateCache, $rootScope, $compile, $timeout, foundationApi) {
    return modalFactory;

    function modalFactory(config) {
      var self = this, //for prototype functions
          container = angular.element(config.container || document.body),
          id = config.id || foundationApi.generateUuid(),
          attached = false,
          destroyed = false,
          html,
          element,
          scope
      ;

      var props = [
        'animationIn',
        'animationOut',
        'overlay',
        'overlayClose'
      ];

      if(config.templateUrl) {
        //get template
        $http.get(config.templateUrl, {
          cache: $templateCache
        }).then(function (response) {
          html = response.data;
          assembleDirective();
        });

      } else if(config.template) {
        //use provided template
        html = config.template;
        assembleDirective();
      }

      self.activate = activate;
      self.deactivate = deactivate;
      self.toggle = toggle;
      self.destroy = destroy;

      return {
        activate: activate,
        deactivate: deactivate,
        toggle: toggle,
        destroy: destroy
      };

      function checkStatus() {
        if(destroyed) {
          throw "Error: Modal was destroyed. Delete the object and create a new ModalFactory instance."
        }
      }

      function activate() {
        checkStatus();
        $timeout(function() {
          init(true);
          foundationApi.publish(id, 'show');
        }, 0, false);
      }

      function deactivate() {
        checkStatus();
        $timeout(function() {
          init(false);
          foundationApi.publish(id, 'hide');
        }, 0, false);
      }

      function toggle() {
        checkStatus();
        $timeout(function() {
          init(true);
          foundationApi.publish(id, 'toggle');
        }, 0, false);
      }

      function init(state) {
        if(!attached && html.length > 0) {
          element = angular.element(html);
          var modalEl = container.append(element);

          scope.active = state;
          $compile(element)(scope);
          attached = true;
        }
      }

      function assembleDirective() {
        html = '<zf-modal id="' + id + '">' + html + '</zf-modal>';

        scope = $rootScope.$new();

        for(var prop in props) {
          scope[prop] = config[prop];
        }
      }

      function destroy() {
        self.deactivate();
        setTimeout(function() {
          scope.$destroy();
          element.remove();
          destroyed = true;
        }, 3000);
      }

    }

  }

})();
<commit_msg>Store data in attributes rather than scope<commit_after>(function() {
  'use strict';

  angular.module('foundation.modal', ['foundation.core'])
    .directive('zfModal', modalDirective)
    .service('ModalFactory', ModalFactory)
  ;

  modalDirective.$inject = ['FoundationApi'];

  function modalDirective(foundationApi) {

    var directive = {
      restrict: 'EA',
      templateUrl: 'components/modal/modal.html',
      transclude: true,
      scope: true,
      replace: true,
      compile: compile
    };

    return directive;

    function compile(tElement, tAttrs, transclude) {
      var type = 'modal';

      return {
        pre: preLink,
        post: postLink
      };

      function preLink(scope, iElement, iAttrs, controller) {
          iAttrs.$set('zf-closable', type);
      }

      function postLink(scope, element, attrs) {
        var dialog = angular.element(element.children()[0]);

        scope.active = scope.active || false;
        scope.overlay = attrs.overlay === 'true' || attrs.overlayClose === 'true' ? true : false;
        scope.overlayClose = attrs.overlayClose === 'true' ? true : false;

        var animationIn = attrs.animationIn || 'fadeIn';
        var animationOut = attrs.animationOut || 'fadeOut';
        var overlayIn = 'fadeIn';
        var overlayOut = 'fadeOut';

        scope.hideOverlay = function() {
          if(scope.overlayClose) {
            scope.hide();
          }
        };

        scope.hide = function() {
          scope.active = false;
          animate();
          return;
        };

        scope.show = function() {
          scope.active = true;
          animate();
          dialog.tabIndex = -1;
          dialog[0].focus();
          return;
        };

        scope.toggle = function() {
          scope.active = !scope.active;
          animate();
          return;
        };

        init();

        //setup
        foundationApi.subscribe(attrs.id, function(msg) {
          if(msg === 'show' || msg === 'open') {
            scope.show();
          } else if (msg === 'close' || msg === 'hide') {
            scope.hide();
          } else if (msg === 'toggle') {
            scope.toggle();
          }

          if (!scope.$root.$$phase) {
            scope.$apply();
          }

          return;
        });

        function animate() {
          //animate both overlay and dialog
          if(!scope.overlay) {
            element.css('background', 'transparent');
          }

          foundationApi.animate(element, scope.active, overlayIn, overlayOut);
          foundationApi.animate(dialog, scope.active, animationIn, animationOut);
        }

        function init() {
          if(scope.active) {
            scope.show();
          }
        }
      }
    }
  }

  ModalFactory.$inject = ['$http', '$templateCache', '$rootScope', '$compile', '$timeout', 'FoundationApi'];

  function ModalFactory($http, $templateCache, $rootScope, $compile, $timeout, foundationApi) {
    return modalFactory;

    function modalFactory(config) {
      var self = this, //for prototype functions
          container = angular.element(config.container || document.body),
          id = config.id || foundationApi.generateUuid(),
          attached = false,
          destroyed = false,
          html,
          element,
          scope
      ;

      var props = [
        'animationIn',
        'animationOut',
        'overlay',
        'overlayClose'
      ];

      if(config.templateUrl) {
        //get template
        $http.get(config.templateUrl, {
          cache: $templateCache
        }).then(function (response) {
          html = response.data;
          assembleDirective();
        });

      } else if(config.template) {
        //use provided template
        html = config.template;
        assembleDirective();
      }

      self.activate = activate;
      self.deactivate = deactivate;
      self.toggle = toggle;
      self.destroy = destroy;

      return {
        activate: activate,
        deactivate: deactivate,
        toggle: toggle,
        destroy: destroy
      };

      function checkStatus() {
        if(destroyed) {
          throw "Error: Modal was destroyed. Delete the object and create a new ModalFactory instance."
        }
      }

      function activate() {
        checkStatus();
        $timeout(function() {
          init(true);
          foundationApi.publish(id, 'show');
        }, 0, false);
      }

      function deactivate() {
        checkStatus();
        $timeout(function() {
          init(false);
          foundationApi.publish(id, 'hide');
        }, 0, false);
      }

      function toggle() {
        checkStatus();
        $timeout(function() {
          init(true);
          foundationApi.publish(id, 'toggle');
        }, 0, false);
      }

      function init(state) {
        if(!attached && html.length > 0) {
          var modalEl = container.append(element);

          scope.active = state;
          $compile(element)(scope);
          attached = true;
        }
      }

      function assembleDirective() {
        html = '<zf-modal id="' + id + '">' + html + '</zf-modal>';

        element = angular.element(html);

        scope = $rootScope.$new();

        for(var prop in props) {
          if(config[prop]) {
            element.attr(prop, config[prop]);
          }
        }
      }

      function destroy() {
        self.deactivate();
        setTimeout(function() {
          scope.$destroy();
          element.remove();
          destroyed = true;
        }, 3000);
      }

    }

  }

})();
<|endoftext|>


<commit_before>// Copyright 2014-2018, University of Colorado Boulder

/**
 * Node that represents a "beaker container" in the view.  A beaker container is a beaker that contains fluid, and in
 * which other objects can be placed, which generally displaces the fluid.
 *
 * See the header comments in the parent class for some important information about how this class is used on the
 * canvas.
 *
 * @author John Blanco
 * @author Jesse Greenberg
 * @author Andrew Adare
 */
define( function( require ) {
  'use strict';

  // modules
  var BeakerView = require( 'ENERGY_FORMS_AND_CHANGES/common/view/BeakerView' );
  var EFACConstants = require( 'ENERGY_FORMS_AND_CHANGES/common/EFACConstants' );
  var energyFormsAndChanges = require( 'ENERGY_FORMS_AND_CHANGES/energyFormsAndChanges' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Matrix3 = require( 'DOT/Matrix3' );
  var Shape = require( 'KITE/Shape' );
  var ThermalElementDragHandler = require( 'ENERGY_FORMS_AND_CHANGES/intro/view/ThermalElementDragHandler' );
  var Vector2 = require( 'DOT/Vector2' );

  // constants
  var BLOCK_PERSPECTIVE_ANGLE = EFACConstants.BLOCK_PERSPECTIVE_ANGLE;
  var BLOCK_PERSPECTIVE_EDGE_PROPORTION = EFACConstants.BLOCK_PERSPECTIVE_EDGE_PROPORTION;

  /**
   * @param {Beaker} beaker
   * @param {EFACIntroModel} model
   * @param {ModelViewTransform2} modelViewTransform
   * @param {function} constrainPosition
   * @param {Object} [options]
   */
  function BeakerContainerView( beaker, model, modelViewTransform, constrainPosition, options ) {

    var self = this;
    BeakerView.call( this, beaker, model.energyChunksVisibleProperty, modelViewTransform, options );

    // @private
    this.beaker = beaker;

    // variables for creating reusable shapes for doing the updates to the clipping areas
    var beakerRectangleWidthInView = -modelViewTransform.modelToViewDeltaY( beaker.width );
    var beakerRectangleHeightInView = -modelViewTransform.modelToViewDeltaY( beaker.height );

    // @private {Shape} - A shape that corresponds to the untransformed beaker content shape, used for the energy chunk
    // clip area.  It is extended a bit up and down for chunks that go to the rim of the beaker and for those that
    // go between the beaker and the heater/cooler.
    this.untransformedBeakerClipShape = new Shape.rect(
      -beakerRectangleWidthInView / 2,
      -beakerRectangleHeightInView * 1.5,
      beakerRectangleWidthInView,
      beakerRectangleHeightInView * 2
    );

    // @private - These values are used for calculating the clipping caused by the presence of blocks in the beaker.
    // They are computed once here so that they don't have to be recomputed every time the clipping shape is updated.
    // This assumes the blocks are all the same size and do not change size.
    this.blockWidthInView = modelViewTransform.modelToViewDeltaX( model.brick.width );
    this.blockHeightInView = -modelViewTransform.modelToViewDeltaY( model.brick.height );
    var perspectiveEdgeSize = this.blockWidthInView * BLOCK_PERSPECTIVE_EDGE_PROPORTION;
    this.forwardProjectionVector = new Vector2( -perspectiveEdgeSize / 2, 0 ).rotated( -BLOCK_PERSPECTIVE_ANGLE );

    // For each block that can go in the beaker we need to add a listener that will update the clipping area when that
    // block is moved.  The clipping area hides energy chunks that overlap with blocks, making it look much less
    // visually distracting, as though the energy chunks in the beaker are behind the blocks.
    model.blocks.forEach( function( block ) {
      block.positionProperty.link( function() {
        self.updateEnergyChunkClipArea( beaker, model.blocks, modelViewTransform );
      } );
    } );

    // update the clipping mask when the position of the beaker moves
    beaker.positionProperty.link( function() {
      self.updateEnergyChunkClipArea( beaker, model.blocks, modelViewTransform );
    } );

    this.grabNode.addInputListener( new ThermalElementDragHandler(
      beaker,
      this.grabNode,
      modelViewTransform,
      constrainPosition
    ) );
  }

  energyFormsAndChanges.register( 'BeakerContainerView', BeakerContainerView );

  return inherit( BeakerView, BeakerContainerView, {

    /**
     * Update the clipping area that is used to hide energy chunks that are in the beaker but occluded by blocks that
     * are ALSO in the beaker.
     * @param {Beaker} beaker
     * @param {Block[]} blocks
     * @param {ModelViewTransform2} modelViewTransform
     * @private
     */
    updateEnergyChunkClipArea: function( beaker, blocks, modelViewTransform ) {

      // The clip area is defined by an outer rectangle that is basically the entire beaker area and then some inner
      // rectangles for the blocks if they overlap with the beaker.  The inner pieces have to be drawn with the opposite
      // winding order from the outer ones in order to create the "hole" effect.  The outer shape extends above and
      // below the basic beaker model rectangle in order to prevent clipping of energy chunks that are positioned at
      // the upper and lower rim of the beaker and energy chunks moving between the beaker and the heater/cooler.
      var clipArea = this.untransformedBeakerClipShape.transformed(
        Matrix3.translationFromVector( modelViewTransform.modelToViewPosition( beaker.positionProperty.get() ) )
      );

      // add the "holes" in the clip mask that correspond to the blocks
      this.addProjectedBlocksToClipArea( blocks, clipArea, modelViewTransform );

      // set the updated clip area
      this.energyChunkRootNode.clipArea = clipArea;
    },

    /**
     * Add shapes corresponded to the provided blocks to the provide clip area shape, accounting for any 3D projection
     * used for the blocks.  This essentially creates "holes" in the clip mask preventing anything in the parent node
     * (generally energy chunks) from being rendered in the same place as the blocks.
     * @param {Block[]} blocks
     * @param {Shape} clipAreaShape
     * @param {ModelViewTransform2} modelViewTransform
     * @private
     */
    addProjectedBlocksToClipArea: function( blocks, clipAreaShape, modelViewTransform ) {

      // Make sure there aren't more blocks than this method can deal with.  There are some assumptions built in that
      // would not work for more than two blocks, see the code and comments below for details.
      assert && assert( blocks.length <= 2, 'number of blocks exceeds what this method is designed to handle' );

      // index for C-style loops, which are used to maximize performance
      var i;

      // hoisted block variable
      var block;

      // if neither of the blocks is in the beaker then there are no "holes" to add
      var blocksInBeaker = [];
      for ( i = 0; i < blocks.length; i++ ) {
        block = blocks[ i ];
        if ( this.beaker.beakerBounds.containsPoint( block.positionProperty.value ) ||
             this.beaker.topSurface.elementOnSurfaceProperty.value === block ) {
          blocksInBeaker.push( block );
          break;
        }
      }
      if ( !blocksInBeaker.length === 0 ) {

        // nothing to do, bail
        return;
      }

      // use the bounds of the shape for faster tests, assumes that it is rectangular
      var chipAreaShapeBounds = clipAreaShape.bounds;

      // determine whether the blocks are stacked upon each other
      var blocksAreStacked = false;
      if ( blocksInBeaker.length === 2 ) {
        blocksAreStacked = blocksInBeaker[ 0 ].isStackedUpon( blocksInBeaker[ 1 ] ) ||
                           blocksInBeaker[ 1 ].isStackedUpon( blocksInBeaker[ 0 ] );
      }

      if ( blocksAreStacked ) {

        // When the blocks are stacked, draw a single shape the encompasses both.  This is necessary because if the
        // shapes are drawn separately and the overlap in the clipping area, a space is created where the energy chunks
        // aren't occluded.
        var bottomBlock;
        if ( blocksInBeaker[ 0 ].isStackedUpon( blocksInBeaker[ 1 ] ) ) {
          bottomBlock = blocksInBeaker[ 1 ];
        }
        else {
          bottomBlock = blocksInBeaker[ 0 ];
        }

        var bottomBlockPositionInView = modelViewTransform.modelToViewPosition( bottomBlock.positionProperty.value );

        if ( chipAreaShapeBounds.containsPoint( bottomBlockPositionInView ) ) {
          clipAreaShape.moveTo(
            bottomBlockPositionInView.x - this.blockWidthInView / 2 + this.forwardProjectionVector.x,
            bottomBlockPositionInView.y + this.forwardProjectionVector.y
          );
          clipAreaShape.lineToRelative( this.blockWidthInView, 0 );
          clipAreaShape.lineToRelative( -this.forwardProjectionVector.x * 2, -this.forwardProjectionVector.y * 2 );
          clipAreaShape.lineToRelative( 0, -this.blockHeightInView * 2 );
          clipAreaShape.lineToRelative( -this.blockWidthInView, 0 );
          clipAreaShape.lineToRelative( this.forwardProjectionVector.x * 2, this.forwardProjectionVector.y * 2 );
          clipAreaShape.lineToRelative( 0, this.blockHeightInView * 2 );
        }
      }
      else {

        // C-style loop for best performance
        for ( i = 0; i < blocksInBeaker.length; i++ ) {
          block = blocksInBeaker[ i ];
          var blockPositionInView = modelViewTransform.modelToViewPosition( block.positionProperty.value );

          // The following code makes some assumptions that are known to be true for the EFAC simulation but which
          // wouldn't necessarily true for a generalized version of this.  Those assumptions are that the provided shape
          // is rectangular and that the position of the block is the bottom center.
          if ( chipAreaShapeBounds.containsPoint( blockPositionInView ) ) {
            clipAreaShape.moveTo(
              blockPositionInView.x - this.blockWidthInView / 2 + this.forwardProjectionVector.x,
              blockPositionInView.y + this.forwardProjectionVector.y
            );
            clipAreaShape.lineToRelative( this.blockWidthInView, 0 );
            clipAreaShape.lineToRelative( -this.forwardProjectionVector.x * 2, -this.forwardProjectionVector.y * 2 );
            clipAreaShape.lineToRelative( 0, -this.blockHeightInView );
            clipAreaShape.lineToRelative( -this.blockWidthInView, 0 );
            clipAreaShape.lineToRelative( this.forwardProjectionVector.x * 2, this.forwardProjectionVector.y * 2 );
            clipAreaShape.lineToRelative( 0, this.blockHeightInView );
          }
        }
      }
    }
  } );
} );

<commit_msg>Extend beaker clipping area to accommodate chunks coming down from the air, see https://github.com/phetsims/energy-forms-and-changes/issues/146<commit_after>// Copyright 2014-2018, University of Colorado Boulder

/**
 * Node that represents a "beaker container" in the view.  A beaker container is a beaker that contains fluid, and in
 * which other objects can be placed, which generally displaces the fluid.
 *
 * See the header comments in the parent class for some important information about how this class is used on the
 * canvas.
 *
 * @author John Blanco
 * @author Jesse Greenberg
 * @author Andrew Adare
 */
define( function( require ) {
  'use strict';

  // modules
  var BeakerView = require( 'ENERGY_FORMS_AND_CHANGES/common/view/BeakerView' );
  var EFACConstants = require( 'ENERGY_FORMS_AND_CHANGES/common/EFACConstants' );
  var EFACQueryParameters = require( 'ENERGY_FORMS_AND_CHANGES/common/EFACQueryParameters' );
  var energyFormsAndChanges = require( 'ENERGY_FORMS_AND_CHANGES/energyFormsAndChanges' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Matrix3 = require( 'DOT/Matrix3' );
  var Path = require( 'SCENERY/nodes/Path' );
  var Shape = require( 'KITE/Shape' );
  var ThermalElementDragHandler = require( 'ENERGY_FORMS_AND_CHANGES/intro/view/ThermalElementDragHandler' );
  var Vector2 = require( 'DOT/Vector2' );

  // constants
  var BLOCK_PERSPECTIVE_ANGLE = EFACConstants.BLOCK_PERSPECTIVE_ANGLE;
  var BLOCK_PERSPECTIVE_EDGE_PROPORTION = EFACConstants.BLOCK_PERSPECTIVE_EDGE_PROPORTION;

  /**
   * @param {Beaker} beaker
   * @param {EFACIntroModel} model
   * @param {ModelViewTransform2} modelViewTransform
   * @param {function} constrainPosition
   * @param {Object} [options]
   */
  function BeakerContainerView( beaker, model, modelViewTransform, constrainPosition, options ) {

    var self = this;
    BeakerView.call( this, beaker, model.energyChunksVisibleProperty, modelViewTransform, options );

    // @private
    this.beaker = beaker;

    // variables for creating reusable shapes for doing the updates to the clipping areas
    var beakerRectangleWidthInView = -modelViewTransform.modelToViewDeltaY( beaker.width );
    var beakerRectangleHeightInView = -modelViewTransform.modelToViewDeltaY( beaker.height );

    // @private {Shape} - A shape that corresponds to the untransformed beaker content shape, used for the energy chunk
    // clip area.  It is extended a ways up for chunks that come from the top of the air and extends down for those that
    // go between the beaker and the heater/cooler.
    this.untransformedBeakerClipShape = new Shape.rect(
      -beakerRectangleWidthInView / 2,
      -beakerRectangleHeightInView * 5,
      beakerRectangleWidthInView,
      beakerRectangleHeightInView * 5.5
    );

    // @private - These values are used for calculating the clipping caused by the presence of blocks in the beaker.
    // They are computed once here so that they don't have to be recomputed every time the clipping shape is updated.
    // This assumes the blocks are all the same size and do not change size.
    this.blockWidthInView = modelViewTransform.modelToViewDeltaX( model.brick.width );
    this.blockHeightInView = -modelViewTransform.modelToViewDeltaY( model.brick.height );
    var perspectiveEdgeSize = this.blockWidthInView * BLOCK_PERSPECTIVE_EDGE_PROPORTION;
    this.forwardProjectionVector = new Vector2( -perspectiveEdgeSize / 2, 0 ).rotated( -BLOCK_PERSPECTIVE_ANGLE );

    if ( EFACQueryParameters.showHelperShapes ) {
      this.clipAreaHelperNode = new Path( this.untransformedBeakerClipShape, {
        fill: 'rgba(255, 0, 0, 0.2)'
      } );
      this.energyChunkRootNode.addChild( this.clipAreaHelperNode );
    }

    // For each block that can go in the beaker we need to add a listener that will update the clipping area when that
    // block is moved.  The clipping area hides energy chunks that overlap with blocks, making it look much less
    // visually distracting, as though the energy chunks in the beaker are behind the blocks.
    model.blocks.forEach( function( block ) {
      block.positionProperty.link( function() {
        self.updateEnergyChunkClipArea( beaker, model.blocks, modelViewTransform );
      } );
    } );

    // update the clipping mask when the position of the beaker moves
    beaker.positionProperty.link( function() {
      self.updateEnergyChunkClipArea( beaker, model.blocks, modelViewTransform );
    } );

    this.grabNode.addInputListener( new ThermalElementDragHandler(
      beaker,
      this.grabNode,
      modelViewTransform,
      constrainPosition
    ) );
  }

  energyFormsAndChanges.register( 'BeakerContainerView', BeakerContainerView );

  return inherit( BeakerView, BeakerContainerView, {

    /**
     * Update the clipping area that is used to hide energy chunks that are in the beaker but occluded by blocks that
     * are ALSO in the beaker.
     * @param {Beaker} beaker
     * @param {Block[]} blocks
     * @param {ModelViewTransform2} modelViewTransform
     * @private
     */
    updateEnergyChunkClipArea: function( beaker, blocks, modelViewTransform ) {

      // The clip area is defined by an outer rectangle that is basically the entire beaker area and then some inner
      // rectangles for the blocks if they overlap with the beaker.  The inner pieces have to be drawn with the opposite
      // winding order from the outer ones in order to create the "hole" effect.  The outer shape extends above and
      // below the basic beaker model rectangle in order to prevent clipping of energy chunks that are positioned at
      // the upper and lower rim of the beaker and energy chunks moving between the beaker and the heater/cooler.
      var clipArea = this.untransformedBeakerClipShape.transformed(
        Matrix3.translationFromVector( modelViewTransform.modelToViewPosition( beaker.positionProperty.get() ) )
      );

      // add the "holes" in the clip mask that correspond to the blocks
      this.addProjectedBlocksToClipArea( blocks, clipArea, modelViewTransform );

      // set the updated clip area
      this.energyChunkRootNode.clipArea = clipArea;
      // this.energyChunkRootNode.clipArea = null;

      if ( this.clipAreaHelperNode ) {
        this.clipAreaHelperNode.setShape( clipArea );
      }
    },

    /**
     * Add shapes corresponded to the provided blocks to the provide clip area shape, accounting for any 3D projection
     * used for the blocks.  This essentially creates "holes" in the clip mask preventing anything in the parent node
     * (generally energy chunks) from being rendered in the same place as the blocks.
     * @param {Block[]} blocks
     * @param {Shape} clipAreaShape
     * @param {ModelViewTransform2} modelViewTransform
     * @private
     */
    addProjectedBlocksToClipArea: function( blocks, clipAreaShape, modelViewTransform ) {

      // Make sure there aren't more blocks than this method can deal with.  There are some assumptions built in that
      // would not work for more than two blocks, see the code and comments below for details.
      assert && assert( blocks.length <= 2, 'number of blocks exceeds what this method is designed to handle' );

      // index for C-style loops, which are used to maximize performance
      var i;

      // hoisted block variable
      var block;

      // if neither of the blocks is in the beaker then there are no "holes" to add
      var blocksInBeaker = [];
      for ( i = 0; i < blocks.length; i++ ) {
        block = blocks[ i ];
        if ( this.beaker.beakerBounds.containsPoint( block.positionProperty.value ) ||
             this.beaker.topSurface.elementOnSurfaceProperty.value === block ) {
          blocksInBeaker.push( block );
          break;
        }
      }
      if ( !blocksInBeaker.length === 0 ) {

        // nothing to do, bail
        return;
      }

      // use the bounds of the shape for faster tests, assumes that it is rectangular
      var chipAreaShapeBounds = clipAreaShape.bounds;

      // determine whether the blocks are stacked upon each other
      var blocksAreStacked = false;
      if ( blocksInBeaker.length === 2 ) {
        blocksAreStacked = blocksInBeaker[ 0 ].isStackedUpon( blocksInBeaker[ 1 ] ) ||
                           blocksInBeaker[ 1 ].isStackedUpon( blocksInBeaker[ 0 ] );
      }

      if ( blocksAreStacked ) {

        // When the blocks are stacked, draw a single shape the encompasses both.  This is necessary because if the
        // shapes are drawn separately and the overlap in the clipping area, a space is created where the energy chunks
        // aren't occluded.
        var bottomBlock;
        if ( blocksInBeaker[ 0 ].isStackedUpon( blocksInBeaker[ 1 ] ) ) {
          bottomBlock = blocksInBeaker[ 1 ];
        }
        else {
          bottomBlock = blocksInBeaker[ 0 ];
        }

        var bottomBlockPositionInView = modelViewTransform.modelToViewPosition( bottomBlock.positionProperty.value );

        if ( chipAreaShapeBounds.containsPoint( bottomBlockPositionInView ) ) {
          clipAreaShape.moveTo(
            bottomBlockPositionInView.x - this.blockWidthInView / 2 + this.forwardProjectionVector.x,
            bottomBlockPositionInView.y + this.forwardProjectionVector.y
          );
          clipAreaShape.lineToRelative( this.blockWidthInView, 0 );
          clipAreaShape.lineToRelative( -this.forwardProjectionVector.x * 2, -this.forwardProjectionVector.y * 2 );
          clipAreaShape.lineToRelative( 0, -this.blockHeightInView * 2 );
          clipAreaShape.lineToRelative( -this.blockWidthInView, 0 );
          clipAreaShape.lineToRelative( this.forwardProjectionVector.x * 2, this.forwardProjectionVector.y * 2 );
          clipAreaShape.lineToRelative( 0, this.blockHeightInView * 2 );
        }
      }
      else {

        // C-style loop for best performance
        for ( i = 0; i < blocksInBeaker.length; i++ ) {
          block = blocksInBeaker[ i ];
          var blockPositionInView = modelViewTransform.modelToViewPosition( block.positionProperty.value );

          // The following code makes some assumptions that are known to be true for the EFAC simulation but which
          // wouldn't necessarily true for a generalized version of this.  Those assumptions are that the provided shape
          // is rectangular and that the position of the block is the bottom center.
          if ( chipAreaShapeBounds.containsPoint( blockPositionInView ) ) {
            clipAreaShape.moveTo(
              blockPositionInView.x - this.blockWidthInView / 2 + this.forwardProjectionVector.x,
              blockPositionInView.y + this.forwardProjectionVector.y
            );
            clipAreaShape.lineToRelative( this.blockWidthInView, 0 );
            clipAreaShape.lineToRelative( -this.forwardProjectionVector.x * 2, -this.forwardProjectionVector.y * 2 );
            clipAreaShape.lineToRelative( 0, -this.blockHeightInView );
            clipAreaShape.lineToRelative( -this.blockWidthInView, 0 );
            clipAreaShape.lineToRelative( this.forwardProjectionVector.x * 2, this.forwardProjectionVector.y * 2 );
            clipAreaShape.lineToRelative( 0, this.blockHeightInView );
          }
        }
      }
    }
  } );
} );

<|endoftext|>


<commit_before>/*
 *  Bootstrap TouchSpin - v3.1.1
 *  A mobile and touch friendly input spinner component for Bootstrap 3.
 *  http://www.virtuosoft.eu/code/bootstrap-touchspin/
 *
 *  Made by István Ujj-Mészáros
 *  Under Apache License v2.0 License
 */
!function(a){"use strict";function b(a,b){return a+".touchspin_"+b}function c(c,d){return a.map(c,function(a){return b(a,d)})}var d=0;a.fn.TouchSpin=function(b){if("destroy"===b)return void this.each(function(){var b=a(this),d=b.data();a(document).off(c(["mouseup","touchend","touchcancel","mousemove","touchmove","scroll","scrollstart"],d.spinnerid).join(" "))});var e={min:0,max:100,initval:"",replacementval:"",step:1,decimals:0,stepinterval:100,forcestepdivisibility:"round",stepintervaldelay:500,verticalbuttons:!1,verticalupclass:"glyphicon glyphicon-chevron-up",verticaldownclass:"glyphicon glyphicon-chevron-down",prefix:"",postfix:"",prefix_extraclass:"",postfix_extraclass:"",booster:!0,boostat:10,maxboostedstep:!1,mousewheel:!0,buttondown_class:"btn btn-default",buttonup_class:"btn btn-default",buttondown_txt:"-",buttonup_txt:"+"},f={min:"min",max:"max",initval:"init-val",replacementval:"replacement-val",step:"step",decimals:"decimals",stepinterval:"step-interval",verticalbuttons:"vertical-buttons",verticalupclass:"vertical-up-class",verticaldownclass:"vertical-down-class",forcestepdivisibility:"force-step-divisibility",stepintervaldelay:"step-interval-delay",prefix:"prefix",postfix:"postfix",prefix_extraclass:"prefix-extra-class",postfix_extraclass:"postfix-extra-class",booster:"booster",boostat:"boostat",maxboostedstep:"max-boosted-step",mousewheel:"mouse-wheel",buttondown_class:"button-down-class",buttonup_class:"button-up-class",buttondown_txt:"button-down-txt",buttonup_txt:"button-up-txt"};return this.each(function(){function g(){if(!J.data("alreadyinitialized")){if(J.data("alreadyinitialized",!0),d+=1,J.data("spinnerid",d),!J.is("input"))return void console.log("Must be an input.");j(),h(),u(),m(),p(),q(),r(),s(),D.input.css("display","block")}}function h(){""!==B.initval&&""===J.val()&&J.val(B.initval)}function i(a){l(a),u();var b=D.input.val();""!==b&&(b=Number(D.input.val()),D.input.val(b.toFixed(B.decimals)))}function j(){B=a.extend({},e,K,k(),b)}function k(){var b={};return a.each(f,function(a,c){var d="bts-"+c;J.is("[data-"+d+"]")&&(b[a]=J.data(d))}),b}function l(b){B=a.extend({},B,b)}function m(){var a=J.val(),b=J.parent();""!==a&&(a=Number(a).toFixed(B.decimals)),J.data("initvalue",a).val(a),J.addClass("form-control"),b.hasClass("input-group")?n(b):o()}function n(b){b.addClass("bootstrap-touchspin");var c,d,e=J.prev(),f=J.next(),g=''+B.prefix+"",h=''+B.postfix+"";e.hasClass("input-group-btn")?(c='<button class="'+B.buttondown_class+' bootstrap-touchspin-down" type="button">'+B.buttondown_txt+"</button>",e.append(c)):(c='<button class="'+B.buttondown_class+' bootstrap-touchspin-down" type="button">'+B.buttondown_txt+"</button>",a(c).insertBefore(J)),f.hasClass("input-group-btn")?(d='<button class="'+B.buttonup_class+' bootstrap-touchspin-up" type="button">'+B.buttonup_txt+"</button>",f.prepend(d)):(d='<button class="'+B.buttonup_class+' bootstrap-touchspin-up" type="button">'+B.buttonup_txt+"</button>",a(d).insertAfter(J)),a(g).insertBefore(J),a(h).insertAfter(J),C=b}function o(){var b;b=B.verticalbuttons?'
'+B.prefix+''+B.postfix+'<button class="'+B.buttondown_class+' bootstrap-touchspin-up" type="button"></button><button class="'+B.buttonup_class+' bootstrap-touchspin-down" type="button"></button>
':'
<button class="'+B.buttondown_class+' bootstrap-touchspin-down" type="button">'+B.buttondown_txt+'</button>'+B.prefix+''+B.postfix+'<button class="'+B.buttonup_class+' bootstrap-touchspin-up" type="button">'+B.buttonup_txt+"</button>
",C=a(b).insertBefore(J),a(".bootstrap-touchspin-prefix",C).after(J),J.hasClass("input-sm")?C.addClass("input-group-sm"):J.hasClass("input-lg")&&C.addClass("input-group-lg")}function p(){D={down:a(".bootstrap-touchspin-down",C),up:a(".bootstrap-touchspin-up",C),input:a("input",C),prefix:a(".bootstrap-touchspin-prefix",C).addClass(B.prefix_extraclass),postfix:a(".bootstrap-touchspin-postfix",C).addClass(B.postfix_extraclass)}}function q(){""===B.prefix&&D.prefix.hide(),""===B.postfix&&D.postfix.hide()}function r(){J.on("keydown",function(a){var b=a.keyCode||a.which;38===b?("up"!==M&&(w(),z()),a.preventDefault()):40===b&&("down"!==M&&(x(),y()),a.preventDefault())}),J.on("keyup",function(a){var b=a.keyCode||a.which;38===b?A():40===b&&A()}),J.on("blur",function(){u()}),D.down.on("keydown",function(a){var b=a.keyCode||a.which;(32===b||13===b)&&("down"!==M&&(x(),y()),a.preventDefault())}),D.down.on("keyup",function(a){var b=a.keyCode||a.which;(32===b||13===b)&&A()}),D.up.on("keydown",function(a){var b=a.keyCode||a.which;(32===b||13===b)&&("up"!==M&&(w(),z()),a.preventDefault())}),D.up.on("keyup",function(a){var b=a.keyCode||a.which;(32===b||13===b)&&A()}),D.down.on("mousedown.touchspin",function(a){D.down.off("touchstart.touchspin"),J.is(":disabled")||(x(),y(),a.preventDefault(),a.stopPropagation())}),D.down.on("touchstart.touchspin",function(a){D.down.off("mousedown.touchspin"),J.is(":disabled")||(x(),y(),a.preventDefault(),a.stopPropagation())}),D.up.on("mousedown.touchspin",function(a){D.up.off("touchstart.touchspin"),J.is(":disabled")||(w(),z(),a.preventDefault(),a.stopPropagation())}),D.up.on("touchstart.touchspin",function(a){D.up.off("mousedown.touchspin"),J.is(":disabled")||(w(),z(),a.preventDefault(),a.stopPropagation())}),D.up.on("mouseout touchleave touchend touchcancel",function(a){M&&(a.stopPropagation(),A())}),D.down.on("mouseout touchleave touchend touchcancel",function(a){M&&(a.stopPropagation(),A())}),D.down.on("mousemove touchmove",function(a){M&&(a.stopPropagation(),a.preventDefault())}),D.up.on("mousemove touchmove",function(a){M&&(a.stopPropagation(),a.preventDefault())}),a(document).on(c(["mouseup","touchend","touchcancel"],d).join(" "),function(a){M&&(a.preventDefault(),A())}),a(document).on(c(["mousemove","touchmove","scroll","scrollstart"],d).join(" "),function(a){M&&(a.preventDefault(),A())}),J.on("mousewheel DOMMouseScroll",function(a){if(B.mousewheel&&J.is(":focus")){var b=a.originalEvent.wheelDelta||-a.originalEvent.deltaY||-a.originalEvent.detail;a.stopPropagation(),a.preventDefault(),0>b?x():w()}})}function s(){J.on("touchspin.uponce",function(){A(),w()}),J.on("touchspin.downonce",function(){A(),x()}),J.on("touchspin.startupspin",function(){z()}),J.on("touchspin.startdownspin",function(){y()}),J.on("touchspin.stopspin",function(){A()}),J.on("touchspin.updatesettings",function(a,b){i(b)})}function t(a){switch(B.forcestepdivisibility){case"round":return(Math.round(a/B.step)*B.step).toFixed(B.decimals);case"floor":return(Math.floor(a/B.step)*B.step).toFixed(B.decimals);case"ceil":return(Math.ceil(a/B.step)*B.step).toFixed(B.decimals);default:return a}}function u(){var a,b,c;return a=J.val(),""===a?void(""!==B.replacementval&&(J.val(B.replacementval),J.trigger("change"))):void(B.decimals>0&&"."===a||(b=parseFloat(a),isNaN(b)&&(b=""!==B.replacementval?B.replacementval:0),c=b,b.toString()!==a&&(c=b),bB.max&&(c=B.max),c=t(c),Number(a).toString()!==c.toString()&&(J.val(c),J.trigger("change"))))}function v(){if(B.booster){var a=Math.pow(2,Math.floor(L/B.boostat))*B.step;return B.maxboostedstep&&a>B.maxboostedstep&&(a=B.maxboostedstep,E=Math.round(E/a)*a),Math.max(B.step,a)}return B.step}function w(){u(),E=parseFloat(D.input.val()),isNaN(E)&&(E=0);var a=E,b=v();E+=b,E>B.max&&(E=B.max,J.trigger("touchspin.on.max"),A()),D.input.val(Number(E).toFixed(B.decimals)),a!==E&&J.trigger("change")}function x(){u(),E=parseFloat(D.input.val()),isNaN(E)&&(E=0);var a=E,b=v();E-=b,E<B.min&&(E=B.min,J.trigger("touchspin.on.min"),A()),D.input.val(E.toFixed(B.decimals)),a!==E&&J.trigger("change")}function y(){A(),L=0,M="down",J.trigger("touchspin.on.startspin"),J.trigger("touchspin.on.startdownspin"),H=setTimeout(function(){F=setInterval(function(){L++,x()},B.stepinterval)},B.stepintervaldelay)}function z(){A(),L=0,M="up",J.trigger("touchspin.on.startspin"),J.trigger("touchspin.on.startupspin"),I=setTimeout(function(){G=setInterval(function(){L++,w()},B.stepinterval)},B.stepintervaldelay)}function A(){switch(clearTimeout(H),clearTimeout(I),clearInterval(F),clearInterval(G),M){case"up":J.trigger("touchspin.on.stopupspin"),J.trigger("touchspin.on.stopspin");break;case"down":J.trigger("touchspin.on.stopdownspin"),J.trigger("touchspin.on.stopspin")}L=0,M=!1}var B,C,D,E,F,G,H,I,J=a(this),K=J.data(),L=0,M=!1;g()})}}(jQuery);<commit_msg>cleanup<commit_after><|endoftext|>


<commit_before>/*
    enable test container with redis as default, but it may take time.
    NOTE THAT you can also directly connect with the local redis with two ways, i.e.,
    1) the command
        docker run -d -v <absolute path to dump.rdb>:/data/dump.rdb -p 6379:6379 redis
    2) docker-compose, i.e., test-redis-db.yml
 */
const { DockerComposeEnvironment , Wait } = require("testcontainers");

(async ()=>{

    const environment = await new DockerComposeEnvironment(__dirname, "test-redis-db.yml")
        .withWaitStrategy("redis_1", Wait.forLogMessage("Ready to accept connections"))
        .up();

    const container = await environment.getContainer("redis_1");
    process.env.REDIS_PORT = container.getMappedPort(50000);

    console.log(process.env.REDIS_PORT);
    const AppController = require("./app-driver");
    const em = require("evomaster-client-js");
    const controller = new em.EMController(new AppController());

    port = process.env.EM_PORT || 40100;
    controller.setPort(port);
    controller.startTheControllerServer();
})();

// const AppController = require("./app-driver");
// const em = require("evomaster-client-js");
// const controller = new em.EMController(new AppController());
//
// port = process.env.EM_PORT || 40100;
// controller.setPort(port);
// controller.startTheControllerServer();
<commit_msg>update disease em<commit_after>(async ()=>{
    const AppController = require("./app-driver");
    const em = require("evomaster-client-js");
    const app = new AppController();
    const controller = new em.EMController(app);

    await app.setupForGeneratedTest();

    port = process.env.EM_PORT || 40100;
    controller.setPort(port);
    controller.startTheControllerServer();
})();
<|endoftext|>