<commit_before>/* Javascript VAPID library.
 *
 */

'use strict';

var webCrypto = window.crypto.subtle;

function ord(c){
    /* return an ordinal for a character */
    return c.charCodeAt(0);
}

function chr(c){
    /* return a character for a given ordinal */
    return String.fromCharCode(c);
}

var vapid = {
    /* English:US */
    enus: {
        info: {
            OK_VAPID_KEYS: "VAPID Keys defined.",
        },
        errs: {
            ERR_VAPID_KEY: "VAPID generate keys error: ",
            ERR_PUB_R_KEY: "Invalid Public Key record. Please use a valid RAW Formatted record.",
            ERR_PUB_D_KEY: "Invalid Public Key record. Please use a valid DER Formatted record.",
            ERR_NO_KEYS: "No keys defined. Please use generate_keys() or load a public key.",
            ERR_CLAIM_MIS: "Claim missing ",
            ERR_SIGN: "Sign error",
            ERR_VERIFY_SG: "Verify Error: Auth signature invalid: ",
            ERR_VERIFY_KE: "Verify Error: Key invalid: ",
            ERR_SIGNATURE: "Signature Invalid",
            ERR_VERIFY: "Verify error",
        }
    },

    _private_key:  "",
    _public_key: "",

    /* Generate and verify a VAPID token */
    generate_keys: function() {
       /* Generate the public and private keys
        */
       return webCrypto.generateKey(
          {name: "ECDSA", namedCurve: "P-256"},
          true,
          ["sign", "verify"])
           .then(keys => {
              this._private_key = keys.privateKey;
              this._public_key = keys.publicKey;
              console.info(this.lang.info.OK_VAPID_KEYS);
           })
           .catch(fail => {
               console.error(this.lang.errs.ERR_VAPID_KEY, fail);
               });
    },

    _str_to_array: function(str) {
        /* convert a string into a ByteArray
         *
         * TextEncoders would be faster, but have a habit of altering
         * byte order
         */
        let split = str.split("");
        let reply = new Uint8Array(split.length);
        for (let i in split) {
            reply[i] = String.charCodeAt(split[i]);
        }
        return reply;
    },

    _array_to_str: function(array) {
        /* convert a ByteArray into a string
         */
        return String.fromCharCode.apply(null, new Uint8Array(array));
    },

    toUrlBase64: function(data) {
        /* Convert a binary array into a URL safe base64 string */
        return btoa(data)
            .replace(/\+/g, "-")
            .replace(/\//g, "_");
    },

    fromUrlBase64: function(data) {
        /* return a binary array from a URL safe base64 string */
        return this._str_to_array(atob(data
                                       .replace(/\-/g, "+")
                                       .replace(/\_/g, "/")));
    },

    /* A fully featured DER library is available at
     * https://github.com/indutny/asn1.js
     */

    export_private_der: function() {
        /* Generate a DER sequence. This can be read in via
         * something like python's
         * ecdsa.keys.SigningKey
         *     .from_der(base64.urlsafe_b64decode("MHc..."))
         */
        return webCrypto.exportKey("jwk", this._private_key)
            .then(k => {
                // verifying key
                let xv = String.fromCharCode.apply(null, this.fromUrlBase64(k.x));
                let yv = String.fromCharCode.apply(null, this.fromUrlBase64(k.y));
                // private key
                let dv = String.fromCharCode.apply(null, this.fromUrlBase64(k.d));

                // verifying key (public)
                let vk = '\x00\x04' + xv + yv;
                // \x02 is integer
                let int1 = '\x02\x01\x01'; // integer 1
                // \x04 is octet string
                let dvstr = '\x04' + chr(dv.length) + dv;
                let curve_oid = "\x06\x08" +
                    "\x2a\x86\x48\xce\x3d\x03\x01\x07";
                // \xaX is a construct, low byte is order.
                let curve_oid_const = '\xa0' + chr(curve_oid.length) +
                    curve_oid;
                // \x03 is a bitstring
                let vk_enc = '\x03' + chr(vk.length) + vk;
                let vk_const = '\xa1' + chr(vk_enc.length) + vk_enc;
                // \x30 is a sequence start.
                let seq = int1 + dvstr + curve_oid_const + vk_const;
                let rder = "\x30" + chr(seq.length) + seq;
                return this.toUrlBase64(rder);
            })
            .catch(err => console.error(err))
    },

    export_public_der: function () {
        /* Generate a DER sequence containing the public key info */
        return webCrypto.exportKey("jwk", this._public_key)
            .then(k => {
                // raw keys always begin with a 4
                let xv = this.fromUrlBase64(k.x);
                let yv = this.fromUrlBase64(k.y);

                let point = "\x00\x04" +
                    String.fromCharCode.apply(null, xv) +
                    String.fromCharCode.apply(null, yv);
                window.Kpoint = point;
                // a combination of the oid_ecPublicKey + p256 encoded oid
                let prefix = "\x30\x13" +  // sequence + length
                    "\x06\x07" + "\x2a\x86\x48\xce\x3d\x02\x01" +
                    "\x06\x08" + "\x2a\x86\x48\xce\x3d\x03\x01\x07"
                let encPoint = "\x03" + chr(point.length) + point
                let rder = "\x30" + chr(prefix.length + encPoint.length) +
                    prefix + encPoint;
                let der = this.toUrlBase64(rder);
                return der;
            });
    },

    export_public_raw: function() {
        return webCrypto.exportKey('raw', this._public_key)
            .then( key => {
                return this.toUrlBase64(this._array_to_str(key));
            })
    },

    import_public_raw: function(raw) {
        if (typeof(raw) == "string") {
            raw = this.fromUrlBase64(raw);
        }
        let err = new Error(this.lang.errs.ERR_PUB_KEY);

        // Raw is supposed to start with a 0x04, but some libraries don't. sigh.
        if (raw.length == 65 && raw[0] != 4) {
            throw err;
        }

        raw= raw.slice(-64);
        let x = this.toUrlBase64(String.fromCharCode.apply(null, raw.slice(0,32)));
        let y = this.toUrlBase64(String.fromCharCode.apply(null, raw.slice(32,64)));

        // Convert to a JWK and import it.
        let jwk = {
            crv: "P-256",
            ext: true,
            key_ops: ["verify"],
            kty: "EC",
            x: x,
            y, y
        };

        return webCrypto.importKey('jwk', jwk, 'ECDSA', true, ["verify"])
            .then(k => this._public_key = k)
    },

    import_public_der: function(derArray) {
        /* Import a DER formatted public key string.
         *
         * The Crypto-Key p256ecdsa=... key is such a thing.
         * Returns a promise containing the public key.
         */
        if (typeof(derArray) == "string") {
            derArray = this.fromUrlBase64(derArray);
        }
        /* Super light weight public key import function */
        let err = new Error(this.lang.errs.ERR_PUB_D_KEY);
        // Does the record begin with "\x30"
        if (derArray[0] != 48) { throw err}
        // is this an ECDSA record? (looking for \x2a and \x86
        if (derArray[6] != 42 && derArray[7] != 134) { throw err}
        if (derArray[15] != 42 && derArray[16] != 134) { throw err}
        // Public Key Record usually beings @ offset 23.
        if (derArray[23] != 3 && derArray[24] != 40 &&
                derArray[25] != 0 && derArray[26] != 4) {
            throw err;
        }
        // pubkey offset starts at byte 25
        let x = this.toUrlBase64(String.fromCharCode
                .apply(null, derArray.slice(27, 27+32)));
        let y = this.toUrlBase64(String.fromCharCode
                .apply(null, derArray.slice(27+32, 27+64)));

        // Convert to a JWK and import it.
        let jwk = {
            crv: "P-256",
            ext: true,
            key_ops: ["verify"],
            kty: "EC",
            x: x,
            y, y
        };

        return webCrypto.importKey('jwk', jwk, 'ECDSA', true, ["verify"])

    },

    sign: function(claims) {
        /* Sign a claims object and return the headers that can be used to decrypt the string.
         *
         * Returns a promise containing an object identifying the headers and values to include
         * to specify VAPID auth.
        */
        if (this._public_key == "") {
            throw new Error(this.lang.errs.ERR_NO_KEYS);
        }
        if (!claims.hasOwnProperty("exp")) {
            claims.exp = parseInt(Date.now()*.001) + 86400;
        }
        ["sub","aud"].forEach(function(key){
            if (! claims.hasOwnProperty(key)) {
                throw new Error(this.lang.errs.ERR_CLAIM_MIS, key);
            }
        })
        let alg = {name:"ECDSA", namedCurve: "P-256", hash:{name:"SHA-256"}};
        let headStr = btoa(JSON.stringify({typ:"JWT",alg:"ES256"}));
        let claimStr = btoa(JSON.stringify(claims));
        let content = headStr + "." + claimStr;
        let signatory = this._str_to_array(content);
        return webCrypto.sign(
            alg,
            this._private_key,
            signatory)
            .then(signature => {
                let sig = this.toUrlBase64(this._array_to_str(signature));
                /* The headers consist of the constructed JWT as the "authorization"
                 * and the raw Public key as the p256ecdsa element of "Crypto-Key"
                 * Note that Crypto-Key can contain many elements, separated by a ","
                 * You may need to append this value to an existing "Crypto-Key"
                 * header value.
                 */
                return webCrypto.exportKey('raw', this._public_key)
                    .then( key => {
                        let pubKey = this.toUrlBase64(this._array_to_str(key));
                        return {
                            authorization: "Bearer " + content + "." + sig,
                            "crypto-key": "p256ecdsa=" + pubKey,
                            publicKey: pubKey,
                        }
                    })
            })
            .catch(err => {
                console.error(this.lang.errs.ERR_SIGN, err);
            })
    },

    validate: function(string) {
        /* Sign the token for the developer Dashboard */
        let alg = {name:"ECDSA", namedCurve: "P-256", hash:{name:"SHA-256"}};
        let t2v = this._str_to_array(string);
        return webCrypto.sign(alg, this._private_key, t2v)
            .then(signed => {
                let sig = this.toUrlBase64(this._array_to_str(signed));
                return sig;
            });
    },

    validateCheck: function(sig, string) {
        /* verify a given signature string matches */
        let alg = { name: "ECDSA", namedCurve: "P-256", hash:{name:"SHA-256"}};
        let vsig = this.fromUrlBase64(sig);
        let t2v = this.fromUrlBase64(string);
        return webCrypto.verify(alg, this._public_key, vsig, t2v);
    },

    verify: function(token, public_key=null) {
        /* Verify a VAPID token.
         *
         * Token is the Authorization Header, Public Key is the Crypto-Key header.
        */

        // Ideally, just the bearer token, Cheat a little to be nice to the dev.
        if (token.search("earer ") > -1) {
            token = token.split(" ")[1];
        }

        // Again, ideally, just the p256ecdsa token.
        if (public_key != null) {

            if (public_key.search('p256ecdsa') > -1) {
                let sc = /p256ecdsa=([^;,]+)/i;
                public_key = sc.exec(public_key)[1];
            }

            // If there's no public key already defined, load the public_key
            // and try again.
            return this.import_public_raw(public_key)
                .then(key => {
                    this._public_key = key;
                    return this.verify(token);
                })
                .catch(err => {
                    console.error("Verify error", err);
                    throw err;
                });
        }
        if (this._public_key == "") {
            throw new Error(this.lang.errs.ERR_NO_KEYS);
        }

        let alg = { name: "ECDSA", namedCurve: "P-256", hash: {name: "SHA-256" }};
        let items = token.split('.');
        let signature;
        let key;
        try {
            signature = this.fromUrlBase64(items[2]);
        } catch (err) {
            throw new Error(this.lang.errs.ERR_VERIFY_SG + err.message);
        }
        try {
            key = this.fromUrlBase64(items[1]);
        } catch (err) {
            throw new Error(this.lang.errs.ERR_VERIFY_KE + err.message);
        }
        let content = items.slice(0,2).join('.');
        let signatory = this._str_to_array(content);
        return webCrypto.verify(
            alg,
            this._public_key,
            signature,
            signatory)
           .then(valid => {
               if (valid) {
                   return JSON.parse(String.fromCharCode
                                        .apply(null, this.fromUrlBase64(items[1])))
               }
               throw new Error(this.lang.errs.ERR_SIGNATURE);
           })
           .catch(err => {
               console.error(this.lang.errs.ERR_VERIFY, err);
               throw new Error (this.lang.errs.ERR_VERIFY + ": " + err.message);
           });
    }
}

vapid.lang = vapid.enus;
<commit_msg>Use unpadded "base64url" encoding for VAPID.<commit_after>/* Javascript VAPID library.
 *
 */

'use strict';

var webCrypto = window.crypto.subtle;

function ord(c){
    /* return an ordinal for a character */
    return c.charCodeAt(0);
}

function chr(c){
    /* return a character for a given ordinal */
    return String.fromCharCode(c);
}

var vapid = {
    /* English:US */
    enus: {
        info: {
            OK_VAPID_KEYS: "VAPID Keys defined.",
        },
        errs: {
            ERR_VAPID_KEY: "VAPID generate keys error: ",
            ERR_PUB_R_KEY: "Invalid Public Key record. Please use a valid RAW Formatted record.",
            ERR_PUB_D_KEY: "Invalid Public Key record. Please use a valid DER Formatted record.",
            ERR_NO_KEYS: "No keys defined. Please use generate_keys() or load a public key.",
            ERR_CLAIM_MIS: "Claim missing ",
            ERR_SIGN: "Sign error",
            ERR_VERIFY_SG: "Verify Error: Auth signature invalid: ",
            ERR_VERIFY_KE: "Verify Error: Key invalid: ",
            ERR_SIGNATURE: "Signature Invalid",
            ERR_VERIFY: "Verify error",
        }
    },

    _private_key:  "",
    _public_key: "",

    /* Generate and verify a VAPID token */
    generate_keys: function() {
       /* Generate the public and private keys
        */
       return webCrypto.generateKey(
          {name: "ECDSA", namedCurve: "P-256"},
          true,
          ["sign", "verify"])
           .then(keys => {
              this._private_key = keys.privateKey;
              this._public_key = keys.publicKey;
              console.info(this.lang.info.OK_VAPID_KEYS);
           })
           .catch(fail => {
               console.error(this.lang.errs.ERR_VAPID_KEY, fail);
               });
    },

    _str_to_array: function(str) {
        /* convert a string into a ByteArray
         *
         * TextEncoders would be faster, but have a habit of altering
         * byte order
         */
        let reply = new Uint8Array(str.length);
        for (let i = 0; i < str.length; i++) {
            reply[i] = str.charCodeAt(i);
        }
        return reply;
    },

    _array_to_str: function(array) {
        /* convert a ByteArray into a string
         */
        return String.fromCharCode.apply(null, new Uint8Array(array));
    },

    toUrlBase64: function(data) {
        /* Convert a binary array into a URL safe base64 string */
        return btoa(data)
            .replace(/\+/g, "-")
            .replace(/\//g, "_")
            .replace(/=/g, "");
    },

    fromUrlBase64: function(data) {
        /* return a binary array from a URL safe base64 string */
        if (data.length % 4 == 2) {
            data += "==";
        } else if (data.length % 4 == 3) {
            data += "=";
        }
        return this._str_to_array(atob(data
                                       .replace(/\-/g, "+")
                                       .replace(/\_/g, "/")));
    },

    /* A fully featured DER library is available at
     * https://github.com/indutny/asn1.js
     */

    export_private_der: function() {
        /* Generate a DER sequence. This can be read in via
         * something like python's
         * ecdsa.keys.SigningKey
         *     .from_der(base64.urlsafe_b64decode("MHc..."))
         */
        return webCrypto.exportKey("jwk", this._private_key)
            .then(k => {
                // verifying key
                let xv = String.fromCharCode.apply(null, this.fromUrlBase64(k.x));
                let yv = String.fromCharCode.apply(null, this.fromUrlBase64(k.y));
                // private key
                let dv = String.fromCharCode.apply(null, this.fromUrlBase64(k.d));

                // verifying key (public)
                let vk = '\x00\x04' + xv + yv;
                // \x02 is integer
                let int1 = '\x02\x01\x01'; // integer 1
                // \x04 is octet string
                let dvstr = '\x04' + chr(dv.length) + dv;
                let curve_oid = "\x06\x08" +
                    "\x2a\x86\x48\xce\x3d\x03\x01\x07";
                // \xaX is a construct, low byte is order.
                let curve_oid_const = '\xa0' + chr(curve_oid.length) +
                    curve_oid;
                // \x03 is a bitstring
                let vk_enc = '\x03' + chr(vk.length) + vk;
                let vk_const = '\xa1' + chr(vk_enc.length) + vk_enc;
                // \x30 is a sequence start.
                let seq = int1 + dvstr + curve_oid_const + vk_const;
                let rder = "\x30" + chr(seq.length) + seq;
                return this.toUrlBase64(rder);
            })
            .catch(err => console.error(err))
    },

    export_public_der: function () {
        /* Generate a DER sequence containing the public key info */
        return webCrypto.exportKey("jwk", this._public_key)
            .then(k => {
                // raw keys always begin with a 4
                let xv = this.fromUrlBase64(k.x);
                let yv = this.fromUrlBase64(k.y);

                let point = "\x00\x04" +
                    String.fromCharCode.apply(null, xv) +
                    String.fromCharCode.apply(null, yv);
                window.Kpoint = point;
                // a combination of the oid_ecPublicKey + p256 encoded oid
                let prefix = "\x30\x13" +  // sequence + length
                    "\x06\x07" + "\x2a\x86\x48\xce\x3d\x02\x01" +
                    "\x06\x08" + "\x2a\x86\x48\xce\x3d\x03\x01\x07"
                let encPoint = "\x03" + chr(point.length) + point
                let rder = "\x30" + chr(prefix.length + encPoint.length) +
                    prefix + encPoint;
                let der = this.toUrlBase64(rder);
                return der;
            });
    },

    export_public_raw: function() {
        return webCrypto.exportKey('raw', this._public_key)
            .then( key => {
                return this.toUrlBase64(this._array_to_str(key));
            })
    },

    import_public_raw: function(raw) {
        if (typeof(raw) == "string") {
            raw = this.fromUrlBase64(raw);
        }
        let err = new Error(this.lang.errs.ERR_PUB_KEY);

        // Raw is supposed to start with a 0x04, but some libraries don't. sigh.
        if (raw.length == 65 && raw[0] != 4) {
            throw err;
        }

        raw= raw.slice(-64);
        let x = this.toUrlBase64(String.fromCharCode.apply(null, raw.slice(0,32)));
        let y = this.toUrlBase64(String.fromCharCode.apply(null, raw.slice(32,64)));

        // Convert to a JWK and import it.
        let jwk = {
            crv: "P-256",
            ext: true,
            key_ops: ["verify"],
            kty: "EC",
            x: x,
            y, y
        };

        return webCrypto.importKey('jwk', jwk, 'ECDSA', true, ["verify"])
            .then(k => this._public_key = k)
    },

    import_public_der: function(derArray) {
        /* Import a DER formatted public key string.
         *
         * The Crypto-Key p256ecdsa=... key is such a thing.
         * Returns a promise containing the public key.
         */
        if (typeof(derArray) == "string") {
            derArray = this.fromUrlBase64(derArray);
        }
        /* Super light weight public key import function */
        let err = new Error(this.lang.errs.ERR_PUB_D_KEY);
        // Does the record begin with "\x30"
        if (derArray[0] != 48) { throw err}
        // is this an ECDSA record? (looking for \x2a and \x86
        if (derArray[6] != 42 && derArray[7] != 134) { throw err}
        if (derArray[15] != 42 && derArray[16] != 134) { throw err}
        // Public Key Record usually beings @ offset 23.
        if (derArray[23] != 3 && derArray[24] != 40 &&
                derArray[25] != 0 && derArray[26] != 4) {
            throw err;
        }
        // pubkey offset starts at byte 25
        let x = this.toUrlBase64(String.fromCharCode
                .apply(null, derArray.slice(27, 27+32)));
        let y = this.toUrlBase64(String.fromCharCode
                .apply(null, derArray.slice(27+32, 27+64)));

        // Convert to a JWK and import it.
        let jwk = {
            crv: "P-256",
            ext: true,
            key_ops: ["verify"],
            kty: "EC",
            x: x,
            y, y
        };

        return webCrypto.importKey('jwk', jwk, 'ECDSA', true, ["verify"])

    },

    sign: function(claims) {
        /* Sign a claims object and return the headers that can be used to decrypt the string.
         *
         * Returns a promise containing an object identifying the headers and values to include
         * to specify VAPID auth.
        */
        if (this._public_key == "") {
            throw new Error(this.lang.errs.ERR_NO_KEYS);
        }
        if (!claims.hasOwnProperty("exp")) {
            claims.exp = parseInt(Date.now()*.001) + 86400;
        }
        ["sub","aud"].forEach(function(key){
            if (! claims.hasOwnProperty(key)) {
                throw new Error(this.lang.errs.ERR_CLAIM_MIS, key);
            }
        })
        let alg = {name:"ECDSA", namedCurve: "P-256", hash:{name:"SHA-256"}};
        let headStr = btoa(JSON.stringify({typ:"JWT",alg:"ES256"}));
        let claimStr = btoa(JSON.stringify(claims));
        let content = headStr + "." + claimStr;
        let signatory = this._str_to_array(content);
        return webCrypto.sign(
            alg,
            this._private_key,
            signatory)
            .then(signature => {
                let sig = this.toUrlBase64(this._array_to_str(signature));
                /* The headers consist of the constructed JWT as the "authorization"
                 * and the raw Public key as the p256ecdsa element of "Crypto-Key"
                 * Note that Crypto-Key can contain many elements, separated by a ","
                 * You may need to append this value to an existing "Crypto-Key"
                 * header value.
                 */
                return webCrypto.exportKey('raw', this._public_key)
                    .then( key => {
                        let pubKey = this.toUrlBase64(this._array_to_str(key));
                        return {
                            authorization: "Bearer " + content + "." + sig,
                            "crypto-key": "p256ecdsa=" + pubKey,
                            publicKey: pubKey,
                        }
                    })
            })
            .catch(err => {
                console.error(this.lang.errs.ERR_SIGN, err);
            })
    },

    validate: function(string) {
        /* Sign the token for the developer Dashboard */
        let alg = {name:"ECDSA", namedCurve: "P-256", hash:{name:"SHA-256"}};
        let t2v = this._str_to_array(string);
        return webCrypto.sign(alg, this._private_key, t2v)
            .then(signed => {
                let sig = this.toUrlBase64(this._array_to_str(signed));
                return sig;
            });
    },

    validateCheck: function(sig, string) {
        /* verify a given signature string matches */
        let alg = { name: "ECDSA", namedCurve: "P-256", hash:{name:"SHA-256"}};
        let vsig = this.fromUrlBase64(sig);
        let t2v = this.fromUrlBase64(string);
        return webCrypto.verify(alg, this._public_key, vsig, t2v);
    },

    verify: function(token, public_key=null) {
        /* Verify a VAPID token.
         *
         * Token is the Authorization Header, Public Key is the Crypto-Key header.
        */

        // Ideally, just the bearer token, Cheat a little to be nice to the dev.
        if (token.search("earer ") > -1) {
            token = token.split(" ")[1];
        }

        // Again, ideally, just the p256ecdsa token.
        if (public_key != null) {

            if (public_key.search('p256ecdsa') > -1) {
                let sc = /p256ecdsa=([^;,]+)/i;
                public_key = sc.exec(public_key)[1];
            }

            // If there's no public key already defined, load the public_key
            // and try again.
            return this.import_public_raw(public_key)
                .then(key => {
                    this._public_key = key;
                    return this.verify(token);
                })
                .catch(err => {
                    console.error("Verify error", err);
                    throw err;
                });
        }
        if (this._public_key == "") {
            throw new Error(this.lang.errs.ERR_NO_KEYS);
        }

        let alg = { name: "ECDSA", namedCurve: "P-256", hash: {name: "SHA-256" }};
        let items = token.split('.');
        let signature;
        let key;
        try {
            signature = this.fromUrlBase64(items[2]);
        } catch (err) {
            throw new Error(this.lang.errs.ERR_VERIFY_SG + err.message);
        }
        try {
            key = this.fromUrlBase64(items[1]);
        } catch (err) {
            throw new Error(this.lang.errs.ERR_VERIFY_KE + err.message);
        }
        let content = items.slice(0,2).join('.');
        let signatory = this._str_to_array(content);
        return webCrypto.verify(
            alg,
            this._public_key,
            signature,
            signatory)
           .then(valid => {
               if (valid) {
                   return JSON.parse(String.fromCharCode
                                        .apply(null, this.fromUrlBase64(items[1])))
               }
               throw new Error(this.lang.errs.ERR_SIGNATURE);
           })
           .catch(err => {
               console.error(this.lang.errs.ERR_VERIFY, err);
               throw new Error (this.lang.errs.ERR_VERIFY + ": " + err.message);
           });
    }
}

vapid.lang = vapid.enus;
<|endoftext|>


<commit_before>/*
 * (c) Copyright Ascensio System SIA 2010-2019
 *
 * This program is a free software product. You can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License (AGPL)
 * version 3 as published by the Free Software Foundation. In accordance with
 * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
 * that Ascensio System SIA expressly excludes the warranty of non-infringement
 * of any third-party rights.
 *
 * This program is distributed WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR  PURPOSE. For
 * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
 *
 * You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha
 * street, Riga, Latvia, EU, LV-1050.
 *
 * The  interactive user interfaces in modified source and object code versions
 * of the Program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU AGPL version 3.
 *
 * Pursuant to Section 7(b) of the License you must retain the original Product
 * logo when distributing the program. Pursuant to Section 7(e) we decline to
 * grant you any rights under trademark law for use of our trademarks.
 *
 * All the Product's GUI elements, including illustrations and icon sets, as
 * well as technical writing content are licensed under the terms of the
 * Creative Commons Attribution-ShareAlike 4.0 International. See the License
 * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
 *
 */

"use strict";

(function (window, undefined)
{
    window['AscFonts'] = window['AscFonts'] || {};

    window['AscFonts'].isEngineReady = false;
    window['AscFonts'].api = null;
    window['AscFonts'].onSuccess = null;
    window['AscFonts'].onError = null;
    window['AscFonts'].maxLoadingIndex = 4; // engine, file, manager, wasm
    window['AscFonts'].curLoadingIndex = 0;

    window['AscFonts'].allocate = function(size)
    {
        if (typeof(Uint8Array) != 'undefined' && !window.opera)
            return new Uint8Array(size);

        var arr = new Array(size);
        for (var i=0;i<size;i++)
            arr[i] = 0;
        return arr;
    };
    window['AscFonts'].allocateData = function(size)
    {
        return { data : window['AscFonts'].allocate(size) };
    };

    window['AscFonts'].onLoadModule = function()
	{
		++window['AscFonts'].curLoadingIndex;

		if (window['AscFonts'].curLoadingIndex == window['AscFonts'].curLoadingMax)
		{
			if (window['AscFonts'].api)
			{
                window['AscFonts'].isEngineReady = true;
                window['AscFonts'].onSuccess.call(window['AscFonts'].api);
			}

			delete window['AscFonts'].curLoadingIndex;
            delete window['AscFonts'].maxLoadingIndex;
            delete window['AscFonts'].api;
            delete window['AscFonts'].onSuccess;
            delete window['AscFonts'].onError;
		}
	};

    window['AscFonts'].load = function(api, onSuccess, onError)
    {
        if (window["NATIVE_EDITOR_ENJINE"] === true || window["Native"] !== undefined)
        {
            window['AscFonts'].isEngineReady = false;
            onSuccess.call(api);
            return;
        }

        window['AscFonts'].api = api;
        window['AscFonts'].onSuccess = onSuccess;
        window['AscFonts'].onError = onError;

        var url = "../../../../sdkjs/common/libfont";
        var useWasm = false;
        var webAsmObj = window["WebAssembly"];
        if (typeof webAsmObj === "object")
		{
            if (typeof webAsmObj["Memory"] === "function")
			{
				if ((typeof webAsmObj["instantiateStreaming"] === "function") || (typeof webAsmObj["instantiate"] === "function"))
					useWasm = true;
			}
		}

		useWasm ? (url += "/wasm") : (url += "/js");
		if (!useWasm)
            window['AscFonts'].onLoadModule();

		var _onSuccess = function(){
		};
		var _onError = function(){
            window['AscFonts'].onError();
		};

        if (window['AscNotLoadAllScript'])
        {
            AscCommon.loadScript(url + "/engine.js", _onSuccess, _onError);
            AscCommon.loadScript(url + "/file.js", _onSuccess, _onError);
            AscCommon.loadScript(url + "/manager.js", _onSuccess, _onError);
        }
        else
        {
            AscCommon.loadScript(url + "/engine.js", _onSuccess, _onError);
        }
    };

    function FontStream(data, size)
    {
        this.data = data;
        this.size = size;
    }

    window['AscFonts'].FontStream = FontStream;

    window['AscFonts'].FT_Common = {
        UintToInt : function(v)
        {
            return (v>2147483647)?v-4294967296:v;
        },
        UShort_To_Short : function(v)
        {
            return (v>32767)?v-65536:v;
        },
        IntToUInt : function(v)
        {
            return (v<0)?v+4294967296:v;
        },
        Short_To_UShort : function(v)
        {
            return (v<0)?v+65536:v;
        },
        memset : function(d,v,s)
        {
            for (var i=0;i<s;i++)
                d[i]=v;
        }
    };

    function CPointer()
    {
        this.obj    = null;
        this.data   = null;
        this.pos    = 0;
    }

    function FT_Memory()
    {
        this.canvas = document.createElement('canvas');
        this.canvas.width = 1;
        this.canvas.height = 1;
        this.ctx    = this.canvas.getContext('2d');

        this.Alloc = function(size)
        {
            var p = new CPointer();
            p.obj = this.ctx.createImageData(1,parseInt((size + 3) / 4));
            p.data = p.obj.data;
            p.pos = 0;
            return p;
        };
        this.AllocHeap = function()
        {
            // TODO: нужно посмотреть, как эта память будет использоваться.
            // нужно ли здесь делать стек, либо все время от нуля делать??
        };
        this.CreateStream = function(size)
        {
            var _size = parseInt((size + 3) / 4);
            var obj = this.ctx.createImageData(1,_size);
            return new FT_Stream(obj.data,_size);
        };
    }

    function CRasterMemory()
    {
        this.width = 0;
        this.height = 0;
        this.pitch = 0;

        this.m_oBuffer = null;
        this.CheckSize = function(w, h)
        {
            if (this.width < (w + 1) || this.height < (h + 1))
            {
                this.width = Math.max(this.width, w + 1);
                this.pitch = 4 * this.width;
                this.height = Math.max(this.height, h + 1);

                this.m_oBuffer = null;
                this.m_oBuffer = g_memory.ctx.createImageData(this.width, this.height);
            }
        };
    }

    window['AscFonts'].FT_Memory = FT_Memory;

    window['AscFonts'].raster_memory = new CRasterMemory();
    window['AscFonts'].g_memory = new FT_Memory();

})(window, undefined);<commit_msg>MInimization bugs<commit_after>/*
 * (c) Copyright Ascensio System SIA 2010-2019
 *
 * This program is a free software product. You can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License (AGPL)
 * version 3 as published by the Free Software Foundation. In accordance with
 * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
 * that Ascensio System SIA expressly excludes the warranty of non-infringement
 * of any third-party rights.
 *
 * This program is distributed WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR  PURPOSE. For
 * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
 *
 * You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha
 * street, Riga, Latvia, EU, LV-1050.
 *
 * The  interactive user interfaces in modified source and object code versions
 * of the Program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU AGPL version 3.
 *
 * Pursuant to Section 7(b) of the License you must retain the original Product
 * logo when distributing the program. Pursuant to Section 7(e) we decline to
 * grant you any rights under trademark law for use of our trademarks.
 *
 * All the Product's GUI elements, including illustrations and icon sets, as
 * well as technical writing content are licensed under the terms of the
 * Creative Commons Attribution-ShareAlike 4.0 International. See the License
 * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
 *
 */

"use strict";

(function (window, undefined)
{
    window['AscFonts'] = window['AscFonts'] || {};

    window['AscFonts'].isEngineReady = false;
    window['AscFonts'].api = null;
    window['AscFonts'].onSuccess = null;
    window['AscFonts'].onError = null;
    window['AscFonts'].maxLoadingIndex = 4; // engine, file, manager, wasm
    window['AscFonts'].curLoadingIndex = 0;

    window['AscFonts'].allocate = function(size)
    {
        if (typeof(Uint8Array) != 'undefined' && !window.opera)
            return new Uint8Array(size);

        var arr = new Array(size);
        for (var i=0;i<size;i++)
            arr[i] = 0;
        return arr;
    };
    window['AscFonts'].allocateData = function(size)
    {
        return { data : window['AscFonts'].allocate(size) };
    };

    window['AscFonts'].onLoadModule = function()
	{
		++window['AscFonts'].curLoadingIndex;

		if (window['AscFonts'].curLoadingIndex == window['AscFonts'].curLoadingMax)
		{
			if (window['AscFonts'].api)
			{
                window['AscFonts'].isEngineReady = true;
                window['AscFonts'].onSuccess.call(window['AscFonts'].api);
			}

			delete window['AscFonts'].curLoadingIndex;
            delete window['AscFonts'].maxLoadingIndex;
            delete window['AscFonts'].api;
            delete window['AscFonts'].onSuccess;
            delete window['AscFonts'].onError;
		}
	};

    window['AscFonts'].load = function(api, onSuccess, onError)
    {
        if (window["NATIVE_EDITOR_ENJINE"] === true || window["Native"] !== undefined)
        {
            window['AscFonts'].isEngineReady = false;
            onSuccess.call(api);
            return;
        }

        window['AscFonts'].api = api;
        window['AscFonts'].onSuccess = onSuccess;
        window['AscFonts'].onError = onError;

        var url = "../../../../sdkjs/common/libfont";
        var useWasm = false;
        var webAsmObj = window["WebAssembly"];
        if (typeof webAsmObj === "object")
		{
            if (typeof webAsmObj["Memory"] === "function")
			{
				if ((typeof webAsmObj["instantiateStreaming"] === "function") || (typeof webAsmObj["instantiate"] === "function"))
					useWasm = true;
			}
		}

		useWasm ? (url += "/wasm") : (url += "/js");
		if (!useWasm)
            window['AscFonts'].onLoadModule();

		var _onSuccess = function(){
		};
		var _onError = function(){
            window['AscFonts'].onError();
		};

        if (window['AscNotLoadAllScript'])
        {
            AscCommon.loadScript(url + "/engine.js", _onSuccess, _onError);
            AscCommon.loadScript(url + "/file.js", _onSuccess, _onError);
            AscCommon.loadScript(url + "/manager.js", _onSuccess, _onError);
        }
        else
        {
            AscCommon.loadScript(url + "/engine.js", _onSuccess, _onError);
        }
    };

    function FontStream(data, size)
    {
        this.data = data;
        this.size = size;
    }

    window['AscFonts'].FontStream = FontStream;

    window['AscFonts'].FT_Common = {
        UintToInt : function(v)
        {
            return (v>2147483647)?v-4294967296:v;
        },
        UShort_To_Short : function(v)
        {
            return (v>32767)?v-65536:v;
        },
        IntToUInt : function(v)
        {
            return (v<0)?v+4294967296:v;
        },
        Short_To_UShort : function(v)
        {
            return (v<0)?v+65536:v;
        },
        memset : function(d,v,s)
        {
            for (var i=0;i<s;i++)
                d[i]=v;
        }
    };

    function CPointer()
    {
        this.obj    = null;
        this.data   = null;
        this.pos    = 0;
    }

    function FT_Memory()
    {
        this.canvas = document.createElement('canvas');
        this.canvas.width = 1;
        this.canvas.height = 1;
        this.ctx    = this.canvas.getContext('2d');

        this.Alloc = function(size)
        {
            var p = new CPointer();
            p.obj = this.ctx.createImageData(1,parseInt((size + 3) / 4));
            p.data = p.obj.data;
            p.pos = 0;
            return p;
        };
        this.AllocHeap = function()
        {
            // TODO: нужно посмотреть, как эта память будет использоваться.
            // нужно ли здесь делать стек, либо все время от нуля делать??
        };
        this.CreateStream = function(size)
        {
            var _size = parseInt((size + 3) / 4);
            var obj = this.ctx.createImageData(1,_size);
            return new FontStream(obj.data,_size);
        };
    }

    function CRasterMemory()
    {
        this.width = 0;
        this.height = 0;
        this.pitch = 0;

        this.m_oBuffer = null;
        this.CheckSize = function(w, h)
        {
            if (this.width < (w + 1) || this.height < (h + 1))
            {
                this.width = Math.max(this.width, w + 1);
                this.pitch = 4 * this.width;
                this.height = Math.max(this.height, h + 1);

                this.m_oBuffer = null;
                this.m_oBuffer = g_memory.ctx.createImageData(this.width, this.height);
            }
        };
    }

    window['AscFonts'].FT_Memory = FT_Memory;

    window['AscFonts'].raster_memory = new CRasterMemory();
    window['AscFonts'].g_memory = new FT_Memory();

})(window, undefined);<|endoftext|>


<commit_before>console.log("Helllo world");
var virus = document.querySelector("div");
virus.style.top = "400px";
var x = 1;
var velocity = .1; // px per millisecond
var startTime = Date.now();

timer = setInterval(function() {
	var t = Date.now() - startTime;
	x = velocity * t;
	virus.style.left = x + "px";
	if ( x > screen.width) {
	startTime = Date.now();
	}
},20); // ms  | 1000/20 = 50 frames per second (50 fps)
<commit_msg>Add y-dimension, downwards movement, clickHandler<commit_after>console.log("Helllo world");
var virus = document.querySelector("div");
virus.style.top = "400px";
var x = 1;
var y = 0;
var vy = 0;
var velocity = .1; // px per millisecond
var startTime = Date.now();
var clickTime;
timer = setInterval(function animate() {
	var t = Date.now() - startTime;
	x = velocity * t;
	virus.style.left = x + "px";
	if ( x > document.body.clientWidth) {
		startTime = Date.now();
	}
	if (vy >= .1) {
		var t = Date.now() - clickTime;
		y = vy * t + 400;
		virus.style.top = y + "px";
	}
},20); // ms  | 1000/20 = 50 frames per second (50 fps)

virus.addEventListener("click", function onclick(event) {
	vy = .1;
	clickTime = Date.now();
});
<|endoftext|>


<commit_before>
function Game() {
    this.trump=''
    this.players = new Array()
    this.cards = new Array()
    this.ontable = new Array()
    this.playerturn=-1         // left-of-dealer = 1
    this.dealer=-1
    this.bidwinner=-1
    this.ourscore=0
    this.theirscore=0
    this.ourtricks=0
    this.theirtricks=0
    this.trumpcard = new Card()
    this.phase = 'pregame'   // options: 'pregame', 'ready', 'preround', 'bidding', 'discard', 'tricks', 'end'
    this.teammate = ''       // the requested or present teammate's username
    this.alone = false

    this.partner = function(idx) {
        return (idx+2)%players.length
    }

    // Assuming the game's in bidding phase
    this.isFirstBiddingRound = function() {
        return this.trumpcard.isReal()
    }
}

function Card(s, t) {
    this.suit = typeof s === 'undefined' ? '' : s
    this.type = typeof t === 'undefined' ? '' : t

    this.toString = function() {
        return this.isReal() ? this.type + this.suit : 'back'
    }
    this.isReal = function() {
        return this.type !== '' && this.type !== ''
    }
}

function ajaxparseobj(doc) {
    var game = new Game()
    game.trump = doc.getElementsByTagName('trump').item(0).childNodes[0].data
    if(game.trump === 'none')
        game.trump = ''
    game.cards = ajaxgetcards(doc.getElementsByTagName('cards').item(0))
    game.ontable = ajaxgetcards(doc.getElementsByTagName('ontable').item(0))
    var playerlist = doc.getElementsByTagName('player')
    for(var i=0; i < playerlist.length; ++i) {
        game.players.push(playerlist.item(0).childNodes[0].data)
    }
    game.trumpcard = ajaxgetcards(doc.getElementsByTagName('trumpcard').item(0))
    if(game.trumpcard.length > 0)
        game.trumpcard = game.trumpcard[0]
    else
        game.trumpcard = new Card()
    game.teammate = doc.getElementsByTagName('teammate').item(0).childNodes[0]
    if(typeof game.teammate === 'undefined')
        game.teammate = ''
    else
        game.teammate = game.teammate.data
    game.alone = (doc.getElementsByTagName('alone').item(0).childNodes[0].data === 'true')
    game.phase = doc.getElementsByTagName('phase').item(0).childNodes[0].data
    game.playerturn = parseInt(doc.getElementsByTagName('playerturn').item(0).childNodes[0].data)
    game.dealer = parseInt(doc.getElementsByTagName('dealer').item(0).childNodes[0].data)
    game.bidwinner = parseInt(doc.getElementsByTagName('bidwinner').item(0).childNodes[0].data)
    game.ourscore = parseInt(doc.getElementsByTagName('ourscore').item(0).childNodes[0].data)
    game.theirscore = parseInt(doc.getElementsByTagName('theirscore').item(0).childNodes[0].data)
    game.ourtricks = parseInt(doc.getElementsByTagName('ourtricks').item(0).childNodes[0].data)
    game.theirtricks = parseInt(doc.getElementsByTagName('theirtricks').item(0).childNodes[0].data)
    return game
}

function ajaxgetcards(top) {
    var cardlist = top.getElementsByTagName('card')
    var ret = new Array()
    for(var i=0; i < cardlist.length; ++i) {
        ret.push(getcard(cardlist.item(i)))
    }
    return ret
}

function getcard(top) {
    var next = new Card()
    next.suit = top.getElementsByTagName('suit').item(0).childNodes[0].data
    next.type = top.getElementsByTagName('type').item(0).childNodes[0].data
    return next
}

function ajaxupdategame(callback) {
    var req = new XMLHttpRequest()
    if(req) {
        req.open("POST", "ctrl", true)
        req.onreadystatechange = function() {
            if(req.readyState === 4 && req.status === 200)
                callback(ajaxparseobj(req.responseXML.documentElement))
        }
        req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
        req.setRequestHeader('Ajax', 'get')
        req.setRequestHeader('resource', 'game')
        req.send('')
    }
}

function ajaxget(resource, callback) {
    var req = new XMLHttpRequest()
    if(req) {
        req.open("POST", "ctrl", true)
        req.readystateonchange = function() {
            if(req.readyState === 4 && req.status === 200)
                callback(req.responseXML.documentElement.childNodes[0].data)
        }
        req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
        req.setRequestHeader('Ajax', 'get')
        req.setRequestHeader('resource', resource)
        req.send('')
    }
}

/// Response will be one of "true", "false", "invalid action", "invalid data", and "crash".
///   "true": the action was completed successfully.
///   "false": the action could not be completed, e.g. not your turn.
///   "invalid action": there is no action called by the given name.
///   "invalid data": data string was not in an acceptable format
///   "crash": there's a bug server-side
/// If you get "false" or "invalid", I would expect that either there's a bug
//   or the user is trying to hack the system.
/// Check for all three to make debugging easier, but if the client code is
///  correct the response should almost always be "true".
function ajaxaction(action, actiondata, callback) {
    var req = new XMLHttpRequest()
    if(req) {
        req.open('POST', 'ctrl', true)
        req.onreadystatechange = function() {
            if(req.readyState === 4 && req.status === 200)
                callback(req.responseXML.documentElement.childNodes[0].data)
        }
        req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
        req.setRequestHeader('Ajax', 'set')
        req.setRequestHeader('action', action)
        req.setRequestHeader('actiondata', actiondata)
        req.send('')
    }
}

// Can leave off actiondata if it's not needed, eg doaction('deal')
/***** List of actions ******
 * 'deal': shuffle & deal cards to everyone to start a round
 * 'play', card: play the card given by 'card', eg doaction('play', '2c') for
 *             the 2 of clubs. The toString() method in Card will give you '2c', etc.
 * 'declare trump', trump, alone: declare trump, 'trump' is ignored if it's the first
 *                                round of bidding. If the player wants to go it alone,
 *                                append ":alone" to the end of trump, like so:
 *                                    doaction('declare trump', 's:alone')
 *                                or
 *                                    doaction('declare trump', ':alone')
 * 'pass': used during bidding rounds
 * 'begin': player considers himself ready to begin the game, valid during 'pregame' phase
 * 'request teammate', teammate: valid during 'pregame' phase, sending this multiple times
 *                               is supported
 * 'discard', card: discard the card given by 'card', for use by dealer when he
 *                  is told to "pick it up" during the first bidding round
 *                  (ie after someone sends 'declare trump' during first bidding round)
 */
function doaction(action, actiondata) {
    if(typeof actiondata === 'undefined')
        actiondata = ''
    ajaxaction(action, actiondata,
        function(result) {
            if(result !== 'true') {
                alert('Either we\'ve experienced a bug, or you\'re trying to hack the game.')
            } else {
                update()
            }
        }
    );
}

gameobj = null
function update() {
    ajaxupdategame(function(obj) {
        updatehtml(gameobj, obj)
        gameobj = obj
    })
}
<commit_msg>Minor changes<commit_after>
function Game() {
    this.trump=''
    this.players = new Array()
    this.cards = new Array()
    this.ontable = new Array()
    this.playerturn=-1         // left-of-dealer = 1
    this.dealer=-1
    this.bidwinner=-1
    this.ourscore=0
    this.theirscore=0
    this.ourtricks=0
    this.theirtricks=0
    this.trumpcard = new Card()
    this.phase = 'pregame'   // options: 'pregame', 'ready', 'preround', 'bidding', 'discard', 'tricks', 'end'
    this.teammate = ''       // the requested or present teammate's username
    this.alone = false

    this.partner = function(idx) {
        return (idx+2)%players.length
    }

    // Assuming the game's in bidding phase
    this.isFirstBiddingRound = function() {
        return this.trumpcard.isReal()
    }
}

function Card(s, t) {
    this.suit = typeof s === 'undefined' ? '' : s
    this.type = typeof t === 'undefined' ? '' : t

    this.toString = function() {
        return this.isReal() ? this.type.toString() + this.suit.toString() : 'back'
    }
    this.isReal = function() {
        return this.type !== '' && this.type !== ''
    }
}

function ajaxparseobj(doc) {
    var game = new Game()
    game.trump = doc.getElementsByTagName('trump').item(0).childNodes[0].data
    if(game.trump === 'none')
        game.trump = ''
    game.cards = ajaxgetcards(doc.getElementsByTagName('cards').item(0))
    game.ontable = ajaxgetcards(doc.getElementsByTagName('ontable').item(0))
    var playerlist = doc.getElementsByTagName('player')
    for(var i=0; i < playerlist.length; ++i) {
        game.players.push(playerlist.item(0).childNodes[0].data)
    }
    game.trumpcard = ajaxgetcards(doc.getElementsByTagName('trumpcard').item(0))
    if(game.trumpcard.length > 0)
        game.trumpcard = game.trumpcard[0]
    else
        game.trumpcard = new Card()
    game.teammate = doc.getElementsByTagName('teammate').item(0).childNodes[0]
    if(typeof game.teammate === 'undefined')
        game.teammate = ''
    else
        game.teammate = game.teammate.data
    game.alone = (doc.getElementsByTagName('alone').item(0).childNodes[0].data === 'true')
    game.phase = doc.getElementsByTagName('phase').item(0).childNodes[0].data
    game.playerturn = parseInt(doc.getElementsByTagName('playerturn').item(0).childNodes[0].data)
    game.dealer = parseInt(doc.getElementsByTagName('dealer').item(0).childNodes[0].data)
    game.bidwinner = parseInt(doc.getElementsByTagName('bidwinner').item(0).childNodes[0].data)
    game.ourscore = parseInt(doc.getElementsByTagName('ourscore').item(0).childNodes[0].data)
    game.theirscore = parseInt(doc.getElementsByTagName('theirscore').item(0).childNodes[0].data)
    game.ourtricks = parseInt(doc.getElementsByTagName('ourtricks').item(0).childNodes[0].data)
    game.theirtricks = parseInt(doc.getElementsByTagName('theirtricks').item(0).childNodes[0].data)
    return game
}

function ajaxgetcards(top) {
    var cardlist = top.getElementsByTagName('card')
    var ret = new Array()
    for(var i=0; i < cardlist.length; ++i) {
        ret.push(getcard(cardlist.item(i)))
    }
    return ret
}

function getcard(top) {
    var next = new Card()
    next.suit = top.getElementsByTagName('suit').item(0).childNodes[0].data
    next.type = top.getElementsByTagName('type').item(0).childNodes[0].data
    return next
}

function ajaxupdategame(callback) {
    var req = new XMLHttpRequest()
    if(req) {
        req.open("POST", "ctrl", true)
        req.onreadystatechange = function() {
            if(req.readyState === 4 && req.status === 200)
                callback(ajaxparseobj(req.responseXML.documentElement))
        }
        req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
        req.setRequestHeader('Ajax', 'get')
        req.setRequestHeader('resource', 'game')
        req.send('')
    }
}

function ajaxget(resource, callback) {
    var req = new XMLHttpRequest()
    if(req) {
        req.open("POST", "ctrl", true)
        req.readystateonchange = function() {
            if(req.readyState === 4 && req.status === 200)
                callback(req.responseXML.documentElement.childNodes[0].data)
        }
        req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
        req.setRequestHeader('Ajax', 'get')
        req.setRequestHeader('resource', resource)
        req.send('')
    }
}

/// Response will be one of "true", "false", "invalid action", "invalid data", and "crash".
///   "true": the action was completed successfully.
///   "false": the action could not be completed, e.g. not your turn.
///   "invalid action": there is no action called by the given name.
///   "invalid data": data string was not in an acceptable format
///   "crash": there's a bug server-side
/// If you get "false" or "invalid", I would expect that either there's a bug
//   or the user is trying to hack the system.
/// Check for all three to make debugging easier, but if the client code is
///  correct the response should almost always be "true".
function ajaxaction(action, actiondata, callback) {
    var req = new XMLHttpRequest()
    if(req) {
        req.open('POST', 'ctrl', true)
        req.onreadystatechange = function() {
            if(req.readyState === 4 && req.status === 200)
                callback(req.responseXML.documentElement.childNodes[0].data)
        }
        req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
        req.setRequestHeader('Ajax', 'set')
        req.setRequestHeader('action', action)
        req.setRequestHeader('actiondata', actiondata)
        req.send('')
    }
}

// Can leave off actiondata if it's not needed, eg doaction('deal')
/***** List of actions ******
 * 'deal': shuffle & deal cards to everyone to start a round
 * 'play', card: play the card given by 'card', eg doaction('play', '2c') for
 *             the 2 of clubs. The toString() method in Card will give you '2c', etc.
 * 'declare trump', trump, alone: declare trump, 'trump' is ignored if it's the first
 *                                round of bidding. If the player wants to go it alone,
 *                                append ":alone" to the end of trump, like so:
 *                                    doaction('declare trump', 's:alone')
 *                                or
 *                                    doaction('declare trump', ':alone')
 * 'pass': used during bidding rounds
 * 'begin': player considers himself ready to begin the game, valid during 'pregame' phase
 * 'request teammate', teammate: valid during 'pregame' phase, sending this multiple times
 *                               is supported
 * 'discard', card: discard the card given by 'card', for use by dealer when he
 *                  is told to "pick it up" during the first bidding round
 *                  (ie after someone sends 'declare trump' during first bidding round)
 */
function doaction(action, actiondata) {
    if(typeof actiondata === 'undefined')
        actiondata = ''
    ajaxaction(action, actiondata,
        function(result) {
            if(result !== 'true') {
                alert('Either we\'ve experienced a bug, or you\'re trying to hack the game.')
            } else {
                update()
            }
        }
    );
}

gameobj = null
function update() {
    ajaxupdategame(function(obj) {
        updatehtml(gameobj, obj) // might need to clone the gameobj for this to work right
        gameobj = obj
    })
}
<|endoftext|>


<commit_before>//gallery = function() {
//    return{
//        init: function() {
//            $('.pictureThumbs img').hover(function() {
//                $('img#picture').attr('src', this.src);
//            });
//        }
//    };
//}();

            $('.pictureThumbs img').hover(function() {
                $('img#picture').attr('src', this.src);
            });

function initialize() {
    var mapProp = {
      center:new google.maps.LatLng(51.508742,-0.120850),
      zoom:5,
      mapTypeId:google.maps.MapTypeId.ROADMAP
    };
    var map=new google.maps.Map(document.getElementById("googleMap"), mapProp);
}

//    $(document).ready(function() {
//        alert('Hello, World!');
//    });<commit_msg>gallery<commit_after>

//$(function(){
//  //bnd to all images with te changer class for click
//  $('img.changer').click(function(){
//    //grab the image just clicked as a jquery object.
//    var img = $(this);
//    img .data('originalsrc', img.attr('src')) //stow the original src
//        .attr('src', img.data('altsrc')       //update the src
//        .data('mode','alt')                   //tag that the img isin alt mode.
//        ; //done with img
//  });
//});

            $('.thumbs ul li a img').click(function() {
                $('.image-gallery .big-image img#default').attr('src', this.src);
            });

//gallery = function() {
//    return{
//        init: function() {
//            $('.thumbs ul li a img').hover(function() {
//                $('img#default').attr('src', this.src);
//            });
//        }
//    };
//}();

function initialize() {
    var mapProp = {
      center:new google.maps.LatLng(51.508742,-0.120850),
      zoom:5,
      mapTypeId:google.maps.MapTypeId.ROADMAP
    };
    var map=new google.maps.Map(document.getElementById("googleMap"), mapProp);
}

//    $(document).ready(function() {
//        alert('Hello, World!');
//    });

<|endoftext|>


<commit_before>Travis.Helpers.Common = {
  colorForStatus: function(result) {
    return result == 0 ? 'green' : result == 1 ? 'red' : null;
  },

  timeAgoInWords: function(date) {
    return $.timeago.distanceInWords(date);
  },

  durationFrom: function(started, finished) {
    started  = started  && new Date(this._normalizeDateString(started)).getTime();
    finished = finished ? new Date(this._normalizeDateString(finished)).getTime() : this._nowUtc();
    return started && finished ? Math.round((finished - started) / 1000) : 0;
  },

  readableTime: function(duration) {
    var days    = Math.floor(duration / 86400);
    var hours   = Math.floor(duration % 86400 / 3600);
    var minutes = Math.floor(duration % 3600 / 60);
    var seconds = duration % 60;

    if(days > 0) {
      return 'more than 24 hrs';
    } else {
      var result = [];
      if(hours  == 1) { result.push(hours + ' hr'); }
      if(hours   > 1) { result.push(hours + ' hrs'); }
      if(minutes > 0) { result.push(minutes + ' min'); }
      if(seconds > 0) { result.push(seconds + ' sec'); }
      return result.length > 0 ? result.join(' ') : '-';
    }
  },

  _normalizeDateString: function(string) {
    if(window.JHW) {
      // TODO i'm not sure why we need to do this. in the chrome console the
      // Date constructor would take a string like "2011-09-02T15:53:20.927Z"
      // whereas in unit tests this returns an "invalid date". wtf ...
      string = string.replace('T', ' ').replace(/-/g, '/');
      string = string.replace('Z', '').replace(/\..*$/, '');
    }
    return string;
  },

  _nowUtc: function() {
    var now = new Date();
    return Date.UTC(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), now.getMinutes(), now.getSeconds(), now.getMilliseconds());
  }
};
<commit_msg>Fix for "duration time bug".<commit_after>Travis.Helpers.Common = {
  colorForStatus: function(result) {
    return result == 0 ? 'green' : result == 1 ? 'red' : null;
  },

  timeAgoInWords: function(date) {
    return $.timeago.distanceInWords(date);
  },

  durationFrom: function(started, finished) {
    started  = started  && this._toUtc(new Date(this._normalizeDateString(started)));
    finished = finished ? this._toUtc(new Date(this._normalizeDateString(finished))) : this._nowUtc();
    return started && finished ? Math.round((finished - started) / 1000) : 0;
  },

  readableTime: function(duration) {
    var days    = Math.floor(duration / 86400);
    var hours   = Math.floor(duration % 86400 / 3600);
    var minutes = Math.floor(duration % 3600 / 60);
    var seconds = duration % 60;

    if(days > 0) {
      return 'more than 24 hrs';
    } else {
      var result = [];
      if(hours  == 1) { result.push(hours + ' hr'); }
      if(hours   > 1) { result.push(hours + ' hrs'); }
      if(minutes > 0) { result.push(minutes + ' min'); }
      if(seconds > 0) { result.push(seconds + ' sec'); }
      return result.length > 0 ? result.join(' ') : '-';
    }
  },

  _normalizeDateString: function(string) {
    if(window.JHW) {
      // TODO i'm not sure why we need to do this. in the chrome console the
      // Date constructor would take a string like "2011-09-02T15:53:20.927Z"
      // whereas in unit tests this returns an "invalid date". wtf ...
      string = string.replace('T', ' ').replace(/-/g, '/');
      string = string.replace('Z', '').replace(/\..*$/, '');
    }
    return string;
  },

  _nowUtc: function() {
    return this._toUtc(new Date());
  },

  _toUtc: function(date) {
    return Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds());
  }
};
<|endoftext|>


