<commit_before>import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';

import * as actions from 'reduxes/actions';

import NavMenuItem from './NavMenuItem';

import Event from 'vendors/Event';

class NavMenuList extends Component {

    constructor(props) {
        super(props);
    }

    render() {

        const {navMenu, expandMenuName, activatedMenu, expandMenu, updateActivatedMenu} = this.props;

        return (

                    {
                        navMenu.map((menu, index) =>
                            <NavMenuItem key={(menu && menu.text) || index}
                                         expandMenuName={expandMenuName}
                                         activatedMenu={activatedMenu}
                                         options={menu}
                                         expandMenu={expandMenu}
                                         updateActivatedMenu={updateActivatedMenu}/>
                        )
                    }

        );

    }
}

NavMenuList.propTypes = {

    navMenu: PropTypes.array,
    expandMenuName: PropTypes.string,
    activatedMenu: PropTypes.object,

    expandMenu: PropTypes.func,
    updateActivatedMenu: PropTypes.func

};

export default connect(state => ({
    navMenu: state.navMenu.menu,
    expandMenuName: state.navMenu.expandMenuName,
    activatedMenu: state.navMenu.activatedMenu
}), dispatch => bindActionCreators({
    expandMenu: actions.expandMenu,
    updateActivatedMenu: actions.updateActivatedMenu
}, dispatch))(NavMenuList);<commit_msg>update NavMenuList<commit_after>import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';

import * as actions from 'reduxes/actions';

import NavMenuItem from './NavMenuItem';

import Event from 'vendors/Event';

class NavMenuList extends Component {

    constructor(props) {
        super(props);
    }

    render() {

        const {navMenu, expandMenuName, activatedMenu, expandMenu, updateActivatedMenu} = this.props;

        return (

                    {
                        navMenu && navMenu.map((menu, index) =>
                            <NavMenuItem key={(menu && menu.text) || index}
                                         expandMenuName={expandMenuName}
                                         activatedMenu={activatedMenu}
                                         options={menu}
                                         expandMenu={expandMenu}
                                         updateActivatedMenu={updateActivatedMenu}/>
                        )
                    }

        );

    }
}

NavMenuList.propTypes = {

    navMenu: PropTypes.array,
    expandMenuName: PropTypes.string,
    activatedMenu: PropTypes.object,

    expandMenu: PropTypes.func,
    updateActivatedMenu: PropTypes.func

};

export default connect(state => ({
    navMenu: state.navMenu.menu,
    expandMenuName: state.navMenu.expandMenuName,
    activatedMenu: state.navMenu.activatedMenu
}), dispatch => bindActionCreators({
    expandMenu: actions.expandMenu,
    updateActivatedMenu: actions.updateActivatedMenu
}, dispatch))(NavMenuList);<|endoftext|>


<commit_before>/* See license.txt for terms of usage */

