<commit_before>Cryptodog.multiParty = function() {};

(function() {
    'use strict';

    var usedIVs = [];

    var correctIvLength = function(iv) {
        var ivAsWordArray = CryptoJS.enc.Base64.parse(iv);
        var ivAsArray = ivAsWordArray.words;

        // Adds 0 as the 4th element, causing the equivalent
        // bytestring to have a length of 16 bytes, with
        // \x00\x00\x00\x00 at the end.
        // Without this, crypto-js will take in a counter of
        // 12 bytes, and the first 2 counter iterations will
        // use 0, instead of 0 and then 1.
        // See https://github.com/cryptocat/cryptocat/issues/258
        ivAsArray.push(0);

        return CryptoJS.lib.WordArray.create(ivAsArray);
    };

    // AES-CTR-256 encryption
    // No padding, starting IV of 0
    // Input: WordArray, Output: Base64
    // Key input: WordArray
    var encryptAES = function(msg, c, iv) {
        var opts = {
            mode: CryptoJS.mode.CTR,
            iv: correctIvLength(iv),
            padding: CryptoJS.pad.NoPadding
        };
        var aesctr = CryptoJS.AES.encrypt(msg, c, opts);
        return aesctr.toString();
    };

    // AES-CTR-256 decryption
    // No padding, starting IV of 0
    // Input: Base64, Output: WordArray
    // Key input: WordArray
    var decryptAES = function(msg, c, iv) {
        var opts = {
            mode: CryptoJS.mode.CTR,
            iv: correctIvLength(iv),
            padding: CryptoJS.pad.NoPadding
        };
        var aesctr = CryptoJS.AES.decrypt(msg, c, opts);
        return aesctr;
    };

    // HMAC-SHA512
    // Output: Base64
    // Key input: WordArray
    var HMAC = function(msg, key) {
        return CryptoJS.HmacSHA512(msg, key).toString(CryptoJS.enc.Base64);
    };

    Cryptodog.multiParty.maxMessageLength = 5000;

    // Generate private key (32 random bytes)
    // Represented as BigInt
    Cryptodog.multiParty.genPrivateKey = function() {
        return BigInt.randBigInt(256);
    };

    // Generate public key (Curve 25519 Diffie-Hellman with basePoint 9)
    // Represented as BigInt
    Cryptodog.multiParty.genPublicKey = function(privateKey) {
        return Curve25519.ecDH(privateKey);
    };

    // Generate shared secrets
    // First 256 bytes are for encryption, last 256 bytes are for HMAC.
    // Represented as WordArrays
    Cryptodog.multiParty.genSharedSecret = function(nickname) {
        // I need to convert the BigInt to WordArray here. I do it using the Base64 representation.
        var sharedSecret = CryptoJS.SHA512(
            CryptoJS.enc.Base64.parse(
                BigInt.bigInt2base64(
                    Curve25519.ecDH(Cryptodog.me.mpPrivateKey, Cryptodog.buddies[nickname].mpPublicKey),
                    32
                )
            )
        );

        return {
            message: CryptoJS.lib.WordArray.create(sharedSecret.words.slice(0, 8)),
            hmac: CryptoJS.lib.WordArray.create(sharedSecret.words.slice(8, 16))
        };
    };

    // Get fingerprint
    // If nickname is null, returns own fingerprint
    Cryptodog.multiParty.genFingerprint = function(nickname) {
        var key = Cryptodog.me.mpPublicKey;
        if (nickname) {
            key = Cryptodog.buddies[nickname].mpPublicKey;
        }
        return CryptoJS.SHA512(CryptoJS.enc.Base64.parse(BigInt.bigInt2base64(key, 32)))
            .toString()
            .substring(0, 40)
            .toUpperCase();
    };

    Cryptodog.multiParty.PublicKey = function(key) {
        this.type = 'public_key';
        this.text = BigInt.bigInt2base64(key, 32);
    };

    Cryptodog.multiParty.PublicKeyRequest = function(name) {
        this.type = 'public_key_request';
        if (name) {
            this.text = name;
        } else {
            this.text = '';
        }
    };

    // Issue a warning for decryption failure to the main conversation window
    Cryptodog.multiParty.messageWarning = function(sender) {
        var messageWarning = Cryptodog.locale['warnings']['messageWarning'].replace('(NICKNAME)', sender);
        Cryptodog.addToConversation(messageWarning, sender, 'groupChat', 'warning');
    };

    // Generate message tag. 8 rounds of SHA512
    // Input: WordArray
    // Output: Base64
    Cryptodog.multiParty.messageTag = function(message) {
        for (var i = 0; i < 8; i++) {
            message = CryptoJS.SHA512(message);
        }

        return message.toString(CryptoJS.enc.Base64);
    };

    Cryptodog.multiParty.sendMessage = function(message) {
        // Convert from UTF8
        message = CryptoJS.enc.Utf8.parse(message);

        // Add 64 bytes of padding
        message.concat(Cryptodog.random.rawBytes(64));

        var encrypted = {
            text: {},
            type: 'message'
        };

        var sortedRecipients = [];
        for (var b in Cryptodog.buddies) {
            if (Cryptodog.buddies[b].mpSecretKey) {
                sortedRecipients.push(b);
            }
        }

        sortedRecipients.sort();

        var hmac = CryptoJS.lib.WordArray.create();

        for (var i = 0; i < sortedRecipients.length; i++) {
            // Generate a random IV
            var iv = Cryptodog.random.encodedBytes(12, CryptoJS.enc.Base64);

            // Do not reuse IVs
            while (usedIVs.indexOf(iv) >= 0) {
                iv = Cryptodog.random.encodedBytes(12, CryptoJS.enc.Base64);
            }

            usedIVs.push(iv);

            // Encrypt the message
            encrypted['text'][sortedRecipients[i]] = {};

            encrypted['text'][sortedRecipients[i]]['message'] = encryptAES(
                message,
                Cryptodog.buddies[sortedRecipients[i]].mpSecretKey['message'],
                iv
            );

            encrypted['text'][sortedRecipients[i]]['iv'] = iv;

            // Append to HMAC
            hmac.concat(CryptoJS.enc.Base64.parse(encrypted['text'][sortedRecipients[i]]['message']));
            hmac.concat(CryptoJS.enc.Base64.parse(encrypted['text'][sortedRecipients[i]]['iv']));
        }

        encrypted['tag'] = message.clone();

        for (var i = 0; i < sortedRecipients.length; i++) {
            // Compute the HMAC
            encrypted['text'][sortedRecipients[i]]['hmac'] = HMAC(
                hmac,
                Cryptodog.buddies[sortedRecipients[i]].mpSecretKey['hmac']
            );

            // Append to tag
            encrypted['tag'].concat(CryptoJS.enc.Base64.parse(encrypted['text'][sortedRecipients[i]]['hmac']));
        }

        // Compute tag
        encrypted['tag'] = Cryptodog.multiParty.messageTag(encrypted['tag']);
        return JSON.stringify(encrypted);
    };

    Cryptodog.multiParty.receiveMessage = function(sender, myName, message) {
        var buddy = Cryptodog.buddies[sender];

        try {
            message = JSON.parse(message);
        } catch (err) {
            console.log('multiParty: failed to parse message object');
            return false;
        }

        var type = message.type;

        if (type === 'public_key') {
            if (typeof message.text !== 'string') {
                console.log('multiParty: invalid public key from ' + sender);
                return false;
            }

            var publicKey = BigInt.base642bigInt(message.text);

            if (buddy.mpPublicKey && BigInt.equals(buddy.mpPublicKey, publicKey)) {
                // We already have this key.
                return false;
            } else if (buddy.mpPublicKey && !BigInt.equals(buddy.mpPublicKey, publicKey)) {
                // If it's a different key than the one we have, warn user.
                Cryptodog.UI.removeAuthAndWarn(sender);
            } else if (!buddy.mpPublicKey && buddy.authenticated) {
                // If we're missing their key and they're authenticated, warn user (prevents a possible active attack).
                Cryptodog.UI.removeAuthAndWarn(sender);
            }

            buddy.updateMpKeys(publicKey);
        } else if (type === 'public_key_request') {
            if (!message.text || message.text === Cryptodog.me.nickname) {
                Cryptodog.xmpp.sendPublicKey();
            }
        } else if (type === 'message') {
            var text = message['text'];

            if (typeof text[myName] !== 'object') {
                console.log('multiParty: invalid message from ' + sender);
                Cryptodog.multiParty.messageWarning(sender);
                return false;
            } else {
                if (!(buddy.mpSecretKey)) {
                    // We don't have the sender's key - they're "borked".
                    // Request their key and warn the user.
                    console.log('Requesting public key from ' + sender);
                    Cryptodog.xmpp.requestPublicKey(sender);
                    Cryptodog.multiParty.messageWarning(sender);
                    return false;
                }

                var recipients = Object.keys(Cryptodog.buddies);
                recipients.push(Cryptodog.me.nickname);
                recipients.splice(recipients.indexOf(sender), 1);

                // Find missing recipients: those for whom the message isn't encrypted
                var missingRecipients = [];

                for (var i = 0; i < recipients.length; i++) {
                    try {
                        if (typeof text[recipients[i]] === 'object') {
                            var noMessage = typeof text[recipients[i]]['message'] !== 'string';
                            var noIV = typeof text[recipients[i]]['iv'] !== 'string';
                            var noHMAC = typeof text[recipients[i]]['hmac'] !== 'string';

                            if (noMessage || noIV || noHMAC) {
                                missingRecipients.push(recipients[i]);
                            }
                        } else {
                            missingRecipients.push(recipients[i]);
                        }
                    } catch (err) {
                        missingRecipients.push(recipients[i]);
                    }
                }

                if (missingRecipients.length) {
                    Cryptodog.addToConversation(missingRecipients, sender, 'groupChat', 'missingRecipients');
                }

                // Sort recipients
                var sortedRecipients = Object.keys(text).sort();

                // Check HMAC
                var hmac = CryptoJS.lib.WordArray.create();

                for (var i = 0; i < sortedRecipients.length; i++) {
                    if (missingRecipients.indexOf(sortedRecipients[i]) < 0) {
                        hmac.concat(CryptoJS.enc.Base64.parse(text[sortedRecipients[i]]['message']));
                        hmac.concat(CryptoJS.enc.Base64.parse(text[sortedRecipients[i]]['iv']));
                    }
                }

                if (!OTR.HLP.compare(text[myName]['hmac'], HMAC(hmac, buddy.mpSecretKey['hmac']))) {
                    console.log('multiParty: HMAC failure');
                    Cryptodog.multiParty.messageWarning(sender);
                    return false;
                }

                // Check IV reuse
                if (usedIVs.indexOf(text[myName]['iv']) >= 0) {
                    console.log('multiParty: IV reuse detected, possible replay attack');
                    Cryptodog.multiParty.messageWarning(sender);
                    return false;
                }

                usedIVs.push(text[myName]['iv']);

                if (text[myName]['message'].length > Cryptodog.multiParty.maxMessageLength) {
                    Cryptodog.multiParty.messageWarning(sender);
                    console.log('multiParty: refusing to decrypt large message (' + text[myName]['message'].length + ' bytes) from ' + sender);
                    return false;
                }

                // Decrypt
                var plaintext = decryptAES(
                    text[myName]['message'],
                    buddy.mpSecretKey['message'],
                    text[myName]['iv']
                );

                // Check tag
                var messageTag = plaintext.clone();
                for (var i = 0; i < sortedRecipients.length; i++) {
                    messageTag.concat(CryptoJS.enc.Base64.parse(text[sortedRecipients[i]]['hmac']));
                }

                if (Cryptodog.multiParty.messageTag(messageTag) !== message['tag']) {
                    console.log('multiParty: message tag failure');
                    Cryptodog.multiParty.messageWarning(sender);
                    return false;
                }

                // Remove padding
                if (plaintext.sigBytes < 64) {
                    console.log('multiParty: invalid plaintext size');
                    Cryptodog.multiParty.messageWarning(sender);
                    return false;
                }

                plaintext = CryptoJS.lib.WordArray.create(plaintext.words, plaintext.sigBytes - 64);

                // Convert to UTF8
                return plaintext.toString(CryptoJS.enc.Utf8);
            }
        } else {
            console.log('multiParty: unknown message type "' + type + '" from ' + sender);
        }

        return false;
    };

    // Reset everything except my own key pair
    Cryptodog.multiParty.reset = function() {
        usedIVs = [];
    };
})();
<commit_msg>Fix DoS via non-UTF-8 messages (credit: @superp00t, @pavona)<commit_after>Cryptodog.multiParty = function() {};