<commit_before>Template.offer.events({
    'click .cancel.offer.button': function (event) {
        event.preventDefault();

        var now = new Date(),
            userId = Meteor.user()._id,
            offerId = this._id,

            offer = document.getElementById(offerId),
            cancelAttributes = {
                cancelledBy: userId,
                cancelledAt: now
            };
            console.log (offerId);
        Meteor.call('cancelOffer', offerId, cancelAttributes, function (error) {
            if (error) {
                console.error('cancelOffer method failed: ' + error.reason);
            } else {
                console.log('Cancelled offer ' + offerId);
            }
        });
    },
    'click .accept.offer.button': function (event) {
        event.preventDefault();

        var now = new Date(),
            userId = Meteor.user()._id,
            offerId = this._id

        Meteor.call('acceptOffer', offerId, function (error) {
            if (error) {
                console.error('acceptOffer method failed: ' + error.reason);
            } else {
              }
            });

// @TODO: send to trades/:tradeId + filter out offers if openTrade true
                FlowRouter.go('trades/:tradeId');
            }
        });
<commit_msg>counter offer event outlined in todo<commit_after>Template.offer.events({
    'click .cancel.offer.button': function (event) {
        event.preventDefault();

        var now = new Date(),
            userId = Meteor.user()._id,
            offerId = this._id,
            offer = document.getElementById(offerId),
            cancelAttributes = {
                cancelledBy: userId,
                cancelledAt: now
            };

        Meteor.call('cancelOffer', offerId, cancelAttributes, function (error) {
            if (error) {
                console.error('cancelOffer method failed: ' + error.reason);
            } else {
                console.log('Cancelled offer ' + offerId);
            }
        });
    },
    'click .counter.offer.button': function (event) {
        event.preventDefault();

        var offerId = this._id;

        console.log('counter offer clicked ' + offerId);

        // @TODO: toggle offerModal true
        // if cancelCounter => offerModal false
        // if createCounter => call createOffer method
        //     reverse selectedItemId with offeredItemId
        //     call cancelOffer method on offerId
    },
    'click .accept.offer.button': function (event) {
        event.preventDefault();

        var now = new Date(),
            userId = Meteor.user()._id,
            offerId = this._id;

        Meteor.call('acceptOffer', offerId, function (error) {
            if (error) {
                console.error('acceptOffer method failed: ' + error.reason);
            } else {
            }
        });

        // @TODO: send to trades/:tradeId + filter out offers if openTrade true
        FlowRouter.go('trades/:tradeId');
    }
});
<|endoftext|>


