<commit_before>(function() {
    var Dialog = function(opts) {
        this._pfx = 'dib';
        this._id = prefix(this._pfx);
        this._opts = opts || {};
        return this.init();
    };
    Dialog.prototype.init = function() {
        this.isOpened = false;
        if (!this._opts || !this._opts.el) this.makeUniqueDlg.call(this, this._opts);
        else this._$dlg = opts.el;
        return this;
    };
    Dialog.prototype.makeUniqueDlg = function(opts) {
        var self = this;
        this.title = opts.title || this.title || false;
        opts.draggable = opts.draggable || false;
        this._$dlg = $('
');
        this._$dlg.attr('id', this._id).addClass(this._pfx+'-dialog');
        var $btn_close = $('').appendTo(this._$dlg);
        $btn_close.on('click', function() {
            self.close();
        });
        if (this.title) {
            var $title = $('
');
            $title.addClass(this._pfx+'-dialog-title').text(this.title);
            this._$dlg.prepend($title);
            if (opts.draggable) draggable(this._$dlg, $title);
        }
        if (opts.mask) {
            this._$mask = $('
');
            this._$dlg.appendTo(this._$mask);
            this._$mask.appendTo($('body'));
        } else this._$dlg.appendTo($('body'));

        var $content = $('
');
        $content.addClass(this._pfx+'-dialog-content');
        this._$dlg.append($content);
        if (opts.content) this.setContent(opts.content);

        if (opts.btns && opts.btns.length > 0) {
            var $btnsWrapper = $('
');
            $btnsWrapper.addClass(this._pfx+'-dialog-btns-wrapper');
            this._$btns = $('
');
            this._$btns.addClass(this._pfx+'-dialog-btns');
            for (var i = 0; i < opts.btns.length; i++) {
                this.addBtn(opts.btns[i]);
            }
            $btnsWrapper.append(this._$btns);
            this._$dlg.append($btnsWrapper);
        }
        moveToCenter(this._$dlg);
        $(window).resize(function() {
            moveToCenter(self._$dlg);
        });
        $(document).on('mousedown', function(e) {
            if (self.isOpened && $(e.target).closest('#'+self._id).length < 1) {
                self._$dlg.addClass('shake');
                $('.shake').one('animationend', function() {
                    $(this).removeClass('shake');
                });
            }
        });
        return this;
    };
    Dialog.prototype.addBtn = function(opts) {
        var self = this;
        var $btn = $('');
        $btn.addClass(this._pfx+'-dialog-btn');
        if (opts.important && opts.important === true) {
            $btn.addClass(this._pfx+'-dialog-btn-important');
        }
        if (opts.disabled && opts.disabled === true) {
            $btn.addClass(this._pfx+'-dialog-btn-disabled');
        } else {
            $btn.on('click', function(e) {
                if (opts.callback) opts.callback.call(self, opts);
            });
        }
        $btn.text(opts.name)

        this._$btns.append($btn);
        moveToCenter(this._$dlg);
        return this;
    };
    Dialog.prototype.setTitle = function(newTitle) {
        var $title = this._$dlg.find('.'+this._pfx+'-dialog-title');
        console.log($title);
        if ($title && $title.length < 1) {
            this.title = newTitle;
            $title = $('
');
            $title.addClass(this._pfx+'-dialog-title').text(this.title);
            this._$dlg.prepend($title);
        } else {
            this.title = newTitle;
            $title.text(this.title);
        }
        return this;
    };
    Dialog.prototype.setContent = function(newContent) {
        this._$dlg.find('.'+this._pfx+'-dialog-content').html(newContent);
        moveToCenter(this._$dlg);
        return this;
    };
    Dialog.prototype.open = function() {
        if (this.isOpened) return this;
        this.isOpened = true;
        this._$dlg.addClass('fade-in');
        $('.fade-in').one('animationend', function() {
            $(this).removeClass('fade-in');
        });
        if (this._opts && this._opts.onBeforeOpen) this._opts.onBeforeOpen.call(this);
        this._$dlg.show();
        if (this._opts && this._opts.onAfterOpen) this._opts.onAfterOpen.call(this);
        return this;
    };
    Dialog.prototype.close = function() {
        if (!this.isOpened) return this;
        var self = this;
        this.isOpened = false;
        if (this._opts && this._opts.onBeforeClose) this._opts.onBeforeClose.call(this);
        if (this._$mask) {
            this._$mask.fadeOut(80, function() {
                $(this).remove();
                if (self._opts && self._opts.onAfterClose) self._opts.onAfterClose.call(self);
            });
        } else {
            this._$dlg.remove();
            if (self._opts && self._opts.onAfterClose) self._opts.onAfterClose.call(self);
        }
        return this;
    };

    function prefix(symbol) {
        return symbol+'-'+('xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
            var r = Math.random() * 16 | 0,
                v = c == 'x' ? r : (r & 0x3 | 0x8);
            return v.toString(16);
        }).toUpperCase());
    }

    function moveToCenter($dom) {
        var l = ($(window).width() - $dom.outerWidth()) / 2;
        var t = ($(window).height() - $dom.outerHeight()) / 2;
        $dom.css({left: l, top: t});
    }
    function draggable($dom, $target) {
        var isMouseDown = false;
        var lastX = 0;
        var lastY = 0;
        var deltaX = 0;
        var deltaY = 0;
        $target.off('mousedown').on('mousedown', function() {
            isMouseDown = true;
        });
        $(document).on('mousedown', function(e) {
            lastX = e.clientX;
            lastY = e.clientY;
            deltaX = e.clientX - parseInt($dom.css('left'));
            deltaY = e.clientY - parseInt($dom.css('top'));
            $dom.css('transition', 'none');
        });
        $(document).on('mouseup', function() {
            isMouseDown = false;
            $dom.css('transition', 'all 0.6s');
        });
        $(document).on('mousemove', function(e) {
            setTimeout(function() {
                if (!isMouseDown) return false;
                $dom.css({left: e.clientX-deltaX, top: e.clientY-deltaY});
                lastX = e.clientX;
                lastY = e.clientY;
            }, 0);
        });
    }
    function extend(Child, Parent) {
    　　var F = function() {};
        F.prototype = Parent.prototype;
        Child.prototype = new F();
        Child.prototype.constructor = Child;
        Child.uber = Parent.prototype;
    }

    var Alert = function(newContent) {
        return new Dialog({
            mask: true,
            content: '
'+newContent+'
',
            btns: [
                {name: '关闭', callback: function(opts) {
                    this.close();
                }}
            ]
        });
    };

    var root = typeof exports !== "undefined" && exports !== null ? exports : window;
    root.Dialog = Dialog;
    root.Alert = Alert;
}());<commit_msg>fix shake triggered when clicking scroll bar<commit_after>(function() {
    var Dialog = function(opts) {
        this._pfx = 'dib';
        this._id = prefix(this._pfx);
        this._opts = opts || {};
        return this.init();
    };
    Dialog.prototype.init = function() {
        this.isOpened = false;
        if (!this._opts || !this._opts.el) this.makeUniqueDlg.call(this, this._opts);
        else this._$dlg = opts.el;
        return this;
    };
    Dialog.prototype.makeUniqueDlg = function(opts) {
        var self = this;
        this.title = opts.title || this.title || false;
        opts.draggable = opts.draggable || false;
        this._$dlg = $('
');
        this._$dlg.attr('id', this._id).addClass(this._pfx+'-dialog');
        var $btn_close = $('').appendTo(this._$dlg);
        $btn_close.on('click', function() {
            self.close();
        });
        if (this.title) {
            var $title = $('
');
            $title.addClass(this._pfx+'-dialog-title').text(this.title);
            this._$dlg.prepend($title);
            if (opts.draggable) draggable(this._$dlg, $title);
        }
        if (opts.mask) {
            this._$mask = $('
');
            this._$dlg.appendTo(this._$mask);
            this._$mask.appendTo($('body'));
        } else this._$dlg.appendTo($('body'));

        var $content = $('
');
        $content.addClass(this._pfx+'-dialog-content');
        this._$dlg.append($content);
        if (opts.content) this.setContent(opts.content);

        if (opts.btns && opts.btns.length > 0) {
            var $btnsWrapper = $('
');
            $btnsWrapper.addClass(this._pfx+'-dialog-btns-wrapper');
            this._$btns = $('
');
            this._$btns.addClass(this._pfx+'-dialog-btns');
            for (var i = 0; i < opts.btns.length; i++) {
                this.addBtn(opts.btns[i]);
            }
            $btnsWrapper.append(this._$btns);
            this._$dlg.append($btnsWrapper);
        }
        moveToCenter(this._$dlg);
        $(window).resize(function() {
            moveToCenter(self._$dlg);
        });
        $('body').on('mousedown', function(e) {
            if (self.isOpened && $(e.target).closest('#'+self._id).length < 1) {
                self._$dlg.addClass('shake');
                $('.shake').one('animationend', function() {
                    $(this).removeClass('shake');
                });
            }
        });
        return this;
    };
    Dialog.prototype.addBtn = function(opts) {
        var self = this;
        var $btn = $('');
        $btn.addClass(this._pfx+'-dialog-btn');
        if (opts.important && opts.important === true) {
            $btn.addClass(this._pfx+'-dialog-btn-important');
        }
        if (opts.disabled && opts.disabled === true) {
            $btn.addClass(this._pfx+'-dialog-btn-disabled');
        } else {
            $btn.on('click', function(e) {
                if (opts.callback) opts.callback.call(self, opts);
            });
        }
        $btn.text(opts.name)

        this._$btns.append($btn);
        moveToCenter(this._$dlg);
        return this;
    };
    Dialog.prototype.setTitle = function(newTitle) {
        var $title = this._$dlg.find('.'+this._pfx+'-dialog-title');
        console.log($title);
        if ($title && $title.length < 1) {
            this.title = newTitle;
            $title = $('
');
            $title.addClass(this._pfx+'-dialog-title').text(this.title);
            this._$dlg.prepend($title);
        } else {
            this.title = newTitle;
            $title.text(this.title);
        }
        return this;
    };
    Dialog.prototype.setContent = function(newContent) {
        this._$dlg.find('.'+this._pfx+'-dialog-content').html(newContent);
        moveToCenter(this._$dlg);
        return this;
    };
    Dialog.prototype.open = function() {
        if (this.isOpened) return this;
        this.isOpened = true;
        this._$dlg.addClass('fade-in');
        $('.fade-in').one('animationend', function() {
            $(this).removeClass('fade-in');
        });
        if (this._opts && this._opts.onBeforeOpen) this._opts.onBeforeOpen.call(this);
        this._$dlg.show();
        if (this._opts && this._opts.onAfterOpen) this._opts.onAfterOpen.call(this);
        return this;
    };
    Dialog.prototype.close = function() {
        if (!this.isOpened) return this;
        var self = this;
        this.isOpened = false;
        if (this._opts && this._opts.onBeforeClose) this._opts.onBeforeClose.call(this);
        if (this._$mask) {
            this._$mask.fadeOut(80, function() {
                $(this).remove();
                if (self._opts && self._opts.onAfterClose) self._opts.onAfterClose.call(self);
            });
        } else {
            this._$dlg.remove();
            if (self._opts && self._opts.onAfterClose) self._opts.onAfterClose.call(self);
        }
        return this;
    };

    function prefix(symbol) {
        return symbol+'-'+('xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
            var r = Math.random() * 16 | 0,
                v = c == 'x' ? r : (r & 0x3 | 0x8);
            return v.toString(16);
        }).toUpperCase());
    }

    function moveToCenter($dom) {
        var l = ($(window).width() - $dom.outerWidth()) / 2;
        var t = ($(window).height() - $dom.outerHeight()) / 2;
        $dom.css({left: l, top: t});
    }
    function draggable($dom, $target) {
        var isMouseDown = false;
        var lastX = 0;
        var lastY = 0;
        var deltaX = 0;
        var deltaY = 0;
        $target.off('mousedown').on('mousedown', function() {
            isMouseDown = true;
        });
        $(document).on('mousedown', function(e) {
            lastX = e.clientX;
            lastY = e.clientY;
            deltaX = e.clientX - parseInt($dom.css('left'));
            deltaY = e.clientY - parseInt($dom.css('top'));
            $dom.css('transition', 'none');
        });
        $(document).on('mouseup', function() {
            isMouseDown = false;
            $dom.css('transition', 'all 0.6s');
        });
        $(document).on('mousemove', function(e) {
            setTimeout(function() {
                if (!isMouseDown) return false;
                $dom.css({left: e.clientX-deltaX, top: e.clientY-deltaY});
                lastX = e.clientX;
                lastY = e.clientY;
            }, 0);
        });
    }
    function extend(Child, Parent) {
    　　var F = function() {};
        F.prototype = Parent.prototype;
        Child.prototype = new F();
        Child.prototype.constructor = Child;
        Child.uber = Parent.prototype;
    }

    var Alert = function(newContent) {
        return new Dialog({
            mask: true,
            content: '
'+newContent+'
',
            btns: [
                {name: '关闭', callback: function(opts) {
                    this.close();
                }}
            ]
        });
    };

    var root = typeof exports !== "undefined" && exports !== null ? exports : window;
    root.Dialog = Dialog;
    root.Alert = Alert;
}());<|endoftext|>


<commit_before>/**
 * +--------------------------------------------------------------------+
 * | This HTML_CodeSniffer file is Copyright (c)                        |
 * | Squiz Pty Ltd (ABN 77 084 670 600)                                 |
 * +--------------------------------------------------------------------+
 * | IMPORTANT: Your use of this Software is subject to the terms of    |
 * | the Licence provided in the file licence.txt. If you cannot find   |
 * | this file please contact Squiz (www.squiz.com.au) so we may        |
 * | provide you a copy.                                                |
 * +--------------------------------------------------------------------+
 *
 */

_global.HTMLCS_WCAG2AAA_Sniffs_Principle1_Guideline1_3_1_3_5 = {
    /**
   * Determines the elements to register for processing.
   *
   * Each element of the returned array can either be an element name, or "_top"
   * which is the top element of the tested code.
   *
   * @returns {Array} The list of elements.
   */
    register: function() {
        return ["_top", "input", "select", "textarea"];
    },

    /**
   * Checks that the values of the autocomplete attribute are values listed in the HTML 5.2 spec.
   *
   * @param {DOMNode} element The element registered.
   */
    checkValidAttributes: function(element) {
        var valid_attributes = [
            "additional-name",
            "address-level1",
            "address-level2",
            "address-level3",
            "address-level4",
            "address-line1",
            "address-line2",
            "address-line3",
            "bday",
            "bday-year",
            "bday-day",
            "bday-month",
            "billing",
            "cc-additional-name",
            "cc-csc",
            "cc-exp",
            "cc-exp-month",
            "cc-exp-year",
            "cc-family-name",
            "cc-given-name",
            "cc-name",
            "cc-number",
            "cc-type",
            "country",
            "country-name",
            "current-password",
            "email",
            "family-name",
            "fax",
            "given-name",
            "home",
            "honorific-prefix",
            "honorific-suffix",
            "impp",
            "language",
            "mobile",
            "name",
            "new-password",
            "nickname",
            "off",
            "on",
            "organization",
            "organization-title",
            "pager",
            "photo",
            "postal-code",
            "sex",
            "shipping",
            "street-address",
            "tel-area-code",
            "tel",
            "tel-country-code",
            "tel-extension",
            "tel-local",
            "tel-local-prefix",
            "tel-local-suffix",
            "tel-national",
            "transaction-amount",
            "transaction-currency",
            "url",
            "username",
            "work"
        ];

        var valuesStr = element.getAttribute("autocomplete");
        var values = valuesStr.split(" ");

        var hasFaultyValue = false;

        for (var i = 0; i < values.length; i++) {
            values[i] = values[i].trim();
            var x = values[i];
            if (valid_attributes.indexOf(x) === -1 && x.indexOf("section-") !== 0) {
                hasFaultyValue = true;
            }
        }

        if (hasFaultyValue === true) {
            HTMLCS.addMessage(
                HTMLCS.WARNING,
                element,
                _global.HTMLCS.getTranslation("1_3_5_H98.FaultyValue").replace(/\{\{valuesStr\}\}/g, valuesStr),
                "H98"
            );
        }
    },

    /**
   * Checks that the values of the autocomplete attribute match the right control groups.
   *
   * This uses the logic from the followingtable in the HTML 5.2 spec:
   * https://www.w3.org/TR/html52/sec-forms.html#autofilling-form-controls-the-autocomplete-attribute
   *
   * @param {DOMNode} element The element registered.
   */
    checkControlGroups: function(element) {
        var textFields = [
            "name",
            "honorific-prefix",
            "given-name",
            "additional-name",
            "family-name",
            "honorific-suffix",
            "nickname",
            "organization-title",
            "username",
            "organization",
            "address-line1",
            "address-line2",
            "address-line3",
            "address-level4",
            "address-level3",
            "address-level2",
            "address-level1",
            "country",
            "country-name",
            "postal-code",
            "cc-name",
            "cc-given-name",
            "cc-additional-name",
            "cc-family-name",
            "cc-number",
            "cc-csc",
            "cc-type",
            "transaction-currency",
            "language",
            "sex",
            "tel-country-code",
            "tel-national",
            "tel-area-code",
            "tel-local",
            "tel-local-prefix",
            "tel-local-suffix",
            "tel-extension"
        ];
        var isText =
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "hidden") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "text") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "search") ||
      element.tagName === "TEXTAREA" ||
      element.tagName === "SELECT";

        var multilineFields = ["street-address"];
        var isMultiline =
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "hidden") ||
      element.tagName === "TEXTAREA" ||
      element.tagName === "SELECT";

        var passwordFields = ["new-password", "current-password"];
        var isPassword =
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "hidden") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "text") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "search") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "password") ||
      element.tagName === "TEXTAREA" ||
      element.tagName === "SELECT";

        var urlFields = ["url", "photo", "impp"];
        var isUrl =
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "hidden") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "text") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "search") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "email") ||
      element.tagName === "TEXTAREA" ||
      element.tagName === "SELECT";

        var telFields = ["tel"];
        var isTel =
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "hidden") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "text") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "search") ||
      (element.tagName === "INPUT" && element.getAttribute("type") === "tel") ||
      element.tagName === "TEXTAREA" ||
      element.tagName === "SELECT";

        var numericFields = [
            "cc-exp-month",
            "cc-exp-year",
            "transaction-amount",
            "bday-day",
            "bday-month",
            "bday-year"
        ];
        var isNumeric =
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "hidden") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "text") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "search") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "number") ||
      element.tagName === "TEXTAREA" ||
      element.tagName === "SELECT";

        var monthFields = ["cc-exp"];
        var isMonth =
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "hidden") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "text") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "search") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "month") ||
      element.tagName === "TEXTAREA" ||
      element.tagName === "SELECT";

        var dateFields = ["bday"];
        var isDate =
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "hidden") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "text") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "search") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "date") ||
      element.tagName === "TEXTAREA" ||
      element.tagName === "SELECT";

        var values = element.getAttribute("autocomplete").split(" ");

        for (var i = 0; i < values.length; i++) {
            values[i] = values[i].trim();
            var x = values[i];
            if (textFields.indexOf(x) > -1 && !isText) {
                HTMLCS.addMessage(
                    HTMLCS.ERROR,
                    element,
                    _global.HTMLCS.getTranslation("1_3_5_H98.InvalidAutoComplete_Text").replace(/\{\{x\}\}/g, x),
                    "H98"
                );
            }
            if (multilineFields.indexOf(x) > -1 && !isMultiline) {
                HTMLCS.addMessage(
                    HTMLCS.ERROR,
                    element,
                    _global.HTMLCS.getTranslation("1_3_5_H98.InvalidAutoComplete_Multiline").replace(/\{\{x\}\}/g, x),
                    "H98"
                );
            }
            if (passwordFields.indexOf(x) > -1 && !isPassword) {
                HTMLCS.addMessage(
                    HTMLCS.ERROR,
                    element,
                    _global.HTMLCS.getTranslation("1_3_5_H98.InvalidAutoComplete_Password").replace(/\{\{x\}\}/g, x),
                    "H98"
                );
            }
            if (urlFields.indexOf(x) > -1 && !isUrl) {
                HTMLCS.addMessage(
                    HTMLCS.ERROR,
                    element,
                    _global.HTMLCS.getTranslation("1_3_5_H98.InvalidAutoComplete_Url").replace(/\{\{x\}\}/g, x),
                    "H98"
                );
            }
            if (telFields.indexOf(x) > -1 && !isTel) {
                HTMLCS.addMessage(
                    HTMLCS.ERROR,
                    element,
                    _global.HTMLCS.getTranslation("1_3_5_H98.InvalidAutoComplete_Telephone").replace(/\{\{x\}\}/g, x),
                    "H98"
                );
            }
            if (numericFields.indexOf(x) > -1 && !isNumeric) {
                HTMLCS.addMessage(
                    HTMLCS.ERROR,
                    element,
                    _global.HTMLCS.getTranslation("1_3_5_H98.InvalidAutoComplete_Numeric").replace(/\{\{x\}\}/g, x),
                    "H98"
                );
            }
            if (monthFields.indexOf(x) > -1 && !isMonth) {
                HTMLCS.addMessage(
                    HTMLCS.ERROR,
                    element,
                    _global.HTMLCS.getTranslation("1_3_5_H98.InvalidAutoComplete_Month").replace(/\{\{x\}\}/g, x),
                    "H98"
                );
            }
            if (dateFields.indexOf(x) > -1 && !isDate) {
                HTMLCS.addMessage(
                    HTMLCS.ERROR,
                    element,
                    _global.HTMLCS.getTranslation("1_3_5_H98.InvalidAutoComplete_Date").replace(/\{\{x\}\}/g, x),
                    "H98"
                );
            }
        }
    },

    /**
   * Process the registered element.
   *
   * @param {DOMNode} element The element registered.
   * @param {DOMNode} top     The top element of the tested code.
   */
    process: function(element, top) {
        if (element === top) {
            var els = HTMLCS.util.getAllElements(element, '*[autocomplete]');
            for (var i = 0; i < els.length; i++) {
                var x = els[i];
                this.checkValidAttributes(x);
                this.checkControlGroups(x);
            }
        } else {
            HTMLCS.addMessage(
                HTMLCS.NOTICE,
                element,
                _global.HTMLCS.getTranslation("1_3_5_H98.Purpose"),
                "H98"
            );
            // See table in https://www.w3.org/TR/html52/sec-forms.html#the-input-element
            var types_to_skip = [
                "hidden",
                "checkbox",
                "radio",
                "file",
                "submit",
                "image",
                "reset",
                "button"
            ];
            if (types_to_skip.indexOf(element.getAttribute("type")) > -1) {
                // Skip any further validation for input types where the autocomplete attribute does not apply, or where the autocomplete element wears the "autofill anchor mantle".
                return;
            }
            if (element.hasAttribute("autocomplete") === false) {
                HTMLCS.addMessage(
                    HTMLCS.NOTICE,
                    element,
                    _global.HTMLCS.getTranslation("1_3_5_H98.MissingAutocomplete"),
                    "H98"
                );
            }
        }
    }
};
<commit_msg>Update Standards/WCAG2AAA/Sniffs/Principle1/Guideline1_3/1_3_5.js<commit_after>/**
 * +--------------------------------------------------------------------+
 * | This HTML_CodeSniffer file is Copyright (c)                        |
 * | Squiz Pty Ltd (ABN 77 084 670 600)                                 |
 * +--------------------------------------------------------------------+
 * | IMPORTANT: Your use of this Software is subject to the terms of    |
 * | the Licence provided in the file licence.txt. If you cannot find   |
 * | this file please contact Squiz (www.squiz.com.au) so we may        |
 * | provide you a copy.                                                |
 * +--------------------------------------------------------------------+
 *
 */

