<commit_before>//
//  Deck_Handler.js
//  unpublished/marketplace/
//
//  Created by Je'Don (ROC) Carter on 9/14/2017
//  Copyright 2017 High Fidelity, Inc.
//
//  Server script that controls some card visibility behavior by swapping textures and creating cards
//
//  Distributed under the Apache License, Version 7.1.
//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function() {
    var _this = this;
    //channels
    var cardChannel;
    //holds ID of all cards
    var cardIDs = [];
    //holds number of the last card grabbed
    var lastCard;
    //holds names of cards for shuffle
    var cards = [];
    var deckLocation;
    var deckRotation;

    _this.preload = function(entityID) {
        print("Loading Deck Handler script.");
        _this.entityID = entityID;
        //set last card number
        lastCard = 52;
        //position of deck handler
        deckLocation = Entities.getEntityProperties(entityID).position;
        deckRotation = Entities.getEntityProperties(entityID).rotation;
        for (i = 1; i <= 52; i++) {
            cards.push(i);
        }
        cards = Shuffle(cards);
        //create a single card
        var cardProperties = {
            "type": "Model",
            "lifetime": -1,
            "dynamic": true,
            "damping": 0.98,
            "angularDamping": 0.98,
            "gravity": {
                x: 0,
                y: -4,
                z: 0
            },
            "textures": '{ "HiddenCardFile": "https://hifi-content.s3.amazonaws.com/jedon/Game_Creater_Toolkit/DeckOfCards/DeckOfCardsTexture/CARD_' + cards[51] + '.jpg"}',
            "rotation": Quat.multiply(deckRotation, Quat.angleAxis(90, {x: 1, y: 0, z: 0})),
            "dimensions": {
                x: .07,
                y: .12,
                z: .006
            },
            userData: JSON.stringify({
                grabbableKey: {
                    grabbable: true,
                    ignoreIK: false
                },
                "held": false,
                "card": true,
                "deckHandlerID": entityID
            }),
            "position": {
                x: deckLocation.x,
                y: deckLocation.y + (.053 + .003),
                z: deckLocation.z
            },
            "collisionless": false,
            "collidesWith": "static,dynamic",
            "modelURL": "https://hifi-content.s3.amazonaws.com/jedon/Game_Creater_Toolkit/DeckOfCards/DeckOfCardsAssets/master_card.fbx",
            name: "CARD_" + cards[51],
            shapeType: "box",
            "script": Script.resolvePath("./Card.js") + "?" + Date.now()
        };
        cardIDs.push(Entities.addEntity(cardProperties));
        //make the deck (used for decoration, makes them think cards are coming from the deck)
        var cardDeckProperties = {
            "type": "Model",
            "lifetime": -1,
            "dynamic": false,
            "textures": '{ "HiddenCardFile": "https://hifi-content.s3.amazonaws.com/jedon/Game_Creater_Toolkit/DeckOfCards/DeckOfCardsTexture/CARD_0.jpg"}',
            "rotation": Quat.multiply(deckRotation, Quat.angleAxis(90, {x: 1, y: 0, z: 0})),
            "dimensions": {
                x: .07,
                y: .12,
                z: .1
            },
            userData: JSON.stringify({
                grabbableKey: {
                    grabbable: false,
                    ignoreIK: false
                },
            }),
            "position": deckLocation,
            "collisionless": false,
            "collidesWith": "dynamic",
            "modelURL": "https://hifi-content.s3.amazonaws.com/jedon/Game_Creater_Toolkit/DeckOfCards/DeckOfCardsAssets/master_card.fbx",
            name: "CARD_DECK",
            shapeType: "box",
        };
        Entities.addEntity(cardDeckProperties);
        //find right of deck
        var direction = Quat.getRight(deckRotation);
        var distance = .7;
        var resetPosition = Vec3.sum(deckLocation, Vec3.multiply(direction, distance));
        //Make reset button
        var resetButtonProperties = {
            "type": "Model",
            "lifetime": -1,
            "dynamic": false,
            "rotation": Quat.multiply(deckRotation, Quat.angleAxis(90, {x: 1, y: 0, z: 0})),
            "dimensions": {
                x: .07,
                y: .12,
                z: .1
            },
            userData: JSON.stringify({
                grabbableKey: {
                    grabbable: false,
                    ignoreIK: false,
                    wantsTrigger: true
                },
                "deckHandlerID": entityID
            }),
            "position": resetPosition,
            "collisionless": false,
            "collidesWith": "",
            "modelURL": "https://hifi-content.s3.amazonaws.com/jedon/Assets/Basic_Cube.fbx",
            name: "CARD_Reset_Button",
            shapeType: "box",
            "script": Script.resolvePath("./Reset_Button.js") + "?" + Date.now()
        };
        Entities.addEntity(resetButtonProperties);
        //get all channels
        cardChannel = "card-channel-".concat(entityID);
        resetChannel = "reset-channel-".concat(entityID);
        Messages.subscribe(cardChannel);
        Messages.subscribe(resetChannel);
        Messages.messageReceived.connect(_this, _this.onReceivedMessage);
    };

    _this.onReceivedMessage = function(channel, message, senderID) {
        if (channel == cardChannel) {
            showOrHideCard(message);
        } else if (channel == resetChannel) {
            resetDeck();
        }
    };

    function showOrHideCard(message) {
        var data = JSON.parse(message);
        if (data[0] == true) {
            var cardName = Entities.getEntityProperties(data[1]).name;
            var usability = {
               textures: '{ "HiddenCardFile": "https://hifi-content.s3.amazonaws.com/jedon/Game_Creater_Toolkit/DeckOfCards/DeckOfCardsTexture/' + cardName + '.jpg"}',
            };
            Entities.editEntity(data[1], usability);
        } else if (data[0] == false) {
            var showChannel = "show-channel".concat(data[1]);
            var dataToPassBack = [data[2], data[3], data[4]];
            Messages.sendMessage(showChannel, JSON.stringify(dataToPassBack));
            var cardName = Entities.getEntityProperties(data[1]).name;
            if ((("CARD_" + cards[lastCard - 1]) == cardName) && (lastCard != 0)) {
                --lastCard;
                //give them time to pickup the card before you spawn a new one
                Script.setTimeout( function() {
                    var cardProperties = {
                        "type": "Model",
                        "lifetime": -1,
                        "dynamic": true,
                        "damping": 0.98,
                        "angularDamping": 0.98,
                        "gravity": {
                            x: 0,
                            y: -4,
                            z: 0
                        },
                        "textures": '{ "HiddenCardFile": "https://hifi-content.s3.amazonaws.com/jedon/Game_Creater_Toolkit/DeckOfCards/DeckOfCardsTexture/CARD_' + cards[lastCard - 1] + '.jpg"}',
                        "rotation": Quat.multiply(deckRotation, Quat.angleAxis(90, {x: 1, y: 0, z: 0})),
                        "dimensions": {
                            x: .07,
                            y: .12,
                            z: .006
                        },
                        userData: JSON.stringify({
                            grabbableKey: {
                                grabbable: true,
                                ignoreIK: false
                            },
                            "held": false,
                            "card": true,
                            "deckHandlerID": _this.entityID
                        }),
                        "position": {
                            x: deckLocation.x,
                            y: deckLocation.y + (.053 + .003),
                            z: deckLocation.z
                        },
                        "collisionless": false,
                        "collidesWith": "static,dynamic",
                        "modelURL": "https://hifi-content.s3.amazonaws.com/jedon/Game_Creater_Toolkit/DeckOfCards/DeckOfCardsAssets/master_card.fbx",
                        name: "CARD_" + cards[lastCard - 1],
                        shapeType: "box",
                        "script": Script.resolvePath("./Card.js") + "?" + Date.now()
                    };
                    cardIDs.push(Entities.addEntity(cardProperties));
                }, 1000);
            }
        }

    }

    function Shuffle(array) {
        var currentIndex = array.length, temporaryValue, randomIndex;
        // While there remain elements to shuffle...
        while (0 !== currentIndex) {
        // Pick a remaining element...
            randomIndex = Math.floor(Math.random() * currentIndex);
            currentIndex -= 1;

            // And swap it with the current element.
            temporaryValue = array[currentIndex];
            array[currentIndex] = array[randomIndex];
            array[randomIndex] = temporaryValue;
        }
        return array;
    }

    function resetDeck() {
        //shuffle deck
        cards = Shuffle(cards);
        //delete all cards
        for (i = 0; i < 52; i++) {
            Entities.deleteEntity(cardIDs[i]);
        }
        cardIDs = [];
        //create a single card
        var cardProperties = {
            "type": "Model",
            "lifetime": -1,
            "dynamic": true,
            "damping": 0.98,
            "angularDamping": 0.98,
            "gravity": {
                x: 0,
                y: -4,
                z: 0
            },
            "textures": '{ "HiddenCardFile": "https://hifi-content.s3.amazonaws.com/jedon/Game_Creater_Toolkit/DeckOfCards/DeckOfCardsTexture/CARD_' + cards[51] + '.jpg"}',
            "rotation": Quat.multiply(deckRotation, Quat.angleAxis(90, {x: 1, y: 0, z: 0})),
            "dimensions": {
                x: .07,
                y: .12,
                z: .006
            },
            userData: JSON.stringify({
                grabbableKey: {
                    grabbable: true,
                    ignoreIK: false
                },
                "held": false,
                "card": true,
                "deckHandlerID": _this.entityID
            }),
            "position": {
                x: deckLocation.x,
                y: deckLocation.y + (.053 + .003),
                z: deckLocation.z
            },
            "collisionless": false,
            "collidesWith": "static,dynamic",
            "modelURL": "https://hifi-content.s3.amazonaws.com/jedon/Game_Creater_Toolkit/DeckOfCards/DeckOfCardsAssets/master_card.fbx",
            name: "CARD_" + cards[51],
            shapeType: "box",
            "script": Script.resolvePath("./Card.js") + "?" + Date.now()
        };
        cardIDs.push(Entities.addEntity(cardProperties));
        //change last card
        lastCard = 52;

    }

    _this.unload = function () {
        Messages.unsubscribe(cardChannel);
        Messages.unsubscribe(resetChannel);
        Messages.messageReceived.connect(_this, _this.onReceivedMessage);
    };
})
<commit_msg>Delete Deck_Handler.js<commit_after><|endoftext|>


<commit_before>'use strict';
const helpers = require('../Utils/helpers.js');

const UNIGRAM_FREQUENCIES = {
    a: 8.04,
    b: 1.48,
    c: 3.34,
    d: 3.82,
    e: 12.49,
    f: 2.40,
    g: 1.87,
    h: 5.05,
    i: 7.57,
    j: 0.16,
    k: 0.54,
    l: 4.07,
    m: 2.51,
    n: 7.23,
    o: 7.64,
    p: 2.14,
    q: 0.12,
    r: 6.28,
    s: 6.51,
    t: 9.28,
    u: 2.73,
    v: 1.05,
    w: 1.68,
    x: 0.23,
    y: 1.66,
    z: 0.09
};