<commit_before>import angular from 'angular';

import clonalComponent from '../clonal/component';

routeConfig.$inject = ['$routeProvider'];
function routeConfig($routeProvider) {
  $routeProvider
    .when('/charts/clonal', {
      template: '<clonal data-package="$resolve.dataPackage"></clonal>',
      datapackageUrl: 'components/charts/clonal/datapackage.json'
    })
    .otherwise({redirectTo: '/charts'});
}

export default angular
  .module('charts-index', ['projectX.dataService'])
  .component('clonal', clonalComponent)
  .config(routeConfig);
<commit_msg>/charts works (one category only)<commit_after>import angular from 'angular';

import chartsComponent from './component';
import clonalComponent from '../clonal/component';

routeConfig.$inject = ['$routeProvider'];
function routeConfig($routeProvider) {
  $routeProvider
    .when('/charts', {
      template: '<charts data-package="$resolve.dataPackage"></charts>',
      datapackageUrl: 'components/charts/charts-index/datapackage.json'
    })
    .when('/charts/clonal', {
      template: '<clonal data-package="$resolve.dataPackage"></clonal>',
      datapackageUrl: 'components/charts/clonal/datapackage.json'
    })
    .otherwise({redirectTo: '/charts'});
}

export default angular
  .module('charts-index', ['projectX.dataService'])
  .component('charts-idx', chartsComponent)
  .component('clonal', clonalComponent)
  .config(routeConfig);
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
	errorHandler = require('./errors.server.controller'),
	Dancer = mongoose.model('Dancer'),
	_ = require('lodash');