_global.HTMLCS_WCAG2AAA_Sniffs_Principle1_Guideline1_3_1_3_5 = {
    /**
   * Determines the elements to register for processing.
   *
   * Each element of the returned array can either be an element name, or "_top"
   * which is the top element of the tested code.
   *
   * @returns {Array} The list of elements.
   */
    register: function() {
        return ["_top", "input", "select", "textarea"];
    },

    /**
   * Checks that the values of the autocomplete attribute are values listed in the HTML 5.2 spec.
   *
   * @param {DOMNode} element The element registered.
   */
    checkValidAttributes: function(element) {
        var valid_attributes = [
            "additional-name",
            "address-level1",
            "address-level2",
            "address-level3",
            "address-level4",
            "address-line1",
            "address-line2",
            "address-line3",
            "bday",
            "bday-year",
            "bday-day",
            "bday-month",
            "billing",
            "cc-additional-name",
            "cc-csc",
            "cc-exp",
            "cc-exp-month",
            "cc-exp-year",
            "cc-family-name",
            "cc-given-name",
            "cc-name",
            "cc-number",
            "cc-type",
            "country",
            "country-name",
            "current-password",
            "email",
            "family-name",
            "fax",
            "given-name",
            "home",
            "honorific-prefix",
            "honorific-suffix",
            "impp",
            "language",
            "mobile",
            "name",
            "new-password",
            "nickname",
            "off",
            "on",
            "organization",
            "organization-title",
            "pager",
            "photo",
            "postal-code",
            "sex",
            "shipping",
            "street-address",
            "tel-area-code",
            "tel",
            "tel-country-code",
            "tel-extension",
            "tel-local",
            "tel-local-prefix",
            "tel-local-suffix",
            "tel-national",
            "transaction-amount",
            "transaction-currency",
            "url",
            "username",
            "work"
        ];

        var valuesStr = element.getAttribute("autocomplete");
        if (typeof valuesStr !== 'string') {
            return;
        }
        var values = valuesStr.split(" ");

        var hasFaultyValue = false;

        for (var i = 0; i < values.length; i++) {
            values[i] = values[i].trim();
            var x = values[i];
            if (valid_attributes.indexOf(x) === -1 && x.indexOf("section-") !== 0) {
                hasFaultyValue = true;
            }
        }

        if (hasFaultyValue === true) {
            HTMLCS.addMessage(
                HTMLCS.WARNING,
                element,
                _global.HTMLCS.getTranslation("1_3_5_H98.FaultyValue").replace(/\{\{valuesStr\}\}/g, valuesStr),
                "H98"
            );
        }
    },

    /**
   * Checks that the values of the autocomplete attribute match the right control groups.
   *
   * This uses the logic from the followingtable in the HTML 5.2 spec:
   * https://www.w3.org/TR/html52/sec-forms.html#autofilling-form-controls-the-autocomplete-attribute
   *
   * @param {DOMNode} element The element registered.
   */
    checkControlGroups: function(element) {
        var textFields = [
            "name",
            "honorific-prefix",
            "given-name",
            "additional-name",
            "family-name",
            "honorific-suffix",
            "nickname",
            "organization-title",
            "username",
            "organization",
            "address-line1",
            "address-line2",
            "address-line3",
            "address-level4",
            "address-level3",
            "address-level2",
            "address-level1",
            "country",
            "country-name",
            "postal-code",
            "cc-name",
            "cc-given-name",
            "cc-additional-name",
            "cc-family-name",
            "cc-number",
            "cc-csc",
            "cc-type",
            "transaction-currency",
            "language",
            "sex",
            "tel-country-code",
            "tel-national",
            "tel-area-code",
            "tel-local",
            "tel-local-prefix",
            "tel-local-suffix",
            "tel-extension"
        ];
        var isText =
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "hidden") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "text") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "search") ||
      element.tagName === "TEXTAREA" ||
      element.tagName === "SELECT";

        var multilineFields = ["street-address"];
        var isMultiline =
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "hidden") ||
      element.tagName === "TEXTAREA" ||
      element.tagName === "SELECT";

        var passwordFields = ["new-password", "current-password"];
        var isPassword =
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "hidden") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "text") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "search") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "password") ||
      element.tagName === "TEXTAREA" ||
      element.tagName === "SELECT";

        var urlFields = ["url", "photo", "impp"];
        var isUrl =
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "hidden") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "text") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "search") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "email") ||
      element.tagName === "TEXTAREA" ||
      element.tagName === "SELECT";

        var telFields = ["tel"];
        var isTel =
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "hidden") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "text") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "search") ||
      (element.tagName === "INPUT" && element.getAttribute("type") === "tel") ||
      element.tagName === "TEXTAREA" ||
      element.tagName === "SELECT";

        var numericFields = [
            "cc-exp-month",
            "cc-exp-year",
            "transaction-amount",
            "bday-day",
            "bday-month",
            "bday-year"
        ];
        var isNumeric =
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "hidden") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "text") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "search") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "number") ||
      element.tagName === "TEXTAREA" ||
      element.tagName === "SELECT";

        var monthFields = ["cc-exp"];
        var isMonth =
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "hidden") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "text") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "search") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "month") ||
      element.tagName === "TEXTAREA" ||
      element.tagName === "SELECT";

        var dateFields = ["bday"];
        var isDate =
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "hidden") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "text") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "search") ||
      (element.tagName === "INPUT" &&
        element.getAttribute("type") === "date") ||
      element.tagName === "TEXTAREA" ||
      element.tagName === "SELECT";

        var values = element.getAttribute("autocomplete").split(" ");

        for (var i = 0; i < values.length; i++) {
            values[i] = values[i].trim();
            var x = values[i];
            if (textFields.indexOf(x) > -1 && !isText) {
                HTMLCS.addMessage(
                    HTMLCS.ERROR,
                    element,
                    _global.HTMLCS.getTranslation("1_3_5_H98.InvalidAutoComplete_Text").replace(/\{\{x\}\}/g, x),
                    "H98"
                );
            }
            if (multilineFields.indexOf(x) > -1 && !isMultiline) {
                HTMLCS.addMessage(
                    HTMLCS.ERROR,
                    element,
                    _global.HTMLCS.getTranslation("1_3_5_H98.InvalidAutoComplete_Multiline").replace(/\{\{x\}\}/g, x),
                    "H98"
                );
            }
            if (passwordFields.indexOf(x) > -1 && !isPassword) {
                HTMLCS.addMessage(
                    HTMLCS.ERROR,
                    element,
                    _global.HTMLCS.getTranslation("1_3_5_H98.InvalidAutoComplete_Password").replace(/\{\{x\}\}/g, x),
                    "H98"
                );
            }
            if (urlFields.indexOf(x) > -1 && !isUrl) {
                HTMLCS.addMessage(
                    HTMLCS.ERROR,
                    element,
                    _global.HTMLCS.getTranslation("1_3_5_H98.InvalidAutoComplete_Url").replace(/\{\{x\}\}/g, x),
                    "H98"
                );
            }
            if (telFields.indexOf(x) > -1 && !isTel) {
                HTMLCS.addMessage(
                    HTMLCS.ERROR,
                    element,
                    _global.HTMLCS.getTranslation("1_3_5_H98.InvalidAutoComplete_Telephone").replace(/\{\{x\}\}/g, x),
                    "H98"
                );
            }
            if (numericFields.indexOf(x) > -1 && !isNumeric) {
                HTMLCS.addMessage(
                    HTMLCS.ERROR,
                    element,
                    _global.HTMLCS.getTranslation("1_3_5_H98.InvalidAutoComplete_Numeric").replace(/\{\{x\}\}/g, x),
                    "H98"
                );
            }
            if (monthFields.indexOf(x) > -1 && !isMonth) {
                HTMLCS.addMessage(
                    HTMLCS.ERROR,
                    element,
                    _global.HTMLCS.getTranslation("1_3_5_H98.InvalidAutoComplete_Month").replace(/\{\{x\}\}/g, x),
                    "H98"
                );
            }
            if (dateFields.indexOf(x) > -1 && !isDate) {
                HTMLCS.addMessage(
                    HTMLCS.ERROR,
                    element,
                    _global.HTMLCS.getTranslation("1_3_5_H98.InvalidAutoComplete_Date").replace(/\{\{x\}\}/g, x),
                    "H98"
                );
            }
        }
    },

    /**
   * Process the registered element.
   *
   * @param {DOMNode} element The element registered.
   * @param {DOMNode} top     The top element of the tested code.
   */
    process: function(element, top) {
        if (element === top) {
            var els = HTMLCS.util.getAllElements(element, '*[autocomplete]');
            for (var i = 0; i < els.length; i++) {
                var x = els[i];
                this.checkValidAttributes(x);
                this.checkControlGroups(x);
            }
        } else {
            HTMLCS.addMessage(
                HTMLCS.NOTICE,
                element,
                _global.HTMLCS.getTranslation("1_3_5_H98.Purpose"),
                "H98"
            );
            // See table in https://www.w3.org/TR/html52/sec-forms.html#the-input-element
            var types_to_skip = [
                "hidden",
                "checkbox",
                "radio",
                "file",
                "submit",
                "image",
                "reset",
                "button"
            ];
            if (types_to_skip.indexOf(element.getAttribute("type")) > -1) {
                // Skip any further validation for input types where the autocomplete attribute does not apply, or where the autocomplete element wears the "autofill anchor mantle".
                return;
            }
            if (element.hasAttribute("autocomplete") === false) {
                HTMLCS.addMessage(
                    HTMLCS.NOTICE,
                    element,
                    _global.HTMLCS.getTranslation("1_3_5_H98.MissingAutocomplete"),
                    "H98"
                );
            }
        }
    }
};
<|endoftext|>