(function() {
    'use strict';

    var usedIVs = [];

    var correctIvLength = function(iv) {
        var ivAsWordArray = CryptoJS.enc.Base64.parse(iv);
        var ivAsArray = ivAsWordArray.words;

        // Adds 0 as the 4th element, causing the equivalent
        // bytestring to have a length of 16 bytes, with
        // \x00\x00\x00\x00 at the end.
        // Without this, crypto-js will take in a counter of
        // 12 bytes, and the first 2 counter iterations will
        // use 0, instead of 0 and then 1.
        // See https://github.com/cryptocat/cryptocat/issues/258
        ivAsArray.push(0);

        return CryptoJS.lib.WordArray.create(ivAsArray);
    };

    // AES-CTR-256 encryption
    // No padding, starting IV of 0
    // Input: WordArray, Output: Base64
    // Key input: WordArray
    var encryptAES = function(msg, c, iv) {
        var opts = {
            mode: CryptoJS.mode.CTR,
            iv: correctIvLength(iv),
            padding: CryptoJS.pad.NoPadding
        };
        var aesctr = CryptoJS.AES.encrypt(msg, c, opts);
        return aesctr.toString();
    };

    // AES-CTR-256 decryption
    // No padding, starting IV of 0
    // Input: Base64, Output: WordArray
    // Key input: WordArray
    var decryptAES = function(msg, c, iv) {
        var opts = {
            mode: CryptoJS.mode.CTR,
            iv: correctIvLength(iv),
            padding: CryptoJS.pad.NoPadding
        };
        var aesctr = CryptoJS.AES.decrypt(msg, c, opts);
        return aesctr;
    };

    // HMAC-SHA512
    // Output: Base64
    // Key input: WordArray
    var HMAC = function(msg, key) {
        return CryptoJS.HmacSHA512(msg, key).toString(CryptoJS.enc.Base64);
    };

    Cryptodog.multiParty.maxMessageLength = 5000;

    // Generate private key (32 random bytes)
    // Represented as BigInt
    Cryptodog.multiParty.genPrivateKey = function() {
        return BigInt.randBigInt(256);
    };

    // Generate public key (Curve 25519 Diffie-Hellman with basePoint 9)
    // Represented as BigInt
    Cryptodog.multiParty.genPublicKey = function(privateKey) {
        return Curve25519.ecDH(privateKey);
    };

    // Generate shared secrets
    // First 256 bytes are for encryption, last 256 bytes are for HMAC.
    // Represented as WordArrays
    Cryptodog.multiParty.genSharedSecret = function(nickname) {
        // I need to convert the BigInt to WordArray here. I do it using the Base64 representation.
        var sharedSecret = CryptoJS.SHA512(
            CryptoJS.enc.Base64.parse(
                BigInt.bigInt2base64(
                    Curve25519.ecDH(Cryptodog.me.mpPrivateKey, Cryptodog.buddies[nickname].mpPublicKey),
                    32
                )
            )
        );

        return {
            message: CryptoJS.lib.WordArray.create(sharedSecret.words.slice(0, 8)),
            hmac: CryptoJS.lib.WordArray.create(sharedSecret.words.slice(8, 16))
        };
    };

    // Get fingerprint
    // If nickname is null, returns own fingerprint
    Cryptodog.multiParty.genFingerprint = function(nickname) {
        var key = Cryptodog.me.mpPublicKey;
        if (nickname) {
            key = Cryptodog.buddies[nickname].mpPublicKey;
        }
        return CryptoJS.SHA512(CryptoJS.enc.Base64.parse(BigInt.bigInt2base64(key, 32)))
            .toString()
            .substring(0, 40)
            .toUpperCase();
    };

    Cryptodog.multiParty.PublicKey = function(key) {
        this.type = 'public_key';
        this.text = BigInt.bigInt2base64(key, 32);
    };

    Cryptodog.multiParty.PublicKeyRequest = function(name) {
        this.type = 'public_key_request';
        if (name) {
            this.text = name;
        } else {
            this.text = '';
        }
    };

    // Issue a warning for decryption failure to the main conversation window
    Cryptodog.multiParty.messageWarning = function(sender) {
        var messageWarning = Cryptodog.locale['warnings']['messageWarning'].replace('(NICKNAME)', sender);
        Cryptodog.addToConversation(messageWarning, sender, 'groupChat', 'warning');
    };

    // Generate message tag. 8 rounds of SHA512
    // Input: WordArray
    // Output: Base64
    Cryptodog.multiParty.messageTag = function(message) {
        for (var i = 0; i < 8; i++) {
            message = CryptoJS.SHA512(message);
        }

        return message.toString(CryptoJS.enc.Base64);
    };

    Cryptodog.multiParty.sendMessage = function(message) {
        // Convert from UTF8
        message = CryptoJS.enc.Utf8.parse(message);

        // Add 64 bytes of padding
        message.concat(Cryptodog.random.rawBytes(64));

        var encrypted = {
            text: {},
            type: 'message'
        };

        var sortedRecipients = [];
        for (var b in Cryptodog.buddies) {
            if (Cryptodog.buddies[b].mpSecretKey) {
                sortedRecipients.push(b);
            }
        }

        sortedRecipients.sort();

        var hmac = CryptoJS.lib.WordArray.create();

        for (var i = 0; i < sortedRecipients.length; i++) {
            // Generate a random IV
            var iv = Cryptodog.random.encodedBytes(12, CryptoJS.enc.Base64);

            // Do not reuse IVs
            while (usedIVs.indexOf(iv) >= 0) {
                iv = Cryptodog.random.encodedBytes(12, CryptoJS.enc.Base64);
            }

            usedIVs.push(iv);

            // Encrypt the message
            encrypted['text'][sortedRecipients[i]] = {};

            encrypted['text'][sortedRecipients[i]]['message'] = encryptAES(
                message,
                Cryptodog.buddies[sortedRecipients[i]].mpSecretKey['message'],
                iv
            );

            encrypted['text'][sortedRecipients[i]]['iv'] = iv;

            // Append to HMAC
            hmac.concat(CryptoJS.enc.Base64.parse(encrypted['text'][sortedRecipients[i]]['message']));
            hmac.concat(CryptoJS.enc.Base64.parse(encrypted['text'][sortedRecipients[i]]['iv']));
        }

        encrypted['tag'] = message.clone();

        for (var i = 0; i < sortedRecipients.length; i++) {
            // Compute the HMAC
            encrypted['text'][sortedRecipients[i]]['hmac'] = HMAC(
                hmac,
                Cryptodog.buddies[sortedRecipients[i]].mpSecretKey['hmac']
            );

            // Append to tag
            encrypted['tag'].concat(CryptoJS.enc.Base64.parse(encrypted['text'][sortedRecipients[i]]['hmac']));
        }

        // Compute tag
        encrypted['tag'] = Cryptodog.multiParty.messageTag(encrypted['tag']);
        return JSON.stringify(encrypted);
    };

    Cryptodog.multiParty.receiveMessage = function(sender, myName, message) {
        var buddy = Cryptodog.buddies[sender];

        try {
            message = JSON.parse(message);
        } catch (err) {
            console.log('multiParty: failed to parse message object');
            return false;
        }

        var type = message.type;

        if (type === 'public_key') {
            if (typeof message.text !== 'string') {
                console.log('multiParty: invalid public key from ' + sender);
                return false;
            }

            var publicKey = BigInt.base642bigInt(message.text);

            if (buddy.mpPublicKey && BigInt.equals(buddy.mpPublicKey, publicKey)) {
                // We already have this key.
                return false;
            } else if (buddy.mpPublicKey && !BigInt.equals(buddy.mpPublicKey, publicKey)) {
                // If it's a different key than the one we have, warn user.
                Cryptodog.UI.removeAuthAndWarn(sender);
            } else if (!buddy.mpPublicKey && buddy.authenticated) {
                // If we're missing their key and they're authenticated, warn user (prevents a possible active attack).
                Cryptodog.UI.removeAuthAndWarn(sender);
            }

            buddy.updateMpKeys(publicKey);
        } else if (type === 'public_key_request') {
            if (!message.text || message.text === Cryptodog.me.nickname) {
                Cryptodog.xmpp.sendPublicKey();
            }
        } else if (type === 'message') {
            var text = message['text'];

            if (typeof text[myName] !== 'object') {
                console.log('multiParty: invalid message from ' + sender);
                Cryptodog.multiParty.messageWarning(sender);
                return false;
            } else {
                if (!(buddy.mpSecretKey)) {
                    // We don't have the sender's key - they're "borked".
                    // Request their key and warn the user.
                    console.log('Requesting public key from ' + sender);
                    Cryptodog.xmpp.requestPublicKey(sender);
                    Cryptodog.multiParty.messageWarning(sender);
                    return false;
                }

                var recipients = Object.keys(Cryptodog.buddies);
                recipients.push(Cryptodog.me.nickname);
                recipients.splice(recipients.indexOf(sender), 1);

                // Find missing recipients: those for whom the message isn't encrypted
                var missingRecipients = [];

                for (var i = 0; i < recipients.length; i++) {
                    try {
                        if (typeof text[recipients[i]] === 'object') {
                            var noMessage = typeof text[recipients[i]]['message'] !== 'string';
                            var noIV = typeof text[recipients[i]]['iv'] !== 'string';
                            var noHMAC = typeof text[recipients[i]]['hmac'] !== 'string';

                            if (noMessage || noIV || noHMAC) {
                                missingRecipients.push(recipients[i]);
                            }
                        } else {
                            missingRecipients.push(recipients[i]);
                        }
                    } catch (err) {
                        missingRecipients.push(recipients[i]);
                    }
                }

                if (missingRecipients.length) {
                    Cryptodog.addToConversation(missingRecipients, sender, 'groupChat', 'missingRecipients');
                }

                // Sort recipients
                var sortedRecipients = Object.keys(text).sort();

                // Check HMAC
                var hmac = CryptoJS.lib.WordArray.create();

                for (var i = 0; i < sortedRecipients.length; i++) {
                    if (missingRecipients.indexOf(sortedRecipients[i]) < 0) {
                        hmac.concat(CryptoJS.enc.Base64.parse(text[sortedRecipients[i]]['message']));
                        hmac.concat(CryptoJS.enc.Base64.parse(text[sortedRecipients[i]]['iv']));
                    }
                }

                if (!OTR.HLP.compare(text[myName]['hmac'], HMAC(hmac, buddy.mpSecretKey['hmac']))) {
                    console.log('multiParty: HMAC failure');
                    Cryptodog.multiParty.messageWarning(sender);
                    return false;
                }

                // Check IV reuse
                if (usedIVs.indexOf(text[myName]['iv']) >= 0) {
                    console.log('multiParty: IV reuse detected, possible replay attack');
                    Cryptodog.multiParty.messageWarning(sender);
                    return false;
                }

                usedIVs.push(text[myName]['iv']);

                if (text[myName]['message'].length > Cryptodog.multiParty.maxMessageLength) {
                    Cryptodog.multiParty.messageWarning(sender);
                    console.log('multiParty: refusing to decrypt large message (' + text[myName]['message'].length + ' bytes) from ' + sender);
                    return false;
                }

                // Decrypt
                var plaintext = decryptAES(
                    text[myName]['message'],
                    buddy.mpSecretKey['message'],
                    text[myName]['iv']
                );

                // Check tag
                var messageTag = plaintext.clone();
                for (var i = 0; i < sortedRecipients.length; i++) {
                    messageTag.concat(CryptoJS.enc.Base64.parse(text[sortedRecipients[i]]['hmac']));
                }

                if (Cryptodog.multiParty.messageTag(messageTag) !== message['tag']) {
                    console.log('multiParty: message tag failure');
                    Cryptodog.multiParty.messageWarning(sender);
                    return false;
                }

                // Remove padding
                if (plaintext.sigBytes < 64) {
                    console.log('multiParty: invalid plaintext size');
                    Cryptodog.multiParty.messageWarning(sender);
                    return false;
                }

                plaintext = CryptoJS.lib.WordArray.create(plaintext.words, plaintext.sigBytes - 64);

                try {
                    return plaintext.toString(CryptoJS.enc.Utf8);
                } catch (e) {
                    console.log('multiParty: invalid UTF-8 message from ' + sender);
                }
            }
        } else {
            console.log('multiParty: unknown message type "' + type + '" from ' + sender);
        }

        return false;
    };

    // Reset everything except my own key pair
    Cryptodog.multiParty.reset = function() {
        usedIVs = [];
    };
})();
<|endoftext|>