/**
 * Create a Dancer
 */
exports.create = function (req, res) {
	var dancer = new Dancer(req.body);
//	dancer.socialID = req.body.socialID;
//	dancer.firstName = req.body.firstName;
//	dancer.lastName = req.body.lastName;
//	dancer.dob = req.body.dob;
//	dancer.isPaid = req.body.isPaid;
//	dancer.partner = req.body.partner;
//	dancer.user = req.body.user;

	dancer.save(function (err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.jsonp(dancer);
		}
	});
};

/**
 * Show the current Dancer
 */
exports.read = function (req, res) {
	res.jsonp(req.dancer);
};

/**
 * Update a Dancer
 */
exports.update = function (req, res) {
	var dancer = req.dancer;

	dancer = _.extend(dancer, req.body);

	dancer.save(function (err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.jsonp(dancer);
		}
	});
};

/**
 * Delete an Dancer
 */
exports.delete = function (req, res) {
	var dancer = req.dancer;

	dancer.remove(function (err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.jsonp(dancer);
		}
	});
};

/**
 * List of Dancers
 */
exports.list = function (req, res) {
	Dancer.find()
        .sort('lastName')
        .populate('category')
        .populate('gender')
        .exec(function (err, dancers) {
            if (err) {
                return res.status(400).send({
                    message: errorHandler.getErrorMessage(err)
                });
            } else {
                res.jsonp(dancers);
            }
        });
};