<commit_before>'use strict';

var openHours = ['6am', '7am', '8am', '9am', '10am', '11am', '12pm', '1pm', '2pm', '3pm', '4pm', '5pm', '6pm', '7pm'];
var locationNames = ['First and Pike', 'Seatac Airport', 'Seattle Center', 'Capital Hill', 'Alki'];

//
//CONSTRUCTOR FUNCTION
//
function StoreSalesProjections (storeName, minCustHour, maxCustHour, avgCookiesCustomer) {
  //PROPERTIES
  this.storeName = storeName;
  this.minCustHour = minCustHour;
  this.maxCustHour = maxCustHour;
  this.avgCookiesCustomer = avgCookiesCustomer;
  this.randomCustHour = [];
  this.projCookiesPerHour = [];
  this.totalDailyCookies = 0;

//METHOD to calculate random customers per hour
  this.calcRandomCustHour = function() {
    for (var i = 0; i < openHours.length; i++) {
      this.randomCustHour.push(Math.floor(Math.random() * (this.maxCustHour - this.minCustHour + 1) + this.minCustHour));
    }
  };

//METHOD to calculate projected number of cookies each hour AND total daily cookes
  this.calcCookiesPerHour = function() {
    for (var i = 0; i < this.randomCustHour.length; i++) {
      var singleHourCookies = Math.ceil(this.randomCustHour[i] * this.avgCookiesCustomer);
      this.projCookiesPerHour.push(singleHourCookies);
      this.totalDailyCookies += singleHourCookies;
    }
  };

// METHOD TO CREATE SALES TABLE DATA CELLS
  this.createSalesDataCells = function () {
    var salesDataTable = document.getElementById('salesDataTable');

    var salesDataRow = document.createElement('tr');
    var salesDataNameCell = document.createElement('td');
    salesDataNameCell.textContent = this.storeName;
    salesDataRow.appendChild(salesDataNameCell);

    for (var i = 0; i < this.projCookiesPerHour.length; i++) {
      var salesDataCell = document.createElement('td');
      salesDataCell.textContent = this.projCookiesPerHour[i];
      salesDataRow.appendChild(salesDataCell);
    }

    var salesDataTotalCell = document.createElement('td');
    salesDataTotalCell.textContent = this.totalDailyCookies;
    salesDataRow.appendChild(salesDataTotalCell);

    salesDataTable.appendChild(salesDataRow);
  };

// // METHOD TO RENDER CUSTOMERS PER HOUR & COOKIES PER HOUR LISTS TO HTML
//   this.renderLists = function() {
//     var ulOfCookiesAndCustomers = document.getElementById('listCookiesAndCustPerHour');
//     for (var i = 0; i < openHours.length; i++) {
//       var liElCookiesAndCustomers = document.createElement('li');
//       liElCookiesAndCustomers.textContent = openHours[i] + ': ' + this.projCookiesPerHour[i] + ' cookies';
//       ulOfCookiesAndCustomers.appendChild(liElCookiesAndCustomers);
//     }
//     var liElTotalDailyCookies = document.createElement('li');
//     liElTotalDailyCookies.textContent = 'Total: ' + this.totalDailyCookies;
//     ulOfCookiesAndCustomers.appendChild(liElTotalDailyCookies);
//   };

  this.calcRandomCustHour();
  this.calcCookiesPerHour();
  // this.renderLists();
};

