define([
    'app',
    'js/templates-loader',
    'moment',
    'modules/modal2',
    'js/d3v4/d3.v4',
    'components/ag-grid/ag-grid-module'
],
    function (app, T, moment, Modal, d3, AgGridModule) {
        var Screen = {
            Models: {},
            Views: {},
            Collections: {},
        };

        Screen.Models.Main = Backbone.Epoxy.Model.extend({
            defaults: {

                productionDate: new moment().subtract(1, 'days').format('MM/DD/YYYY'),

                shiftId: 'D',
                crewId: 'C',
                shifts: [],
                hasData: true,
                isLoading: false,
                tphValues: null,

                showFurnace2Data: true,
                showFurnace1Data: true,

                hasData_yield: false,
                chartsWidth: 450,
                chartHeight: 310,
                heightOffset: 280,
                isLoading_Temperature: false,
                hasData_Temperature: false,
                hasData_temperature_EAFA: false,
                hasData_temperature_EAFB: false,
                hasData_oxygen_EAFA: false,
                hasData_oxygen_EAFB: false,

                hasData_residuals_EAFA: false,
                hasData_residuals_EAFB: false,
                hasData_residuals_EAFA_2: false,
                hasData_residuals_EAFB_2: false,

                hasData_bucket_fluxes_EAFA: false,
                hasData_bucket_fluxes_EAFB: false,

                isLoading_bucket_fluxes_EAFA: true,
                isLoading_bucket_fluxes_EAFB: true,

                hasData_injection_fluxes_EAFA: false,
                hasData_injection_fluxes_EAFB: false,

                isLoading_injection_fluxes_EAFA: true,
                isLoading_injection_fluxes_EAFB: true,

                hasData_baghouse: false,

                hasData_Recipes: false,
                hasData_RecipesAverage: false,

                hasData_manualTemps: false,
                hasData_tapHoleNonFreeOpen: false,

                hasData_EAF: true,
                isLoading_EAF: false,

                hasData_EAF_A: true,
                hasData_EAF_B: true,
                hasData_AllHeats_EAF_A: true,
                hasData_AllHeats_EAF_B: true,

                renderCompleted: false,
                isPageLoading: true,

                EAF_A_TotalHeats: '-'
                , EAF_A_TotalSteelWeight: '-'
                , EAF_A_TotalScrapWeight: '-'
                , EAF_A_TotalElectricEnergy: '-'
                , EAF_A_TotalElectrodeAdds: '-'
                , EAF_A_SteelWeight: '-'
                , EAF_A_ScrapWeight: '-'
                , EAF_A_Fluxes: '-'
                , EAF_A_Yield: '-'
                , EAF_A_TapToTap: '-'
                , EAF_A_PowerOn: '-'
                , EAF_A_InjectedC: '-'
                , EAF_A_EAFLastTemperature: '-'
                , EAF_A_ElectricEnergy: '-'
                , EAF_A_SpecificEnergy: '-'
                , EAF_A_ElectricPower: '-'
                , EAF_A_SmartARC: '-'
                , EAF_B_TotalHeats: '-'
                , EAF_B_TotalSteelWeight: '-'
                , EAF_B_TotalScrapWeight: '-'
                , EAF_B_TotalElectricEnergy: '-'
                , EAF_B_TotalElectrodeAdds: '-'
                , EAF_B_SteelWeight: '-'
                , EAF_B_ScrapWeight: '-'
                , EAF_B_Fluxes: '-'
                , EAF_B_Yield: '-'
                , EAF_B_TapToTap: '-'
                , EAF_B_PowerOn: '-'
                , EAF_B_InjectedC: '-'
                , EAF_B_EAFLastTemperature: '-'
                , EAF_B_ElectricEnergy: '-'
                , EAF_B_SpecificEnergy: '-'
                , EAF_B_ElectricPower: '-'
                , EAF_B_SmartARC: '-'
                , TOTAL_TotalHeats: '-'
                , TOTAL_TotalSteelWeight: '-'
                , TOTAL_TotalScrapWeight: '-'
                , TOTAL_TotalElectricEnergy: '-'
                , TOTAL_TotalElectrodeAdds: '-'
                , TOTAL_SteelWeight: '-'
                , TOTAL_ScrapWeight: '-'
                , TOTAL_Fluxes: '-'
                , TOTAL_Yield: '-'
                , TOTAL_TapToTap: '-'
                , TOTAL_PowerOn: '-'
                , TOTAL_InjectedC: '-'
                , TOTAL_EAFLastTemperature: '-'
                , TOTAL_ElectricEnergy: '-'
                , TOTAL_SpecificEnergy: '-'
                , TOTAL_ElectricPower: '-'
                , TOTAL_SmartARC: '-'
                , TOTAL_SteelWeightSTD: '-'
                , TOTAL_ScrapWeightSTD: '-'
                , TOTAL_YieldSTD: '-'
                , TOTAL_TapToTapSTD: '-'
                , TOTAL_PowerOnSTD: '-'
                , TOTAL_SpecificEnergySTD: '-'

                , Structural_B3Max: '-'
                , Structural_B3Min: '-'
                , Structural_FeOMax: '-'
                , Structural_FeOMin: '-'
                , Structural_MgOMax: '-'
                , Structural_MgOMin: '-'
                , Rail_B3Max: '-'
                , Rail_B3Min: '-'
                , Rail_FeOMax: '-'
                , Rail_FeOMin: '-'
                , Rail_MgOMax: '-'
                , Rail_MgOMin: '-'

                , EAFASprayRingEffectiveness: '-'
                , EAFBSprayRingEffectiveness: '-'
                , EAFABypass: '-'

            },
            initialize: function () {
                this.itemsAllHeats = new Screen.Collections.ItemsAllHeats();
                this.itemsAllDelays = new Screen.Collections.ItemsAllDelays();
                this.itemsTemperature = new Screen.Collections.ItemsTemperature();
                this.itemsInjectionFluxesEAFA = new Screen.Collections.InjectionFluxesEAFA();
                this.itemsInjectionFluxesEAFB = new Screen.Collections.InjectionFluxesEAFB();
                this.itemsBucketFluxesEAFA = new Screen.Collections.BucketFluxesEAFA();
                this.itemsBucketFluxesEAFB = new Screen.Collections.BucketFluxesEAFB();
                this.itemsSlag = new Screen.Collections.ItemsSlag();
                this.itemsBaghouse = new Screen.Collections.ItemsBaghouse();
                this.itemsDelays = new Screen.Collections.ItemsDelays();
                this.itemsRecipes = new Screen.Collections.ItemsRecipes();
                this.itemsRecipesAverage = new Screen.Collections.ItemsRecipesAverage();
                this.itemsLogBook = new Screen.Collections.ItemsLogBook();
                this.itemsManualTemps = new Screen.Collections.ItemsManualTemps();
                this.itemsTapHoleNonFreeOpen = new Screen.Collections.TapHoleNonFreeOpen();
                this.itemsYield = new Screen.Collections.ItemsYield();
                this.srdCategories = new Screen.Collections.SRDCategories();
            },

            computeds: {
                structuralAims: {
                    deps: ['Structural_B3Max', 'Structural_B3Min', 'Structural_FeOMax', 'Structural_FeOMin', 'Structural_MgOMax', 'Structural_MgOMin'],
                    get: function (B3Max, B3Min, FeOMax, FeOMin, MgOMax, MgOMin) {
                        return `Structural Aims:  B3 ratio ${B3Min} - ${B3Max} | MgO ${MgOMin} - ${MgOMax} | FeO ${FeOMin} - ${FeOMax} `
                    },
                },
                railAims: {
                    deps: ['Rail_B3Max', 'Rail_B3Min', 'Rail_FeOMax', 'Rail_FeOMin', 'Rail_MgOMax', 'Rail_MgOMin'],
                    get: function (B3Max, B3Min, FeOMax, FeOMin, MgOMax, MgOMin) {
                        return `Rail Aims:  B3 ratio ${B3Min} - ${B3Max} | MgO ${MgOMin} - ${MgOMax} | FeO ${FeOMin} - ${FeOMax} `
                    },
                },
                currentShift: {
                    deps: ['shiftId', 'productionDate', 'crewId'],
                    get: function (shiftId, productionDate, crewId) {
                        let shift = shiftId === 'D' ? 'Day Shift' : 'Night Shift';
                        let crew = `${crewId} Crew`;
                        return `${productionDate} | ${shift} | ${crew}`
                    },
                },
                SteelWeightConcat: {
                    deps: ['TOTAL_SteelWeight', 'TOTAL_SteelWeightSTD'],
                    get: function (avg, std) { return avg + ' | ' + std },
                },
                ScrapWeightConcat: {
                    deps: ['TOTAL_ScrapWeight', 'TOTAL_ScrapWeightSTD'],
                    get: function (avg, std) { return avg + ' | ' + std },
                },
                YieldConcat: {
                    deps: ['TOTAL_Yield', 'TOTAL_YieldSTD'],
                    get: function (avg, std) { return avg + ' | ' + std },
                },
                PowerOnConcat: {
                    deps: ['TOTAL_PowerOn', 'TOTAL_PowerOnSTD'],
                    get: function (avg, std) { return avg + ' | ' + std },
                },
                TapToTapConcat: {
                    deps: ['TOTAL_TapToTap', 'TOTAL_TapToTapSTD'],
                    get: function (avg, std) { return avg + ' | ' + std },
                },
                SpecificEnergyConcat: {
                    deps: ['TOTAL_SpecificEnergy', 'TOTAL_SpecificEnergySTD'],
                    get: function (avg, std) { return avg + ' | ' + std },
                },
            },

            fetchData: function (params) {
                var that = this,
                    qp = new Core.Database.QueryParameters();

                this.fixedParameters = [
                    { Name: '@ProductionDate', Type: 'DATE', Value: params.productionDate, },
                    { Name: '@ShiftId', Type: 'INT', Value: params.shiftId == '1' ? 1 : (params.shiftId == '2' ? 2 : null), },
                    { Name: '@timeZoneCode', Type: 'CHAR(10)', Value: app.models.user.get('timezoneCode') },
                ];

                _.each(this.fixedParameters, function (qpParams) {
                    qp.Add(qpParams.Name, qpParams.Type, qpParams.Value);
                });

                Core.Json.CallProcedure(
                    app.DatabaseNames.MES + '.EAF.GetShiftReport',
                    qp,
                    {
                        onSuccess: function (resp) {
                            try {
                                if (resp) {
                                    if (resp.Table) that.totalsAndAverages(resp.Table);

                                    if (resp.Table1) {
                                        const mergedModels = {};
                                        resp.Table1.forEach(model => {
                                            const materialName = model['Material'];
                                            if (!mergedModels[materialName]) {
                                                mergedModels[materialName] = { ...model };
                                            } else {
                                                Object.keys(model).forEach(key => {
                                                    if (key !== 'Material') {
                                                        if (model[key] !== null) {
                                                            mergedModels[materialName][key] = model[key];
                                                        }
                                                    }
                                                });
                                            }
                                        });
                                        const mergedList = Object.values(mergedModels);
                                        that.itemsRecipes.setDataColl(mergedList);
                                    }

                                    if (resp.Table2) that.itemsDelays.setDataColl(resp.Table2);
                                    if (resp.Table3) that.itemsLogBook.setDataColl(resp.Table3);

                                    if (resp.Table4) that.itemsAllHeats.setDataColl(resp.Table4, params.shiftId, params.productionDate);
                                    that.set({
                                        showFurnace1Data: _.where(resp.Table4, { Furnace: 'EAF A' }) ? true : false,
                                        showFurnace2Data: _.where(resp.Table4, { Furnace: 'EAF B' }) ? true : false,
                                    });
                                    if (resp.Table5) that.itemsAllDelays.setDataColl(resp.Table5, params.shiftId, params.productionDate);

                                    if (resp.Table6) that.itemsTemperature.setDataColl(resp.Table6);

                                    if (resp.Table9) that.itemsInjectionFluxesEAFA.setDataColl(resp.Table9);
                                    if (resp.Table10) that.itemsInjectionFluxesEAFB.setDataColl(resp.Table10);

                                    if (resp.Table7) that.itemsBucketFluxesEAFA.setDataColl(resp.Table7);
                                    if (resp.Table8) that.itemsBucketFluxesEAFB.setDataColl(resp.Table8);

                                    if (resp.Table11) that.itemsSlag.setDataColl(resp.Table11);
                                    if (resp.Table12) that.itemsBaghouse.setDataColl(resp.Table12);

                                    if (resp.Table13) {
                                        that.set({ hasData_yield: true });
                                        that.itemsYield.setDataColl(resp.Table13);
                                    }
                                    if (resp.Table14 && resp.Table14[0]) {
                                        that.set({
                                            EAFASprayRingEffectiveness: resp.Table14[0].EAFASprayRingEffectiveness
                                            , EAFBSprayRingEffectiveness: resp.Table14[0].EAFBSprayRingEffectiveness
                                            , EAFABypass: `${resp.Table14[0].EAFABypassBreakerUtilization || resp.Table14[0].EAFABypassBreakerUtilization === 0 ? resp.Table14[0].EAFABypassBreakerUtilization : '-'} | ${resp.Table14[0].EAFAConditionsBypassBreakerMaintained || resp.Table14[0].EAFAConditionsBypassBreakerMaintained === 0 ? resp.Table14[0].EAFAConditionsBypassBreakerMaintained : '-'} `
                                        })
                                    }
                                    if (resp.Table15) that.srdCategories.setDataColl(resp.Table15);
                                    if (resp.Table16) that.itemsRecipesAverage.setDataColl(resp.Table16);
                                    if (resp.Table17) that.itemsManualTemps.setDataColl(resp.Table17.filter(x => x.ManualTemp != 0));
                                    if (resp.Table18) that.itemsTapHoleNonFreeOpen.setDataColl(resp.Table18);
                                    that.trigger('fetch', resp);
                                }
                                else {
                                    if ((resp) && (resp.Message))
                                        console.error(new Error(resp.Message).stack);
                                    else
                                        console.error(new Error('SERVER_RESPONSE_NOT_VALID').stack);
                                    that.set({ isLoading: false, hasData: false });
                                }
                            }
                            catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
                        },
                        onFailure: function (resp) { console.error(resp); },
                        Secured: true,
                        Async: true,
                    },
                    app.ConnectionStrings.app
                );
                return this;
            },

            fetchPickers: function (params, options) {
                var that = this,
                    qp = new Core.Database.QueryParameters(),
                    opt = _.extend({ async: true, }, options);

                Core.Json.CallProcedure(
                    app.DatabaseNames.MES + '.EAF.GetShiftReportPickers',
                    qp,
                    {
                        onSuccess: function (resp) {
                            try {
                                if ((resp) && (resp.Table)) {
                                    _.each(resp.Table, function (obj) {
                                        var tableKey = 'Table' + obj.Index;
                                        switch (obj.Name) {
                                            case 'SHIFTS':
                                                that.set('shifts', _.map(resp[tableKey], function (obj) {
                                                    return { value: obj.Shift, label: obj.Shift };
                                                }));
                                                break;
                                        }
                                    });
                                }
                                if ((resp) && (resp.Table3)) {
                                    _.each(resp.Table3, function (obj) {
                                        if (obj.Name.includes('Struct')) {
                                            switch (obj.Code) {
                                                case 'SLAG_B3_MAX_LIMIT': that.set('Structural_B3Max', obj.Value); break;
                                                case 'SLAG_B3_MIN_LIMIT': that.set('Structural_B3Min', obj.Value); break;
                                                case 'SLAG_FeO_MAX_LIMIT': that.set('Structural_FeOMax', obj.Value); break;
                                                case 'SLAG_FeO_MIN_LIMIT': that.set('Structural_FeOMin', obj.Value); break;
                                                case 'SLAG_MgO_MAX_LIMIT': that.set('Structural_MgOMax', obj.Value); break;
                                                case 'SLAG_MgO_MIN_LIMIT': that.set('Structural_MgOMin', obj.Value); break;
                                            }
                                        }
                                        if (obj.Name.includes('Rail')) {
                                            switch (obj.Code) {
                                                case 'SLAG_B3_MAX_LIMIT': that.set('Rail_B3Max', obj.Value); break;
                                                case 'SLAG_B3_MIN_LIMIT': that.set('Rail_B3Min', obj.Value); break;
                                                case 'SLAG_FeO_MAX_LIMIT': that.set('Rail_FeOMax', obj.Value); break;
                                                case 'SLAG_FeO_MIN_LIMIT': that.set('Rail_FeOMin', obj.Value); break;
                                                case 'SLAG_MgO_MAX_LIMIT': that.set('Rail_MgOMax', obj.Value); break;
                                                case 'SLAG_MgO_MIN_LIMIT': that.set('Rail_MgOMin', obj.Value); break;
                                            }
                                        }
                                    });
                                }
                                else {
                                    if ((resp) && (resp.Message)) console.error(new Error(resp.Message).stack);
                                    else console.error(new Error('SERVER_RESPONSE_NOT_VALID').stack);
                                }
                            }
                            catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
                        },
                        onFailure: function (resp) { console.error(resp); },
                        Secured: true,
                        Async: opt.async,
                    },
                    app.ConnectionStrings.app
                );
                return this;
            },

            totalsAndAverages: function (data) {
                var hasData = false;

                if (data.length > 0) {
                    this.set({ crewId: data[0].CrewId });
                    if (data.length == 3) {
                        this.set({
                            hasData_EAF_A: true,
                            hasData_EAF_B: true,
                            CREW_ID: data[0].CrewId ? data[0].CrewId : '-'

                            , EAF_A_TotalHeats: data[0].TotalHeats ? data[0].TotalHeats : '-'
                            , EAF_A_TotalSteelWeight: data[0].TotalSteelWeight ? data[0].TotalSteelWeight.toFixed(1) : '-'
                            , EAF_A_TotalScrapWeight: data[0].TotalScrapWeight ? data[0].TotalScrapWeight.toFixed(1) : '-'
                            , EAF_A_TotalElectricEnergy: data[0].TotalElectricEnergy ? data[0].TotalElectricEnergy.toFixed(1) : '-'
                            , EAF_A_TotalElectrodeAdds: data[0].TotalElectrodeAdds ? data[0].TotalElectrodeAdds : '-'
                            , EAF_A_SteelWeight: data[0].SteelWeight ? data[0].SteelWeight.toFixed(1) : '-'
                            , EAF_A_ScrapWeight: data[0].ScrapWeight ? data[0].ScrapWeight.toFixed(1) : '-'
                            , EAF_A_Fluxes: data[0].Fluxes ? data[0].Fluxes.toFixed(1) : '-'
                            , EAF_A_Yield: data[0].Yield ? data[0].Yield.toFixed(1) : '-'
                            , EAF_A_TapToTap: data[0].TapToTap ? data[0].TapToTap.toFixed(1) : '-'
                            , EAF_A_PowerOn: data[0].PowerOn ? data[0].PowerOn.toFixed(1) : '-'
                            , EAF_A_InjectedC: data[0].InjectedC ? data[0].InjectedC.toFixed(1) : '-'
                            , EAF_A_EAFLastTemperature: data[0].EAFLastTemperature ? data[0].EAFLastTemperature.toFixed(1) : '-'
                            , EAF_A_ElectricEnergy: data[0].ElectricEnergy ? data[0].ElectricEnergy.toFixed(1) : '-'
                            , EAF_A_SpecificEnergy: data[0].SpecificEnergy ? data[0].SpecificEnergy.toFixed(1) : '-'
                            , EAF_A_ElectricPower: data[0].ElectricPower ? data[0].ElectricPower.toFixed(1) : '-'
                            , EAF_A_SmartARC: data[0].SmartARC ? data[0].SmartARC : '-'

                            , EAF_B_TotalHeats: data[1].TotalHeats ? data[1].TotalHeats : '-'
                            , EAF_B_TotalSteelWeight: data[1].TotalSteelWeight ? data[1].TotalSteelWeight.toFixed(1) : '-'
                            , EAF_B_TotalScrapWeight: data[1].TotalScrapWeight ? data[1].TotalScrapWeight.toFixed(1) : '-'
                            , EAF_B_TotalElectricEnergy: data[1].TotalElectricEnergy ? data[1].TotalElectricEnergy.toFixed(1) : '-'
                            , EAF_B_TotalElectrodeAdds: data[1].TotalElectrodeAdds ? data[1].TotalElectrodeAdds : '-'
                            , EAF_B_SteelWeight: data[1].SteelWeight ? data[1].SteelWeight.toFixed(1) : '-'
                            , EAF_B_ScrapWeight: data[1].ScrapWeight ? data[1].ScrapWeight.toFixed(1) : '-'
                            , EAF_B_Fluxes: data[1].Fluxes ? data[1].Fluxes.toFixed(1) : '-'
                            , EAF_B_Yield: data[1].Yield ? data[1].Yield.toFixed(1) : '-'
                            , EAF_B_TapToTap: data[1].TapToTap ? data[1].TapToTap.toFixed(1) : '-'
                            , EAF_B_PowerOn: data[1].PowerOn ? data[1].PowerOn.toFixed(1) : '-'
                            , EAF_B_InjectedC: data[1].InjectedC ? data[1].InjectedC.toFixed(1) : '-'
                            , EAF_B_EAFLastTemperature: data[1].EAFLastTemperature ? data[1].EAFLastTemperature.toFixed(1) : '-'
                            , EAF_B_ElectricEnergy: data[1].ElectricEnergy ? data[1].ElectricEnergy.toFixed(1) : '-'
                            , EAF_B_SpecificEnergy: data[1].SpecificEnergy ? data[1].SpecificEnergy.toFixed(1) : '-'
                            , EAF_B_ElectricPower: data[1].ElectricPower ? data[1].ElectricPower.toFixed(1) : '-'
                            , EAF_B_SmartARC: data[1].SmartARC ? data[1].SmartARC : '-'

                            , TOTAL_TotalHeats: data[2].TotalHeats ? data[2].TotalHeats : '-'
                            , TOTAL_TotalSteelWeight: data[2].TotalSteelWeight ? data[2].TotalSteelWeight.toFixed(1) : '-'
                            , TOTAL_TotalScrapWeight: data[2].TotalScrapWeight ? data[2].TotalScrapWeight.toFixed(1) : '-'
                            , TOTAL_TotalElectricEnergy: data[2].TotalElectricEnergy ? data[2].TotalElectricEnergy.toFixed(1) : '-'
                            , TOTAL_TotalElectrodeAdds: data[2].TotalElectrodeAdds ? data[2].TotalElectrodeAdds : '-'
                            , TOTAL_SteelWeight: data[2].SteelWeight ? data[2].SteelWeight.toFixed(1) : '-'
                            , TOTAL_ScrapWeight: data[2].ScrapWeight ? data[2].ScrapWeight.toFixed(1) : '-'
                            , TOTAL_Fluxes: data[2].Fluxes ? data[2].Fluxes.toFixed(1) : '-'
                            , TOTAL_Yield: data[2].Yield ? data[2].Yield.toFixed(1) : '-'
                            , TOTAL_TapToTap: data[2].TapToTap ? data[2].TapToTap.toFixed(1) : '-'
                            , TOTAL_PowerOn: data[2].PowerOn ? data[2].PowerOn.toFixed(1) : '-'
                            , TOTAL_InjectedC: data[2].InjectedC ? data[2].InjectedC.toFixed(1) : '-'
                            , TOTAL_EAFLastTemperature: data[2].EAFLastTemperature ? data[2].EAFLastTemperature.toFixed(1) : '-'
                            , TOTAL_ElectricEnergy: data[2].ElectricEnergy ? data[2].ElectricEnergy.toFixed(1) : '-'
                            , TOTAL_SpecificEnergy: data[2].SpecificEnergy ? data[2].SpecificEnergy.toFixed(1) : '-'
                            , TOTAL_ElectricPower: data[2].ElectricPower ? data[2].ElectricPower.toFixed(1) : '-'
                            , TOTAL_SmartARC: data[2].SmartARC ? data[2].SmartARC : '-'

                            , TOTAL_SteelWeightSTD: data[2].SteelWeightSTD ? data[2].SteelWeightSTD : '-'
                            , TOTAL_ScrapWeightSTD: data[2].ScrapWeightSTD ? data[2].ScrapWeightSTD : '-'
                            , TOTAL_YieldSTD: data[2].YieldSTD ? data[2].YieldSTD : '-'
                            , TOTAL_TapToTapSTD: data[2].TapToTapSTD ? data[2].TapToTapSTD : '-'
                            , TOTAL_PowerOnSTD: data[2].PowerOnSTD ? data[2].PowerOnSTD : '-'
                            , TOTAL_SpecificEnergySTD: data[2].SpecificEnergySTD ? data[2].SpecificEnergySTD : '-'
                        });
                    } else if (data.length == 2) {
                        if (data[0].EAF == 'EAF A') {
                            this.set({
                                hasData_EAF_A: true,
                                CREW_ID: data[0].CrewId ? data[0].CrewId : '-'
                                , EAF_A_TotalHeats: data[0].TotalHeats ? data[0].TotalHeats : '-'
                                , EAF_A_TotalSteelWeight: data[0].TotalSteelWeight ? data[0].TotalSteelWeight.toFixed(1) : '-'
                                , EAF_A_TotalScrapWeight: data[0].TotalScrapWeight ? data[0].TotalScrapWeight.toFixed(1) : '-'
                                , EAF_A_TotalElectricEnergy: data[0].TotalElectricEnergy ? data[0].TotalElectricEnergy.toFixed(1) : '-'
                                , EAF_A_TotalElectrodeAdds: data[0].TotalElectrodeAdds ? data[0].TotalElectrodeAdds : '-'
                                , EAF_A_SteelWeight: data[0].SteelWeight ? data[0].SteelWeight.toFixed(1) : '-'
                                , EAF_A_ScrapWeight: data[0].ScrapWeight ? data[0].ScrapWeight.toFixed(1) : '-'
                                , EAF_A_Fluxes: data[0].Fluxes ? data[0].Fluxes.toFixed(1) : '-'
                                , EAF_A_Yield: data[0].Yield ? data[0].Yield.toFixed(1) : '-'
                                , EAF_A_TapToTap: data[0].TapToTap ? data[0].TapToTap.toFixed(1) : '-'
                                , EAF_A_PowerOn: data[0].PowerOn ? data[0].PowerOn.toFixed(1) : '-'
                                , EAF_A_InjectedC: data[0].InjectedC ? data[0].InjectedC.toFixed(1) : '-'
                                , EAF_A_EAFLastTemperature: data[0].EAFLastTemperature ? data[0].EAFLastTemperature.toFixed(1) : '-'
                                , EAF_A_ElectricEnergy: data[0].ElectricEnergy ? data[0].ElectricEnergy.toFixed(1) : '-'
                                , EAF_A_SpecificEnergy: data[0].SpecificEnergy ? data[0].SpecificEnergy.toFixed(1) : '-'
                                , EAF_A_ElectricPower: data[0].ElectricPower ? data[0].ElectricPower.toFixed(1) : '-'
                                , EAF_A_SmartARC: data[0].SmartARC ? data[0].SmartARC : '-'
                                , EAF_B_TotalHeats: '-', EAF_B_TotalSteelWeight: '-', EAF_B_TotalScrapWeight: '-'
                                , EAF_B_TotalElectricEnergy: '-', EAF_B_TotalElectrodeAdds: '-', EAF_B_SteelWeight: '-'
                                , EAF_B_ScrapWeight: '-', EAF_B_Fluxes: '-', EAF_B_Yield: '-', EAF_B_TapToTap: '-'
                                , EAF_B_PowerOn: '-', EAF_B_InjectedC: '-', EAF_B_EAFLastTemperature: '-'
                                , EAF_B_ElectricEnergy: '-', EAF_B_SpecificEnergy: '-', EAF_B_ElectricPower: '-', EAF_B_SmartARC: '-'
                            });
                        } else if (data[0].EAF == 'EAF B') {
                            this.set({
                                hasData_EAF_B: true,
                                CREW_ID: data[0].CrewId ? data[0].CrewId : '-'
                                , EAF_B_TotalHeats: data[0].TotalHeats ? data[0].TotalHeats : '-'
                                , EAF_B_TotalSteelWeight: data[0].TotalSteelWeight ? data[0].TotalSteelWeight.toFixed(1) : '-'
                                , EAF_B_TotalScrapWeight: data[0].TotalScrapWeight ? data[0].TotalScrapWeight.toFixed(1) : '-'
                                , EAF_B_TotalElectricEnergy: data[0].TotalElectricEnergy ? data[0].TotalElectricEnergy.toFixed(1) : '-'
                                , EAF_B_TotalElectrodeAdds: data[0].TotalElectrodeAdds ? data[0].TotalElectrodeAdds : '-'
                                , EAF_B_SteelWeight: data[0].SteelWeight ? data[0].SteelWeight.toFixed(1) : '-'
                                , EAF_B_ScrapWeight: data[0].ScrapWeight ? data[0].ScrapWeight.toFixed(1) : '-'
                                , EAF_B_Fluxes: data[0].Fluxes ? data[0].Fluxes.toFixed(1) : '-'
                                , EAF_B_Yield: data[0].Yield ? data[0].Yield.toFixed(1) : '-'
                                , EAF_B_TapToTap: data[0].TapToTap ? data[0].TapToTap.toFixed(1) : '-'
                                , EAF_B_PowerOn: data[0].PowerOn ? data[0].PowerOn.toFixed(1) : '-'
                                , EAF_B_InjectedC: data[0].InjectedC ? data[0].InjectedC.toFixed(1) : '-'
                                , EAF_B_EAFLastTemperature: data[0].EAFLastTemperature ? data[0].EAFLastTemperature.toFixed(1) : '-'
                                , EAF_B_ElectricEnergy: data[0].ElectricEnergy ? data[0].ElectricEnergy.toFixed(1) : '-'
                                , EAF_B_SpecificEnergy: data[0].SpecificEnergy ? data[0].SpecificEnergy.toFixed(1) : '-'
                                , EAF_B_ElectricPower: data[0].ElectricPower ? data[0].ElectricPower.toFixed(1) : '-'
                                , EAF_B_SmartARC: data[0].SmartARC ? data[0].SmartARC : '-'
                                , EAF_A_TotalHeats: '-', EAF_A_TotalSteelWeight: '-', EAF_A_TotalScrapWeight: '-'
                                , EAF_A_TotalElectricEnergy: '-', EAF_A_TotalElectrodeAdds: '-', EAF_A_SteelWeight: '-'
                                , EAF_A_ScrapWeight: '-', EAF_A_Fluxes: '-', EAF_A_Yield: '-', EAF_A_TapToTap: '-'
                                , EAF_A_PowerOn: '-', EAF_A_InjectedC: '-', EAF_A_EAFLastTemperature: '-'
                                , EAF_A_ElectricEnergy: '-', EAF_A_SpecificEnergy: '-', EAF_A_ElectricPower: '-', EAF_A_SmartARC: '-'
                            })
                        }

                        this.set({
                            CREW_ID: data[0].CrewId ? data[0].CrewId : '-'
                            , TOTAL_TotalHeats: data[1].TotalHeats ? data[1].TotalHeats : '-'
                            , TOTAL_TotalSteelWeight: data[1].TotalSteelWeight ? data[1].TotalSteelWeight.toFixed(1) : '-'
                            , TOTAL_TotalScrapWeight: data[1].TotalScrapWeight ? data[1].TotalScrapWeight.toFixed(1) : '-'
                            , TOTAL_TotalElectricEnergy: data[1].TotalElectricEnergy ? data[1].TotalElectricEnergy.toFixed(1) : '-'
                            , TOTAL_TotalElectrodeAdds: data[1].TotalElectrodeAdds ? data[1].TotalElectrodeAdds : '-'
                            , TOTAL_SteelWeight: data[1].SteelWeight ? data[1].SteelWeight.toFixed(1) : '-'
                            , TOTAL_ScrapWeight: data[1].ScrapWeight ? data[1].ScrapWeight.toFixed(1) : '-'
                            , TOTAL_Fluxes: data[1].Fluxes ? data[1].Fluxes.toFixed(1) : '-'
                            , TOTAL_Yield: data[1].Yield ? data[1].Yield.toFixed(1) : '-'
                            , TOTAL_TapToTap: data[1].TapToTap ? data[1].TapToTap.toFixed(1) : '-'
                            , TOTAL_PowerOn: data[1].PowerOn ? data[1].PowerOn.toFixed(1) : '-'
                            , TOTAL_InjectedC: data[1].InjectedC ? data[1].InjectedC.toFixed(1) : '-'
                            , TOTAL_EAFLastTemperature: data[1].EAFLastTemperature ? data[1].EAFLastTemperature.toFixed(1) : '-'
                            , TOTAL_ElectricEnergy: data[1].ElectricEnergy ? data[1].ElectricEnergy.toFixed(1) : '-'
                            , TOTAL_SpecificEnergy: data[1].SpecificEnergy ? data[1].SpecificEnergy.toFixed(1) : '-'
                            , TOTAL_ElectricPower: data[1].ElectricPower ? data[1].ElectricPower.toFixed(1) : '-'
                            , TOTAL_SmartARC: data[1].SmartARC ? data[1].SmartARC : '-'
                            , TOTAL_SteelWeightSTD: data[1].SteelWeightSTD ? data[1].SteelWeightSTD : '-'
                            , TOTAL_ScrapWeightSTD: data[1].ScrapWeightSTD ? data[1].ScrapWeightSTD : '-'
                            , TOTAL_YieldSTD: data[1].YieldSTD ? data[1].YieldSTD : '-'
                            , TOTAL_TapToTapSTD: data[1].TapToTapSTD ? data[1].TapToTapSTD : '-'
                            , TOTAL_PowerOnSTD: data[1].PowerOnSTD ? data[1].PowerOnSTD : '-'
                            , TOTAL_SpecificEnergySTD: data[1].SpecificEnergySTD ? data[1].SpecificEnergySTD : '-'
                        })
                    }
                    hasData = true;
                }
                this.trigger('TotalsAndAverages', hasData);
            },
        });

        Screen.Collections.ItemsCollection = Backbone.Collection.extend({
            setDataColl: function (data) {
                var newColl, that = this;
                newColl = data;
                that.set(newColl).trigger('fetch', that, data);
            },
        });

        Screen.generateID = function (viewParams) {
            try { return 'eaf-shift-report'; } catch (Error) { }
        }

        Screen.Views.Main = Backbone.Epoxy.View.extend({
            template: 'eaf-shift-report'
            , id: 'eaf-shift-report'
            , title: 'EAF Shift Report'
            , isCacheable: false
            , events: function () {
                return {
                    'click #refreshBtn': this.refreshBtn_click,
                    'click #forwardNavBtn': this.forwardNavBtn_click,
                    'click #backNavBtn': this.backNavBtn_click,
                    'click #downloadPdfBtn': this.downloadPdfBtn_click,
                };
            }
            , bindings: 'data-bind'
            , viewParams: null
            , initialize: function () {
                this.options.state = app.view_states.loading;
                this.options.onappend = (_.isFunction(this.options.onappend)) ? this.options.onappend : function () { };
                this.model = new Screen.Models.Main();
                this.bindEvents();
            }

            , forwardNavBtn_click: function () { this.UpdateNav(true); }
            , backNavBtn_click: function () { this.UpdateNav(false); }

            , refreshBtn_click: function (model) {
                this.UpdateURL();
                this.refresh();
            }

            , downloadPdfBtn_click: function () {
                try {
                    var params = this.model.toJSON();
                    var shift = '1';
                    if (params.shiftId == 'D') shift = '1';
                    else if (params.shiftId == 'N') shift = '2';

                    var btn = this.$('#downloadPdfBtn');
                    var btnIcon = btn.find('svg');
                    var btnText = btn.find('span');
                    var originalText = btnText.text();

                    // Show loading state
                    btn.prop('disabled', true);
                    btnIcon.hide();
                    btnText.html('<svg class="sr-spin" style="display:inline-block;vertical-align:middle;margin-right:4px;" width="16" height="16" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><circle style="opacity:0.25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" fill="none"/><path style="opacity:0.75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"/></svg> Generating...');

                    var serviceUrl = (app.config && app.config.shiftReportPdfServiceUrl)
                        ? app.config.shiftReportPdfServiceUrl
                        : 'http://localhost:5050';

                    fetch(serviceUrl + '/api/shift-report/generate', {
                        method: 'POST',
                        headers: { 'Content-Type': 'application/json' },
                        body: JSON.stringify({
                            productionDate: new moment(params.productionDate, 'MM/DD/YYYY').format('YYYY-MM-DD'),
                            shiftId: parseInt(shift),
                            reportType: 'EAF'
                        })
                    })
                    .then(function (res) {
                        if (!res.ok) throw new Error('PDF generation failed: ' + res.statusText);
                        return res.blob();
                    })
                    .then(function (blob) {
                        var url = window.URL.createObjectURL(blob);
                        window.open(url, '_blank');
                        setTimeout(function () {
                            window.URL.revokeObjectURL(url);
                        }, 60000);
                    })
                    .catch(function (err) {
                        console.error('PDF download error:', err);
                        alert('Failed to download PDF. Please ensure the PDF service is running.');
                    })
                    .finally(function () {
                        // Restore button
                        btn.prop('disabled', false);
                        btnIcon.show();
                        btnText.text(originalText);
                    });
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            }

            , bindEvents: function () {
                this.listenTo(this.model, 'fetch', this.fetched);
                this.listenTo(this.model, 'furnace2_READY', this.furnace1_chart);
                this.listenTo(this.model.itemsAllHeats, 'fetch', this.itemsAllHeats_ready);
                this.listenTo(this.model.itemsTemperature, 'fetch', this.itemsTemperature_ready);
                this.listenTo(this.model.itemsInjectionFluxesEAFA, 'fetch', this.itemsInjectionFluxesEAFA_ready);
                this.listenTo(this.model.itemsInjectionFluxesEAFB, 'fetch', this.itemsInjectionFluxesEAFB_ready);
                this.listenTo(this.model.itemsBucketFluxesEAFA, 'fetch', this.itemsBucketFluxesEAFA_ready);
                this.listenTo(this.model.itemsBucketFluxesEAFB, 'fetch', this.itemsBucketFluxesEAFB_ready);
                this.listenTo(this.model.itemsBaghouse, 'fetch', this.itemsBaghouse_ready);
                this.listenTo(this.model.itemsDelays, 'fetch', this.itemsDelaysEAFA_ready);
                this.listenTo(this.model.itemsSlag, 'fetch', this.itemsSlag_ready);
                this.listenTo(this.model.itemsRecipes, 'fetch', this.itemsRecipes_ready);
                this.listenTo(this.model.itemsRecipesAverage, 'fetch', this.recipesAverage_ready);
                this.listenTo(this.model.itemsManualTemps, 'fetch', this.manualTemps_ready);
                this.listenTo(this.model.itemsTapHoleNonFreeOpen, 'fetch', this.tapHoleNonFreeOpen_ready);
                this.listenTo(this.model.itemsLogBook, 'fetch', this.itemsLogBook_ready);
                this.listenTo(this.model, 'TotalsAndAverages', this.buildTotalsGrid);
            },

            itemsAllHeats_ready: function () {
                try {
                    var that = this;
                    setTimeout(function () {
                        that.model.set({ hasData_allHeats: (that.model.itemsAllHeats.length > 0), isLoading_allHeats: false });
                        that.furnace2_chart();
                    }, 100);
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            },

            furnace1_chart: function () {
                var that = this;
                setTimeout(function () {
                    that.getChart(true, 1400);
                    that.model.set({ isLoading_furnace1: false });
                }, 100);
            },
            furnace2_chart: function () {
                var that = this;
                setTimeout(function () {
                    that.getChart(false, 1400);
                    that.model.trigger('furnace2_READY', that);
                }, 100);
            },

            getYieldChart: function (className, width, height) {
                var data = this.model.itemsYield.toJSON();
                var margin = { top: 44, right: 20, bottom: 100, left: 45 };
                var w = width - margin.left - margin.right;
                var h = height - margin.top - margin.bottom;
                var colorA = '#3b82f6', colorB = '#f43f5e';

                var xScale = d3.scaleBand()
                    .domain(data.map(function (d) { return d.date + ' ' + d.shift; }))
                    .range([0, w]).padding(0.1);
                var yScale = d3.scaleLinear().domain([80, 110]).range([h, 0]);

                var svg = d3.select(className).append('svg')
                    .attr('width', width).attr('height', height)
                    .append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

                // Horizontal grid lines
                svg.append('g').call(d3.axisLeft(yScale).tickSize(-w).tickFormat('').ticks(6))
                    .selectAll('line').style('stroke', '#e2e8f0').style('stroke-dasharray', '3,3');
                svg.selectAll('.domain').remove();

                // Axes
                svg.append('g').attr('transform', 'translate(0,' + h + ')')
                    .call(d3.axisBottom(xScale)).selectAll('text')
                    .style('text-anchor', 'end').attr('dx', '-.8em').attr('dy', '.15em')
                    .attr('transform', 'rotate(-55)').style('font-size', '10px').style('fill', '#64748b');
                svg.append('g').call(d3.axisLeft(yScale).ticks(6))
                    .selectAll('text').style('font-size', '11px').style('fill', '#64748b');

                function clampY(val) { if (val > 110) return yScale(110); if (val < 80) return yScale(80); return yScale(val); }
                function cx(d) { return xScale(d.date + ' ' + d.shift) + xScale.bandwidth() / 2; }

                // Area fills
                svg.append('path').datum(data).attr('fill', 'rgba(59,130,246,0.07)')
                    .attr('d', d3.area().curve(d3.curveMonotoneX)
                        .x(cx).y0(h).y1(function (d) { return d.yieldEAFA ? clampY(d.yieldEAFA) : h; }));
                svg.append('path').datum(data).attr('fill', 'rgba(244,63,94,0.07)')
                    .attr('d', d3.area().curve(d3.curveMonotoneX)
                        .x(cx).y0(h).y1(function (d) { return d.yieldEAFB ? clampY(d.yieldEAFB) : h; }));

                // Lines
                svg.append('path').datum(data).attr('fill', 'none').attr('stroke', colorA).attr('stroke-width', 2.5)
                    .attr('d', d3.line().curve(d3.curveMonotoneX).x(cx)
                        .y(function (d) { return d.yieldEAFA ? clampY(d.yieldEAFA) : h; }));
                svg.append('path').datum(data).attr('fill', 'none').attr('stroke', colorB).attr('stroke-width', 2.5)
                    .attr('d', d3.line().curve(d3.curveMonotoneX).x(cx)
                        .y(function (d) { return d.yieldEAFB ? clampY(d.yieldEAFB) : h; }));

                // Dots EAF A
                svg.selectAll('.dot-a').data(data).enter().append('circle')
                    .attr('fill', '#fff').attr('stroke', colorA).attr('stroke-width', 2)
                    .attr('cx', cx).attr('cy', function (d) { return d.yieldEAFA ? clampY(d.yieldEAFA) : h; })
                    .attr('r', function (d) { return d.yieldEAFA ? 4 : 0; })
                    .on('mouseover', function () { d3.select(this).transition().duration(200).attr('r', 7).attr('stroke-width', 3); })
                    .on('mouseout', function () { d3.select(this).transition().duration(200).attr('r', 4).attr('stroke-width', 2); })
                    .append('svg:title').text(function (d) { return d.yieldEAFA ? 'EAF A - ' + d.date + ' ' + d.shift + ' - Yield: ' + d.yieldEAFA : ''; });

                // Dots EAF B
                svg.selectAll('.dot-b').data(data).enter().append('circle')
                    .attr('fill', '#fff').attr('stroke', colorB).attr('stroke-width', 2)
                    .attr('cx', cx).attr('cy', function (d) { return d.yieldEAFB ? clampY(d.yieldEAFB) : h; })
                    .attr('r', function (d) { return d.yieldEAFB ? 4 : 0; })
                    .on('mouseover', function () { d3.select(this).transition().duration(200).attr('r', 7).attr('stroke-width', 3); })
                    .on('mouseout', function () { d3.select(this).transition().duration(200).attr('r', 4).attr('stroke-width', 2); })
                    .append('svg:title').text(function (d) { return d.yieldEAFB ? 'EAF B - ' + d.date + ' ' + d.shift + ' - Yield: ' + d.yieldEAFB : ''; });

                // Legend (centered)
                var lg = svg.append('g').attr('transform', 'translate(' + (w / 2 - 110) + ',-28)');
                lg.append('line').attr('x1', 0).attr('y1', 0).attr('x2', 24).attr('y2', 0).attr('stroke', colorA).attr('stroke-width', 3);
                lg.append('circle').attr('cx', 12).attr('cy', 0).attr('r', 4).attr('fill', '#fff').attr('stroke', colorA).attr('stroke-width', 2);
                lg.append('text').attr('x', 30).attr('y', 4).text('EAF A Yield').style('font-size', '12px').style('fill', '#475569');
                lg.append('line').attr('x1', 130).attr('y1', 0).attr('x2', 154).attr('y2', 0).attr('stroke', colorB).attr('stroke-width', 3);
                lg.append('circle').attr('cx', 142).attr('cy', 0).attr('r', 4).attr('fill', '#fff').attr('stroke', colorB).attr('stroke-width', 2);
                lg.append('text').attr('x', 160).attr('y', 4).text('EAF B Yield').style('font-size', '12px').style('fill', '#475569');
            },

            // GANTT chart - modern HTML/CSS timeline
            getChart: function (furnace1, width) {
                var heats = [], delays = [];
                var chart_class;
                if (furnace1) {
                    heats = this.model.itemsAllHeats.getHeats_furnace1();
                    delays = this.model.itemsAllDelays.getDelay_furnace1();
                    chart_class = ".furnace1-gantt-screen";
                } else {
                    heats = this.model.itemsAllHeats.getHeats_furnace2();
                    delays = this.model.itemsAllDelays.getDelay_furnace2();
                    chart_class = ".furnace2-gantt-screen";
                }

                var $container = this.$el.find(chart_class);
                $container.empty();

                if (heats.length === 0 && delays.length === 0) return;

                var params = this.model.toJSON();
                var shiftId = params.shiftId;
                var divisions = 12;

                // Build time labels
                var timeLabels = [];
                if (shiftId === 'D') {
                    timeLabels = ['9AM','10AM','11AM','12PM','1PM','2PM','3PM','4PM','5PM','6PM','7PM','8PM','9PM'];
                } else {
                    timeLabels = ['9PM','10PM','11PM','12AM','1AM','2AM','3AM','4AM','5AM','6AM','7AM','8AM','9AM'];
                }

                // Helper: calculate left % and width % for a data item
                function calcPosition(d) {
                    var start = parseFloat(d.startHour);
                    var offset;
                    if (d.shift === 'D') { offset = start - 9; }
                    else {
                        offset = (d.startHour >= 21) ? start - 21 : start + 3;
                    }
                    var leftPct = (offset / divisions) * 100;
                    var widthPct = (d.diffHours > 0) ? (d.diffHours / divisions) * 100 : 0;
                    return { left: leftPct, width: widthPct };
                }

                // Build tooltip text
                function tooltipText(d) {
                    var startT = moment(d.start).format('HH:mm');
                    var endT = moment(d.end).format('HH:mm');
                    if (d.heatName) {
                        return 'Heat: ' + d.heatName + '\nStart: ' + startT + ' | End: ' + endT + '\nDuration: ' + d.duration;
                    }
                    return (d.name || 'Delay') + '\nStart: ' + startT + ' | End: ' + endT + '\nDuration: ' + d.duration;
                }

                // Time axis
                var axisHtml = '<div class="sr-gantt-axis">';
                for (var i = 0; i < timeLabels.length; i++) {
                    var pos = (i / divisions) * 100;
                    axisHtml += '<span class="sr-gantt-tick" style="left:' + pos + '%">' + timeLabels[i] + '</span>';
                }
                axisHtml += '</div>';

                // Grid lines
                var gridHtml = '';
                for (var i = 0; i <= divisions; i++) {
                    var pos = (i / divisions) * 100;
                    gridHtml += '<div class="sr-gantt-gridline" style="left:' + pos + '%"></div>';
                }

                // Heats lane
                var heatsHtml = '<div class="sr-gantt-lane"><span class="sr-gantt-lane-label">Heats</span><div class="sr-gantt-lane-track">' + gridHtml;
                _.each(heats, function (d) {
                    var pos = calcPosition(d);
                    var tip = tooltipText(d);
                    heatsHtml += '<div class="sr-gantt-bar sr-gantt-bar--heat" style="left:' + pos.left + '%;width:' + pos.width + '%;background-color:' + d.color + ';" title="' + tip.replace(/"/g, '&quot;') + '">';
                    heatsHtml += '<span class="sr-gantt-bar-label">' + (d.name || '') + '</span>';
                    heatsHtml += '</div>';
                });
                heatsHtml += '</div></div>';

                // Delays lane
                var delaysHtml = '<div class="sr-gantt-lane"><span class="sr-gantt-lane-label">Delays</span><div class="sr-gantt-lane-track">' + gridHtml;
                _.each(delays, function (d) {
                    var pos = calcPosition(d);
                    var tip = tooltipText(d);
                    delaysHtml += '<div class="sr-gantt-bar sr-gantt-bar--delay" style="left:' + pos.left + '%;width:' + pos.width + '%;background-color:' + d.color + ';" title="' + tip.replace(/"/g, '&quot;') + '">';
                    delaysHtml += '<span class="sr-gantt-bar-label">' + (d.name || '') + '</span>';
                    delaysHtml += '</div>';
                });
                delaysHtml += '</div></div>';

                $container.html(
                    '<div class="sr-gantt-timeline">' +
                        axisHtml +
                        heatsHtml +
                        delaysHtml +
                    '</div>'
                );
            },

            // GANTT legend - HTML badges instead of SVG
            referencesChart: function () {
                var srdCategories = [...new Map(this.model.srdCategories.models.map(item => [item.get('description'), item])).values()];
                var $legend = this.$el.find('#gantt-legend-categories');
                $legend.find('.dynamic-badge').remove();
                $legend.append('<div class="flex items-center gap-1 dynamic-badge"><span class="inline-block w-5 h-5 rounded" style="background:#f84e4e; border:1px solid #505050;"></span><span class="text-sm text-gray-600">Not Declared</span></div>');
                srdCategories.forEach(function (cat) {
                    $legend.append('<div class="flex items-center gap-1 dynamic-badge"><span class="inline-block w-5 h-5 rounded" style="background:' + cat.get('color') + '; border:1px solid #505050;"></span><span class="text-sm text-gray-600">' + cat.get('description') + '</span></div>');
                });
            },

            temperatureChart: function (data, id, width) {
                var domainBand = [];
                _.each(data, function (obj, i) {
                    domainBand[i] = obj.HeatName;
                });
                var title = '';
                var chartWidth = width;
                var chartHeight = this.model.get('chartHeight');
                var heightOffset = this.model.get('heightOffset');
                var chartMarginLeft = 80;
                var maxAxisBottom = chartWidth - 100;
                var maxAxisLeftHeight = 250;
                var minValueBottomAxis = 0;
                var maxValueLeft = 3200;
                var minValueLeft = 2700;
                if (data.length > 0) {
                    minValueBottomAxis = maxAxisBottom / (2 * data.length);
                    var svg = d3.select(id)
                        .append("svg")
                        .attr("width", chartWidth)
                        .attr("height", chartHeight)
                        .append("g")
                        .attr("transform", "translate(" + chartMarginLeft + "," + heightOffset + ")");
                    var x = d3.scaleBand()
                        .domain(domainBand)
                        .range([0, maxAxisBottom]);
                    svg.append("g")
                        .attr("class", 'dashed x_text')
                        .call(d3.axisBottom(x).tickSize(-maxAxisLeftHeight).ticks(7))
                        .select(".domain").attr("class", 'wdashed');
                    svg.selectAll("g")
                        .select(".x_text .tick text")
                        .attr("y", function (d, i) {
                            if (i % 2 == 0) return 3
                            else return 17;
                        })
                    var y = d3.scaleLinear()
                        .domain([minValueLeft, maxValueLeft])
                        .range([0, -maxAxisLeftHeight]);
                    svg.append("g")
                        .attr("class", "y_text dashed")
                        .call(d3.axisLeft(y).tickSize(-maxAxisBottom).ticks(10))
                        .select(".domain").remove();
                    svg.selectAll("g")
                        .select(".y_text .tick text")
                        .attr("x", function (d, i) { return -20; });
                    if (id == '.temperature_EAFA') {
                        title = "EAF A - Temperature";
                        this.model.set({ hasData_temperature_EAFA: true });
                    }
                    else if (id == '.temperature_EAFB') {
                        title = "EAF B - Temperature";
                        this.model.set({ hasData_temperature_EAFB: true });
                    }
                    svg.selectAll("whatever")
                        .data(data).enter().append("circle")
                        .attr("color", "black")
                        .attr("cx", function (d) { if (d.EAFLastTemperature) return x(d.HeatName); })
                        .attr("cy", function (d) {
                            if (d.EAFLastTemperature) {
                                if (d.EAFLastTemperature > maxValueLeft) return y(maxValueLeft);
                                else if (d.EAFLastTemperature < minValueLeft) return y(minValueLeft);
                                else return y(d.EAFLastTemperature);
                            }
                        })
                        .attr("transform", "translate(" + minValueBottomAxis + ", 0)")
                        .attr("r", function (d) { if (d.EAFLastTemperature) return 4; else return 0; })
                        .on('mouseover', function () { d3.select(this).transition().duration(500).attr('stroke', 'green').attr('stroke-width', 3) })
                        .on('mouseout', function () { d3.select(this).transition().duration(500).attr('stroke', 'black').attr('stroke-width', 1) })
                        .append("svg:title")
                        .text(function (d) { if (d.EAFLastTemperature) return "Last Temperature: " + d.EAFLastTemperature; else return ''; })
                    // References
                    svg.append("circle").attr("cy", -270).attr("cx", 0).attr("fill", "black").attr("r", 6)
                    svg.append("text").attr("y", -265).attr("x", 10).text("Last Temperature")
                    // Red 3000F line
                    svg.append("line").style("stroke", "red")
                        .attr("x1", 0).attr("y1", y(3000)).attr("x2", maxAxisBottom).attr("y2", y(3000))
                    // Aim Temps
                    svg.append("circle").attr("cy", -270).attr("cx", 150).attr("fill", "blue").attr("r", 6)
                    svg.append("text").attr("y", -265).attr("x", 160).text("Temperature Aim")
                    svg.selectAll("whatever")
                        .data(data).enter().append("circle")
                        .attr("fill", "blue")
                        .attr("cx", function (d) { if (d.EAFAimTemperature) return x(d.HeatName); })
                        .attr("cy", function (d) {
                            if (d.EAFAimTemperature) {
                                if (d.EAFAimTemperature > maxValueLeft) return y(maxValueLeft);
                                else if (d.EAFAimTemperature < minValueLeft) return y(minValueLeft);
                                else return y(d.EAFAimTemperature);
                            }
                        })
                        .attr("transform", "translate(" + minValueBottomAxis + ", 0)")
                        .attr("r", function (d) { if (d.EAFAimTemperature) return 2; else return 0; })
                        .on('mouseover', function () { d3.select(this).transition().duration(500).attr('stroke', 'green').attr('stroke-width', 3) })
                        .on('mouseout', function () { d3.select(this).transition().duration(500).attr('stroke', 'blue').attr('stroke-width', 1) })
                        .append("svg:title")
                        .text(function (d) { if (d.EAFLastTemperature) return "Temperature Aim: " + d.EAFAimTemperature; else return ''; })
                    svg.append("path").datum(data).attr("fill", "none").attr("stroke", "blue").attr("stroke-width", 2)
                        .attr("d", d3.line().curve(d3.curveMonotoneX)
                            .x(function (d) { return x(d.HeatName) })
                            .y(function (d) {
                                if (d.EAFAimTemperature) {
                                    if (d.EAFAimTemperature > maxValueLeft) return y(maxValueLeft);
                                    else if (d.EAFAimTemperature < minValueLeft) return y(minValueLeft);
                                    else return y(d.EAFAimTemperature);
                                } else return 0;
                            })
                        ).attr("transform", "translate(" + minValueBottomAxis + ", 0)")
                }
            },
            oxygenChart: function (data, id, width) {
                var domainBand = [];
                _.each(data, function (obj, i) { domainBand[i] = obj.HeatName; });
                var chartWidth = width;
                var chartHeight = this.model.get('chartHeight');
                var heightOffset = this.model.get('heightOffset');
                var chartMarginLeft = 80;
                var maxAxisBottom = chartWidth - 100;
                var maxAxisLeftHeight = 250;
                var minValueBottomAxis = 0;
                var maxValueLeft = 1500;
                var minValueLeft = 0;
                if (data.length > 0) {
                    minValueBottomAxis = maxAxisBottom / (2 * data.length);
                    var svg = d3.select(id)
                        .append("svg").attr("width", chartWidth).attr("height", chartHeight)
                        .append("g").attr("transform", "translate(" + chartMarginLeft + "," + heightOffset + ")");
                    var x = d3.scaleBand().domain(domainBand).range([0, maxAxisBottom]);
                    svg.append("g").attr("class", 'dashed x_text')
                        .call(d3.axisBottom(x).tickSize(-maxAxisLeftHeight).ticks(7))
                        .select(".domain").attr("class", 'wdashed');
                    svg.selectAll("g").select(".x_text .tick text")
                        .attr("y", function (d, i) { if (i % 2 == 0) return 3; else return 17; })
                    var y = d3.scaleLinear().domain([minValueLeft, maxValueLeft]).range([0, -maxAxisLeftHeight]);
                    svg.append("g").attr("class", "y_text dashed")
                        .call(d3.axisLeft(y).tickSize(-maxAxisBottom).ticks(20))
                        .select(".domain").remove();
                    svg.selectAll("g").select(".y_text .tick text").attr("x", function (d, i) { return -20; });
                    if (id == '.oxygen_EAFA') {
                        this.model.set({ hasData_oxygen_EAFA: true });
                    } else if (id == '.oxygen_EAFB') {
                        this.model.set({ hasData_oxygen_EAFB: true });
                    }
                    svg.selectAll("whatever").data(data).enter().append("circle")
                        .attr("fill", "blue")
                        .attr("cx", function (d) { if (d.EAFLastO2) return x(d.HeatName); })
                        .attr("cy", function (d) {
                            if (d.EAFLastO2) {
                                if (d.EAFLastO2 > maxValueLeft) return y(maxValueLeft);
                                else if (d.EAFLastO2 < minValueLeft) return y(minValueLeft);
                                else return y(d.EAFLastO2);
                            }
                        })
                        .attr("transform", "translate(" + minValueBottomAxis + ", 0)")
                        .attr("r", function (d) { if (d.EAFLastO2) return 4; else return 0; })
                        .on('mouseover', function () { d3.select(this).transition().duration(500).attr('stroke', 'green').attr('stroke-width', 3) })
                        .on('mouseout', function () { d3.select(this).transition().duration(500).attr('stroke', 'blue').attr('stroke-width', 1) })
                        .append("svg:title")
                        .text(function (d) { if (d.EAFLastO2) return "Last O2: " + d.EAFLastO2; else return ''; })
                    // References
                    svg.append("circle").attr("cy", -270).attr("cx", 0).attr("fill", "blue").attr("r", 6)
                    svg.append("text").attr("y", -265).attr("x", 10).text("Last O2")
                    // Aim O2
                    svg.append("circle").attr("cy", -270).attr("cx", 100).attr("fill", "green").attr("r", 6)
                    svg.append("text").attr("y", -265).attr("x", 110).text("O2 Aim")
                    svg.selectAll("whatever").data(data).enter().append("circle")
                        .attr("fill", "green")
                        .attr("cx", function (d) { if (d.EAFAimO2) return x(d.HeatName); })
                        .attr("cy", function (d) {
                            if (d.EAFAimO2) {
                                if (d.EAFAimO2 > maxValueLeft) return y(maxValueLeft);
                                else if (d.EAFAimO2 < minValueLeft) return y(minValueLeft);
                                else return y(d.EAFAimO2);
                            }
                        })
                        .attr("transform", "translate(" + minValueBottomAxis + ", 0)")
                        .attr("r", function (d) { if (d.EAFAimO2) return 2; else return 0; })
                        .on('mouseover', function () { d3.select(this).transition().duration(500).attr('stroke', 'red').attr('stroke-width', 3) })
                        .on('mouseout', function () { d3.select(this).transition().duration(500).attr('stroke', 'green').attr('stroke-width', 1) })
                        .append("svg:title")
                        .text(function (d) { if (d.EAFAimO2) return "Oxygen Aim: " + d.EAFAimO2; else return ''; })
                    svg.append("path").datum(data).attr("fill", "none").attr("stroke", "green").attr("stroke-width", 2)
                        .attr("d", d3.line().curve(d3.curveMonotoneX)
                            .x(function (d) { return x(d.HeatName) })
                            .y(function (d) {
                                if (d.EAFAimO2) {
                                    if (d.EAFAimO2 > maxValueLeft) return y(maxValueLeft);
                                    else if (d.EAFAimO2 < minValueLeft) return y(minValueLeft);
                                    else return y(d.EAFAimO2);
                                } else return 0;
                            })
                        ).attr("transform", "translate(" + minValueBottomAxis + ", 0)")
                }
            },
            residualsChart: function (data, id, width) {
                var domainBand = [];
                _.each(data, function (obj, i) { domainBand[i] = obj.HeatName; });
                var title = '';
                var chartWidth = width;
                var chartHeight = this.model.get('chartHeight');
                var heightOffset = this.model.get('heightOffset');
                var chartMarginLeft = 80;
                var maxAxisBottom = chartWidth - 100;
                var maxAxisLeftHeight = 250;
                var minValueBottomAxis = 0;
                var maxValueLeft = 0.7;
                var minValueLeft = 0;
                if (data.length > 0) {
                    minValueBottomAxis = maxAxisBottom / (2 * data.length);
                    var svg = d3.select(id)
                        .append("svg").attr("width", chartWidth).attr("height", chartHeight)
                        .append("g").attr("transform", "translate(" + chartMarginLeft + "," + heightOffset + ")");
                    var x = d3.scaleBand().domain(domainBand).range([0, maxAxisBottom]);
                    svg.append("g").attr("class", 'x_text dashed')
                        .call(d3.axisBottom(x).tickSize(-maxAxisLeftHeight).ticks(7))
                        .select(".domain").attr("class", 'wdashed');
                    svg.selectAll("g").select(".x_text .tick text")
                        .attr("y", function (d, i) { if (i % 2 == 0) return 3; else return 17; })
                    var y = d3.scaleLinear().domain([minValueLeft, maxValueLeft]).range([0, -maxAxisLeftHeight]);
                    svg.append("g").attr("class", "y_text dashed")
                        .call(d3.axisLeft(y).tickSize(-maxAxisBottom).ticks(10))
                        .select(".domain").remove();
                    svg.selectAll("g").select(".y_text .tick text").attr("x", function (d, i) { return -20; });
                    if (id == '.residuals_EAFA') {
                        this.model.set({ hasData_residuals_EAFA: true });
                    } else if (id == '.residuals_EAFB') {
                        this.model.set({ hasData_residuals_EAFB: true });
                    }
                    // Cu series (black)
                    svg.selectAll("whatever").data(data).enter().append("circle")
                        .attr("fill", "black")
                        .attr("cx", function (d) { if (d.Cu) return x(d.HeatName); })
                        .attr("cy", function (d) { if (d.Cu) { if (d.Cu > maxValueLeft) return y(maxValueLeft); else if (d.Cu < minValueLeft) return y(minValueLeft); else return y(d.Cu); } })
                        .attr("transform", "translate(" + minValueBottomAxis + ", 0)")
                        .attr("r", function (d) { if (d.Cu) return 4; else return 0; })
                        .on('mouseover', function () { d3.select(this).transition().duration(500).attr('stroke', 'green').attr('stroke-width', 3) })
                        .on('mouseout', function () { d3.select(this).transition().duration(500).attr('stroke', 'black').attr('stroke-width', 1) })
                        .append("svg:title").text(function (d) { if (d.Cu) return "Cu: " + d.Cu; else return ''; })
                    svg.append("path").datum(data).attr("fill", "none").attr("stroke", "black").attr("stroke-width", 2)
                        .attr("d", d3.line().curve(d3.curveMonotoneX).x(function (d) { return x(d.HeatName) }).y(function (d) { if (d.Cu) { if (d.Cu > maxValueLeft) return y(maxValueLeft); else if (d.Cu < minValueLeft) return y(minValueLeft); else return y(d.Cu); } else return 0; }))
                        .attr("transform", "translate(" + minValueBottomAxis + ", 0)")
                    // Cr series (red)
                    svg.selectAll("whatever").data(data).enter().append("circle")
                        .attr("fill", "red")
                        .attr("cx", function (d) { if (d.Cr) return x(d.HeatName); })
                        .attr("cy", function (d) { if (d.Cr) { if (d.Cr > maxValueLeft) return y(maxValueLeft); else if (d.Cr < minValueLeft) return y(minValueLeft); else return y(d.Cr); } })
                        .attr("transform", "translate(" + minValueBottomAxis + ", 0)")
                        .attr("r", function (d) { if (d.Cr) return 4; else return 0; })
                        .on('mouseover', function () { d3.select(this).transition().duration(500).attr('stroke', 'green').attr('stroke-width', 3) })
                        .on('mouseout', function () { d3.select(this).transition().duration(500).attr('stroke', 'black').attr('stroke-width', 1) })
                        .append("svg:title").text(function (d) { if (d.Cr) return "Cr: " + d.Cr; else return ''; })
                    svg.append("path").datum(data).attr("fill", "none").attr("stroke", "red").attr("stroke-width", 2)
                        .attr("d", d3.line().curve(d3.curveMonotoneX).x(function (d) { return x(d.HeatName) }).y(function (d) { if (d.Cr) { if (d.Cr > maxValueLeft) return y(maxValueLeft); else if (d.Cr < minValueLeft) return y(minValueLeft); else return y(d.Cr); } else return 0; }))
                        .attr("transform", "translate(" + minValueBottomAxis + ", 0)")
                    // References
                    svg.append("circle").attr("cy", -270).attr("cx", 0).attr("color", "black").attr("r", 6)
                    svg.append("text").attr("y", -265).attr("x", 10).text("Cu")
                    svg.append("circle").attr("cy", -270).attr("cx", 50).attr("fill", "red").attr("r", 6)
                    svg.append("text").attr("y", -265).attr("x", 60).text("Cr")
                }
            },
            residualsChart2: function (data, id, width) {
                var domainBand = [];
                _.each(data, function (obj, i) { domainBand[i] = obj.HeatName; });
                var title = '';
                var chartWidth = width;
                var chartHeight = this.model.get('chartHeight');
                var heightOffset = this.model.get('heightOffset');
                var chartMarginLeft = 80;
                var maxAxisBottom = chartWidth - 100;
                var maxAxisLeftHeight = 250;
                var minValueBottomAxis = 0;
                var maxValueLeft = 0.05;
                var minValueLeft = 0;
                if (data.length > 0) {
                    minValueBottomAxis = maxAxisBottom / (2 * data.length);
                    var svg = d3.select(id)
                        .append("svg").attr("width", chartWidth).attr("height", chartHeight)
                        .append("g").attr("transform", "translate(" + chartMarginLeft + "," + heightOffset + ")");
                    var x = d3.scaleBand().domain(domainBand).range([0, maxAxisBottom]);
                    svg.append("g").attr("class", 'x_text dashed')
                        .call(d3.axisBottom(x).tickSize(-maxAxisLeftHeight).ticks(7))
                        .select(".domain").attr("class", 'wdashed');
                    svg.selectAll("g").select(".x_text .tick text")
                        .attr("y", function (d, i) { if (i % 2 == 0) return 3; else return 17; })
                    var y = d3.scaleLinear().domain([minValueLeft, maxValueLeft]).range([0, -maxAxisLeftHeight]);
                    svg.append("g").attr("class", "y_text dashed")
                        .call(d3.axisLeft(y).tickSize(-maxAxisBottom).ticks(10))
                        .select(".domain").remove();
                    svg.selectAll("g").select(".y_text .tick text").attr("x", function (d, i) { return -20; });
                    if (id == '.residuals_EAFA_2') {
                        this.model.set({ hasData_residuals_EAFA_2: true });
                    } else if (id == '.residuals_EAFB_2') {
                        this.model.set({ hasData_residuals_EAFB_2: true });
                    }
                    // Mo (black)
                    svg.selectAll("whatever").data(data).enter().append("circle").attr("fill", "black")
                        .attr("cx", function (d) { if (d.Mo) return x(d.HeatName); })
                        .attr("cy", function (d) { if (d.Mo) { if (d.Mo > maxValueLeft) return y(maxValueLeft); else if (d.Mo < minValueLeft) return y(minValueLeft); else return y(d.Mo); } })
                        .attr("transform", "translate(" + minValueBottomAxis + ", 0)")
                        .attr("r", function (d) { if (d.Mo) return 4; else return 0; })
                        .on('mouseover', function () { d3.select(this).transition().duration(500).attr('stroke', 'green').attr('stroke-width', 3) })
                        .on('mouseout', function () { d3.select(this).transition().duration(500).attr('stroke', 'black').attr('stroke-width', 1) })
                        .append("svg:title").text(function (d) { if (d.Mo) return "Mo: " + d.Mo; else return ''; })
                    svg.append("path").datum(data).attr("fill", "none").attr("stroke", "black").attr("stroke-width", 2)
                        .attr("d", d3.line().curve(d3.curveMonotoneX).x(function (d) { return x(d.HeatName) }).y(function (d) { if (d.Mo) { if (d.Mo > maxValueLeft) return y(maxValueLeft); else if (d.Mo < minValueLeft) return y(minValueLeft); else return y(d.Mo); } else return 0; }))
                        .attr("transform", "translate(" + minValueBottomAxis + ", 0)")
                    // Sn (red)
                    svg.selectAll("whatever").data(data).enter().append("circle").attr("fill", "red")
                        .attr("cx", function (d) { if (d.Sn) return x(d.HeatName); })
                        .attr("cy", function (d) { if (d.Sn) { if (d.Sn > maxValueLeft) return y(maxValueLeft); else if (d.Sn < minValueLeft) return y(minValueLeft); else return y(d.Sn); } })
                        .attr("transform", "translate(" + minValueBottomAxis + ", 0)")
                        .attr("r", function (d) { if (d.Sn) return 4; else return 0; })
                        .on('mouseover', function () { d3.select(this).transition().duration(500).attr('stroke', 'green').attr('stroke-width', 3) })
                        .on('mouseout', function () { d3.select(this).transition().duration(500).attr('stroke', 'red').attr('stroke-width', 1) })
                        .append("svg:title").text(function (d) { if (d.Sn) return "Sn: " + d.Sn; else return ''; })
                    svg.append("path").datum(data).attr("fill", "none").attr("stroke", "red").attr("stroke-width", 2)
                        .attr("d", d3.line().curve(d3.curveMonotoneX).x(function (d) { return x(d.HeatName) }).y(function (d) { if (d.Sn) { if (d.Sn > maxValueLeft) return y(maxValueLeft); else if (d.Sn < minValueLeft) return y(minValueLeft); else return y(d.Sn); } else return 0; }))
                        .attr("transform", "translate(" + minValueBottomAxis + ", 0)")
                    // P (blue)
                    svg.selectAll("whatever").data(data).enter().append("circle").attr("fill", "blue")
                        .attr("cx", function (d) { if (d.P) return x(d.HeatName); })
                        .attr("cy", function (d) { if (d.P) { if (d.P > maxValueLeft) return y(maxValueLeft); else if (d.P < minValueLeft) return y(minValueLeft); else return y(d.P); } })
                        .attr("transform", "translate(" + minValueBottomAxis + ", 0)")
                        .attr("r", function (d) { if (d.P) return 4; else return 0; })
                        .on('mouseover', function () { d3.select(this).transition().duration(500).attr('stroke', 'green').attr('stroke-width', 3) })
                        .on('mouseout', function () { d3.select(this).transition().duration(500).attr('stroke', 'blue').attr('stroke-width', 1) })
                        .append("svg:title").text(function (d) { if (d.P) return "P: " + d.P; else return ''; })
                    svg.append("path").datum(data).attr("fill", "none").attr("stroke", "blue").attr("stroke-width", 2)
                        .attr("d", d3.line().curve(d3.curveMonotoneX).x(function (d) { return x(d.HeatName) }).y(function (d) { if (d.P) { if (d.P > maxValueLeft) return y(maxValueLeft); else if (d.P < minValueLeft) return y(minValueLeft); else return y(d.P); } else return 0; }))
                        .attr("transform", "translate(" + minValueBottomAxis + ", 0)")
                    // Zn (green)
                    svg.selectAll("whatever").data(data).enter().append("circle").attr("fill", "green")
                        .attr("cx", function (d) { if (d.Zn) return x(d.HeatName); })
                        .attr("cy", function (d) { if (d.Zn) { if (d.Zn > maxValueLeft) return y(maxValueLeft); else if (d.Zn < minValueLeft) return y(minValueLeft); else return y(d.Zn); } })
                        .attr("transform", "translate(" + minValueBottomAxis + ", 0)")
                        .attr("r", function (d) { if (d.Zn) return 4; else return 0; })
                        .on('mouseover', function () { d3.select(this).transition().duration(500).attr('stroke', 'green').attr('stroke-width', 3) })
                        .on('mouseout', function () { d3.select(this).transition().duration(500).attr('stroke', 'blue').attr('stroke-width', 1) })
                        .append("svg:title").text(function (d) { if (d.Zn) return "Zn: " + d.Zn; else return ''; })
                    svg.append("path").datum(data).attr("fill", "none").attr("stroke", "green").attr("stroke-width", 2)
                        .attr("d", d3.line().curve(d3.curveMonotoneX).x(function (d) { return x(d.HeatName) }).y(function (d) { if (d.Zn) { if (d.Zn > maxValueLeft) return y(maxValueLeft); else if (d.Zn < minValueLeft) return y(minValueLeft); else return y(d.Zn); } else return 0; }))
                        .attr("transform", "translate(" + minValueBottomAxis + ", 0)")
                    // References
                    svg.append("circle").attr("cy", -270).attr("cx", 0).attr("color", "black").attr("r", 6)
                    svg.append("text").attr("y", -265).attr("x", 10).text("Mo")
                    svg.append("circle").attr("cy", -270).attr("cx", 50).attr("fill", "red").attr("r", 6)
                    svg.append("text").attr("y", -265).attr("x", 60).text("Sn")
                    svg.append("circle").attr("cy", -270).attr("cx", 100).attr("fill", "blue").attr("r", 6)
                    svg.append("text").attr("y", -265).attr("x", 110).text("P")
                    svg.append("circle").attr("cy", -270).attr("cx", 150).attr("fill", "green").attr("r", 6)
                    svg.append("text").attr("y", -265).attr("x", 160).text("Zn")
                }
            },
            injectionFluxes: function (data, id, width) {
                var domainBand = [];
                _.each(data, function (obj, i) { domainBand[i] = obj.HeatName; });
                var title = '';
                var chartWidth = width;
                var chartHeight = this.model.get('chartHeight');
                var heightOffset = this.model.get('heightOffset');
                var chartMarginLeft = 80;
                var maxAxisBottom = chartWidth - 100;
                var maxAxisLeftHeight = 250;
                var minValueBottomAxis = 0;
                var maxValueLeft = 480;
                var minValueLeft = 280;
                if (data.length > 0) {
                    minValueBottomAxis = maxAxisBottom / (2 * data.length);
                    var svg = d3.select(id)
                        .append("svg").attr("width", chartWidth).attr("height", chartHeight)
                        .append("g").attr("transform", "translate(" + chartMarginLeft + "," + heightOffset + ")");
                    var x = d3.scaleBand().domain(domainBand).range([0, maxAxisBottom]);
                    svg.append("g").attr("class", 'x_text dashed')
                        .call(d3.axisBottom(x).tickSize(-maxAxisLeftHeight).ticks(7))
                        .select(".domain").attr("class", 'wdashed');
                    svg.selectAll("g").select(".x_text .tick text")
                        .attr("y", function (d, i) { if (i % 2 == 0) return 3; else return 17; })
                    var y = d3.scaleLinear().domain([minValueLeft, maxValueLeft]).range([0, -maxAxisLeftHeight]);
                    svg.append("g").attr("class", "y_text dashed")
                        .call(d3.axisLeft(y).tickSize(-maxAxisBottom).ticks(10))
                        .select(".domain").remove();
                    svg.selectAll("g").select(".y_text .tick text").attr("x", function (d, i) { return -20; });
                    if (id == '.injection_fluxes_EAFA') {
                        this.model.set({ hasData_injection_fluxes_EAFA: true });
                    } else if (id == '.injection_fluxes_EAFB') {
                        this.model.set({ hasData_injection_fluxes_EAFB: true });
                    }
                    // SpecificEnergy (red)
                    svg.selectAll("whatever").data(data).enter().append("circle").attr("fill", "red")
                        .attr("cx", function (d) { if (d.SpecificEnergy) return x(d.HeatName); })
                        .attr("cy", function (d) { if (d.SpecificEnergy) { if (d.SpecificEnergy > maxValueLeft) return y(maxValueLeft); else if (d.SpecificEnergy < minValueLeft) return y(minValueLeft); else return y(d.SpecificEnergy); } })
                        .attr("transform", "translate(" + minValueBottomAxis + ", 0)")
                        .attr("r", function (d) { if (d.SpecificEnergy) return 4; else return 0; })
                        .on('mouseover', function () { d3.select(this).transition().duration(500).attr('stroke', 'red').attr('stroke-width', 3) })
                        .on('mouseout', function () { d3.select(this).transition().duration(500).attr('stroke', 'red').attr('stroke-width', 1) })
                        .append("svg:title").text(function (d) { if (d.SpecificEnergy) return "SpecificEnergy: " + d.SpecificEnergy; else return ''; })
                    svg.append("path").datum(data).attr("fill", "none").attr("stroke", "red").attr("stroke-width", 2)
                        .attr("d", d3.line().curve(d3.curveMonotoneX).x(function (d) { return x(d.HeatName) }).y(function (d) { if (d.SpecificEnergy) { if (d.SpecificEnergy > maxValueLeft) return y(maxValueLeft); else if (d.SpecificEnergy < minValueLeft) return y(minValueLeft); else return y(d.SpecificEnergy); } else return 0; }))
                        .attr("transform", "translate(" + minValueBottomAxis + ", 0)")
                    // References
                    svg.append("circle").attr("cy", -270).attr("cx", 0).attr("fill", "red").attr("r", 6)
                    svg.append("text").attr("y", -265).attr("x", 10).text("KWH/Ton")
                }
            },
            bucketFluxes: function (data, id, width) {
                var domainBand = [];
                _.each(data, function (obj, i) { domainBand[i] = obj.HeatName; });
                var title = '';
                var chartWidth = width;
                var chartHeight = this.model.get('chartHeight');
                var heightOffset = this.model.get('heightOffset');
                var chartMarginLeft = 80;
                var maxAxisBottom = chartWidth - 100;
                var maxAxisLeftHeight = 250;
                var minValueBottomAxis = 0;
                var maxValueLeft = 10000;
                var minValueLeft = 0;
                if (data.length > 0) {
                    minValueBottomAxis = maxAxisBottom / (2 * data.length);
                    var svg = d3.select(id)
                        .append("svg").attr("width", chartWidth).attr("height", chartHeight)
                        .append("g").attr("transform", "translate(" + chartMarginLeft + "," + heightOffset + ")");
                    var x = d3.scaleBand().domain(domainBand).range([0, maxAxisBottom]);
                    svg.append("g").attr("class", 'x_text dashed')
                        .call(d3.axisBottom(x).tickSize(-maxAxisLeftHeight).ticks(7))
                        .select(".domain").attr("class", 'wdashed');
                    svg.selectAll("g").select(".x_text .tick text")
                        .attr("y", function (d, i) { if (i % 2 == 0) return 3; else return 17; })
                    var y = d3.scaleLinear().domain([minValueLeft, maxValueLeft]).range([0, -maxAxisLeftHeight]);
                    svg.append("g").attr("class", "y_text dashed")
                        .call(d3.axisLeft(y).tickSize(-maxAxisBottom).ticks(10))
                        .select(".domain").remove();
                    svg.selectAll("g").select(".y_text .tick text").attr("x", function (d, i) { return -20; });
                    if (id == '.bucket_fluxes_EAFA') {
                        this.model.set({ hasData_bucket_fluxes_EAFA: true });
                    } else if (id == '.bucket_fluxes_EAFB') {
                        this.model.set({ hasData_bucket_fluxes_EAFB: true });
                    }
                    // Helper to add a series
                    var that = this;
                    function addSeries(field, color, label) {
                        svg.selectAll("whatever").data(data).enter().append("circle").attr("fill", color)
                            .attr("cx", function (d) { if (d[field]) return x(d.HeatName); })
                            .attr("cy", function (d) { if (d[field]) { if (d[field] > maxValueLeft) return y(maxValueLeft); else if (d[field] < minValueLeft) return y(minValueLeft); else return y(d[field]); } })
                            .attr("transform", "translate(" + minValueBottomAxis + ", 0)")
                            .attr("r", function (d) { if (d[field]) return 4; else return 0; })
                            .on('mouseover', function () { d3.select(this).transition().duration(500).attr('stroke', color).attr('stroke-width', 3) })
                            .on('mouseout', function () { d3.select(this).transition().duration(500).attr('stroke', color).attr('stroke-width', 1) })
                            .append("svg:title").text(function (d) { if (d[field]) return label + ": " + d[field]; else return ''; })
                        svg.append("path").datum(data).attr("fill", "none").attr("stroke", color).attr("stroke-width", 2)
                            .attr("d", d3.line().curve(d3.curveMonotoneX).x(function (d) { return x(d.HeatName) }).y(function (d) { if (d[field]) { if (d[field] > maxValueLeft) return y(maxValueLeft); else if (d[field] < minValueLeft) return y(minValueLeft); else return y(d[field]); } else return 0; }))
                            .attr("transform", "translate(" + minValueBottomAxis + ", 0)")
                    }
                    addSeries('HiCal', 'blue', 'HiCal');
                    addSeries('Dolo', 'red', 'Dolo');
                    addSeries('Carbon', 'green', 'Carbon');
                    addSeries('InjCarbon', '#e0640d', 'Inj Carbon');
                    addSeries('InjCarbon_N', '#2aa89d', 'N. Inj Carbon');
                    addSeries('TotalCarbon', '#bd2dbf', 'Total Carbon');
                    addSeries('InjDolo', '#14213d', 'Dolo Injection');
                    // References (two rows)
                    svg.append("circle").attr("cy", -274).attr("cx", 0).attr("fill", "blue").attr("r", 6)
                    svg.append("text").attr("y", -270).attr("x", 10).text("HiCal").attr("class", "reference")
                    svg.append("circle").attr("cy", -260).attr("cx", 0).attr("fill", "red").attr("r", 6)
                    svg.append("text").attr("y", -255).attr("x", 10).text("Charge Dolo").attr("class", "reference")
                    svg.append("circle").attr("cy", -274).attr("cx", 90).attr("fill", "green").attr("r", 6)
                    svg.append("text").attr("y", -270).attr("x", 100).text("Charge Carbon").attr("class", "reference")
                    svg.append("circle").attr("cy", -260).attr("cx", 90).attr("fill", "#e0640d").attr("r", 6)
                    svg.append("text").attr("y", -255).attr("x", 100).text("#1&4 Inj. Carbon").attr("class", "reference")
                    svg.append("circle").attr("cy", -274).attr("cx", 210).attr("fill", "#2aa89d").attr("r", 6)
                    svg.append("text").attr("y", -270).attr("x", 220).text("N. Inj Carbon").attr("class", "reference")
                    svg.append("circle").attr("cy", -260).attr("cx", 210).attr("fill", "#bd2dbf").attr("r", 6)
                    svg.append("text").attr("y", -255).attr("x", 220).text("Tot Carbon").attr("class", "reference")
                    svg.append("circle").attr("cy", -274).attr("cx", 310).attr("fill", "#14213d").attr("r", 6)
                    svg.append("text").attr("y", -270).attr("x", 320).text("Dolo Injection").attr("class", "reference")
                }
            },
            baghouse_Chart: function (id, width) {
                var baghouseData = this.model.itemsBaghouse.toJSON()
                var params = this.model.toJSON();
                var minDatetime = new moment(params.productionDate, 'MM/DD/YYYY').format('YYYY-MM-DD') + ' ' + (params.shiftId === 'D' ? '09:00:00' : '21:00:00')
                var maxDatetime = (params.shiftId === 'D' ? new moment(params.productionDate, 'MM/DD/YYYY').format('YYYY-MM-DD') : new moment(params.productionDate, 'MM/DD/YYYY').add(1, 'days').format('YYYY-MM-DD'))
                    + ' ' + (params.shiftId === 'D' ? '21:00:00' : '09:00:00');
                var title = '';
                var chartWidth = width;
                var chartHeight = 410;
                var chartMarginLeft = 110;
                var maxAxisBottom = chartWidth - 100;
                var maxAxisLeftHeight = 300;
                var minValueBottomAxis = 0;
                if (baghouseData.length > 0) {
                    minValueBottomAxis = maxAxisBottom / (2 * baghouseData.length);
                    var svg = d3.select(id)
                        .append("svg").attr("width", chartWidth + chartMarginLeft).attr("height", chartHeight)
                        .append("g").attr("transform", "translate(" + chartMarginLeft + "," + 350 + ")");
                    var x = d3.scaleTime()
                        .domain([new Date(minDatetime), new Date(maxDatetime)])
                        .range([0, maxAxisBottom]);
                    svg.append("g").attr("class", 'x_text dashed')
                        .call(d3.axisBottom(x).tickSize(-maxAxisLeftHeight).ticks(20))
                        .select(".domain").attr("class", 'wdashed');
                    svg.selectAll("g").select(".x_text .tick text")
                        .attr("y", function (d, i) { if (i % 2 == 0) return 3; else return 17; })
                    // Y axis 1: RU (black) 1000-3600
                    var y = d3.scaleLinear().domain([1000, 3600]).range([0, -maxAxisLeftHeight]);
                    svg.append("g").attr("class", "y_text dashed").call(d3.axisLeft(y)).select(".domain").remove();
                    svg.selectAll("g").select(".y_text .tick text").attr("x", function (d, i) { return -20; });
                    // Y axis 2: DU (red) 0-100
                    var y0 = d3.scaleLinear().domain([0, 100]).range([0, -maxAxisLeftHeight]);
                    svg.append("g").attr("class", "y0_text dashed").style("fill", "red").call(d3.axisLeft(y0)).select(".domain").remove();
                    svg.selectAll("g").select(".y0_text .tick text").style("fill", "red").attr("x", function (d, i) { return -64; });
                    // Y axis 3: VA (blue) -20 to 0
                    var y1 = d3.scaleLinear().domain([-20, 0]).range([0, -maxAxisLeftHeight]);
                    svg.append("g").attr("class", "y1_text dashed").style("fill", "blue").call(d3.axisLeft(y1)).select(".domain").remove();
                    svg.selectAll("g").select(".y1_text .tick text").style("fill", "blue").attr("x", function (d, i) { return -96; });
                    if (id == '.baghouse') {
                        this.model.set({ hasData_baghouse: true });
                    }
                    // RU line (black)
                    var ru_data = _.where(baghouseData, { tagName: 'RU' });
                    svg.append("path").datum(ru_data).attr("fill", "none").attr("stroke", "black").attr("stroke-width", 1)
                        .attr("d", d3.line().curve(d3.curveMonotoneX)
                            .x(function (d) { return x(new Date(d.datetime)) })
                            .y(function (d) { if (d.value > 3600) return y(3600); else if (d.value < 1000) return y(1000); else return y(d.value); }))
                    // DU line (red)
                    var du_data = _.where(baghouseData, { tagName: 'DU' });
                    svg.append("path").datum(du_data).attr("fill", "none").attr("stroke", "red").attr("stroke-width", 1)
                        .attr("d", d3.line().curve(d3.curveMonotoneX)
                            .x(function (d) { return x(new Date(d.datetime)) })
                            .y(function (d) { if (d.value > 100) return y0(100); else if (d.value < 0) return y0(0); else return y0(d.value); }))
                    // VA line (blue)
                    var va_data = _.where(baghouseData, { tagName: 'VA' });
                    svg.append("path").datum(va_data).attr("fill", "none").attr("stroke", "blue").attr("stroke-width", 1)
                        .attr("d", d3.line().curve(d3.curveMonotoneX)
                            .x(function (d) { return x(new Date(d.datetime)) })
                            .y(function (d) { if (d.value > 0) return y1(0); else if (d.value < -150) return y1(-150); else return y1(d.value); }))
                    // References
                    svg.append("circle").attr("cy", -325).attr("cx", 15).attr("fill", "blue").attr("r", 6)
                    svg.append("text").attr("y", -320).attr("x", 30).text("Convey Vacuum")
                    svg.append("circle").attr("cy", -325).attr("cx", 140).attr("fill", "red").attr("r", 6)
                    svg.append("text").attr("y", -320).attr("x", 155).text("Dust Silo Level")
                    svg.append("circle").attr("cy", -325).attr("cx", 260).attr("fill", "black").attr("r", 6)
                    svg.append("text").attr("y", -320).attr("x", 275).text("Convey Runtime")
                }
            },

            // Build totals ag-Grid
            buildTotalsGrid: function () {
                var m = this.model.toJSON();
                var v = function (val) { return (val !== undefined && val !== null) ? val : '-'; };
                var metrics = [
                    { label: 'Total Heats', a: m.EAF_A_TotalHeats, b: m.EAF_B_TotalHeats, t: m.TOTAL_TotalHeats, highlight: true },
                    { label: 'Total Steel Weight', a: m.EAF_A_TotalSteelWeight, b: m.EAF_B_TotalSteelWeight, t: m.TOTAL_TotalSteelWeight, highlight: true },
                    { label: 'Total Scrap Weight', a: m.EAF_A_TotalScrapWeight, b: m.EAF_B_TotalScrapWeight, t: m.TOTAL_TotalScrapWeight },
                    { label: 'Total MWH', a: m.EAF_A_TotalElectricEnergy, b: m.EAF_B_TotalElectricEnergy, t: m.TOTAL_TotalElectricEnergy },
                    { label: 'Electrode Adds', a: m.EAF_A_TotalElectrodeAdds, b: m.EAF_B_TotalElectrodeAdds, t: m.TOTAL_TotalElectrodeAdds },
                    { divider: true },
                    { label: 'Avg Steel Weight', a: m.EAF_A_SteelWeight, b: m.EAF_B_SteelWeight, t: m.TOTAL_SteelWeight, std: m.TOTAL_SteelWeightSTD },
                    { label: 'Avg Scrap Weight', a: m.EAF_A_ScrapWeight, b: m.EAF_B_ScrapWeight, t: m.TOTAL_ScrapWeight, std: m.TOTAL_ScrapWeightSTD },
                    { label: 'Fluxes', a: m.EAF_A_Fluxes, b: m.EAF_B_Fluxes, t: m.TOTAL_Fluxes },
                    { label: 'Yield', a: m.EAF_A_Yield, b: m.EAF_B_Yield, t: m.TOTAL_Yield, std: m.TOTAL_YieldSTD },
                    { label: 'Tap to Tap', a: m.EAF_A_TapToTap, b: m.EAF_B_TapToTap, t: m.TOTAL_TapToTap, std: m.TOTAL_TapToTapSTD },
                    { label: 'Power On', a: m.EAF_A_PowerOn, b: m.EAF_B_PowerOn, t: m.TOTAL_PowerOn, std: m.TOTAL_PowerOnSTD },
                    { label: 'Injected C', a: m.EAF_A_InjectedC, b: m.EAF_B_InjectedC, t: m.TOTAL_InjectedC },
                    { label: 'Last Temp', a: m.EAF_A_EAFLastTemperature, b: m.EAF_B_EAFLastTemperature, t: m.TOTAL_EAFLastTemperature },
                    { label: 'KWH/Ton', a: m.EAF_A_SpecificEnergy, b: m.EAF_B_SpecificEnergy, t: m.TOTAL_SpecificEnergy, std: m.TOTAL_SpecificEnergySTD },
                    { label: 'Electric Power', a: m.EAF_A_ElectricPower, b: m.EAF_B_ElectricPower, t: m.TOTAL_ElectricPower },
                    { label: 'SmartARC', a: m.EAF_A_SmartARC, b: m.EAF_B_SmartARC, t: m.TOTAL_SmartARC },
                    { divider: true },
                    { label: 'Spray Ring Eff.', a: m.EAFASprayRingEffectiveness, b: m.EAFBSprayRingEffectiveness, t: '-' },
                    { label: 'Bypass (Util | Cond)', a: m.EAFABypass, b: '-', t: '-' },
                ];

                var html = '<table class="sr-totals-table">' +
                    '<thead><tr>' +
                    '<th class="sr-tt-label">Metric</th>' +
                    '<th class="sr-tt-val">EAF A</th>' +
                    '<th class="sr-tt-val">EAF B</th>' +
                    '<th class="sr-tt-val sr-tt-total-col">Total / Avg</th>' +
                    '</tr></thead><tbody>';

                metrics.forEach(function (row) {
                    if (row.divider) {
                        html += '<tr class="sr-tt-divider"><td colspan="4"></td></tr>';
                        return;
                    }
                    var totalDisplay = v(row.t);
                    if (row.std && row.std !== '-') {
                        totalDisplay += ' <span class="sr-tt-std">\u00B1 ' + row.std + '</span>';
                    }
                    var cls = row.highlight ? ' sr-tt-highlight' : '';
                    html += '<tr class="sr-tt-row' + cls + '">' +
                        '<td class="sr-tt-label">' + row.label + '</td>' +
                        '<td class="sr-tt-val">' + v(row.a) + '</td>' +
                        '<td class="sr-tt-val">' + v(row.b) + '</td>' +
                        '<td class="sr-tt-val sr-tt-total-col">' + totalDisplay + '</td>' +
                        '</tr>';
                });
                html += '</tbody></table>';
                this.$el.find('.totals-grid-container').html(html);
            },

            render: function (container, viewParams) {
                var that = this;
                var thatContainer = (this.options.container) ? this.options.container : container;
                this.options.container = thatContainer;
                var customPath = '/app/pages/eaf/' + this.template + '/';

                T.render.call(
                    this,
                    this.template,
                    function (tmp) {
                        if (!that.options.i18n) that.options.i18n = {};
                        app.getI18NJed(
                            that,
                            that.template,
                            function (i18nJED) {
                                that.options.i18n[that.template] = i18nJED;
                                that.$el.html(tmp());
                                that.applyBindings();
                                that.append(thatContainer, that.$el);

                                // Totals - rendered as custom HTML table in buildTotalsGrid()

                                // Delays ag-Grid
                                that.delaysGrid = new AgGridModule({
                                    columnDefs: [
                                        { field: 'Furnace', headerName: 'Furnace', flex: 1 },
                                        { field: 'TopDelayCodeReason', headerName: 'Area', flex: 2 },
                                        { field: 'Code', headerName: 'Code', flex: 1 },
                                        { field: 'Duration', headerName: 'Min', fieldType: 'Integer', flex: 1 },
                                        { field: 'Start', headerName: 'Start', fieldType: 'Datetime', format: 'HH:mm', flex: 1 },
                                        { field: 'End', headerName: 'End', fieldType: 'Datetime', format: 'HH:mm', flex: 1 },
                                    ],
                                    pagination: false,
                                    gridOptions: {
                                        defaultColDef: { sortable: false, filter: false, resizable: true, suppressMenu: true },
                                    }
                                });
                                that.delaysGrid.render();
                                that.$el.find('.delays-grid-container').append(that.delaysGrid.$el);

                                // Slag ag-Grid
                                that.slagGrid = new AgGridModule({
                                    columnDefs: [
                                        { field: 'heatName', headerName: 'Heat', flex: 1 },
                                        { field: 'timestamp', headerName: 'Time', flex: 1 },
                                        { field: 'CaO', headerName: 'CaO', fieldType: 'Float', decimals: 1, flex: 1 },
                                        { field: 'SiO2', headerName: 'SiO2', fieldType: 'Float', decimals: 1, flex: 1 },
                                        { field: 'Al2O3', headerName: 'Al2O3', fieldType: 'Float', decimals: 1, flex: 1 },
                                        { field: 'TiO2', headerName: 'TiO2', fieldType: 'Float', decimals: 1, flex: 1 },
                                        { field: 'B3ratio', headerName: 'B3', fieldType: 'Float', decimals: 2, flex: 1,
                                            cellClassRules: {
                                                'slag-danger': function(params) { return params.data && params.data.B3Status === 'DANGER'; },
                                                'slag-warning': function(params) { return params.data && params.data.B3Status === 'WARNING'; },
                                                'slag-normal': function(params) { return params.data && params.data.B3Status === 'NORMAL'; },
                                            }
                                        },
                                        { field: 'MgO', headerName: 'MgO', fieldType: 'Float', decimals: 1, flex: 1,
                                            cellClassRules: {
                                                'slag-danger': function(params) { return params.data && params.data.MgOStatus === 'DANGER'; },
                                            }
                                        },
                                        { field: 'FeO', headerName: 'FeO', fieldType: 'Float', decimals: 1, flex: 1,
                                            cellClassRules: {
                                                'slag-danger': function(params) { return params.data && params.data.FeOStatus === 'DANGER'; },
                                            }
                                        },
                                        { field: 'MnO', headerName: 'MnO', fieldType: 'Float', decimals: 1, flex: 1 },
                                        { field: 'total', headerName: 'Total', fieldType: 'Float', decimals: 1, flex: 1,
                                            cellClassRules: {
                                                'slag-danger': function(params) { return params.data && params.data.TotalStatus === 'DANGER'; },
                                            }
                                        },
                                        { field: 'Cr2O3', headerName: 'Cr2O3', fieldType: 'Float', decimals: 1, flex: 1 },
                                        { field: 'P2O5', headerName: 'P2O5', fieldType: 'Float', decimals: 1, flex: 1 },
                                        { field: 'SO3', headerName: 'SO3', fieldType: 'Float', decimals: 1, flex: 1 },
                                        { field: 'dirtLoad', headerName: 'Dirt Load', fieldType: 'Float', decimals: 2, flex: 1 },
                                        { field: 'TotalSlagVolume', headerName: 'Slag Vol', fieldType: 'Float', decimals: 2, flex: 1 },
                                        { field: 'refractoryWear', headerName: 'Ref Wear', fieldType: 'Float', decimals: 2, flex: 1,
                                            cellClassRules: {
                                                'slag-danger': function(params) { return params.data && params.data.RefractoryWearStatus === 'DANGER'; },
                                                'slag-normal': function(params) { return params.data && params.data.RefractoryWearStatus === 'NORMAL'; },
                                            }
                                        },
                                        { field: 'steelLoss', headerName: 'Steel Loss', fieldType: 'Float', decimals: 2, flex: 1 },
                                    ],
                                    pagination: false,
                                    gridOptions: {
                                        defaultColDef: { sortable: false, filter: false, resizable: true, suppressMenu: true },
                                        domLayout: 'autoHeight',
                                    }
                                });
                                that.slagGrid.render();
                                that.$el.find('.slag-grid-container').append(that.slagGrid.$el);

                                // LogBook ag-Grid
                                that.logBookGrid = new AgGridModule({
                                    columnDefs: [
                                        { field: 'Timestamp', headerName: 'Time' },
                                        { field: 'ShiftId', headerName: 'Shift' },
                                        { field: 'CrewId', headerName: 'Crew' },
                                        { field: 'Title', headerName: 'Title' },
                                        { field: 'Description', headerName: 'Description', flex: 2 },
                                        { field: 'Author', headerName: 'Author' },
                                    ],
                                    pagination: false,
                                    gridOptions: {
                                        defaultColDef: { sortable: false, filter: false, resizable: true, suppressMenu: true },
                                        domLayout: 'autoHeight',
                                    }
                                });
                                that.logBookGrid.render();
                                that.$el.find('.logbook-grid-container').append(that.logBookGrid.$el);

                                that.applyBindings();

                                that.$el.find('.input-date').datepicker();

                                // Append view to main container
                                that.append(thatContainer, that.$el);

                                // Load pickers synchronously
                                that.model.fetchPickers({ async: false });

                                var fixedParams = _.extend({}, viewParams);
                                if (fixedParams.productionDate)
                                    fixedParams.productionDate = new moment(fixedParams.productionDate, 'YYYYMMDD').format('MM/DD/YYYY');
                                if (fixedParams.shiftId)
                                    fixedParams.shiftId = fixedParams.shiftId;
                                that.model.set(fixedParams);

                                // Set loading state to false when everything is ready
                                that.model.set({ isPageLoading: false });

                                that.refresh();
                                that.UpdateURL();
                            },
                            true,
                            customPath
                        );
                    },
                    customPath
                );
            },

            refresh: function () {
                try {
                    // Clear chart containers
                    this.$el.find('.furnace1-gantt-screen').html('');
                    this.$el.find('.furnace2-gantt-screen').html('');
                    this.$el.find('.yield').html('');
                    this.$el.find('.temperature_EAFA').html('');
                    this.$el.find('.oxygen_EAFA').html('');
                    this.$el.find('.residuals_EAFA').html('');
                    this.$el.find('.residuals_EAFA_2').html('');
                    this.$el.find('.bucket_fluxes_EAFA').html('');
                    this.$el.find('.injection_fluxes_EAFA').html('');
                    this.$el.find('.temperature_EAFB').html('');
                    this.$el.find('.oxygen_EAFB').html('');
                    this.$el.find('.residuals_EAFB').html('');
                    this.$el.find('.residuals_EAFB_2').html('');
                    this.$el.find('.bucket_fluxes_EAFB').html('');
                    this.$el.find('.injection_fluxes_EAFB').html('');
                    this.$el.find('.baghouse').html('');

                    var params = this.model.toJSON();
                    var shift = '1';
                    if (params.shiftId == 'D') shift = '1';
                    else if (params.shiftId == 'N') shift = '2';

                    this.model.set({
                        isLoading: true,
                        hasData: false,
                    });
                    this.model.fetchData({
                        productionDate: params.productionDate,
                        shiftId: shift,
                    });
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            },

            UpdateNav: function (nav) {
                var params = this.model.toJSON();
                if (nav && this.model.get('shiftId') == 'N')
                    this.model.set('productionDate', new moment(params.productionDate, 'MM/DD/YYYY').add(1, 'day').format('MM/DD/YYYY'));
                if (!nav && this.model.get('shiftId') == 'D')
                    this.model.set('productionDate', new moment(params.productionDate, 'MM/DD/YYYY').subtract(1, 'day').format('MM/DD/YYYY'));
                if (this.model.get('shiftId') == 'N') this.model.set('shiftId', 'D');
                else if (this.model.get('shiftId') == 'D') this.model.set('shiftId', 'N');
                this.UpdateURL();
                this.refresh();
            },

            UpdateURL: function () {
                try {
                    var params = this.model.toJSON();
                    app.router.navigate(
                        app.router.resolveURL(
                            app.router.currentModule,
                            _.extend({}, params, {
                                productionDate: new moment(params.productionDate, "MM/DD/YYYY").format("YYYYMMDD"),
                            }),
                            false
                        ),
                        { trigger: false }
                    );
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            },

            fetched: function (resp) {
                var model = this.model.toJSON();
                var shiftId = model.shiftId;
                var productionDate = model.productionDate;
                var crewId = model.crewId;
                this.model.set({
                    isLoading: false,
                    renderCompleted: true,
                    hasData:
                        (resp && resp.Table && resp.Table.length > 0) ||
                        (resp && resp.Table1 && resp.Table1.length > 0) ||
                        (resp && resp.Table2 && resp.Table2.length > 0) ? true : false,
                });
            },

            // -- Collection ready handlers
            itemsBaghouse_ready: function () {
                try {
                    var that = this;
                    setTimeout(function () {
                        that.model.set({ hasData_baghouse: (that.model.itemsBaghouse.length > 0) });
                    }, 100);
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            },
            itemsSlag_ready: function () {
                try {
                    var that = this;
                    setTimeout(function () {
                        that.model.set({ hasData_Slag: (that.model.itemsSlag.length > 0), isLoading_Slag: false });
                        if (that.slagGrid) that.slagGrid.setRowDataAsCollection(that.model.itemsSlag);
                    }, 100);
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            },
            itemsTemperature_ready: function () {
                try {
                    var that = this;
                    setTimeout(function () {
                        that.model.set({ hasData_Temperature: (that.model.itemsTemperature.length > 0), isLoading_Temperature: false });
                        if (that.model.get('hasData_yield')) {
                            that.getYieldChart('.yield', 1200, 500);
                        }
                        that.baghouse_Chart(".baghouse", 1000);
                        if (that.model.get('hasData_Temperature')) {
                            that.residualsChart(that.model.itemsTemperature.getDataChartEAFA(), ".residuals_EAFA", 750);
                            that.residualsChart2(that.model.itemsTemperature.getDataChartEAFA(), ".residuals_EAFA_2", 750);
                            that.residualsChart(that.model.itemsTemperature.getDataChartEAFB(), ".residuals_EAFB", 750);
                            that.residualsChart2(that.model.itemsTemperature.getDataChartEAFB(), ".residuals_EAFB_2", 750);
                            that.temperatureChart(that.model.itemsTemperature.getDataChartEAFA(), ".temperature_EAFA", 750);
                            that.oxygenChart(that.model.itemsTemperature.getDataChartEAFA(), ".oxygen_EAFA", 750);
                            that.temperatureChart(that.model.itemsTemperature.getDataChartEAFB(), ".temperature_EAFB", 750);
                            that.oxygenChart(that.model.itemsTemperature.getDataChartEAFB(), ".oxygen_EAFB", 750);
                        }
                        that.referencesChart();
                    }, 100);
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            },
            itemsBucketFluxesEAFA_ready: function () {
                try {
                    var that = this;
                    setTimeout(function () {
                        that.model.set({ hasData_bucket_fluxes_EAFA: (that.model.itemsBucketFluxesEAFA.length > 0), isLoading_bucket_fluxes_EAFA: false });
                        if (that.model.get('hasData_bucket_fluxes_EAFA')) {
                            that.bucketFluxes(that.model.itemsBucketFluxesEAFA.getDataChart(), ".bucket_fluxes_EAFA", 750);
                        }
                    }, 100);
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            },
            itemsBucketFluxesEAFB_ready: function () {
                try {
                    var that = this;
                    setTimeout(function () {
                        that.model.set({ hasData_bucket_fluxes_EAFB: (that.model.itemsBucketFluxesEAFB.length > 0), isLoading_bucket_fluxes_EAFB: false });
                        if (that.model.get('hasData_bucket_fluxes_EAFB')) {
                            that.bucketFluxes(that.model.itemsBucketFluxesEAFB.getDataChart(), ".bucket_fluxes_EAFB", 750);
                        }
                    }, 100);
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            },
            itemsInjectionFluxesEAFA_ready: function () {
                try {
                    var that = this;
                    setTimeout(function () {
                        that.model.set({ hasData_injection_fluxes_EAFA: (that.model.itemsInjectionFluxesEAFA.length > 0), isLoading_injection_fluxes_EAFA: false });
                        if (that.model.get('hasData_injection_fluxes_EAFA')) {
                            that.injectionFluxes(that.model.itemsInjectionFluxesEAFA.getDataChart(), ".injection_fluxes_EAFA", 750);
                        }
                    }, 100);
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            },
            itemsInjectionFluxesEAFB_ready: function () {
                try {
                    var that = this;
                    setTimeout(function () {
                        that.model.set({ hasData_injection_fluxes_EAFB: (that.model.itemsInjectionFluxesEAFB.length > 0), isLoading_injection_fluxes_EAFB: false });
                        if (that.model.get('hasData_injection_fluxes_EAFB')) {
                            that.injectionFluxes(that.model.itemsInjectionFluxesEAFB.getDataChart(), ".injection_fluxes_EAFB", 750);
                        }
                    }, 100);
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            },
            itemsRecipes_ready: function () {
                try {
                    var that = this;
                    setTimeout(function () {
                        that.model.set({ hasData_Recipes: (that.model.itemsRecipes.length > 0), isLoading_Recipes: false });
                    }, 100);
                    that.buildRecipesGrid();
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            },
            buildRecipesGrid: function () {
                var that = this;
                if (!that.model.itemsRecipes.length) return;
                var recipeNames = Object.keys(that.model.itemsRecipes.at(0).attributes).filter(function(key) { return key.endsWith('-Plan'); }).map(function(key) { return key.slice(0, -5); });
                var columnDefs = [{ field: 'Material', headerName: 'Material', pinned: 'left', width: 150 }];
                recipeNames.forEach(function (name) {
                    columnDefs.push({
                        headerName: name,
                        children: [
                            { field: name + '-Plan', headerName: 'Plan', minWidth: 70, flex: 1,
                              valueFormatter: function(params) { return params.value === null ? '0' : params.value; } },
                            { field: name + '-Actual', headerName: 'Actual', minWidth: 70, flex: 1,
                              valueFormatter: function(params) { return params.value === null ? '0' : params.value; } },
                        ]
                    });
                });
                var recipesGrid = new AgGridModule({
                    columnDefs: columnDefs,
                    pagination: false,
                    gridOptions: {
                        defaultColDef: { sortable: false, filter: false, resizable: true, cellStyle: { textAlign: 'center' }, suppressMenu: true },
                        domLayout: 'autoHeight',
                    }
                });
                recipesGrid.render();
                that.$el.find('.recipes-grid-container').empty().append(recipesGrid.$el);
                recipesGrid.setRowDataAsCollection(that.model.itemsRecipes);
            },
            recipesAverage_ready: function () {
                try {
                    var that = this;
                    setTimeout(function () {
                        that.model.set({ hasData_RecipesAverage: (that.model.itemsRecipesAverage.length > 0), isLoading_RecipesAverage: false });
                        that.buildRecipesAverageGrid();
                    }, 100);
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            },
            buildRecipesAverageGrid: function () {
                var that = this;
                if (!that.model.itemsRecipesAverage.length) return;
                var fixedColumns = [
                    { name: 'HeatsProduced', label: 'Heats Produced', highlight: false },
                    { name: 'Yield', label: '% Yield', highlight: false },
                    { name: 'CrMiss', label: 'Cr Miss', highlight: true },
                    { name: 'CuMiss', label: 'Cu Miss', highlight: true },
                    { name: 'MoMiss', label: 'Mo Miss', highlight: true },
                    { name: 'NiMiss', label: 'Ni Miss', highlight: true },
                    { name: 'PMiss', label: 'P Miss', highlight: true },
                    { name: 'PbMiss', label: 'Pb Miss', highlight: true },
                    { name: 'SnMiss', label: 'Sn Miss', highlight: true },
                ];
                var totalModels = that.model.itemsRecipesAverage.length;
                var columnWidth = totalModels > 0 ? (80 / totalModels) + '%' : 'auto';

                // Build transposed table
                var table = document.createElement('table');
                table.className = 'sr-recipes-avg-table';
                var thead = document.createElement('thead');
                var headerRow = document.createElement('tr');
                var thEmpty = document.createElement('th');
                thEmpty.textContent = '';
                headerRow.appendChild(thEmpty);
                that.model.itemsRecipesAverage.forEach(function (model) {
                    var th = document.createElement('th');
                    th.textContent = model.get('Recipe') || '';
                    th.style.width = columnWidth;
                    headerRow.appendChild(th);
                });
                thead.appendChild(headerRow);
                table.appendChild(thead);

                var tbody = document.createElement('tbody');
                table.appendChild(tbody);
                fixedColumns.forEach(function (column) {
                    var tr = document.createElement('tr');
                    var tdLabel = document.createElement('td');
                    tdLabel.className = 'sr-ravg-label';
                    tdLabel.textContent = column.label;
                    tr.appendChild(tdLabel);
                    that.model.itemsRecipesAverage.forEach(function (model) {
                        var cellValue = model.get(column.name) || '';
                        var tdValue = document.createElement('td');
                        tdValue.className = 'sr-ravg-value';
                        if (parseFloat(cellValue) > 0 && column.highlight) {
                            tdValue.style.backgroundColor = '#fecaca';
                            tdValue.style.fontWeight = 'bold';
                        }
                        tdValue.textContent = cellValue || '';
                        tdValue.style.width = columnWidth;
                        tr.appendChild(tdValue);
                    });
                    tbody.appendChild(tr);
                });
                that.$el.find('.recipes-average-grid-container').empty().append(table);
            },
            manualTemps_ready: function () {
                try {
                    var that = this;
                    setTimeout(function () {
                        that.model.set({ hasData_manualTemps: (that.model.itemsManualTemps.length > 0) });
                        that.buildManualTempGrid();
                    }, 100);
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            },
            buildManualTempGrid: function () {
                var that = this;
                var $container = that.$el.find('.manualtemps-grid-container');
                $container.empty();

                if (that.model.itemsManualTemps.length === 0) {
                    $container.append('<div class="sr-grid-empty-msg">Zero Manual Temperaturess Recorded</div>');
                    return;
                }

                var manualTempsGrid = new AgGridModule({
                    columnDefs: [
                        { field: 'FurnaceCode', headerName: 'EAF Name' },
                        { field: 'HeatName', headerName: 'Heat Name' },
                        { field: 'ManualTemp', headerName: 'Manual Temps' },
                    ],
                    pagination: false,
                    gridOptions: {
                        defaultColDef: { sortable: false, filter: false, resizable: true, cellStyle: { textAlign: 'center' }, suppressMenu: true },
                        domLayout: 'autoHeight',
                    }
                });
                manualTempsGrid.render();
                $container.append(manualTempsGrid.$el);
                manualTempsGrid.setRowDataAsCollection(that.model.itemsManualTemps);
            },
            tapHoleNonFreeOpen_ready: function () {
                try {
                    var that = this;
                    setTimeout(function () {
                        that.model.set({ hasData_tapHoleNonFreeOpen: (that.model.itemsTapHoleNonFreeOpen.length > 0) });
                        that.buildTapHoleNonFreeOpenGrid();
                    }, 100);
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            },
            buildTapHoleNonFreeOpenGrid: function () {
                var that = this;
                var $container = that.$el.find('.taphole-grid-container');
                $container.empty();

                if (that.model.itemsTapHoleNonFreeOpen.length === 0) {
                    $container.append('<div class="sr-grid-empty-msg">Zero Non-Free Opens Recorded</div>');
                    return;
                }

                var tapHoleGrid = new AgGridModule({
                    columnDefs: [
                        { field: 'FurnaceCode', headerName: 'EAF Name' },
                        { field: 'HeatName', headerName: 'Heat Name' },
                        { field: 'HeatsOnTapHole', headerName: 'Heats on Tap Hole' },
                        { field: 'NonFreeOpenDuration', headerName: 'Non Free Open Duration',
                            valueFormatter: function(params) {
                                if (params.value == null) return '';
                                return Math.round(params.value / 60) + ' mins.';
                            }
                        },
                    ],
                    pagination: false,
                    gridOptions: {
                        defaultColDef: { sortable: false, filter: false, resizable: true, cellStyle: { textAlign: 'center' }, suppressMenu: true },
                        domLayout: 'autoHeight',
                    }
                });
                tapHoleGrid.render();
                $container.append(tapHoleGrid.$el);
                tapHoleGrid.setRowDataAsCollection(that.model.itemsTapHoleNonFreeOpen);
            },
            itemsDelaysEAFA_ready: function () {
                try {
                    var that = this;
                    setTimeout(function () {
                        if (that.delaysGrid) {
                            that.delaysGrid.setRowDataAsCollection(that.model.itemsDelays);
                        }
                    }, 100);
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            },
            itemsPourback_ready: function () {
                try {
                    var that = this;
                    setTimeout(function () {
                        that.model.set({ hasData_Pourback: (that.model.itemsPourback.length > 0), isLoading_Pourback: false });
                    }, 100);
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            },
            itemsLogBook_ready: function () {
                try {
                    var that = this;
                    setTimeout(function () {
                        that.model.set({ hasData_LogBook: (that.model.itemsLogBook.length > 0), isLoading_LogBook: false });
                        if (that.logBookGrid) {
                            that.logBookGrid.setRowDataAsCollection(that.model.itemsLogBook);
                        }
                    }, 100);
                } catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
            },

            // Common functions
            append: function (container, el) {
                el = (el != null && el != undefined) ? el : this.$el;
                if (this.options.state == app.view_states.loading || this.options.state == app.view_states.shown) {
                    this.options.state = app.view_states.shown;
                    container.append(el);
                    this.options.onappend(this);
                }
                if (this.options.state == app.view_states.hidden) { container.append(el); }
                if (this.options.state == app.view_states.closed) { return; }
            },
            bindViewScopedEvents: function () { },
            unbindViewScopedEvents: function () { },
            close: function () {
                this.options.state = app.view_states.closed;
                this.remove();
                this.unbindViewScopedEvents();
                this.unbind();
            },
            show: function () {
                this.options.state = app.view_states.shown;
                this.bindEvents();
                this.$el.show();
            },
            hide: function () {
                this.options.state = app.view_states.hidden;
                this.$el.hide();
                this.unbind();
                this.stopListening();
            },
            preRender: function () {
                app.models.subnavbar.set('subnavbar', false);
            },
            reRender: function (viewParams) {
                try { this.refresh(); } catch (Error) { }
            }
        });

        Screen.Models.ItemAllHeat = Backbone.Epoxy.Model.extend({
            parse: function (obj) {
                var result = {
                    Furnace: obj.Furnace,
                    HeatName: obj.HeatName,
                    Start: new moment(obj.Start).format("YYYY-MM-DD HH:mm:ss"),
                    End: new moment(obj.End).format("YYYY-MM-DD HH:mm:ss"),
                    PowerOn: obj.PowerOn,
                };
                return result;
            },
        });

        Screen.Collections.ItemsAllHeats = Backbone.Collection.extend({
            model: Screen.Models.ItemAllHeat,
            furnace1_heats: [],
            furnace2_heats: [],
            setDataColl: function (data, shiftId, productionDate) {
                var newColl, that = this;
                that.setDataChart(data, shiftId, productionDate);
                newColl = _.map(data, that.model.prototype.parse);
                that.set(newColl).trigger('fetch', that, data);
            },
            setDataChart: function (heats, shiftId, prodDate) {
                var furnace1_heats = [], furnace2_heats = [], aux_heats = [];
                var aux_start = null, aux_end = null, shift = 'D', aux_duration = null;
                var START_D = new moment(prodDate + ' ' + '09:00:00', 'MM/DD/YYYY HH:mm:ss').format('YYYY-MM-DD HH:mm:ss'),
                    END_D = new moment(prodDate + ' ' + '21:00:00', 'MM/DD/YYYY HH:mm:ss').format('YYYY-MM-DD HH:mm:ss'),
                    START_N = new moment(prodDate + ' ' + '21:00:00', 'MM/DD/YYYY HH:mm:ss').format('YYYY-MM-DD HH:mm:ss'),
                    END_N = new moment(prodDate + ' ' + '09:00:00', 'MM/DD/YYYY HH:mm:ss').add(1, 'd').format('YYYY-MM-DD HH:mm:ss');
                if (shiftId == '1') shift = 'D';
                else if (shiftId == '2') shift = 'N';
                _.each(heats, function (obj) {
                    var start = new moment(obj.Start).format("YYYY-MM-DD HH:mm:ss");
                    var end = new moment(obj.End).format("YYYY-MM-DD HH:mm:ss");
                    var push = false;
                    if (shift == 'D') {
                        if (moment(START_D).isBefore(end) && moment(start).isBefore(END_D)) {
                            push = true;
                            if (moment(start).isBefore(START_D)) aux_start = START_D; else aux_start = start;
                            if (moment(END_D).isBefore(end)) aux_end = END_D; else aux_end = end;
                        }
                    } else if (shift == 'N') {
                        if (moment(START_N).isBefore(end) && moment(start).isBefore(END_N)) {
                            push = true;
                            if (moment(start).isBefore(START_N)) aux_start = START_N; else aux_start = start;
                            if (moment(END_N).isBefore(end)) aux_end = END_N; else aux_end = end;
                        }
                    }
                    if (push) {
                        var aux_start_diff = moment(new moment(aux_start, 'YYYY-MM-DD HH:mm:ss').format('YYYY-MM-DD HH:mm:ss'), 'YYYY-MM-DD HH:mm:ss');
                        var aux_end_diff = moment(new moment(aux_end, 'YYYY-MM-DD HH:mm:ss').format('YYYY-MM-DD HH:mm:ss'), 'YYYY-MM-DD HH:mm:ss');
                        var diffHours = aux_end_diff.diff(aux_start_diff, 'hours', true).toFixed(2);
                        var m = parseInt(new moment(aux_start, 'YYYY-MM-DD HH:mm:SS').format('mm')) / 60;
                        var s = parseInt(new moment(aux_start, 'YYYY-MM-DD HH:mm:SS').format('ss')) / 3600;
                        var aux = parseFloat(parseInt(new moment(aux_start, 'YYYY-MM-DD HH:mm:ss').format('HH')) + m + s).toFixed(2);
                        var aux_color = '#3ebf3c';
                        if (parseFloat(obj.Duration) > parseFloat(40) && parseFloat(obj.Duration) <= parseFloat(50)) aux_color = '#f3f317';
                        else if (parseFloat(obj.Duration) > parseFloat(50)) aux_color = '#ff5900';
                        if (obj.Furnace == 'EAF A') {
                            furnace1_heats.push({
                                type: 'heat', name: obj.HeatName.substr(obj.HeatName.length - 3),
                                diffHours: diffHours, duration: obj.Duration + "'", color: obj.Color,
                                start: new moment(obj.Start).format("YYYY-MM-DD HH:mm:ss"), startHour: aux,
                                end: new moment(obj.End).format("YYYY-MM-DD HH:mm:ss"), shift: shift,
                                powerOn: obj.PowerOn, enabledS1: obj.EnabledS1, enabledS2: obj.EnabledS2,
                                enabledS3: obj.EnabledS3, enabledS4: obj.EnabledS4, heatName: obj.HeatName,
                            });
                        } else if (obj.Furnace == 'EAF B') {
                            furnace2_heats.push({
                                type: 'heat', name: obj.HeatName.substr(obj.HeatName.length - 3),
                                diffHours: diffHours, duration: obj.Duration + "'", color: obj.Color,
                                start: new moment(obj.Start).format("YYYY-MM-DD HH:mm:ss"), startHour: aux,
                                end: new moment(obj.End).format("YYYY-MM-DD HH:mm:ss"), shift: shift,
                                powerOn: obj.PowerOn, enabledS1: obj.EnabledS1, enabledS2: obj.EnabledS2,
                                enabledS3: obj.EnabledS3, enabledS4: obj.EnabledS4, heatName: obj.HeatName,
                            });
                        }
                    }
                });
                this.furnace1_heats = furnace1_heats;
                this.furnace2_heats = furnace2_heats;
            },
            getHeats_furnace1: function () { return this.furnace1_heats; },
            getHeats_furnace2: function () { return this.furnace2_heats; },
        });

        Screen.Models.ItemAllDelay = Backbone.Epoxy.Model.extend({
            parse: function (obj) {
                var result = {
                    Furnace: obj.Furnace, Code: obj.Code,
                    Start: new moment(obj.Start).format("YYYY-MM-DD HH:mm:ss"),
                    End: new moment(obj.End).format("YYYY-MM-DD HH:mm:ss"),
                    Duration: obj.Duration, Color: obj.Color,
                };
                return result;
            },
        });
        Screen.Collections.ItemsAllDelays = Backbone.Collection.extend({
            model: Screen.Models.ItemAllDelay,
            furnace1_delays: [],
            furnace2_delays: [],
            setDataColl: function (data, shiftId, productionDate) {
                var newColl, that = this;
                that.setDataChart(data, shiftId, productionDate);
                newColl = _.map(data, that.model.prototype.parse);
                that.set(newColl).trigger('fetch', that, data);
            },
            setDataChart: function (delays, shiftId, prodDate) {
                var furnace1_delays = [], furnace2_delays = [], aux_delay = [];
                var aux_start = null, aux_end = null, shift = 'D', aux_duration = null;
                var START_D = new moment(prodDate + ' ' + '09:00:00', 'MM/DD/YYYY HH:mm:ss').format('YYYY-MM-DD HH:mm:ss'),
                    END_D = new moment(prodDate + ' ' + '21:00:00', 'MM/DD/YYYY HH:mm:ss').format('YYYY-MM-DD HH:mm:ss'),
                    START_N = new moment(prodDate + ' ' + '21:00:00', 'MM/DD/YYYY HH:mm:ss').format('YYYY-MM-DD HH:mm:ss'),
                    END_N = new moment(prodDate + ' ' + '09:00:00', 'MM/DD/YYYY HH:mm:ss').add(1, 'd').format('YYYY-MM-DD HH:mm:ss');
                if (shiftId == '1') shift = 'D';
                else if (shiftId == '2') shift = 'N';
                _.each(delays, function (obj) {
                    var start = new moment(obj.Start).format("YYYY-MM-DD HH:mm:ss");
                    var end = new moment(obj.End).format("YYYY-MM-DD HH:mm:ss");
                    var push = false;
                    if (shift == 'D') {
                        if (moment(START_D).isBefore(end) && moment(start).isBefore(END_D)) {
                            push = true;
                            if (moment(start).isBefore(START_D)) aux_start = START_D; else aux_start = start;
                            if (moment(END_D).isBefore(end)) aux_end = END_D; else aux_end = end;
                        }
                    } else if (shift == 'N') {
                        if (moment(START_N).isBefore(end) && moment(start).isBefore(END_N)) {
                            push = true;
                            if (moment(start).isBefore(START_N)) aux_start = START_N; else aux_start = start;
                            if (moment(END_N).isBefore(end)) aux_end = END_N; else aux_end = end;
                        }
                    }
                    if (push) {
                        var aux_start_diff = moment(new moment(aux_start, 'YYYY-MM-DD HH:mm:ss').format('YYYY-MM-DD HH:mm:ss'), 'YYYY-MM-DD HH:mm:ss');
                        var aux_end_diff = moment(new moment(aux_end, 'YYYY-MM-DD HH:mm:ss').format('YYYY-MM-DD HH:mm:ss'), 'YYYY-MM-DD HH:mm:ss');
                        var diffHours = aux_end_diff.diff(aux_start_diff, 'hours', true).toFixed(2);
                        var m = parseInt(new moment(aux_start, 'YYYY-MM-DD HH:mm:SS').format('mm')) / 60;
                        var s = parseInt(new moment(aux_start, 'YYYY-MM-DD HH:mm:SS').format('ss')) / 3600;
                        var aux = parseFloat(parseInt(new moment(aux_start, 'YYYY-MM-DD HH:mm:ss').format('HH')) + m + s).toFixed(2);
                        if (obj.Furnace == 'EAF A') {
                            furnace1_delays.push({
                                type: 'delay', name: obj.Duration + "'", diffHours: diffHours,
                                duration: obj.Duration + "'", color: obj.Color,
                                start: new moment(obj.Start).format("YYYY-MM-DD HH:mm:ss"), startHour: aux,
                                end: new moment(obj.End).format("YYYY-MM-DD HH:mm:ss"), shift: shift,
                            });
                        } else if (obj.Furnace == 'EAF B') {
                            furnace2_delays.push({
                                type: 'delay', name: obj.Duration + "'", diffHours: diffHours,
                                duration: obj.Duration + "'", color: obj.Color,
                                start: new moment(obj.Start).format("YYYY-MM-DD HH:mm:ss"), startHour: aux,
                                end: new moment(obj.End).format("YYYY-MM-DD HH:mm:ss"), shift: shift,
                            });
                        }
                    }
                });
                this.furnace1_delays = furnace1_delays;
                this.furnace2_delays = furnace2_delays;
            },
            getDelay_furnace1: function () { return this.furnace1_delays; },
            getDelay_furnace2: function () { return this.furnace2_delays; },
        });

        Screen.Collections.ItemsTemperature = Backbone.Collection.extend({
            data_EAFA: [],
            data_EAFB: [],
            setDataColl: function (data) {
                var newColl, that = this;
                that.setDataChart(data);
                that.set(data).trigger('fetch', that, data);
            },
            setDataChart: function (data) {
                var that = this;
                this.data_EAFA = [];
                this.data_EAFB = [];
                _.each(data, function (obj) {
                    if (obj.EAFName == 'EAF A') that.data_EAFA.push(obj);
                    else that.data_EAFB.push(obj);
                });
            },
            getDataChartEAFA: function () { return this.data_EAFA; },
            getDataChartEAFB: function () { return this.data_EAFB; },
        });

        Screen.Collections.InjectionFluxesEAFA = Backbone.Collection.extend({
            data: [],
            setDataColl: function (data) {
                var newColl, that = this;
                that.setDataChart(data);
                that.set(that.data).trigger('fetch', that, data);
            },
            setDataChart: function (data) { this.data = []; this.data = data; },
            getDataChart: function () { return this.data; },
        });
        Screen.Collections.InjectionFluxesEAFB = Backbone.Collection.extend({
            data: [],
            setDataColl: function (data) {
                var newColl, that = this;
                that.setDataChart(data);
                that.set(that.data).trigger('fetch', that, data);
            },
            setDataChart: function (data) { this.data = []; this.data = data; },
            getDataChart: function () { return this.data; },
        });

        Screen.Collections.BucketFluxesEAFA = Backbone.Collection.extend({
            data: [],
            setDataColl: function (data) {
                var newColl, that = this;
                that.setDataChart(data);
                that.set(that.data).trigger('fetch', that, data);
            },
            setDataChart: function (data) { this.data = []; this.data = data; },
            getDataChart: function () { return this.data; },
        });
        Screen.Collections.BucketFluxesEAFB = Backbone.Collection.extend({
            data: [],
            setDataColl: function (data) {
                var newColl, that = this;
                that.setDataChart(data);
                that.set(that.data).trigger('fetch', that, data);
            },
            setDataChart: function (data) { this.data = []; this.data = data; },
            getDataChart: function () { return this.data; },
        });

        Screen.Models.ItemBaghouse = Backbone.Epoxy.Model.extend({
            parse: function (obj) {
                var result = {
                    tagName: obj.tagName,
                    value: obj.Value,
                    datetime: new moment(obj.Datetime).format("MM-DD-YY HH:mm:ss"),
                };
                return result;
            },
        });
        Screen.Collections.ItemsBaghouse = Backbone.Collection.extend({
            fixedParameters: [],
            model: Screen.Models.ItemBaghouse,
            setDataColl: function (data) {
                var newColl, that = this;
                newColl = _.map(data, that.model.prototype.parse);
                that.set(newColl).trigger('fetch', that, data);
            },
        });

        Screen.Models.ItemYield = Backbone.Epoxy.Model.extend({
            parse: function (obj) {
                var result = {
                    date: obj.ProductionDate,
                    shift: obj.ShiftId === 'D' ? 'Day' : 'Night',
                    yieldEAFA: obj.EAFAYield,
                    yieldEAFB: obj.EAFBYield,
                };
                return result;
            },
        });
        Screen.Collections.ItemsYield = Backbone.Collection.extend({
            fixedParameters: [],
            model: Screen.Models.ItemYield,
            setDataColl: function (data) {
                var newColl, that = this;
                newColl = _.map(data, that.model.prototype.parse);
                that.set(newColl).trigger('fetch', that, data);
            },
        });

        Screen.Models.ItemDelay = Backbone.Epoxy.Model.extend({
            parse: function (obj) {
                var result = {
                    RN: obj.rn, Furnace: obj.Furnace, Code: obj.Code,
                    Start: new moment(obj.Start).format("YYYY-MM-DD HH:mm:ss"),
                    End: new moment(obj.End).format("YYYY-MM-DD HH:mm:ss"),
                    Duration: obj.Duration, TopDelayCodeReason: obj.TopDelayCodeReason,
                };
                return result;
            },
        });
        Screen.Collections.ItemsDelays = Backbone.Collection.extend({
            model: Screen.Models.ItemDelay,
            setDataColl: function (data) {
                var newColl, that = this;
                newColl = _.map(data, that.model.prototype.parse);
                that.set(newColl).trigger('fetch', that, data);
            },
        });

        Screen.Models.ItemSlag = Backbone.Epoxy.Model.extend({
            parse: function (obj) {
                var result = {
                    heatName: obj.HeatName, typeName: obj.TypeName,
                    timestamp: new moment(obj.TimestampString).format("HH:mm"),
                    MgO: obj.MgO, CaO: obj.CaO, Fe2O3: obj.Fe2O3, Al2O3: obj.Al2O3,
                    SiO2: obj.SiO2, MnO: obj.MnO, Cr2O3: obj.Cr2O3, TiO2: obj.TiO2,
                    P2O5: obj.P2O5, SO3: obj.SO3, B3ratio: obj.B3ratio, FeO: obj.FeO,
                    total: obj.Total, refractoryWear: obj.RefractoryWear,
                    steelLoss: obj.FeinSlag, comments: obj.Comments,
                    totalMin: obj.TotalMin, dirtLoad: obj.DirtLoad,
                    TotalSlagVolume: obj.TotalSlagVolume,
                    LbsMgOAdded: obj.LbsMgOAdded, LbsMgOnSlag: obj.LbsMgOnSlag,
                    B3Status: obj.B3Status, MgOStatus: obj.MgOStatus,
                    FeOStatus: obj.FeOStatus, RefractoryWearStatus: obj.RefractoryWearStatus,
                    TotalStatus: obj.TotalStatus,
                };
                return result;
            },
        });
        Screen.Collections.ItemsSlag = Backbone.Collection.extend({
            model: Screen.Models.ItemSlag,
            fixedParameters: [],
            setDataColl: function (data) {
                var newColl, that = this;
                newColl = _.map(data, that.model.prototype.parse);
                that.set(newColl).trigger('fetch', that, data);
            },
        });

        Screen.Collections.ItemsRecipes = Backbone.Collection.extend({
            setDataColl: function (data) {
                var newColl, that = this;
                newColl = data;
                that.set(newColl).trigger('fetch', that, data);
            },
        });

        Screen.Collections.ItemsRecipesAverage = Backbone.Collection.extend({
            setDataColl: function (data) {
                var newColl, that = this;
                newColl = data;
                that.set(newColl).trigger('fetch', that, data);
            },
        });

        Screen.Collections.ItemsManualTemps = Backbone.Collection.extend({
            setDataColl: function (data) {
                var newColl, that = this;
                newColl = data;
                that.set(newColl).trigger('fetch', that, data);
            },
        });

        Screen.Collections.TapHoleNonFreeOpen = Backbone.Collection.extend({
            setDataColl: function (data) {
                var newColl, that = this;
                newColl = data;
                that.set(newColl).trigger('fetch', that, data);
            },
        });

        Screen.Models.ItemLogBook = Backbone.Epoxy.Model.extend({
            parse: function (obj) {
                var result = {
                    Title: obj.Title, Author: obj.Author,
                    ShiftId: obj.ShiftID, CrewId: obj.CrewId,
                    Description: obj.Description,
                    Timestamp: new moment(obj.Timestamp).format('HH:mm'),
                };
                return result;
            },
        });
        Screen.Collections.ItemsLogBook = Backbone.Collection.extend({
            model: Screen.Models.ItemLogBook,
            setDataColl: function (data) {
                var newColl, that = this;
                newColl = _.map(data, that.model.prototype.parse);
                that.set(newColl).trigger('fetch', that, data);
            },
        });

        Screen.Models.SRDCategory = Backbone.Epoxy.Model.extend({
            parse: function (obj) {
                var result = {
                    id: obj.SRDCategoryId,
                    description: obj.Description,
                    color: obj.Color,
                };
                return result;
            },
        });
        Screen.Collections.SRDCategories = Backbone.Collection.extend({
            fixedParameters: [],
            model: Screen.Models.SRDCategory,
            setDataColl: function (data) {
                var newColl, that = this;
                newColl = _.map(data, that.model.prototype.parse);
                that.set(newColl).trigger('fetch', that, data);
            },
        });

        // Required, return the module for AMD compliance.
        return Screen;

    });