<commit_before>const { createFilePath } = require(`gatsby-source-filesystem`)

// To add the slug field to each post
exports.onCreateNode = ({ node, getNode, actions }) => {
  const { createNodeField } = actions
  // Ensures we are processing only markdown files
  if (node.internal.type === "MarkdownRemark") {
    // Use `createFilePath` to turn markdown files in our `data/faqs` directory into `/faqs/slug`
    const slug = createFilePath({
      node,
      getNode,
      basePath: "pages",
    })

    // Creates new query'able field with name of 'slug'
    createNodeField({
      node,
      name: "slug",
      value: `/${slug.slice(12)}`,
    })
  }
}
<commit_msg>feat: add a new method to create pages<commit_after>const path = require('path')
const { createFilePath } = require(`gatsby-source-filesystem`)

// To add the slug field to each post
exports.onCreateNode = ({ node, getNode, actions }) => {
  const { createNodeField } = actions
  // Ensures we are processing only markdown files
  if (node.internal.type === "MarkdownRemark") {
    // Use `createFilePath` to turn markdown files in our `data/faqs` directory into `/faqs/slug`
    const slug = createFilePath({
      node,
      getNode,
      basePath: "pages",
    })

    // Creates new query'able field with name of 'slug'
    createNodeField({
      node,
      name: "slug",
      value: `/${slug.slice(12)}`,
    })
  }
}

exports.createPages = ({ graphql, actions }) => {
  const { createPage } = actions

  return graphql(`
    {
      allMarkdownRemark {
        edges {
          node {
            fields {
              slug
            }
          }
        }
      }
    }
  `).then(result => {
    result.data.allMarkdownRemark.edges.forEach(({ node }) => {
      createPage({
        path: node.fields.slug,
        component: path.resolve('./src/templates/blog-post.js'),
        context: {
          slug: node.fields.slug
        }
      })
    })
  })
}
<|endoftext|>


<commit_before>const choo = require('choo');
const html = require('choo/html');
const util = require('./util');
const app = choo();

var localStorage = window.localStorage;
var socket;
var unread = 0;
var isBlurred = false;