function getAllPossible(str) {
    let byteArray1 = helpers.getByteValuesFromHexString(str);
    let byteArrLen = byteArray1.length;

    let possibilities = [];
    for (let i = 0; i < 256; i++) {
        let xorByteArr = new Array(byteArrLen);
        xorByteArr.fill(i);
        let xoredValues = helpers.XOR(byteArray1, xorByteArr);
        let string = helpers.getASCIIStringFromHexValues(xoredValues);
        possibilities.push(string);
    }

    return possibilities;
}

function crossEntropy(str, freqArr) {
    str = str.replace(/\s/g, '');
    let sum = 0;
    let nonAlphabetical = 0;
    let len = str.length;
    for (let i = 0; i < len; i++) {
        let charIndex = str.charCodeAt(i);
        if (charIndex >= 65 && charIndex <= 90) {
            var charFreq = freqArr[charIndex - 65];
            sum += log2(charFreq);
        } else if (charIndex >= 97 && charIndex <= 122) {
            var charFreq = freqArr[charIndex - 97];
            sum += log2(charFreq);
        }
        else {
            nonAlphabetical++;
        }
    }

    if (nonAlphabetical > (len / 2)) {
        return Infinity; //Mostly gibberish that might skew the entropy values
    }
    return -(sum / (len - nonAlphabetical));
}

function decrypt(str) {
    let normalizedFreqs = helpers.values(UNIGRAM_FREQUENCIES).map(freq => freq / 100);

    let possibilities = getAllPossible(str);
    let entropies = [];
    for (let i = 0, len = possibilities.length; i < len; i++) {
        let entropy = crossEntropy(possibilities[i], normalizedFreqs);
        entropies.push([i, entropy]);
        //console.log("Entry "+ i +" : " + entropy)
    }

    entropies.sort(function (x, y) {
        // Compare by lowest entropy, break ties by lowest shift
        if (x[1] < y[1]) return -1;
        else if (x[1] > y[1]) return 1;
        else if (x[0] > y[0]) return -1;
        else if (x[0] < y[0]) return 1;
        else return 0;
    });

    let bestAnswerIndex = entropies[0][0];
    return possibilities[bestAnswerIndex];
    //shift is also bestAnswerIndex
}

function decryptMany(strs) {
    let normalizedFreqs = helpers.values(UNIGRAM_FREQUENCIES).map(freq => freq / 100);

    let entropies = [];
    let possibilities = [];
    for (let str of strs) {
        let strPossibilities = getAllPossible(str);
        let indexCounter = 0;
        for (let i = 0, len = strPossibilities.length; i < len; i++) {
            let entropy = crossEntropy(strPossibilities[i], normalizedFreqs);
            let index = i + 256 * indexCounter;
            if (entropy === Infinity) {
                //continue;
            }
            //console.log("Entry "+ i +" : " + entropy);
            entropies.push([i, entropy]);
            possibilities.push(strPossibilities[i]);
        }
        indexCounter++;
    }

    console.log(possibilities.length);
    entropies.sort(function (x, y) {
        // Compare by lowest entropy, break ties by lowest shift
        if (x[1] < y[1]) return -1;
        else if (x[1] > y[1]) return 1;
        else if (x[0] < y[0]) return -1;
        else if (x[0] > y[0]) return 1;
        else return 0;
    });

    let bestAnswerIndex = entropies[0][0];
    console.log(entropies.length);
    console.log(bestAnswerIndex);

    for (let i = 0; i < entropies.length; i++) {
        var index = entropies[i][0];
        var decode = possibilities[index];
        if (!isGibberish(decode)) {
            //console.log(possibilities[index]);
        }
    }

    return possibilities[bestAnswerIndex];
}

function isGibberish(str) {
    str = str.toLowerCase();
    let sum = 0;
    let nonAlphabetical = 0;
    let len = str.length;
    for (let i = 0; i < len; i++) {
        let charIndex = str.charCodeAt(i);
        if (charIndex >= 97 && charIndex <= 122) {
        } else {
            nonAlphabetical++;
        }
    }

    if (nonAlphabetical > len / 5) {
        return true;
    }

    return false;
}

function log2(val) {
    if (Math.log2) {
        return Math.log2(val);
    } else {
        return Math.log(val) / Math.log(2);
    }
}

exports.decrypt = decrypt;
exports.decryptMany = decryptMany;
exports.isGibberish = isGibberish;

//fix entropy selection algo
//use gibberish content to filter
//if they are same, then use one that is more lowercase<commit_msg>fix entropy code to use non alphabetical content as a weight<commit_after>'use strict';
const helpers = require('../Utils/helpers.js');

const UNIGRAM_FREQUENCIES = {
    a: 8.04,
    b: 1.48,
    c: 3.34,
    d: 3.82,
    e: 12.49,
    f: 2.40,
    g: 1.87,
    h: 5.05,
    i: 7.57,
    j: 0.16,
    k: 0.54,
    l: 4.07,
    m: 2.51,
    n: 7.23,
    o: 7.64,
    p: 2.14,
    q: 0.12,
    r: 6.28,
    s: 6.51,
    t: 9.28,
    u: 2.73,
    v: 1.05,
    w: 1.68,
    x: 0.23,
    y: 1.66,
    z: 0.09
};

function getAllPossible(str) {
    let byteArray1 = helpers.getByteValuesFromHexString(str);
    let byteArrLen = byteArray1.length;

    let possibilities = [];
    for (let i = 0; i < 256; i++) {
        let xorByteArr = new Array(byteArrLen);
        xorByteArr.fill(i);
        let xoredValues = helpers.XOR(byteArray1, xorByteArr);
        let string = helpers.getASCIIStringFromHexValues(xoredValues);
        possibilities.push(string);
    }

    return possibilities;
}

function crossEntropy(str, freqArr) {
    str = str.replace(/\s/g, '');
    let sum = 0;
    let nonAlphabetical = 0;
    let len = str.length;
    for (let i = 0; i < len; i++) {
        let charIndex = str.charCodeAt(i);
        if (charIndex >= 65 && charIndex <= 90) {
            var charFreq = freqArr[charIndex - 65];
            sum += log2(charFreq);
        } else if (charIndex >= 97 && charIndex <= 122) {
            var charFreq = freqArr[charIndex - 97];
            sum += log2(charFreq);
        }
        else {
            nonAlphabetical++;
        }
    }

    if (nonAlphabetical > (len / 2)) {
        return Infinity; //Mostly gibberish that might skew the entropy values
    }
    return -(sum / (len - nonAlphabetical));
}

function decrypt(str) {
    let normalizedFreqs = helpers.values(UNIGRAM_FREQUENCIES).map(freq => freq / 100);

    let possibilities = getAllPossible(str);
    let entropies = [];
    for (let i = 0, len = possibilities.length; i < len; i++) {
        let entropy = crossEntropy(possibilities[i], normalizedFreqs);
        let hasNonAlphabetical = +containsNonAlphabetical(possibilities[i]);//cast to number
        entropies.push([i, entropy, hasNonAlphabetical]);
        //console.log("Entry "+ i +" : " + entropy)
    }

    entropies.sort(function (x, y) {
        // Compare by lowest entropy, then by nonAlphabetical char count and finally break ties by lowest shift
        if (x[1] < y[1]) return -1;
        else if (x[1] > y[1]) return 1;
        else if (x[2] < y[2]) return -1;
        else if (x[2] > y[2]) return 1;
        else if (x[0] < y[0]) return -1;
        else if (x[0] > y[0]) return 1;
        else return 0;
    });

    let bestAnswerIndex = entropies[0][0];
    return possibilities[bestAnswerIndex];
    //shift is also bestAnswerIndex
}

function decryptMany(strs) {
    let normalizedFreqs = helpers.values(UNIGRAM_FREQUENCIES).map(freq => freq / 100);

    let entropies = [];
    let possibilities = [];
    for (let str of strs) {
        let strPossibilities = getAllPossible(str);
        let indexCounter = 0;
        for (let i = 0, len = strPossibilities.length; i < len; i++) {
            let entropy = crossEntropy(strPossibilities[i], normalizedFreqs);
            let index = i + 256 * indexCounter;
            if (entropy === Infinity) {
                //continue;
            }
            //console.log("Entry "+ i +" : " + entropy);
            entropies.push([i, entropy]);
            possibilities.push(strPossibilities[i]);
        }
        indexCounter++;
    }

    console.log(possibilities.length);
    entropies.sort(function (x, y) {
        // Compare by lowest entropy, break ties by lowest shift
        if (x[1] < y[1]) return -1;
        else if (x[1] > y[1]) return 1;
        else if (x[0] < y[0]) return -1;
        else if (x[0] > y[0]) return 1;
        else return 0;
    });

    let bestAnswerIndex = entropies[0][0];
    console.log(entropies.length);
    console.log(bestAnswerIndex);

    for (let i = 0; i < entropies.length; i++) {
        var index = entropies[i][0];
        var decode = possibilities[index];
        if (!isGibberish(decode)) {
            //console.log(possibilities[index]);
        }
    }

    return possibilities[bestAnswerIndex];
}

function containsNonAlphabetical(str) {
    //strip all whitespace, use regex?
    let strippedStr = str.replace(/\s/g, '').toLowerCase();
    let strLen = strippedStr.length;

    for (let i = 0; i < strLen; i++) {
        let charCode = strippedStr.charCodeAt(i);
        if (charCode < 97 || charCode > 122) {
            return true;
        }
    }

    return false;
}

function isGibberish(str) {
    str = str.toLowerCase();
    let sum = 0;
    let nonAlphabetical = 0;
    let len = str.length;
    for (let i = 0; i < len; i++) {
        let charIndex = str.charCodeAt(i);
        if (charIndex >= 97 && charIndex <= 122) {
        } else {
            nonAlphabetical++;
        }
    }

    if (nonAlphabetical > len / 5) {
        return true;
    }

    return false;
}

function log2(val) {
    if (Math.log2) {
        return Math.log2(val);
    } else {
        return Math.log(val) / Math.log(2);
    }
}

exports.decrypt = decrypt;
exports.decryptMany = decryptMany;
exports.isGibberish = isGibberish;

//fix entropy selection algo
//use gibberish content to filter
//if they are same, then use one that is more lowercase<|endoftext|>