var firstPike = new StoreSalesProjections(locationNames[0], 23, 65, 6.3); //eslint-disable-line
var seatacAirport = new StoreSalesProjections(locationNames[1], 3, 24, 1.2); //eslint-disable-line
var seattleCenter = new StoreSalesProjections(locationNames[2], 11, 38, 3.7); //eslint-disable-line
var capitalHill = new StoreSalesProjections(locationNames[3], 20, 38, 2.3); //eslint-disable-line
var alki = new StoreSalesProjections(locationNames[4], 2, 16, 4.6); //eslint-disable-line

makeHeaderRow();
firstPike.createSalesDataCells();
seatacAirport.createSalesDataCells();
seattleCenter.createSalesDataCells();
capitalHill.createSalesDataCells();
alki.createSalesDataCells();

//STAND ALONE FUNCTION FOR HEADER ROW
function makeHeaderRow () {
  var salesDataTable = document.getElementById('salesDataTable');

  var salesDataRow = document.createElement('tr');
  var salesDataEmptyHeader = document.createElement('th');
  salesDataEmptyHeader.textContent = '';
  salesDataRow.appendChild(salesDataEmptyHeader);

  for (var i = 0; i < openHours.length; i++) {
    var salesDataHeader = document.createElement('th');
    salesDataHeader.textContent = openHours[i];
    salesDataRow.appendChild(salesDataHeader);
  }
  salesDataEmptyHeader = document.createElement('th');
  salesDataEmptyHeader.textContent = 'Daily Total';
  salesDataRow.appendChild(salesDataEmptyHeader);

  salesDataTable.appendChild(salesDataRow);
};

// STAND-ALONE FUNCTION FOR FOOTER **STRETCH GOAL, not working on yet**
// function makeFooterRow () {
//   var salesDataTable = document.getElementById('salesDataTable');
//
//   var salesDataRow = document.createElement('tr');
//   var salesDataEmptyHeader = document.createElement('th');
//   salesDataEmptyHeader.textContent = '';
//   salesDataRow.appendChild(salesDataEmptyHeader);
//
//   for (var i = 0; i < openHours.length; i++) {
//     var salesDataHeader = document.createElement('th');
//     salesDataHeader.textContent = openHours[i];
//     salesDataRow.appendChild(salesDataHeader);
//   }
//   var salesDataEmptyHeader = document.createElement('th');
//   salesDataEmptyHeader.textContent = 'Daily Total';
//   salesDataRow.appendChild(salesDataEmptyHeader);
//   salesDataTable.appendChild(salesDataRow);
// };
//
// makeFooterRow();
<commit_msg>Added a footer row<commit_after>'use strict';

var openHours = ['6am', '7am', '8am', '9am', '10am', '11am', '12pm', '1pm', '2pm', '3pm', '4pm', '5pm', '6pm', '7pm'];
var locationNames = ['First and Pike', 'Seatac Airport', 'Seattle Center', 'Capital Hill', 'Alki'];

//
//CONSTRUCTOR FUNCTION
//
function StoreSalesProjections (storeName, minCustHour, maxCustHour, avgCookiesCustomer) {
  //PROPERTIES
  this.storeName = storeName;
  this.minCustHour = minCustHour;
  this.maxCustHour = maxCustHour;
  this.avgCookiesCustomer = avgCookiesCustomer;
  this.randomCustHour = [];
  this.projCookiesPerHour = [];
  this.totalDailyCookies = 0;
  this.totalHourlyCookies = 0;

//METHOD to calculate random customers per hour
  this.calcRandomCustHour = function() {
    for (var i = 0; i < openHours.length; i++) {
      this.randomCustHour.push(Math.floor(Math.random() * (this.maxCustHour - this.minCustHour + 1) + this.minCustHour));
    }
  };

//METHOD to calculate projected number of cookies each hour AND total daily cookes
  this.calcCookiesPerHour = function() {
    for (var i = 0; i < this.randomCustHour.length; i++) {
      var singleHourCookies = Math.ceil(this.randomCustHour[i] * this.avgCookiesCustomer);
      this.projCookiesPerHour.push(singleHourCookies);
      this.totalDailyCookies += singleHourCookies;
    }
  };

// METHOD TO CREATE SALES TABLE DATA CELLS
  this.createSalesDataCells = function () {
    var salesDataTable = document.getElementById('salesDataTable');

    var salesDataRow = document.createElement('tr');
    var salesDataNameCell = document.createElement('td');
    salesDataNameCell.textContent = this.storeName;
    salesDataRow.appendChild(salesDataNameCell);

    for (var i = 0; i < this.projCookiesPerHour.length; i++) {
      var salesDataCell = document.createElement('td');
      salesDataCell.textContent = this.projCookiesPerHour[i];
      salesDataRow.appendChild(salesDataCell);
    }

    var salesDataTotalCell = document.createElement('td');
    salesDataTotalCell.textContent = this.totalDailyCookies;
    salesDataRow.appendChild(salesDataTotalCell);

    salesDataTable.appendChild(salesDataRow);
  };

// // METHOD TO RENDER CUSTOMERS PER HOUR & COOKIES PER HOUR LISTS TO HTML
//   this.renderLists = function() {
//     var ulOfCookiesAndCustomers = document.getElementById('listCookiesAndCustPerHour');
//     for (var i = 0; i < openHours.length; i++) {
//       var liElCookiesAndCustomers = document.createElement('li');
//       liElCookiesAndCustomers.textContent = openHours[i] + ': ' + this.projCookiesPerHour[i] + ' cookies';
//       ulOfCookiesAndCustomers.appendChild(liElCookiesAndCustomers);
//     }
//     var liElTotalDailyCookies = document.createElement('li');
//     liElTotalDailyCookies.textContent = 'Total: ' + this.totalDailyCookies;
//     ulOfCookiesAndCustomers.appendChild(liElTotalDailyCookies);
//   };

  this.calcRandomCustHour();
  this.calcCookiesPerHour();
  // this.renderLists();
};