app.model({
    state: {
        now: new Date(),
        users: [],
        messages: [],
        nick: localStorage.nick ? localStorage.nick : '',
        loggedIn: false,
        connected: false,
        showOptions: false,
        notify: localStorage.notify === 'true',
        dark: localStorage.dark === 'true',
        boss: localStorage.boss === 'true'
    },
    reducers: {
        setNick: (nick, state) => {
            localStorage.nick = nick;
            return {nick: nick};
        },
        setUsers: (users, state) => ({users: users}),
        addUser: (user, state) => ({users: [...state.users, user]}),
        deleteUser: (user, state) => ({users: state.users.filter(u => u.id !== user.id)}),
        setLoggedIn: (loggedIn, state) => ({loggedIn: loggedIn}),
        setConnected: (connected, state) => ({connected: connected}),
        setShowOptions: (showOptions, state) => ({showOptions: showOptions}),
        setNotify: (notify, state) => {
            if (notify) { Notification.requestPermission(); }
            localStorage.notify = notify;
            return {notify: notify};
        },
        setDark: (dark, state) => {
            localStorage.dark = dark;
            return {dark: dark};
        },
        setBoss: (boss, state) => {
            localStorage.boss = boss;
            return {boss: boss};
        },
        receiveMessage: (message, state) => {
            if (state.messages.some(m => m.time.getTime() === message.time.getTime()))
                return state;
            if (isBlurred) {
                unread++;
                document.title = `(${unread}) nekocafe`;
                if (state.notify) {
                    var n = new Notification('nekocafe', {tag: 'nekocafe', body: `<${message.nick}> ${message.message}`});
                }
            }
            return {messages: [...state.messages, message]};
        },
        setTime: (time, state) => ({now: time})
    },
    effects: {
        sendMessage: (data, state, send, done) => {
            socket.emit('message', data);
        },
        login: (data, state, send, done) => {
            send('setLoggedIn', true, done);
            socket = io();
            socket.on('connect', () => {
                socket.emit('nick', {nick: state.nick});
                send('setConnected', true, done);
            });
            socket.on('disconnect', () => {
                send('setConnected', false, done);
            });
            socket.on('message', (msg) => {
                var embedDivs = msg.embeds.map(snippet => {
                    const div = html`<div></div>`;
                    div.innerHTML = snippet;
                    if (snippet.includes('
')) {
                        twttr.widgets.load(div);
                    }
                    return html`

                                ${div}

                    `;
                });
                send(
                    'receiveMessage',
                    {
                        time: new Date(msg.time),
                        nick: msg.nick,
                        message: msg.message,
                        embeds: embedDivs
                    },
                    done);
            });
            socket.on('users', (users) => send('setUsers', users, done));
            socket.on('join', (user) => send('addUser', user, done));
            socket.on('part', (user) => send('deleteUser', user, done));
            socket.on('time', (time) => send('setTime', new Date(time), done));
        }
    },
    subscriptions: {
        blur: (send, done) => {
            window.addEventListener('blur', () => {
                unread = 0;
                isBlurred = true;
            }, false);
            window.addEventListener('focus', () => {
                document.title = 'nekocafe';
                isBlurred = false;
            }, false);
        }
    }
});

function scrollDown() {
    var body = document.body;
    var html = document.getElementsByTagName('html')[0];
    // other browsers
    body.scrollTop = body.scrollHeight;
    // firefox
    html.scrollTop = html.scrollHeight;
}

const messageTime = (now, time) => html`
    ${util.relTime(now, time)}
`;

const messageText = (message) => {
    const msgSpan = html`<span></span>`;
    msgSpan.innerHTML = util.hotLink(util.escapeHtml(message.message));
    return html`
        ${'<' + message.nick + '>'} ${msgSpan}
    `;
};

const messageItem = (state, message, installOnload) => {
    const embeds = state.boss ? [] : message.embeds;
    if (installOnload)
        return html`

                    ${messageTime(state.now, message.time)}
                    ${messageText(message)}

                ${embeds}

        `;
    else
        return html`

                    ${messageTime(state.now, message.time)}
                    ${messageText(message)}

                ${embeds}

        `;
}

const messageList  = (state) => {
    const messages = state.messages;
    return html`

            ${messages.map((message, i) => messageItem(state, message, i+1 === messages.length))}

    `;
};

const spinner = (state) => {
    return html`<p class="navbar-text">Connecting...</div>`;
};

const loginWidget = (nick, oninput, login) => {
    return html`
        <form class="navbar-form login" onsubmit=${login}>

                <input class="form-control" type="text" name="nick" placeholder="Name"
                    oninput=${oninput} value=${nick} autofocus required />

            <button class="btn btn-primary" type="submit">Connect</button>
        </form>
    `;
}

const statusBar = (state, send) => {
    function login(e) {
        e.preventDefault();
        send('login');
    }

    function oninput(e) {
        send('setNick', e.target.value);
    }

    var status = html`<p class="navbar-text" href="">${state.users.map(u => u.nick).join(', ')}</a>`;

    return html`

                        ${!state.loggedIn ? loginWidget(state.nick, oninput, login) :
                            (state.connected ? status : spinner(state))}

                        <button class="btn btn-default navbar-btn navbar-right" type="button"
                            tabindex="-1" onclick=${() => send('setShowOptions', !state.showOptions)}>${state.showOptions? '😺' : '🐱'}</button>

    `;
};

const optionsWidget = (state, send) => {
    return html`
        <form class="options" onload=${scrollDown}>
            Online

${state.users.map(u => u.nick).join(', ')}

            Options

                    <input type="checkbox" ${state.notify ? 'checked': ''}
                        onchange=${e => send('setNotify', e.target.checked)} />
                    Notifications

                    <input type="checkbox" ${state.dark ? 'checked' : ''}
                        onchange=${e => send('setDark', e.target.checked)} />
                    Dark mode

                    <input type="checkbox" ${state.boss ? 'checked' : ''}
                        onchange=${e => send('setBoss', e.target.checked)} />
                    Boss mode

        </form>
    `;
};

function disableIf(exp) {
    return exp ? 'disabled' : '';
}

const messageBar = (state, send) => {
    function onSubmit(e) {
        e.preventDefault();
        send('sendMessage', e.target.children[0].children[0].value);
        e.target.reset();
    }
    return html`

                <form class="navbar-form messageWidget" onsubmit=${onSubmit}>

                        <input ${disableIf(!state.connected)} id="message" name="message"
                            class="form-control" placeholder="Enter message" autocomplete="off" />

                            <button ${disableIf(!state.connected)} class="btn btn-default" type="submit">Send</button>

                </form>

    `;
}

const mainView = (state, prev, send) => html`

        ${statusBar(state, send)}

            ${messageList(state)}
            ${state.showOptions ? optionsWidget(state, send) : ''}

        ${messageBar(state, send)}

`;

app.router(route => [
    route('/', mainView)
]);

const tree = app.start('#root');
<commit_msg>Revert "move embed rendering to receive thunk"<commit_after>const choo = require('choo');
const html = require('choo/html');
const util = require('./util');
const app = choo();

var localStorage = window.localStorage;
var socket;
var unread = 0;
var isBlurred = false;

app.model({
    state: {
        now: new Date(),
        users: [],
        messages: [],
        nick: localStorage.nick ? localStorage.nick : '',
        loggedIn: false,
        connected: false,
        showOptions: false,
        notify: localStorage.notify === 'true',
        dark: localStorage.dark === 'true',
        boss: localStorage.boss === 'true'
    },
    reducers: {
        setNick: (nick, state) => {
            localStorage.nick = nick;
            return {nick: nick};
        },
        setUsers: (users, state) => ({users: users}),
        addUser: (user, state) => ({users: [...state.users, user]}),
        deleteUser: (user, state) => ({users: state.users.filter(u => u.id !== user.id)}),
        setLoggedIn: (loggedIn, state) => ({loggedIn: loggedIn}),
        setConnected: (connected, state) => ({connected: connected}),
        setShowOptions: (showOptions, state) => ({showOptions: showOptions}),
        setNotify: (notify, state) => {
            if (notify) { Notification.requestPermission(); }
            localStorage.notify = notify;
            return {notify: notify};
        },
        setDark: (dark, state) => {
            localStorage.dark = dark;
            return {dark: dark};
        },
        setBoss: (boss, state) => {
            localStorage.boss = boss;
            return {boss: boss};
        },
        receiveMessage: (message, state) => {
            if (state.messages.some(m => m.time.getTime() === message.time.getTime()))
                return state;
            if (isBlurred) {
                unread++;
                document.title = `(${unread}) nekocafe`;
                if (state.notify) {
                    var n = new Notification('nekocafe', {tag: 'nekocafe', body: `<${message.nick}> ${message.message}`});
                }
            }
            return {messages: [...state.messages, message]};
        },
        setTime: (time, state) => ({now: time})
    },
    effects: {
        sendMessage: (data, state, send, done) => {
            socket.emit('message', data);
        },
        login: (data, state, send, done) => {
            send('setLoggedIn', true, done);
            socket = io();
            socket.on('connect', () => {
                socket.emit('nick', {nick: state.nick});
                send('setConnected', true, done);
            });
            socket.on('disconnect', () => {
                send('setConnected', false, done);
            });
            socket.on('message', (msg) => send(
                'receiveMessage',
                {
                    time: new Date(msg.time),
                    nick: msg.nick,
                    message: msg.message,
                    embeds: msg.embeds
                },
                done));
            socket.on('users', (users) => send('setUsers', users, done));
            socket.on('join', (user) => send('addUser', user, done));
            socket.on('part', (user) => send('deleteUser', user, done));
            socket.on('time', (time) => send('setTime', new Date(time), done));
        }
    },
    subscriptions: {
        blur: (send, done) => {
            window.addEventListener('blur', () => {
                unread = 0;
                isBlurred = true;
            }, false);
            window.addEventListener('focus', () => {
                document.title = 'nekocafe';
                isBlurred = false;
            }, false);
        }
    }
});

function scrollDown() {
    var body = document.body;
    var html = document.getElementsByTagName('html')[0];
    // other browsers
    body.scrollTop = body.scrollHeight;
    // firefox
    html.scrollTop = html.scrollHeight;
}

const messageTime = (now, time) => html`
    ${util.relTime(now, time)}
`;

const messageText = (message) => {
    const msgSpan = html`<span></span>`;
    msgSpan.innerHTML = util.hotLink(util.escapeHtml(message.message));
    return html`
        ${'<' + message.nick + '>'} ${msgSpan}
    `;
};

const embedRow = (snippet) => {
    const embed = html`<div></div>`;
    embed.innerHTML = snippet;
    twttr.widgets.load(embed);
    return html`

                ${embed}

    `;
};

const messageItem = (state, message, installOnload) => {
    const embeds = state.boss ? [] : message.embeds;
    if (installOnload)
        return html`

                    ${messageTime(state.now, message.time)}
                    ${messageText(message)}

                ${embeds.map(embedRow)}

        `;
    else
        return html`

                    ${messageTime(state.now, message.time)}
                    ${messageText(message)}

                ${embeds.map(embedRow)}

        `;
}

const messageList  = (state) => {
    const messages = state.messages;
    return html`

            ${messages.map((message, i) => messageItem(state, message, i+1 === messages.length))}

    `;
};

const spinner = (state) => {
    return html`<p class="navbar-text">Connecting...</div>`;
};

const loginWidget = (nick, oninput, login) => {
    return html`
        <form class="navbar-form login" onsubmit=${login}>

                <input class="form-control" type="text" name="nick" placeholder="Name"
                    oninput=${oninput} value=${nick} autofocus required />

            <button class="btn btn-primary" type="submit">Connect</button>
        </form>
    `;
}

const statusBar = (state, send) => {
    function login(e) {
        e.preventDefault();
        send('login');
    }

    function oninput(e) {
        send('setNick', e.target.value);
    }

    var status = html`<p class="navbar-text" href="">${state.users.map(u => u.nick).join(', ')}</a>`;

    return html`

                        ${!state.loggedIn ? loginWidget(state.nick, oninput, login) :
                            (state.connected ? status : spinner(state))}

                        <button class="btn btn-default navbar-btn navbar-right" type="button"
                            tabindex="-1" onclick=${() => send('setShowOptions', !state.showOptions)}>${state.showOptions? '😺' : '🐱'}</button>

    `;
};

const optionsWidget = (state, send) => {
    return html`
        <form class="options" onload=${scrollDown}>
            Online

${state.users.map(u => u.nick).join(', ')}

            Options

                    <input type="checkbox" ${state.notify ? 'checked': ''}
                        onchange=${e => send('setNotify', e.target.checked)} />
                    Notifications

                    <input type="checkbox" ${state.dark ? 'checked' : ''}
                        onchange=${e => send('setDark', e.target.checked)} />
                    Dark mode

                    <input type="checkbox" ${state.boss ? 'checked' : ''}
                        onchange=${e => send('setBoss', e.target.checked)} />
                    Boss mode

        </form>
    `;
};

function disableIf(exp) {
    return exp ? 'disabled' : '';
}

const messageBar = (state, send) => {
    function onSubmit(e) {
        e.preventDefault();
        send('sendMessage', e.target.children[0].children[0].value);
        e.target.reset();
    }
    return html`

                <form class="navbar-form messageWidget" onsubmit=${onSubmit}>

                        <input ${disableIf(!state.connected)} id="message" name="message"
                            class="form-control" placeholder="Enter message" autocomplete="off" />

                            <button ${disableIf(!state.connected)} class="btn btn-default" type="submit">Send</button>

                </form>

    `;
}

const mainView = (state, prev, send) => html`

        ${statusBar(state, send)}

            ${messageList(state)}
            ${state.showOptions ? optionsWidget(state, send) : ''}

        ${messageBar(state, send)}

`;

app.router(route => [
    route('/', mainView)
]);

const tree = app.start('#root');
<|endoftext|>


<commit_before>/* global Cache */
const Command = require('../Command')
const request = require('request-promise')

const Contributors = require('../../Contributors.json')

module.exports =
class WhoisCommand extends Command {
  constructor (client) {
    super(client, {
      name: 'whois',
      properName: 'Whois',
      aliases: ['roblox'],
      userPermissions: [],
      description: "`<user>` Get a user's Roblox profile link.",

      args: [
        {
          key: 'member',
          label: 'member',
          prompt: 'What is their username?',
          type: 'member',
          default: false
        }
      ]
    })
  }

  // TODO: DRY this up and make the method that gets user data a method in DiscordMember
  async fn (msg, args) {
    let member = args.member
    let data = {}
    if (member) { // If the member specified exists,
      let editMessage = await msg.reply(`:mag: Looking up ${member.displayName.replace('@', '')}`)

      let id = member.user.id
      try {
        // Read user data from memory, or request it if there isn't any cached.
        data = await Cache.get('users', id)

        if (!data) {
          data = await request({
            uri: `https://verify.eryn.io/api/user/${id}`,
            json: true,
            simple: false
          })
        }
      } catch (e) {
        return editMessage.edit("An error occured while fetching that user's data.")
      }

      if (data.status === 'ok') {
        let apiUserData = {}
        try {
          apiUserData = await request({
            uri: `http://api.roblox.com/users/${data.robloxId}`,
            json: true,
            simple: false
          })
        } catch (e) {
          return false
        }

        if (apiUserData.Username) {
          data.robloxUsername = apiUserData.Username
        }

        let profileLink = `https://www.roblox.com/users/${data.robloxId}/profile`
        let avatarURL = `https://assetgame.roblox.com/Thumbs/Avatar.ashx?username=${data.robloxUsername}`

        let bio = 'Bio failed to load'
        let joinDate = 'Unknown'
        let pastNames = 'Unknown'
        try {
          let profileSource = await request({
            uri: profileLink
          })

          joinDate = profileSource.match(/Join Date
(.*?)<li/)[1]
          bio = profileSource.match(/<meta name=description content=".*? is one of millions playing, creating and exploring the endless possibilities of Roblox. Join .*? on Roblox and explore together! ((?:.|\n)*?)"/m)[1]
          pastNames = profileSource.match(//)[1]
        } catch (e) {}

        let bc = 'Unknown'
        try {
          bc = await Cache.get(`bindings.${data.robloxId}`, 'bc')
          if (!bc) {
            let response = await request({
              uri: `https://www.roblox.com/Thumbs/BCOverlay.ashx?username=${data.robloxUsername}`,
              simple: false,
              resolveWithFullResponse: true
            })

            let url = response.request.uri.href
            bc = 'NBC'

            if (url.includes('overlay_obcOnly')) {
              bc = 'OBC'
            } else if (url.includes('overlay_tbcOnly')) {
              bc = 'TBC'
            } else if (url.includes('overlay_bcOnly')) {
              bc = 'BC'
            }

            Cache.set(`bindings.${data.robloxId}`, 'bc', bc)
          }
        } catch (e) {}

        // Make sure the data is cached so we don't have to use the API in the future
        Cache.set('users', id, data)

        let embed = {
          title: 'View Profile',
          url: profileLink,
          author: {
            name: data.robloxUsername + roverContributor,
            url: profileLink,
            icon_url: avatarURL
          },
          color: args.member.displayColor,
          thumbnail: {
            url: avatarURL
          },
          description: bio.replace('@', '@ '),
          fields: [
            { name: 'Join Date', value: joinDate, inline: true },
            { name: 'Builders Club', value: bc, inline: true },
            { name: 'Past Usernames', value: pastNames, inline: true }
          ]
        };

        if (Contributors.includes(id)) embed.fields.push({ name: 'User Tags', value: "RoVer Contributor", inline: true });

        editMessage.edit({embed}).catch(console.error);
      } else {
        editMessage.edit(`${member.displayName} doesn't seem to be verified.`)
      }
    }
  }
}
<commit_msg>Standard Linting<commit_after>/* global Cache */
const Command = require('../Command')
const request = require('request-promise')

const Contributors = require('../../Contributors.json')

module.exports =
class WhoisCommand extends Command {
  constructor (client) {
    super(client, {
      name: 'whois',
      properName: 'Whois',
      aliases: ['roblox'],
      userPermissions: [],
      description: "`<user>` Get a user's Roblox profile link.",

      args: [
        {
          key: 'member',
          label: 'member',
          prompt: 'What is their username?',
          type: 'member',
          default: false
        }
      ]
    })
  }

  // TODO: DRY this up and make the method that gets user data a method in DiscordMember
  async fn (msg, args) {
    let member = args.member
    let data = {}
    if (member) { // If the member specified exists,
      let editMessage = await msg.reply(`:mag: Looking up ${member.displayName.replace('@', '')}`)

      let id = member.user.id
      try {
        // Read user data from memory, or request it if there isn't any cached.
        data = await Cache.get('users', id)

        if (!data) {
          data = await request({
            uri: `https://verify.eryn.io/api/user/${id}`,
            json: true,
            simple: false
          })
        }
      } catch (e) {
        return editMessage.edit("An error occured while fetching that user's data.")
      }

      if (data.status === 'ok') {
        let apiUserData = {}
        try {
          apiUserData = await request({
            uri: `http://api.roblox.com/users/${data.robloxId}`,
            json: true,
            simple: false
          })
        } catch (e) {
          return false
        }

        if (apiUserData.Username) {
          data.robloxUsername = apiUserData.Username
        }

        let profileLink = `https://www.roblox.com/users/${data.robloxId}/profile`
        let avatarURL = `https://assetgame.roblox.com/Thumbs/Avatar.ashx?username=${data.robloxUsername}`

        let bio = 'Bio failed to load'
        let joinDate = 'Unknown'
        let pastNames = 'Unknown'
        try {
          let profileSource = await request({
            uri: profileLink
          })

          joinDate = profileSource.match(/Join Date
(.*?)<li/)[1]
          bio = profileSource.match(/<meta name=description content=".*? is one of millions playing, creating and exploring the endless possibilities of Roblox. Join .*? on Roblox and explore together! ((?:.|\n)*?)"/m)[1]
          pastNames = profileSource.match(//)[1]
        } catch (e) {}

        let bc = 'Unknown'
        try {
          bc = await Cache.get(`bindings.${data.robloxId}`, 'bc')
          if (!bc) {
            let response = await request({
              uri: `https://www.roblox.com/Thumbs/BCOverlay.ashx?username=${data.robloxUsername}`,
              simple: false,
              resolveWithFullResponse: true
            })

            let url = response.request.uri.href
            bc = 'NBC'

            if (url.includes('overlay_obcOnly')) {
              bc = 'OBC'
            } else if (url.includes('overlay_tbcOnly')) {
              bc = 'TBC'
            } else if (url.includes('overlay_bcOnly')) {
              bc = 'BC'
            }

            Cache.set(`bindings.${data.robloxId}`, 'bc', bc)
          }
        } catch (e) {}

        // Make sure the data is cached so we don't have to use the API in the future
        Cache.set('users', id, data)

        let embed = {
          title: 'View Profile',
          url: profileLink,
          author: {
            name: data.robloxUsername,
            url: profileLink,
            icon_url: avatarURL
          },
          color: args.member.displayColor,
          thumbnail: {
            url: avatarURL
          },
          description: bio.replace('@', '@ '),
          fields: [
            { name: 'Join Date', value: joinDate, inline: true },
            { name: 'Builders Club', value: bc, inline: true },
            { name: 'Past Usernames', value: pastNames, inline: true }
          ]
        }

        if (Contributors.includes(id)) embed.fields.push({ name: 'User Tags', value: 'RoVer Contributor', inline: true })

        editMessage.edit({embed}).catch(console.error)
      } else {
        editMessage.edit(`${member.displayName} doesn't seem to be verified.`)
      }
    }
  }
}
<|endoftext|>


<commit_before>// Foundation JavaScript
// Documentation can be found at: http://foundation.zurb.com/docs
$(document).foundation('equalizer', 'reflow');<commit_msg>Removed un-necessary reflow command<commit_after>// Foundation JavaScript
// Documentation can be found at: http://foundation.zurb.com/docs
$(document).foundation();<|endoftext|>


<commit_before>var extractTextWebpackPlugin = require('extract-text-webpack-plugin')

exports.modifyWebpackConfig = function(config, env) {
  config.merge({
    postcss (wp) {
      return [
        require('postcss-cssnext')({ browsers: ['last 20 versions', '> 2%', 'ie 9'] }),
      ]
    },
  })

  if (env === 'build-css') {
    config.removeLoader('sass');
    config.loader('sass', {
      test: /\.(sass|scss)/,
      exclude: /\.module\.(sass|scss)$/,
      loader: extractTextWebpackPlugin.extract(['css?minimize', 'postcss', 'sass']),
    })
  }

  if (env === 'develop') {
    config.removeLoader('sass');
    config.loader('sass', {
      test: /\.(sass|scss)/,
      exclude: /\.module\.(sass|scss)$/,
      loaders: ['style', 'css', 'postcss', 'sass'],
    })
  }

  return config
}
<commit_msg>wip on custom webpack config<commit_after><|endoftext|>


<commit_before>define([
	"dcl/dcl",
	"intern!object",
	"intern/chai!assert",
	"dojo/sniff",
	"delite/register",
	"deliteful/RadioButton",
	"./resources/CheckBox-shared"
], function (dcl, registerSuite, assert, has, register, RadioButton, commonSuite) {

	var container,
		html = "<form id='form1'>" +
			"<d-radio-button id='rb1' value='rb1' name='choice'></d-radio-button>" +
			"<d-radio-button id='rb2' value='rb2' name='choice' checked='true'></d-radio-button>" +
			"<d-radio-button id='rb3' value='rb3' name='choice'></d-radio-button>" +
			"</form>" +
			"<d-radio-button id='rb4'></d-radio-button>";

	var suite = {
		setup: function () {
			dcl.mix(commonSuite, {
				baseClass: "d-radio-button",
				defaultWidget: "rb4",
				labelForTarget: "rb3",
				inputType: "radio"
			});
		},

		"init state": function () {
			var d = this.async(1000);
			setTimeout(d.callback(function () {
				var rb = document.getElementById("rb4");
				var elt = rb.querySelector("input[type='radio']");
				assert.ok(elt, "Missing wrapped input element.");
				assert.strictEqual(rb.valueNode, rb.firstChild, "Unexpected value for 'valueNode' property.");
				assert.strictEqual(rb.valueNode, rb.focusNode, "Unexpected value for 'focusNode' property.");
				rb = document.getElementById("rb3");
				assert.strictEqual(rb.value, "rb3",
					"Unexpected default value for 'value' property if 'value' specified/unchecked");
				assert.strictEqual(rb.value, rb.valueNode.value,
					"Unexpected value for 'rb3' wrapped input 'value' property.");
				assert.strictEqual(rb.name, "choice",
					"Unexpected default value for 'value' property if 'value' specified/unchecked");
				assert.strictEqual(rb.name, rb.valueNode.name,
					"Unexpected value for 'rb3' wrapped input 'name' property.");
				rb = document.getElementById("rb2");
				assert.ok(rb.checked, "Unexpected default value for 'checked' property if 'checked' specified.");
			}), 300);
			return d;
		},

		"checked (RadioButton)": function () {
			var d = this.async(1000);
			setTimeout(d.callback(function () {
				var rb = document.getElementById("rb1"),
					inp = rb.valueNode;
				rb.checked = true;
				rb.deliver();
				rb = document.getElementById("rb1");
				assert.isTrue(rb.checked, "Unexpected value for 'checked' property after set.");
				assert.strictEqual(rb.checked, inp.checked,
					"Unexpected value for 'checked' property of wrapped input.");
				// check it's the only checked button in the form
				var rb2 = document.getElementById("rb2");
				assert.isFalse(rb2.checked, "Unexpected value for rb2 'checked' property after set.");
				assert.isFalse(rb2.valueNode.checked, "Unexpected value for rb2 'checked' property of wrapped input.");
				var rb3 = document.getElementById("rb3");
				assert.isFalse(rb3.checked, "Unexpected value for rb3 'checked' property after set.");
				assert.isFalse(rb3.valueNode.checked, "Unexpected value for rb3 'checked' property of wrapped input.");
			}), 100);
			return d;
		},

		"toggle": function () {
			// a checked radio button should not be unchecked by toggle
			var rb = document.getElementById("rb2");
			rb.toggle();
			rb.deliver();
			assert.isTrue(rb.checked, "Unexpected value for 'checked' property after toggle() on rb2");
			rb = document.getElementById("rb1");
			rb.toggle();
			rb.deliver();
			assert.isTrue(rb.checked, "Unexpected value for 'checked' property after toggle() on rb1");
		},

		// overrides shared impl.
		"changeEvent (RadioButton)": function () {
			var d = this.async(1000);
			setTimeout(d.callback(function () {
				var rb3 = document.getElementById("rb3"),
					lbl4 = document.getElementById("lbl4"),
					fired = false;
				rb3.on("change", function () {
					fired = true;
				});
				rb3.focusNode.click();
				assert.isTrue(fired, "Missing 'change' event when input node is clicked.");
				fired = false;
				var rb1 = document.getElementById("rb1");
				rb1.checked = true;
				rb1.deliver();
				lbl4.click();
				assert.isTrue(fired, "Missing 'change' event when labelFor is clicked.");
			}));
			return d;
		},

		"on-click": function () {
			// test issue raised in https://bugs.dojotoolkit.org/ticket/17613
			// FIXME: This tests fails on IE 10
			if (has("ie") > 9 && has("ie") < 11) {
				console.log("WARNING: SKIPPING RadioButton `labelFor` TEST ON IE10. THIS TEST NEEDS TO BE FIXED.");
				return;
			}
			var d = this.async(1000),
				rb3 = document.getElementById("rb3");
			setTimeout(d.rejectOnError(function () {
				rb3.on("click", d.callback(function () {
					assert.isTrue(rb3.checked, "Unexpected checked state for rb3 after in rb3 on-click handler");
					assert.isTrue(rb3.focusNode.checked,
						"Unexpected checked state for rb3's wrapped input in rb3 on-click handler");
					["rb1", "rb2"].forEach(function (rb) {
						rb = document.getElementById(rb);
						assert.isFalse(rb.checked,
								"Unexpected checked state for " + rb.id + " in rb3 on-click handler");
						assert.isFalse(rb.focusNode.checked,
								"Unexpected checked state for " + rb.id + "'s wrapped input in rb3 on-click handler");
					});
				}));
				rb3.focusNode.click();
			}));
			return d;
		},

		afterEach: function () {
			container.parentNode.removeChild(container);
		}
	};
	dcl.mix(suite, commonSuite.testCases);

	// Markup
	var markupSuite = {
		name: "deliteful/RadioButton: markup",
		beforeEach: function () {
			container = document.createElement("div");
			document.body.appendChild(container);
			container.innerHTML = html;
			register.parse();
		}
	};
	dcl.mix(markupSuite, suite);
	registerSuite(markupSuite);

	var progSuite = {
		name: "deliteful/RadioButton: programmatic",
		beforeEach: function () {
			container = document.createElement("div");
			document.body.appendChild(container);
			var form = document.createElement("form");
			form.id = "form1";
			container.appendChild(form);

			var rb = new RadioButton({id: "rb1", value: "rb1", name: "choice"});
			form.appendChild(rb);
			rb.startup();
			rb = new RadioButton({id: "rb2", value: "rb2", checked: true, name: "choice"});
			form.appendChild(rb);
			rb.startup();
			var lbl4 = document.createElement("label");
			lbl4.id = "lbl4";
			lbl4.setAttribute("for", "rb3");
			lbl4.textContent = "rb3";
			container.appendChild(lbl4);
			rb = new RadioButton({id: "rb3", value: "rb3", name: "choice"});
			form.appendChild(rb);
			rb.startup();
			rb = new RadioButton({id: "rb4"});
			container.appendChild(rb);
			rb.startup();
		}
	};
	dcl.mix(progSuite, suite);
	registerSuite(progSuite);
});<commit_msg>fix unit test<commit_after>define([
	"dcl/dcl",
	"intern!object",
	"intern/chai!assert",
	"dojo/sniff",
	"delite/register",
	"deliteful/RadioButton",
	"./resources/CheckBox-shared"
], function (dcl, registerSuite, assert, has, register, RadioButton, commonSuite) {

	var container,
		html = "<form id='form1'>" +
			"<d-radio-button id='rb1' value='rb1' name='choice'></d-radio-button>" +
			"<d-radio-button id='rb2' value='rb2' name='choice' checked='true'></d-radio-button>" +
			"<d-radio-button id='rb3' value='rb3' name='choice'></d-radio-button>" +
			"</form>" +
			"<d-radio-button id='rb4'></d-radio-button>";

	var suite = {
		setup: function () {
			dcl.mix(commonSuite, {
				baseClass: "d-radio-button",
				defaultWidget: "rb4",
				labelForTarget: "rb3",
				inputType: "radio"
			});
		},

		"init state": function () {
			var d = this.async(1000);
			setTimeout(d.callback(function () {
				var rb = document.getElementById("rb4");
				var elt = rb.querySelector("input[type='radio']");
				assert.ok(elt, "Missing wrapped input element.");
				assert.strictEqual(rb.valueNode, rb.firstChild, "Unexpected value for 'valueNode' property.");
				assert.strictEqual(rb.valueNode, rb.focusNode, "Unexpected value for 'focusNode' property.");
				rb = document.getElementById("rb3");
				assert.strictEqual(rb.value, "rb3",
					"Unexpected default value for 'value' property if 'value' specified/unchecked");
				assert.strictEqual(rb.value, rb.valueNode.value,
					"Unexpected value for 'rb3' wrapped input 'value' property.");
				assert.strictEqual(rb.name, "choice",
					"Unexpected default value for 'value' property if 'value' specified/unchecked");
				assert.strictEqual(rb.name, rb.valueNode.name,
					"Unexpected value for 'rb3' wrapped input 'name' property.");
				rb = document.getElementById("rb2");
				assert.ok(rb.checked, "Unexpected default value for 'checked' property if 'checked' specified.");
			}), 300);
			return d;
		},

		"checked (RadioButton)": function () {
			var d = this.async(1000);
			setTimeout(d.callback(function () {
				var rb = document.getElementById("rb1"),
					inp = rb.valueNode;
				rb.checked = true;
				rb.deliver();
				rb = document.getElementById("rb1");
				assert.isTrue(rb.checked, "Unexpected value for 'checked' property after set.");
				assert.strictEqual(rb.checked, inp.checked,
					"Unexpected value for 'checked' property of wrapped input.");
				// check it's the only checked button in the form
				var rb2 = document.getElementById("rb2");
				assert.isFalse(rb2.checked, "Unexpected value for rb2 'checked' property after set.");
				assert.isFalse(rb2.valueNode.checked, "Unexpected value for rb2 'checked' property of wrapped input.");
				var rb3 = document.getElementById("rb3");
				assert.isFalse(rb3.checked, "Unexpected value for rb3 'checked' property after set.");
				assert.isFalse(rb3.valueNode.checked, "Unexpected value for rb3 'checked' property of wrapped input.");
			}), 100);
			return d;
		},

		"toggle": function () {
			// a checked radio button should not be unchecked by toggle
			var rb = document.getElementById("rb2");
			rb.toggle();
			rb.deliver();
			assert.isTrue(rb.checked, "Unexpected value for 'checked' property after toggle() on rb2");
			rb = document.getElementById("rb1");
			rb.toggle();
			rb.deliver();
			assert.isTrue(rb.checked, "Unexpected value for 'checked' property after toggle() on rb1");
		},

		// overrides shared impl.
		"changeEvent (RadioButton)": function () {
			var d = this.async(1000);
			setTimeout(d.callback(function () {
				var rb3 = document.getElementById("rb3"),
					lbl4 = document.getElementById("lbl4"),
					fired = false;
				rb3.on("change", function () {
					fired = true;
				});
				rb3.focusNode.click();
				assert.isTrue(fired, "Missing 'change' event when input node is clicked.");
				fired = false;
				var rb1 = document.getElementById("rb1");
				rb1.checked = true;
				rb1.deliver();
				lbl4.click();
				assert.isTrue(fired, "Missing 'change' event when labelFor is clicked.");
			}));
			return d;
		},

		"on-click": function () {
			// test issue raised in https://bugs.dojotoolkit.org/ticket/17613
			var d = this.async(1000),
				rb3 = document.getElementById("rb3");
			setTimeout(d.rejectOnError(function () {
				rb3.on("click", function () {
					assert.isTrue(rb3.checked, "Unexpected checked state for rb3 after in rb3 on-click handler");
					assert.isTrue(rb3.focusNode.checked,
						"Unexpected checked state for rb3's wrapped input in rb3 on-click handler");
					["rb1", "rb2"].forEach(function (rb) {
						rb = document.getElementById(rb);
						assert.isFalse(rb.checked,
								"Unexpected checked state for " + rb.id + " in rb3 on-click handler");
						assert.isFalse(rb.focusNode.checked,
								"Unexpected checked state for " + rb.id + "'s wrapped input in rb3 on-click handler");
					});
				});
				setTimeout(d.callback(function () {
					rb3.focusNode.click();
				}), 300);
			}), 300);
			return d;
		},

		afterEach: function () {
			container.parentNode.removeChild(container);
		}
	};
	dcl.mix(suite, commonSuite.testCases);

	// Markup
	var markupSuite = {
		name: "deliteful/RadioButton: markup",
		beforeEach: function () {
			container = document.createElement("div");
			document.body.appendChild(container);
			container.innerHTML = html;
			register.parse();
		}
	};
	dcl.mix(markupSuite, suite);
	registerSuite(markupSuite);

	var progSuite = {
		name: "deliteful/RadioButton: programmatic",
		beforeEach: function () {
			container = document.createElement("div");
			document.body.appendChild(container);
			var form = document.createElement("form");
			form.id = "form1";
			container.appendChild(form);

			var rb = new RadioButton({id: "rb1", value: "rb1", name: "choice"});
			form.appendChild(rb);
			rb.startup();
			rb = new RadioButton({id: "rb2", value: "rb2", checked: true, name: "choice"});
			form.appendChild(rb);
			rb.startup();
			var lbl4 = document.createElement("label");
			lbl4.id = "lbl4";
			lbl4.setAttribute("for", "rb3");
			lbl4.textContent = "rb3";
			container.appendChild(lbl4);
			rb = new RadioButton({id: "rb3", value: "rb3", name: "choice"});
			form.appendChild(rb);
			rb.startup();
			rb = new RadioButton({id: "rb4"});
			container.appendChild(rb);
			rb.startup();
		}
	};
	dcl.mix(progSuite, suite);
	registerSuite(progSuite);
});<|endoftext|>


<commit_before>(function() {
  var module = angular.module('loom_geogit_service', []);

  // Private Variables
  var nextRepoId = 0;

  // services
  var q, http, rootScope;

  var issueRequest = function(URL, deferredResponse) {
    http.jsonp(URL).then(function(response) {
      if (!goog.isDef(response.data.response.success) || response.data.response.success === true) {
        // Check for merge conflicts
        if (goog.isDef(response.data.response.Merge) && goog.isDef(response.data.response.Merge.conflicts)) {
          // Handle Merge Conflicts
          rootScope.$broadcast('geogit-merge-conflicts', response.data.response.Merge);
          deferredResponse.reject('CONFLICTS');
        } else {
          deferredResponse.resolve(response.data.response);
        }
      } else {
        deferredResponse.reject(response.data.response.error);
      }
    }, function(reject) {
      deferredResponse.reject(reject);
    }, function(update) {
      deferredResponse.update(update);
    });
  };

  module.provider('geogitService', function() {
    // public variables
    this.repos = [];

    this.$get = function($q, $http, $rootScope) {
      q = $q;
      http = $http;
      rootScope = $rootScope;
      this.addRepo(
          new GeoGitRepo('http://geoserver.rogue.lmnsolutions.com/geoserver/geogit/geonode:tegu_op_repo', 'master')
      );
      return this;
    };

    this.beginTransaction = function(repoId) {
      var deferredResponse = q.defer();
      var service = this;

      service.command(repoId, 'beginTransaction').then(function(response) {
        if (response.success === true) {
          var transaction = new GeoGitTransaction(service.command, repoId, response.Transaction);
          deferredResponse.resolve(transaction);
        } else {
          deferredResponse.reject(response.error);
        }
      }, function(reject) {
        deferredResponse.reject(reject);
      }, function(update) {
        deferredResponse.update(update);
      });

      return deferredResponse.promise;
    };

    this.command = function(repoId, command, options) {
      var deferredResponse = q.defer();
      var repo = this.repos[repoId];
      if (goog.isDefAndNotNull(repo)) {
        var URL = repo.url + '/' + command + '?output_format=JSON&callback=JSON_CALLBACK';
        if (goog.isDefAndNotNull(options)) {
          for (var property in options) {
            if (property !== null && options.hasOwnProperty(property)) {
              var underscore = property.indexOf('_');
              var trimmed;
              if (underscore > 0) {
                trimmed = property.substring(0, property.indexOf('_'));
              } else {
                trimmed = property;
              }
              if (goog.isArray(options[property])) {
                for (var i = 0; i < options[property].length; i++) {
                  var element = options[property][i];
                  URL += '&' + trimmed + '=' + element;
                }
              } else {
                URL += '&' + trimmed + '=' + options[property];
              }
            }
          }
        }
        issueRequest(URL, deferredResponse);
      }
      return deferredResponse.promise;
    };

    this.addRepo = function(repo) {
      repo.id = nextRepoId;
      nextRepoId = nextRepoId + 1;
      this.repos.push(repo);
      return repo.id;
    };

    this.parseFeatureType = function(featureType) {
      if (featureType) {
        var split = featureType.split(':');
        if (split.length === 1) {
          return {
            typeName: split[0]
          };
        }
        return {
          workspace: split[0],
          typeName: split[1]
        };
      }
      return null;
    };

    this.isLayerGroup = function(layer) {
      var featureType = layer.getSource().getParams().LAYERS;
      var url = layer.get('metadata').url + '/wms?SERVICE=WMS&VERSION=1.1.1&REQUEST=DescribeLayer&layers=' +
          featureType;
      url = '/proxy/?url=' + encodeURIComponent(url);

      var deferredResponse = q.defer();
      http.get(url).then(function(response) {
        // TODO: Refactor once there is a proper DescribeLayer parser
        // TODO: Test with imagery, or any layer without an associated WFS
        // TODO: Check to make sure it isn't a layer group with one layer
        var strings = response.data.split('</LayerDescription>');
        if (strings.length <= 2) {
          deferredResponse.resolve(response.data);
        } else {
          deferredResponse.reject('This is a layer group');
        }
      }, function(reject) {
        deferredResponse.reject(reject);
      }, function(update) {
        deferredResponse.update(update);
      });
      return deferredResponse.promise;
    };

    this.getLayer = function(layer) {
      var featureType = layer.getSource().getParams().LAYERS;

      var parsedFeatureType = this.parseFeatureType(featureType);
      // TODO: Make this work with a proxy once it supports authentication
      var url = layer.get('metadata').url + '/rest/layers/' + featureType + '.json';

      var deferredResponse = q.defer();
      http.get(url).then(function(response) {

        var resourceUrl = response.data.layer.resource.href;

        var datastoreStartIndex = resourceUrl.indexOf(parsedFeatureType.workspace + '/datastores');
        datastoreStartIndex = datastoreStartIndex + parsedFeatureType.workspace.length + 12;

        var datastoreEnd = resourceUrl.substr(datastoreStartIndex);
        var datastoreEndIndex = datastoreEnd.indexOf('/');
        var datastore = datastoreEnd.substring(0, datastoreEndIndex);
        deferredResponse.resolve(datastore);
      }, function(reject) {
        deferredResponse.reject(reject);
      }, function(update) {
        deferredResponse.update(update);
      });
      return deferredResponse.promise;
    };

    this.getDataStore = function(layer, name) {
      var featureType = layer.getSource().getParams().LAYERS;

      var parsedFeatureType = this.parseFeatureType(featureType);
      // TODO: Make this work with a proxy once it supports authentication
      var url = layer.get('metadata').url + '/rest/workspaces/' + parsedFeatureType.workspace + '/datastores/' + name +
          '.json';

      var deferredResponse = q.defer();
      http.get(url).then(function(response) {
        console.log(response);
        if (goog.isDefAndNotNull(response.data) && goog.isDefAndNotNull(response.data.dataStore) &&
            goog.isDefAndNotNull(response.data.dataStore.type)) {
          deferredResponse.resolve(response.data.dataStore);
        }
        deferredResponse.reject('Something went wrong when getting the datastore.');
      }, function(reject) {
        deferredResponse.reject(reject);
      }, function(update) {
        deferredResponse.update(update);
      });
      return deferredResponse.promise;
    };

    this.getFeatureType = function(layer, dataStore) {
      var featureType = layer.getSource().getParams().LAYERS;

      var parsedFeatureType = this.parseFeatureType(featureType);
      // TODO: Make this work with a proxy once it supports authentication
      var url = layer.get('metadata').url + '/rest/workspaces/' + parsedFeatureType.workspace + '/datastores/' +
          dataStore.name + '/featuretypes/' + parsedFeatureType.typeName + '.json';

      var deferredResponse = q.defer();
      http.get(url).then(function(response) {
        var metadata = layer.get('metadata');
        metadata.projection = response.data.featureType.srs;
        metadata.isGeoGit = true;
        metadata.geogitStore = dataStore.name;
        metadata.nativeName = response.data.featureType.nativeName;
        deferredResponse.resolve(response.data.featureType);
      }, function(reject) {
        deferredResponse.reject(reject);
      }, function(update) {
        deferredResponse.update(update);
      });
      return deferredResponse.promise;
    };

    this.isGeoGit = function(layer) {
      if (goog.isDefAndNotNull(layer)) {
        console.log(layer);
        if (!goog.isDefAndNotNull(layer.get('metadata').isGeoGit)) {
          // First check to see if this is a layer group
          var geogitService = this;
          var promise = this.isLayerGroup(layer);
          promise.then(function(resolved) {
            // Then get the layer information from the server for the datastore name
            console.log('resolved', resolved);
            promise = geogitService.getLayer(layer);
            promise.then(function(resolved) {
              // Then get the datastore to determine if it is a geogit datastore or not
              console.log('resolved', resolved);
              promise = geogitService.getDataStore(layer, resolved);
              promise.then(function(resolved) {
                // Finally get the needed information stored on the layer and create the repo object
                console.log('resolved', resolved);
                if (resolved.type === 'GeoGIT') {
                  promise = geogitService.getFeatureType(layer, resolved);
                  promise.then(function(resolved) {
                    console.log('resolved', resolved);
                    var id = geogitService.addRepo({url: layer.get('metadata').url});
                    layer.get('metadata').repoId = id;
                    console.log(layer);
                  }, function(rejected) {
                    console.log('rejected', rejected);
                  });
                } else {
                  layer.get('metadata').isGeoGit = false;
                }
              }, function(rejected) {
                console.log('rejected', rejected);
              });
            }, function(rejected) {
              console.log('rejected', rejected);
            });
          }, function(rejected) {
            console.log('rejected', rejected);
          });
        }
      }
      // If it is then get all of the information needed
    };
  });

}());
<commit_msg>GeoGitService cleanup.<commit_after>(function() {
  var module = angular.module('loom_geogit_service', []);

  // Private Variables
  var nextRepoId = 0;

  // services
  var q, http, rootScope;

  var service_ = null;

  var issueRequest = function(URL, deferredResponse) {
    http.jsonp(URL).then(function(response) {
      if (!goog.isDef(response.data.response.success) || response.data.response.success === true) {
        // Check for merge conflicts
        if (goog.isDef(response.data.response.Merge) && goog.isDef(response.data.response.Merge.conflicts)) {
          // Handle Merge Conflicts
          rootScope.$broadcast('geogit-merge-conflicts', response.data.response.Merge);
          deferredResponse.reject('CONFLICTS');
        } else {
          deferredResponse.resolve(response.data.response);
        }
      } else {
        deferredResponse.reject(response.data.response.error);
      }
    }, function(reject) {
      deferredResponse.reject(reject);
    }, function(update) {
      deferredResponse.update(update);
    });
  };

  module.provider('geogitService', function() {
    // public variables
    this.repos = [];

    this.$get = function($q, $http, $rootScope) {
      service_ = this;
      q = $q;
      http = $http;
      rootScope = $rootScope;
      return service_;
    };

    this.beginTransaction = function(repoId) {
      var deferredResponse = q.defer();

      service_.command(repoId, 'beginTransaction').then(function(response) {
        if (response.success === true) {
          var transaction = new GeoGitTransaction(service_.command, repoId, response.Transaction);
          deferredResponse.resolve(transaction);
        } else {
          deferredResponse.reject(response.error);
        }
      }, function(reject) {
        deferredResponse.reject(reject);
      }, function(update) {
        deferredResponse.update(update);
      });

      return deferredResponse.promise;
    };

    this.command = function(repoId, command, options) {
      var deferredResponse = q.defer();
      var repo = service_.repos[repoId];
      if (goog.isDefAndNotNull(repo)) {
        var URL = repo.url + '/' + command + '?output_format=JSON&callback=JSON_CALLBACK';
        if (goog.isDefAndNotNull(options)) {
          for (var property in options) {
            if (property !== null && options.hasOwnProperty(property)) {
              var underscore = property.indexOf('_');
              var trimmed;
              if (underscore > 0) {
                trimmed = property.substring(0, property.indexOf('_'));
              } else {
                trimmed = property;
              }
              if (goog.isArray(options[property])) {
                for (var i = 0; i < options[property].length; i++) {
                  var element = options[property][i];
                  URL += '&' + trimmed + '=' + element;
                }
              } else {
                URL += '&' + trimmed + '=' + options[property];
              }
            }
          }
        }
        issueRequest(URL, deferredResponse);
      }
      return deferredResponse.promise;
    };

    this.addRepo = function(repo) {
      repo.id = nextRepoId;
      nextRepoId = nextRepoId + 1;
      service_.repos[repo.id] = repo;
      return repo.id;
    };

    this.parseWorkspaceRoute = function(featureType) {
      if (featureType) {
        var split = featureType.split(':');
        if (split.length === 1) {
          return {
            typeName: split[0]
          };
        }
        return {
          workspace: split[0],
          typeName: split[1]
        };
      }
      return null;
    };

    this.isNotLayerGroup = function(layer) {
      var featureType = layer.getSource().getParams().LAYERS;
      var url = layer.get('metadata').url + '/wms?SERVICE=WMS&VERSION=1.1.1&REQUEST=DescribeLayer&layers=' +
          featureType;
      if (SERVER_SERVICE_USE_PROXY) {
        url = '/proxy/?url=' + encodeURIComponent(url);
      }

      var deferredResponse = q.defer();
      http.get(url).then(function(response) {
        // TODO: Refactor once there is a proper DescribeLayer parser
        // TODO: Test with imagery, or any layer without an associated WFS
        // TODO: Check to make sure it isn't a layer group with one layer
        var strings = response.data.split('</LayerDescription>');
        if (strings.length <= 2) {
          deferredResponse.resolve(response.data);
        } else {
          deferredResponse.reject('This is a layer group');
        }
      }, function(reject) {
        deferredResponse.reject(reject);
      });
      return deferredResponse.promise;
    };

    this.getDataStoreName = function(layer) {
      var featureType = layer.getSource().getParams().LAYERS;

      var workspaceRoute = service_.parseWorkspaceRoute(featureType);
      // TODO: Make this work with a proxy once it supports authentication
      var url = layer.get('metadata').url + '/rest/layers/' + featureType + '.json';

      var deferredResponse = q.defer();
      http.get(url).then(function(response) {

        var resourceUrl = response.data.layer.resource.href;

        var datastoreStartIndex = resourceUrl.indexOf(workspaceRoute.workspace + '/datastores');
        datastoreStartIndex = datastoreStartIndex + workspaceRoute.workspace.length + 12;

        var datastoreEnd = resourceUrl.substr(datastoreStartIndex);
        var datastoreEndIndex = datastoreEnd.indexOf('/');
        var datastore = datastoreEnd.substring(0, datastoreEndIndex);
        deferredResponse.resolve(datastore);
      }, function(reject) {
        deferredResponse.reject(reject);
      });
      return deferredResponse.promise;
    };

    this.getDataStore = function(layer, name) {
      var featureType = layer.getSource().getParams().LAYERS;

      var workspaceRoute = service_.parseWorkspaceRoute(featureType);
      // TODO: Make this work with a proxy once it supports authentication
      var url = layer.get('metadata').url + '/rest/workspaces/' + workspaceRoute.workspace + '/datastores/' + name +
          '.json';

      var deferredResponse = q.defer();
      http.get(url).then(function(response) {
        console.log(response);
        if (goog.isDefAndNotNull(response.data) && goog.isDefAndNotNull(response.data.dataStore) &&
            goog.isDefAndNotNull(response.data.dataStore.type)) {
          deferredResponse.resolve(response.data.dataStore);
        }
        deferredResponse.reject('Something went wrong when getting the datastore.');
      }, function(reject) {
        deferredResponse.reject(reject);
      });
      return deferredResponse.promise;
    };

    this.getFeatureType = function(layer, dataStore) {
      var featureType = layer.getSource().getParams().LAYERS;

      var workspaceRoute = service_.parseWorkspaceRoute(featureType);
      // TODO: Make this work with a proxy once it supports authentication
      var url = layer.get('metadata').url + '/rest/workspaces/' + workspaceRoute.workspace + '/datastores/' +
          dataStore.name + '/featuretypes/' + workspaceRoute.typeName + '.json';

      var deferredResponse = q.defer();
      http.get(url).then(function(response) {
        response.data.featureType.workspace = workspaceRoute.workspace;
        deferredResponse.resolve(response.data.featureType);
      }, function(reject) {
        deferredResponse.reject(reject);
      });
      return deferredResponse.promise;
    };

    this.isGeoGit = function(layer) {
      if (goog.isDefAndNotNull(layer)) {
        console.log(layer);
        var metadata = layer.get('metadata');
        if (!goog.isDefAndNotNull(metadata.isGeoGit)) {
          // First check to see if this is not a layer group
          service_.isNotLayerGroup(layer).then(function() {
            // Then get the layer information from the server for the datastore name
            console.log('not a layer group');
            service_.getDataStoreName(layer).then(function(dataStoreName) {
              // Then get the datastore to determine if it is a geogit datastore or not
              console.log('resolved', dataStoreName);
              service_.getDataStore(layer, dataStoreName).then(function(dataStore) {
                // Finally get the needed information stored on the layer and create the repo object
                console.log('resolved', dataStore);
                if (dataStore.type === 'GeoGIT') {
                  service_.getFeatureType(layer, dataStore).then(function(featureType) {
                    console.log('resolved', featureType);
                    var id = service_.addRepo(
                        new GeoGitRepo(metadata.url + '/geogit/' + featureType.workspace + ':' + dataStore.name));
                    metadata.projection = featureType.srs;
                    metadata.isGeoGit = true;
                    metadata.workspace = featureType.workspace;
                    metadata.geogitStore = dataStore.name;
                    metadata.nativeName = featureType.nativeName;
                    metadata.repoId = id;
                    console.log(layer);
                  }, function(rejected) {
                    console.log('rejected', rejected);
                  });
                } else {
                  metadata.isGeoGit = false;
                }
              }, function(rejected) {
                console.log('rejected', rejected);
              });
            }, function(rejected) {
              console.log('rejected', rejected);
            });
          }, function(rejected) {
            console.log('rejected', rejected);
          });
        }
      }
      // If it is then get all of the information needed
    };
  });

}());
<|endoftext|>


<commit_before>angular.module('app.services').factory('follows', function ($http, JsCollection, JsModel, serverConfig, session, $q) {

  var follows = new JsCollection();
  follows.model = JsModel.extend({
    _put: function () {
      return $http.put(serverConfig.url + '/api/follow/' + this.get('id'), this.toJSON()).then(function () {
        return follows.load();
      });
    },
    _post: function () {
      return $http.post(serverConfig.url + '/api/follow/', this.toJSON()).then(function () {
        return follows.load();
      });
    },
    _delete: function () {
      return $http['delete'](serverConfig.url + '/api/follow/' + this.get('id')).then(function () {
        return follows.load();
      });
    },
    collection: function () {
      return follows;
    },
    approve: function () {
      this.set('status', 1);
      return this._put();
    },
    unapprove: function () {
      this.set('status', 0);
      return this._put();
    },
    follow: function () {
      if (this.isFollower()) {
        return follows.getByUserId(this.get('follower').id)._post();
      }

      return this._post();
    },
    unfollow: function () {
      if (this.isFollowing()) {
        return this._delete();
      }

      return follows.getByUserId(this.get('follower').id)._delete();
    },
    isFollow: function () {
      return this.isFollowing() ? this.has('id') : follows.getByUserId(this.get('follower').id).has('id');
    },
    isApproved: function () {
      return 'active' === this.get('status');
    },
    isFollowing: function () {
      return this.get('follower').id === session.user_id;
    },
    isFollower: function () {
      return this.get('user').id === session.user_id;
    }
  });

  follows.load = function () {
    return $http.get(serverConfig.url + '/api/v2/user/followers').then(function (response) {
      follows.loaded = true;
      return follows.set(response.data.payload);
    });
  };
  follows.getByUserId = function (id) {
    var follow = this.find(function (item) {
      return item.get('user').id === id;
    });
    if (!follow) {
      follow = new this.model({
        status: 0,
        user: {
          id: id
        },
        follower: {
          id: session.user_id
        }
      });
    }
    return follow;
  };
  follows.getByFollowerId = function (id) {
    var follow = this.find(function (item) {
      return item.get('follower').id === id;
    });
    if (!follow) {
      follow = new this.model({
        status: 0,
        user: {
          id: session.user_id
        },
        follower: {
          id: id
        }
      });
    }
    return follow;
  };
  follows.getFollowing = function () {
    var f = this.filter(function (item) {
      return item.isFollowing();
    })
    return(f)
  };
  follows.getFollowers = function () {
    var f =  this.filter(function (item) {
      return item.isFollower();
    });
    return(f)
  };

  follows.loadAndGetFollowing = function(){
    var deferred = $q.defer();
    if(!follows.loaded){
      follows.load().then(function(){
        deferred.resolve(follows.getFollowing())
      })
    } else {
      deferred.resolve(follows.getFollowing())
    }

    return deferred.promise;
  }

  return follows;
});
<commit_msg>#122: adapt approve/unapprove<commit_after>angular.module('app.services').factory('follows', function ($http, JsCollection, JsModel, serverConfig, session, $q) {

  var follows = new JsCollection();
  follows.model = JsModel.extend({
    _put2: function () {
      console.log(JSON.stringify(this))
      var payload = this.toJSON()
      var headers = {headers: {'Content-Type': 'application/json'}}
      return $http.patch(serverConfig.url + '/api/v2/user/followers/' + this.get('id'), payload, headers).then(function () {
        return follows.load();
      });
    },
    _put: function () {
      return $http.put(serverConfig.url + '/api/follow/' + this.get('id'), this.toJSON()).then(function () {
        return follows.load();
      });
    },
    _post: function () {
      return $http.post(serverConfig.url + '/api/follow/', this.toJSON()).then(function () {
        return follows.load();
      });
    },
    _delete: function () {
      return $http['delete'](serverConfig.url + '/api/follow/' + this.get('id')).then(function () {
        return follows.load();
      });
    },
    collection: function () {
      return follows;
    },
    approve: function () {
      this.set('status', 'active');
      return this._put();
    },
    unapprove: function () {
      this.set('status', 'inactive');
      return this._put();
    },
    follow: function () {
      if (this.isFollower()) {
        return follows.getByUserId(this.get('follower').id)._post();
      }

      return this._post();
    },
    unfollow: function () {
      if (this.isFollowing()) {
        return this._delete();
      }

      return follows.getByUserId(this.get('follower').id)._delete();
    },
    isFollow: function () {
      return this.isFollowing() ? this.has('id') : follows.getByUserId(this.get('follower').id).has('id');
    },
    isApproved: function () {
      return 'active' === this.get('status');
    },
    isFollowing: function () {
      return this.get('follower').id === session.user_id;
    },
    isFollower: function () {
      return this.get('user').id === session.user_id;
    }
  });

  follows.load = function () {
    return $http.get(serverConfig.url + '/api/v2/user/followers').then(function (response) {
      follows.loaded = true;
      return follows.set(response.data.payload);
    });
  };
  follows.getByUserId = function (id) {
    var follow = this.find(function (item) {
      return item.get('user').id === id;
    });
    if (!follow) {
      follow = new this.model({
        status: 0,
        user: {
          id: id
        },
        follower: {
          id: session.user_id
        }
      });
    }
    return follow;
  };
  follows.getByFollowerId = function (id) {
    var follow = this.find(function (item) {
      return item.get('follower').id === id;
    });
    if (!follow) {
      follow = new this.model({
        status: 0,
        user: {
          id: session.user_id
        },
        follower: {
          id: id
        }
      });
    }
    return follow;
  };
  follows.getFollowing = function () {
    var f = this.filter(function (item) {
      return item.isFollowing();
    })
    return(f)
  };
  follows.getFollowers = function () {
    var f =  this.filter(function (item) {
      return item.isFollower();
    });
    return(f)
  };

  follows.loadAndGetFollowing = function(){
    var deferred = $q.defer();
    if(!follows.loaded){
      follows.load().then(function(){
        deferred.resolve(follows.getFollowing())
      })
    } else {
      deferred.resolve(follows.getFollowing())
    }

    return deferred.promise;
  }

  return follows;
});
<|endoftext|>