<commit_before>export default {
    "$schema": "http://json-schema.org/draft-04/schema#",
    "definitions": {
        "formula": {
            "type": "object",
            "properties": {
                "formula": {
                    "type": "string"
                },
                "start": {
                    "type": "string",
                    "format": "date-time"
                },
                "end": {
                    "type": "string",
                    "format": "date-time"
                },
                "variables": {
                    "type": "array",
                    "uniqueItems": true,
                    "items": {
                        "type": "string"
                    }
                },
                "measurementType": {
                    "type": "array",
                    "uniqueItems": true,
                    "items": {
                        "type": "string"
                    }
                }
            },
            "required": ["formula", "start", "end", "variables", "measurementType"]
        },
        "measurementInfo": {
            "type": "object",
            "properties": {
                "type": {
                    "type": "string"
                },
                "tags": {
                    "type": "array",
                    "minItems": 1,
                    "uniqueItems": true,
                    "items": {
                        "type": "string"
                    }
                },
                "primaryTags": {
                    "type": "array",
                    "minItems": 1,
                    "uniqueItems": true,
                    "items": {
                        "type": "string"
                    }
                },
                "description": {
                    "type": "string"
                }
            },
            "required": ["type"],
            "anyOf": [{
                "required": ["tags"]
            }, {
                "required": ["primaryTags"]
            }, {
                "required": ["description"]
            }],
            "additionalProperties": false
        }
    },
    "type": "object",
    "properties": {
        "id": {
            "type": "string"
        },
        "name": {
            "type": "string"
        },
        "description": {
            "type": "string"
        },
        "shortDescription": {
            "type": "string"
        },
        "sampleDeltaInMS": {
            "type": "integer",
            "minimum": 10000
        },
        "unitOfMeasurement": {
            "type": "string"
        },
        "virtual": {
            "type": "boolean"
        },
        "formulas": {
            "type": "array",
            "items": {
                "$ref": "#/definitions/formula"
            }
        },
        "tags": {
            "type": "array",
            "uniqueItems": true,
            "items": {
                "type": "string"
            }
        },
        "primaryTags": {
            "type": "array",
            "uniqueItems": true,
            "items": {
                "type": "string"
            }
        },
        "measurementsInfo": {
            "type": "array",
            "items": {
                "$ref": "#/definitions/measurementInfo"
            }
        },
        "siteId": {
            "type": "string"
        },
        "userId": {
            "type": "string"
        },
        "parentSensorId": {
            "type": "string"
        },
        "createdByUser": {
            "type": "boolean"
        }
    },
    "additionalProperties": false,
    "required": [
        "id",
        "name"
    ]
};
<commit_msg>Added name to sensors' API<commit_after>export default {
    "$schema": "http://json-schema.org/draft-04/schema#",
    "definitions": {
        "formula": {
            "type": "object",
            "properties": {
                "formula": {
                    "type": "string"
                },
                "start": {
                    "type": "string",
                    "format": "date-time"
                },
                "end": {
                    "type": "string",
                    "format": "date-time"
                },
                "variables": {
                    "type": "array",
                    "uniqueItems": true,
                    "items": {
                        "type": "string"
                    }
                },
                "measurementType": {
                    "type": "array",
                    "uniqueItems": true,
                    "items": {
                        "type": "string"
                    }
                }
            },
            "required": ["formula", "start", "end", "variables", "measurementType"]
        },
        "measurementInfo": {
            "type": "object",
            "properties": {
                "type": {
                    "type": "string"
                },
                "tags": {
                    "type": "array",
                    "minItems": 1,
                    "uniqueItems": true,
                    "items": {
                        "type": "string"
                    }
                },
                "primaryTags": {
                    "type": "array",
                    "minItems": 1,
                    "uniqueItems": true,
                    "items": {
                        "type": "string"
                    }
                },
                "description": {
                    "type": "string"
                },
                "name": {
                    "type": "string"
                }
            },
            "required": ["type"],
            "anyOf": [{
                "required": ["tags"]
            }, {
                "required": ["primaryTags"]
            }, {
                "required": ["description"]
            }, {
                "required": ["name"]
            }],
            "additionalProperties": false
        }
    },
    "type": "object",
    "properties": {
        "id": {
            "type": "string"
        },
        "name": {
            "type": "string"
        },
        "description": {
            "type": "string"
        },
        "shortDescription": {
            "type": "string"
        },
        "sampleDeltaInMS": {
            "type": "integer",
            "minimum": 10000
        },
        "unitOfMeasurement": {
            "type": "string"
        },
        "virtual": {
            "type": "boolean"
        },
        "formulas": {
            "type": "array",
            "items": {
                "$ref": "#/definitions/formula"
            }
        },
        "tags": {
            "type": "array",
            "uniqueItems": true,
            "items": {
                "type": "string"
            }
        },
        "primaryTags": {
            "type": "array",
            "uniqueItems": true,
            "items": {
                "type": "string"
            }
        },
        "measurementsInfo": {
            "type": "array",
            "items": {
                "$ref": "#/definitions/measurementInfo"
            }
        },
        "siteId": {
            "type": "string"
        },
        "userId": {
            "type": "string"
        },
        "parentSensorId": {
            "type": "string"
        },
        "createdByUser": {
            "type": "boolean"
        }
    },
    "additionalProperties": false,
    "required": [
        "id",
        "name"
    ]
};
<|endoftext|>


<commit_before>import React from 'react';
import TabView from '@economist/component-tabview';
import AnimatedPanel from '@economist/component-animatedpanel';
import ArticleStore from '@economist/component-articlestore';
import Gobbet from '@economist/component-wifgobbet';
import ImageCaption from '@economist/component-imagecaption';
import Video from '@economist/component-video';

const articleStore = new ArticleStore('/content');
const articleComponent = {
  Image: 'img',
  Pullquote: 'blockquote',
  ArticleSubHead: 'h3',
  Gobbet,
  ImageCaption,
  Video,
  AnimatedPanel,
};
export default class ArticleTemplate extends React.Component {

  static get propTypes() {
    return {
      id: React.PropTypes.string.isRequired,
    };
  }

  static get store() {
    return articleStore;
  }

  static addComponentType(component, name) {
    articleComponent[name || component.name] = component;
  }

  renderJSONContents(contents) {
    return (contents || []).map((contentPiece, key) => {
      if (typeof contentPiece === 'string') {
        return (

        );
      }
      const Component = articleComponent[contentPiece.component];
      if (!Component) {
        throw new Error('Unknown component ' + contentPiece.component);
      }
      const children = this.renderJSONContents(contentPiece.content);
      return (
        <Component key={key} {...contentPiece.props}>
          {children}
        </Component>
      );
    });
  }

  renderTabView() {
    const sections = articleStore
      .getWhere((item) => item.id !== this.props.id)
      .reduce((total, article) => {
        const section = article.attributes.section;
        total[section] = total[section] || [];
        total[section].push(article);
        return total;
      }, {});
    return (
      <TabView>
        {Object.keys(sections).map((title, key) => (

            {sections[title].map((article) => (
              <a href={`/article/${article.id}`}>

                  <img
                    src={`${article.attributes.tileimage['1x']}`}
                    srcSet={this.getSrcSet(article.attributes.tileimage)}
                  />

{article.attributes.title}

            ))}

        ))}
      </TabView>
    );
  }

  getSrcSet(image) {
    return Object.keys(image).map((key) => `${image[key]} ${key}`).join(',');
  }

  render() {
    const article = articleStore.get(this.props.id);
    if (!article) {
      if (this.state && this.state.requested) {
        throw new Error('Already requested article id, but failed');
      }
      this.state = { requesting: true };
      articleStore.fetch(this.props.id).then(() => this.setState({ requesting: false, requested: true }));
      return (

          Loading

      );
    }
    const contents = this.renderJSONContents(article.attributes.content);
    const tabs = this.renderTabView();
    return (

            <img
              className="ArticleTemplate--image"
              src={`${article.attributes.mainimage['1x']}`}
              srcSet={this.getSrcSet(article.attributes.mainimage)}
            />

{article.attributes.section}

{article.attributes.flytitle}

{article.attributes.title}

{article.attributes.rubric}

          {contents}

        {tabs}

    );
  }
}
<commit_msg>Make all title and image props optional<commit_after>import React from 'react';
import TabView from '@economist/component-tabview';
import AnimatedPanel from '@economist/component-animatedpanel';
import ArticleStore from '@economist/component-articlestore';
import Gobbet from '@economist/component-wifgobbet';
import ImageCaption from '@economist/component-imagecaption';
import Video from '@economist/component-video';

const articleStore = new ArticleStore('/content');
const articleComponent = {
  Image: 'img',
  Pullquote: 'blockquote',
  ArticleSubHead: 'h3',
  Gobbet,
  ImageCaption,
  Video,
  AnimatedPanel,
};
export default class ArticleTemplate extends React.Component {

  static get propTypes() {
    return {
      id: React.PropTypes.string.isRequired,
    };
  }

  static get store() {
    return articleStore;
  }

  static addComponentType(component, name) {
    articleComponent[name || component.name] = component;
  }

  renderJSONContents(contents) {
    return (contents || []).map((contentPiece, key) => {
      if (typeof contentPiece === 'string') {
        return (

        );
      }
      const Component = articleComponent[contentPiece.component];
      if (!Component) {
        throw new Error('Unknown component ' + contentPiece.component);
      }
      const children = this.renderJSONContents(contentPiece.content);
      return (
        <Component key={key} {...contentPiece.props}>
          {children}
        </Component>
      );
    });
  }

  renderTabView() {
    const sections = articleStore
      .getWhere((item) => item.id !== this.props.id)
      .reduce((total, article) => {
        const section = article.attributes.section;
        total[section] = total[section] || [];
        total[section].push(article);
        return total;
      }, {});
    return (
      <TabView>
        {Object.keys(sections).map((title, key) => (

            {sections[title].map((article) => (
              <a href={`/article/${article.id}`}>

                  <img
                    src={`${article.attributes.tileimage['1x']}`}
                    srcSet={this.getSrcSet(article.attributes.tileimage)}
                  />

{article.attributes.title}

            ))}

        ))}
      </TabView>
    );
  }

  getSrcSet(image) {
    return Object.keys(image).map((key) => `${image[key]} ${key}`).join(',');
  }

  renderHeader(attributes) {
    let section = null;
    let flytitle = null;
    let title = null;
    if (attributes.section) {
      section = (

          {attributes.section}

      );
    }
    if (attributes.flytitle) {
      flytitle = (

          {attributes.flytitle}

      );
    }
    if (attributes.title) {
      title = (

          {attributes.title}

      );
    }
    if (section || flytitle || title) {
      return (

          {section}
          {flytitle}
          {title}

      );
    }
  }

  render() {
    const article = articleStore.get(this.props.id);
    if (!article) {
      if (this.state && this.state.requested) {
        throw new Error('Already requested article id, but failed');
      }
      this.state = { requesting: true };
      articleStore.fetch(this.props.id).then(() => this.setState({ requesting: false, requested: true }));
      return (

          Loading

      );
    }
    const contents = this.renderJSONContents(article.attributes.content);
    const tabs = this.renderTabView();
    let image = null;
    if (article.attributes.mainimage) {
      image = (<img
        className="ArticleTemplate--image"
        src={`${article.attributes.mainimage['1x']}`}
        srcSet={this.getSrcSet(article.attributes.mainimage)}
      />);
    }
    return (

            {image}
            {this.renderHeader(article.attributes)}

{article.attributes.rubric}

          {contents}

        {tabs}

    );
  }
}
<|endoftext|>


<commit_before>import externalRequest from 'request';
import Rx from 'rxjs';
const externalRequest$ = Rx.Observable.bindNodeCallback(externalRequest);

import * as apiConfigCreators from './apiConfigCreators';
import { duotoneRef } from '../util/duotone';