var firstPike = new StoreSalesProjections(locationNames[0], 23, 65, 6.3); //eslint-disable-line
var seatacAirport = new StoreSalesProjections(locationNames[1], 3, 24, 1.2); //eslint-disable-line
var seattleCenter = new StoreSalesProjections(locationNames[2], 11, 38, 3.7); //eslint-disable-line
var capitalHill = new StoreSalesProjections(locationNames[3], 20, 38, 2.3); //eslint-disable-line
var alki = new StoreSalesProjections(locationNames[4], 2, 16, 4.6); //eslint-disable-line

makeHeaderRow();
firstPike.createSalesDataCells();
seatacAirport.createSalesDataCells();
seattleCenter.createSalesDataCells();
capitalHill.createSalesDataCells();
alki.createSalesDataCells();
makeFooterRow();

//STAND ALONE FUNCTION FOR HEADER ROW
function makeHeaderRow () {
  var salesDataTable = document.getElementById('salesDataTable');

  var salesDataRow = document.createElement('tr');
  var salesDataEmptyHeader = document.createElement('th');
  salesDataEmptyHeader.textContent = '';
  salesDataRow.appendChild(salesDataEmptyHeader);

  for (var i = 0; i < openHours.length; i++) {
    var salesDataHeader = document.createElement('th');
    salesDataHeader.textContent = openHours[i];
    salesDataRow.appendChild(salesDataHeader);
  }
  salesDataEmptyHeader = document.createElement('th');
  salesDataEmptyHeader.textContent = 'Daily Total';
  salesDataRow.appendChild(salesDataEmptyHeader);

  salesDataTable.appendChild(salesDataRow);
};

// STAND-ALONE FUNCTION FOR FOOTER **STRETCH GOAL, not working on yet**
function makeFooterRow () {
  var salesDataTable = document.getElementById('salesDataTable');

  var salesFooterRow = document.createElement('tr');
  var salesDataEmptyFooter = document.createElement('td');
  salesDataEmptyFooter.textContent = '';
  salesFooterRow.appendChild(salesDataEmptyFooter);

  for (var i = 0; i < openHours.length; i++) {
    var salesDataFooter = document.createElement('td');
    salesDataFooter.textContent = 'total';
    salesFooterRow.appendChild(salesDataFooter);
  }
  var salesDataFooterTotal = document.createElement('td');
  salesDataFooterTotal.textContent = 'TOTAL';
  salesFooterRow.appendChild(salesDataFooterTotal);
  salesDataTable.appendChild(salesFooterRow);
};
<|endoftext|>


<commit_before>'use strict';

const LocalCallStore = require('../src/LocalCallStore.js');

describe('Local call store', ()=> {
    const storageKey = 'testStorageKey';

    let scenario;

    afterEach(()=> {
        scenario.tearDown();
    });

    describe('Retrieving calls', ()=> {

        describe('From empty original data', ()=> {
            beforeEach(()=> {
                scenario = new Scenario();
            });

            it('getCalls returns an empty set', ()=> {
                const calls = scenario.store.getCalls();
                expect(calls).toEqual([]);
            });
        });

        describe('From non-empty original data', ()=> {
            const originalData = [
                ['The', 'first', 'call`s', 'arguments'],
                ['The', 'second', 'call`s'],
                ['And', 'the', 'third`s']
            ];
            let calls;

            beforeEach(()=> {
                scenario = new Scenario(originalData);
                calls = scenario.store.getCalls();
            });

            it('getCalls returns an object per stored call', ()=> {
                expect(calls.length).toEqual(originalData.length);
            });

            it('each object comes with the parameters for each stored call', ()=> {
                const parametersSet = calls.map(call => call.callArguments);
                expect(parametersSet).toEqual(originalData);
            });

            it('each object comes with a null `this` binding', ()=> {
                const bindingsSet = calls.map(call => call.thisBinding);
                expect(bindingsSet).toEqual([null, null, null]);
            });

            it('each object comes with an `onExecuted` callback', ()=> {
                const callbacks = calls.map(call => call.onExecuted);
                callbacks.forEach(callback => {
                    expect(callback).toEqual(jasmine.any(Function));
                });
            });

            it('if I call the `onExecuted` callback from a stored call, it deletes it', ()=> {
                calls[1].onExecuted();
                const callData = scenario.getCallData();
                expect(callData.length).toEqual(originalData.length - 1);
                expect(callData[0]).toEqual(originalData[0]);
                expect(callData[1]).toEqual(originalData[2]);
            });

            it('after deleting the stored call, getCalls does not return it', ()=> {
                calls[1].onExecuted();
                expect(scenario.store.getCalls().length).toBe(2);
            });
        });

        describe('From calls recorded subsequently', ()=> {
            const newData = [
                ['arg', 'set', 1],
                ['arg', 'set', 2],
                ['arg', 'set', 3]
            ];
            let calls;

            beforeEach(()=> {
                scenario = new Scenario();
                newData.forEach(newDatum => scenario.store.record(newDatum));
                calls = scenario.store.getCalls();
            });

            it('getCalls returns an object per stored call', ()=> {
                expect(calls.length).toEqual(newData.length);
            });

            it('each object comes with the parameters for each stored call', ()=> {
                const parametersSet = calls.map(call => call.callArguments);
                expect(parametersSet).toEqual(newData);
            });

            it('each object comes with a null `this` binding', ()=> {
                const bindingsSet = calls.map(call => call.thisBinding);
                expect(bindingsSet).toEqual([null, null, null]);
            });

            it('each object comes with an `onExecuted` callback', ()=> {
                const callbacks = calls.map(call => call.onExecuted);
                callbacks.forEach(callback => {
                    expect(callback).toEqual(jasmine.any(Function));
                });
            });

            it('if I call the `onExecuted` callback from a stored call, it deletes it', ()=> {
                calls[1].onExecuted();
                const callData = scenario.getCallData();
                expect(callData.length).toEqual(newData.length - 1);
                expect(callData[0]).toEqual(newData[0]);
                expect(callData[1]).toEqual(newData[2]);
            });

            it('after deleting the stored call, getCalls does not return it', ()=> {
                calls[1].onExecuted();
                expect(scenario.store.getCalls().length).toBe(2);
            });
        });

        function Scenario(startingData) {
            if (startingData) {
                localStorage.setItem(storageKey, JSON.stringify(startingData));
            }

            this.store = new LocalCallStore(storageKey);

            this.getCallData = function () {
                return JSON.parse(localStorage.getItem(storageKey));
            };

            this.tearDown = function () {
                localStorage.removeItem(storageKey);
            }
        }
    });

    describe('Handling storage errors', ()=> {
        let store, mockStorage;

        beforeEach(()=> {
            LocalCallStore._storage = new BreakableStorage();
            store = new LocalCallStore('mock storage key');
            mockStorage = LocalCallStore._storage;
        });

        afterEach(()=> {
            LocalCallStore._storage = window.localStorage;
        });

        it('Does not swallow errors', ()=> {
            mockStorage.shouldThrow = true;

            const faultyRecording = function() {
                store.record([1, 2, 3]);
            };

            expect(faultyRecording).toThrow(mockStorage.storageException);
        });

        it('Calling getCalls() after a storage error returns only the successfully stored calls', ()=> {
            const firstParams = ['alpha', 'beta', 'gamma'];
            const secondParams = [2, 4, 8];
            const thirdParams = ['Agamemnon', 'Menelaus', 'Clytemnestra'];

            // Store succeeds
            mockStorage.shouldThrow = false;
            store.record(firstParams);

            // Store fails (e.g. storage full)
            try {
                mockStorage.shouldThrow = true;
                store.record(secondParams);
            } catch (e) {}

            // Store succeeds again (e.g. data just fits)
            mockStorage.shouldThrow = false;
            store.record(thirdParams);

            const storedCalls = store.getCalls();
            const storedCallArgs = storedCalls.map(storedCall => storedCall.callArguments);
            expect(storedCallArgs).toEqual([firstParams, thirdParams]);
        });

        function BreakableStorage() {
            this.shouldThrow = false;
            this.storageException = new Error('Mock storage exception');

            this.getItem = (key) => {
                return window.localStorage.getItem(key);
            };
            this.setItem = (key, value)=> {
                if (this.shouldThrow) {
                    throw this.storageException;
                } else {
                    window.localStorage.setItem(key, value);
                }
            };
        }
    })

});
<commit_msg>Test razing of localStorage data<commit_after>'use strict';

const LocalCallStore = require('../src/LocalCallStore.js');

