    /*

    Siesta 5.3.0
    Copyright(c) 2009-2019 Bryntum AB
    https://bryntum.com/contact
    https://bryntum.com/products/siesta/license

    */
    Ext.define('Siesta.Project.Browser.UI.TestGrid', {
        extend     : 'Ext.tree.Panel',
        alias      : 'widget.testgrid',

        requires   : [
            'Siesta.Project.Browser.UI.FilterableTreeView',
            'Siesta.Project.Browser.UI.TestGridController',
            'Siesta.Project.Browser.UI.TestNameColumn'
        ],

        controller  : 'testgrid',

        stateful                : true,
        rootVisible             : false,
        header                  : false,
        rowLines                : false,
        useArrows               : true,
        border                  : false,
        cls                     : 'tr-testgrid',
        iconCls                 : 'tr-status-neutral-small',
        width                   : 340,
        collapsible             : true,
        expanded                : true,
        viewType                : 'filterabletreeview',
        enableColumnMove        : false,

        project                 : null,
        lines                   : false,

        filter                  : null,
        filterGroups            : false,

        resultSummary           : null,

        stateConfig             : null,

        showSizeControls        : false,

        isReadOnlyReport        : false,

        menuHideTimeout         : false,

        viewConfig : {
            enableTextSelection : true,
            toggleOnDblClick    : false,
            markDirty           : false,
            trackOver           : false,
            getRowClass         : function(record) {
                if (record.get('descriptor').isSystemDescriptor) {
                    return 'siesta-system-descriptor';
                }
            },

            // Avoid DOM updates when irrelevant
            shouldUpdateCell    : function(record, column, changedFieldNames) {

                if (column.dataIndex === 'passCount' &&
                    changedFieldNames &&
                    !(
                        Ext.Array.contains(changedFieldNames, 'passCount') ||
                        Ext.Array.contains(changedFieldNames, 'todoPassCount')
                    )
                ) {
                    return 0;
                }

                if (column.dataIndex === 'failCount' &&
                    changedFieldNames &&
                    !(
                        Ext.Array.contains(changedFieldNames, 'failCount') ||
                        Ext.Array.contains(changedFieldNames, 'todoFailCount')
                    )
                ) {
                    return 0;
                }

                return Ext.tree.View.prototype.shouldUpdateCell.apply(this, arguments);
            }
        },

        initComponent : function () {
            var me      = this;
            var R       = Siesta.Resource('Siesta.Project.Browser.UI.TestGrid');
            var state   = this.stateConfig;

            Ext.apply(this, {
                title      : R.get('title'),

                columns : {
                    // Hack, prevent Ext JS grid column to react to click/keys in filter field
                    createFocusableContainerKeyNav  : Ext.emptyFn,
                    // EOF Hack
                    items                        : [
                        {
                            xtype   : 'testnamecolumn',
                            store   : this.store,
                            project : this.project
                        },
                        {
                            header       : R.get('passText'),
                            width        : 35,
                            sortable     : false,
                            tdCls        : 'x-unselectable result-cell',
                            menuDisabled : true,
                            dataIndex    : 'passCount',
                            align        : 'center',
                            renderer     : this.passedColumnRenderer,
                            scope        : this
                        },
                        {
                            header       : R.get('failText'),
                            width        : 35,
                            sortable     : false,
                            tdCls        : 'x-unselectable result-cell',
                            menuDisabled : true,
                            dataIndex    : 'failCount',
                            align        : 'center',
                            renderer     : this.failedColumnRenderer,
                            scope        : this
                        },
                        {
                            header      : 'Time',
                            width       : 50,
                            sortable    : false,
                            dataIndex   : 'time',
                            align       : 'center',
                            renderer    : function (value, meta, record) {
                                if (!record.isLeaf()) return ''
                                return value + 'ms';
                            },
                            hidden      : !(this.project.showTestDurationColumn || this.isReadOnlyReport)
                        }
                    ]
                },

                bbar : {
                    xtype    : 'toolbar',
                    cls      : 'main-bbar siesta-toolbar',
                    border   : false,
                    height   : 45,
                    defaults : {
                        scale       : 'large',
                        width       : 30,
                        tooltipType : 'title'
                    },

                    items : this.getBottomBarItems(state, R)
                },

                dockedItems : this.showSizeControls ? [
                    {
                        xtype  : 'toolbar',
                        cls    : 'size-toolbar',
                        border : true,
                        dock   : 'bottom',
                        items  : [
                            {
                                xtype     : 'slider',
                                itemId    : 'framesizeSlider',
                                width     : 130,
                                value     : 3,
                                increment : 1,
                                minValue  : 0,
                                maxValue  : this.viewportSizes.length - 1,
                                listeners : {
                                    change : this.onDimensionOrOrientationChange,
                                    scope  : this
                                }
                            },
                            {
                                xtype  : 'label',
                                cls    : 'size-label',
                                itemId : 'sizeLabel',
                                width  : 65
                            },
                            {
                                boxLabel  : R.get('landscape'),
                                itemId    : 'orientationCheckbox',
                                xtype     : 'checkbox',
                                checked   : true,
                                listeners : {
                                    change : this.onDimensionOrOrientationChange,
                                    scope  : this
                                }
                            }
                        ]
                    }
                ] : []
            })

            this.callParent(arguments);

            this.getView().on('beforerefresh', function () {
                var trigger = me.down('#trigger')

                if (me.filterGroups)    trigger.setFilterGroups(me.filterGroups)
                if (me.filter)          trigger.setValue(me.filter)

                // cancel refresh if there's a filter - in this case an additional refresh will be triggered by
                // the filtering which will be already not canceled since this is 1 time listener
                return !me.filter
            }, null, { single : true })

            this.on('viewready', this.onMyViewReady, this, { delay : 10 });
        },

        getBottomBarItems : function (state, R) {
            var items       = []

            if (!this.isReadOnlyReport) {
                items.push(
                    {
                        glyph      : 0xf04b,
                        cls        : 'run-checked',
                        text       : '<span class="subicon fa-check"></span>',
                        tooltip    : R.get('runCheckedText'),
                        actionName : 'run-checked'
                    },
                    {
                        glyph      : 0xf04e,
                        cls        : 'run-all',
                        tooltip    : R.get('runAllText'),
                        actionName : 'run-all'
                    },
                    {
                        glyph      : 0xf04b,
                        cls        : 'run-failed',
                        text       : '<span class="subicon fa-bug"></span>',
                        tooltip    : R.get('runFailedText'),
                        actionName : 'run-failed'
                    },
                    {
                        glyph   : 0xf013,
                        tooltip : R.get('optionsText'),
                        cls     : 'options',
                        action  : 'options',

                        listeners : {
                            mouseover       : this.onOptionsButtonMouseOver,
                            mouseout        : this.onOptionsButtonMouseOut,
                            scope           : this
                        },

                        menu    : {
                            itemId : 'tool-menu',

                            listeners   : {
                                mouseenter  : {
                                    fn      : this.onOptionsMenuMouseEnter,
                                    // delay to cancel the hide _after_ the "mouseleave" from the sub-menu
                                    delay   : 100
                                },
                                mouseover   : {
                                    fn      : this.onOptionsMenuMouseEnter,
                                    delay   : 100
                                },
                                mouseleave  : this.onOptionsMenuMouseLeave,
                                scope       : this
                            },

                            items  : [
                                {
                                    text    : R.get('mouseSimulation'),
                                    iconCls : 'x-fa fa-mouse-pointer',
                                    menu    : {
                                        listeners   : {
                                            mouseenter  : this.onOptionsMenuMouseEnter,
                                            mouseleave  : this.onOptionsMenuMouseLeave,
                                            scope       : this
                                        },
                                        items   : [
                                            {
                                                text    : "Slow (accurate)",
                                                option  : 'mouseSimSlow',
                                                checked : !state.speedRun && !state.superSpeedRun,

                                                group   : 'mouse-sim',

                                                tooltip : "Slow and accurate mouse simulation"
                                            },
                                            {
                                                text    : "Fast (accurate)",
                                                option  : 'mouseSimFast',
                                                checked : state.speedRun && !state.superSpeedRun,

                                                group   : 'mouse-sim',

                                                tooltip : "Fast, but still accurate mouse simulation"
                                            },
                                            {
                                                text    : "Fastest (not accurate)",
                                                option  : 'mouseSimFastest',
                                                checked : state.superSpeedRun,

                                                group   : 'mouse-sim',

                                                tooltip : R.get('superSpeedRunTooltip')
                                            }
                                        ]
                                    }
                                },
                                {
                                    text    : R.get('transparentExText'),
                                    option  : 'transparentEx',
                                    checked : state.transparentEx,

                                    tooltip : R.get('transparentExTooltip')
                                },
    //                            {
    //                                text    : R.get('cachePreloadsText'),
    //                                option  : 'cachePreload',
    //                                checked : state.cachePreload
    //                            },
                                {
                                    text    : R.get('breakOnFailText'),
                                    option  : 'breakOnFail',
                                    checked : state.breakOnFail,

                                    tooltip : R.get('breakOnFailTooltip')
                                },
                                {
                                    text    : R.get('debuggerOnFailText'),
                                    option  : 'debuggerOnFail',
                                    checked : state.debuggerOnFail,

                                    tooltip : R.get('debuggerOnFailTooltip')
                                },
                                {
                                    itemId  : 'observerModeMenuItem',

                                    text    : R.get('observerModeText'),
                                    option  : 'observerMode',
                                    checked : state.observerMode,

                                    tooltip : R.get('observerModeTooltip')
                                },
                                {
                                    text    : R.get('autoLaunchText'),
                                    option  : 'autoRun',
                                    checked : state.autoRun,

                                    tooltip : R.get('autoLaunchTooltip')
                                },
                                { xtype : 'menuseparator' },
                                {
                                    text   : R.get('aboutText'),
                                    itemId : 'aboutSiesta'
                                },
                                {
                                    text       : R.get('documentationText'),
                                    href       : R.get('siestaDocsUrl'),
                                    hrefTarget : '_blank'
                                }
                            ]
                        }
                    }
                )
            }

            items.push(
                '->',
                {
                    xtype  : 'component',
                    cls    : 'summary-bar',
                    border : false,
                    width  : 55,
                    itemId : 'result-summary',
                    data   : {
                        pass : 0,
                        fail : 0
                    },
                    tpl    : '<div><span class="total-pass">{pass}</span><span class="icon fa-check"></span></div><div><span class="total-fail">{fail}</span><span class="icon fa-bug"></span></div>'
                }
            )

            return items
        },

        onHistoryChange : function(token) {
            if (token) {
                var testFile = this.store.getNodeById(token)

                if (testFile) {
                    this.selectTestFile(testFile);
                }
            }
        },

        onDimensionOrOrientationChange : function (slider, val) {
            var newSize = this.viewportSizes[this.framesizeSlider.getValue()];
            var landscape = this.orientationCheckbox.getValue();

            this.sizeLabel.setText(newSize.join('x'));
            this.fireEvent('framesizechange', slider, newSize[0], newSize[1], landscape);
        },

        getFilterValue : function () {
            return this.down('#trigger').getValue()
        },

        getFilterGroups : function () {
            return this.down('#trigger').getFilterGroups()
        },

        passedColumnRenderer : function (value, meta, record) {
            if (!record.isLeaf()) return ''

            if (record.data.todoPassCount > 0) {
                value += ' <span title="' + record.data.todoPassCount + ' ' + Siesta.Resource('Siesta.Project.Browser.UI.TestGrid', 'todoPassedText') + '" class="tr-test-todo tr-test-todo-pass">+ ' + record.data.todoPassCount + '</span>';
            }

            return value;
        },

        failedColumnRenderer : function (value, meta, record) {
            if (!record.isLeaf()) return ''

            if (record.data.todoFailCount > 0) {
                value += ' <span title="' + record.data.todoFailCount + ' ' + Siesta.Resource('Siesta.Project.Browser.UI.TestGrid', 'todoFailedText') + '" class="tr-test-todo tr-test-todo-fail">+ ' + record.data.todoFailCount + '</span>';
            }
            return value;
        },

        onOptionsButtonMouseOver : function (button) {
            clearTimeout(this.menuHideTimeout)

            button.showMenu()

            button.menu.hide = function () {}
            setTimeout(function () {
                delete button.menu.hide
            }, 1000)
        },

        onOptionsButtonMouseOut : function (button) {
            var me      = this

            if (this.menuHideTimeout) return

            this.menuHideTimeout = setTimeout(function () {
                delete button.menu.hide
                button.hideMenu()

                me.menuHideTimeout  = null
            }, 500)
        },

        onOptionsMenuMouseEnter : function (menu) {
            clearTimeout(this.menuHideTimeout)

            this.menuHideTimeout    = null
        },

        onOptionsMenuMouseLeave : function () {
            var me          = this
            var menu        = this.down('#tool-menu')

            if (this.menuHideTimeout) return

            this.menuHideTimeout = setTimeout(function () {
                delete menu.hide
                menu.hide()

                me.menuHideTimeout  = null
            }, 500)
        },

        afterRender : function () {
            this.callParent(arguments);

            this.summaryPassEl  = this.el.down('.total-pass');
            this.summaryFailEl  = this.el.down('.total-fail');

            if (this.showSizeControls) {
                this.orientationCheckbox    = this.down('#orientationCheckbox');
                this.sizeLabel              = this.down('#sizeLabel');
                this.framesizeSlider        = this.down('#framesizeSlider');

                var size                    = this.viewportSizes[ this.framesizeSlider.getValue() ];

                this.sizeLabel.setText(size.join('x'));
            }
        },

        onMyViewReady : function() {
            Ext.History.on('change', this.onHistoryChange, this);

            var hash = window.location.hash;

            if (hash) {
                this.onHistoryChange(hash.substring(1));
            }
        },

        updateStatus : function (pass, fail) {
            this.summaryPassEl.update(String(pass));
            this.summaryFailEl.update(String(fail));
        },

        setFilterValue : function (value) {
            this.down('treefilter').setValue(value)
        },

        selectTestFile : function(testFile) {
            if (testFile) {
                testFile.bubble(function(parent) { parent.expand(); });

                // Make sure test is not filtered out
                if (this.store.getById(testFile.id) && this.store.indexOf(testFile) >= 0 ) {
                    if (!this.getCollapsed() && this.getView().getNodes().length > 0) {
                        this.ensureVisible(testFile);
                    }
                    this.getSelectionModel().select(testFile)
                }
            }
        }
    })