const parseResponseFlags = ({ headers }) =>
	(headers['X-Meetup-Flags'] || '')
		.split(',')
		.map(pair => pair.split('='))
		.reduce((flags, [key, val]) => {
			flags[key] = val === 'true';
			return flags;
		}, {});

/**
 * Given the current request and API server host, proxy the request to the API
 * and return the responses corresponding to the provided queries.
 *
 * This module plugs in to any system that provides a `request` object with:
 * - headers
 * - method ('get'/'post')
 * - query string parameters parsed as a plain object (for GET requests)
 * - payload/body (for POST requests)
 *
 * @module ApiProxy
 */

/**
 * mostly error handling - any case where the API does not satisfy the
 * "api response" formatting requirement: plain object containing the requested
 * values
 *
 * This utility is specific to the response format of the API being consumed
 * @param response {String} the raw response body text from an API call
 * @return responseObj the JSON-parsed text, possibly with error info
 */
export const parseApiResponse = ([response, body]) => {
	let value;
	const flags = parseResponseFlags(response);

	if (!response.ok) {
		return {
			value: { error: response.statusText },
			flags,
		};
	}
	try {
		value = JSON.parse(body);
		if (value && value.problem) {
			value = { error: `API problem: ${value.problem}: ${value.details}` };
		}
		return { value, flags };
	} catch(err) {
		return {
			value: { error: err.message },
			flags
		};
	}
};

/**
 * Translate a query into an API `endpoint` + `params`. The translation is based
 * on the Meetup REST API.
 *
 * This function serves as an adapter between the structure of a query and the
 * API-specific config needed to get that data. Note that *each* required
 * endpoint needs to be manually configured
 *
 * {@link http://www.meetup.com/meetup_api/docs/batch/}
 *
 * @param {Object} query a query object from the application
 * @return {Object} the arguments for api request, including endpoint
 */
export function queryToApiConfig({ type, params, flags }) {
	const configCreator = apiConfigCreators[type];
	if (!configCreator) {
		throw new ReferenceError(`No API specified for query type ${type}`);
	}
	return configCreator(params);
}

/**
 * Join the key-value params object into a querystring-like
 * string. use `encodeURIComponent` _only_ if `doEncode` is provided,
 * otherwise the caller is responsible for encoding
 *
 * @param {Object} params plain object of keys and values to format
 * @return {String}
 */
function urlFormatParams(params, doEncode) {
	return Object.keys(params || {})
		.reduce((dataParams, paramKey) => {
			const paramValue = encodeURIComponent(params[paramKey]);
			dataParams.push(`${paramKey}=${paramValue}`);
			return dataParams;
		}, []).join('&');
}

/**
 * Transform each query into the arguments needed for a `request` call.
 *
 * Some request options are constant for all queries, and these are curried into
 * a function that can be called with a single query as part of the request
 * stream
 *
 * @see {@link https://www.npmjs.com/package/request}
 *
 * @param {Object} externalRequestOpts request options that will be applied to
 *   every query request
 * @param {Object} apiConfig { endpoint, params, flags }
 *   call)
 * @return {Object} externalRequestOptsQuery argument for the call to
 *   `externalRequest` for the query
 */
export const buildRequestArgs = externalRequestOpts =>
	({ endpoint, params, flags }) => {

		const opts = { ...externalRequestOpts };
		opts.url = `/${endpoint}`;

		const formattedParams = urlFormatParams(params, opts.method === 'get');

		if (flags) {
			opts.headers['X-Meetup-Request-Flags'] = flags.join(',');
		}

		switch (opts.method) {
		case 'get':
			opts.url += `?${formattedParams}`;
			opts.headers['X-Meta-Photo-Host'] = 'secure';
			break;
		case 'post':
			opts.body = formattedParams;
			opts.headers['content-type'] = 'application/x-www-form-urlencoded';
			break;
		}

		return opts;
	};

/**
 * Format apiResponse to match expected state structure
 *
 * @param {Object} apiResponse JSON-parsed api response data
 */
export const apiResponseToQueryResponse = query => ({ flags, value }) => ({
	[query.ref]: {
		type: query.type,
		value,
		flags
	}
});

/**
 * Parse request for queries and request options
 * @return {Object} { queries, externalRequestOpts }
 */
export function parseRequest(request, baseUrl) {
	const {
		headers,
		method,
		query,
		payload,
	} = request;

	const externalRequestOpts = {
		baseUrl,
		method,
		headers: { ...headers },  // make a copy to be immutable
		mode: 'no-cors',
		time: true,
		agentOptions: {
			rejectUnauthorized: baseUrl.indexOf('.dev') === -1
		}
	};

	// Forward the Hapi request headers from the client query
	// except for `host` and `accept-encoding`
	// which should be provided by the external api request
	delete externalRequestOpts.headers['host'];
	delete externalRequestOpts.headers['accept-encoding'];
	delete externalRequestOpts.headers['content-length'];  // original request content-length is irrelevant

	const queriesJSON = request.method === 'get' ? query.queries : payload.queries;
	const queries = JSON.parse(queriesJSON);
	return { queries, externalRequestOpts };
}

/**
 * From a provided set of signed duotone URLs, create a function that injects
 * the full duotone URL into a group object with the key `duotoneUrl`.
 *
 * @param {Object} duotoneUrls map of `[duotoneRef]: url template root`
 * @param {Object} group group object from API
 * @return {Object} the mutated group object
 */
export const groupDuotoneSetter = duotoneUrls => group => {
	const photo = group.key_photo || group.group_photo || {};
	const duotoneKey = group.photo_gradient && duotoneRef(
			group.photo_gradient.light_color,
			group.photo_gradient.dark_color
		);
	const duotoneUrlRoot = duotoneKey && duotoneUrls[duotoneKey];
	if (duotoneUrlRoot && photo.id) {
		group.duotoneUrl = `${duotoneUrlRoot}/${photo.id}.jpeg`;
	}
	return group;
};

/**
 * From a provided set of signed duotoneUrls, create a function that injects
 * the full duotone URL into an query response containing objects that support
 * duotoned images (anything containing group or event objects
 *
 * @param {Object} duotoneUrls map of `[duotoneRef]: url template root`
 * @param {Object} queryResponse { type: <type>, value: <API object> }
 * @return {Object} the modified queryResponse
 */
export const apiResponseDuotoneSetter = duotoneUrls => {
	const setGroupDuotone = groupDuotoneSetter(duotoneUrls);
	return queryResponse => {
		// inject duotone URLs into any group query response
		Object.keys(queryResponse)
			.forEach(key => {
				const { type, value } = queryResponse[key];
				let groups;
				switch (type) {
				case 'group':
					groups = value instanceof Array ? value : [value];
					groups.forEach(setGroupDuotone);
					break;
				case 'home':
					(value.rows || []).map(({ items }) => items)
						.forEach(items => items.filter(({ type }) => type === 'group')
							.forEach(({ group }) => setGroupDuotone(group))
						);
					break;
				}
			});
		return queryResponse;
	};
};

export const makeApiRequest = (request, API_TIMEOUT, duotoneUrls) => {
	const setApiResponseDuotones = apiResponseDuotoneSetter(duotoneUrls);

	return ([requestOpts, query]) => {
		request.log(['api'], JSON.stringify(requestOpts.url));
		return externalRequest$(requestOpts)
			.timeout(API_TIMEOUT, new Error('API response timeout'))
			.do(([response]) => request.log(['api'], `${response.elapsedTime}ms - ${response.request.uri.path}`)) // log api response time
			.map(parseApiResponse)             // parse into plain object
			.catch(error =>
				Rx.Observable.of({ value: { error: error.message } })
			)
			.map(apiResponseToQueryResponse(query))    // convert apiResponse to app-ready queryResponse
			.map(setApiResponseDuotones);        // special duotone prop
	};
};

/**
 * This function transforms a single request to the application server into a
 * parallel array of requests to the API server, and then re-assembles the
 * API responses into an array of 'query responses' - i.e. API responses that
 * are formatted with properties from their corresponding query (ref, type).
 *
 * Most of the `options` for the `externalRequest` are shared for all the API
 * requests, so these are initialized in `parseRequest`. `buildRequestArgs`
 * then curries those into a function that can accept a `query` to write the
 * query-specific options.
 *
 * @param {Request} request Hapi request object
 * @param {Object} baseUrl API server base URL for all API requests
 * @return Array$ contains all API responses corresponding to the provided queries
 */
const apiProxy$ = ({ API_TIMEOUT=5000, baseUrl, duotoneUrls }) => {

	return request => {

		// 1. get the queries and the 'universal' `externalRequestOpts` from the request
		const { queries, externalRequestOpts } = parseRequest(request, baseUrl);

		// 2. curry a function that uses `externalRequestOpts` as a base from which
		// to build the query-specific API request options object
		const apiConfigToRequestOptions = buildRequestArgs(externalRequestOpts);

		// 3. map the queries onto an array of api request observables
		const apiRequests$ = queries
			.map(queryToApiConfig)
			.map(apiConfigToRequestOptions)
			.map((opts, i) => ([opts, queries[i]]))  // zip the query back into the opts
			.map(makeApiRequest(request, API_TIMEOUT, duotoneUrls));

		// 4. zip them together to send them parallel and receive them in order
		return Rx.Observable.zip(...apiRequests$);
	};
};

export default apiProxy$;

<commit_msg>make sure the flags get through to api config<commit_after>import externalRequest from 'request';
import Rx from 'rxjs';
const externalRequest$ = Rx.Observable.bindNodeCallback(externalRequest);

import * as apiConfigCreators from './apiConfigCreators';
import { duotoneRef } from '../util/duotone';

const parseResponseFlags = ({ headers }) =>
	(headers['X-Meetup-Flags'] || '')
		.split(',')
		.filter(pair => pair)
		.map(pair => pair.split('='))
		.reduce((flags, [key, val]) => {
			flags[key] = val === '1';
			return flags;
		}, {});

/**
 * Given the current request and API server host, proxy the request to the API
 * and return the responses corresponding to the provided queries.
 *
 * This module plugs in to any system that provides a `request` object with:
 * - headers
 * - method ('get'/'post')
 * - query string parameters parsed as a plain object (for GET requests)
 * - payload/body (for POST requests)
 *
 * @module ApiProxy
 */

/**
 * mostly error handling - any case where the API does not satisfy the
 * "api response" formatting requirement: plain object containing the requested
 * values
 *
 * This utility is specific to the response format of the API being consumed
 * @param response {String} the raw response body text from an API call
 * @return responseObj the JSON-parsed text, possibly with error info
 */
export const parseApiResponse = ([response, body]) => {
	let value;
	const flags = parseResponseFlags(response);

	if (!response.ok) {
		return {
			value: { error: response.statusText },
			flags,
		};
	}
	try {
		value = JSON.parse(body);
		if (value && value.problem) {
			value = { error: `API problem: ${value.problem}: ${value.details}` };
		}
		return { value, flags };
	} catch(err) {
		return {
			value: { error: err.message },
			flags
		};
	}
};