exports.listMale = function (req, res) {
	Dancer.find()
		.sort('lastName')
		.populate('category')
		.populate('gender')
		.exec(function (err, dancers) {
			if (err) {
				return res.status(400).send({
					message: errorHandler.getErrorMessage(err)
				});
			} else {
				var men = dancers.filter(function (doc) {
                    if (doc.gender.name === 'Male') {
                        return doc;
                    }
			    });
				res.jsonp(men);
			}
		});
};

exports.listFemale = function (req, res) {
	Dancer.find()
		.sort('lastName')
		.populate('category')
		.populate('gender')
		.exec(function (err, dancers) {
			if (err) {
				return res.status(400).send({
					message: errorHandler.getErrorMessage(err)
				});
			} else {
				var women = dancers.filter(function (doc) {
					if (doc.gender.name === 'Female') {
						return doc;
                    }
				});
				res.jsonp(women);
			}
		});
};

/**
 * Dancer middleware
 */
exports.dancerByID = function (req, res, next, id) {
	Dancer.findById(id)
		.populate('category')
		.populate('gender')
		.exec(function (err, dancer) {
            if (err) {
                return next(err);
            }

            if (!dancer) {
                return next(new Error('Failed to load Dancer ' + id));
            }

		// prints "The dancer's info"
//		console.log('The dancer %s info: category %s, gender %s',
//		dancer.lastName, dancer.category.name, dancer.gender._id );
            req.dancer = dancer;
            next();
        });
};