describe('Local call store', ()=> {
    const storageKey = 'testStorageKey';

    let scenario;

    afterEach(()=> {
        scenario.tearDown();
    });

    describe('Retrieving calls', ()=> {

        describe('From empty original data', ()=> {
            beforeEach(()=> {
                scenario = new Scenario();
            });

            it('getCalls returns an empty set', ()=> {
                const calls = scenario.store.getCalls();
                expect(calls).toEqual([]);
            });
        });

        describe('From non-empty original data', ()=> {
            const originalData = [
                ['The', 'first', 'call`s', 'arguments'],
                ['The', 'second', 'call`s'],
                ['And', 'the', 'third`s']
            ];
            let calls;

            beforeEach(()=> {
                scenario = new Scenario(originalData);
                calls = scenario.store.getCalls();
            });

            it('getCalls returns an object per stored call', ()=> {
                expect(calls.length).toEqual(originalData.length);
            });

            it('each object comes with the parameters for each stored call', ()=> {
                const parametersSet = calls.map(call => call.callArguments);
                expect(parametersSet).toEqual(originalData);
            });

            it('each object comes with a null `this` binding', ()=> {
                const bindingsSet = calls.map(call => call.thisBinding);
                expect(bindingsSet).toEqual([null, null, null]);
            });

            it('each object comes with an `onExecuted` callback', ()=> {
                const callbacks = calls.map(call => call.onExecuted);
                callbacks.forEach(callback => {
                    expect(callback).toEqual(jasmine.any(Function));
                });
            });

            it('if I call the `onExecuted` callback from a stored call, it deletes it', ()=> {
                calls[1].onExecuted();
                const callData = scenario.getCallData();
                expect(callData.length).toEqual(originalData.length - 1);
                expect(callData[0]).toEqual(originalData[0]);
                expect(callData[1]).toEqual(originalData[2]);
            });

            it('after deleting the stored call, getCalls does not return it', ()=> {
                calls[1].onExecuted();
                expect(scenario.store.getCalls().length).toBe(2);
            });
        });

        describe('From calls recorded subsequently', ()=> {
            const newData = [
                ['arg', 'set', 1],
                ['arg', 'set', 2],
                ['arg', 'set', 3]
            ];
            let calls;

            beforeEach(()=> {
                scenario = new Scenario();
                newData.forEach(newDatum => scenario.store.record(newDatum));
                calls = scenario.store.getCalls();
            });

            it('getCalls returns an object per stored call', ()=> {
                expect(calls.length).toEqual(newData.length);
            });

            it('each object comes with the parameters for each stored call', ()=> {
                const parametersSet = calls.map(call => call.callArguments);
                expect(parametersSet).toEqual(newData);
            });

            it('each object comes with a null `this` binding', ()=> {
                const bindingsSet = calls.map(call => call.thisBinding);
                expect(bindingsSet).toEqual([null, null, null]);
            });

            it('each object comes with an `onExecuted` callback', ()=> {
                const callbacks = calls.map(call => call.onExecuted);
                callbacks.forEach(callback => {
                    expect(callback).toEqual(jasmine.any(Function));
                });
            });

            it('if I call the `onExecuted` callback from a stored call, it deletes it', ()=> {
                calls[1].onExecuted();
                const callData = scenario.getCallData();
                expect(callData.length).toEqual(newData.length - 1);
                expect(callData[0]).toEqual(newData[0]);
                expect(callData[1]).toEqual(newData[2]);
            });

            it('after deleting the stored call, getCalls does not return it', ()=> {
                calls[1].onExecuted();
                expect(scenario.store.getCalls().length).toBe(2);
            });

            it('if I delete everything, all the localStorage data is razed', ()=> {
                calls.forEach(call => call.onExecuted());
                expect(scenario.getCallData()).toBeNull();
            })
        });

        function Scenario(startingData) {
            if (startingData) {
                localStorage.setItem(storageKey, JSON.stringify(startingData));
            }

            this.store = new LocalCallStore(storageKey);

            this.getCallData = function () {
                return JSON.parse(localStorage.getItem(storageKey));
            };

            this.tearDown = function () {
                localStorage.removeItem(storageKey);
            }
        }
    });

    describe('Handling storage errors', ()=> {
        let store, mockStorage;

        beforeEach(()=> {
            LocalCallStore._storage = new BreakableStorage();
            store = new LocalCallStore('mock storage key');
            mockStorage = LocalCallStore._storage;
        });

        afterEach(()=> {
            LocalCallStore._storage = window.localStorage;
        });

        it('Does not swallow errors', ()=> {
            mockStorage.shouldThrow = true;

            const faultyRecording = function() {
                store.record([1, 2, 3]);
            };

            expect(faultyRecording).toThrow(mockStorage.storageException);
        });

        it('Calling getCalls() after a storage error returns only the successfully stored calls', ()=> {
            const firstParams = ['alpha', 'beta', 'gamma'];
            const secondParams = [2, 4, 8];
            const thirdParams = ['Agamemnon', 'Menelaus', 'Clytemnestra'];

            // Store succeeds
            mockStorage.shouldThrow = false;
            store.record(firstParams);

            // Store fails (e.g. storage full)
            try {
                mockStorage.shouldThrow = true;
                store.record(secondParams);
            } catch (e) {}

            // Store succeeds again (e.g. data just fits)
            mockStorage.shouldThrow = false;
            store.record(thirdParams);

            const storedCalls = store.getCalls();
            const storedCallArgs = storedCalls.map(storedCall => storedCall.callArguments);
            expect(storedCallArgs).toEqual([firstParams, thirdParams]);
        });

        function BreakableStorage() {
            this.shouldThrow = false;
            this.storageException = new Error('Mock storage exception');

            this.getItem = (key) => {
                return window.localStorage.getItem(key);
            };
            this.setItem = (key, value)=> {
                if (this.shouldThrow) {
                    throw this.storageException;
                } else {
                    window.localStorage.setItem(key, value);
                }
            };
        }
    })

});
<|endoftext|>


<commit_before>// @flow
import React from 'react'
import SelectField from 'material-ui/SelectField'
import MenuItem from 'material-ui/MenuItem'
import { handleSelectFieldChange, pureComponent } from 'utils'

const PostFormFlow = ({ flowId, sourceFlows, input, error }: Props) => (
  <SelectField
    id="PostFormFlow"
    floatingLabelText="Поток"
    hintText="Выберите поток"
    value={flowId}
    errorText={error}
    onChange={handleSelectFieldChange('flow', input, sourceFlows, !!error)}
  >
    {sourceFlows.map(({ id, name }) =>
      <MenuItem key={id} value={id} primaryText={name} />
    )}
  </SelectField>
)

// PostFormFlow.propTypes = {
//   flowId: PropTypes.string,
//   sourceFlows: PropTypes.arrayOf(PropTypes.shape({
//
//   })),
//   input: PropTypes.func,
//   error: PropTypes.string,
// }

type Props = {
  flowId: string,
  sourceFlows: Array<{
    id: string,
    name: string,
  }>,
  input: Function,
  error?: string,
}

export default pureComponent(PostFormFlow)
<commit_msg>Исправиль flowId?<commit_after>// @flow
import React from 'react'
import SelectField from 'material-ui/SelectField'
import MenuItem from 'material-ui/MenuItem'
import { handleSelectFieldChange, pureComponent } from 'utils'

const PostFormFlow = ({ flowId, sourceFlows, input, error }: Props) => (
  <SelectField
    id="PostFormFlow"
    floatingLabelText="Поток"
    hintText="Выберите поток"
    value={flowId}
    errorText={error}
    onChange={handleSelectFieldChange('flow', input, sourceFlows, !!error)}
  >
    {sourceFlows.map(({ id, name }) =>
      <MenuItem key={id} value={id} primaryText={name} />
    )}
  </SelectField>
)

// PostFormFlow.propTypes = {
//   flowId: PropTypes.string,
//   sourceFlows: PropTypes.arrayOf(PropTypes.shape({
//
//   })),
//   input: PropTypes.func,
//   error: PropTypes.string,
// }

type Props = {
  flowId?: string,
  sourceFlows: Array<{
    id: string,
    name: string,
  }>,
  input: Function,
  error?: string,
}

export default pureComponent(PostFormFlow)
<|endoftext|>


<commit_before>Paloquiz.states.Finish = function(game) {
    this.score = 0;
    this.hiscoresButton;
    this.hiscoresLabel;
    this.exitButton;
    this.exitLabel;
    this.fbButton;
}

Paloquiz.states.Finish.prototype = {

    create: function() {
        getJSON('/status', function(gameStatus) {
            if (gameStatus['status'] !== 'finish') {
                this.status.start('Router');
                return;
            }
            getJSON('/summary', function(summary) {
                this.score = summary['score'];
                this.answers = summary['answers'];
                this.numQuestions = summary['num_questions'];
                this.createScoreText();
                this.createUI();
                fbInit(function() {
                    this.fbButton.visible = false;
                    fbGetUserScore(function(currentScore) {
                        if (this.score > currentScore) {
                            fbPublishScore(currentScore);
                        }
                    }, this);
                }, this);
            }, this);
        }, this);
    },

    shutdown: function() {},

    createScoreText: function() {;
        var scoreBaseTextSize = Math.round(.06 * this.world.height);
        var scoreTextColor = '#ff4f1a';

        var scoreHeader = this.add.text(
            this.world.centerX,
            this.world.height * .2,
            'Puntuación', {
                font: 'Pixel Art',
                fontSize: scoreBaseTextSize + 'px',
                fill: scoreTextColor,
                align: 'center'
            });
        scoreHeader.anchor.setTo(.5, 0);

        var scoreText = this.add.text(
            this.world.centerX,
            this.world.height * .33,
            this.score, {
                font: 'Pixel Art',
                fontSize: Math.round(scoreBaseTextSize * 2.2) + 'px',
                fill: scoreTextColor,
                align: 'center'
            });
        scoreText.anchor.setTo(.5, 0);
    },

    createUI: function() {
        // Hiscores
        this.hiscoresButton =
            this.add.button(this.world.centerX, this.world.height * .55,
                'finishButton',
                function() {
                    fbLogIn(function() {
                        this.state.start('Highscores');
                    }, this);
                }, this, 2, 1, 0);
        this.hiscoresButton.smoothed = false;
        this.hiscoresButton.width = this.world.width * .6;
        this.hiscoresButton.height = this.hiscoresButton.width * .3;
        this.hiscoresButton.anchor.setTo(.5, 0);

        // Exit
        this.exitButton =
            this.add.button(this.hiscoresButton.x,
                this.hiscoresButton.y + this.hiscoresButton.height * 1.1,
                'finishButton',
                function() {
                    getJSON('/finish', function() {
                        this.state.start('Router');
                    }, this);
                }, this, 2, 1, 0);
        this.exitButton.smoothed = false;
        this.exitButton.width = this.hiscoresButton.width;
        this.exitButton.height = this.hiscoresButton.height;
        this.exitButton.anchor.setTo(.5, 0);

        // Labels
        var labelTextSize = Math.round(this.hiscoresButton.height / 3.0);
        var labelStyle = this.positionTextStyle = {
            font: 'Pixel Art',
            fontSize: labelTextSize + 'px',
            fill: 'white',
            align: 'center'
        };

        this.hiscoresLabel = this.add.text(
            this.world.centerX,
            this.hiscoresButton.y + this.hiscoresButton.height / 2,
            'Clasificación', labelStyle);
        this.hiscoresLabel.anchor.setTo(.5, .5);

        this.exitLabel = this.add.text(
            this.world.centerX,
            this.exitButton.y + this.exitButton.height / 2,
            'Salir', labelStyle);
        this.exitLabel.anchor.setTo(.5, .5);

        this.createFbUI();
    },

    createFbUI: function() {
        var buttonSize = .1 * Math.min(this.world.width, this.world.height);

        this.fbButton = this.add.button(0, 0, 'fbButton', function() {
            fbLogIn(function() {
                getJSON('/status', function(gameStatus) {
                    this.score = gameStatus['score'];
                    fbGetUserScore(function(currentScore) {
                        if (this.score > currentScore) {
                            fbPublishScore(currentScore, function() {
                                    this.fbButton.visible = false;
                                }, this);
                        }
                    }, this);
                }, this);
            }, this);
        }, this, 1, 0, 1);
        this.fbButton.smoothed = false;
        this.fbButton.height = buttonSize;
        this.fbButton.width = buttonSize;
        this.fbButton.anchor.setTo(0, 0);
        this.fbButton.x = this.fbButton.width * .2;
        this.fbButton.y = this.fbButton.height * .2;
        this.fbButton.visible = true;
    }
}<commit_msg>Same error of publishing wrong score<commit_after>Paloquiz.states.Finish = function(game) {
    this.score = 0;
    this.hiscoresButton;
    this.hiscoresLabel;
    this.exitButton;
    this.exitLabel;
    this.fbButton;
}