/**
 * Translate a query into an API `endpoint` + `params`. The translation is based
 * on the Meetup REST API.
 *
 * This function serves as an adapter between the structure of a query and the
 * API-specific config needed to get that data. Note that *each* required
 * endpoint needs to be manually configured
 *
 * {@link http://www.meetup.com/meetup_api/docs/batch/}
 *
 * @param {Object} query a query object from the application
 * @return {Object} the arguments for api request, including endpoint
 */
export function queryToApiConfig({ type, params, flags }) {
	const configCreator = apiConfigCreators[type];
	if (!configCreator) {
		throw new ReferenceError(`No API specified for query type ${type}`);
	}
	return {
		...configCreator(params),  // endpoint, params
		flags,
	};
}

/**
 * Join the key-value params object into a querystring-like
 * string. use `encodeURIComponent` _only_ if `doEncode` is provided,
 * otherwise the caller is responsible for encoding
 *
 * @param {Object} params plain object of keys and values to format
 * @return {String}
 */
function urlFormatParams(params, doEncode) {
	return Object.keys(params || {})
		.reduce((dataParams, paramKey) => {
			const paramValue = encodeURIComponent(params[paramKey]);
			dataParams.push(`${paramKey}=${paramValue}`);
			return dataParams;
		}, []).join('&');
}

/**
 * Transform each query into the arguments needed for a `request` call.
 *
 * Some request options are constant for all queries, and these are curried into
 * a function that can be called with a single query as part of the request
 * stream
 *
 * @see {@link https://www.npmjs.com/package/request}
 *
 * @param {Object} externalRequestOpts request options that will be applied to
 *   every query request
 * @param {Object} apiConfig { endpoint, params, flags }
 *   call)
 * @return {Object} externalRequestOptsQuery argument for the call to
 *   `externalRequest` for the query
 */
export const buildRequestArgs = externalRequestOpts =>
	({ endpoint, params, flags }) => {

		const opts = { ...externalRequestOpts };
		opts.url = `/${endpoint}`;

		const formattedParams = urlFormatParams(params, opts.method === 'get');

		if (flags) {
			opts.headers['X-Meetup-Request-Flags'] = flags.join(',');
		}

		switch (opts.method) {
		case 'get':
			opts.url += `?${formattedParams}`;
			opts.headers['X-Meta-Photo-Host'] = 'secure';
			break;
		case 'post':
			opts.body = formattedParams;
			opts.headers['content-type'] = 'application/x-www-form-urlencoded';
			break;
		}

		return opts;
	};

/**
 * Format apiResponse to match expected state structure
 *
 * @param {Object} apiResponse JSON-parsed api response data
 */
export const apiResponseToQueryResponse = query => ({ value, flags }) => ({
	[query.ref]: {
		type: query.type,
		value,
		flags
	}
});

/**
 * Parse request for queries and request options
 * @return {Object} { queries, externalRequestOpts }
 */
export function parseRequest(request, baseUrl) {
	const {
		headers,
		method,
		query,
		payload,
	} = request;

	const externalRequestOpts = {
		baseUrl,
		method,
		headers: { ...headers },  // make a copy to be immutable
		mode: 'no-cors',
		time: true,
		agentOptions: {
			rejectUnauthorized: baseUrl.indexOf('.dev') === -1
		}
	};

	// Forward the Hapi request headers from the client query
	// except for `host` and `accept-encoding`
	// which should be provided by the external api request
	delete externalRequestOpts.headers['host'];
	delete externalRequestOpts.headers['accept-encoding'];
	delete externalRequestOpts.headers['content-length'];  // original request content-length is irrelevant

	const queriesJSON = request.method === 'get' ? query.queries : payload.queries;
	const queries = JSON.parse(queriesJSON);
	return { queries, externalRequestOpts };
}

/**
 * From a provided set of signed duotone URLs, create a function that injects
 * the full duotone URL into a group object with the key `duotoneUrl`.
 *
 * @param {Object} duotoneUrls map of `[duotoneRef]: url template root`
 * @param {Object} group group object from API
 * @return {Object} the mutated group object
 */
export const groupDuotoneSetter = duotoneUrls => group => {
	const photo = group.key_photo || group.group_photo || {};
	const duotoneKey = group.photo_gradient && duotoneRef(
			group.photo_gradient.light_color,
			group.photo_gradient.dark_color
		);
	const duotoneUrlRoot = duotoneKey && duotoneUrls[duotoneKey];
	if (duotoneUrlRoot && photo.id) {
		group.duotoneUrl = `${duotoneUrlRoot}/${photo.id}.jpeg`;
	}
	return group;
};

/**
 * From a provided set of signed duotoneUrls, create a function that injects
 * the full duotone URL into an query response containing objects that support
 * duotoned images (anything containing group or event objects
 *
 * @param {Object} duotoneUrls map of `[duotoneRef]: url template root`
 * @param {Object} queryResponse { type: <type>, value: <API object> }
 * @return {Object} the modified queryResponse
 */
export const apiResponseDuotoneSetter = duotoneUrls => {
	const setGroupDuotone = groupDuotoneSetter(duotoneUrls);
	return queryResponse => {
		// inject duotone URLs into any group query response
		Object.keys(queryResponse)
			.forEach(key => {
				const { type, value } = queryResponse[key];
				let groups;
				switch (type) {
				case 'group':
					groups = value instanceof Array ? value : [value];
					groups.forEach(setGroupDuotone);
					break;
				case 'home':
					(value.rows || []).map(({ items }) => items)
						.forEach(items => items.filter(({ type }) => type === 'group')
							.forEach(({ group }) => setGroupDuotone(group))
						);
					break;
				}
			});
		return queryResponse;
	};
};

export const makeApiRequest = (request, API_TIMEOUT, duotoneUrls) => {
	const setApiResponseDuotones = apiResponseDuotoneSetter(duotoneUrls);

	return ([requestOpts, query]) => {
		request.log(['api'], JSON.stringify(requestOpts.url));
		return externalRequest$(requestOpts)
			.timeout(API_TIMEOUT, new Error('API response timeout'))
			.do(([response]) => request.log(['api'], `${response.elapsedTime}ms - ${response.request.uri.path}`)) // log api response time
			.map(parseApiResponse)             // parse into plain object
			.catch(error =>
				Rx.Observable.of({ value: { error: error.message } })
			)
			.map(apiResponseToQueryResponse(query))    // convert apiResponse to app-ready queryResponse
			.map(setApiResponseDuotones);        // special duotone prop
	};
};

/**
 * This function transforms a single request to the application server into a
 * parallel array of requests to the API server, and then re-assembles the
 * API responses into an array of 'query responses' - i.e. API responses that
 * are formatted with properties from their corresponding query (ref, type).
 *
 * Most of the `options` for the `externalRequest` are shared for all the API
 * requests, so these are initialized in `parseRequest`. `buildRequestArgs`
 * then curries those into a function that can accept a `query` to write the
 * query-specific options.
 *
 * @param {Request} request Hapi request object
 * @param {Object} baseUrl API server base URL for all API requests
 * @return Array$ contains all API responses corresponding to the provided queries
 */
const apiProxy$ = ({ API_TIMEOUT=5000, baseUrl, duotoneUrls }) => {

	return request => {

		// 1. get the queries and the 'universal' `externalRequestOpts` from the request
		const { queries, externalRequestOpts } = parseRequest(request, baseUrl);

		// 2. curry a function that uses `externalRequestOpts` as a base from which
		// to build the query-specific API request options object
		const apiConfigToRequestOptions = buildRequestArgs(externalRequestOpts);

		// 3. map the queries onto an array of api request observables
		const apiRequests$ = queries
			.map(queryToApiConfig)
			.map(apiConfigToRequestOptions)
			.map((opts, i) => ([opts, queries[i]]))  // zip the query back into the opts
			.map(makeApiRequest(request, API_TIMEOUT, duotoneUrls));

		// 4. zip them together to send them parallel and receive them in order
		return Rx.Observable.zip(...apiRequests$);
	};
};

export default apiProxy$;

<|endoftext|>


<commit_before>'use strict';

const { Suite } = require('./helpers');
const { config, data } = require('./fixtures/data.json');
const { DataFrame, ColumnTransform } = require('./src');

const run = async () => {
    const suite = Suite('Transform Column');

    const dataFrame = DataFrame.fromJSON(config, data);
    for (const column of dataFrame.columns) {
        const fieldInfo = `${column.name} (${column.config.type}${column.config.array ? '[]' : ''})`;
        Object.entries(ColumnTransform)
            .filter(([, transform]) => {
                if (!transform.accepts || !transform.accepts.length) return false;
                if (!transform.accepts.includes(column.vector.type)) return false;
                if (transform.required_args && transform.required_args.length) return false;
                return true;
            })
            .forEach(([name, transform]) => {
                suite.add(`${fieldInfo} ${name}`, {
                    fn() {
                        column.transform(transform);
                    }
                });
            });
    }

    return suite.run({
        async: true,
        initCount: 2,
        minSamples: 2,
        maxTime: 20,
    });
};
if (require.main === module) {
    run().then((suite) => {
        suite.on('complete', () => {});
    });
} else {
    module.exports = run;
}
<commit_msg>Update transform-column to work with the latest functions<commit_after>'use strict';

const { isEmpty } = require('@terascope/utils');
const { FieldType } = require('@terascope/types');
const { Suite } = require('./helpers');
const { config, data } = require('./fixtures/data.json');
const {
    DataFrame, functionConfigRepository,
    FunctionDefinitionType, dataFrameAdapter
} = require('./src');

/**
 * @todo add tuple support
 * @param fieldType {import('@terascope/types').FieldType}
*/
function getFieldType(fieldType) {
    if (fieldType === FieldType.Long) return FieldType.Number;
    if (fieldType === FieldType.Byte) return FieldType.Number;
    if (fieldType === FieldType.Short) return FieldType.Number;
    if (fieldType === FieldType.Float) return FieldType.Number;
    if (fieldType === FieldType.Double) return FieldType.Number;
    if (fieldType === FieldType.Integer) return FieldType.Number;
    if (fieldType === FieldType.Domain) return FieldType.String;
    if (fieldType === FieldType.Keyword) return FieldType.String;
    if (fieldType === FieldType.KeywordCaseInsensitive) return FieldType.String;
    if (fieldType === FieldType.KeywordPathAnalyzer) return FieldType.String;
    if (fieldType === FieldType.KeywordCaseInsensitive) return FieldType.String;
    if (fieldType === FieldType.KeywordTokens) return FieldType.String;
    if (fieldType === FieldType.KeywordTokensCaseInsensitive) return FieldType.String;
    if (fieldType === FieldType.Hostname) return FieldType.String;
    if (fieldType === FieldType.Text) return FieldType.String;
    return fieldType;
}