/**
 * Dancer authorization middleware
 */
exports.hasAuthorization = function (req, res, next) {
//	if (req.dancer.socialID !== req.socialID) {
	if (req.dancer.id !== req.id) {
		return res.status(403).send('Dancer is not authorized');
	}
	next();
};
<commit_msg>[create-pair] Warning processing<commit_after>'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
	errorHandler = require('./errors.server.controller'),
	Dancer = mongoose.model('Dancer'),
	_ = require('lodash');

/**
 * Create a Dancer
 */
exports.create = function (req, res) {
	var dancer = new Dancer(req.body);
//	dancer.socialID = req.body.socialID;
//	dancer.firstName = req.body.firstName;
//	dancer.lastName = req.body.lastName;
//	dancer.dob = req.body.dob;
//	dancer.isPaid = req.body.isPaid;
//	dancer.partner = req.body.partner;
//	dancer.user = req.body.user;

	dancer.save(function (err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.jsonp(dancer);
		}
	});
};

/**
 * Show the current Dancer
 */
exports.read = function (req, res) {
	res.jsonp(req.dancer);
};

/**
 * Update a Dancer
 */
exports.update = function (req, res) {
	var dancer = req.dancer;

	dancer = _.extend(dancer, req.body);

	dancer.save(function (err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.jsonp(dancer);
		}
	});
};

/**
 * Delete an Dancer
 */
exports.delete = function (req, res) {
	var dancer = req.dancer;

	dancer.remove(function (err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.jsonp(dancer);
		}
	});
};

/**
 * List of Dancers
 */
