/// <reference path="http://localhost:65492/IndustrialDashboard/Scripts/IndustrialDashboard-debug.js" />
//SCREEN-BOILERPLATE

//this boilerplate builds screens that are gonna be shown in the <div class="content"> tag of our main HTML.
//do not build app components with this boilerplate, only screens (reports)

define([
  // Application variable, always include it to have access to app methods.
  "app",

  //templates-loader: this loads templates async.
  "js/templates-loader",

  "modules/modal2",

  "backgrid",

  "js/dashboard-assets/progressbar/progressbar",

  "moment", 

  "oee-module-screens/delays/delay-information-modal/delay-information-modal",

  "oee-module-assets/bootstrap-toggle/bootstrap2-toggle",

  "oee-module-assets/dashboard-assets/jquery-circle-progress/circle-progress",

  "js/jquery.selectonfocus/jquery.selectonfocus",
],

function (app, T, Modal, Backgrid, ProgressBar, moment, delayInformationModal) {

    //replace all "OEE" with your view's name.
    var OEE = { Models: {}, Views: {}, Collections: {} }; 

    OEE.Models.Main = Backbone.Epoxy.Model.extend({
        defaults: {
            productionItem: null,
            delaysInformation: null,
            start: null, 
            end: null, 
            productionItemId: null,
            productionItemName: null,
            productionItemBadUnitProducedManualEnabled: null,
            productionItemBadUnitProducedManualUnits: null,

            productionItemParentId: null,
            productionItemParentName: null,
            productionItemParentOverviewLink: null, 

            shiftDuration: 5,

            batchNumber: null,

            isLoadingProdData: false,
            isLoadingDelaysData: false,
        },
        computeds: {
        }, 
        initialize: function(){
            this.attributes.productionItem = new OEE.Models.ProductionItem();
            this.attributes.delaysInformation = new OEE.Models.DelaysInformation();

            this.bindEvents(); 
        },
        bindEvents: function () {
            this.listenTo(this.get("productionItem"), "change:name", function () { this.set("productionItemName", this.get("productionItem").get("name")); });
            this.listenTo(this.get("productionItem"), "change:parentId", function () { this.set("productionItemParentId", this.get("productionItem").get("parentId")); });
            this.listenTo(this.get("productionItem"), "change:parentId", this.checkParentIdLink);
            this.listenTo(this.get("productionItem"), "change:parentName", function () { this.set("productionItemParentName", this.get("productionItem").get("parentName")); });
            this.listenTo(this.get("productionItem"), "change:batchNumber", function () { this.set("batchNumber", this.get("productionItem").get("batchNumber")); });
            this.listenTo(this.get("productionItem"), "change:badUnitProducedManualEnabled", function () { this.set("productionItemBadUnitProducedManualEnabled", this.get("productionItem").get("badUnitProducedManualEnabled")); });
            this.listenTo(this.get("productionItem"), "change:badUnitProducedManualUnits", function () { this.set("productionItemBadUnitProducedManualUnits", this.get("productionItem").get("badUnitProducedManualUnits")); });
        },
        checkParentIdLink: function (a, b, c) {
            this.set("productionItemParentOverviewLink", app.router.resolveURL("/!/oeeOverview(/:id)", { id: b }, true, true));
        }, 
    });

    //the generate id method is called everytime a view is going to be shown by the router and returns and id that
    //is used by the router to store in cache (if it is cacheable) and to know the current shown view.
    //this is useful in case your view is reusable, and displays different data depending on url parameters 
    //(such as a catalog view, or a report that doesnt change in terms of UI but it does change in terms of data)
    //so you can always use the same view on the router but the cache can tell which view is which by using differents ids.
    OEE.generateID = function (viewParams) {
        try {
            //if the viewparams change the view id, then evaluate the viewparams here
            //and return the appropiate id.
            return "oee"; 
        } catch (Error) { }
    }

    OEE.Models.ProductionItem = Backbone.Model.extend({
        defaults: {
            id: null,
            name: null, 

            parentId: null,
            parentName: null,

            oee: null, 
            availability: null,
            quality: null,
            performance: null,

            targetQty: null,
            actualQty: null,
	        badQty: null, 

            targetProductionRate: null,
            actualProductionRate: null,

            machineStatus: null,
            maintenanceStatus: null,
            runningMachineStatus: null, 
            outOfService: null, 
            hasMaintenanceSwitch: null, 

            //batch
            batchId: null,
            batchStart: null, 
            batchNumber: null,
            batchProduct: null,

            //setpoints
            setpointOEE: null,

            badUnitProducedManualEnabled: null,
            badUnitProducedManualUnits: null,
        },
        initialize: function () {
            //this.attributes.children = new OEEOverview.Collections.ProductionItems({}); 
        },
        fetch: function (opt) {
            var callback = _.isObject(opt) ? opt.callback : null;

            var id = _.isObject(opt) ? opt.id : opt;
            var from = (_.isObject(opt) && opt.from) ? opt.from : null;
            var to = (_.isObject(opt) && opt.to) ? opt.to : null;

            var cache = {
                enabled: (_.isObject(opt) && _.isObject(opt.cache) && _.isBoolean(opt.cache.enabled)) ? opt.cache.enabled : true, 
                refresh: (_.isObject(opt) && _.isObject(opt.cache) && _.isBoolean(opt.cache.refresh)) ? opt.cache.refresh : false, 
            }; 

            id = (id) ? id : null;

            var that = this;
            var QP = new Core.Database.QueryParameters();

            QP.Add("ProductionItemId", "INT", id);
            QP.Add("From", "DATETIME", from);
            QP.Add("To", "DATETIME", to);
            QP.Add("Timezone", "VARCHAR", app.models.user.get("timezoneCode"));

            Core.Json.CallProcedure(app.DatabaseNames.OEE + ".WebApp.GetOEE", QP, {
                onSuccess: function (data) {
                    if (data && data.Table) {
                        var generalData = data.Table[0]; 
                        var oeeData = data.Table1[0];
                        var productionData = data.Table2[0]; 
                        var batchData = data.Table3[0];

                        var productionItemModel = _.extend({}, that.attributes, {
                            id: oeeData.Id,
                            name: oeeData.Name, 

                            parentId: oeeData.ParentId,
                            parentName: oeeData.ParentName,
                            badUnitProducedManualEnabled: oeeData.BadUnitProducedManualEnabled,
                            badUnitProducedManualUnits: oeeData.BadUnitProducedManualUnits,

                            oee: oeeData.OEE,
                            availability: oeeData.Availability,
                            quality: oeeData.Quality,
                            performance: oeeData.Performance,

                            targetQty: productionData.TargetQuantity,
                            actualQty: productionData.ActualQuantity,
			                badQty: productionData.BadQuantity,
                            targetProductionRate: productionData.TargetProductionRate,
                            actualProductionRate: productionData.ActualProductionRate,

                            machineStatus: productionData.MachineStatus,
                            maintenanceStatus: productionData.MaintenanceStatus,
                            runningMachineStatus: productionData.RunningMachineStatus, 
                            outOfService: productionData.OutOfService, 
                            hasMaintenanceSwitch: productionData.HasMaintenanceSwitch, 

                            batchId: batchData.Id,
                            batchStart: batchData.Start, 
                            batchNumber: batchData.Number,
                            batchProduct: batchData.Product,

                            setpointOEE: productionData.SetpointOEE,
                        });

                        that.set(productionItemModel, { from: "fetch" });

                        if (callback != null && _.isFunction(callback))
                            callback.call(this, that, "SUCCESS");
                    } else {
                        if (callback != null && _.isFunction(callback))
                            callback.call(this, that, "ERROR", data.Message);
                    }
                },
                onError: function (d) {
                    d; 
                }, 
                Secured: true,
                Cache: cache.enabled,
                RefreshCache: cache.refresh, 
            }, app.ConnectionStrings.app);
        },
    });

    OEE.Models.Delay = Backbone.Model.extend({
        defaults: {
            id: null,
            delayCodeId: null,
            delayCodeParentId: null, 
            start: null,
            end: null,
            duration: null,
            upToDate: false, 
        }
    });

    OEE.Models.Event = Backbone.Model.extend({
        defaults: {
            id: null,
            start: null,
            end: null,
            duration: null, 
            type: null,
            inMaintenance: null,
            batchId: null,
            batchNumber: null,
            product: null, 
            upToDate: false, 

            ///(optionals)
            delayId: null, 
            delayCodeId: null,
            delayCodeParentId: null,
            delayCodeExcludesAvailability: null,
            delayForceIncludeInAvailability: null, 
        }, 
    });

    OEE.Collections.Events = Backbone.Collection.extend({
        model: OEE.Models.Event, 
    }); 

    OEE.Models.DelayCode = Backbone.Model.extend({
        defaults: {
            id: null,
            code: null,
            description: null,
            color: null, 
        },
    }); 

    OEE.Models.TimeAccount = Backbone.Model.extend({
        defaults: {
            id: null,
            code: null,
            description: null,
            color: null,
            duration: null, 
        }
    });

    OEE.Collections.Delays = Backbone.Collection.extend({
        model: OEE.Models.Delay,
    }); 

    OEE.Collections.TimeAccounts = Backbone.Collection.extend({
        model: OEE.Models.TimeAccount, 
    }); 

    OEE.Collections.DelayCodes = Backbone.Collection.extend({
        model: OEE.Models.DelayCode, 
    }); 

    OEE.Models.DelaysInformation = Backbone.Epoxy.Model.extend({
        defaults: {
            delays: null,
            timeAccounts: null,
            delayCodes: null,
            events: null, 
        },
        initialize: function () {
            this.attributes.delays = new OEE.Collections.Delays();
            this.attributes.timeAccounts = new OEE.Collections.TimeAccounts();
            this.attributes.delayCodes = new OEE.Collections.DelayCodes();
            this.attributes.events = new OEE.Collections.Events(); 
        }, 
        fetch: function (opt) {
            var callback = _.isObject(opt) ? opt.callback : null;
            var id = _.isObject(opt) ? opt.id : opt;
            id = (id) ? id : null;

            var from = (_.isObject(opt) && opt.from) ? opt.from : null;
            var to = (_.isObject(opt) && opt.to) ? opt.to : null;

            var cache = {
                enabled: (_.isObject(opt) && _.isObject(opt.cache) && _.isBoolean(opt.cache.enabled)) ? opt.cache.enabled : true,
                refresh: (_.isObject(opt) && _.isObject(opt.cache) && _.isBoolean(opt.cache.refresh)) ? opt.cache.refresh : false,
            };

            var that = this;
            var QP = new Core.Database.QueryParameters();

            QP.Add("ProductionItemId", "INT", id);
            QP.Add("From", "DATETIME", from);
            QP.Add("To", "DATETIME", to);
            QP.Add("Timezone", "VARCHAR", app.models.user.get("timezoneCode"));

            Core.Json.CallProcedure(app.DatabaseNames.OEE + ".WebApp.GetDelayEvents", QP, {
                onSuccess: function (data) {
                    if (data && data.Table) {
                        var generalData = data.Table[0];
                        var events = data.Table1;
                        var delays = data.Table2;
                        var timeaccounts = data.Table3;
                        var delayCodes = data.Table4;
                        
                        var eventModelsArr = [];
                        for (var i = 0, len = events.length; i < len; i++) {
                            var evData = events[i]; 
                            var eventModel = {
                                start: evData.Start,
                                end: evData.End,
                                duration: evData.Duration,
                                type: evData.Type,
                                inMaintenance: evData.InMaintenance,
                                batchId: evData.BatchId,
                                batchNumber: evData.BatchNumber,
                                product: evData.Product, 
                                upToDate: evData.UpToDate, 

                                ///(optionals)
                                delayId: evData.DelayId, 
                                delayCodeId: evData.DelayCodeId,
                                delayCodeParentId: evData.DelayCodeParentId,
                                delayCodeExcludesAvailability: evData.DelayCodeExcludesAvailability,
                                delayForceIncludeInAvailability: evData.DelayForceIncludeInAvailability,
                            };

                            eventModelsArr.push(eventModel); 
                        }

                        var delayModelsArr = []
                        for (var i = 0; i < delays.length; i++) {
                            var delayModel = new OEE.Models.Delay({
                                id: (delays[i].Id != -2) ? delays[i].Id : null,
                                delayCodeId: delays[i].DelayCodeId,
                                delayCodeParentId: delays[i].DelayCodeParentId,
                                start: delays[i].Start,
                                end: delays[i].End,
                                duration: delays[i].Duration,
                                upToDate: delays[i].UpToDate, 
                            });                            

                            delayModelsArr.push(delayModel); 
                        }

                        //Calculate total duration of current delay (if exists).
                        //If current delay is a split, get the total duration of the parent delay.
                        var curDelayDuration = 0;

                        if (delays.length > 0) {
                            var obj = delays[0];

                            //Only do calculation if last delay is the current delay.
                            if (obj.IsCurrentDelay == true) {
                                //If last delay is a split, get the duration of all subdelays of parent item.
                                //Else, get the duration of the last delay.
                                if (obj.ParentId != null) {
                                    var parentDelayId = obj.ParentId;

                                    curDelayDuration += obj.Duration;

                                    for (var i = 1; i < delays.length; i++) {
                                        obj = delays[i];

                                        if (obj.ParentId == parentDelayId)
                                            curDelayDuration += obj.Duration;
                                        else
                                            break;
                                    }
                                }
                                else {
                                    curDelayDuration = obj.Duration;
                                }
                            }
                        }

                        var timeAccountModelsArr = []; 
                        for (var i = 0; i < timeaccounts.length; i++) {
                            var timeaccountModel = new OEE.Models.TimeAccount({
                                id: timeaccounts[i].Id,
                                code: timeaccounts[i].Code,
                                description: timeaccounts[i].Description,
                                color: timeaccounts[i].Color,
                                duration: timeaccounts[i].Duration,
                            });

                            timeAccountModelsArr.push(timeaccountModel); 
                        }

                        var delayCodesArr = [];
                        for (var i = 0; i < delayCodes.length; i++) {
                            var delayCodeModel = new OEE.Models.DelayCode({
                                id: delayCodes[i].Id,
                                code: delayCodes[i].Code,
                                description: delayCodes[i].Description,
                                color: delayCodes[i].Color,
                            });

                            delayCodesArr.push(delayCodeModel); 
                        }

                        var notDeclared = new OEE.Models.DelayCode({
                            id: -1,
                            code: "NOTDECLARED",
                            description: "Not Declared",
                            color: "#de2e27",
                        }); 

                        delayCodesArr.push(notDeclared); 

                        that.get("events").set(eventModelsArr); 
                        that.get("delays").set(delayModelsArr);
                        that.get("timeAccounts").set(timeAccountModelsArr);
                        that.get("delayCodes").set(delayCodesArr);
                        that.set("currentDelayDuration", curDelayDuration);

                        if (callback != null && _.isFunction(callback))
                            callback.call(this, that);
                    }
                },
                Secured: true,
                Cache: cache.enabled,
                RefreshCache: cache.refresh,
            }, app.ConnectionStrings.app);
        }, 
    }); 

    OEE.Views.Main = Backbone.Epoxy.View.extend({
        template: "oee"
        , id: "oee"
        , title: "OEE"
        //default not cacheable, change this if you want the view to be cacheable
        // if the view is set as cacheable should also have a refresh method to reset the view without erasing the DOM.
        , isCacheable: false
        , bindings: "data-bind"
        , initialize: function () {
            this.options.state = app.view_states.loading;
            this.options.onappend = (_.isFunction(this.options.onappend)) ? this.options.onappend : function () { };

            var productionItemId = null;

            if (this.options.viewParams) {
                productionItemId = (this.options.viewParams.id != null && this.options.viewParams.id != undefined)
                    ? this.options.viewParams.id : null;
            }

            var model = new OEE.Models.Main({
                productionItemId: productionItemId, 
            });

            this.options.MYREFERENCES = {
                subviews: {
                }
                , autoRefresh: {
                    enabled: true
                    , toid: null
                },
                isRendering: false,
                isRendered: false, 
            };

            this.options.graph = {
                outOfProductionModalShown: false,
                setProductionModeLoading: false, 
                blinkingLightManteinance: false,
                blinkingLightTOUT: null,
                maintenanceToggled: false, 
                productQty: {
                    progressbars: {
                        actual: null,
                        target: null,
                        options: null, 
                    }
                },
                oee: {
                    circleProgress: {
                        oee: null,
                        ava: null,
                        per: null,
                        qua: null, 
                    }
                },
                halfCircle: {},
                events: {
                    grid: null,
                    collection: null,
                    model: null,
                    col: null,
                },
                timeAccounts: {
                    grid: null,
                    collection: null,
                    model: null,
                    col: null,
                }
            }; 

            this.options.demouser = $.inArray("DemoUserRole", app.models.user.get("roles")) != -1; 
            this.options.writePermission = app.models.securityManager.getModuleActionValue("OEE.oee", "write"); 

            this.model = model;

            this.bindingSources = {
                delaysInformation: this.model.get('delaysInformation'),
            };

            this.bindEvents();
            //_.bindAll(this);
        }

        , templatesLoaded: {
            addBadManualModal: null,
            closeBatchModal: null,
        }

        , events: function () {
            return {
                //"click .btn-open-batch": "openBatch",
                //"click .btn-open-batch": _.debounce(this.openBatch_click, 500, true),
                "click .btn-open-batch": this.openBatch_click,
                //"click .btn-close-batch": "closeBatch",
                //"click .btn-close-batch": _.debounce(this.closeBatchBtn_click, 500, true),
                "click .btn-close-batch": this.closeBatchBtn_click,
                "change #in-maintenance-toggle": _.debounce(this.inMaintenanceToggleCbx_change, 500, true),
                "click .btn-start-production-mode": "startProductionMode",
                "click .btn-end-production-mode": "stopProductionMode",
                //"click .btn-bad-qty-add-manual": _.debounce(this.badQtyAddManualBtn_click, 500, true),
                "click .btn-bad-qty-add-manual": this.badQtyAddManualBtn_click,
            };
        }

        , render: function (container) {
            var that = this;

            var thatContainer = (this.options.container) ? this.options.container : container;
            this.options.container = thatContainer;
            //the screens have a custompath, so it has to be specified in the customPath variable that is
            //then sent to the template loader.
            var customPath = app.oeeModule.screensPath + "oee/";

            var fetchLocale = function (cb) {
                //getInternationalizationData
                if (!that.options.i18n) that.options.i18n = {};

                if (!that.options.i18n[that.template]) {
                    app.getI18NJed(that, that.template, function (i18nJED) {
                        //storing internationalization data
                        that.options.i18n[that.template] = i18nJED;
                        cb.call(this, that);
                    }, true, customPath);
                } else {
                    cb.call(this, that); 
                }
            };

            if (this.model.get("productionItem").get("id")) {
                this.options.MYREFERENCES.isRendering = true; 
                T.render.call(this, this.template, function (tmp) {
                    fetchLocale(function () {
                        var productionItem = that.model.get("productionItem");

                        that.$el.html(tmp(productionItem.toJSON()));
                        that.applyBindings();
                        that.append(thatContainer, that.$el);
                        that.$el.find('[data-toggle]').bootstrapToggle();


                        that.drawCircles();
                        that.renderTimeAccountsGrid();
                        that.drawProgressGraph();
                        that.renderEventsGrid();
                        that.refreshBatchInfo();

                        that.options.MYREFERENCES.isRendering = false;
                        that.options.MYREFERENCES.isRendered = true;

                        that.refreshGraph();
                    }); 
                }, customPath, "main_template");
            } else {
                fetchLocale(function () {
                    that.$el.html('<div class="loading-poster" style="text-align:center;margin-top:10px;font-size:24px;"><i class="fa fa-5x fa-cog fa-spin"></i><br /><br />' + app.translate(that, "loading_label") + '</div>');
                    that.append(thatContainer, that.$el);
                }); 
            }

            if (!this.options.MYREFERENCES.isRendered)
                this._startAutoRefresh();
        }

        , _getPercent: function (val, opt, topVal) {
            //topVal = _.isNumber(topVal) ? topVal : null
            //var val = (Math.round((val * 100) / opt.total)) / 100;
            //return (topVal == null || val <= topVal) ? val : topVal;

            topVal = _.isNumber(topVal) ? topVal : null
            var p = (((val * 100) / opt.total) / 100); 
            var final = Math.floor(p * 100) / 100;

            return (final == 0 && p != 0 && (opt && opt.displayMinimum)) ? 0.01 :
            (topVal == null || final <= topVal) ? final : topVal;
        }
        , _sign: function (num) {
            // IE does not support method sign here
            if (typeof Math.sign === 'undefined') {
                if (num > 0) {
                    return 1;
                }
                if (num < 0) {
                    return -1;
                }
                return 0;
            }
            return Math.sign(num);
        }
        , precise_round: function (num, decimals) {
            var t = Math.pow(10, decimals);
            return (Math.round((num * t) + (decimals > 0 ? 1 : 0) * (this._sign(num) * (10 / Math.pow(100, decimals)))) / t).toFixed(decimals);
        }
        , _getValue: function (perc, opt, topVal) {
            topVal = _.isNumber(topVal) ? topVal : null
            var val = this.precise_round((perc * opt.total) / 100, 2);
            val = parseFloat(val);
            val = (val > 1) ? Math.round(val) : val;
            val = (val * 100).toFixed(0);
            return (topVal == null || val <= topVal) ? val : topVal;
        }
        , renderEventsGrid: function () {
            var that = this; 

            var eventModel = Backbone.Model.extend({
                defaults: {
                    id: null,
                    start: null,
                    end: null,
                    duration: null,
                    cause: null,
                    details: null,
                    upToDate: false, 
                },
            });

            var eventsCollection = Backbone.Collection.extend({
                model: eventModel,
                comparator: function (a, b) {
                    var starta = a.get('start');
                    var startb = b.get('start');

                    if (starta > startb) return -1;
                    else if (starta < startb) return 1;
                    else return 0; 
                }, 
            }); 

            this.options.graph.events.model = eventModel;
            this.options.graph.events.col = eventsCollection; 

            var events = this.options.graph.events.collection = new eventsCollection();
            
            var columns = [
                {
                    name: "start",
                    label: app.translate(that, "start_label"),
                    editable: false,
                    cell: Backgrid.StringCell.extend({
                        orderSeparator: '',
                        className: "string-cell",
                    })
                },
                {
                    name: "end",
                    label: app.translate(that, "end_label"),
                    editable: false,
                    cell: Backgrid.StringCell.extend({
                        orderSeparator: '',
                        className: "string-cell",
                    })
                },
                {
                    name: "duration",
                    label: app.translate(that, "duration_label"),
                    editable: false,
                    cell: Backgrid.StringCell.extend({
                        orderSeparator: '',
                        className: "string-cell",
                        render: function () {
                            this.$el.html(this.secondsToHHMMSS(this.model.get("duration")));
                            this.delegateEvents();
                            return this;
                        },
                        secondsToHHMMSS: function (d) {
                            d = Number(d);
                            var h = Math.floor(d / 3600);
                            var m = Math.floor(d % 3600 / 60);
                            var s = Math.floor(d % 3600 % 60);
                            return ((h > 0 ? h + ":" + (m < 10 ? "0" : "") : "") + m + ":" + (s < 10 ? "0" : "") + s);
                        },
                    })
                },
            ];

            var CustomRow = Backgrid.Row.extend({
                events: {
                    "click": "onClick"
                },
                className: "clickeable-row",
                initialize: function () {
                    Backgrid.Row.prototype.initialize.apply(this, arguments);

                    this.bindEvents();
                },
                render: function () {
                    Backgrid.Row.prototype.render.apply(this, arguments);

                    if (!this.model.get("upToDate")) {
                        this.$el.addClass("disabled-row");
                        //this.$el.tooltip({
                        //    placement: 'top',
                        //    title: 'Processing delay...', 
                        //}); 
                    } else {
                        this.$el.removeClass("disabled-row");
                        //this.$el.tooltip('destroy'); 
                    }

                    return this;
                },
                onClick: _.debounce(function (e) {
                    var target = $(e.target); 
                    var data_prevent = (target.closest("[data-preventclick]").length > 0) ? target.closest("[data-preventclick]").data("preventclick") : false; 

                    if (!data_prevent && this.model.get("upToDate")) {
                        that.showDelayInfoModalFromList(this.model); 
                    }
                }, 500, true), 
                bindEvents: function () {
                    this.listenTo(this.model, "change:upToDate", this.render); 
                }, 
            });

            var grid = new Backgrid.Grid({
                row: CustomRow, 
                className: "backgrid table table-hover",
                columns: columns,
                collection: events,
            });

            this.$el.find(".events-grid-container").append(grid.render().el);
            this.refreshEventsGrid(); 
        }
        , showDelayInfoModalFromProgress: function (e) {
            if (this.options.demouser || !this.options.writePermission) return;

            var target = $(e.target);
            var delayId = target.data("delay-id");

            var m = this.options.graph.events.collection.findWhere({ id: delayId, });

            this.showDelayInfoModal(delayId, m); 
        }
        , showDelayInfoModalFromList: function (m) {
            //demouser
            if (this.options.demouser || !this.options.writePermission) return;

            this.showDelayInfoModal(m.get("id"), m);
        }
        , showDelayInfoModal: function (delayId, m) {
            var that = this;
            var delayinfomodal = new OEE.Views.DelayInformationModal({
                viewParams: {
                    rowData: {
                        id: delayId,
                        model: m,
                    },
                }
            });

            delayinfomodal.on("delayCodeSelected", function () {
                // Comment out remove due to the following.
                // - Since this method is also executed from progress bar graph' items,
                //   some items are not in the collection because not all items are loaded
                //   for pagination.
                // - If the declared delay is the current delay, there is a weird effect because
                //   the delays appears again after refresh but 'disabled' (which is fine).
                //   So to not remove it manually and wait for the refresh to prevent this effect.
                //m.collection.remove(m);

                that.refresh({
                    refreshDelaysData: true,
                    refreshCacheDelays: true,
                });
            });

            delayinfomodal.show();
        }
        , refreshEventsGrid: function () {
            var that = this; 
            var events = this.options.graph.events.collection;
            var eventModel = this.options.graph.events.model; 

            var last_3_hours = new moment(app.models.navBarClock.get("date")).subtract(3, 'hours');

            var eventsArr = []; 
            this.model.get("delaysInformation").get("delays").filter(function (m) {
                return !m.get("delayCodeId"); 
            }).filter(function (m) {
                var end = moment(m.get("end"));
                return end.isAfter(last_3_hours) || end.isSame(last_3_hours);
            }).forEach(function (m) {
                var delayCode = that.model.get("delaysInformation").get("delayCodes").findWhere({ id: m.get("delayCodeId") });
                if (!delayCode) delayCode = that.model.get("delaysInformation").get("delayCodes").findWhere({ id: -1 }); 

                eventsArr.push(new eventModel({
                    id: m.get("id"),
                    start: new moment(m.get("start")).format("YYYY-MM-DD HH:mm:ss"),
                    end: new moment(m.get("end")).format("YYYY-MM-DD HH:mm:ss"),
                    duration: m.get("duration"),
                    cause: (delayCode) ? delayCode.get("code") : null,
                    details: (delayCode) ? delayCode.get("description") : null,
                    upToDate: m.get("upToDate"), 
                }));
            });

            events.set(eventsArr); 
        }
        , drawCircles: function () {
            var that = this; 

            // Arc layout
            $.circleProgress.defaults.arcCoef = 1; // default; range: 0..1
            $.circleProgress.defaults.startAngle = -1.5 * Math.PI; // default

            $.circleProgress.defaults.drawArc = function (v) {
                var ctx = this.ctx,
                    r = this.radius,
                    t = this.getThickness(),
                    c = this.arcCoef,
                    a = this.startAngle + (1 - c) * Math.PI;

                v = Math.max(0, Math.min(1, v));

                ctx.save();
                ctx.beginPath();

                if (!this.reverse) {
                    ctx.arc(r, r, r - t / 2, a, a + 2 * c * Math.PI * v);
                } else {
                    ctx.arc(r, r, r - t / 2, a + 2 * c * Math.PI, a + 2 * c * (1 - v) * Math.PI, a);
                }

                ctx.lineWidth = t;
                ctx.lineCap = this.lineCap;
                ctx.strokeStyle = this.arcFill;
                ctx.stroke();
                ctx.restore();
            };

            $.circleProgress.defaults.drawEmptyArc = function (v) {
                var ctx = this.ctx,
                    r = this.radius,
                    t = this.getThickness(),
                    c = this.arcCoef,
                    a = this.startAngle + (1 - c) * Math.PI;

                v = Math.max(0, Math.min(1, v));

                if (v < 1) {
                    ctx.save();
                    ctx.beginPath();

                    if (v <= 0) {
                        ctx.arc(r, r, r - t / 2, a, a + 2 * c * Math.PI);
                    } else {
                        if (!this.reverse) {
                            ctx.arc(r, r, r - t / 2, a + 2 * c * Math.PI * v, a + 2 * c * Math.PI);
                        } else {
                            ctx.arc(r, r, r - t / 2, a, a + 2 * c * (1 - v) * Math.PI);
                        }
                    }

                    ctx.lineWidth = t;
                    ctx.strokeStyle = this.emptyFill;
                    ctx.stroke();
                    ctx.restore();
                }
            };

            this.$el.find(".circle").each(function(){
                var $circle = $(this);
                var val = $circle.data("circle-value"); 
                var circle_container = $circle.parent(".circle-container"); 
                var circle_gradient = ($circle.data("circle-gradient")) ? $circle.data("circle-gradient") : ['#29D65A', '#29D67D'];
                var circle_thickness = ($circle.data("circle-thickness")) ? $circle.data("circle-thickness") : 25;
                var circle_size = ($circle.data("circle-size")) ? $circle.data("circle-size") : 80;
                var circle_name = $circle.data("circle-name"); 
                var size = Math.floor((circle_size * circle_container.width()) / 100); 

                that.options.graph.oee.circleProgress[circle_name] = $circle; 

                that.options.graph.oee.circleProgress[circle_name].circleProgress({
                    value: (val) ? val/100 : 0,
                    size: size,
                    startAngle: -Math.PI / 4 * 2,
                    thickness: circle_thickness,
                    fill: {
                        gradient: circle_gradient,
                    }
                }); 

                that.options.graph.oee.circleProgress[circle_name].on("circle-animation-progress", function (event, progress, stepValue) {
                    var progress_display = $circle.find(".progress-display");
                    if (progress_display.length > 0) {
                        progress_display.text((stepValue * 100).toFixed(0) + "%"); 
                    }
                }).on("circle-animation-end", function (event) {
                    var progress_display = $circle.find(".progress-display");
                    if (progress_display.length > 0) {
                        var actual = parseFloat(($(event.target).circleProgress('value') * 100).toFixed(0)); 
                        progress_display.text(_.isNumber(actual) ? actual + "%" : "-");
                    }
                });

                if ($circle.find('.set-point').length) {
                    that.options.graph.setpoint = $circle.find('.set-point'); 
                    that.options.graph.setpoint.circleProgress({
                        value: 0,
                        size: size - 40,
                        startAngle: -Math.PI / 4 * 2,
                        thickness: circle_thickness - 15,
                        fill: { gradient: ['#1300ff', '#006cff'] },
                        emptyFill: "transparent",
                    });
                }
            });

            var halfcircle_opt = {
                total: null,
                actual: null,
            };

            this.$el.find(".half-circle").each(function () {
                var $half_circle = $(this);
                var val = $half_circle.data("circle-value");
                var circle_container = $half_circle.parent(".circle-container");
                var circle_size = ($half_circle.data("circle-size")) ? $half_circle.data("circle-size") : 80;
                var circle_thickness = ($half_circle.data("circle-thickness")) ? $half_circle.data("circle-thickness") : 25;
                var circle_name = $half_circle.data("circle-name");
                var size = (circle_size * circle_container.width()) / 100;

                that.options.graph.halfCircle[circle_name] = { graph: null, opts: null }; 
                that.options.graph.halfCircle[circle_name].graph = $half_circle;
                that.options.graph.halfCircle[circle_name].opts = halfcircle_opt;

                that.options.graph.halfCircle[circle_name].graph.circleProgress({
                    value: 0,
                    size: size,
                    thickness: circle_thickness,
                    arcCoef: 0.5,
                    fill: {
                        gradient: ["#ecee12", "#258A3D"],
                    },
                    initWidget: function () {
                        var canvas = this.canvas = this.canvas || $('<canvas>').prependTo(this.el)[0];
                        canvas.width = this.size;
                        canvas.height = this.size / 2;
                        this.ctx = canvas.getContext('2d');
                    },
                }).on("circle-animation-progress", function (event, progress, stepValue) {
                    var progress_display = that.$el.find(".half-circle-progress-display");
                    if (progress_display.length > 0) {
                        var value = "-"; 
                        if (_.isNumber(that.options.graph.halfCircle[circle_name].opts.actual)) {
                            value = that._getValue(stepValue, that.options.graph.halfCircle[circle_name].opts, that.options.graph.halfCircle[circle_name].opts.actual);
                        }
                        //progress_display.text(value); 
                    }
                }).on("circle-animation-end", function (event) {
                    var progress_display = that.$el.find(".half-circle-progress-display");
                    if (progress_display.length > 0) {
                        var actual = that.options.graph.halfCircle[circle_name].opts.actual; 
                        progress_display.text(_.isNumber(actual) ? actual : "-"); 
                    }
                });
            }); 

            var progressbarset_options = {
                total: this.model.get("productionItem").get("targetQty") + 50,
                actual: this.model.get("productionItem").get("actualQty"),
                target: this.model.get("productionItem").get("targetQty"),
            }; 

            this.options.graph.productQty.progressbars.options = progressbarset_options; 
            
            var progressbar_actual = this.options.graph.productQty.progressbars.actual = new ProgressBar.Line('.progressbar-actual', {
                color: "#57A639",
                trailColor: 'transparent',
                trailWidth: 1,
                easing: 'easeInOut',
                strokeWidth: 4,
            });

            var progressbar_target = this.options.graph.productQty.progressbars.target = new ProgressBar.Line('.progressbar-target', {
                color: "#CCCCCC",
                trailColor: 'transparent',
                trailWidth: 1,
                easing: 'easeInOut',
                strokeWidth: 4,
                from: { color: '#FFF' },
                to: { color: '#CCCCCC' },
                step: function (state, bar) {
                }
            });

            var pbar_container = this.$el.find(".pbar-container");
            pbar_container.find("svg").css({
                height: "25px",
                width: "100%",
                display: "block",
                "overflow-y": "hidden",
            });
            
        }
        , renderTimeAccountsGrid: function () {
            var that = this; 
            var timeAccountModel = Backbone.Model.extend({
                defaults: {
                    name: null,
                    duration: null,
                    color: null, 
                },
            });

            this.options.graph.timeAccounts.model = timeAccountModel; 

            var timeAccountsCollection = Backbone.Collection.extend({
                model: timeAccountModel,
                initialize: function () {
                    Backbone.Collection.prototype.initialize.apply(this, arguments);

                    //this.listenTo(this, "add", this.changed);
                    //this.listenTo(this, "remove", this.changed);
                    //this.listenTo(this, "reset", this.changed);
                    //this.listenTo(this, "change:duration", this.changed); 
                },
                changed: function (a, b, c) {
                    //var m1 = _.map(this.toJSON(), function (t) { return t.duration ? t.duration : 0 });
                    //var m2 = _.sortBy(_.map(this.toJSON(), function (t) { return t.duration ? t.duration : 0 }), function (a) { return parseInt(a, 10) }).reverse(); 

                    //if (m1.join('_') != m2.join('_')) {
                    //    this.comparator = function (a, b) {
                    //        var da = a.get("duration");
                    //        var db = b.get("duration");

                    //        if (da > db) return -1;
                    //        else if (da < db) return 1;
                    //        else return 0;
                    //    };

                    //    this.sort();

                    //    this.comparator = null; 
                    //}
                }
            });

            this.options.graph.timeAccounts.col = timeAccountsCollection; 

            var timeAccounts = new timeAccountsCollection(); 

            this.options.graph.timeAccounts.collection = timeAccounts; 

            var columns = [
                {
                    name: "name",
                    label: app.translate(that, "name_label"),
                    editable: false,
                    cell: Backgrid.StringCell.extend({
                        orderSeparator: '',
                        className: "string-cell",
                    })
                },
                {
                    name: "duration",
                    label: app.translate(that, "duration_label"),
                    editable: false,
                    cell: Backgrid.StringCell.extend({
                        orderSeparator: '',
                        className: "string-cell",
                        render: function () {
                            this.$el.html(this.secondsToHHMMSS(this.model.get("duration"))); 
                            this.delegateEvents();
                            return this;
                        },
                        secondsToHHMMSS: function (d) {
                            d = Number(d);
                            var h = Math.floor(d / 3600);
                            var m = Math.floor(d % 3600 / 60);
                            var s = Math.floor(d % 3600 % 60);
                            return ((h > 0 ? h + ":" + (m < 10 ? "0" : "") : "") + m + ":" + (s < 10 ? "0" : "") + s);
                        }, 
                    })
                },
                {
                    name: "graph",
                    label: app.translate(that, "graph_label"),
                    editable: false,
                    cell: Backgrid.StringCell.extend({
                        template: _.template("<div style='height:20px;width:180px;position:relative;' class='bar-graph-container'></div>"), 
                        orderSeparator: '',
                        className: "string-cell",
                        initialize: function () {
                            _.bindAll(this); 
                            Backgrid.StringCell.prototype.initialize.apply(this, arguments);

                            this.options.graph = {
                                pbar: null, 
                            };

                            this.bindEvents();
                        }, 
                        render: function () {
                            this.$el.html(this.template()); 
                            var pbar_container = this.$el.find(".bar-graph-container");
                            var val = this.model.get("duration"); 

                            var pbar = this.options.graph.pbar = new ProgressBar.Line(pbar_container.get(0), {
                                color: this.model.get("color"),
                                trailColor: 'transparent',
                                easing: 'easeInOut',
                                step: function (state, bar) {
                                }
                            });

                            pbar_container.find("svg").css({
                                height: "15px",
                                width: "100%",
                                "overflow-y": "hidden",
                            }); 

                            var opt = {
                                total: Math.abs(that.model.get("end").diff(that.model.get("start"), 'seconds')), 
                            };

                            setTimeout(function () {
                                pbar.animate(that._getPercent(val, _.extend({}, opt, { displayMinimum: true })));
                            }, 300);

                            this.delegateEvents();
                            return this;
                        },
                        refresh: function () {
                            var opt = {
                                total: Math.abs(that.model.get("end").diff(that.model.get("start"), 'seconds')),
                            };

                            var val = this.model.get("duration");
                            this.options.graph.pbar.animate(that._getPercent(val, _.extend({}, opt, { displayMinimum: true })));
                        }, 
                        bindEvents: function () {
                            this.listenTo(this.model, "change", _.debounce(this.refresh, 100)); 
                        }, 
                    })
                },
            ];

            var grid = new Backgrid.Grid({
                className: "backgrid table table-hover table-condensed time-accounts-table",
                columns: columns,
                collection: timeAccounts,
            });

            this.options.graph.timeAccounts.grid = grid; 

            this.$el.find(".time-accounts-grid-container").append(grid.render().el);

            this.refreshTimeAccountsGrid(); 
        }
        , refreshTimeAccountsGrid: function () {
            var that = this; 
            var timeAccounts = this.options.graph.timeAccounts.collection;

            var timeaccountsarr = [];
            this.model.get("delaysInformation").get("timeAccounts").forEach(function (m) {
                timeaccountsarr.push({
                    id: m.get("id"), 
                    name: app.translate(that, m.get("code")),
                    duration: m.get("duration"),
                    color: m.get("color"),
                });
            });

            timeAccounts.set(timeaccountsarr);
        }
        , refreshBatchInfo: function () {
            var prodItem = this.model.get("productionItem");
            var batchNumber = prodItem.get("batchNumber");
            var batchProduct = prodItem.get("batchProduct"); 
            var batchStart = prodItem.get("batchStart"); 

            var btnOpenBatch = this.$el.find(".btn-open-batch");
            var btnCloseBatch = this.$el.find(".btn-close-batch");

            //demouser
            if (this.options.demouser || !this.options.writePermission) {
                btnOpenBatch.attr("disabled", true);
                btnCloseBatch.attr("disabled", true);
            }

            if (batchNumber) {
                btnCloseBatch.removeClass("hide");
                btnOpenBatch.addClass("hide");
            } else {
                btnCloseBatch.addClass("hide");
                btnOpenBatch.removeClass("hide");
            }

            this.$el.find(".batch-number-span").text(batchNumber ? batchNumber : "");
            this.$el.find(".batch-product-name-span").text(batchProduct ? batchProduct : "");
            this.$el.find(".batch-start-span").text(batchStart ? batchStart : "");
        }
        , drawProgressGraph: function () {
            var that = this; 

            //total in seconds
            var start = this.model.get("start"), end = this.model.get("end"); 
            var total = Math.abs(this.model.get("end").diff(this.model.get("start"), 'seconds'));
            var opt = {
                total: total, 
            }; 

            var itemCategory = Backbone.Model.extend({
                defaults: {
                    id: null, 
                    name: null,
                    color: null, 
                }, 
            });

            var itemCategoriesCollection = Backbone.Collection.extend({
                model: itemCategory, 
            }); 
            
            var itemCategories = new itemCategoriesCollection();
            itemCategories.add(new itemCategory({
                id: -2,
                name: "ACTIVE",
                color: "#3aea3a",
            })); 
            itemCategories.add(new itemCategory({
                id: -1,
                name: "NOTDECLARED",
                color: "#de2e27",
            }));

            this.model.get("delaysInformation").get("delayCodes").forEach(function (m) {
                itemCategories.add(
                    new itemCategory({
                        id: m.get("id"),
                        name: m.get("code"),
                        color: m.get("color"), 
                    })
                );
            });

            var microdelayCat; 
            if (microdelayCat = itemCategories.findWhere({ name: "MICRODELAY" })){
                microdelayCat.set("color", "#008CFF");
            }

            var progressItemModel = Backbone.Model.extend({
                defaults: {
                    duration: null,
                    category: null,
                    start: null,
                    end: null,
                    inMaintenance: null,

                    delayId: null,
                    delayCodeExcludesAvailability: null,
                    delayForceIncludeInAvailability: null,
                },
            });

            var progressItemsCollection = Backbone.Collection.extend({
                model: progressItemModel,
            });

            var progressItems = new progressItemsCollection(); 

            var events = this.model.get("delaysInformation").get("events");

            events = this.model.get("delaysInformation").get("events").sortBy(function (m) {
                return m.get("start");
            });

            var batches = _.uniq(this.model.get("delaysInformation").get("events").map(function (m) {
                return { batchId: (m.get('batchId') ? m.get('batchId') : -1), batchNumber: m.get('batchNumber'), product: m.get('product'), color: null }; 
            }), null, function (a) {
                return a.batchId; 
            });

            var t_color = 0; var batch_line_colors = ['blue', 'yellow', 'green', 'orange', 'purple']; 
            _.forEach(batches, function (m) {
                m.color = batch_line_colors[t_color];
                if (t_color < 4) t_color++;
                else t_color = 0; 
            });

            //var batchGroups = [];
            //events.forEach(function (m, inx) {
            //});

            var batchesEvents = [];
            var cb = {
                id: null,
                start: null,
                end: null, 
            }; 

            events.forEach(function (m, inx, collection) {
                if (!cb.id || cb.id != ((m.get('batchId')) ? m.get('batchId') : -1)) {
                    if (cb.id) {
                        cb.end = collection[inx - 1].get('end');
                        batchesEvents.push(cb);
                    }

                    cb = {
                        id: (m.get('batchId')) ? m.get('batchId') : -1,
                        start: m.get('start'),
                        end: null,
                    };
                }

                var pim = new progressItemModel({
                    id: m.get("id"), 
                    duration: m.get("duration"),
                    start: m.get("start"),
                    end: m.get("end"), 
                    category: itemCategories.findWhere({ id: m.get("delayCodeId") }),
                    inMaintenance: m.get("inMaintenance"),

                    delayId: m.get("delayId"),
                    delayCodeExcludesAvailability: m.get("delayCodeExcludesAvailability"),
                    delayForceIncludeInAvailability: m.get("delayForceIncludeInAvailability"),
                });

                if (!pim.get("category")) {
                    if (m.get("type") == "DELAY") {
                        pim.set("category", itemCategories.findWhere({ id: -1 }));
                    } else if (m.get("type") == "PRODUCTION") {
                        pim.set("category", itemCategories.findWhere({ id: -2 }));
                    }
                }

                progressItems.add(pim);

                if (collection.length - 1 == inx) {
                    cb.end = m.get('end');
                    batchesEvents.push(cb); 
                }
            }); 

            var lastbatch = {
                id: -1,
                start: null,
                end: this.model.get("start").format("YYYY-MM-DD HH:mm:ss"),
            };

            var batchesContainer = this.$el.find('.batch-indicators-container');
            batchesContainer.empty(); 

            _.forEach(batchesEvents, function (m, inx, arr) {
                if (lastbatch != null && lastbatch.end != m.start) {
                    var nodatabar = $("<div class='batch-line'></div>");
                    var nodataduration = Math.abs((new moment.duration(new moment(lastbatch.end).diff(new moment(m.start)))).asSeconds());
                    var nodatawd = ((nodataduration * 100) / total);
                    nodatabar.css("width", nodatawd + "%");
                    batchesContainer.append(nodatabar);
                }

                var batchProps = _.findWhere(batches, { batchId: (m.id) ? m.id : -1 }); 
                var batchLine = $('<div class="batch-line ' + batchProps.color + '"></div>');

                var start = new moment(m.start); var end = new moment(m.end); 
                var duration = Math.abs((new moment.duration(start.diff(end))).asSeconds()); 

                var wd = ((duration * 100) / total);
                batchLine.css("width", wd + "%");
                batchLine.css('opacity', (1 / arr.length) *(inx+1)); 

                var popover_title = _.template('<h4 style="padding:0px;"><%= title %></h4>');
                var popover_content = _.template('<div><dl class="dl-horizontal">'
                                        + '<dt style="width:80px;">' + app.translate(that, 'batch_po_batchNumber_label') + '</dt><dd style="margin-left:120px;"><%= batchNumber %></dd>'
                                        + '<dt style="width:80px;">' + app.translate(that, 'batch_po_product_label') + '</dt><dd style="margin-left:120px;"><%= product %></dd>'
                                        + '</dl></div>');

                var $po = batchLine.popover({
                    placement: "top",
                    html: true,
                    trigger: "hover",
                    title: popover_title({
                        title: app.translate(that, 'batch_po_title'),
                    }),
                    content: popover_content({
                        batchNumber: batchProps.batchNumber,
                        product: batchProps.product, 
                    }),
                    delay: { show: 150, hide: 0 },
                    //container: '.progress',
                });

                batchesContainer.append(batchLine);

                lastbatch = m; 
            });

            var container = this.$el.find(".progress-bar-container");
            var pbar = container.find(".progress"); 
            pbar.empty(); 

            var totalwd = 0;
            var lastpi = new progressItemModel({
                duration: null,
                category: null,
                start: null,
                end: this.model.get("start").format("YYYY-MM-DD HH:mm:ss.sss"),
            });

            progressItems.forEach(function (m) {
                if (lastpi != null && lastpi.get("end") != m.get("start")) {
                    var nodatabar = $("<div style='cursor:pointer;' class='bar bar-white no-data-bar'></div>");
                    var nodataduration = Math.abs((new moment.duration(new moment(lastpi.get("end")).diff(new moment(m.get("start"))))).asSeconds());
                    var nodatawd = ((nodataduration * 100) / total);
                    nodatabar.css("width", nodatawd + "%");

                    var popover_title = _.template('<h4 style="color: #000;padding:0px;"><%= title %></h4>');
                    var popover_content = _.template('<div><dl class="dl-horizontal">'
                                            + '<dt style="width:100px;">Start Time</dt><dd style="margin-left:120px;"><%= startTime %></dd>'
                                            + '<dt style="width:100px;">Duration</dt><dd style="margin-left:120px;"><%= duration %></dd>'
                                            + '</dl></div>');

                    var $npo = nodatabar.popover({
                        placement: "top",
                        html: true,
                        trigger: "hover",
                        title: popover_title({
                            title: app.translate(that, "no_data_available_po_title"),
                        }),
                        content: popover_content({
                            startTime: new moment(lastpi.get("end")).format("YYYY-MM-DD HH:mm:ss"),
                            duration: that.secondsToHMS(nodataduration),
                        }),
                        delay: { show: 150, hide: 0 },
                        //container: '.progress',
                    });

                    $npo.on("shown", function () {
                        pbar.find(".popover-title").css({
                            "background-color": "#fff"
                        });
                    });

                    pbar.append(nodatabar);
                }

                var bar = $("<div style='cursor:pointer;' class='bar bar-popover bar-hover bar-custom-color'></div>");
                //var bar = $("<div style='cursor:pointer;position:absolute;' class='bar bar-popover bar-hover'></div>"); 
                var wd = ((m.get("duration") * 100) / total); 

                while ((totalwd + wd) > 100) {
                    wd--; 
                }
                totalwd += wd;

                //var momentStart = new moment(m.get("start"));
                //var left = ((moment.duration(Math.abs(momentStart.diff(start))).asSeconds() * 100) / total);
                //bar.css("left", left + "%"); 

                //bar.addClass("bar-" + m.get("category").get("color")); 
                if (m.get("delayId") != null) {
                    bar.attr("data-delay-id", m.get("delayId"));
                }

                if (!m.get('inMaintenance'))
                    bar.css("background-color", m.get("category").get("color"));
                else bar.css("background-color", '#FFEF00');

                var not_included_in_ava = false;
                var included_cause_exceeded = false; 

                if (m.get("delayId") != null) { //is delay
                    not_included_in_ava = m.get("delayCodeExcludesAvailability") && !m.get("delayForceIncludeInAvailability");
                    included_cause_exceeded = m.get("delayForceIncludeInAvailability");
                }

                var popover_title = _.template('<h4 style="color: #fff;padding:0px;"><%= title %></h4>');
                var popover_content = _.template('<div><dl class="dl-horizontal">' 
                                        + '<dt style="width:100px;">' + app.translate(that, 'bar_po_reason') + '</dt><dd style="margin-left:120px;"><%= reason %></dd>'
                                        + '<dt style="width:100px;">' + app.translate(that, 'bar_po_start_time') + '</dt><dd style="margin-left:120px;"><%= startTime %></dd>'
                                        + '<dt style="width:100px;">' + app.translate(that, 'bar_po_duration') + '</dt><dd style="margin-left:120px;"><%= duration %></dd>'
                                        + '</dl></div>'

                                        + ((not_included_in_ava) ? '<div><span style="color:#0F63FF;font-size:14px;">*' + app.translate(that, "delay_not_included_availability") + '</span></div>' : '')
                                        + ((included_cause_exceeded) ? '<div><span style="color:#FF2E2E;font-size:14px;">*' + app.translate(that, "delay_exceeded_standard_time") + '</span></div>' : '')

                                        );

                //creating popover
                var mod = that.model.get("delaysInformation").get("events").findWhere({ id: m.get("id") });

                var $po = bar.popover({
                    placement: "top",
                    html: true,
                    trigger: "hover",
                    title: popover_title({
                        title: app.translate(that, m.get("category").get("name")), 
                    }),
                    content: popover_content({
                        reason: app.translate(that, m.get("category").get("name")),
                        startTime: new moment(m.get("start")).format("YYYY-MM-DD HH:mm:ss"),
                        duration: that.secondsToHMS(m.get("duration")), 
                    }),
                    delay: { show: 150, hide: 0 },
                    //container: '.progress',
                }); 

                $po.on("shown", function () {
                    if (!m.get('inMaintenance'))
                        pbar.find(".popover-title").css({ "background-color": m.get("category").get("color") });
                    else pbar.find(".popover-title").css({ "background-color": '#FFEF00', 'color': '#000' });
                }); 

                bar.css("width", wd + "%"); 
                pbar.append(bar);

                lastpi = m; 
            });

            pbar.find('[data-delay-id]').on("click", _.bind(that.showDelayInfoModalFromProgress, that));
            //pbar.find('[data-delay-id]').on("click", _.bind(that.showDelayInfoModalFromProgress, that));

            var pb_timeline = this.$el.find(".progress-bar-timeline");
            pb_timeline.empty();
            var t_divisions = 12; 
            var div_duration = total / t_divisions;

            var st = start.clone(); 
            var arr = _.times(t_divisions, function (inx) {
                return st.clone().add(div_duration * inx, 'seconds'); 
            });

            _.each(arr, function (val, inx) {
                var lbl = $('<small style="position:absolute;top:-15px;"></small>');
                var marker = $('<div style="position:absolute;top:-40px;height:40px;width:0px;border-left:1px dashed #ccc;"></div>');

                lbl.text(val.format("HH:mm"));
                var left = (inx * div_duration * 100) / total;

                lbl.css({ left: (left + 0.5) + '%' });
                marker.css({ left: left + '%' });
                pb_timeline.append(marker);
                pb_timeline.append(lbl);
            }); 
        }

        , secondsToHMS: function (d) {
            d = Number(d);
            var h = Math.floor(d / 3600);
            var m = Math.floor(d % 3600 / 60);
            var s = Math.floor(d % 3600 % 60);
            return ((h > 0 ? h + " hs " : "") + m + " min " + s + " seg");
        }
        , secondsToHHMMSS: function (d) {
            d = Number(d);
            var h = Math.floor(d / 3600);
            var m = Math.floor(d % 3600 / 60);
            var s = Math.floor(d % 3600 % 60);
            return ((h > 0 ? h + ":" + (m < 10 ? "0" : "") : "") + m + ":" + (s < 10 ? "0" : "") + s);
        }

        , openBatch: function () {
            try {
                var that = this,
                    clickedBtn = this.$el.find('.btn-open-batch');

                //Disable clicked button to prevent issues with multiple clicks.
                clickedBtn.attr("disabled", true);

                var modal = new OEE.Views.OpenBatchModal({
                    rowData: {
                        productionItemId: this.model.get("productionItemId"), 
                    },
                });

                var fn_modal_shown_hidden = function () {
                    try {
                        //Enable button in both events, shown and hidden, just to prevent issues if one of them fails
                        //or it is not executed for some reason.
                        clickedBtn.attr("disabled", false);
                    }
                    catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
                };

                modal.on("shown", fn_modal_shown_hidden);
                modal.on("hidden", fn_modal_shown_hidden);
                modal.on("success", function () {
                    try {
                        that._autoRefresh({
                            refreshCacheDelays: true,
                            refreshCachePItem: true,
                            refreshData: true,
                        });
                        //that.refresh({
                        //    //refreshCacheDelays: true,
                        //    refreshCachePItem: true,
                        //    refreshData: true,
                        //});
                    }
                    catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }

                    //Set button enable here again just to be sure and prevent issues if shown event fails.
                    try {
                        clickedBtn.attr("disabled", false);
                    }
                    catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
                });

                modal.show();
            }
            catch (e) {
                console.error((e.stack) ? e.stack : new Error(e).stack);

                try {
                    clickedBtn.attr("disabled", false);
                }
                catch (e2) { console.error((e2.stack) ? e2.stack : new Error(e2).stack); }
            }            
        }

        //, closeBatch: _.debounce(function () {
        //    var that = this;
        //    var productionItem = this.model.get("productionItem"); 

        //    var modal = new Modal.Views.Main({
        //        id: "close-batch-modal"
        //        , title: app.translate(that, "close_batch_modal_title")
        //        , content: app.translate(that, "close_batch_modal_body", [productionItem.get("batchNumber"), productionItem.get("batchProduct")])
        //        , allowCancel: true
        //        , buttons_type: "CUSTOM-BUTTONS"
        //        , custom_buttons: [
        //            { id: 'continue', classNames: 'btn-large btn-primary', btn_lbl: app.translate(that, 'continue_btn_label') },
        //            { id: 'cancel', classNames: 'btn-large', btn_lbl: app.translate(that, 'cancel_btn_label') },
        //        ]
        //        , okCloses: true
        //    });

        //    var modal_closing_batch = new Modal.Views.Main({
        //        id: "closing-batch-modal"
        //        , title: app.translate(that, "closing_batch_modal_title")
        //        , content: '<div class="row-fluid">' +
        //                        '<div class="span12">' +
        //                            '<div style="text-align:center;margin-top:10px;">' +
        //                                '<i class="fa fa-5x fa-cog fa-spin"></i><br />' +
        //                                '<span style="font-size: 20px;top:3px;position:relative;">Closing Batch...</span>' +
        //                            '</div>' +
        //                        '</div>' +
        //                    '</div>'
        //        , allowCancel: false
        //        , buttons_type: "CUSTOM-BUTTONS"
        //        , okCloses: false
        //    });

        //    function close_modal_continue() {
        //        var stillProcessing = true;

        //        setTimeout(function () {
        //            if (stillProcessing) modal_closing_batch.show();
        //        }, 500);

        //        var QP = new QueryParameters();
        //        QP.Add("Id", "INT", productionItem.get("batchId"));
        //        QP.Add("MachineProductionItemId", "INT", productionItem.get("id"));

        //        app.CallProcedure(app.DatabaseNames.OEE + ".WebApp.CloseBatch", QP, {
        //            onSuccess: function (data) {
        //                stillProcessing = false;

        //                //hiding both buttons while it refreshes
        //                that.$el.find('.btn-close-batch').addClass('hide');
        //                that.$el.find('.btn-open-batch').addClass('hide');

        //                that.refresh();
        //                modal_closing_batch.hide();
        //            },
        //            Secured: true,
        //        }, app.ConnectionStrings.app);
        //    }

        //    modal.on("customButtonClicked", function (e) {
        //        e.modal.hide();
        //        if (e.buttonid == "continue") {
        //            close_modal_continue(); 
        //        }
        //    });

        //    modal.on("shown", function () {
        //    });

        //    modal.show(); 
        //}, 500, true)
        , showCloseBatchModal: function () {
            try {
                var that = this,
                    productionItem = this.model.get('productionItem').toJSON(),
                    customPath = app.oeeModule.screensPath + "oee/",
                    clickedBtn = this.$el.find('.btn-close-batch');

                //Disable clicked button to prevent issues with multiple clicks.
                clickedBtn.attr("disabled", true);

                var fn_showModal = function () {
                    var modal = new OEE.Views.CloseBatchModal({
                        model: new OEE.Models.CloseBatchModal({
                            batchId: productionItem.batchId,
                            batchNumber: productionItem.batchNumber,
                            batchProduct: productionItem.batchProduct,
                            productionItemId: productionItem.id,
                            badManualEnabled: productionItem.badUnitProducedManualEnabled,
                            unitsLegend: productionItem.badUnitProducedManualUnits,
                            currentQuantity: productionItem.badQty,
                            productionQty: (productionItem.badQty) ? productionItem.badQty : 0,
                        }),
                        parent: that,
                        template: that.templatesLoaded.closeBatchModal,
                    });

                    var fn_modal_shown_hidden = function () {
                        try {
                            //Enable button in both events, shown and hidden, just to prevent issues if one of them fails
                            //or it is not executed for some reason.
                            clickedBtn.attr("disabled", false);
                        }
                        catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
                    };

                    that.listenToOnce(modal, 'shown', fn_modal_shown_hidden)
                        .listenToOnce(modal, 'hidden', fn_modal_shown_hidden)
                        .listenToOnce(modal, 'finish', function (modal, data) {
                            try {
                                if (data.result == 'SUCCESS') {
                                    //hiding both buttons while it refreshes
                                    that.$el.find('.btn-close-batch').addClass('hide');
                                    that.$el.find('.btn-open-batch').addClass('hide');


                                    that._autoRefresh({
                                        refreshCacheDelays: true,
                                        refreshCachePItem: true,
                                        refreshData: true,
                                    });
                                    //that.refresh({
                                    //    //refreshCacheDelays: true,
                                    //    refreshCachePItem: true,
                                    //    refreshData: true,
                                    //});
                                }
                            }
                            catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
                        });

                    modal.show();
                };

                if (!this.templatesLoaded.closeBatchModal) {
                    T.render.call(this, this.template, function (tmp) {
                        that.templatesLoaded.closeBatchModal = tmp;
                        fn_showModal();
                    }, customPath, "close_batch_modal_template");
                } else {
                    fn_showModal()
                    //setTimeout(function () {
                    //    T.render.call(that, that.template, function (tmp) {
                    //        if (!that.templatesLoaded.closeBatchModal) {
                    //            fn_showModal(tmp);
                    //        }
                    //    }, customPath, "add_bad_manual_modal_template");
                    //}, 500);
                }
            }
            catch (e) {
                console.error((e.stack) ? e.stack : new Error(e).stack);

                try {
                    clickedBtn.attr("disabled", false);
                }
                catch (e2) { console.error((e2.stack) ? e2.stack : new Error(e2).stack); }
            }
        }
        , closeBatch: function (options) {
            var that = this,
                qp = new QueryParameters(),
                opt = _.extend({ async: true, }, options);

            for (var pn in opt.params)
                qp.Add(pn, 'VARCHAR', opt.params[pn]);

            Core.Json.CallProcedure(
                app.DatabaseNames.OEE + '.WebApp.CloseBatch',
                qp,
                {
                    onSuccess: function (resp) {
                        try {
                            if ((!resp) || (!resp.Message)) {
                                if (opt.success != null && _.isFunction(opt.success))
                                    opt.success(resp);
                            }
                            else {
                                var errorMsg = (resp.Message)
                                                    ? resp.Message
                                                    : 'TRANSACTION_ERROR';

                                console.error(new Error(errorMsg).stack);

                                if (opt.error != null && _.isFunction(opt.error))
                                    opt.error(errorMsg);
                            }
                        }
                        catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                    },
                    onError: function (errorMsg) {
                        try {
                            console.error(new Error(errorMsg).stack);

                            if (opt.error != null && _.isFunction(opt.error))
                                opt.error(errorMsg);
                        }
                        catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                    },
                    Async: opt.async,
                    Secured: true,
                },
                app.ConnectionStrings.app
            );
        }
        , updateMaintenanceMode: function (e) {
            var that = this;
            var maintenanceToggle = this.$el.find('#in-maintenance-toggle');
            this.options.graph.maintenanceToggled = true;

            maintenanceToggle.bootstrapToggle('disable');

            var productionItem = this.model.get("productionItem");
            var maintenanceModeToggle = this.$el.find("#in-maintenance-toggle"); 
            var isChecked = maintenanceModeToggle.is(":checked"); 

            function update_maintenance_mode() {
                var QP = new Core.Database.QueryParameters();
                QP.Add('MachineProductionItemId', 'INT', productionItem.get("id"));
                QP.Add('MaintenanceMode', 'BIT', isChecked);

                app.CallProcedure(app.DatabaseNames.OEE + '.WebApp.UpdateMaintenanceMode', QP, {
                    onSuccess: function (data) {
                        that.options.graph.maintenanceToggled = false;
                        that._autoRefresh({
                            refreshCacheDelays: true,
                            refreshCachePItem: true,
                            refreshData: true,
                        });
                    },
                    onError: function (data) {
                        that.options.graph.maintenanceToggled = false;
                        that._autoRefresh({
                            refreshCacheDelays: true,
                            refreshCachePItem: true,
                        });
                    },
                    Secured: true,
                }, app.ConnectionStrings.app);
            }

            if (isChecked) {
                var modal = new Modal.Views.Main({
                    id: "maintenance-mode-confirmation-modal"
                    , title: app.translate(that, "maintenance_mode_confirmation_modal_title")
                    , content: app.translate(that, "maintenance_mode_confirmation_modal_body")
                    , allowCancel: false
                    , buttons_type: "CUSTOM-BUTTONS"
                    , custom_buttons: [
                        { id: 'continue', classNames: 'btn-large btn-primary', btn_lbl: app.translate(that, 'continue_btn_label') },
                        { id: 'cancel', classNames: 'btn-large', btn_lbl: app.translate(that, 'cancel_btn_label') },
                    ]
                    , okCloses: true
                });

                function maintenance_mode_confirmation_modal_continue() {
                    update_maintenance_mode(); 
                }

                function maintenance_mode_confirmation_modal_cancel() {
                    that.options.graph.maintenanceToggled = false;

                    maintenanceToggle.bootstrapToggle('enable');
                    maintenanceToggle.bootstrapToggle('off', true); 

                    //that._autoRefresh();
                }

                modal.on("shown", function () {
                });

                modal.on("customButtonClicked", function (e) {
                    e.modal.hide();
                    if (e.buttonid == "continue") {
                        maintenance_mode_confirmation_modal_continue();
                    } else {
                        maintenance_mode_confirmation_modal_cancel(); 
                    }
                });

                modal.show();
            } else {
                update_maintenance_mode(); 
            }
        }
        , startProductionMode: function () {
            var that = this; 
            var actions = this.$el.find(".out-of-production-mode-actions");
            var start_button = actions.find(".btn-start-production-mode"); 
            var loading_icon = actions.find(".loading-icon"); 

            this.options.graph.setProductionModeLoading = true; 
            this.updateOutOfProductionMode(false, function (success, data) {
                if (!success) {
                    that.options.graph.setProductionModeLoading = false;
                    start_button.removeClass("hide");
                    loading_icon.addClass("hide");
                } else {
                    setTimeout(function () {
                        that.options.graph.setProductionModeLoading = false;
                    }, 1500);
                }
            });

            start_button.addClass("hide");
            loading_icon.removeClass("hide"); 
        }
        , stopProductionMode: function () {
            var that = this; 
            this.options.graph.outOfProductionModalShown = true;

            this.updateOutOfProductionMode(true, function (success, data) {
                if (!success) {
                    that.options.graph.outOfProductionModalShown = false;

                    if (this.options.writePermission) {
                        that.$el.find(".btn-end-production-mode").attr("disabled", false);
                    }
                } else {
                    setTimeout(function () {
                        that.options.graph.outOfProductionModalShown = false;
                    }, 1500);
                }
            });

            this.$el.find(".btn-end-production-mode").attr("disabled", true);
        }
        , updateOutOfProductionMode: function (outOfProduction, callback) {
            var that = this;

            var productionItem = this.model.get("productionItem");

            function update_service_mode() {
                var QP = new Core.Database.QueryParameters();
                QP.Add('MachineProductionItemId', 'INT', productionItem.get("id"));
                QP.Add('OutOfService', 'BIT', outOfProduction);

                app.CallProcedure(app.DatabaseNames.OEE + '.WebApp.UpdateServiceMode', QP, {
                    onSuccess: function (data) {
                        var success = false; 
                        if (data && data.Table && data.Table[0] && data.Table[0].Status == 'OK') {
                            success = true;
                        }

                        if (callback)
                            callback.call(that, success, data); 

                        that._autoRefresh({
                            refreshCacheDelays: true,
                            refreshCachePItem: true,
                            refreshData: true,
                        });
                    },
                    onError: function (data) {
                        that._autoRefresh();

                        if (callback) {
                            callback.call(that, false, data);
                        }
                    },
                    Secured: true,
                }, app.ConnectionStrings.app);
            }

            if (outOfProduction == true) {
                var modal = new Modal.Views.Main({
                    id: "out-of-production-confirmation-modal"
                    , title: app.translate(that, "out_of_production_confirmation_modal_title")
                    , content: app.translate(that, "out_of_production_confirmation_modal_body")
                    , allowCancel: false
                    , buttons_type: "CUSTOM-BUTTONS"
                    , custom_buttons: [
                        { id: 'continue', classNames: 'btn-large btn-primary', btn_lbl: app.translate(that, 'continue_btn_label') },
                        { id: 'cancel', classNames: 'btn-large', btn_lbl: app.translate(that, 'cancel_btn_label') },
                    ]
                    , okCloses: true
                });

                modal.on("shown", function () {
                });

                modal.on("customButtonClicked", function (e) {
                    e.modal.hide();
                    if (e.buttonid == "continue") {
                        update_service_mode();
                    } else {
                        if (callback) {
                            callback.call(that, false); 
                        }
                    };
                });

                modal.show();
            } else {
                update_service_mode();
            }
        }
        , _startAutoRefresh: function () {
            try {
                if (this.options.MYREFERENCES.autoRefresh.toid != null) {
                    clearTimeout(this.options.MYREFERENCES.autoRefresh.toid);
                    this.options.MYREFERENCES.autoRefresh.toid = null;
                }

                this.options.MYREFERENCES.autoRefresh.enabled = true;

                this._autoRefresh();
            } catch (Error) { }
        }

        , _autoRefresh: function (opt) {
            if (this.options.MYREFERENCES.autoRefresh.toid != null) {
                clearTimeout(this.options.MYREFERENCES.autoRefresh.toid);
                this.options.MYREFERENCES.autoRefresh.toid = null;
            }

            this.refresh(opt);

            if (this.options.MYREFERENCES.autoRefresh.enabled == true) {
                this.options.MYREFERENCES.autoRefresh.toid = setTimeout(_.bind(this._autoRefresh, this), 3000);
            }
        }

        , _stopAutoRefresh: function () {
            if (this.options.MYREFERENCES.autoRefresh.toid != null) {
                clearTimeout(this.options.MYREFERENCES.autoRefresh.toid);
                this.options.MYREFERENCES.autoRefresh.toid = null;
            }
            this.options.MYREFERENCES.autoRefresh.enabled = false;
        }

        , preExecRender: _.debounce(function () {
            var rendered = this.options.MYREFERENCES.isRendered;
            var rendering = this.options.MYREFERENCES.isRendering; 

            if (!rendered && !rendering)
                this.render();
            else if (!rendering)
                this.refreshGraph();
            else
                setTimeout(_.bind(this.preExecRender, this), 100);
        }, 500)
        , refreshGraph: function () {
            try{
                var that = this;

                //check if has batch and is running
                var batchId = this.model.get("productionItem").get("batchId"); 
                var machineStatus = this.model.get("productionItem").get("machineStatus");
                var maintenanceStatus = this.model.get("productionItem").get("maintenanceStatus");
                var runningMachineStatus = this.model.get("productionItem").get("runningMachineStatus");
                var outOfService = this.model.get("productionItem").get("outOfService"); 

                var hasMaintenanceSwitch = this.model.get('productionItem').get('hasMaintenanceSwitch'); 

                var maintenanceSwitch = this.$el.find(".in-maintenance-switch");

                

                if (hasMaintenanceSwitch) {
                    var maintenanceToggle = this.$el.find('#in-maintenance-toggle');
                    var currentMToggleStatus = this.$el.find('#in-maintenance-toggle').prop("checked"); 
                    var mToggleIsDisabled = this.$el.find('#in-maintenance-toggle').prop("disabled"); 

                    if (!this.options.graph.maintenanceToggled) {
                        if (currentMToggleStatus != maintenanceStatus) {
                            if (mToggleIsDisabled) maintenanceToggle.bootstrapToggle('enable');
                            maintenanceToggle.bootstrapToggle((maintenanceStatus) ? 'on' : 'off', true);
                            if (mToggleIsDisabled) maintenanceToggle.bootstrapToggle('disable');
                        }
                    }

                    maintenanceSwitch.removeClass('not-visible');

                    if (runningMachineStatus == "RUNNING" && !maintenanceStatus) {
                        maintenanceToggle.bootstrapToggle('disable');
                    } else {
                        maintenanceToggle.bootstrapToggle('enable');
                    }

                    if (this.options.demouser || !this.options.writePermission) {
                        maintenanceToggle.bootstrapToggle('disable');
                    }
                }
                else {
                    maintenanceSwitch.addClass('not-visible');
                }

                if (maintenanceStatus == true) {
                    this.setNoBatchOverlay(false);
                    this.setMaintenanceModeOverlay(true);
                } else {
                    this.setMaintenanceModeOverlay(maintenanceStatus);
                    this.setNoBatchOverlay(!batchId);
                }

                var outOfProductionBtn = this.$el.find(".btn-end-production-mode");
                var outOfProductionOverlay = this.$el.find(".out-of-production-mode-overlay");
                var outOfProductionActions = this.$el.find(".out-of-production-mode-actions");

                if (outOfService == 1) {
                    outOfProductionBtn.addClass("hide");

                    if (!this.options.graph.setProductionModeLoading) {
                        outOfProductionActions.find(".btn-start-production-mode").removeClass("hide");
                        outOfProductionActions.find(".loading-icon").addClass("hide");
                    }

                    outOfProductionOverlay.removeClass("hide");
                    outOfProductionActions.removeClass("hide");
                } else if (outOfService == 0) {
                    if (!this.options.graph.outOfProductionModalShown && this.options.writePermission) {
                        outOfProductionBtn.attr("disabled", false);
                    }
                    outOfProductionBtn.removeClass("hide");
                    outOfProductionOverlay.addClass("hide");
                    outOfProductionActions.addClass("hide");
                } else {
                    outOfProductionBtn.addClass("hide");
                    outOfProductionOverlay.addClass("hide");
                    outOfProductionActions.addClass("hide");
                }

                this.options.graph.setpoint.circleProgress('value', this.model.get("productionItem").get("setpointOEE") / 100.0);

                if (this.model.get("productionItem").get("setpointOEE")) {
                    this.$el.find(".set-point-text").text((this.model.get("productionItem").get("setpointOEE")).toFixed(0) + '%');
                } else {
                    this.$el.find(".set-point-text").text('');
                }

                this.options.graph.oee.circleProgress["oee"].circleProgress('value', this.model.get("productionItem").get("oee") / 100.0);
                this.options.graph.oee.circleProgress["ava"].circleProgress('value', this.model.get("productionItem").get("availability") / 100.0);
                this.options.graph.oee.circleProgress["per"].circleProgress('value', this.model.get("productionItem").get("performance") / 100.0);
                this.options.graph.oee.circleProgress["qua"].circleProgress('value', this.model.get("productionItem").get("quality") / 100.0);

                var targetProductionRate = this.model.get("productionItem").get("targetProductionRate");
                var actualProductionRate = this.model.get("productionItem").get("actualProductionRate"); 
                var halfcircle_opt = {
                    total: (_.isNumber(targetProductionRate)) ? targetProductionRate * 1.25 : null,
                    actual: this.model.get("productionItem").get("actualProductionRate"),
                };

                this.options.graph.halfCircle["productionRate"].opts = halfcircle_opt;

                this.$el.find(".target-production-rate-display").text(_.isNumber(targetProductionRate) ? targetProductionRate : "-");

                if (_.isNumber(halfcircle_opt.actual) && _.isNumber(halfcircle_opt.total)) {
                    this.options.graph.halfCircle["productionRate"].graph.circleProgress('value', this._getPercent(this.model.get("productionItem").get("actualProductionRate"), this.options.graph.halfCircle["productionRate"].opts));
                } else {
                    if (this.options.graph.halfCircle["productionRate"].graph.circleProgress('value') != 0)
                        this.options.graph.halfCircle["productionRate"].graph.circleProgress('value', 0);

                    this.$el.find(".actual-production-rate-display").text(_.isNumber(actualProductionRate) ? actualProductionRate : "-");
                }

                this.$el.find(".actual-production-rate-display").text((_.isNumber(actualProductionRate)) ? actualProductionRate : '-'); 

                var progressbar_target = this.options.graph.productQty.progressbars.target;
                var progressbar_actual = this.options.graph.productQty.progressbars.actual;

                var progressbarset_options = {
                    total: this.model.get("productionItem").get("targetQty") * 1.10,
                    actual: this.model.get("productionItem").get("actualQty"),
                    target: this.model.get("productionItem").get("targetQty"),
                };

                this.options.graph.productQty.progressbars.options = progressbarset_options;

                var badQty = this.model.get("productionItem").get("badQty");
                that.$el.find(".good-qty-display").text((progressbarset_options.actual != null) ? (progressbarset_options.actual > -1) ? progressbarset_options.actual : "Error" : "-");
		        this.$el.find(".bad-qty-display").text((badQty != null) ? (badQty > -1) ? badQty : "Error" : "-");
		        this.$el.find(".target-qty-display").text((progressbarset_options.target != null) ? progressbarset_options.target : "-");

		        var lights = this.$el.find('*[class*="light-icon-"]');
		        lights.find(".lc-indicator")
                    .removeClass("lightcolor-red")
                    .removeClass("lightcolor-yellow")
                    .removeClass("lightcolor-green");

		        if (machineStatus == "RUNNING" && maintenanceStatus) {
		            that.setBlinkingMaintenanceLight(true); 
		        } else {
		            that.setBlinkingMaintenanceLight(false); 
		            lights.each(function (inx, obj) {
		                obj = $(obj);
		                var status = obj.data("lightstatus");
		                var color = obj.data("lightcolor");

		                var sts = machineStatus;
		                if (machineStatus == "STOPPED" && maintenanceStatus) sts = "MAINTENANCE";

		                if (status.toUpperCase() == sts.toUpperCase()) {
		                    obj.find(".lc-indicator").addClass("lightcolor-" + color);
		                }
		            });
		        }

                if (progressbarset_options.target) {
                    progressbar_target.animate(that._getPercent(progressbarset_options.target, progressbarset_options), function () {
                        progressbar_actual.animate(that._getPercent(progressbarset_options.actual, progressbarset_options, 1), function () {
                        });
                    });
                } else {
                    progressbar_actual.animate(0, function () {
                        progressbar_target.animate(0, function () {
                        });
                    });
                }


                this.drawProgressGraph();
                this.refreshEventsGrid();
                this.refreshTimeAccountsGrid();
                this.refreshBatchInfo();


                //Set is loading variables to false here to remove loading poster after all is rendered.
                //Use a setTimeout to prevent issue with timing.
                setTimeout(
                    function () {
                        that.model.set({
                            isLoadingProdData: false,
                            isLoadingDelaysData: false,
                        });
                    },
                    100
                );
            }
            catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack);  }
        }
        , setBlinkingMaintenanceLight: function (v) {
            if (v && !this.options.graph.blinkingLightMaintenance) {
                this.options.graph.blinkingLightMaintenance = true;
                this.blinkMaintenanceLight(true); 
            } else if (!v) {
                this.blinkMaintenanceLight(false); 
            }
        }
        , blinkMaintenanceLight: function (v) {
            var light = this.$el.find('[data-lightstatus="MAINTENANCE"]');
            var lindicator = light.find(".lc-indicator");

            if (v) {
                lindicator.toggleClass('blinkyellow');
                if (this.options.graph.blinkingLightMaintenance) {
                    this.options.graph.blinkingLightTOUT = setTimeout(_.bind(function () { this.blinkMaintenanceLight(true); }, this), 500);
                }
            } else {
                this.options.graph.blinkingLightMaintenance = false;
                if (this.options.graph.blinkingLightTOUT != null) {
                    clearTimeout(this.options.graph.blinkingLightTOUT);
                    this.options.graph.blinkingLightTOUT = null;
                }

                lindicator.removeClass('blinkyellow');
            }
        }
        , setNoBatchOverlay: function (p) {
            var overlayText = this.$el.find(".overlay-text-no-batch"); 
            var overlays = this.$el.find(".overlay");
            var circles = this.$el.find(".main-oee-circle");

            if (p) {
                if (overlays.hasClass("hide")) {
                    overlays.removeClass("hide"); circles.addClass("overlayed-circle"); overlayText.removeClass("hide");
                }
            }
            else { overlays.addClass("hide"); circles.removeClass("overlayed-circle"); overlayText.addClass("hide"); }
        }
        , setMaintenanceModeOverlay: function (p) {
            var overlayText = this.$el.find(".overlay-text-maintenance-mode");
            var overlays = this.$el.find(".overlay");
            var circles = this.$el.find(".main-oee-circle");

            if (p) {
                if (overlays.hasClass("hide")) {
                    overlays.removeClass("hide"); circles.addClass("overlayed-circle"); overlayText.removeClass("hide");
                }
            }
            else { overlays.addClass("hide"); circles.removeClass("overlayed-circle"); overlayText.addClass("hide"); }
        }
        , refresh: function (a) {
            try {
                var that = this;
                var fromURL = false; 
                var id = (a && a.id) ? a.id : null; 
                fromURL = (id != null); 

                if (a) {
                    this.model.set({
                        isLoadingProdData: (a.refreshData),
                        isLoadingDelaysData: (a.refreshDelaysData),
                    });

                    //this.$el.find(".overlay-group-data").addClass("hide-overlay");
                    //this.$el.find(".overlay-group-loading").removeClass("hide-overlay");
                }
                

                var cacheRefresh = {
                    pitem: (a && _.isBoolean(a.refreshCachePItem)) ? a.refreshCachePItem : false,
                    delays: (a && _.isBoolean(a.refreshCacheDelays)) ? a.refreshCacheDelays : false,
                }; 

                if (id) this.model.set("productionItemId", id);
                else id = this.model.get("productionItemId"); 

                if (fromURL) {
                    this.$el.find(".in-maintenance-switch").addClass('not-visible');
                }

                var after_callback = _.after(2, _.bind(this.preExecRender, this));
                this.getCurrentShiftScope(
                    {
                        id: id,
                    },
                    function (data) {
                        that.model.set({
                            start: new moment(data.start),
                            end: new moment(data.end),
                        });

                        that.model.get("productionItem").fetch({
                            id: that.model.get("productionItemId"),
                            from: that.model.get("start").format("YYYY-MM-DD HH:mm:ss"),
                            to: that.model.get("end").format("YYYY-MM-DD HH:mm:ss"),
                            callback: function (ref, status, msg) {
                                try {
                                    if (status == "SUCCESS") {
                                        after_callback.call();

                                        if (!that.options.MYREFERENCES.autoRefresh.enabled) {
                                            that.$el.find(".invalid-machine-poster").remove();
                                            that.$el.find(".dashboard-container").show();
                                            that.$el.find(".loading-poster").show();
                                            that.$el.find(".dashboard-container").show();

                                            that._startAutoRefresh(); 
                                        }
                                    } else {
                                        if (msg && msg == "INVALID_PRODUCTION_ITEM_ID") {
                                            app.models.appController.set("menuChanged", true);
                                        }

                                        that.$el.find(".loading-poster").hide();
                                        that.$el.find(".dashboard-container").hide();

                                        that.$el.find(".invalid-machine-poster").remove();
                                        //that.$el.append('<div class="invalid-machine-poster" style="text-align:center;margin-top:10px;font-size:24px;"><i class="fa fa-5x fa-exclamation-circle"></i><br /><br />' + app.translate(that, "invalid_production_item_label") + '<br /><br /><a style="font-size:20px;" href="!/oeeOverview">' + app.translate(that, "back_to_overview_label") + '</a></div>');
                                        that.$el.append('<div class="invalid-machine-poster" style="text-align:center;margin-top:10px;font-size:24px;"><i class="fa fa-5x fa-exclamation-circle"></i><br /><br />' + app.translate(that, "invalid_production_item_label") + '</div>');

                                        that._stopAutoRefresh(); 
                                    }
                                }
                                catch (e) {
                                    console.error((e.stack) ? e.stack : new Error(e).stack);

                                    that.model.set('isLoadingProdData', false);

                                    //that.$el.find(".overlay-group-data").removeClass("hide-overlay");
                                    //that.$el.find(".overlay-group-loading").addClass("hide-overlay");
                                }
                            },
                            cache: {
                                enabled: true,
                                refresh: cacheRefresh.pitem, 
                            }
                        });

                        that.model.get("delaysInformation").fetch({
                            id: that.model.get("productionItemId"),
                            from: that.model.get("start").format("YYYY-MM-DD HH:mm:ss"),
                            to: that.model.get("end").format("YYYY-MM-DD HH:mm:ss"),
                            callback: after_callback,
                            cache: {
                                enabled: true,
                                refresh: cacheRefresh.delays, 
                            }
                        });
                    }
                );

            }
            catch (e) {
                console.error((e.stack) ? e.stack : new Error(e).stack);

                try {
                    this.model.set('isLoadingProdData', false);

                    //this.$el.find(".overlay-group-data").removeClass("hide-overlay");
                    //this.$el.find(".overlay-group-loading").addClass("hide-overlay");
                }
                catch (e2) { console.error((e2.stack) ? e2.stack : new Error(e2).stack); }
            }
        }
        , getCurrentShiftScope: function (opt, callback) {
            var that = this; 
            var options = _.extend({
                id: null, 
            }, opt); 
            
            var ret = {
                start: null, 
                end: null, 
            }; 

            var QP = new Core.Database.QueryParameters();
            QP.Add("MachineProductionItemId", "INT", options.id); 

            Core.Json.CallProcedure(app.DatabaseNames.OEE + ".WebApp.GetCurrentShiftStartEnd", QP, {
                onSuccess: function (data) {
                    if (data && data.Table && data.Table[0]) {
                        var data = data.Table[0]; 
                        _.extend(ret, {
                            start: data.From,
                            end: data.To, 
                        }); 
                    }

                    if (_.isFunction(callback)) callback.call(that, ret); 
                },
                onError: function(data){
                    if (_.isFunction(callback)) callback.call(that, ret);
                }, 
                Async: true,
                Secured: true,
                Cache: true, 
            }, app.ConnectionStrings.app);
        }

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

            for (var pn in opt.params)
                qp.Add(pn, 'VARCHAR', opt.params[pn]);

            Core.Json.CallProcedure(
                app.DatabaseNames.OEE + '.WebApp.AddManualBadProduction',
                qp,
                {
                    onSuccess: function (resp) {
                        try {
                            if ((!resp) || (!resp.Message)) {
                                if (opt.success != null && _.isFunction(opt.success))
                                    opt.success(resp);
                            }
                            else {
                                var errorMsg = (resp.Message)
                                                    ? resp.Message
                                                    : 'TRANSACTION_ERROR';

                                console.error(new Error(errorMsg).stack);

                                if (opt.error != null && _.isFunction(opt.error))
                                    opt.error(errorMsg);
                            }
                        }
                        catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                    },
                    onError: function (errorMsg) {
                        try {
                            console.error(new Error(errorMsg).stack);

                            if (opt.error != null && _.isFunction(opt.error))
                                opt.error(errorMsg);
                        }
                        catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                    },
                    Async: opt.async,
                    Secured: true,
                },
                app.ConnectionStrings.app
            );
        }
        , showAddBadManualModal: function () {
            try {
                var that = this,
                    productionItem = this.model.get('productionItem').toJSON(),
                    customPath = app.oeeModule.screensPath + "oee/",
                    clickedBtn = this.$el.find('.btn-bad-qty-add-manual');

                //Disable clicked button to prevent issues with multiple clicks.
                clickedBtn.attr("disabled", true);

                var fn_showModal = function () {
                    var modal = new OEE.Views.AddBadManualModal({
                        model: new OEE.Models.AddBadManualModal({
                            batchId: productionItem.batchId,
                            productionItemId: productionItem.id,
                            unitsLegend: productionItem.badUnitProducedManualUnits,
                            currentQuantity: productionItem.badQty,
                        }),
                        parent: that,
                        template: that.templatesLoaded.addBadManualModal,
                    });

                    var fn_modal_shown_hidden = function () {
                        try {
                            //Enable button in both events, shown and hidden, just to prevent issues if one of them fails
                            //or it is not executed for some reason.
                            clickedBtn.attr("disabled", false);
                        }
                        catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
                    };

                    that.listenToOnce(modal, 'shown', fn_modal_shown_hidden)
                        .listenToOnce(modal, 'hidden', fn_modal_shown_hidden)
                        .listenToOnce(modal, 'finish', function (modal, data) {
                            try {
                                if (data.result == 'SUCCESS') {
                                    that._autoRefresh({
                                        refreshCacheDelays: true,
                                        refreshCachePItem: true,
                                        refreshData: true,
                                    });
                                    //that.refreshGraph();
                                }
                            }
                            catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
                        });

                    modal.show();
                };

                if (!this.templatesLoaded.addBadManualModal) {
                    T.render.call(this, this.template, function (tmp) {
                        that.templatesLoaded.addBadManualModal = tmp;
                        fn_showModal();
                    }, customPath, "add_bad_manual_modal_template");
                } else {
                    fn_showModal()
                    //setTimeout(function () {
                    //    T.render.call(that, that.template, function (tmp) {
                    //        if (!that.templatesLoaded.addBadManualModal) {
                    //            fn_showModal(tmp);
                    //        }
                    //    }, customPath, "add_bad_manual_modal_template");
                    //}, 500);
                }
            }
            catch (e) {
                console.error((e.stack) ? e.stack : new Error(e).stack);

                try {
                    clickedBtn.attr("disabled", false);
                }
                catch (e2) { console.error((e2.stack) ? e2.stack : new Error(e2).stack); }
            }
        }

        , openBatch_click: function (e) {
            try {
                this.openBatch();
            }
            catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
        }
        , closeBatchBtn_click: function (e) {
            try {
                this.showCloseBatchModal();
            }
            catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
        }
        , badQtyAddManualBtn_click: function (e) {
            try {
                this.showAddBadManualModal();
            }
            catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
        }
        , inMaintenanceToggleCbx_change: function (e) {
            try {
                this.updateMaintenanceMode();
            }
            catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
        }

        , 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) {
                //appending view to the main container and set state to shown

                this.options.state = app.view_states.shown;
                container.append(el);

                this.options.onappend(this);
            }

            if (this.options.state == app.view_states.hidden) {
                //append and remain hidden
                container.append(el);
            }

            if (this.options.state == app.view_states.closed) {
                //return without appending.
                return;
            }
        }

        , bindEvents: function () {
            //this function should be in every view that uses listenTo anywhere
            //all the model bindings or view-model binding should be here, to manage
            //the show/hide view easily
            this.listenTo(this.model.get("productionItem"), "change", this.preExecRender);
        }

        , bindViewScopedEvents: function () {
            var that = this; 
        }

        , unbindViewScopedEvents: function () {
            this._stopAutoRefresh(); 
        }

        , close: function () {
            this.options.state = app.view_states.closed;

            this.closeSubviews(); 
            this.remove();
            this.unbindViewScopedEvents();
            this.unbind();
        }

        , closeSubviews: function () {
            _.each(this.options.MYREFERENCES.subviews, function (sview) {
                sview.close();
            });
        }

        , show: function () {
            this.options.state = app.view_states.shown;

            this.showSubviews(); 
            this.bindEvents();
            this.$el.show();
        }

        , showSubviews: function () {
            _.each(this.options.MYREFERENCES.subviews, function (sview) {
                sview.show();
            });
        }

        , hide: function () {
            this.options.state = app.view_states.hidden;

            this.hideSubviews();

            this.$el.hide();
            this.unbind();
            this.stopListening();
        }

        , hideSubviews: function () {
            _.each(this.options.MYREFERENCES.subviews, function (sview) {
                sview.hide();
            });
        }

        , preRender: function () {
            app.models.subnavbar.set("subnavbar", false);
        }

        , reRender: function () {
            this.refresh(); 
        }
    });

    OEE.Models.BatchInformation = Backbone.Model.extend({
        defaults: {
            id: null,
            productionItem: null,
            status: null,
            number: null,
            product: null,
            quantity: null,
            start: null, 
            shift: null, 
        }, 
    });

    OEE.Collections.BatchesInformation = Backbone.Collection.extend({
        model: OEE.Models.BatchInformation,
        fetch: function (opt) {
            var options = {
                method: "set",
                async: true,
                callback: null,
                data: {
                    id: null, 
                }
            };

            options = _.extend(options, (opt) ? opt : {});

            var that = this,
                QP = new Core.Database.QueryParameters();

            QP.Add("ProductionItemId", "INT", options.data.id);
            QP.Add("Timezone", "VARCHAR", app.models.user.get("timezoneCode")); 

            var items = [];

            Core.Json.CallProcedure(app.DatabaseNames.OEE + ".WebApp.GetAvailableBatches", QP, {
                onSuccess: function (data) {
                    if (Core.Object.Eval(data, "Table")) {
                        var itemsData = data.Table;
                        
                        items = _.map(itemsData, function (d) {
                            return {
                                id: d.Id,
                                productionItem: d.ProductionItem,
                                status: d.Status,
                                number: d.Number,
                                product: d.Product,
                                quantity: d.Quantity,
                                start: d.Start,
                                shift: d.Shift,
                            }; 
                        });
                    }

                    that[options.method](items, { from: "fetch" });

                    if (options.callback != null && _.isFunction(options.callback))
                        options.callback.call(this, that);
                },
                Async: options.async,
                Secured: true,
            }, app.ConnectionStrings.app);

            return this;
        },
    }); 

    OEE.Models.OpenBatchModal = Backbone.Epoxy.Model.extend({
        defaults: {
            batches: null,
            promptingConfirmation: false,
        },
        initialize: function () {
            this.attributes.batches = new OEE.Collections.BatchesInformation(); 
        }, 
    }); 

    OEE.Views.OpenBatchModal = Backbone.Epoxy.View.extend({
        template: "oee"
        , id: "oee"
        , title: ""
        , className: "modal hide fade modal-wider"
        //default not cacheable, change this if you want the view to be cacheable
        // if the view is set as cacheable should also have a refresh method to reset the view without erasing the DOM.
        , isCacheable: true
        , initialize: function () {

            this.options.MYREFERENCES = {
                autoRefresh: {
                    enabled: true
                    , toid: null
                },
                rowData: null,
            };

            this.options.grids = {
                batches: null, 
            }; 

            this.options.rowData = _.extend({
                productionItemId: null, 
            }, this.options.rowData);

            this.model = new OEE.Models.OpenBatchModal(); 

            this.options._isRendered = false;

            this.bindEvents();
            //_.bindAll(this);
        },
        events: {
            'click .close, .btn-close': function (event) {
                event.preventDefault();

                this.trigger('cancel');

                if (this.options.content && this.options.content.trigger) {
                    this.options.content.trigger('cancel', this);
                }
            },
        },

        render: function (container) {
            var that = this;
            var thatContainer = container;

            //the screens have a custompath, so it has to be specified in the customPath variable that is
            //then sent to the template loader.
            var customPath = app.oeeModule.screensPath + "oee/";

            if (this.options.loaded) {
                T.render.call(this, this.template, function (tmp) {
                    if (!that.options.i18n) that.options.i18n = {};
                    app.getI18NJed(that, that.template, function (i18nJED) {
                        //storing internationalization data
                        that.options.i18n[that.template] = i18nJED;

                        //loading the view and appeding it to the views's $el.
                        that.$el.html(tmp());
                        that.applyBindings();

                        var columns = [
                            {
                                name: "productionItem",
                                label: app.translate(that, "productionItem_label"),
                                editable: false,
                                sortable: false,
                                cell: Backgrid.StringCell.extend({
                                    orderSeparator: '',
                                    className: "string-cell align-center-cell productionItem",
                                })
                            },
                            {
                                name: "number",
                                label: app.translate(that, "number_label"),
                                editable: false,
                                sortable: false,
                                cell: Backgrid.StringCell.extend({
                                    className: "string-cell align-center-cell number"
                                })
                            },
                            {
                                name: "product",
                                label: app.translate(that, "product_label"),
                                editable: false,
                                sortable: false,
                                cell: Backgrid.StringCell.extend({
                                    className: "string-cell align-center-cell description"
                                }),
                            },
                            {
                                name: "quantity",
                                label: app.translate(that, "quantity_label"),
                                editable: false,
                                sortable: false,
                                cell: Backgrid.StringCell.extend({
                                    className: "string-cell align-center-cell quantity"
                                }),
                            },
                            {
                                name: "start",
                                label: app.translate(that, "start_label"),
                                editable: false,
                                sortable: false,
                                cell: Backgrid.StringCell.extend({
                                    className: "string-cell align-center-cell shift"
                                }),
                            },
                            //{
                            //    name: "shift",
                            //    label: app.translate(that, "shift_label"),
                            //    editable: false,
                            //    sortable: false,
                            //    cell: Backgrid.StringCell.extend({
                            //        className: "string-cell align-center-cell shift"
                            //    }),
                            //},
                        ];

                        var CustomRow = Backgrid.Row.extend({
                            events: {
                                "click": "onClick"
                            },
                            className: "clickeable-row",
                            onClick: _.debounce(function (e) {
                                var target = $(e.target);
                                that.batchClicked(this.model);
                            }, 500, true)
                        });

                        var grid = that.options.grids.batches = new Backgrid.Grid({
                            row: CustomRow,
                            className: "backgrid table table-hover",
                            columns: columns,
                            collection: that.model.get("batches"),
                        });


                        that.checkData(); 
                        // Render the grid and attach the root to your HTML document
                        that.$el.find(".batches-table").append(grid.render().el);

                    }, true, customPath);

                }, customPath, "open_batch_modal_template");
            } else {
                setTimeout(function () {
                    T.render.call(that, that.template, function (tmp) {
                        if (!that.options.loaded) {
                            that.$el.html(tmp());
                        }
                    }, customPath, "open_batch_loading_modal_template");
                }, 500);
            }

            that.options._isRendered = true;

            if (!this.options.loaded)
                this.refresh();
        }
        , checkData: function () {
            if (this.model.get("batches").length > 0) {
                this.$el.find(".batches-section").removeClass("hide");
                this.$el.find(".no-batches-section").addClass("hide");
            } else {
                this.$el.find(".batches-section").addClass("hide");
                this.$el.find(".no-batches-section").removeClass("hide");
            }
        }
        , batchClicked: function (model) {
            try {
                var that = this;

                this.model.set('promptingConfirmation', true);

                var modal = new Modal.Views.Main({
                    id: "open-batch-confirmation-modal"
                    , title: app.translate(that, "open_batch_confirmation_title")
                    , content: app.translate(that, "open_batch_confirmation_body", [model.get("number"), model.get("product")])
                    , allowCancel: true
                    , buttons_type: "CUSTOM-BUTTONS"
                    , custom_buttons: [
                        { id: 'continue', classNames: 'btn-large btn-primary', btn_lbl: app.translate(that, 'continue_btn_label') },
                        { id: 'cancel', classNames: 'btn-large', btn_lbl: app.translate(that, 'cancel_btn_label') },
                    ]
                    , okCloses: true
                });

                function open_modal_continue() {
                    var customPath = app.oeeModule.screensPath + "oee/";

                    //show opening template
                    T.render.call(
                        that,
                        that.template,
                        function (tmp) {
                            try {
                                that.$el.html(tmp());
                            }
                            finally {
                                that.model.set('promptingConfirmation', false);
                            }
                        },
                        customPath,
                        "open_batch_opening_modal_template"
                    );

                    var QP = new QueryParameters();
                    QP.Add("BatchId", "INT", model.get("id"));
                    QP.Add("MachineProductionItemId", "INT", that.options.rowData.productionItemId);

                    app.CallProcedure(app.DatabaseNames.OEE + ".WebApp.OpenBatch", QP, {
                        onSuccess: function (data) {
                            that.hide();

                            //hiding both buttons while it refreshes
                            that.$el.find('.btn-close-batch').addClass('hide');
                            that.$el.find('.btn-open-batch').addClass('hide');

                            that.refresh();

                            that.trigger('success', that);
                        },
                        Secured: true,
                    }, app.ConnectionStrings.app);
                }

                modal.on("shown", function () {
                });

                modal.on("customButtonClicked", function (e) {
                    e.modal.hide();
                    if (e.buttonid == "continue")
                        open_modal_continue();
                    else
                        that.model.set('promptingConfirmation', false);
                });

                modal.on("cancel", function () {
                    try {
                        that.model.set('promptingConfirmation', false);
                    }
                    catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
                });

                modal.show();
            }
            catch (e) {
                console.error((e.stack) ? e.stack : new Error(e).stack);

                this.model.set('promptingConfirmation', false);
            }
        }

        , refresh: function () {
            try {
                var that = this; 
                this.model.get("batches").fetch({
                    data: {
                        id: this.options.rowData.productionItemId, 
                    },
                    callback: function () {
                        that.options.loaded = true;
                        that.render();
                    }, 
                }); 
            }
            catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
        }

        , show: function () {

            if (!this.options._isRendered) {
                this.render();
                setTimeout(_.bind(this.show, this), 100);
                return;
            }

            var self = this,
            $el = this.$el;

            //creating modal
            $el.modal({
                keyboard: false,
                backdrop: 'static',
            });

            //-----------------------------------------------------------------------
            // Comment out this code because on this screen all modals 
            // do not hide when backdrop clicked or escape key pressed.
            //-----------------------------------------------------------------------
            //$backdrop = $('.modal-backdrop');

            //$backdrop.one('click', function () {
            //    if (self.options.content && self.options.content.trigger) {
            //        self.options.content.trigger('cancel', self);
            //    }

            //    self.trigger('cancel');
            //});

            //$(document).one('keyup.dismiss.modal', function (e) {
            //    e.which == 27 && self.trigger('cancel');

            //    if (self.options.content && self.options.content.trigger) {
            //        e.which == 27 && self.options.content.trigger('shown', self);
            //    }
            //});
            //-----------------------------------------------------------------------

            this.on('cancel', function () {
                self.hide();
            });

            return this;

        }
        , hide: function () {
            var self = this,
            $el = this.$el;

            $el.one('hidden', function onHidden(e) {
                // Ignore events propagated from interior objects, like bootstrap tooltips
                if (e.target !== e.currentTarget) {
                    return $el.one('hidden', onHidden);
                }

                if (self.options.content && self.options.content.trigger) {
                    self.options.content.trigger('hidden', self);
                }

                self.trigger('hidden');

                self.close();
            });

            $el.modal('hide');
        }

        , bindEvents: function () {
            //this function should be in every view that uses listenTo anywhere
            //all the model bindings or view-model binding should be here, to manage
            //the show/hide view easily

            //rendering as soon as getting data in the model
        }

        , close: function () {
            this.remove();
            this.unbind();
        }

        , preRender: function () {
        }

        , reRender: function () {
        }
    });

    OEE.Models.AddBadManualModal = Backbone.Epoxy.Model.extend({
        defaults: {
            batchId: null,
            currentQuantity: null,
            productionItemId: null,
            productionQty: null,
            unitsLegend: null,

            //errorMsg: "",
            processing: false,
        },
    });

    OEE.Views.AddBadManualModal = Modal.Views.SimpleModal.extend({
        className: Modal.Views.SimpleModal.prototype.className + ' add-bad-manual-modal',
        events: function () {
            return _.extend(Modal.Views.SimpleModal.prototype.events.apply(this, arguments), {
                'keypress #productionQtyTbx': this.productionQtyTbx_keyPress,
            });
        },

        initialize: function (options) {
            _.extend(this, options);

            if (!this.model)
                this.model = new Screen.Models.AddBadManualModal();


            this.bindEvents();

            return this;
        },

        save: function () {
            var that = this,
                attrs = this.model.toJSON();

            if (attrs.processing == false) {
                this.model.set('processing', true);

                try
                {
                    //Comment out early hide call because break the current workflow of loading posters.
                    ////Hide modal without wait for transaction result.
                    //this.hide();

                    this.parent.addManualBadProduction({
                        params: {
                            batchId: attrs.batchId,
                            productionItemId: attrs.productionItemId,
                            productionQty: attrs.productionQty,
                        },
                        success: function (id) {
                            try {
                                that.model.set('processing', false);

                                that.finish({ result: 'SUCCESS', });

                                that.hide();
                                //setTimeout(_.bind(that.hide, that), 1500);
                            }
                            catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
                        },
                        error: function (errorMsg) {
                            try {
                                app.views.topMessages.showMessage(app.translate([that.parent, app], errorMsg), { stay: 5 * 1000, });
                            }
                            catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
                            finally {
                                that.model.set('processing', false);
                            }
                        },
                    });
                }
                catch (e) {
                    this.model.set('processing', false);
                    throw e;
                }
            }
        },
        show: function () {
            if (!this.isRendered) {
                this.render();
            }
            else {
                this.$el.modal({
                    keyboard: false,
                    backdrop: 'static',
                });
            }

            return this;
        },

        acceptBtn_click: function (e) {
            try {
                this.save();
        }
        catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
        },
        productionQtyTbx_keyPress: function (e) {
            try {
                if ((e.which == 13) && (e.shiftKey == false)) {
                    //Validate values
                    if (parseInt(this.model.get('productionQty'), 10) > 0)
                        this.save();

                    e.preventDefault();
                }
            }
            catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
        },
    });

    OEE.Models.CloseBatchModal = Backbone.Epoxy.Model.extend({
        defaults: {
            badManualEnabled: null,
            batchId: null,
            batchNumber: null,
            batchProduct: null,
            comments: null,
            currentQuantity: null,
            productionItemId: null,
            productionQty: null,
            unitsLegend: null,

            //errorMsg: "",
            processing: false,
        },
    });

    OEE.Views.CloseBatchModal = Modal.Views.SimpleModal.extend({
        className: Modal.Views.SimpleModal.prototype.className + ' close-batch-modal',
        events: function () {
            return _.extend(Modal.Views.SimpleModal.prototype.events.apply(this, arguments), {
                'keypress #productionQtyTbx': this.productionQtyTbx_keyPress,
            });
        },

        initialize: function (options) {
            _.extend(this, options);

            if (!this.model)
                this.model = new Screen.Models.CloseBatchModal();


            this.bindEvents();

            return this;
        },

        save: function () {
            var that = this,
                attrs = this.model.toJSON();

            if (attrs.processing == false) {
                this.model.set('processing', true);

                try {
                    this.parent.closeBatch({
                        params: {
                            id: attrs.batchId,
                            machineProductionItemId: attrs.productionItemId,
                            badManualProductionQuantity: attrs.productionQty,
                            comments: attrs.comments,
                        },
                        success: function (id) {
                            try {
                                //that.model.set('processing', false);

                                that.finish({ result: 'SUCCESS', });

                                that.hide();
                            }
                            catch (e) {
                                that.model.set('processing', false);

                                console.error((e.stack) ? e.stack : new Error(e).stack);
                            }
                        },
                        error: function (errorMsg) {
                            try {
                                //app.views.topMessages.showMessage(app.translate([that.parent, app], errorMsg), { stay: 5 * 1000, });
                                app.views.topMessages.showMessage(app.translate([that.parent, app], 'ERROR_TRY_AGAIN'), { stay: 5 * 1000, });
                            }
                            catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
                            finally {
                                that.model.set('processing', false);
                            }
                        },
                    });
                }
                catch (e) {
                    this.model.set('processing', false);
                    throw e;
                }
            }
        },
        show: function () {
            if (!this.isRendered) {
                this.render();
            }
            else {
                this.$el.modal({
                    keyboard: false,
                    backdrop: 'static',
                });
            }

            return this;
        },

        acceptBtn_click: function (e) {
            try {
                this.save();
            }
            catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
        },
        productionQtyTbx_keyPress: function (e) {
            try {
                if ((e.which == 13) && (e.shiftKey == false)) {
                    //Validate values
                    if (parseInt(this.model.get('productionQty'), 10) >= 0)
                        this.save();

                    e.preventDefault();
                }
            }
            catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
        },
    });

    OEE.Views.DelayInformationModal = delayInformationModal.Views.DelayInformationModal.extend({
        show: function () {

            if (!this.options._isRendered) {
                this.render();
                setTimeout(this.show, 100);
                return;
            }

            var self = this,
            $el = this.$el;

            //creating modal
            $el.modal({
                keyboard: false
                , backdrop: "static"
            });


            //-----------------------------------------------------------------------
            // Comment out this code because on this screen all modals 
            // do not hide when backdrop clicked or escape key pressed.
            //-----------------------------------------------------------------------
            //$backdrop = $('.modal-backdrop');

            //$backdrop.one('click', function () {
            //    if (self.options.content && self.options.content.trigger) {
            //        self.options.content.trigger('cancel', self);
            //    }

            //    self.trigger('cancel');
            //});

            //$(document).one('keyup.dismiss.modal', function (e) {
            //    e.which == 27 && self.trigger('cancel');

            //    if (self.options.content && self.options.content.trigger) {
            //        e.which == 27 && self.options.content.trigger('shown', self);
            //    }
            //});
            //-----------------------------------------------------------------------

            this.on('cancel', function () {
                self.hide();
            });

            return this;

        }
    });

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

});