const run = async () => {
    const suite = Suite('Transform/Validate Column');

    const dataFrame = DataFrame.fromJSON(config, data);
    for (const column of dataFrame.columns) {
        const fieldInfo = `${column.name} (${column.config.type}${column.config.array ? '[]' : ''})`;
        Object.entries(functionConfigRepository)
            .filter(([, fnDef]) => {
                if (fnDef.type !== FunctionDefinitionType.FIELD_TRANSFORM
                    && fnDef.type !== FunctionDefinitionType.FIELD_VALIDATION) {
                    return false;
                }
                if (fnDef.required_arguments && fnDef.required_arguments.length) {
                    return false;
                }
                if (
                    !isEmpty(fnDef.accepts)
                    && !fnDef.accepts.includes(getFieldType(column.config.type))
                ) {
                    return false;
                }
                return true;
            })
            .forEach(([name, fnDef]) => {
                suite.add(`${fieldInfo} ${name}`, {
                    fn() {
                        dataFrameAdapter(fnDef).column(column);
                    }
                });
            });
    }

    return suite.run({
        async: true,
        initCount: 2,
        minSamples: 2,
        maxTime: 20,
    });
};
if (require.main === module) {
    run().then((suite) => {
        suite.on('complete', () => {});
    });
} else {
    module.exports = run;
}
<|endoftext|>


<commit_before>'use strict';
((rb, document) => {
  const aria = rb.aria;
  const DESCRIBED_BY_ERROR_ID_ATTR = 'data-rb-describedbyerrorid';

  function onClassMutation(mutations) {
    const mutationsLen = mutations.length;

    for (let k = 0; k < mutationsLen; k++) {
      const mutation = mutations[k];
      const el = mutation.target;
      const message = el.parentNode.querySelector('.validation-message');

      let describedByErrorId = '';
      let describedByIds = [];
      let i = -1;

      if (mutation.oldValue !== 'invalid' && mutation.target.classList.contains('invalid')) {
        // If "invalid" was added, do the decoratin'
        el.setAttribute(aria.invalid, 'true');
        describedByErrorId = el.getAttribute(DESCRIBED_BY_ERROR_ID_ATTR);
        describedByIds = el.getAttribute(aria.describedby).split(' ');
        i = describedByIds.indexOf(describedByErrorId);

        if (message) {
          message.setAttribute(aria.role, 'alert');
          message.setAttribute(aria.live, 'polite');

          // If a validation message exists && the related element is newly invalid,
          // add message id to described-by
          if (i === -1) {
            describedByIds.push(describedByErrorId);
            el.setAttribute(aria.describedby, describedByIds.join(' '));
          }
        }
      } else if (mutation.oldValue === 'invalid' && !el.classList.contains('invalid')) {
        // If "invalid" was removed
        el.setAttribute(aria.invalid, 'false');
        describedByErrorId = el.getAttribute(DESCRIBED_BY_ERROR_ID_ATTR);
        describedByIds = el.getAttribute(aria.describedby).split(' ');
        i = describedByIds.indexOf(describedByErrorId);

        if (message) {
          message.removeAttribute('role');
          message.removeAttribute(aria.live);

          // If a validation message exists && the related element longer invalid,
          // remove message id from described-by
          if (i > -1) {
            describedByIds.splice(i, 1);
            el.setAttribute(aria.describedby, describedByIds.join(' '));
          }
        }
      }
    }
  }

  function decorateInputs() {
    const formEls = document.querySelectorAll('.form-group input, .form-group select, .form-group textarea, .form-group fieldset');
    const formElsLen = formEls.length;

    for (let i = 0; i < formElsLen; i++) {
      const formEl = formEls[i];

      // Set an observer to listen for .invalid.
      const observer = new MutationObserver(mutations => { onClassMutation(mutations); });
      observer.observe(formEl, { subtree: false, attributes: true, attributeOldValue: true, attributeFilter: ['class'] });

      const messages = formEl.parentNode.querySelectorAll('.validation-message, .helper-text');
      const msgLen = messages.length;

      if (msgLen > 0) {
        let describedByIds = '';

        if (formEl.hasAttribute(aria.describedby)) {
          describedByIds = `${formEl.getAttribute(aria.describedby)} `;
        }

        for (let j = 0; j < msgLen; j++) {
          const thisMsg = messages[j];
          const id = thisMsg.id ? thisMsg.id : `rb-a11y_${rb.getShortId()}`;
          describedByIds += `${id} `;

          if (thisMsg.classList.contains('validation-message')) {
            formEl.setAttribute(DESCRIBED_BY_ERROR_ID_ATTR, id);
          }

          // Don't clobber any existing attributes!
          if (!thisMsg.id) {
            thisMsg.id = id;
          }
        }

        if (!formEl.hasAttribute(aria.describedby)) {
          formEl.setAttribute(aria.describedby, describedByIds.trim());
        }
      }
    }
  }

  rb.onDocumentReady(decorateInputs);
  rb.forms = rb.forms || {};
  rb.forms.decorateInputs = decorateInputs;
})(window.rb, document);
<commit_msg>feat(Forms): Update form error handling to better support HTML5 `invalid` attribute.<commit_after>'use strict';
((rb, document) => {
  const aria = rb.aria;
  const DESCRIBED_BY_ERROR_ID_ATTR = 'data-rb-describedbyerrorid';

  function onClassMutation(mutations) {
    const mutationsLen = mutations.length;

    for (let k = 0; k < mutationsLen; k++) {
      const mutation = mutations[k];
      const el = mutation.target;
      const message = el.parentNode.querySelector('.validation-message');

      let describedByErrorId = '';
      let describedByIds = [];
      let i = -1;

      if (mutation.oldValue !== 'invalid' && mutation.target.classList.contains('invalid')) {
        // If "invalid" was added, do the decoratin'
        el.setAttribute(aria.invalid, 'true');
        describedByErrorId = el.getAttribute(DESCRIBED_BY_ERROR_ID_ATTR);
        describedByIds = el.getAttribute(aria.describedby).split(' ');
        i = describedByIds.indexOf(describedByErrorId);

        if (message) {
          message.setAttribute(aria.role, 'alert');
          message.setAttribute(aria.live, 'polite');

          // If a validation message exists && the related element is newly invalid,
          // add message id to described-by
          if (i === -1) {
            describedByIds.push(describedByErrorId);
            el.setAttribute(aria.describedby, describedByIds.join(' '));
          }
        }
      } else if (mutation.oldValue === 'invalid' && !el.classList.contains('invalid')) {
        // If "invalid" was removed
        el.setAttribute(aria.invalid, 'false');
        describedByErrorId = el.getAttribute(DESCRIBED_BY_ERROR_ID_ATTR);
        describedByIds = el.getAttribute(aria.describedby).split(' ');
        i = describedByIds.indexOf(describedByErrorId);

        if (message) {
          message.removeAttribute('role');
          message.removeAttribute(aria.live);

          // If a validation message exists && the related element longer invalid,
          // remove message id from described-by
          if (i > -1) {
            describedByIds.splice(i, 1);
            el.setAttribute(aria.describedby, describedByIds.join(' '));
          }
        }
      }
    }
  }

  function decorateInputs() {
    const formEls = document.querySelectorAll(
      '.form-group input, .form-group select, .form-group textarea, .form-group fieldset'
    );
    const formElsLen = formEls.length;

    for (let i = 0; i < formElsLen; i++) {
      const el = formEls[i];

      // "invalid" will be raised by the checkValidity() call below.
      // .invalid is added and will be handled by the MutationObserver
      // created by this function.
      // https://daverupert.com/2017/11/happier-html5-forms/
      el.addEventListener('invalid', () => {
        el.classList.add('invalid');
      }, false);

      // Set an observer to listen for .invalid.
      const observer = new MutationObserver(mutations => { onClassMutation(mutations); });
      observer.observe(el, {
        subtree: false,
        attributes: true,
        attributeOldValue: true,
        attributeFilter: ['class']
      });

      const messages = el.parentNode.querySelectorAll('.validation-message, .helper-text');
      const msgLen = messages.length;

      if (msgLen > 0) {
        let describedByIds = '';

        if (el.hasAttribute(aria.describedby)) {
          describedByIds = `${el.getAttribute(aria.describedby)} `;
        }

        for (let j = 0; j < msgLen; j++) {
          const thisMsg = messages[j];
          const id = thisMsg.id ? thisMsg.id : `rb-a11y_${rb.getShortId()}`;
          describedByIds += `${id} `;

          if (thisMsg.classList.contains('validation-message')) {
            el.setAttribute(DESCRIBED_BY_ERROR_ID_ATTR, id);
          }

          // Don't clobber any existing attributes!
          if (!thisMsg.id) {
            thisMsg.id = id;
          }
        }

        if (!el.hasAttribute(aria.describedby)) {
          el.setAttribute(aria.describedby, describedByIds.trim());
        }
      }
    }

    const formsAndGroups =
      document.querySelectorAll('form.validate-on-blur, .form-group.validate-on-blur');
    for (const el of formsAndGroups) {
      el.addEventListener('blur', () => {
        el.checkValidity();
      });
    }
  }

  rb.onDocumentReady(decorateInputs);
  rb.forms = rb.forms || {};
  rb.forms.decorateInputs = decorateInputs;
})(window.rb, document);
<|endoftext|>


<commit_before>/*jslint node: true */
'use strict';
var _ = require('lodash');

module.exports = (function () {
  function Argument(value, func) {
    this.value = value;
    this.func = func;
  }

  Argument.prototype.toGroovy = function() {
    return this.parse();
  };

  Argument.prototype.parse = function() {
    var argument = this.value;

    if (argument === null) {
      return 'null';
    }

    if (this.isClassReference()) {
      return argument.toString();
    }

    if (this.isFloat()) {
      return this.value;
    }

    // Handle ids that are not numbers but pass parseFloat test
    // (ie. Titan edge ids)
    if (_.isString(argument) && this.isFloat()) {
      return "'" + argument + "'";
    }

    if (_.isArray(argument)) {
      var parsedArray = _.map(argument, function(element) {
        if (_.isString(element)) {
          return "'" + element + "'";
        }

        return element;
      });

      return parsedArray.toString();
    }

    return "'"+ argument +"'";
  };

  Argument.prototype.isFloat = function() {
    return !_.isNaN(parseFloat(this.value)) && this.value.slice(-1) === 'f';
    // return this.value.slice(-1) === 'f';
  };

  Argument.prototype.isClassReference = function() {
    var graphRegex = /^T\.(gt|gte|eq|neq|lte|lt|decr|incr|notin|in)$|^Contains\.(IN|NOT_IN)$|^g\.|^Vertex(\.class)$|^Edge(\.class)$|^String(\.class)$|^Integer(\.class)$|^Geoshape(\.class)$|^Direction\.(OUT|IN|BOTH)$|^TitanKey(\.class)$|^TitanLabel(\.class)$/;

    return _.isString(this.value) && graphRegex.test(this.value);
  };

  return Argument;
})();
<commit_msg>Fixed bug with boolean argument<commit_after>/*jslint node: true */
'use strict';
var _ = require('lodash');