exports.list = function (req, res) {
	Dancer.find()
        .sort('lastName')
//        .populate('category')
//        .populate('gender')
        .exec(function (err, dancers) {
            if (err) {
                return res.status(400).send({
                    message: errorHandler.getErrorMessage(err)
                });
            } else {
                res.jsonp(dancers);
            }
        });
};

exports.listMale = function (req, res) {
	Dancer.find()
		.sort('lastName')
//		.populate('category')
//		.populate('gender')
		.exec(function (err, dancers) {
			if (err) {
				return res.status(400).send({
					message: errorHandler.getErrorMessage(err)
				});
			} else {
//				var men = dancers.filter(function (doc) {
//                    if (doc.gender.name === 'Male') {
//                        return doc;
//                    }
//			    });
				res.jsonp(res);
			}
		});
};

exports.listFemale = function (req, res) {
	Dancer.find()
		.sort('lastName')
//		.populate('category')
//		.populate('gender')
		.exec(function (err, dancers) {
			if (err) {
				return res.status(400).send({
					message: errorHandler.getErrorMessage(err)
				});
			} else {
//				var women = dancers.filter(function (doc) {
//					if (doc.gender.name === 'Female') {
//						return doc;
//                    }
//				});
				res.jsonp(res);
			}
		});
};

/**
 * Dancer middleware
 */
exports.dancerByID = function (req, res, next, id) {
	Dancer.findById(id)
		.populate('category')
		.populate('gender')
		.exec(function (err, dancer) {
            if (err) {
                return next(err);
            }

            if (!dancer) {
                return next(new Error('Failed to load Dancer ' + id));
            }

		// prints "The dancer's info"
//		console.log('The dancer %s info: category %s, gender %s',
//		dancer.lastName, dancer.category.name, dancer.gender._id );
            req.dancer = dancer;
            next();
        });
};

/**
 * Dancer authorization middleware
 */
exports.hasAuthorization = function (req, res, next) {
//	if (req.dancer.socialID !== req.socialID) {
	if (req.dancer.id !== req.id) {
		return res.status(403).send('Dancer is not authorized');
	}
	next();
};
<|endoftext|>


<commit_before><commit_msg>Refactor and cleanup of the express middleware.<commit_after><|endoftext|>


<commit_before><commit_msg>mod_admin_web: Correctly access items in NamedNodeMaps<commit_after><|endoftext|>


<commit_before>/* MIT License
   Copyright 2015
   Created by partheseas
   Weave - Make webs */

"use strict";

// Basic universal type detection and property setting.
Function.prototype.check = function (a) { return a!=null&&(a.constructor===this.prototype.constructor) }
Function.prototype.is = function (a) { console.log('Depreciated ::is!'); return this.check(a) }
Object.extend = function (o,e) {o!=null&&e!=null&&Object.keys(e).forEach(p => o[p]=e[p]);return o}

// TODO: Here are all the things I still need to bring over from Trailer.
// URL Redirects, ReadFile with Content-Length***,
// Should we allow Constant HTTP Headers? Do they need to be written directly to each individual
// request? How will they be written?

// ***So I've since learned that the Content-Length header is not supposed to be
// used if the Transfer-Encoding is Chunked. So that's not really something
// that we need to do, because we always use Transfer-Encoding Chunked. ***

// TODO: All the things that are going to be new features in Weave that have
// not yet found a spot for implementation or are still being discussed.

// # Groundwork - v0.1 (WebSocks)
// Get the basics in, along with a couple of goodies. Be generally reliable,
// strong routing features, a base WebSocket implementation, document generation
// through a server-side DOM tree for error pages, etc., basic caching ground work.

// # Console/file logging - v0.2
// Build a robust error and warning backend system with the ability to log to files

// # Remote debugging / Admin panel - v0.3
// Add the ability to have an online debug console/admin panel that you would
// activate with App.addInterface( "/net-internals", new weave.AdminPanel(App) )
// or something similar. You'd be able to monitor an incoming request queue,
// interface directly with the App collection from a secure, remote, REPL with
// syntax highlighting, pretty printing, and more.

// # Command Line Interface - v0.4 (Librarian (Sh))
// Could be helpful for things like clearing a cache or updating a setting.
// The main reason this would be important, is it would provide a simple way
// to actively maintain your server while it is running, without needing to
// close a process, and open a new one. You could essentially "hot-swap" your
// configuration of the directory by using the command line. This could also
// also provide a simple way to start a server from the command line without
// directly handling a node process instance. This would allow you to close
// your terminal window when it's unneeded instead of leaving it open all the
// time. This is something that I've wanted to be able to do for a while, but
// is incredibly hard to implement, and would need to be outlined in detail.

// # File system caching, chunking - v0.5 (Snacks)
// Read large files in chunks rather than entirely at once to avoid eating memory.
// Cache smaller files in memory for quick access, with easily set parameters for
// how much we can store.

// # Partial downloads, uploads, streaming - v0.6 (Sherbet Ice-Stream)
// Enable the ability to stream a video file from a certain time stamp, to
// resume a disrupted download at a later time, and to accept user uploads.

// # Standalone operation - v0.7
// Polish the CLI enough to be able to run entirely from the command line.

// # SPDY Protocol / GZip / HTTP 2.0 / HTTPS support - v0.8
// Add support for Google's SPDY protocol and for HTTP 2.0. HTTPS support is
// hard for me to implement without access to any sort of secure certificate
// of my own to work with, and is a requirement for SPDY.
// So it seems that HTTPS support is simply creating an extra server, using
// certificates supplied, and doing some extra stuff to hook up the server
// to the app. Not sure how supporting multiple certificates would work.

// # Stability & fine tuned error/warning reporting - v0.9
// As we approach feature completeness and having a production ready product,
// fine tune the stability of the Weave to ensure that one request being mishandled
// or any third party code is never able to completely break the process. try, catch
// pairs on any calls to external resources, argument checking on all built in methods,
// and ensuring that any and all errors are incredibly tracable to make debugging
// quick and painless.

// # Release - v1.0
// All desired features polished and in place for the world to see!

let crypto = require( 'crypto' )
let http = require( 'http' )
let path = require( 'path' )

let weave = module.exports = exports = {
  version: '0.1.11',

  servers: {}, apps: {}, hosts: {},
  constants: { WebSocketUUID: "258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
               HOME: process.env.HOME || process.env.HOMEDRIVE + process.env.HOMEPATH || "/",
               STATUS_CODES: http.STATUS_CODES,
               STATUS_DESCRIPTIONS: {
                 404: "The file you requested does not exist."
               } },

  configuration: {
    'location': path.join( __dirname, 'http/default' ),

    'cache': {
      maxCacheSize: 500*1024, // 500*1024 is 500MB
      maxCachedFileSize: 5*1024 // 5*1024 is 5MB
    }
   // adminEmail: String,
   // logOutput: String,
  },

  util: {
    SHA1_64: function ( data ) {
      return crypto.createHash( "sha1" ).update( data ).digest( "base64" ) },
    RNDM_RG: function ( min, max, base ) {
      let r = Math.floor( ( Math.random() * ( ( max + 1 ) - min ) ) + min );
      return base ? r.toString( base ) : r },
    READ_BITS: function ( byte ) {
      return [ byte >= 128 ? 1 : 0,
      (byte %= 128) >= 64  ? 1 : 0,
      (byte %= 64)  >= 32  ? 1 : 0,
      (byte %= 32)  >= 16  ? 1 : 0,
      (byte %= 16)  >= 8   ? 1 : 0,
      (byte %= 8)   >= 4   ? 1 : 0,
      (byte %= 4)   >= 2   ? 1 : 0,
      (byte %= 2)   == 1   ? 1 : 0 ] },
    BINARY_UINT: function ( binary ) {
      return Number.parseInt( binary.join(''), 2 ) },
    times: function ( times, task ) {
      var t = 0; while ( t++ < times ) { task() } } },

  Dictionary: require( './utilities/MimeDictionary' ),
  Garden: require( './utilities/Garden'),

  HTTPError: function ( code, description ) {
    if ( !Number.is( code ) ) {
      console.error( 'HTTPError requires argument code to be a number!' )
    }

    return Object.create( weave.HTTPError.prototype, {
      constructor: {
        value: weave.HTTPError,
        enumerable: false, writable: false, configurable: true },
      status: {
        value: weave.constants.STATUS_CODES[ code ],
        enumerable: true, writable: false, configurable: true },
      statusCode: {
        value: code,
        enumerable: true, writable: false, configurable: true },
      description: {
        value: description || weave.constants.STATUS_DESCRIPTIONS[ code ],
        enumerable: true, writable: false, configurable: true }
    })
  }
};

[ 'app', 'cache', 'connection', 'router', 'manifest', 'printer', 'websocket' ].forEach( module => require( `./${module}` ) )

process.argv.forEach( function ( arg ) {
  switch ( arg ) {
    case "--aww-heck-yes":
      console.log( "aww heck yes" )
      break;
    case "--weave-verbose":
      weave.Garden.enableDebug()
      break;
    case "--enable-weave-repl":
      require( './developer/repl' )
      weave.connectREPL( process.stdin, process.stdout )
      break;
    case "--enable-weave-instruments":
      require( './developer/instruments' )
      break;
  }
})
<commit_msg>Make weave a function (to allow usage similar to express or connect)<commit_after>/* MIT License
   Copyright 2015
   Created by partheseas
   Weave - Make webs */

"use strict";

// Basic universal type detection and property setting.
Function.prototype.check = function (a) { return a!=null&&(a.constructor===this.prototype.constructor) }
Function.prototype.is = function (a) { console.log('Depreciated ::is!'); return this.check(a) }

// TODO: Here are all the things I still need to bring over from Trailer.
// URL Redirects, ReadFile with Content-Length***,
// Should we allow Constant HTTP Headers? Do they need to be written directly to each individual
// request? How will they be written?

// ***So I've since learned that the Content-Length header is not supposed to be
// used if the Transfer-Encoding is Chunked. So that's not really something
// that we need to do, because we always use Transfer-Encoding Chunked. ***

// TODO: All the things that are going to be new features in Weave that have
// not yet found a spot for implementation or are still being discussed.

// # Groundwork - v0.1 (WebSocks)
// Get the basics in, along with a couple of goodies. Be generally reliable,
// strong routing features, a base WebSocket implementation, document generation
// through a server-side DOM tree for error pages, etc., basic caching ground work.

// # Console/file logging - v0.2
// Build a robust error and warning backend system with the ability to log to files

// # Remote debugging / Admin panel - v0.3
// Add the ability to have an online debug console/admin panel that you would
// activate with App.addInterface( "/net-internals", new weave.AdminPanel(App) )
// or something similar. You'd be able to monitor an incoming request queue,
// interface directly with the App collection from a secure, remote, REPL with
// syntax highlighting, pretty printing, and more.

// # Command Line Interface - v0.4 (Librarian (Sh))
// Could be helpful for things like clearing a cache or updating a setting.
// The main reason this would be important, is it would provide a simple way
// to actively maintain your server while it is running, without needing to
// close a process, and open a new one. You could essentially "hot-swap" your
// configuration of the directory by using the command line. This could also
// also provide a simple way to start a server from the command line without
// directly handling a node process instance. This would allow you to close
// your terminal window when it's unneeded instead of leaving it open all the
// time. This is something that I've wanted to be able to do for a while, but
// is incredibly hard to implement, and would need to be outlined in detail.