define([
    "firebug/lib/trace",
    "firebug/lib/object",
    "firebug/lib/domplate",
    "firebug/lib/locale",
    "firebug/lib/dom",
    "firebug/lib/css",
    "firebug/lib/events",
    "firebug/lib/persist",
    "firebug/css/selectorModule",
    "firebug/css/selectorEditor"
],
function(FBTrace, Obj, Domplate, Locale, Dom, Css, Events, Persist, CSSSelectorsModule,
    SelectorEditor) {

with (Domplate) {

// ********************************************************************************************* //
// CSS Computed panel (HTML side panel)

function CSSSelectorsPanel() {}

CSSSelectorsPanel.prototype = Obj.extend(Firebug.Panel,
{
    template: domplate(
    {
        selectorsTag:
            DIV({"class": "selectorTrials a11yCSSView", role: "list", "aria-label":
                Locale.$STR("aria.labels.Selectors")},
                TAG("$selectorEditorRow"),
                DIV({"class": "elementsGroups"})
            ),

        selectorEditorRow:
            DIV({"class": "selectorEditorContainer editorContainer a11yFocusNoTab",
                role: "button", "tabindex" : "0",
                "aria-label": Locale.$STR("a11y.labels.press_enter_to_add_new_selector"),
                onclick: "$onClickEditor"},
                Locale.$STR("css.selector.TryASelector")
            ),

        elementsGroupTag:
            DIV({"class": "elementsGroup foldableGroup", $opened: "$group.opened",
                role: "list", _repObject: "$group"},
                H1({"class": "cssElementsHeader groupHeader focusRow", role: "listitem"},
                    DIV({"class": "twisty", role: "presentation"}),
                    SPAN({"class": "cssElementsLabel groupLabel"}, "$group.selector"),
                    DIV({"class": "closeButton selectorGroupRemoveButton"})
                )
            ),

        elementsTag:
            TABLE({"class": "cssElementsTable groupContent", role: "list"},
                TBODY({role: "presentation"},
                    FOR("element", "$elements",
                        TR({"class": "focusRow cssElementsRow cssElements",
                                role: "listitem", _repObject: "$element"},
                            TD({"class": "cssElement", role: "presentation"},
                                TAG("$element|getElementTag", {object: "$element"})
                            )
                        )
                    )
                )
            ),

        getElementTag: function(value)
        {
            var rep = Firebug.getRep(value);
            var tag = rep.shortTag ? rep.shortTag : rep.tag;
            return tag;
        },

        onClickEditor: function(event)
        {
            var target = event.currentTarget;
            var panel = Firebug.getElementPanel(target);
            Firebug.Editor.startEditing(target, "");
        }
    }),

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Events

    onClick: function(event)
    {
        if (!Events.isLeftClick(event))
            return;

        var header = Dom.getAncestorByClass(event.target, "groupHeader");
        if (header)
        {
            var removeButton = Dom.getAncestorByClass(event.target, "selectorGroupRemoveButton");
            if (removeButton)
            {
                var group = Firebug.getRepObject(event.target);
                this.removeGroup(group.selector);
            }
            else
            {
                this.toggleGroup(event.target);
            }
        }
    },

    onMutationObserve: function(records)
    {
        this.refresh();
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Extends Panel

    name: "selectors",
    parentPanel: "stylesheet",
    order: 0,

    initialize: function()
    {
        this.groups = [];
        this.onClick = Obj.bind(this.onClick, this);
        this.onMutationObserve = this.onMutationObserve.bind(this);

        Firebug.Panel.initialize.apply(this, arguments);
    },

    destroy: function(state)
    {
        state.groups = this.groups;
        Persist.persistObjects(this, state);

        Firebug.Panel.destroyNode.apply(this, arguments);
    },

    initializeNode: function(oldPanelNode)
    {
        Firebug.Panel.initializeNode.apply(this, arguments);

        Events.addEventListener(this.panelNode, "click", this.onClick, false);
    },

    destroyNode: function()
    {
        Firebug.Panel.destroyNode.apply(this, arguments);

        Events.removeEventListener(this.panelNode, "click", this.onClick, false);
    },

    supportsObject: function(object)
    {
        return 0;
    },

    show: function(state)
    {
        Persist.restoreObjects(this, state);

        if (state)
        {
            if (state.groups)
                this.groups = state.groups;
        }

        this.refresh();

        this.mutationObserver = new MutationObserver(this.onMutationObserve);
        this.mutationObserver.observe(this.context.window.document, {
            attributes: true,
            childList: true,
            characterData: true,
            subtree: true
        });
    },

    hide: function()
    {
        this.mutationObserver.disconnect();
        this.mutationObserver = null;
    },

    getEditor: function(target, value)
    {
        if (!this.editor)
            this.editor = new CSSSelectorsPanelEditor(this.document);

        return this.editor;
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Groups

    addGroup: function(selector)
    {
        var group = {
            selector: selector,
            opened: true
        }
        this.groups.push(group);

        this.displayGroup(group);
    },

    displayGroup: function(group)
    {
        var elementsGroups = this.panelNode.getElementsByClassName("elementsGroups")[0];
        var action = elementsGroups.getElementsByClassName("noSelection")[0] ?
            "replace" : "append";
        var elementsGroup = this.template.elementsGroupTag[action](
            {group: group, windows: this.context.windows}, elementsGroups);

        try
        {
            var elements = CSSSelectorsModule.matchElements(this.context.windows, group.selector);
            if (elements.length != 0)
            {
                this.template.elementsTag.append({elements: elements}, elementsGroup);
            }
            else
            {
                WarningTemplate.noSelectionResultsTag.append({}, elementsGroup);
            }
        }
        catch(e)
        {
            WarningTemplate.invalidSelectorTag.append({}, elementsGroup);
        }
    },

    toggleGroup: function(node)
    {
        var groupsNode = Dom.getAncestorByClass(node, "elementsGroups");
        var groupNode = Dom.getAncestorByClass(node, "elementsGroup");
        var group = Firebug.getRepObject(groupNode);

        Css.toggleClass(groupNode, "opened");
        var opened = Css.hasClass(groupNode, "opened");
        group.opened = opened;

        if (opened)
        {
            var offset = Dom.getClientOffset(node);
            var titleAtTop = offset.y < groupsNode.scrollTop;

            Dom.scrollTo(groupNode, groupsNode, null,
                groupNode.offsetHeight > groupsNode.clientHeight || titleAtTop ? "top" : "bottom");
        }
    },

    removeGroup: function(selector)
    {
        for (var i=0, len=this.groups.length; i<len; ++i)
        {
            if (this.groups[i].selector == selector)
            {
                this.groups.splice(i, 1);

                // Remove elements group from display
                var elementsGroup = this.panelNode.getElementsByClassName("elementsGroup")[i];
                elementsGroup.parentNode.removeChild(elementsGroup);
                break;
            }
        }
    },

    refresh: function()
    {
        var parentNode = this.template.selectorsTag.replace(
                {groups: this.groups, windows: this.context.windows}, this.panelNode);

        if (this.groups.length == 0)
        {
            var elementsGroups = parentNode.getElementsByClassName("elementsGroups")[0];
            WarningTemplate.noSelectionTag.replace({}, elementsGroups);
        }
        else
        {
            for (var i=0, len=this.groups.length; i<len; ++i)
                this.displayGroup(this.groups[i]);
        }
    }
});

function CSSSelectorsPanelEditor(doc)
{
    this.box = this.tag.replace({}, doc, this);
    this.input = this.box;

    Firebug.InlineEditor.prototype.initialize.call(this);
    this.tabNavigation = false;
    this.fixedWidth = true;
}

CSSSelectorsPanelEditor.prototype = domplate(SelectorEditor.prototype,
{
    tag:
        INPUT({"class": "fixedWidthEditor selectorsPanelEditor a11yFocusNoTab",
            type: "text",
            title: Locale.$STR("Selector"),
            oninput: "$onInput",
            onkeypress: "$onKeyPress"}
        ),

    saveEdit: function(target, value, previousValue)
    {
        var saveSuccess = this.isValidSelector(value);
        this.box.setAttribute("saveSuccess", saveSuccess);
    },

    endEditing: function(target, value, cancel)
    {
        if (cancel || value == "")
            return;

        this.panel.addGroup(value);
    },

    isValidSelector: function(value)
    {
        try {
            this.panel.panelNode.querySelector(value);
            return true;
        }
        catch (e)
        {
            return false;
        }
    }
});

//********************************************************************************************* //

var WarningTemplate = domplate(Firebug.Rep,
{
    noSelectionTag:
        DIV({"class": "selectorWarning noSelection"},
            SPAN(Locale.$STR("css.selector.noSelection"))
        ),

    noSelectionResultsTag:
        DIV({"class": "selectorWarning noSelectionResults"},
            SPAN(Locale.$STR("css.selector.noSelectionResults"))
        ),

    invalidSelectorTag:
        DIV({"class": "selectorWarning invalidSelector"},
            SPAN(Locale.$STR("css.selector.invalidSelector"))
        )
});

//********************************************************************************************* //
//Registration

Firebug.registerPanel(CSSSelectorsPanel);

return CSSSelectorsPanel;

//********************************************************************************************* //
}});
<commit_msg>Issue 6540 (Dynamically update selector groups when framed elements change) http://code.google.com/p/fbug/issues/detail?id=6540<commit_after>/* See license.txt for terms of usage */

define([
    "firebug/lib/trace",
    "firebug/lib/object",
    "firebug/lib/domplate",
    "firebug/lib/locale",
    "firebug/chrome/window",
    "firebug/lib/dom",
    "firebug/lib/css",
    "firebug/lib/events",
    "firebug/lib/persist",
    "firebug/css/selectorModule",
    "firebug/css/selectorEditor"
],
function(FBTrace, Obj, Domplate, Locale, Win, Dom, Css, Events, Persist, CSSSelectorsModule,
    SelectorEditor) {

with (Domplate) {

// ********************************************************************************************* //
// CSS Computed panel (HTML side panel)

function CSSSelectorsPanel() {}

CSSSelectorsPanel.prototype = Obj.extend(Firebug.Panel,
{
    template: domplate(
    {
        selectorsTag:
            DIV({"class": "selectorTrials a11yCSSView", role: "list", "aria-label":
                Locale.$STR("aria.labels.Selectors")},
                TAG("$selectorEditorRow"),
                DIV({"class": "elementsGroups"})
            ),

        selectorEditorRow:
            DIV({"class": "selectorEditorContainer editorContainer a11yFocusNoTab",
                role: "button", "tabindex" : "0",
                "aria-label": Locale.$STR("a11y.labels.press_enter_to_add_new_selector"),
                onclick: "$onClickEditor"},
                Locale.$STR("css.selector.TryASelector")
            ),

        elementsGroupTag:
            DIV({"class": "elementsGroup foldableGroup", $opened: "$group.opened",
                role: "list", _repObject: "$group"},
                H1({"class": "cssElementsHeader groupHeader focusRow", role: "listitem"},
                    DIV({"class": "twisty", role: "presentation"}),
                    SPAN({"class": "cssElementsLabel groupLabel"}, "$group.selector"),
                    DIV({"class": "closeButton selectorGroupRemoveButton"})
                )
            ),

        elementsTag:
            TABLE({"class": "cssElementsTable groupContent", role: "list"},
                TBODY({role: "presentation"},
                    FOR("element", "$elements",
                        TR({"class": "focusRow cssElementsRow cssElements",
                                role: "listitem", _repObject: "$element"},
                            TD({"class": "cssElement", role: "presentation"},
                                TAG("$element|getElementTag", {object: "$element"})
                            )
                        )
                    )
                )
            ),

        getElementTag: function(value)
        {
            var rep = Firebug.getRep(value);
            var tag = rep.shortTag ? rep.shortTag : rep.tag;
            return tag;
        },

        onClickEditor: function(event)
        {
            var target = event.currentTarget;
            var panel = Firebug.getElementPanel(target);
            Firebug.Editor.startEditing(target, "");
        }
    }),

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Events

    onClick: function(event)
    {
        if (!Events.isLeftClick(event))
            return;

        var header = Dom.getAncestorByClass(event.target, "groupHeader");
        if (header)
        {
            var removeButton = Dom.getAncestorByClass(event.target, "selectorGroupRemoveButton");
            if (removeButton)
            {
                var group = Firebug.getRepObject(event.target);
                this.removeGroup(group.selector);
            }
            else
            {
                this.toggleGroup(event.target);
            }
        }
    },

    onMutationObserve: function(records)
    {
        this.refresh();
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Extends Panel

    name: "selectors",
    parentPanel: "stylesheet",
    order: 0,

    initialize: function()
    {
        this.groups = [];
        this.onClick = Obj.bind(this.onClick, this);
        this.onMutationObserve = this.onMutationObserve.bind(this);

        Firebug.Panel.initialize.apply(this, arguments);
    },

    destroy: function(state)
    {
        state.groups = this.groups;
        Persist.persistObjects(this, state);

        Firebug.Panel.destroyNode.apply(this, arguments);
    },

    initializeNode: function(oldPanelNode)
    {
        Firebug.Panel.initializeNode.apply(this, arguments);

        Events.addEventListener(this.panelNode, "click", this.onClick, false);
    },

    destroyNode: function()
    {
        Firebug.Panel.destroyNode.apply(this, arguments);

        Events.removeEventListener(this.panelNode, "click", this.onClick, false);
    },

    supportsObject: function(object)
    {
        return 0;
    },

    show: function(state)
    {
        Persist.restoreObjects(this, state);

        if (state)
        {
            if (state.groups)
                this.groups = state.groups;
        }

        this.refresh();

        this.observeMutations();
    },

    watchWindow: function(context, win)
    {
        this.observeMutations(win);
    },

    hide: function()
    {
        this.context.mutationObserver.disconnect();
        this.context.mutationObserver = null;
    },

    getEditor: function(target, value)
    {
        if (!this.editor)
            this.editor = new CSSSelectorsPanelEditor(this.document);

        return this.editor;
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // General

    observeMutations: function(win)
    {
        var context = this.context;
        if (!context.mutationObserver)
            context.mutationObserver = new MutationObserver(this.onMutationObserve);

        function addObserver(win)
        {
            var doc = win.document;
            context.mutationObserver.observe(doc, {
                attributes: true,
                childList: true,
                characterData: true,
                subtree: true
            });
        }

        // If a window is specified, use it, otherwise register observers for all
        // context windows (including the main window and all embedded iframes).
        if (win)
            addObserver(win);
        else
            Win.iterateWindows(this.context.window, addObserver);
    },

    refresh: function()
    {
        var parentNode = this.template.selectorsTag.replace(
                {groups: this.groups, windows: this.context.windows}, this.panelNode);

        if (this.groups.length == 0)
        {
            var elementsGroups = parentNode.getElementsByClassName("elementsGroups")[0];
            WarningTemplate.noSelectionTag.replace({}, elementsGroups);
        }
        else
        {
            for (var i=0, len=this.groups.length; i<len; ++i)
                this.displayGroup(this.groups[i]);
        }
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Groups

    addGroup: function(selector)
    {
        var group = {
            selector: selector,
            opened: true
        }
        this.groups.push(group);

        this.displayGroup(group);
    },

    displayGroup: function(group)
    {
        var elementsGroups = this.panelNode.getElementsByClassName("elementsGroups")[0];
        var action = elementsGroups.getElementsByClassName("noSelection")[0] ?
            "replace" : "append";
        var elementsGroup = this.template.elementsGroupTag[action](
            {group: group, windows: this.context.windows}, elementsGroups);

        try
        {
            var elements = CSSSelectorsModule.matchElements(this.context.windows, group.selector);
            if (elements.length != 0)
            {
                this.template.elementsTag.append({elements: elements}, elementsGroup);
            }
            else
            {
                WarningTemplate.noSelectionResultsTag.append({}, elementsGroup);
            }
        }
        catch(e)
        {
            WarningTemplate.invalidSelectorTag.append({}, elementsGroup);
        }
    },

    toggleGroup: function(node)
    {
        var groupsNode = Dom.getAncestorByClass(node, "elementsGroups");
        var groupNode = Dom.getAncestorByClass(node, "elementsGroup");
        var group = Firebug.getRepObject(groupNode);

        Css.toggleClass(groupNode, "opened");
        var opened = Css.hasClass(groupNode, "opened");
        group.opened = opened;

        if (opened)
        {
            var offset = Dom.getClientOffset(node);
            var titleAtTop = offset.y < groupsNode.scrollTop;

            Dom.scrollTo(groupNode, groupsNode, null,
                groupNode.offsetHeight > groupsNode.clientHeight || titleAtTop ? "top" : "bottom");
        }
    },

    removeGroup: function(selector)
    {
        for (var i=0, len=this.groups.length; i<len; ++i)
        {
            if (this.groups[i].selector == selector)
            {
                this.groups.splice(i, 1);

                // Remove elements group from display
                var elementsGroup = this.panelNode.getElementsByClassName("elementsGroup")[i];
                elementsGroup.parentNode.removeChild(elementsGroup);
                break;
            }
        }
    }
});

function CSSSelectorsPanelEditor(doc)
{
    this.box = this.tag.replace({}, doc, this);
    this.input = this.box;

    Firebug.InlineEditor.prototype.initialize.call(this);
    this.tabNavigation = false;
    this.fixedWidth = true;
}

CSSSelectorsPanelEditor.prototype = domplate(SelectorEditor.prototype,
{
    tag:
        INPUT({"class": "fixedWidthEditor selectorsPanelEditor a11yFocusNoTab",
            type: "text",
            title: Locale.$STR("Selector"),
            oninput: "$onInput",
            onkeypress: "$onKeyPress"}
        ),

    saveEdit: function(target, value, previousValue)
    {
        var saveSuccess = this.isValidSelector(value);
        this.box.setAttribute("saveSuccess", saveSuccess);
    },

    endEditing: function(target, value, cancel)
    {
        if (cancel || value == "")
            return;

        this.panel.addGroup(value);
    },

    isValidSelector: function(value)
    {
        try {
            this.panel.panelNode.querySelector(value);
            return true;
        }
        catch (e)
        {
            return false;
        }
    }
});

//********************************************************************************************* //

var WarningTemplate = domplate(Firebug.Rep,
{
    noSelectionTag:
        DIV({"class": "selectorWarning noSelection"},
            SPAN(Locale.$STR("css.selector.noSelection"))
        ),

    noSelectionResultsTag:
        DIV({"class": "selectorWarning noSelectionResults"},
            SPAN(Locale.$STR("css.selector.noSelectionResults"))
        ),

    invalidSelectorTag:
        DIV({"class": "selectorWarning invalidSelector"},
            SPAN(Locale.$STR("css.selector.invalidSelector"))
        )
});

//********************************************************************************************* //
//Registration

Firebug.registerPanel(CSSSelectorsPanel);

return CSSSelectorsPanel;

//********************************************************************************************* //
}});
<|endoftext|>


<commit_before>var Class = require('../../utils/Class');
var BaseSound = require('../BaseSound');
var SoundEvent = require('../SoundEvent');
var SoundValueEvent = require('../SoundValueEvent');
var HTML5AudioSound = new Class({
    Extends: BaseSound,
    initialize: function HTML5AudioSound(manager, key, config) {
        if (config === void 0) { config = {}; }
        /**
         * An array containing all HTML5 Audio tags that could be used for individual
         * sound's playback. Number of instances depends on the config value passed
         * to the Loader#audio method call, default is 1.
         *
         * @private
         * @property {HTMLAudioElement[]} tags
         */
        this.tags = manager.game.cache.audio.get(key);
        if (!this.tags) {
            console.error('No audio loaded in cache with key: \'' + key + '\'!');
            return;
        }
        /**
         * Reference to an HTML5 Audio tag used for playing sound.
         *
         * @private
         * @property {HTMLAudioElement} audio
         * @default null
         */
        this.audio = null;
        /**
         * Audio tag's playback position recorded on previous
         * update method call. Set to 0 if sound is not playing.
         *
         * @private
         * @property {number} previousTime
         * @default 0
         */
        this.previousTime = 0;
        /**
         * Timestamp as generated by the Request Animation Frame or SetTimeout
         * representing the time at which the delayed sound playback should start.
         * Set to 0 if sound playback is not delayed.
         *
         * @private
         * @property {number} startTime
         * @default 0
         */
        this.startTime = 0;
        this.duration = this.tags[0].duration;
        this.totalDuration = this.tags[0].duration;
        BaseSound.call(this, manager, key, config);
    },
    play: function (markerName, config) {
        if (!BaseSound.prototype.play.call(this, markerName, config)) {
            return false;
        }
        //  \/\/\/ isPlaying = true, isPaused = false \/\/\/
        if (!this.pickAndPlayAudioTag()) {
            return false;
        }
        this.events.dispatch(new SoundEvent(this, 'SOUND_PLAY'));
        return true;
    },
    pause: function () {
        if (this.startTime > 0) {
            return false;
        }
        if (!BaseSound.prototype.pause.call(this)) {
            return false;
        }
        //  \/\/\/ isPlaying = false, isPaused = true \/\/\/
        this.currentConfig.seek = this.audio.currentTime
            - (this.currentMarker ? this.currentMarker.start : 0);
        this.stopAndReleaseAudioTag();
        this.events.dispatch(new SoundEvent(this, 'SOUND_PAUSE'));
        return true;
    },
    resume: function () {
        if (this.startTime > 0) {
            return false;
        }
        if (!BaseSound.prototype.resume.call(this)) {
            return false;
        }
        //  \/\/\/ isPlaying = true, isPaused = false \/\/\/
        if (!this.pickAndPlayAudioTag()) {
            return false;
        }
        this.events.dispatch(new SoundEvent(this, 'SOUND_RESUME'));
        return true;
    },
    stop: function () {
        if (!BaseSound.prototype.stop.call(this)) {
            return false;
        }
        //  \/\/\/ isPlaying = false, isPaused = false \/\/\/
        this.stopAndReleaseAudioTag();
        this.events.dispatch(new SoundEvent(this, 'SOUND_STOP'));
        return true;
    },
    pickAndPlayAudioTag: function () {
        if (!this.pickAudioTag()) {
            this.reset();
            return false;
        }
        var seek = this.currentConfig.seek;
        var delay = this.currentConfig.delay;
        var offset = (this.currentMarker ? this.currentMarker.start : 0) + seek;
        this.previousTime =
            this.audio.currentTime = offset;
        this.applyConfig();
        if (delay === 0) {
            this.startTime = 0;
            if (this.audio.paused) {
                this.audio.play();
            }
        }
        else {
            this.startTime = window.performance.now() + delay;
            if (!this.audio.paused) {
                this.audio.pause();
            }
        }
        this.resetConfig();
        return true;
    },
    pickAudioTag: function () {
        if (!this.audio) {
            for (var i = 0; i < this.tags.length; i++) {
                var audio = this.tags[i];
                if (audio.dataset.used === 'false') {
                    audio.dataset.used = 'true';
                    this.audio = audio;
                    return true;
                }
            }
            if (!this.manager.override) {
                return false;
            }
            var otherSounds_1 = [];
            this.manager.forEachActiveSound(function (sound) {
                if (sound.key === this.key && sound.isPlaying) {
                    otherSounds_1.push(sound);
                }
            });
            otherSounds_1.sort(function (a1, a2) {
                if (a1.loop === a2.loop) {
                    return a2.seek - a1.seek;
                }
                return a1.loop ? 1 : -1;
            });
            var selectedSound = otherSounds_1[0];
            this.audio = selectedSound.audio;
            selectedSound.reset();
            selectedSound.audio = null;
            selectedSound.startTime = 0;
            selectedSound.previousTime = 0;
        }
        return true;
    },
    stopAndReleaseAudioTag: function () {
        this.audio.pause();
        this.audio.dataset.used = 'false';
        this.audio = null;
        this.startTime = 0;
        this.previousTime = 0;
    },
    reset: function () {
        BaseSound.prototype.stop.call(this);
    },
    update: function (time, delta) {
        if (this.isPlaying) {
            // handling delayed playback
            if (this.startTime > 0) {
                if (this.startTime < time) {
                    this.previousTime =
                        this.audio.currentTime += (time - this.startTime) / 1000;
                    this.startTime = 0;
                    this.audio.play();
                }
                return;
            }
            // handle looping and ending
            var startTime = this.currentMarker ? this.currentMarker.start : 0;
            var endTime = startTime + this.duration;
            var currentTime = this.audio.currentTime;
            if (currentTime >= endTime) {
                if (this.currentConfig.loop) {
                    currentTime =
                        this.audio.currentTime = startTime + (currentTime - endTime);
                }
                else {
                    this.reset();
                    this.stopAndReleaseAudioTag();
                    this.events.dispatch(new SoundEvent(this, 'SOUND_ENDED'));
                    return;
                }
            }
            else if (currentTime < startTime) {
                currentTime =
                    this.audio.currentTime += startTime;
            }
            if (currentTime < this.previousTime) {
                this.events.dispatch(new SoundEvent(this, 'SOUND_LOOP'));
            }
            this.previousTime = currentTime;
        }
    },
    destroy: function () {
        BaseSound.prototype.destroy.call(this);
        // TODO release all HTML5 Audio tag related stuff
    },
    setMute: function () {
        if (this.audio) {
            this.audio.muted = this.currentConfig.mute || this.manager.mute;
        }
    },
    setVolume: function () {
        if (this.audio) {
            this.audio.volume = this.currentConfig.volume * this.manager.volume;
        }
    },
    setRate: function () {
        BaseSound.prototype.setRate.call(this);
        if (this.audio) {
            this.audio.playbackRate = this.totalRate;
        }
    }
});
/**
 * Mute setting.
 *
 * @name Phaser.Sound.HTML5AudioSound#mute
 * @property {boolean} mute
 */
Object.defineProperty(HTML5AudioSound.prototype, 'mute', {
    get: function () {
        return this.currentConfig.mute;
    },
    set: function (value) {
        this.currentConfig.mute = value;
        this.setMute();
        this.events.dispatch(new SoundValueEvent(this, 'SOUND_MUTE', value));
    }
});
/**
 * Volume setting.
 *
 * @name Phaser.Sound.HTML5AudioSound#volume
 * @property {number} volume
 */
Object.defineProperty(HTML5AudioSound.prototype, 'volume', {
    get: function () {
        return this.currentConfig.volume;
    },
    set: function (value) {
        this.currentConfig.volume = value;
        this.setVolume();
        this.events.dispatch(new SoundValueEvent(this, 'SOUND_VOLUME', value));
    }
});
/**
 * Current position of playing sound.
 *
 * @name Phaser.Sound.HTML5AudioSound#seek
 * @property {number} seek
 */
Object.defineProperty(HTML5AudioSound.prototype, 'seek', {
    get: function () {
        if (this.isPlaying) {
            return this.audio.currentTime;
        }
        else if (this.isPaused) {
            return this.currentConfig.seek;
        }
        else {
            return 0;
        }
    },
    set: function (value) {
        if (this.startTime > 0) {
            return;
        }
        if (this.isPlaying || this.isPaused) {
            value = Math.min(Math.max(0, value), this.duration);
            if (this.isPlaying) {
                this.audio.currentTime = value;
            }
            else if (this.isPaused) {
                this.currentConfig.seek = value;
            }
            this.events.dispatch(new SoundValueEvent(this, 'SOUND_SEEK', value));
        }
    }
});
/**
 * Property indicating whether or not
 * the sound or current sound marker will loop.
 *
 * @name Phaser.Sound.HTML5AudioSound#loop
 * @property {boolean} loop
 */
Object.defineProperty(HTML5AudioSound.prototype, 'loop', {
    get: function () {
        return this.currentConfig.loop;
    },
    set: function (value) {
        this.currentConfig.loop = value;
        if (this.audio) {
            this.audio.loop = value;
        }
    }
});
module.exports = HTML5AudioSound;
<commit_msg>Updating previousTime property when setting seek value<commit_after>var Class = require('../../utils/Class');
var BaseSound = require('../BaseSound');
var SoundEvent = require('../SoundEvent');
var SoundValueEvent = require('../SoundValueEvent');
var HTML5AudioSound = new Class({
    Extends: BaseSound,
    initialize: function HTML5AudioSound(manager, key, config) {
        if (config === void 0) { config = {}; }
        /**
         * An array containing all HTML5 Audio tags that could be used for individual
         * sound's playback. Number of instances depends on the config value passed
         * to the Loader#audio method call, default is 1.
         *
         * @private
         * @property {HTMLAudioElement[]} tags
         */
        this.tags = manager.game.cache.audio.get(key);
        if (!this.tags) {
            console.error('No audio loaded in cache with key: \'' + key + '\'!');
            return;
        }
        /**
         * Reference to an HTML5 Audio tag used for playing sound.
         *
         * @private
         * @property {HTMLAudioElement} audio
         * @default null
         */
        this.audio = null;
        /**
         * Audio tag's playback position recorded on previous
         * update method call. Set to 0 if sound is not playing.
         *
         * @private
         * @property {number} previousTime
         * @default 0
         */
        this.previousTime = 0;
        /**
         * Timestamp as generated by the Request Animation Frame or SetTimeout
         * representing the time at which the delayed sound playback should start.
         * Set to 0 if sound playback is not delayed.
         *
         * @private
         * @property {number} startTime
         * @default 0
         */
        this.startTime = 0;
        this.duration = this.tags[0].duration;
        this.totalDuration = this.tags[0].duration;
        BaseSound.call(this, manager, key, config);
    },
    play: function (markerName, config) {
        if (!BaseSound.prototype.play.call(this, markerName, config)) {
            return false;
        }
        //  \/\/\/ isPlaying = true, isPaused = false \/\/\/
        if (!this.pickAndPlayAudioTag()) {
            return false;
        }
        this.events.dispatch(new SoundEvent(this, 'SOUND_PLAY'));
        return true;
    },
    pause: function () {
        if (this.startTime > 0) {
            return false;
        }
        if (!BaseSound.prototype.pause.call(this)) {
            return false;
        }
        //  \/\/\/ isPlaying = false, isPaused = true \/\/\/
        this.currentConfig.seek = this.audio.currentTime
            - (this.currentMarker ? this.currentMarker.start : 0);
        this.stopAndReleaseAudioTag();
        this.events.dispatch(new SoundEvent(this, 'SOUND_PAUSE'));
        return true;
    },
    resume: function () {
        if (this.startTime > 0) {
            return false;
        }
        if (!BaseSound.prototype.resume.call(this)) {
            return false;
        }
        //  \/\/\/ isPlaying = true, isPaused = false \/\/\/
        if (!this.pickAndPlayAudioTag()) {
            return false;
        }
        this.events.dispatch(new SoundEvent(this, 'SOUND_RESUME'));
        return true;
    },
    stop: function () {
        if (!BaseSound.prototype.stop.call(this)) {
            return false;
        }
        //  \/\/\/ isPlaying = false, isPaused = false \/\/\/
        this.stopAndReleaseAudioTag();
        this.events.dispatch(new SoundEvent(this, 'SOUND_STOP'));
        return true;
    },
    pickAndPlayAudioTag: function () {
        if (!this.pickAudioTag()) {
            this.reset();
            return false;
        }
        var seek = this.currentConfig.seek;
        var delay = this.currentConfig.delay;
        var offset = (this.currentMarker ? this.currentMarker.start : 0) + seek;
        this.previousTime =
            this.audio.currentTime = offset;
        this.applyConfig();
        if (delay === 0) {
            this.startTime = 0;
            if (this.audio.paused) {
                this.audio.play();
            }
        }
        else {
            this.startTime = window.performance.now() + delay;
            if (!this.audio.paused) {
                this.audio.pause();
            }
        }
        this.resetConfig();
        return true;
    },
    pickAudioTag: function () {
        if (!this.audio) {
            for (var i = 0; i < this.tags.length; i++) {
                var audio = this.tags[i];
                if (audio.dataset.used === 'false') {
                    audio.dataset.used = 'true';
                    this.audio = audio;
                    return true;
                }
            }
            if (!this.manager.override) {
                return false;
            }
            var otherSounds_1 = [];
            this.manager.forEachActiveSound(function (sound) {
                if (sound.key === this.key && sound.isPlaying) {
                    otherSounds_1.push(sound);
                }
            });
            otherSounds_1.sort(function (a1, a2) {
                if (a1.loop === a2.loop) {
                    return a2.seek - a1.seek;
                }
                return a1.loop ? 1 : -1;
            });
            var selectedSound = otherSounds_1[0];
            this.audio = selectedSound.audio;
            selectedSound.reset();
            selectedSound.audio = null;
            selectedSound.startTime = 0;
            selectedSound.previousTime = 0;
        }
        return true;
    },
    stopAndReleaseAudioTag: function () {
        this.audio.pause();
        this.audio.dataset.used = 'false';
        this.audio = null;
        this.startTime = 0;
        this.previousTime = 0;
    },
    reset: function () {
        BaseSound.prototype.stop.call(this);
    },
    update: function (time, delta) {
        if (this.isPlaying) {
            // handling delayed playback
            if (this.startTime > 0) {
                if (this.startTime < time) {
                    this.previousTime =
                        this.audio.currentTime += (time - this.startTime) / 1000;
                    this.startTime = 0;
                    this.audio.play();
                }
                return;
            }
            // handle looping and ending
            var startTime = this.currentMarker ? this.currentMarker.start : 0;
            var endTime = startTime + this.duration;
            var currentTime = this.audio.currentTime;
            if (currentTime >= endTime) {
                if (this.currentConfig.loop) {
                    currentTime =
                        this.audio.currentTime = startTime + (currentTime - endTime);
                }
                else {
                    this.reset();
                    this.stopAndReleaseAudioTag();
                    this.events.dispatch(new SoundEvent(this, 'SOUND_ENDED'));
                    return;
                }
            }
            else if (currentTime < startTime) {
                currentTime =
                    this.audio.currentTime += startTime;
            }
            if (currentTime < this.previousTime) {
                this.events.dispatch(new SoundEvent(this, 'SOUND_LOOP'));
            }
            this.previousTime = currentTime;
        }
    },
    destroy: function () {
        BaseSound.prototype.destroy.call(this);
        // TODO release all HTML5 Audio tag related stuff
    },
    setMute: function () {
        if (this.audio) {
            this.audio.muted = this.currentConfig.mute || this.manager.mute;
        }
    },
    setVolume: function () {
        if (this.audio) {
            this.audio.volume = this.currentConfig.volume * this.manager.volume;
        }
    },
    setRate: function () {
        BaseSound.prototype.setRate.call(this);
        if (this.audio) {
            this.audio.playbackRate = this.totalRate;
        }
    }
});
/**
 * Mute setting.
 *
 * @name Phaser.Sound.HTML5AudioSound#mute
 * @property {boolean} mute
 */
Object.defineProperty(HTML5AudioSound.prototype, 'mute', {
    get: function () {
        return this.currentConfig.mute;
    },
    set: function (value) {
        this.currentConfig.mute = value;
        this.setMute();
        this.events.dispatch(new SoundValueEvent(this, 'SOUND_MUTE', value));
    }
});
/**
 * Volume setting.
 *
 * @name Phaser.Sound.HTML5AudioSound#volume
 * @property {number} volume
 */
Object.defineProperty(HTML5AudioSound.prototype, 'volume', {
    get: function () {
        return this.currentConfig.volume;
    },
    set: function (value) {
        this.currentConfig.volume = value;
        this.setVolume();
        this.events.dispatch(new SoundValueEvent(this, 'SOUND_VOLUME', value));
    }
});
/**
 * Current position of playing sound.
 *
 * @name Phaser.Sound.HTML5AudioSound#seek
 * @property {number} seek
 */
Object.defineProperty(HTML5AudioSound.prototype, 'seek', {
    get: function () {
        if (this.isPlaying) {
            return this.audio.currentTime;
        }
        else if (this.isPaused) {
            return this.currentConfig.seek;
        }
        else {
            return 0;
        }
    },
    set: function (value) {
        if (this.startTime > 0) {
            return;
        }
        if (this.isPlaying || this.isPaused) {
            value = Math.min(Math.max(0, value), this.duration);
            if (this.isPlaying) {
                this.previousTime =
                    this.audio.currentTime = value;
            }
            else if (this.isPaused) {
                this.currentConfig.seek = value;
            }
            this.events.dispatch(new SoundValueEvent(this, 'SOUND_SEEK', value));
        }
    }
});
/**
 * Property indicating whether or not
 * the sound or current sound marker will loop.
 *
 * @name Phaser.Sound.HTML5AudioSound#loop
 * @property {boolean} loop
 */
Object.defineProperty(HTML5AudioSound.prototype, 'loop', {
    get: function () {
        return this.currentConfig.loop;
    },
    set: function (value) {
        this.currentConfig.loop = value;
        if (this.audio) {
            this.audio.loop = value;
        }
    }
});
module.exports = HTML5AudioSound;
<|endoftext|>


<commit_before>const Discord = require('discord.js');
module.exports = async function (message, hotline){
    let me = message.embeds.shift();
    const embed = new Discord.RichEmbed();
    if(me && me!=null)
    {
        if(me.author && me.author.name)
            embed.author = {
                name: me.author.name,
                icon_url: me.author.icon_url
            }
        if(me.description)
            embed.description = me.description
        if(me.fields && me.fields.length >0)
        {
            me.fields.map(function(f){
                embed.fields.push({
                          name: f.name,
                          value: f.value,
                          inline: f.inline
                          });
            });
        }
        if(me.thumbnail)
            embed.thumbnail = me.thumbnail;
        if(me.image)
            embed.thumbnail = me.image;
        if(me.url)
            embed.url = me.url;
        if(me.title)
            embed.title = me.title;
        if(me.color)
            embed.color = me.color;
        if(me.timestamp)
            embed.timestamp = me.timestamp;
        if(me.footer)
            embed.footer = {
                icon_url: me.footer.icon_url,
                text: me.footer.text
              };
        return embed;
        //return message.reply("Embeds not supported yet")
    }
    embed.setAuthor(message.author.username, message.author.displayAvatarURL())
    if (!hotline)
        embed.setTitle((message.guild)?(message.channel.name + " in " + message.guild.name):(" in a DM with " + message.channel.recipient.tag));
    if (!message.content.trim() == "")
        embed.setDescription(message.cleanContent);
    else
        embed.setDescription("**" + (( me.author && me.author!=null)?(me.author.name+ " : "):"")  + ((me.title && me.title!=null)?me.title:"") + "**\n	" + (me.description || "") + ((me.footer) ? ("\n*At " + me.footer.text + "*") : ""));
    if (!hotline)
        embed.setTimestamp(message.createdAt)
    let color = message.member;
    if (color) {
        color = color.roles.filter((r) => {
            if (r.color !== 0)
                return r;
        }).array().sort((r1, r2) => {
            if (r1.position < r2.position)
                return 1;
        }).shift();
        if (color)
            color = color.color;
    }
    else
        color = 0;
    embed.setColor(color);
    //embed.setTitle("Quote");
    return embed;
}
<commit_msg>use avatarURL<commit_after>const Discord = require('discord.js');
module.exports = async function (message, hotline){
    let me = message.embeds.shift();
    const embed = new Discord.RichEmbed();
    if(me && me!=null)
    {
        if(me.author && me.author.name)
            embed.author = {
                name: me.author.name,
                icon_url: me.author.icon_url
            }
        if(me.description)
            embed.description = me.description
        if(me.fields && me.fields.length >0)
        {
            me.fields.map(function(f){
                embed.fields.push({
                          name: f.name,
                          value: f.value,
                          inline: f.inline
                          });
            });
        }
        if(me.thumbnail)
            embed.thumbnail = me.thumbnail;
        if(me.image)
            embed.thumbnail = me.image;
        if(me.url)
            embed.url = me.url;
        if(me.title)
            embed.title = me.title;
        if(me.color)
            embed.color = me.color;
        if(me.timestamp)
            embed.timestamp = me.timestamp;
        if(me.footer)
            embed.footer = {
                icon_url: me.footer.icon_url,
                text: me.footer.text
              };
        return embed;
        //return message.reply("Embeds not supported yet")
    }
    embed.setAuthor(message.author.username, message.author.avatarURL())
    if (!hotline)
        embed.setTitle((message.guild)?(message.channel.name + " in " + message.guild.name):(" in a DM with " + message.channel.recipient.tag));
    if (!message.content.trim() == "")
        embed.setDescription(message.cleanContent);
    else
        embed.setDescription("**" + (( me.author && me.author!=null)?(me.author.name+ " : "):"")  + ((me.title && me.title!=null)?me.title:"") + "**\n	" + (me.description || "") + ((me.footer) ? ("\n*At " + me.footer.text + "*") : ""));
    if (!hotline)
        embed.setTimestamp(message.createdAt)
    let color = message.member;
    if (color) {
        color = color.roles.filter((r) => {
            if (r.color !== 0)
                return r;
        }).array().sort((r1, r2) => {
            if (r1.position < r2.position)
                return 1;
        }).shift();
        if (color)
            color = color.color;
    }
    else
        color = 0;
    embed.setColor(color);
    //embed.setTitle("Quote");
    return embed;
}
<|endoftext|>


<commit_before>(function ($) {

var toInt = function (value, defaultValue) {
	var parsed = parseInt(value);
	return isNaN(parsed) ? defaultValue : parsed;
};

var showCharacterCount = function ($counter, $source) {
	var $fg = $source.closest('.form-group');
	var text = $source.val();
	var length = text ? text.length : 0;

	var max = toInt($counter.data('maxlength'), Infinity);
	$counter.text(isFinite(max) ? length + ' / ' + max : length);

	var min = $fg.find('label').hasClass('requiredField') ? 1 : -Infinity;
	var error = length > max || length < min;

	$fg.toggleClass('has-error', error)
		.closest('form').find('input[type="submit"]').prop('disabled', error);
};

$('textarea.character-counted').each(function () {
	var $source = $(this);
	var $counter = $('#character-counter-template')
		.clone().removeClass('hide').removeAttr('id').insertAfter($source)
		.find('.character-counter').data('maxlength', $source.attr('maxlength'));

	$source.removeAttr('maxlength').on('input change', function () {
		showCharacterCount($counter, $(this));
	});
	showCharacterCount($counter, $source);
});

})(jQuery);
<commit_msg>Take both input and button submits into consideration<commit_after>(function ($) {

var toInt = function (value, defaultValue) {
	var parsed = parseInt(value);
	return isNaN(parsed) ? defaultValue : parsed;
};

var showCharacterCount = function ($counter, $source) {
	var $fg = $source.closest('.form-group');
	var text = $source.val();
	var length = text ? text.length : 0;

	var max = toInt($counter.data('maxlength'), Infinity);
	$counter.text(isFinite(max) ? length + ' / ' + max : length);

	var min = $fg.find('label').hasClass('requiredField') ? 1 : -Infinity;
	var error = length > max || length < min;

	$fg.toggleClass('has-error', error)
		.closest('form')
		.find('input[type="submit"],button[type="submit"]')
		.prop('disabled', error);
};

$('textarea.character-counted').each(function () {
	var $source = $(this);
	var $counter = $('#character-counter-template')
		.clone().removeClass('hide').removeAttr('id').insertAfter($source)
		.find('.character-counter').data('maxlength', $source.attr('maxlength'));

	$source.removeAttr('maxlength').on('input change', function () {
		showCharacterCount($counter, $(this));
	});
	showCharacterCount($counter, $source);
});

})(jQuery);
<|endoftext|>


<commit_before>var $ = require('jquery');
var debounce = require('debounce');
var cost = require('overall-loan-cost');
var objectify = require('objectified');
var formatUSD = require('format-usd');
var unFormatUSD = require('unformat-usd');
var isMoney = require('is-money-usd');
var positive = require('stay-positive');
var amortize = require('amortize');
var humanizeLoanType = require('./humanize-loan-type');
var supportsAccessors = require('./supports-accessors');
var fetchRates = require('./rates');

var loans = {};
var editableFields = ['location', 'minfico', 'maxfico', 'amount-borrowed', 'price', 'discount', 'down-payment', 'rate-structure', 'loan-term', 'loan-type', 'arm-type'];

function createNewForm( id, loanData ) {

  var loan = objectify('#lc-input-' + id, [
    {
      name: 'amount-borrowed',
      source: 'house-price-input - down-payment-input'
    },{
      name: 'price',
      source: 'house-price-input'
    },{
      name: 'down-payment',
      source: 'down-payment-input'
    },{
      name: 'rate-structure',
      source: 'rate-structure-select'
    },{
      name: 'loan-term',
      source: 'loan-term-select'
    },{
      name: 'loan-type',
      source: 'loan-type-select'
    },{
      name: 'arm-type',
      source: function() {
        return $('#arm-type-select-' + id).val();
      }
    },{
      name: 'interest-rate',
      source: 'interest-rate-select'
    },{
      name: 'raw-discount',
      source: function() {
        return $('#points-' + id + ' input:checked' ).val();
      }
    },{
      name: 'discount',
      source: function() {
        var points = loan['raw-discount'] / 100;
        return points * loan['amount-borrowed'];
      }
    },{
      name: 'processing',
      source: function() {
        return loan['amount-borrowed'] / 100;
      }
    },{
      name: 'third-party-services',
      source: function() {
        return 3000;
      }
    },{
      name: 'mortgage-insurance',
      source: function() {
        return 0;
      }
    },{
      name: 'taxes-gov-fees',
      source: function() {
        return 1000;
      }
    },{
      name: 'prepaid-expenses',
      source: function() {
        return 500;
      }
    },{
      name: 'initial-escrow',
      source: function() {
        return 500;
      }
    },{
      name: 'monthly-taxes-insurance',
      source: function() {
        var yearly = loan['price'] / 100,
            propertyTaxes = yearly / 12,
            homeInsurance = (.05 * loan['price']) / 12;
        return propertyTaxes + homeInsurance;
      }
    },{
      name: 'monthly-hoa-dues',
      source: function() {
        return 0;
      }
    },{
      name: 'monthly-principal-interest',
      source: function() {
        return amortize({
          amount: positive( loan['amount-borrowed'] ),
          rate: loan['interest-rate'],
          totalTerm: loan['loan-term'] * 12,
          amortizeTerm: 60 // @todo loan term * 12?
        }).payment;
      }
    },{
      name: 'monthly-mortgage-insurance',
      source: function() {
        return 0;
      }
    },{
      name: 'closing-costs',
      source: function() {
        return loan['down-payment']
             + loan['discount']
             + loan['processing']
             + loan['third-party-services']
             + loan['mortgage-insurance']
             + loan['taxes-gov-fees']
             + loan['prepaid-expenses']
             + loan['initial-escrow'];
      }
    },{
      name: 'monthly-payment',
      source: function() {
        var taxes = loan['monthly-taxes-insurance'],
            insurance = loan['monthly-mortgage-insurance'],
            hoa = loan['monthly-hoa-dues'],
            monthlyPrincipalInterest = loan['monthly-principal-interest'];
        return taxes + insurance + hoa + monthlyPrincipalInterest;
      }
    },{
      name: 'principal-paid',
      source: function() {
        return cost({
          amountBorrowed: positive( loan['amount-borrowed'] ),
          rate: loan['interest-rate'],
          totalTerm: loan['loan-term'] * 12,
          downPayment: loan['down-payment'],
          closingCosts: loan['closing-costs']
        }).totalEquity;
      }
    },{
      name: 'interest-fees-paid',
      source: function() {
        return cost({
          amountBorrowed: positive( loan['amount-borrowed'] ),
          rate: loan['interest-rate'],
          totalTerm: loan['loan-term'] * 12,
          downPayment: loan['down-payment'],
          closingCosts: loan['closing-costs']
        }).totalCost;
      }
    },{
      name: 'overall-cost',
      source: function() {
        return cost({
          amountBorrowed: positive( loan['amount-borrowed'] ),
          rate: loan['interest-rate'],
          totalTerm: loan['loan-term'] * 12,
          downPayment: loan['down-payment'],
          closingCosts: loan['closing-costs']
        }).overallCost;
      }
    }
  ]);

  // Cache these for later
  var $form = $('#lc-input-' + id),
      $rateSelect = $('#interest-rate-select-' + id),
      $amount = $('.loan-amount-display-' + id),
      $closing = $('.closing-costs-display-' + id),
      $downPayment = $('.down-payment-display-' + id),
      $lenderFees = $('.lender-fees-display-' + id),
      $discountAmount = $('.discount-display-' + id),
      $processing = $('.processing-fees-display-' + id),
      $thirdPartyFees = $('.third-party-fees-display-' + id),
      $thirdPartyServices = $('.third-party-services-display-' + id),
      $mortgageInsurance = $('.mortgage-insurance-display-' + id),
      $taxesGovFees = $('.taxes-gov-fees-display-' + id),
      $prepaid = $('.prepaid-expenses-display-' + id),
      $initialEscrow = $('.initial-escrow-display-' + id),
      $monthlyPrincipalInterest = $('.monthly-principal-interest-display-' + id),
      $monthlyMortgageInsurance = $('.monthly-mortgage-insurance-display-' + id),
      $monthlyTaxes = $('.monthly-taxes-insurance-display-' + id),
      $monthlyHOA = $('.monthly-hoa-dues-display-' + id),
      $monthly = $('.monthly-payment-display-' + id),
      $loanTerm = $('.loan-term-display-' + id),
      $principalPaid = $('.principal-paid-display-' + id),
      $interestPaid = $('.interest-fees-paid-display-' + id),
      $overall = $('.overall-costs-display-' + id),
      $interest = $('.interest-rate-display-' + id),
      $percent = $('#percent-dp-input-' + id),
      $down = $('#down-payment-input-' + id),
      $discount = $('.discount-' + id),
      $summaryYear = $('.lc-summary-year-' + id),
      $summaryStruct = $('.lc-summary-structure-' + id),
      $summaryType = $('.lc-summary-type-' + id);

  // Keep track of the last down payment field that was accessed.
  var percentDownAccessedLast;

  // Keep track of any api rate request in progress
  var currentRequest;

  function updateComparisons( changes ) {
    var loanDataChanged = false;

    for ( var i = 0, len = changes.length; i < len; i++ ) {
      if ( changes[i].name == 'down-payment' && typeof percentDownAccessedLast !== 'undefined' && !percentDownAccessedLast ) {
        var val = loan['down-payment'] / loan['price'] * 100;
        $percent.val( Math.round(val) );
        percentDownAccessedLast = false;
      }
      // If a user-editable field has changed, rate needs updating
      if (!loanDataChanged && ($.inArray(changes[i].name, editableFields) !== -1)) {
        loanDataChanged = true;
      }
    }

    if (loanDataChanged) {
      if (currentRequest) {
        getRateData();
      } else {
        $form.removeClass('updating').addClass('update');
      }
    }

    updateLoanDisplay();
  }

  function updateLoanDisplay () {
    $amount.text( formatUSD(positive(loan['amount-borrowed']), {decimalPlaces:0}) );
    $closing.text( formatUSD(loan['closing-costs'], {decimalPlaces:0}) );
    $downPayment.text( formatUSD(loan['down-payment'], {decimalPlaces:0}) );
    $lenderFees.text( formatUSD(loan['discount'] + loan['processing'], {decimalPlaces:0}) );
    $discountAmount.text( formatUSD(loan['discount'], {decimalPlaces:0}) );
    $processing.text( formatUSD(loan['processing'], {decimalPlaces:0}) );
    $thirdPartyFees.text( formatUSD(loan['third-party-services'] + loan['mortgage-insurance'], {decimalPlaces:0}) );
    $thirdPartyServices.text( formatUSD(loan['third-party-services'], {decimalPlaces:0}) );
    $mortgageInsurance.text( formatUSD(loan['mortgage-insurance'], {decimalPlaces:0}) );
    $taxesGovFees.text( formatUSD(loan['taxes-gov-fees'], {decimalPlaces:0}) );
    $prepaid.text( formatUSD(loan['prepaid-expenses'], {decimalPlaces:0}) );
    $initialEscrow.text( formatUSD(loan['initial-escrow'], {decimalPlaces:0}) );
    $monthlyPrincipalInterest.text( formatUSD(loan['monthly-principal-interest'], {decimalPlaces:0}) );
    $monthlyMortgageInsurance.text( formatUSD(loan['monthly-mortgage-insurance'], {decimalPlaces:0}) );
    $monthlyTaxes.text( formatUSD(loan['monthly-taxes-insurance'], {decimalPlaces:0}) );
    $monthlyHOA.text( formatUSD(loan['monthly-hoa-dues'], {decimalPlaces:0}) );
    $monthly.text( formatUSD(loan['monthly-payment'], {decimalPlaces:0}) );
    $loanTerm.text( 'Costs at ' + loan['loan-term'] + ' years' );
    $principalPaid.text( formatUSD(loan['principal-paid'], {decimalPlaces:0}) );
    $interestPaid.text( formatUSD(loan['interest-fees-paid'], {decimalPlaces:0}) );
    $overall.text( formatUSD(loan['overall-cost'], {decimalPlaces:0}) );
    $interest.text( loan['interest-rate'] );
    $discount.text( loan['raw-discount']);
    $summaryYear.text( loan['loan-term'] );
    $summaryStruct.text( loan['rate-structure'] );
    $summaryType.text( humanizeLoanType(loan['loan-type']) );
  }

  function getRateData() {
    if (currentRequest && typeof currentRequest === 'object') {
      currentRequest.abort();
    }

    $form.removeClass('update').addClass('updating');

    currentRequest = fetchRates({
      price: loan['price'],
      loan_amount: loan['amount-borrowed'],
      minfico: loan['minfico'],
      maxfico: loan['maxfico'],
      state: loan['location'],
      rate_structure: loan['rate-structure'],
      loan_term: loan['loan-term'],
      loan_type: loan['loan-type'],
      arm_type: loan['arm-type']
    })
    .done(function (results) {
        currentRequest = null;
        var rates = [];
        for ( key in results.data ) {
          if ( results.data.hasOwnProperty( key ) ) {
            rates.push(key);
          }
        }
        rates.sort();
        updateRateSelect(rates);
        loan.update();
        $form.removeClass('updating');
      })
    .fail(function () {
      currentRequest = null;
      $form.removeClass('updating')
           .addClass('update');
    });
  }

  function updateRateSelect(rates) {
    rates || (rates = []);
    var len = rates.length;
    var half = Math.floor((len - 1) / 2);

    $rateSelect.empty();
    $.each(rates, function(ind, rate) {
      var opt = $("<option></option>")
                  .attr("value", rate)
                  .text(rate + '%');
      if (ind === half) {
          opt.attr('selected', 'selected');
      }
      $rateSelect.append(opt);
    });
  }

  function _updateDownPayment( ev ) {
    var targetID = ev.target.id,
        val;

    if ( /percent/.test(targetID) ) {
      val = $percent.val() / 100 * loan.price;
      $down.val( Math.round(val) );
      percentDownAccessedLast = true;
      loan.update();
      return;
    }

    if ( /down\-payment/.test(targetID) ) {
      percentDownAccessedLast = false;
    }

    if ( /house\-price/.test(targetID) && percentDownAccessedLast !== undefined ) {
      if ( percentDownAccessedLast ) {
        val = $percent.val() / 100 * loan.price || 0;
        $down.val( Math.round(val) );
        loan.update();
      } else {
        val = loan['down-payment'] / loan['price'] * 100 || 0;
        $percent.val( Math.round(val) );
        loan.update();
      }
    }
  }

  // The pricing fields (price, dp, dp %) are wonky and require special handling.
  $('#lc-input-' + id).on( 'keyup', '.pricing input', debounce(_updateDownPayment, 500) );

  // update when the radio buttons are updated
  // todo: there's certainly a cleaner way to do this
  $('#points-' + id).on( 'click', 'input', function updatePoints() {
    loan.update();
  });

  // refresh interest rates when update button is clicked
  $('#interest-rate-update-' + id).click(function updateRates(e) {
    e.preventDefault();
    getRateData();
  });

  function init() {
    // update the loan object with values from form
    loan.update();
    $.extend(loan, loanData);
    loans[ id ] = loan;

    // make initial api request for rates
    getRateData();

    // Observe the loan object for changes *only* if the browser supports it.
    // If the browser doesn't support it, do some drrrrty checking.
    if ( supportsAccessors ) {
      Object.observe( loan, updateComparisons );
    } else {
      var oldLoan = $.extend( {}, loan );
      setInterval(function(){
        // TODO: fix this
        if ( JSON.stringify(loan) !== JSON.stringify(oldLoan) ) {
          updateComparisons([]);
          oldLoan = $.extend( {}, loan );
        }
      }, 500);
    }

  }

  init();

  return loans;
}

module.exports = createNewForm;
<commit_msg>fix(loan-comparison): add points to rate requests<commit_after>var $ = require('jquery');
var debounce = require('debounce');
var cost = require('overall-loan-cost');
var objectify = require('objectified');
var formatUSD = require('format-usd');
var unFormatUSD = require('unformat-usd');
var isMoney = require('is-money-usd');
var positive = require('stay-positive');
var amortize = require('amortize');
var humanizeLoanType = require('./humanize-loan-type');
var supportsAccessors = require('./supports-accessors');
var fetchRates = require('./rates');

var loans = {};
var editableFields = ['location', 'minfico', 'maxfico', 'amount-borrowed', 'price', 'discount', 'down-payment', 'rate-structure', 'loan-term', 'loan-type', 'arm-type'];

function createNewForm( id, loanData ) {

  var loan = objectify('#lc-input-' + id, [
    {
      name: 'amount-borrowed',
      source: 'house-price-input - down-payment-input'
    },{
      name: 'price',
      source: 'house-price-input'
    },{
      name: 'down-payment',
      source: 'down-payment-input'
    },{
      name: 'rate-structure',
      source: 'rate-structure-select'
    },{
      name: 'loan-term',
      source: 'loan-term-select'
    },{
      name: 'loan-type',
      source: 'loan-type-select'
    },{
      name: 'arm-type',
      source: function() {
        return $('#arm-type-select-' + id).val();
      }
    },{
      name: 'interest-rate',
      source: 'interest-rate-select'
    },{
      name: 'raw-discount',
      source: function() {
        return $('#points-' + id + ' input:checked' ).val();
      }
    },{
      name: 'discount',
      source: function() {
        var points = loan['raw-discount'] / 100;
        return points * loan['amount-borrowed'];
      }
    },{
      name: 'processing',
      source: function() {
        return loan['amount-borrowed'] / 100;
      }
    },{
      name: 'third-party-services',
      source: function() {
        return 3000;
      }
    },{
      name: 'mortgage-insurance',
      source: function() {
        return 0;
      }
    },{
      name: 'taxes-gov-fees',
      source: function() {
        return 1000;
      }
    },{
      name: 'prepaid-expenses',
      source: function() {
        return 500;
      }
    },{
      name: 'initial-escrow',
      source: function() {
        return 500;
      }
    },{
      name: 'monthly-taxes-insurance',
      source: function() {
        var yearly = loan['price'] / 100,
            propertyTaxes = yearly / 12,
            homeInsurance = (.05 * loan['price']) / 12;
        return propertyTaxes + homeInsurance;
      }
    },{
      name: 'monthly-hoa-dues',
      source: function() {
        return 0;
      }
    },{
      name: 'monthly-principal-interest',
      source: function() {
        return amortize({
          amount: positive( loan['amount-borrowed'] ),
          rate: loan['interest-rate'],
          totalTerm: loan['loan-term'] * 12,
          amortizeTerm: 60 // @todo loan term * 12?
        }).payment;
      }
    },{
      name: 'monthly-mortgage-insurance',
      source: function() {
        return 0;
      }
    },{
      name: 'closing-costs',
      source: function() {
        return loan['down-payment']
             + loan['discount']
             + loan['processing']
             + loan['third-party-services']
             + loan['mortgage-insurance']
             + loan['taxes-gov-fees']
             + loan['prepaid-expenses']
             + loan['initial-escrow'];
      }
    },{
      name: 'monthly-payment',
      source: function() {
        var taxes = loan['monthly-taxes-insurance'],
            insurance = loan['monthly-mortgage-insurance'],
            hoa = loan['monthly-hoa-dues'],
            monthlyPrincipalInterest = loan['monthly-principal-interest'];
        return taxes + insurance + hoa + monthlyPrincipalInterest;
      }
    },{
      name: 'principal-paid',
      source: function() {
        return cost({
          amountBorrowed: positive( loan['amount-borrowed'] ),
          rate: loan['interest-rate'],
          totalTerm: loan['loan-term'] * 12,
          downPayment: loan['down-payment'],
          closingCosts: loan['closing-costs']
        }).totalEquity;
      }
    },{
      name: 'interest-fees-paid',
      source: function() {
        return cost({
          amountBorrowed: positive( loan['amount-borrowed'] ),
          rate: loan['interest-rate'],
          totalTerm: loan['loan-term'] * 12,
          downPayment: loan['down-payment'],
          closingCosts: loan['closing-costs']
        }).totalCost;
      }
    },{
      name: 'overall-cost',
      source: function() {
        return cost({
          amountBorrowed: positive( loan['amount-borrowed'] ),
          rate: loan['interest-rate'],
          totalTerm: loan['loan-term'] * 12,
          downPayment: loan['down-payment'],
          closingCosts: loan['closing-costs']
        }).overallCost;
      }
    }
  ]);

  // Cache these for later
  var $form = $('#lc-input-' + id),
      $rateSelect = $('#interest-rate-select-' + id),
      $amount = $('.loan-amount-display-' + id),
      $closing = $('.closing-costs-display-' + id),
      $downPayment = $('.down-payment-display-' + id),
      $lenderFees = $('.lender-fees-display-' + id),
      $discountAmount = $('.discount-display-' + id),
      $processing = $('.processing-fees-display-' + id),
      $thirdPartyFees = $('.third-party-fees-display-' + id),
      $thirdPartyServices = $('.third-party-services-display-' + id),
      $mortgageInsurance = $('.mortgage-insurance-display-' + id),
      $taxesGovFees = $('.taxes-gov-fees-display-' + id),
      $prepaid = $('.prepaid-expenses-display-' + id),
      $initialEscrow = $('.initial-escrow-display-' + id),
      $monthlyPrincipalInterest = $('.monthly-principal-interest-display-' + id),
      $monthlyMortgageInsurance = $('.monthly-mortgage-insurance-display-' + id),
      $monthlyTaxes = $('.monthly-taxes-insurance-display-' + id),
      $monthlyHOA = $('.monthly-hoa-dues-display-' + id),
      $monthly = $('.monthly-payment-display-' + id),
      $loanTerm = $('.loan-term-display-' + id),
      $principalPaid = $('.principal-paid-display-' + id),
      $interestPaid = $('.interest-fees-paid-display-' + id),
      $overall = $('.overall-costs-display-' + id),
      $interest = $('.interest-rate-display-' + id),
      $percent = $('#percent-dp-input-' + id),
      $down = $('#down-payment-input-' + id),
      $discount = $('.discount-' + id),
      $summaryYear = $('.lc-summary-year-' + id),
      $summaryStruct = $('.lc-summary-structure-' + id),
      $summaryType = $('.lc-summary-type-' + id);

  // Keep track of the last down payment field that was accessed.
  var percentDownAccessedLast;

  // Keep track of any api rate request in progress
  var currentRequest;

  function updateComparisons( changes ) {
    var loanDataChanged = false;

    for ( var i = 0, len = changes.length; i < len; i++ ) {
      if ( changes[i].name == 'down-payment' && typeof percentDownAccessedLast !== 'undefined' && !percentDownAccessedLast ) {
        var val = loan['down-payment'] / loan['price'] * 100;
        $percent.val( Math.round(val) );
        percentDownAccessedLast = false;
      }
      // If a user-editable field has changed, rate needs updating
      if (!loanDataChanged && ($.inArray(changes[i].name, editableFields) !== -1)) {
        loanDataChanged = true;
      }
    }

    if (loanDataChanged) {
      if (currentRequest) {
        getRateData();
      } else {
        $form.removeClass('updating').addClass('update');
      }
    }

    updateLoanDisplay();
  }

  function updateLoanDisplay () {
    $amount.text( formatUSD(positive(loan['amount-borrowed']), {decimalPlaces:0}) );
    $closing.text( formatUSD(loan['closing-costs'], {decimalPlaces:0}) );
    $downPayment.text( formatUSD(loan['down-payment'], {decimalPlaces:0}) );
    $lenderFees.text( formatUSD(loan['discount'] + loan['processing'], {decimalPlaces:0}) );
    $discountAmount.text( formatUSD(loan['discount'], {decimalPlaces:0}) );
    $processing.text( formatUSD(loan['processing'], {decimalPlaces:0}) );
    $thirdPartyFees.text( formatUSD(loan['third-party-services'] + loan['mortgage-insurance'], {decimalPlaces:0}) );
    $thirdPartyServices.text( formatUSD(loan['third-party-services'], {decimalPlaces:0}) );
    $mortgageInsurance.text( formatUSD(loan['mortgage-insurance'], {decimalPlaces:0}) );
    $taxesGovFees.text( formatUSD(loan['taxes-gov-fees'], {decimalPlaces:0}) );
    $prepaid.text( formatUSD(loan['prepaid-expenses'], {decimalPlaces:0}) );
    $initialEscrow.text( formatUSD(loan['initial-escrow'], {decimalPlaces:0}) );
    $monthlyPrincipalInterest.text( formatUSD(loan['monthly-principal-interest'], {decimalPlaces:0}) );
    $monthlyMortgageInsurance.text( formatUSD(loan['monthly-mortgage-insurance'], {decimalPlaces:0}) );
    $monthlyTaxes.text( formatUSD(loan['monthly-taxes-insurance'], {decimalPlaces:0}) );
    $monthlyHOA.text( formatUSD(loan['monthly-hoa-dues'], {decimalPlaces:0}) );
    $monthly.text( formatUSD(loan['monthly-payment'], {decimalPlaces:0}) );
    $loanTerm.text( 'Costs at ' + loan['loan-term'] + ' years' );
    $principalPaid.text( formatUSD(loan['principal-paid'], {decimalPlaces:0}) );
    $interestPaid.text( formatUSD(loan['interest-fees-paid'], {decimalPlaces:0}) );
    $overall.text( formatUSD(loan['overall-cost'], {decimalPlaces:0}) );
    $interest.text( loan['interest-rate'] );
    $discount.text( loan['raw-discount']);
    $summaryYear.text( loan['loan-term'] );
    $summaryStruct.text( loan['rate-structure'] );
    $summaryType.text( humanizeLoanType(loan['loan-type']) );
  }

  function getRateData() {
    if (currentRequest && typeof currentRequest === 'object') {
      currentRequest.abort();
    }

    $form.removeClass('update').addClass('updating');

    currentRequest = fetchRates({
      price: loan['price'],
      loan_amount: loan['amount-borrowed'],
      minfico: loan['minfico'],
      maxfico: loan['maxfico'],
      state: loan['location'],
      rate_structure: loan['rate-structure'],
      loan_term: loan['loan-term'],
      loan_type: loan['loan-type'],
      arm_type: loan['arm-type'],
      points: loan['raw-discount']
    })
    .done(function (results) {
        currentRequest = null;
        var rates = [];
        for ( key in results.data ) {
          if ( results.data.hasOwnProperty( key ) ) {
            rates.push(key);
          }
        }
        rates.sort();
        updateRateSelect(rates);
        loan.update();
        $form.removeClass('updating');
      })
    .fail(function () {
      currentRequest = null;
      $form.removeClass('updating')
           .addClass('update');
    });
  }

  function updateRateSelect(rates) {
    rates || (rates = []);
    var len = rates.length;
    var half = Math.floor((len - 1) / 2);

    $rateSelect.empty();
    $.each(rates, function(ind, rate) {
      var opt = $("<option></option>")
                  .attr("value", rate)
                  .text(rate + '%');
      if (ind === half) {
          opt.attr('selected', 'selected');
      }
      $rateSelect.append(opt);
    });
  }

  function _updateDownPayment( ev ) {
    var targetID = ev.target.id,
        val;

    if ( /percent/.test(targetID) ) {
      val = $percent.val() / 100 * loan.price;
      $down.val( Math.round(val) );
      percentDownAccessedLast = true;
      loan.update();
      return;
    }

    if ( /down\-payment/.test(targetID) ) {
      percentDownAccessedLast = false;
    }

    if ( /house\-price/.test(targetID) && percentDownAccessedLast !== undefined ) {
      if ( percentDownAccessedLast ) {
        val = $percent.val() / 100 * loan.price || 0;
        $down.val( Math.round(val) );
        loan.update();
      } else {
        val = loan['down-payment'] / loan['price'] * 100 || 0;
        $percent.val( Math.round(val) );
        loan.update();
      }
    }
  }

  // The pricing fields (price, dp, dp %) are wonky and require special handling.
  $('#lc-input-' + id).on( 'keyup', '.pricing input', debounce(_updateDownPayment, 500) );

  // update when the radio buttons are updated
  // todo: there's certainly a cleaner way to do this
  $('#points-' + id).on( 'click', 'input', function updatePoints() {
    loan.update();
  });

  // refresh interest rates when update button is clicked
  $('#interest-rate-update-' + id).click(function updateRates(e) {
    e.preventDefault();
    getRateData();
  });

  function init() {
    // update the loan object with values from form
    loan.update();
    $.extend(loan, loanData);
    loans[ id ] = loan;

    // make initial api request for rates
    getRateData();

    // Observe the loan object for changes *only* if the browser supports it.
    // If the browser doesn't support it, do some drrrrty checking.
    if ( supportsAccessors ) {
      Object.observe( loan, updateComparisons );
    } else {
      var oldLoan = $.extend( {}, loan );
      setInterval(function(){
        // TODO: fix this
        if ( JSON.stringify(loan) !== JSON.stringify(oldLoan) ) {
          updateComparisons([]);
          oldLoan = $.extend( {}, loan );
        }
      }, 500);
    }

  }

  init();

  return loans;
}

module.exports = createNewForm;
<|endoftext|>


<commit_before>import { createElement } from 'lwc';
import errRecordLog from 'c/errRecordLog';
import { getObjectInfo } from 'lightning/uiObjectInfoApi';
import { getNavigateCalledWith } from "lightning/navigation";
import { registerSa11yMatcher } from '@sa11y/jest';

import getData from '@salesforce/apex/ERR_Log_CTRL.getData';
jest.mock(
    '@salesforce/apex/ERR_Log_CTRL.getData',
    () => {
        return {
            default: jest.fn(),
        };
    },
    { virtual: true }
);

import { registerLdsTestWireAdapter } from '@salesforce/sfdx-lwc-jest';

const getObjectInfoAdapter = registerLdsTestWireAdapter(getObjectInfo);

const mockGetObjectInfo = require('./data/getObjectInfo.json');
const mockGetData = require('./data/getData.json');
const mockGetDataErrorLogs = require('./data/getDataErrorLogs.json');

const RECORD_ID = "a0900000008MR9bQAG";

describe('c-err-record-log', () => {
    let component;

    beforeAll(() => {
        registerSa11yMatcher();
    });

    beforeEach(() => {
        component = createElement('c-err-record-log', {
            is: errRecordLog,
        });

        getObjectInfoAdapter.emit(mockGetObjectInfo);
    });

    afterEach(() => {
        clearDOM();
        jest.clearAllMocks();
    });

    /***
    * @description Verifies header is always displayed on the page
    */
    it('should display header', () => {
        document.body.appendChild(component);

        const header = component.shadowRoot.querySelector('h1');
        expect(header).not.toBeNull();
        expect(header.textContent).toBe('c.ERR_RecordLogTitle');
    });

    /***
    * @description Verifies Error Log page elements for the specified record
    */
    describe('on data load', () => {

        beforeEach(() => {
            component.recordId = RECORD_ID;
            getData.mockResolvedValue(mockGetData);

            document.body.appendChild(component);
        });

        /***
        * @description Verifies record detail page is displayed when
        * user clicks on the record name breadcrumb
        */
        it("should navigate to the record detail page", async () => {
            return global.flushPromises()
                .then(async () => {
                    const recordViewBreadcrumb = getElement(component, "breadcrumb Record View Page");
                    expect(recordViewBreadcrumb).not.toBeNull();
                    expect(recordViewBreadcrumb.label).toBe(mockGetData.recordName);

                    dispatchClickEvent(recordViewBreadcrumb);
                })
                .then(async () => {
                    const { pageReference } = getNavigateCalledWith();

                    expect(pageReference.type).toBe("standard__recordPage");
                    expect(pageReference.attributes.recordId).toBe(component.recordId);
                    expect(pageReference.attributes.actionName).toBe("view");
                });
        });

        /***
        * @description Verifies record SObject tab is displayed when
        * user clicks on the record SObject name breadcrumb
        */
        it("should navigate to the record SObject page", async () => {
            return global.flushPromises()
                .then(async () => {
                    const recordSObjectBreadcrumb = getElement(component, "breadcrumb Record SObject Page");
                    expect(recordSObjectBreadcrumb).not.toBeNull();
                    expect(recordSObjectBreadcrumb.label).toBe(mockGetData.sObjectLabelPlural);

                    dispatchClickEvent(recordSObjectBreadcrumb);
                })
                .then(async () => {
                    const { pageReference } = getNavigateCalledWith();

                    expect(pageReference.type).toBe("standard__objectPage");
                    expect(pageReference.attributes.objectApiName).toBe(mockGetData.sObjectType);
                    expect(pageReference.attributes.actionName).toBe("list");
                });
        });

        it('should display error log datatable summary', async () => {
            return global.flushPromises().then(async () => {
                const summary = getElement(component, "text Summary");

                expect(summary).not.toBeNull();
                expect(summary.textContent).toBe("c.geTextListViewItemsCount c.geTextListViewSortedBy");
            });
        });

        it('should display no item message when record has no error logs', async () => {
            return global.flushPromises().then(async () => {
                const message = getElement(component, "text No Items Message");

                expect(message).not.toBeNull();
                expect(message.value).toBe("c.commonNoItems");
            });
        });

        it("should be accessible", async () => {
            return global.flushPromises().then(async () => {
                await expect(component).toBeAccessible();
            });
        });
    });

    /***
    * @description Verifies Error Log page elements when
    * the specified record has error logs
    */
    describe('on datatable displaying error logs', () => {

        beforeEach(() => {
            component.recordId = RECORD_ID;
            getData.mockResolvedValue(mockGetDataErrorLogs);

            document.body.appendChild(component);
        });

        it('should not display no item message', async () => {
            return global.flushPromises().then(async () => {
                const message = getElement(component, "text No Items Message");

                expect(message).toBeNull();
            });
        });

        it('should display error logs', async () => {
            return global.flushPromises().then(async () => {
                const datatable = getElement(component, "datatable Logs");

                expect(datatable).not.toBeNull();

                expect(datatable.columns.length).toBe(4);
                expect(datatable.columns[0].fieldName).toBe("logURL");
                expect(datatable.columns[0].label).toBe(
                    mockGetObjectInfo.fields["Name"].label
                );

                expect(datatable.data.length).toBe(1);
                expect(datatable.data[0].Name).not.toBeNull();
                expect(datatable.data[0].Name).toBe(
                    mockGetDataErrorLogs.data[0].Name
                );
            });
        });

        it("should be accessible", async () => {
            return global.flushPromises().then(async () => {
                await expect(component).toBeAccessible();
            });
        });
    });

});

// Helpers
//////////////

/***
* @description Finds and returns element defined by its "data-qa-locator" on the component
*/
const getElement = (component, qaLocator) => {
    const breadcrumb = component.shadowRoot.querySelector('[data-qa-locator="' + qaLocator + '"]');

    return breadcrumb;
}

/***
* @description Mimics user clicking on the element
*/
const dispatchClickEvent = (element) => {
    element.dispatchEvent(
        new CustomEvent('click')
    );
}
<commit_msg>Add test for no access scenario<commit_after>import { createElement } from 'lwc';
import errRecordLog from 'c/errRecordLog';
import { getObjectInfo } from 'lightning/uiObjectInfoApi';
import { getNavigateCalledWith } from "lightning/navigation";
import { registerSa11yMatcher } from '@sa11y/jest';

import getData from '@salesforce/apex/ERR_Log_CTRL.getData';
jest.mock(
    '@salesforce/apex/ERR_Log_CTRL.getData',
    () => {
        return {
            default: jest.fn(),
        };
    },
    { virtual: true }
);

import { registerLdsTestWireAdapter } from '@salesforce/sfdx-lwc-jest';

const getObjectInfoAdapter = registerLdsTestWireAdapter(getObjectInfo);

const mockGetObjectInfo = require('./data/getObjectInfo.json');
const mockGetData = require('./data/getData.json');
const mockGetDataErrorLogs = require('./data/getDataErrorLogs.json');

const RECORD_ID = "a0900000008MR9bQAG";

describe('c-err-record-log', () => {
    let component;

    beforeAll(() => {
        registerSa11yMatcher();
    });

    beforeEach(() => {
        component = createElement('c-err-record-log', {
            is: errRecordLog,
        });

        getObjectInfoAdapter.emit(mockGetObjectInfo);
    });

    afterEach(() => {
        clearDOM();
        jest.clearAllMocks();
    });

    /***
    * @description Verifies header is always displayed on the page
    */
    it('should display header', () => {
        document.body.appendChild(component);

        const header = component.shadowRoot.querySelector('h1');
        expect(header).not.toBeNull();
        expect(header.textContent).toBe('c.ERR_RecordLogTitle');
    });

    /***
    * @description Verifies Error Log page elements for the specified record
    */
    describe('on data load', () => {

        beforeEach(() => {
            component.recordId = RECORD_ID;
            getData.mockResolvedValue(mockGetData);

            document.body.appendChild(component);
        });

        /***
        * @description Verifies record detail page is displayed when
        * user clicks on the record name breadcrumb
        */
        it("should navigate to the record detail page", async () => {
            return global.flushPromises()
                .then(async () => {
                    const recordViewBreadcrumb = getElement(component, "breadcrumb Record View Page");
                    expect(recordViewBreadcrumb).not.toBeNull();
                    expect(recordViewBreadcrumb.label).toBe(mockGetData.recordName);

                    dispatchClickEvent(recordViewBreadcrumb);
                })
                .then(async () => {
                    const { pageReference } = getNavigateCalledWith();

                    expect(pageReference.type).toBe("standard__recordPage");
                    expect(pageReference.attributes.recordId).toBe(component.recordId);
                    expect(pageReference.attributes.actionName).toBe("view");
                });
        });

        /***
        * @description Verifies record SObject tab is displayed when
        * user clicks on the record SObject name breadcrumb
        */
        it("should navigate to the record SObject page", async () => {
            return global.flushPromises()
                .then(async () => {
                    const recordSObjectBreadcrumb = getElement(component, "breadcrumb Record SObject Page");
                    expect(recordSObjectBreadcrumb).not.toBeNull();
                    expect(recordSObjectBreadcrumb.label).toBe(mockGetData.sObjectLabelPlural);

                    dispatchClickEvent(recordSObjectBreadcrumb);
                })
                .then(async () => {
                    const { pageReference } = getNavigateCalledWith();

                    expect(pageReference.type).toBe("standard__objectPage");
                    expect(pageReference.attributes.objectApiName).toBe(mockGetData.sObjectType);
                    expect(pageReference.attributes.actionName).toBe("list");
                });
        });

        it('should display error log datatable summary', async () => {
            return global.flushPromises().then(async () => {
                const summary = getElement(component, "text Summary");

                expect(summary).not.toBeNull();
                expect(summary.textContent).toBe("c.geTextListViewItemsCount c.geTextListViewSortedBy");
            });
        });

        it('should display no item message when record has no error logs', async () => {
            return global.flushPromises().then(async () => {
                const message = getElement(component, "text No Items Message");

                expect(message).not.toBeNull();
                expect(message.value).toBe("c.commonNoItems");
            });
        });

        it("should be accessible", async () => {
            return global.flushPromises().then(async () => {
                await expect(component).toBeAccessible();
            });
        });
    });

    /***
    * @description Verifies Error Log page elements when
    * the specified record has error logs
    */
    describe('on datatable displaying error logs', () => {

        beforeEach(() => {
            component.recordId = RECORD_ID;
            getData.mockResolvedValue(mockGetDataErrorLogs);

            document.body.appendChild(component);
        });

        it('should not display no item message', async () => {
            return global.flushPromises().then(async () => {
                const message = getElement(component, "text No Items Message");

                expect(message).toBeNull();
            });
        });

        it('should display error logs', async () => {
            return global.flushPromises().then(async () => {
                const datatable = getElement(component, "datatable Logs");
                expect(datatable).not.toBeNull();

                expect(datatable.columns.length).toBe(4);
                expect(datatable.columns[0].fieldName).toBe("logURL");
                expect(datatable.columns[0].type).toBe("url");
                expect(datatable.columns[0].label).toBe(
                    mockGetObjectInfo.fields["Name"].label
                );

                expect(datatable.data.length).toBe(1);
                expect(datatable.data[0].Name).not.toBeNull();
                expect(datatable.data[0].Name).toBe(
                    mockGetDataErrorLogs.data[0].Name
                );
            });
        });

        it("should be accessible", async () => {
            return global.flushPromises().then(async () => {
                await expect(component).toBeAccessible();
            });
        });
    });

    /***
    * @description Verifies No Access illustration is displayed when
    * user has no read access on the record SObject or Error SObject
    */
    describe('on no access', () => {

        beforeEach(() => {
            component.recordId = RECORD_ID;

            let mockGetDataNoAccess = JSON.parse(JSON.stringify(mockGetData));
            mockGetDataNoAccess.hasAccess = false;

            getData.mockResolvedValue(mockGetDataNoAccess);

            document.body.appendChild(component);
        });

        it('should display No Access illustration', async () => {
            return global.flushPromises().then(async () => {
                const illustration = getElement(component, "illustration NoAccess");
                expect(illustration).not.toBeNull();

                expect(illustration.title).toBe('c.commonInsufficientPermissions');
                expect(illustration.message).toBe('c.addrCopyConAddBtnFls');
            });
        });

        it("should be accessible", async () => {
            return global.flushPromises().then(async () => {
                await expect(component).toBeAccessible();
            });
        });
    });

});

// Helpers
//////////////

/***
* @description Finds and returns element defined by its "data-qa-locator" on the component
*/
const getElement = (component, qaLocator) => {
    const breadcrumb = component.shadowRoot.querySelector('[data-qa-locator="' + qaLocator + '"]');

    return breadcrumb;
}

/***
* @description Mimics user clicking on the element
*/
const dispatchClickEvent = (element) => {
    element.dispatchEvent(
        new CustomEvent('click')
    );
}
<|endoftext|>