module.exports = (function () {
  function Argument(value, func) {
    this.value = value;
    this.func = func;
  }

  Argument.prototype.toGroovy = function() {
    return this.parse();
  };

  Argument.prototype.parse = function() {
    var argument = this.value;

    if (argument === null) {
      return 'null';
    }

    if (this.isClassReference()) {
      return argument.toString();
    }

    if (this.isFloat()) {
      return this.value;
    }

    // Handle ids that are not numbers but pass parseFloat test
    // (ie. Titan edge ids)
    if (_.isString(argument) && this.isFloat()) {
      return "'" + argument + "'";
    }

    if (_.isArray(argument)) {
      var parsedArray = _.map(argument, function(element) {
        if (_.isString(element)) {
          return "'" + element + "'";
        }

        return element;
      });

      return parsedArray.toString();
    }

    if (_.isBoolean(argument)) {
      return argument.toString();
    }

    return "'"+ argument +"'";
  };

  Argument.prototype.isFloat = function() {
    return !_.isNaN(parseFloat(this.value)) && this.value.slice(-1) === 'f';
    // return this.value.slice(-1) === 'f';
  };

  Argument.prototype.isClassReference = function() {
    var graphRegex = /^T\.(gt|gte|eq|neq|lte|lt|decr|incr|notin|in)$|^Contains\.(IN|NOT_IN)$|^g\.|^Vertex(\.class)$|^Edge(\.class)$|^String(\.class)$|^Integer(\.class)$|^Geoshape(\.class)$|^Direction\.(OUT|IN|BOTH)$|^TitanKey(\.class)$|^TitanLabel(\.class)$/;

    return _.isString(this.value) && graphRegex.test(this.value);
  };

  return Argument;
})();
<|endoftext|>


<commit_before>$(function(){
    leftStatusBarUpdate();
})
function leftStatusBarUpdate()
{
    var app_name = storage.getItem(config.storageAppNameKey);
    if(s3AuthObj)
        $('#publisher-name').text(s3AuthObj.rootDirectory);

    if(app_name)
        $('#app-list > li').first().text(app_name);

    if(s3AuthObj && app_name)
        $("#app-icon").attr("src", "https://librelio-europe.s3.amazonaws.com/" +
                            s3AuthObj.rootDirectory + "/" + app_name +
                            "/APP/SOURCES/iOS/Icon.png");
}
<commit_msg>Update left.js<commit_after>$(function(){
    leftStatusBarUpdate();
})
function leftStatusBarUpdate()
{
    var app_name = storage.getItem(config.storageAppNameKey);
    if(s3AuthObj)
        $('#publisher-name').text(s3AuthObj.rootDirectory);

    if(app_name)
        $('#app-list > li').first().text(app_name);

    if(s3AuthObj && app_name) {
        $("#app-icon").attr("src", "https://librelio-europe.s3.amazonaws.com/" +
            s3AuthObj.rootDirectory + "/" + app_name +
            "/APP/SOURCES/iOS/Icon.png");

        $("#app-icon").error(function() {
            $("#app-icon").attr("src", "assets/img/no-icon.png");
        });
    }

}
<|endoftext|>


<commit_before>/* globals define: false */
/**
 *  backbone.associate.js v0.0.12
 *  (c) 2013 - 2016, RJ Zaworski and contributors
 *
 *  Presumptionless model relations for Backbone.js
 *  Released under the MIT License
 */
// istanbul ignore next
(function (root, factory) {

  'use strict';

  // CommonJS compatibilty
  if (typeof window == 'undefined') {
    factory(require('underscore'), require('backbone'));
  }
  else if (typeof define === "function" && define.amd) {
    // AMD. Register as an anonymous module.
    define(['underscore', 'backbone'], function(_, Backbone) {
      // Use global variables if the locals are undefined.
      return factory(_ || root._, Backbone || root.Backbone);
    });
  }
  else {
    factory(root._, root.Backbone);
  }

})(this, function (_, Backbone) {

  'use strict';

  var
    // Sift through a map of attributes and initialize any
    // known associations
    _filterAssociates = function (context, attributes, options) {

      var attrs, current = context.attributes,
          key, association, associations = context._associations,
          omit = [];

      for (key in associations) {
        association = associations[key];
        attrs = attributes[key];
        if (_isAssociatedType(association, current[key])) {
          if (_isAssociatedType(association, attrs)) {
            // Reassign associated resource
            current[key] = attrs;
            omit.push(key);
          }
          else if (attrs && attrs !== null)
          {
            // Update attributes of associated resource
            current[key].set(attrs, options);
            omit.push(key);
          }
        }
        else {
          attributes[key] = _buildAssociation(context, association, attrs, options);
        }
      }

      // Skip any attributes that have were handled by associations
      return _.omit(attributes, omit);
    },

    // Check whether the supplied object matches the association type
    _isAssociatedType = function (association, obj) {
      return (obj instanceof association.type);
    },

    // Builds an association
    _buildAssociation = function (context, association, attributes, options) {
      var result = new (association.type)(attributes, options);
      if (association.url) {
        // Assign a sensible default URL by appending the url parameter
        // to the url of the parent model.
        result.url = function () {
          return _.result(context, 'url') + _.result(association, 'url');
        };
      }
      return result;
    },

    // Wraps a method, exposing an "unwrap" method for reverting it later
    _wrapMethod = function (context, wrapper, key) {

      var original = context[key],
          wrapped = _.wrap(original, wrapper);

      wrapped.unwrap = function () {
        context[key] = original;
      };

      context[key] = wrapped;
    },

    // Extensions to Backbone.Model for filtering associate data, etc, etc
    _extensions = {

      // Updates `set` to handle supplied attributes
      set: function (original, key, val, options) {

        var self = this,
            attributes = {};

        if (_.isObject(key)) {
          _.extend(attributes, key);
          if (typeof options === "undefined" || options === null) {
            options = val;
          }
        }
        else {
          attributes[key] = val;
        }

        return original.call(self, _filterAssociates(self, attributes, options), options);
      },

      // Updates `toJSON` to serialize associated objects
      toJSON: function (original, options) {

        var self = this,
            key, associations = self._associations,
            attributes = original.call(self, options);

        for (key in associations) {
          if (_isAssociatedType(associations[key], attributes[key])) {
            attributes[key] = attributes[key].toJSON(options);
          }
        }
        return attributes;
      }
    },

    // Patch initialize method to setup associations and filter initial attributes
    _initialize = function (original, attrs, options) {

      var self = this,
          key, extensions = _.clone(_extensions);

      // Provide associate accessors
      for (key in self._associations) {
        extensions[key] = _.partial(self.get, key);
      }

      // Wrap extensions around existing class methods
      _.each(extensions, _.partial(_wrapMethod, self));

      // Filter any attributes that slipped by without parsing
      _filterAssociates(self, self.attributes, options);

      // Pass control back to the original initialize method
      return original.call(self, attrs, options);
    };

  // Define associations for a model
  Backbone.associate = function (klass, associations) {

    var proto = klass.prototype;

    if (!proto._associations) {
      // Patch initialize method in prototype
      _wrapMethod(proto, _initialize, 'initialize');
    }

    // Extend from an empty object in case proto._associations is undefined
    proto._associations = _.extend({}, proto._associations, associations);
  };

  // Remove model associations
  Backbone.dissociate = function (klass) {
    var proto = klass.prototype;
    proto.initialize.unwrap();
    proto._associations = null;
  };

  return Backbone;
});

<commit_msg>Refactors method structure<commit_after>/* globals define: false */
/**
 *  backbone.associate.js v0.0.12
 *  (c) 2013 - 2016, RJ Zaworski and contributors
 *
 *  Presumptionless model relations for Backbone.js
 *  Released under the MIT License
 */
// istanbul ignore next
(function (root, factory) {

  'use strict';

  // CommonJS compatibilty
  if (typeof window == 'undefined') {
    factory(require('underscore'), require('backbone'));
  }
  else if (typeof define === "function" && define.amd) {
    // AMD. Register as an anonymous module.
    define(['underscore', 'backbone'], function(_, Backbone) {
      // Use global variables if the locals are undefined.
      return factory(_ || root._, Backbone || root.Backbone);
    });
  }
  else {
    factory(root._, root.Backbone);
  }

})(this, function (_, Backbone) {

  'use strict';

  // Sift through a map of attributes and initialize any
  // known associations
  function filterAssociates (context, attributes, options) {

    var attrs, association;
    var current = context.attributes;
    var associations = context._associations;
    var omit = [];

    for (var key in associations) {
      association = associations[key];
      attrs = attributes[key];
      if (isAssociatedType(association, current[key])) {
        if (isAssociatedType(association, attrs)) {
          // Reassign associated resource
          current[key] = attrs;
          omit.push(key);
        }
        else if (attrs && attrs !== null)
        {
          // Update attributes of associated resource
          current[key].set(attrs, options);
          omit.push(key);
        }
      }
      else {
        attributes[key] = buildAssociation(context, association, attrs, options);
      }
    }

    // Skip any attributes that have were handled by associations
    return _.omit(attributes, omit);
  }

  // Check whether the supplied object matches the association type
  function isAssociatedType (association, obj) {
    return (obj instanceof association.type);
  }

  function buildAssociation (context, association, attributes, options) {
    var result = new (association.type)(attributes, options);
    if (association.url) {
      // Assign a sensible default URL by appending the url parameter
      // to the url of the parent model.
      result.url = function () {
        return _.result(context, 'url') + _.result(association, 'url');
      };
    }
    return result;
  }

  // Extensions to Backbone.Model for filtering associate data, etc, etc
  var extensions = {

    // Updates `set` to handle supplied attributes
    set: function (original, key, val, options) {

      var attributes = {};

      if (_.isObject(key)) {
        _.extend(attributes, key);
        if (typeof options === "undefined" || options === null) {
          options = val;
        }
      }
      else {
        attributes[key] = val;
      }

      return original.call(this, filterAssociates(this, attributes, options), options);
    },

    // Updates `toJSON` to serialize associated objects
    toJSON: function (original, options) {
      var associations = this._associations;
      var attributes = original.call(this, options);
      for (var key in associations) {
        if (isAssociatedType(associations[key], attributes[key])) {
          attributes[key] = attributes[key].toJSON(options);
        }
      }
      return attributes;
    }
  };

  // Patch initialize method to setup associations and filter initial attributes
  var initialize = function (attrs, options) {
    var key;

    // Provide associate accessors
    for (key in this._associations) {
      this[key] = _.partial(this.get, key);
    }

    // Wrap extensions around existing class methods
    for (key in extensions) {
      this[key] = _.wrap(this[key], extensions[key]);
    }

    // Filter any attributes that slipped by without parsing
    filterAssociates(this, this.attributes, options);

    // Pass control back to the original initialize method
    return this.__unassociatedInit.call(this, attrs, options);
  };

  // Define associations for a model
  Backbone.associate = function (klass, associations) {
    var proto = klass.prototype;

    if (!proto._associations) {
      // Patch initialize method in prototype
      proto.__unassociatedInit = proto.initialize;
      proto.initialize = initialize;
    }

    // Extend from an empty object in case proto._associations is undefined
    proto._associations = _.extend({}, proto._associations, associations);
  };

  // Remove model associations
  Backbone.dissociate = function (klass) {
    var proto = klass.prototype;
    proto.initialize = proto.__unassociatedInit;
    proto._associations = null;
  };

  return Backbone;
});
<|endoftext|>