// # File system caching, chunking - v0.5 (Snacks)
// Read large files in chunks rather than entirely at once to avoid eating memory.
// Cache smaller files in memory for quick access, with easily set parameters for
// how much we can store.

// # Partial downloads, uploads, streaming - v0.6 (Sherbet Ice-Stream)
// Enable the ability to stream a video file from a certain time stamp, to
// resume a disrupted download at a later time, and to accept user uploads.

// # Standalone operation - v0.7
// Polish the CLI enough to be able to run entirely from the command line.

// # SPDY Protocol / GZip / HTTP 2.0 / HTTPS support - v0.8
// Add support for Google's SPDY protocol and for HTTP 2.0. HTTPS support is
// hard for me to implement without access to any sort of secure certificate
// of my own to work with, and is a requirement for SPDY.
// So it seems that HTTPS support is simply creating an extra server, using
// certificates supplied, and doing some extra stuff to hook up the server
// to the app. Not sure how supporting multiple certificates would work.

// # Stability & fine tuned error/warning reporting - v0.9
// As we approach feature completeness and having a production ready product,
// fine tune the stability of the Weave to ensure that one request being mishandled
// or any third party code is never able to completely break the process. try, catch
// pairs on any calls to external resources, argument checking on all built in methods,
// and ensuring that any and all errors are incredibly tracable to make debugging
// quick and painless.

// # Release - v1.0
// All desired features polished and in place for the world to see!

let crypto = require( 'crypto' )
let http = require( 'http' )
let path = require( 'path' )

let weave = module.exports = exports = (...args) => new weave.App(...args)

Object.assign( weave, {
  version: '0.1.11',

  servers: {}, apps: { anonymous: [] }, hosts: {},
  constants: { WebSocketUUID: '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
               HOME: process.env.HOME || process.env.HOMEDRIVE + process.env.HOMEPATH || '/',
               STATUS_CODES: http.STATUS_CODES,
               STATUS_DESCRIPTIONS: {
                 404: "The file you requested does not exist."
               } },

  configuration: {
    'location': path.join( __dirname, 'http/default' ),

    'cache': {
      maxCacheSize: 500*1024, // 500*1024 is 500MB
      maxCachedFileSize: 5*1024 // 5*1024 is 5MB
    }
   // adminEmail: String,
   // logOutput: String,
  },

  util: {
    SHA1_64: data => crypto.createHash( 'sha1' ).update( data ).digest( 'base64' ),
    RNDM_RG: function ( min, max, base ) {
      let r = Math.floor( ( Math.random() * ( ( max + 1 ) - min ) ) + min );
      return base ? r.toString( base ) : r },
    READ_BITS: byte => [ byte >= 128 ? 1 : 0,
                        (byte %= 128) >= 64  ? 1 : 0,
                        (byte %= 64)  >= 32  ? 1 : 0,
                        (byte %= 32)  >= 16  ? 1 : 0,
                        (byte %= 16)  >= 8   ? 1 : 0,
                        (byte %= 8)   >= 4   ? 1 : 0,
                        (byte %= 4)   >= 2   ? 1 : 0,
                        (byte %= 2)   == 1   ? 1 : 0 ],
    BINARY_UINT: binary => Number.parseInt( binary.join(''), 2 ),
    times: ( times, task ) => { for ( let t = 0; t < times; t++ ) task() } },

  Dictionary: require( './utilities/MimeDictionary' ),
  Garden: require( './utilities/Garden'),

  HTTPError: function ( code, description ) {
    if ( !Number.is( code ) ) {
      console.error( 'HTTPError requires argument code to be a number!' )
    }

    return Object.create( weave.HTTPError.prototype, {
      constructor: {
        value: weave.HTTPError,
        enumerable: false, writable: false, configurable: true },
      status: {
        value: weave.constants.STATUS_CODES[ code ],
        enumerable: true, writable: false, configurable: true },
      statusCode: {
        value: code,
        enumerable: true, writable: false, configurable: true },
      description: {
        value: description || weave.constants.STATUS_DESCRIPTIONS[ code ],
        enumerable: true, writable: false, configurable: true }
    })
  }
});

[ 'app', 'cache', 'connection', 'router', 'manifest', 'printer', 'websocket' ].forEach( module => require( `./${module}` ) )

process.argv.forEach( function ( arg ) {
  switch ( arg ) {
    case "--aww-heck-yes":
      console.log( "aww heck yes" )
      break;
    case "--weave-verbose":
      weave.Garden.enableDebug()
      break;
    case "--enable-weave-repl":
      require( './developer/repl' )
      weave.connectREPL( process.stdin, process.stdout )
      break;
    case "--enable-weave-instruments":
      require( './developer/instruments' )
      break;
  }
})
<|endoftext|>


<commit_before>/**
 * Created by pjurasek on 17.10.16.
 */

var apiClass = undefined;

class Api
{

    constructor(url, auth) {
        this.url = url;
        this.auth = auth;
    }

    getNamespaces() {
        return fetch(this.url+'/dataStore', this.getHeaders())
            .then(response => this.successOnly(response))
            .then(response => response.json());
    }

    deleteNamespace(namespace) {
        return fetch(this.url+'/dataStore/'+namespace, Object.assign({}, {
            'method': 'DELETE',
        }, this.getHeaders()))
            .then(response => this.successOnly(response))
            .then(response => response.json());
    }

    getKeys(namespace) {
        return fetch(this.url+'/dataStore/'+namespace, this.getHeaders())
            .then(response => this.successOnly(response))
            .then(response => response.json());
    }

    getValue(namespace, key) {
        return fetch(this.url+'/dataStore/'+namespace+'/'+key, this.getHeaders())
            .then(response => this.successOnly(response))
            .then(response => response.json());
    }

    getMetaData(namespace, key) {
        return fetch(this.url+'/dataStore/'+namespace+'/'+key+'/metaData', this.getHeaders())
            .then(response => this.successOnly(response))
            .then(response => response.json());
    }

    createValue(namespace, key, value) {
        return fetch(this.url+'/dataStore/'+namespace+'/'+key, Object.assign({}, {
            'method': 'POST',
            'body': JSON.stringify(value),
        }, this.getHeaders()))
            .then(response => this.successOnly(response))
            .then(response => response.json());
    }

    updateValue(namespace, key, value) {
        return fetch(this.url+'/dataStore/'+namespace+'/'+key, Object.assign({}, {
            'method': 'PUT',
            'body': JSON.stringify(value),
        }, this.getHeaders()))
            .then(response => this.successOnly(response))
            .then(response => response.json());
    }

    deleteValue(namespace, key) {
        return fetch(this.url+'/dataStore/'+namespace+'/'+key, Object.assign({}, {
            'method': 'DELETE',
        }, this.getHeaders()))
            .then(response => this.successOnly(response))
            .then(response => response.json());
    }

    successOnly(response) {
        if (response.status >= 200 && response.status < 300) {
            return Promise.resolve(response);
        }
        return Promise.reject(response);
    }

    getHeaders() {
        return {
            'headers': {
                'Content-Type': 'application/json',
                'Authorization': this.auth
            }
        };
    }
}

export default (function getApi() {
    if (typeof apiClass === 'undefined') {
        apiClass = new Api('https://play.dhis2.org/demo/api', `Basic ${btoa('admin:district')}`);
    }

    return apiClass;
})();
<commit_msg>Api: history of key values is tracker<commit_after>/**
 * Created by pjurasek on 17.10.16.
 */

var apiClass = undefined;

class Api
{

    constructor(url, auth) {
        this.url = url;
        this.auth = auth;
    }

    getNamespaces() {
        return fetch(this.url+'/dataStore', this.getHeaders())
            .then(response => this.successOnly(response))
            .then(response => response.json());
    }

    deleteNamespace(namespace) {
        return fetch(this.url+'/dataStore/'+namespace, Object.assign({}, {
            'method': 'DELETE',
        }, this.getHeaders()))
            .then(response => this.successOnly(response))
            .then(response => response.json());
    }

    getKeys(namespace) {
        return fetch(this.url+'/dataStore/'+namespace, this.getHeaders())
            .then(response => this.successOnly(response))
            .then(response => response.json());
    }

    getValue(namespace, key) {
        return fetch(this.url+'/dataStore/'+namespace+'/'+key, this.getHeaders())
            .then(response => this.successOnly(response))
            .then(response => response.json());
    }

    getMetaData(namespace, key) {
        return fetch(this.url+'/dataStore/'+namespace+'/'+key+'/metaData', this.getHeaders())
            .then(response => this.successOnly(response))
            .then(response => response.json());
    }

    createValue(namespace, key, value, log = true) {
        return fetch(this.url+'/dataStore/'+namespace+'/'+key, Object.assign({}, {
            'method': 'POST',
            'body': JSON.stringify(value),
        }, this.getHeaders()))
            .then(response => this.successOnly(response))
            .then(response => response.json())
            .then(response => {
                log && this.updateHistory(namespace, key, value);
                return response;
            });
    }

    updateValue(namespace, key, value, log = true) {
        return fetch(this.url+'/dataStore/'+namespace+'/'+key, Object.assign({}, {
            'method': 'PUT',
            'body': JSON.stringify(value),
        }, this.getHeaders()))
            .then(response => this.successOnly(response))
            .then(response => response.json())
            .then(response => {
                log && this.updateHistory(namespace, key, value);
                return response;
            });
    }

    deleteValue(namespace, key) {
        return fetch(this.url+'/dataStore/'+namespace+'/'+key, Object.assign({}, {
            'method': 'DELETE',
        }, this.getHeaders()))
            .then(response => this.successOnly(response))
            .then(response => response.json())
            .then(response => {
                this.updateHistory(namespace, key, 'DELETED');
                return response;
            });
    }

    getHistory(namespace, key) {

        return fetch(this.url+'/dataStore/HISTORYSTORE/'+id, this.getHeaders());
    }

    updateHistory(namespace, key, newValue) {
        const id = this.buildId(namespace, key);

        return this.getHistory(namespace, key)
            .then(response => {
                if (response.status === 404) {
                    this.createValue('HISTORYSTORE', id, [newValue], false);
                    return null;
                } else {
                    return response.json();
                }
            }).then(history => {
                if (history !== null) {
                    history.unshift(newValue);
                    this.updateValue('HISTORYSTORE', id, history, false);
                }
            });
    }

    successOnly(response) {
        if (response.status >= 200 && response.status < 300) {
            return Promise.resolve(response);
        }
        return Promise.reject(response);
    }

    buildId(namespace, key) {
        return encodeURIComponent(namespace+':'+key);
    }

    getHeaders() {
        return {
            'headers': {
                'Content-Type': 'application/json',
                'Authorization': this.auth
            }
        };
    }
}

export default (function getApi() {
    if (typeof apiClass === 'undefined') {
        apiClass = new Api('https://play.dhis2.org/demo/api', `Basic ${btoa('admin:district')}`);
    }

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