Paloquiz.states.Finish.prototype = {

    create: function() {
        getJSON('/status', function(gameStatus) {
            if (gameStatus['status'] !== 'finish') {
                this.status.start('Router');
                return;
            }
            getJSON('/summary', function(summary) {
                this.score = summary['score'];
                this.answers = summary['answers'];
                this.numQuestions = summary['num_questions'];
                this.createScoreText();
                this.createUI();
                fbInit(function() {
                    this.fbButton.visible = false;
                    fbGetUserScore(function(currentScore) {
                        if (this.score > currentScore) {
                            fbPublishScore(this.score);
                        }
                    }, this);
                }, this);
            }, this);
        }, this);
    },

    shutdown: function() {},

    createScoreText: function() {;
        var scoreBaseTextSize = Math.round(.06 * this.world.height);
        var scoreTextColor = '#ff4f1a';

        var scoreHeader = this.add.text(
            this.world.centerX,
            this.world.height * .2,
            'Puntuación', {
                font: 'Pixel Art',
                fontSize: scoreBaseTextSize + 'px',
                fill: scoreTextColor,
                align: 'center'
            });
        scoreHeader.anchor.setTo(.5, 0);

        var scoreText = this.add.text(
            this.world.centerX,
            this.world.height * .33,
            this.score, {
                font: 'Pixel Art',
                fontSize: Math.round(scoreBaseTextSize * 2.2) + 'px',
                fill: scoreTextColor,
                align: 'center'
            });
        scoreText.anchor.setTo(.5, 0);
    },

    createUI: function() {
        // Hiscores
        this.hiscoresButton =
            this.add.button(this.world.centerX, this.world.height * .55,
                'finishButton',
                function() {
                    fbLogIn(function() {
                        this.state.start('Highscores');
                    }, this);
                }, this, 2, 1, 0);
        this.hiscoresButton.smoothed = false;
        this.hiscoresButton.width = this.world.width * .6;
        this.hiscoresButton.height = this.hiscoresButton.width * .3;
        this.hiscoresButton.anchor.setTo(.5, 0);

        // Exit
        this.exitButton =
            this.add.button(this.hiscoresButton.x,
                this.hiscoresButton.y + this.hiscoresButton.height * 1.1,
                'finishButton',
                function() {
                    getJSON('/finish', function() {
                        this.state.start('Router');
                    }, this);
                }, this, 2, 1, 0);
        this.exitButton.smoothed = false;
        this.exitButton.width = this.hiscoresButton.width;
        this.exitButton.height = this.hiscoresButton.height;
        this.exitButton.anchor.setTo(.5, 0);

        // Labels
        var labelTextSize = Math.round(this.hiscoresButton.height / 3.0);
        var labelStyle = this.positionTextStyle = {
            font: 'Pixel Art',
            fontSize: labelTextSize + 'px',
            fill: 'white',
            align: 'center'
        };

        this.hiscoresLabel = this.add.text(
            this.world.centerX,
            this.hiscoresButton.y + this.hiscoresButton.height / 2,
            'Clasificación', labelStyle);
        this.hiscoresLabel.anchor.setTo(.5, .5);

        this.exitLabel = this.add.text(
            this.world.centerX,
            this.exitButton.y + this.exitButton.height / 2,
            'Salir', labelStyle);
        this.exitLabel.anchor.setTo(.5, .5);

        this.createFbUI();
    },

    createFbUI: function() {
        var buttonSize = .1 * Math.min(this.world.width, this.world.height);

        this.fbButton = this.add.button(0, 0, 'fbButton', function() {
            fbLogIn(function() {
                getJSON('/status', function(gameStatus) {
                    this.score = gameStatus['score'];
                    fbGetUserScore(function(currentScore) {
                        if (this.score > currentScore) {
                            fbPublishScore(currentScore, function() {
                                    this.fbButton.visible = false;
                                }, this);
                        }
                    }, this);
                }, this);
            }, this);
        }, this, 1, 0, 1);
        this.fbButton.smoothed = false;
        this.fbButton.height = buttonSize;
        this.fbButton.width = buttonSize;
        this.fbButton.anchor.setTo(0, 0);
        this.fbButton.x = this.fbButton.width * .2;
        this.fbButton.y = this.fbButton.height * .2;
        this.fbButton.visible = true;
    }
}<|endoftext|>


<commit_before>const API = require('./api');
const FluentDOM = require('@fluent/dom');
const Fluent = require('@fluent/bundle');

const availableLanguages = {
  'en-US': ['/fluent/en-US/main.ftl'],
  en: ['/fluent/en-US/main.ftl'],
  it: ['/fluent/it/main.ftl'],
};

let language;
let englishBundle;
let bundle;

async function load() {
  const response = await API.getLanguage();

  language = response.current || navigator.language;
  if (!availableLanguages.hasOwnProperty(language)) {
    const primary = language.split('-')[0];
    if (availableLanguages.hasOwnProperty(primary)) {
      language = primary;
    } else {
      language = 'en-US';
    }
  }

  if (language !== response.current) {
    // don't bother waiting for this, it's not super important
    API.setLanguage(language);
  }

  const links = availableLanguages[language];
  bundle = new Fluent.FluentBundle(language);
  for (const link of links) {
    try {
      const res = await fetch(link);
      const text = await res.text();
      bundle.addResource(new Fluent.FluentResource(text));
    } catch (e) {
      console.warn('Unable to download language pack:', e);
    }
  }

  if (language === 'en-US') {
    englishBundle = bundle;
  } else {
    const englishLinks = availableLanguages['en-US'];
    englishBundle = new Fluent.FluentBundle('en-US');
    for (const link of englishLinks) {
      try {
        const res = await fetch(link);
        const text = await res.text();
        englishBundle.addResource(new Fluent.FluentResource(text));
      } catch (e) {
        console.warn('Unable to download English language pack:', e);
      }
    }
  }
}

async function *generateBundles(_resourceIds) {
  if (!bundle) {
    await load();
  }
  yield *[bundle, englishBundle];
}

const l10n = new FluentDOM.DOMLocalization([], generateBundles);

function init() {
  l10n.connectRoot(document.documentElement);
  l10n.translateRoots();
}

/**
 * @return {string|null} Translation of unit or null if not contained in bundle
 */
function getMessageStrict(id, vars = {}) {
  const obj = bundle.getMessage(id, vars);
  if (!obj) {
    console.warn('Missing id', id);
    return null;
  }
  return bundle.formatPattern(obj.value, vars);
}

/**
 * @return {string|null} Translation of unit or null if not contained in bundle
 */
function getEnglishMessageStrict(id, vars = {}) {
  const obj = englishBundle.getMessage(id, vars);
  if (!obj) {
    console.warn('Missing id', id);
    return null;
  }
  return englishBundle.formatPattern(obj.value, vars);
}

/**
 * @return {string} Translation of unit or unit's id for debugging purposes
 */
function getMessage(id, vars = {}) {
  let msg = getMessageStrict(id, vars);
  if (msg) {
    return msg;
  }

  msg = getEnglishMessageStrict(id, vars);
  if (msg) {
    return msg;
  }

  return `<${id}>`;
}

/**
 * @return {string} The user's chosen language.
 */
function getLanguage() {
  return language;
}