<commit_before><commit_msg>Delete cartCtrl.js<commit_after><|endoftext|>


<commit_before>var hash = require('hash.murmur3.32')
var fnv = require('b-tree/benchmark/fnv')

function Router (options) {
    this.delegates = options.delegates
    this.extract = options.extract
    var length = 256, dist = Math.floor(length / options.delegates.length)
    this.buckets = []
    this.delegates.forEach(function (del) {
        for (var i=0; i<dist; i++) {
            length--
            this.buckets.push({
                url: del
            })
        }
    }, this)
    while (length-- > 0) {
        this.buckets.push({ url: this.delegates[this.delegates.length - 1] })
    }
}

Router.prototype.match = function (obj) {
    var key = this.extract(obj)
    return this.buckets[fnv(new Buffer(key), 0, Buffer.byteLength(key)).readUIntLE(0, 1)].url
}

Router.prototype.newDelegate = function (del) {
// redistribute buckets
}

Router.prototype.removeDelegate = function () {
// keep old config until migration complete
}

exports.Router = Router
<commit_msg>Move distribution.<commit_after>var hash = require('hash.murmur3.32')
var fnv = require('b-tree/benchmark/fnv')

function Router (options) {
    this.distribute(options.delegates)
    this.extract = options.extract
}

Router.prototype.match = function (obj) {
    var key = this.extract(obj)
    return this.buckets[fnv(new Buffer(key), 0, Buffer.byteLength(key)).readUIntLE(0, 1)].url
}

Router.prototype.distribute = function (delegates) {
    var length = 256, dist = Math.floor(length / delegates.length)
    this.delegates = delegates
    this.buckets = []
    this.delegates.forEach(function (del) {
        for (var i=0; i<dist; i++) {
            length--
            this.buckets.push({
                url: del
            })
        }
    }, this)
    while (length-- > 0) {
        this.buckets.push({ url: this.delegates[this.delegates.length - 1] })
    }
}

Router.prototype.newDelegate = function (del) {
// redistribute buckets
}

Router.prototype.removeDelegate = function () {
// keep old config until migration complete
}

exports.Router = Router
<|endoftext|>


<commit_before>define(["js/core/Application", "js/core/I18n", "app/model/Todo", "app/collection/TodoList", "js/data/ListView"],
    function (Application, I18n, Todo, TodoList, ListView) {

        var ENTER_KEY = 13;

        return Application.inherit("app.TodoClass", {
            inject: {
                i18n: I18n
            },
            /**
             * Initializes the app
             * In this method we set the initial models
             */
            initialize: function () {

                this.set("todoList", new TodoList());
                this.set("filterList", new ListView({
                    list: this.get("todoList"),
                    filter: 'all',
                    filterFnc: function (item) {
                        var filter = this.$.filter;
                        if (filter == "active") {
                            return !item.isCompleted();
                        } else if (filter == "completed") {
                            return item.isCompleted();
                        } else {
                            return true;
                        }
                    }}));
                this.set("newTodo", new Todo());
                this.set("locales", ["en_EN", "de_DE"]);
            },
            /**
             * Are triggered
             */
            showAll: function () {
                this.$.filterList.set("filter", 'all');
            },
            showActive: function () {
                this.$.filterList.set("filter", "active");
            },
            showCompleted: function () {
                this.$.filterList.set("filter", "completed");
            },
            isStringEqual: function (route, filter) {
                return route == filter;
            },
            /**
             * The rest is just controller stuff
             */
            addNewTodo: function (e) {
                if (e.$.keyCode === ENTER_KEY) {
                    var tmp = this.get("newTodo");
                    if (tmp.hasTitle()) {
                        var newTodo = new Todo({title: tmp.get("title")});
                        newTodo.setCompleted(false);
                        this.get("todoList").add(newTodo);
                        tmp.set("title", "");
                    }
                }
            },
            markAllComplete: function (e, input) {
                this.get("todoList").markAll(input.get("checked"));
            },
            clearCompleted: function (e) {
                this.get("todoList").clearCompleted();
            },
            removeTodo: function (e, el) {
                this.get("todoList").remove(e.$);
            },
            sort: function () {
                this.get("todoList").sort(function (t1, t2) {
                    if (t1.get("isDone") && t2.get("isDone")) {
                        return 0;
                    } else if (t1.get("isDone") === true && !t2.get("isDone")) {
                        return 1;
                    } else {
                        return -1;
                    }
                });
            },
            /**
             * Start the application and render it to the body ...
             */
            start: function (parameter, callback) {
                // false - disables autostart
                this.callBase(parameter, false);

                this.$.i18n.set("locale", "en_EN", {silent: true});
                this.$.i18n.loadLocale("en_EN", callback);
            }
        });
    });<commit_msg>removed sort function<commit_after>define(["js/core/Application", "js/core/I18n", "app/model/Todo", "app/collection/TodoList", "js/data/ListView"],
    function (Application, I18n, Todo, TodoList, ListView) {

        var ENTER_KEY = 13;

        return Application.inherit("app.TodoClass", {
            inject: {
                i18n: I18n
            },
            /**
             * Initializes the app
             * In this method we set the initial models
             */
            initialize: function () {

                this.set("todoList", new TodoList());
                this.set("filterList", new ListView({
                    list: this.get("todoList"),
                    filter: 'all',
                    filterFnc: function (item) {
                        var filter = this.$.filter;
                        if (filter == "active") {
                            return !item.isCompleted();
                        } else if (filter == "completed") {
                            return item.isCompleted();
                        } else {
                            return true;
                        }
                    }}));
                this.set("newTodo", new Todo());
                this.set("locales", ["en_EN", "de_DE"]);
            },
            /**
             * Are triggered
             */
            showAll: function () {
                this.$.filterList.set("filter", 'all');
            },
            showActive: function () {
                this.$.filterList.set("filter", "active");
            },
            showCompleted: function () {
                this.$.filterList.set("filter", "completed");
            },
            isStringEqual: function (route, filter) {
                return route == filter;
            },
            /**
             * The rest is just controller stuff
             */
            addNewTodo: function (e) {
                if (e.$.keyCode === ENTER_KEY) {
                    var tmp = this.get("newTodo");
                    if (tmp.hasTitle()) {
                        var newTodo = new Todo({title: tmp.get("title")});
                        newTodo.setCompleted(false);
                        this.get("todoList").add(newTodo);
                        tmp.set("title", "");
                    }
                }
            },
            markAllComplete: function (e, input) {
                this.get("todoList").markAll(input.get("checked"));
            },
            clearCompleted: function (e) {
                this.get("todoList").clearCompleted();
            },
            removeTodo: function (e, el) {
                this.get("todoList").remove(e.$);
            },
            /**
             * Start the application and render it to the body ...
             */
            start: function (parameter, callback) {
                // false - disables autostart
                this.callBase(parameter, false);

                this.$.i18n.set("locale", "en_EN", {silent: true});
                this.$.i18n.loadLocale("en_EN", callback);
            }
        });
    });<|endoftext|>


<commit_before>var ical  = require('ical');
var _     = require('lodash');
var chalk = require('chalk');

var ade = {}

cleanEvent = function(event) {
	return _.omit(
		event,
		'params',
		'dtstamp',
		'uid',
		'created',
		'last-modified',
		'sequence',
		'type'
	);
}

ade.refreshRoomsCache = function() {
	ical.fromURL(
		"https://planif.esiee.fr:8443/jsp/custom/modules/plannings/anonymous_cal.jsp?resources=682,683,684,685,772,719,2841,2556,2112,749,183,185,196,4051,4679,2072,2074,2272,2276,2089,154,700,713,737,774,163,167,701,705,707,708,712,714,715,716,724,725,726,758,759,1057,1858,1908,2090,2108,2281,717,720,721,722,2265,2274,2279,2842&projectId=4&calType=ical&nbWeeks=4",
		{},
		function(err, data) {
			if(err)
				console.error(err);

			var cacheLogPrefix = chalk.bold.underline("[Cache]") + " ";

			console.log(cacheLogPrefix + "Removing previous cache.");
			ade.Activity.remove({}, function(err, activity) {
				if(err)
					console.error(err);
			});

			console.log(cacheLogPrefix + "Populating cache.");
			_.each(data, function(event) {
				var activity = new ade.Activity();

				if(event.location == "M.D."
				   || event.location == "05-Examens-si-multi classes"
				   || event.location == "04-Examens") {
					console.warn(cacheLogPrefix + 'Ommiting event in "' + event.location + '"');
				} else {
					activity.name = event.summary;
					activity.room = event.location;
					activity.start = event.start;
					activity.end  = event.end;

					activity.save(function(err) {
						if(err)
							console.error(err);
					});
				}
			});
			console.log(cacheLogPrefix + "Done.");
		}
	);
}

module.exports = function(Activity) {
	ade.Activity = Activity;
	return {'refreshRoomsCache': ade.refreshRoomsCache};
}
<commit_msg>ADE module refactoring (more clear)<commit_after>var ical  = require('ical');
var _     = require('lodash');
var chalk = require('chalk');

var ade = {}

var cacheLogPrefix = chalk.bold.underline("[Cache]") + " ";

ade.refreshRoomsCache = function() {
	ical.fromURL(
		"https://planif.esiee.fr:8443/jsp/custom/modules/plannings/anonymous_cal.jsp?resources=682,683,684,685,772,719,2841,2556,2112,749,183,185,196,4051,4679,2072,2074,2272,2276,2089,154,700,713,737,774,163,167,701,705,707,708,712,714,715,716,724,725,726,758,759,1057,1858,1908,2090,2108,2281,717,720,721,722,2265,2274,2279,2842&projectId=4&calType=ical&nbWeeks=4",
		{},
		function(err, data) {
			if(err)
				console.error(err);

			console.log(cacheLogPrefix + "Removing previous cache.");
			clearCache();

			console.log(cacheLogPrefix + "Populating cache.");
			populateCache(data);
			console.log(cacheLogPrefix + "Done.");
		}
	);
}

clearCache = function() {
	ade.Activity.remove({}, function(err, activity) {
		if(err)
			console.error(err);
	});
}

populateCache = function(data) {
	_.each(data, function(event) {
		if(event.location == "M.D."
		   || event.location == "05-Examens-si-multi classes"
		   || event.location == "04-Examens") {
			console.warn(cacheLogPrefix + 'Ommiting event in "' + event.location + '"');
		} else {
			saveEvent(event);
		}
	});
}

saveEvent = function(event) {
	var activity = new ade.Activity();

	activity.name = event.summary;
	activity.room = event.location;
	activity.start = event.start;
	activity.end  = event.end;

	activity.save(function(err) {
		if(err)
			console.error(err);
	});
}

module.exports = function(Activity) {
	ade.Activity = Activity;
	return {'refreshRoomsCache': ade.refreshRoomsCache};
}
<|endoftext|>