module.exports = {
  load,
  l10n,
  init,
  getMessage,
  getMessageStrict,
  getLanguage,
};
<commit_msg>Fix fluent setup during wi-fi/router setup. (#2233)<commit_after>const API = require('./api');
const FluentDOM = require('@fluent/dom');
const Fluent = require('@fluent/bundle');

const availableLanguages = {
  'en-US': ['/fluent/en-US/main.ftl'],
  en: ['/fluent/en-US/main.ftl'],
  it: ['/fluent/it/main.ftl'],
};

let language;
let englishBundle;
let bundle;

async function load() {
  let response = {};
  try {
    response = await API.getLanguage();
  } catch (_) {
    // keep going
  }

  language = response.current || navigator.language;
  if (!availableLanguages.hasOwnProperty(language)) {
    const primary = language.split('-')[0];
    if (availableLanguages.hasOwnProperty(primary)) {
      language = primary;
    } else {
      language = 'en-US';
    }
  }

  if (language !== response.current) {
    // don't bother waiting for this, it's not super important
    API.setLanguage(language).catch(() => {});
  }

  const links = availableLanguages[language];
  bundle = new Fluent.FluentBundle(language);
  for (const link of links) {
    try {
      const res = await fetch(link);
      const text = await res.text();
      bundle.addResource(new Fluent.FluentResource(text));
    } catch (e) {
      console.warn('Unable to download language pack:', e);
    }
  }

  if (language === 'en-US') {
    englishBundle = bundle;
  } else {
    const englishLinks = availableLanguages['en-US'];
    englishBundle = new Fluent.FluentBundle('en-US');
    for (const link of englishLinks) {
      try {
        const res = await fetch(link);
        const text = await res.text();
        englishBundle.addResource(new Fluent.FluentResource(text));
      } catch (e) {
        console.warn('Unable to download English language pack:', e);
      }
    }
  }
}

async function *generateBundles(_resourceIds) {
  if (!bundle) {
    await load();
  }
  yield *[bundle, englishBundle];
}

const l10n = new FluentDOM.DOMLocalization([], generateBundles);

function init() {
  l10n.connectRoot(document.documentElement);
  l10n.translateRoots();
}

/**
 * @return {string|null} Translation of unit or null if not contained in bundle
 */
function getMessageStrict(id, vars = {}) {
  const obj = bundle.getMessage(id, vars);
  if (!obj) {
    console.warn('Missing id', id);
    return null;
  }
  return bundle.formatPattern(obj.value, vars);
}

/**
 * @return {string|null} Translation of unit or null if not contained in bundle
 */
function getEnglishMessageStrict(id, vars = {}) {
  const obj = englishBundle.getMessage(id, vars);
  if (!obj) {
    console.warn('Missing id', id);
    return null;
  }
  return englishBundle.formatPattern(obj.value, vars);
}

/**
 * @return {string} Translation of unit or unit's id for debugging purposes
 */
function getMessage(id, vars = {}) {
  let msg = getMessageStrict(id, vars);
  if (msg) {
    return msg;
  }

  msg = getEnglishMessageStrict(id, vars);
  if (msg) {
    return msg;
  }

  return `<${id}>`;
}

/**
 * @return {string} The user's chosen language.
 */
function getLanguage() {
  return language;
}

module.exports = {
  load,
  l10n,
  init,
  getMessage,
  getMessageStrict,
  getLanguage,
};
<|endoftext|>


<commit_before>require('../helper');

var appServer = require('../../app/server');
var assert = require('../support/assert');
var qs = require('querystring');
var log4js = require('log4js');

describe('Logging SQL query on POST requests', function() {

    var SQL_QUERY = "SELECT 'wadus'";
    var API_KEY = 1234;
    var BODY_PAYLOAD = {
        q: SQL_QUERY,
        api_key: API_KEY
    };

    var RESPONSE_OK = {
        statusCode: 200
    };

    var server;
    before(function() {
        global.settings.log_format = ':method :req[Host]:url :status :sql';
        global.log4js = log4js;
        global.log4js.configure({
            appenders: [
                {
                    type: "console",
                    layout: {
                        type:'basic'
                    }
                }
            ]
        });
        server = appServer();
    });

    after(function() {
        global.log4js = null;
        delete global.log4js;
    });

    function createPostRequest(body, contentType, getParams) {
        var url = '/api/v1/sql';
        if (getParams) {
            url += '?' + qs.stringify(getParams);
        }
        return {
            method: 'POST',
            url: url,
            data: body,
            headers: {
                host: 'vizzuality.cartodb.com',
                'Content-Type': contentType
            }
        };
    }

    var LENGHTY_SUFFIX = ' [...]';

    var postScenariosRequests = [
        {
            desc: 'should return json string for application/x-www-form-urlencoded',
            request: createPostRequest(
                qs.stringify(BODY_PAYLOAD), 'application/x-www-form-urlencoded'
            )
        },
        {
            desc: 'should return json string for application/x-www-form-urlencoded, with API key in GET param',
            request: createPostRequest(
                qs.stringify({q: SQL_QUERY}), 'application/x-www-form-urlencoded', {api_key: API_KEY}
            )
        },
        {
            desc: 'should return json string for application/json',
            request: createPostRequest(
                JSON.stringify(BODY_PAYLOAD), 'application/json'
            )
        },
        {
            desc: 'should return json string for application/json, with API key in GET param',
            request: createPostRequest(
                JSON.stringify({q: SQL_QUERY}), 'application/json', {api_key: API_KEY}
            )
        },
        {
            desc: 'should return a substring when sql query is very long',
            request: createPostRequest(
                JSON.stringify({q: "select '" + new Array(2500).join('a') +  "'"}), 'application/json'
            ),
            expectedSQLQueryToLog: "select '" + (new Array(2000 + 1 - "select '".length).join('a')) + LENGHTY_SUFFIX
        }
    ];

    postScenariosRequests.forEach(function(scenario) {
        it(scenario.desc, function(done) {
            var called = 0;

            var getSqlQueryFromRequestBodyFn = server.getSqlQueryFromRequestBody;

            server.getSqlQueryFromRequestBody = function(req) {
                called++;
                var result = getSqlQueryFromRequestBodyFn(req);
                assert.deepEqual(JSON.parse(result), {q: scenario.expectedSQLQueryToLog || SQL_QUERY});
                return result;
            };

            assert.response(server, scenario.request, RESPONSE_OK, function(err, res) {
                assert.ok(!err);
                assert.equal(called, 1);

                server.getSqlQueryFromRequestBody = getSqlQueryFromRequestBodyFn;

                done();
            });
        });
    });

    it('should not log sql query in GET requests', function(done) {
        var called = 0;

        var getSqlQueryFromRequestBodyFn = server.getSqlQueryFromRequestBody;

        server.getSqlQueryFromRequestBody = function(req) {
            called++;
            var result = getSqlQueryFromRequestBodyFn(req);
            assert.equal(result, '');
            return result;
        };

        assert.response(server,
            {
                method: 'GET',
                url: '/api/v1/sql?' + qs.stringify(BODY_PAYLOAD),
                headers: {
                    host: 'vizzuality.cartodb.com'
                }
            },
            RESPONSE_OK,
            function(err) {
                assert.ok(!err);
                assert.equal(called, 1);

                server.getSqlQueryFromRequestBody = getSqlQueryFromRequestBodyFn;

                done();
            }
        );
    });
});
<commit_msg>Remove unused param<commit_after>require('../helper');

var appServer = require('../../app/server');
var assert = require('../support/assert');
var qs = require('querystring');
var log4js = require('log4js');

describe('Logging SQL query on POST requests', function() {

    var SQL_QUERY = "SELECT 'wadus'";
    var API_KEY = 1234;
    var BODY_PAYLOAD = {
        q: SQL_QUERY,
        api_key: API_KEY
    };

    var RESPONSE_OK = {
        statusCode: 200
    };

    var server;
    before(function() {
        global.settings.log_format = ':method :req[Host]:url :status :sql';
        global.log4js = log4js;
        global.log4js.configure({
            appenders: [
                {
                    type: "console",
                    layout: {
                        type:'basic'
                    }
                }
            ]
        });
        server = appServer();
    });

    after(function() {
        global.log4js = null;
        delete global.log4js;
    });

    function createPostRequest(body, contentType, getParams) {
        var url = '/api/v1/sql';
        if (getParams) {
            url += '?' + qs.stringify(getParams);
        }
        return {
            method: 'POST',
            url: url,
            data: body,
            headers: {
                host: 'vizzuality.cartodb.com',
                'Content-Type': contentType
            }
        };
    }

    var LENGHTY_SUFFIX = ' [...]';

    var postScenariosRequests = [
        {
            desc: 'should return json string for application/x-www-form-urlencoded',
            request: createPostRequest(
                qs.stringify(BODY_PAYLOAD), 'application/x-www-form-urlencoded'
            )
        },
        {
            desc: 'should return json string for application/x-www-form-urlencoded, with API key in GET param',
            request: createPostRequest(
                qs.stringify({q: SQL_QUERY}), 'application/x-www-form-urlencoded', {api_key: API_KEY}
            )
        },
        {
            desc: 'should return json string for application/json',
            request: createPostRequest(
                JSON.stringify(BODY_PAYLOAD), 'application/json'
            )
        },
        {
            desc: 'should return json string for application/json, with API key in GET param',
            request: createPostRequest(
                JSON.stringify({q: SQL_QUERY}), 'application/json', {api_key: API_KEY}
            )
        },
        {
            desc: 'should return a substring when sql query is very long',
            request: createPostRequest(
                JSON.stringify({q: "select '" + new Array(2500).join('a') +  "'"}), 'application/json'
            ),
            expectedSQLQueryToLog: "select '" + (new Array(2000 + 1 - "select '".length).join('a')) + LENGHTY_SUFFIX
        }
    ];

    postScenariosRequests.forEach(function(scenario) {
        it(scenario.desc, function(done) {
            var called = 0;

            var getSqlQueryFromRequestBodyFn = server.getSqlQueryFromRequestBody;

            server.getSqlQueryFromRequestBody = function(req) {
                called++;
                var result = getSqlQueryFromRequestBodyFn(req);
                assert.deepEqual(JSON.parse(result), {q: scenario.expectedSQLQueryToLog || SQL_QUERY});
                return result;
            };

            assert.response(server, scenario.request, RESPONSE_OK, function(err) {
                assert.ok(!err);
                assert.equal(called, 1);

                server.getSqlQueryFromRequestBody = getSqlQueryFromRequestBodyFn;

                done();
            });
        });
    });

    it('should not log sql query in GET requests', function(done) {
        var called = 0;

        var getSqlQueryFromRequestBodyFn = server.getSqlQueryFromRequestBody;

        server.getSqlQueryFromRequestBody = function(req) {
            called++;
            var result = getSqlQueryFromRequestBodyFn(req);
            assert.equal(result, '');
            return result;
        };

        assert.response(server,
            {
                method: 'GET',
                url: '/api/v1/sql?' + qs.stringify(BODY_PAYLOAD),
                headers: {
                    host: 'vizzuality.cartodb.com'
                }
            },
            RESPONSE_OK,
            function(err) {
                assert.ok(!err);
                assert.equal(called, 1);

                server.getSqlQueryFromRequestBody = getSqlQueryFromRequestBodyFn;

                done();
            }
        );
    });
});
<|endoftext|>


<commit_before>var minimalUql = require("./e2e.minimal.js");
var urlTest = 'https://www.library.uq.edu.au/exams/papers.php?stub=phil&startyear=2013&endyear=2017';

module.exports = {
    '@tags': ['e2etest', 'exams'],

    'Test reusable components are applied to Exams': function (client) {
        // common uql checks
        minimalUql.commonChecks(client, urlTest);
    }
};<commit_msg>remove exams post-deployment confirmation of reusable components as a call to exams now only hits SSO [PT #167082457]<commit_after><|endoftext|>