//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",
  "backgrid",
  "modules/modal2",

  "js/codemirror-5.22.0/lib/codemirror", 
  "moment",
  "modules/dmt-script",

  //others
  "backgrid/moment-cell",
  "js/codemirror-5.22.0/mode/clike/clike",
  'js/codemirror-5.22.0/addon/hint/show-hint',
  'js/FileSaver',
],

function (app, T, Backgrid, Modal, CodeMirror, moment, Script) {

    //replace all "Screen" with your view's name.
    var Screen = {
        Models: {},
        Views: {},
        Collections: {},
        Config: {
            intellisense: [
                "Api4i.Timeout",
                "Api4i.Timescan",
                "Api4i.TimestampUtc",
                "Api4i.Debug(message)",
                "Api4i.Debug(message, exception)",
                "Api4i.DebugFormat(format, args)",
                "Api4i.DebugFormat(provider, format, args)",
                "Api4i.Error(message)",
                "Api4i.Error(message, exception)",
                "Api4i.ErrorFormat(format, args)",
                "Api4i.ErrorFormat(provider, format, args)",
                "Api4i.Fatal(message)",
                "Api4i.Fatal(message, exception)",
                "Api4i.FatalFormat(format, args)",
                "Api4i.FatalFormat(provider, format, args)",
                "Api4i.Info(message)",
                "Api4i.Info(message, exception)",
                "Api4i.InfoFormat(format, args)",
                "Api4i.InfoFormat(provider, format, args)",
                "Api4i.Warn(message)",
                "Api4i.Warn(message, exception)",
                "Api4i.WarnFormat(format, args)",
                "Api4i.WarnFormat(provider, format, args)",
                "Api4i.ReadTagDigital(tagName)",
                "Api4i.ReadTagReal(tagName)",
                "Api4i.WriteTagDigitalWithContextTimestamp(tagName, value)",
                "Api4i.WriteTagDigitalWithCurrentTimestamp(tagName, value)",
                "Api4i.WriteTagRealWithContextTimestamp(tagName, value)",
                "Api4i.WriteTagRealWithCurrentTimestamp(tagName, value)",
            ],
        },
    };

    Screen.Models.Main = Backbone.Epoxy.Model.extend({
        defaults: {
            id: null,
            name: null,
            //agentId: null,

            currentLock: null,
            currentTab: 'source',
            sourceFilesBinding: [],
            onlineFilesBinding: [],
            enqueuedCommand: false,
            fullLocked: false,
            onlineLocked: false,

            isLoading: true,
            saving: false,
            downloading: false,
        },
    });

    //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.
    Screen.generateID = function (viewParams) {
        try {
            //if the viewparams change the view id, then evaluate the viewparams here
            //and return the appropiate id.
            return "dmt/dmt-editor" + (viewParams && viewParams.id) ? "-" + viewParams.id.toString() : "";
        } catch (Error) { }
    };

    Screen.Views.Main = Backbone.Epoxy.View.extend({
        template: "dmt-editor",
        id: "dmt-editor",
        title: "Logic Builder",
        //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,
        askBeforeCloseOpt: {
            title: null,
            message: null,
        },
        beforeClose: function () {
            if (!this.askBeforeCloseOpt.title || !this.askBeforeCloseOpt.message) {
                this.askBeforeCloseOpt.title = this.options.i18n[this.template].translate("unsaved_changes_modal_title").fetch();
                this.askBeforeCloseOpt.message = this.options.i18n[this.template].translate("unsaved_changes_modal_message").fetch();
            }

            var modified = this.source.get("modified");
            if (modified) return false;
            else return true; 
        },
        events: function () {
            return {
                //'click #newBtn': this.newBtn_click,
                'click #browseBtn': this.browseBtn_click,
                'click #saveBtn': this.saveBtn_click,
                'click #saveAsBtn': this.saveAsBtn_click,
                'click #uploadBtn': this.uploadBtn_click,

                'click #runBtn': this.runBtn_click,
                'click #pauseBtn': this.pauseBtn_click,
                'click #stopBtn': this.stopBtn_click,
                'click #downloadSourceBtn': this.downloadSourceBtn_click,
                'click #downloadOnlineBtn': this.downloadOnlineBtn_click,

                'click .files-input': this.filesInput_click,
                'change .files-input': this.filesInput_changed,
                'click .btn-get-logs': this.getLogsBtn_click,
                'click .btn-upload-file': this.uploadFileBtn_click,
                'click .btn-cancel-upload-file': this.cancelUploadFileBtn_click,
                'click .btn-remove-file': this.removeFileBtn_click,
                'change .files-select': this.filesSelect_changed,
                'click .messages-container': this.messagesContainer_click,
            };
        },

        autoRefresh: null,
        bindingSources: null,

        source: null,
        sourceFiles: null,
        onlineFiles: null,
        listeners: null,

        messagesView: null,
        messagesColl: null,
        firstEditorValue: false,

        templates: null,

        editor: null,
        onlineCode: null,
        currentModal: null,

        initialize: function () {
            this.options.state = app.view_states.loading;
            this.options.onappend = (_.isFunction(this.options.onappend)) ? this.options.onappend : function () { };

            this.messagesColl = new Screen.Collections.Messages();

            //Ask for null values previous to set view objects with default values
            //due to, maybe, they are already set by who instantiate this view.
            if (this.autoRefresh == null) {
                this.autoRefresh = {
                    enabled: null
                    , toid: null
                    , every: 5 * 1000
                };
            }

            this.templates = {
                browseModal: null,
            };

            if (this.model == null)
                this.model = new Screen.Models.Main();

            if (this.source == null)
                this.source = new Screen.Models.Source();

            if (this.sourceFiles == null)
                this.sourceFiles = new Screen.Collections.SourceFiles();

            if (this.onlineFiles == null)
                this.onlineFiles = new Screen.Collections.SourceFiles();

            this.bindingSources = {
                //sourceFiles: this.sourceFiles,
                source: this.source,
                messages: this.messagesColl,
            };

            this.listeners = {};

            this.bindEvents();
        },

        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, "change:fullscreen", this.checkFrameZoom)
            //    .listenTo(this.model, "change:fullscreen", this.checkPreviewDraggable)
            this.listenTo(this.source, "fetch", this.source_fetch)
                .listenTo(this.source, "fetch-online", this.source_fetch_online)
                .listenTo(this.source, "change:scriptName", this.source_change_scriptName);
            //this.listenTo(this.model, "change:fullscreen", this.editorOnFrameExpandCollapse);
            //$(window).on('beforeunload', _.bind(this.window_beforeUnload, this));
        },        
        fitToHeight: function () {
            var messageContainerHeight = ($(window).height() * 0.15);
            var onlineDataHeight = this.$el.find(".online-data").outerHeight(true);
            var sidebar = this.$el.find(".screen-sidebar");
            var margin = 130;
            $(this.$el.find(".messages-container")).height(messageContainerHeight);
            sidebar.height($(window).height() - 80);
            this.$el.find(".resize-height-to-fit").height($(window).height() - messageContainerHeight - margin);
            this.$el.find(".resize-height-source-files").height($(window).height() * 0.45);
        },
        instanceCodeMirror: function () {
            var that = this;

            // Applies automatic mode-aware indentation to the specified range
            CodeMirror.defineExtension("autoIndentRange", function (from, to) {
                var cmInstance = this;
                this.operation(function () {
                    for (var i = from.line; i <= to.line; i++) {
                        cmInstance.indentLine(i, "smart");
                    }
                });
            });

            this.editor = CodeMirror(
                that.$el.find(".editor-container")[0],
                {
                    lineNumbers: true,
                    matchBrackets: true,
                    mode: "text/x-csharp",
                    extraKeys: { "Ctrl-Space": "autocomplete" },
                    //viewportMargin: Infinity, 
                }
            );

            this.onlineCode = CodeMirror(
                that.$el.find(".online-container")[0],
                {
                    lineNumbers: true,
                    readOnly: true,
                    matchBrackets: true,
                    mode: "text/x-csharp"
                    //viewportMargin: Infinity, 
                }
            );

            //Set online code background gray to denote that is disabled
            this.onlineCode.display.wrapper.style.backgroundColor = "#ddd";

            CodeMirror.commands.save = _.debounce(_.bind(this.editor_commands_save, this), 500);

            this.editor.on("changes", _.debounce(_.bind(that.editorChanged, that), 500));

            //this.enableEditorScreen(true); 
        },
        _refresh: function (opt) {
            //console.log('autorefresh: ' + new Date().toString()); 
            if (this.autoRefresh.toid != null) {
                clearTimeout(this.autoRefresh.toid);
                this.autoRefresh.toid = null;
            }

            var attrs = this.model.toJSON();

            //var searchFilter = this.subviews.subnavbarControls.model.get("search");

            this.refreshSource(_.extend(
                {},
                opt,
                {
                    params: {
                        id: attrs.id,
                    }
                })
            );

            if (this.autoRefresh.enabled == true) {
                var that = this;

                this.autoRefresh.toid = setTimeout(
                    function () { that._refresh(); },
                    this.autoRefresh.every
                );
            }
        },
        refresh: function (viewParams) {
            //If currentModal exists, hide it and erase it.
            if (this.currentModal) {
                this.currentModal.hide();
                this.currentModal = null;
            }

            if (viewParams.id)
                viewParams.id = parseInt(viewParams.id, 10);
            else
                viewParams.id = Screen.Models.Main.prototype.defaults.id;

            this.model.set({
                id: viewParams.id,
            });

            //Call first refresh.
            var params = this.model.toJSON();

            this._refresh(_.extend(
                {
                },
                params,
                {
                    onlineRefresh: false,
                    reset: true,
                    open: true, //will call open instead of fetch
                    params: _.extend(
                        {},
                        params,
                        {
                            id: params.id,
                        }
                    ),
                }
            ));
        },
        refreshSource: function (options) {
            try {
                var that = this,
                    modelAttrs = this.model.toJSON(),
                    opt = _.extend({}, {
                        onlineRefresh: true,
                        params: {},
                        refresh: true,
                    }, options);

                if (opt.reset == true) {
                    this.model.set({
                        isLoading: true,
                    });
                }

                if (opt.open) {
                    this.source.open(opt);
                } else {
                    this.source.fetch(opt);
                }
            }
            catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
        },
        refreshUploadedFiles: function () {
            this.model.set(
                {
                    //files: files,
                    sourceFilesBinding: this.sourceFiles.map(function (obj) {
                        return { value: obj.get('id'), label: obj.get('name'), };
                    }),
                    onlineFilesBinding: this.onlineFiles.map(function (obj) {
                        return { value: obj.get('id'), label: obj.get('name'), };
                    }),
                },
                { from: "fetch" }
            );


            //var that = this;

            //callApi(
            //    {
            //        action: 'GET_UPLOADED_FILES',
            //        screenId: that.get("id"),
            //        global: false,
            //    },
            //    function (status, response) {
            //        try
            //        {
            //            if (status == true && response && response.Data && response.Data.Files) {
            //                var files = response.Data.Files;
            //                var sourceFilesBinding = _.map(files, function (value, key) {
            //                    return { label: key, value: key }
            //                });

            //                that.set({ files: files, sourceFilesBinding: sourceFilesBinding }, { from: "fetch" });
            //            }
            //        }
            //        catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
            //    }
            //);
        },
        startAutoRefresh: function (hasDelay) {
            try {
                if (this.autoRefresh.enabled !== true) {
                    var that = this;

                    if (this.autoRefresh.toid != null) {
                        clearTimeout(this.autoRefresh.toid);
                        this.autoRefresh.toid = null;
                    }

                    this.autoRefresh.enabled = true;

                    //use a timeout to execute the first refresh to return the handle to the start function caller.
                    //So when the caller finish it will do the first refresh.
                    this.autoRefresh.toid = setTimeout(
                        function () {
                            that._refresh();
                        },
                        (hasDelay != true)
                            ? 1
                            : this.autoRefresh.every
                    );
                }
            } catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
        },
        stopAutoRefresh: function () {
            if (this.autoRefresh.toid != null) {
                clearTimeout(this.autoRefresh.toid);
                this.autoRefresh.toid = null;
            }
            this.autoRefresh.enabled = false;
        },

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

            this.viewParams = viewParams;

            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/custom-screens/dmt/' + this.template + '/';

            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;
                            //start: before the view is visible, but the template was already loaded (not instanced nor appended)

                            //end:

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

                            that.messagesView = Backbone.Epoxy.View.extend({
                                className: 'single-message',
                                initialize: function () {
                                    this.render();
                                },
                                render: function () {
                                    var template = '<div data-bind="text:format(\'&raquo; $1: $2\', timestamp, message)"></div>';

                                    this.$el.html(template);

                                    this.applyBindings();
                                    return this;
                                },
                            });

                            that.applyBindings();

                            //start: the view was already loaded an is on a div element, but not appended to the main container
                            //here you can perform anything you want DOM related, by getting the dom element via that.$el.find("#id")
                            //or this.$("#id")

                            //var checkCssLoadedfn = function () {
                            //    var ss = document.styleSheets,
                            //        csslook = [
                            //            'assets/libs/js/codemirror-5.22.0/lib/codemirror.css',
                            //            'assets/libs/js/codemirror-5.22.0/addon/lint/lint.css',
                            //            'assets/libs/js/codemirror-5.22.0/addon/hint/show-hint.css',
                            //        ],
                            //        cssfound = [],
                            //        href, look, j;
                            //    var csslookLen = csslook.length;

                            //    for (var i = 0, sslen = ss.length; i < sslen; i++) {
                            //        href = ss[i].href;
                            //        if (href) {
                            //            for (j = 0; j < csslookLen; j++) {
                            //                look = csslook[j];

                            //                if (href.indexOf(look) != -1) {
                            //                    cssfound.push(look);

                            //                    if (csslook.length == cssfound.length)
                            //                        break;
                            //                }
                            //            }
                            //        }
                            //    }

                            //    if (csslook.length == cssfound.length) {
                            //        that.instanceCodeMirror();

                            //        that.editor.clearHistory();
                            //    }
                            //    else {
                            //        setTimeout(_.bind(checkCssLoadedfn, that), 500);
                            //    }
                            //};

                            that.fitToHeight();
                            that.bindViewScopedEvents();

                            that.instanceCodeMirror();

                            //setTimeout(_.bind(checkCssLoadedfn, that), 1000);

                            that.templates.browseModal = Handlebars.compile(that.$el.find('#browse_modal_template').html());
                            //that.templates.browseModal_actionCell = Handlebars.compile(that.$el.find('#action_cell_template').html());

                            var tabs = that.$el.find('a[data-toggle="tab"]');

                            tabs.tab();

                            tabs.on('shown.bs.tab', _.bind(that.tab_changed, that));

                            //end
                            //appending view to the main container 

                            that.append(thatContainer, that.$el);


                            //Set model with view params here to prevent changes on the model when the view bindings are applied.
                            if (viewParams) {
                                that.model.set({
                                    id: viewParams.id,
                                });
                            }

                            //Call first refresh.
                            that._refresh(
                                {
                                    onlineRefresh: false,
                                    open: true,
                                    params: {
                                        id: viewParams.id,
                                    },
                                }
                            );

                            that.startAutoRefresh(true);
                        },
                        true,
                        customPath
                    );
                },
                customPath
            );
        },
        save: function () {
            var that = this,
                attrs = this.source.toJSON(),
                code = this.editor.getValue();

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

            callApi(
                'SaveSource',
                {
                    id: attrs.id,
                    scriptId: attrs.scriptId,
                    code: code,
                    name: attrs.name,
                },
                function (resp, msg, msgParams) {
                    try
                    {
                        that.messagesColl.add( {
                            timestamp: new moment().format('YYYY-MM-DD HH:mm:ss'),
                            message: app.translate([that, app], (!msg) ? "DMT_BUILD_SUCCEDED" : msg, msgParams),
                        });

                        that.$el.find(".messages-container").scrollTop(0);

                        that.source.set('modified', false);
                        that.model.set('saving', false);
                    }
                    catch (error) {
                        that.model.set('saving', false);
                        console.error((error.stack) ? error.stack : new Error(error).stack);
                    }
                },
                function (msg, msgParams) {
                    try {
                        that.messagesColl.add({
                            timestamp: new moment().format('YYYY-MM-DD HH:mm:ss'),
                            message: app.translate([that, app], msg, msgParams),
                        });

                        that.$el.find(".messages-container").scrollTop(0);
                        that.model.set('saving', false);
                    }
                    catch (error) {
                        that.model.set('saving', false);
                        console.error((error.stack) ? error.stack : new Error(error).stack);
                    }
                }
            );

        },
        upload: function () {
            var that = this,
                attrs = this.source.toJSON(),
                code = this.editor.getValue();

            //lock command buttons
            this.model.set({
                //Quickly set current lock to update display.
                currentLock: 'DMTSCRIPTSET',
                enqueuedCommand: true,
                //Quickly set lock to block the screen. Then, lock value
                //will be updated and handled by locks manager.
                fullLocked: true,
            });

            try
            {
                callApi(
                    'SaveSource',
                    {
                        id: attrs.id,
                        scriptId: attrs.scriptId,
                        code: code,
                        name: attrs.scriptName,
                        upload: true,
                    },
                    function (resp, msg, msgParams) {
                        try {
                            if (resp && resp.Data && resp.Data.Table && resp.Data.Table.length > 0) {
                                var commandResp = parseCommandData(resp.Data.Table3[0]);

                                that.source.setRaw(resp.Data.Table[0]);
                                that.model.set('id', that.source.get('id'));

                                that.updateViewOnlineData(resp.Data);

                                //Empty message means that all went ok with code compilation.
                                if (!msg) {
                                    if (commandResp.statusCode == "OK") {
                                        that.messagesColl.add({
                                            timestamp: new moment().format('YYYY-MM-DD HH:mm:ss'),
                                            message: app.translate([that, app], 'DMT_UPLOAD_SUCCESSFUL'),
                                        });
                                    }
                                    else {
                                        that.messagesColl.add({
                                            timestamp: new moment().format('YYYY-MM-DD HH:mm:ss'),
                                            //Also translate command response text.
                                            message: app.translate([that, app], 'DMT_UPLOAD_ERROR', [app.translate([that, app], commandResp.responseText)]),
                                        });
                                    }
                                }
                                else {
                                    that.messagesColl.add({
                                        timestamp: new moment().format('YYYY-MM-DD HH:mm:ss'),
                                        message: app.translate([that, app], msg, msgParams),
                                    });
                                }

                                that.source.set('modified', false);

                                ////If message is not empty, means that something did not validate.
                                ////For example, the compilation. So, just to prevent issues right now,
                                ////if message is not empty, clear current lock and set lock to false.
                                //if (msg) {
                                //    that.model.set({
                                //        //Clear current lock if transaction failed.
                                //        currentLock: null,
                                //        //Set lock to false if transaction failed.
                                //        fullLocked: false,
                                //    })
                                //}
                            }
                            else {
                                ////Set lock to false if transaction failed.
                                //that.model.set({
                                //    //Clear current lock if transaction failed.
                                //    currentLock: null,
                                //    fullLocked: false,
                                //});

                                that.messagesColl.add({
                                    timestamp: new moment().format('YYYY-MM-DD HH:mm:ss'),
                                    message: app.translate([that, app], "DMT_UPLOAD_FAILED"),
                                });
                            }

                            that.$el.find(".messages-container").scrollTop(0);
                        }
                        catch (error) {
                            //try {
                            //    that.model.set({
                            //        //Clear current lock if transaction failed.
                            //        currentLock: null,
                            //        //Set lock to false if transaction failed.
                            //        fullLocked: false,
                            //    });
                            //}
                            //catch (error2) { console.error((error2.stack) ? error2.stack : new Error(error2).stack); }

                            console.error((error.stack) ? error.stack : new Error(error).stack);
                        }
                        finally {
                            try {
                                that.model.set({
                                    //Clear current lock.
                                    currentLock: null,
                                    //Set lock to false.
                                    enqueuedCommand: false,
                                    fullLocked: false,
                                });
                            }
                            catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                        }
                    },
                    function (msg, msgParams) {
                        try {
                            that.messagesColl.add({
                                timestamp: new moment().format('YYYY-MM-DD HH:mm:ss'),
                                message: app.translate([that, app], msg, msgParams),
                            });

                            that.$el.find(".messages-container").scrollTop(0);
                        }
                        catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                        finally {
                            try {
                                that.model.set({
                                    //Clear current lock if transaction failed.
                                    currentLock: null,
                                    enqueuedCommand: false,
                                    //Set lock to false if transaction failed.
                                    fullLocked: false,
                                });
                            }
                            catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                        }
                    }
                );
            }
            catch (error) {
                try {
                    that.model.set({
                        //Clear current lock if transaction failed.
                        currentLock: null,
                        enqueuedCommand: false,
                        //Set lock to false if transaction failed.
                        fullLocked: false,
                    });
                }
                catch (error2) { console.error((error2.stack) ? error2.stack : new Error(error2).stack); }

                console.error((error.stack) ? error.stack : new Error(error).stack);
            }
        },

        cancelUploadFileBtn_click: function (e) {
            try {
                var file_form = this.$el.find(".add-file-form");
                var review_file_form = this.$el.find(".review-file-form");

                var fileinput = this.$el.find(".files-input");
                fileinput.replaceWith(fileinput.val('').clone(true));

                review_file_form.addClass("hide");
                this.$el.find(".file-upload-successful-message").addClass("hide");
                file_form.removeClass("hide");
            }
            catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
        },
        editorChanged: function (instance, changes) {
            try {
                if (this.firstEditorValue) {
                    this.firstEditorValue = false;
                    this.source.set('modified', false);
                }
                else {
                    this.source.set('modified', true);
                }
            }
            catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
        },
        editor_commands_save: function (a, b, c) {
            try {
                this.save();
            }
            catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
        },
        filesInput_changed: function (e) {
            try
            {
                var file_form = this.$el.find(".add-file-form");
                var review_file_form = this.$el.find(".review-file-form"); 

                var fileinput = this.$el.find(".files-input").get(0);
                var files = fileinput.files;

                if (files.length > 0) {
                    var file = files[0];
                    var filename = file.name;

                    this.$el.find(".input-file-filename").val(filename);

                    file_form.addClass("hide");
                    review_file_form.removeClass("hide"); 
                }
            }
            catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
        },
        filesSelect_changed: function () {
            try {
                var that = this;
                var filesSelect = this.$el.find(".files-select");

                if (filesSelect && filesSelect.val() && _.isArray(filesSelect.val()) && filesSelect.val().length > 0) {
                    this.$el.find(".btn-remove-file").attr("disabled", false);
                } else {
                    this.$el.find(".btn-remove-file").attr("disabled", true);
                }
            }
            catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
        },
        removeFileBtn_click: function () {
            var that = this;
            this.currentModal = new Modal.Views.Main({
                    focusOk: false,
                    focusSelector: '#btn-cancel',
                    title: app.translate([this, app], "remove_file_confirm_title"),
                    message: app.translate([this, app], "remove_file_confirm_message"),
                    buttons_type: "CONTINUE-CANCEL",
                });

            this.listenToOnce(this.currentModal, "continue", function (modal) {
                try {
                    var filesSelect = that.$el.find(".files-select");

                    that.sourceFiles.removeItems({
                        params: {
                            id: that.source.get('id'),
                            fileIds: filesSelect.val(),
                        },
                        success: function (coll, resp, msg, msgParams) {
                            try {
                                that.refreshUploadedFiles();

                                that.messagesColl.add(
                                    {
                                        timestamp: new moment().format('YYYY-MM-DD HH:mm:ss'),
                                        message: app.translate([that, app], (!msg) ? 'DMT_FILES_REMOVED_SUCCESSFUL' : msg, msgParams),
                                    }
                                );

                                //empty currentModal
                                that.currentModal = null;

                                that.$el.find(".messages-container").scrollTop(0);
                            }
                            catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                        },
                        error: function (coll, msg, msgParams) {
                            try {
                                that.messagesColl.add(
                                    {
                                        timestamp: new moment().format('YYYY-MM-DD HH:mm:ss'),
                                        message: app.translate([that, app], msg, msgParams),
                                    }
                                );

                                that.$el.find(".messages-container").scrollTop(0);
                            }
                            catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                        },
                    });
                }
                catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
            });

            this.currentModal.show();
        },
        messagesContainer_click: function (e) {
            //Proccess only if a message button was pressed
            if (e.target.parentNode.className == "single-message") {
                //Get all the message lines on the messages container
                var messageLines = this.$el.find('.single-message');

                //Normalize any line previously selected
                messageLines.removeClass('single-message-selected');

                //Display target line as selected
                $(e.target.parentNode).removeClass('single-message-normal');
                $(e.target.parentNode).addClass('single-message-selected');
            }
        },
        saveBtn_click: function (e) {
            try {
                this.save();
            }
            catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
        },
        saveAsBtn_click: function (e) {
            try {
                var that = this
                    , attrs = this.source.toJSON()
                    , clickedBtn = $(e.target)
                    , auxSource = this.source;

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

                //set the current code for the auxiliar source to clone
                auxSource.set('code', this.editor.getValue());

                this.currentModal = new Screen.Views.BrowseModal({
                    model: new Screen.Models.BrowseModal({
                        curSourceId: attrs.id,
                        scriptId: attrs.scriptId,
                        idOnline: attrs.idOnline,
                        nameOnline: attrs.nameOnline,
                    }),
                    i18n: this.options.i18n[this.template],
                    template: this.templates.browseModal,
                    actionsCellTemplate: this.$el.find('#actions_cell_template').html(),
                    createFromExampleCellTemplate: this.$el.find('#create_from_example_cell_template').html(),
                    toCloneSource: auxSource,
                    onlineLabel: app.translate([that, app], 'online_label'),
                });

                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); }
                };

                this.listenToOnce(this.currentModal, 'shown', fn_modal_shown_hidden)
                    .listenToOnce(this.currentModal, 'hidden', fn_modal_shown_hidden)
                    .listenToOnce(this.currentModal, 'finish', function (modal, output) {
                        try {
                            if (output.result == 'SUCCESS') {
                                this.source.setRaw(output.data);
                                this.model.set('id', this.source.get('id'));
                                this.editor.setValue(this.source.get('code'));

                                that.currentModal = null;

                                var params = this.model.toJSON();

                                app.router.navigate(
                                    app.router.resolveURL(
                                        app.router.currentModule,
                                        _.extend(
                                            {},
                                            params,
                                            {}
                                        ),
                                        false
                                    ),
                                    { trigger: false, }
                                );
                            }
                        }
                        catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
                    });;

                this.currentModal.show();
            }
            catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
        },
        browseBtn_click: function (e) {
            try {
                var that = this
                    , attrs = this.source.toJSON()
                    , clickedBtn = $(e.target);

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

                this.currentModal = new Screen.Views.BrowseModal({
                    model: new Screen.Models.BrowseModal({
                        curSourceId: attrs.id,
                        scriptId: attrs.scriptId,
                        idOnline: attrs.idOnline,
                        nameOnline: attrs.nameOnline,
                    }),
                    i18n: this.options.i18n[this.template],
                    template: this.templates.browseModal,
                    actionsCellTemplate: this.$el.find('#actions_cell_template').html(),
                    createFromExampleCellTemplate: this.$el.find('#create_from_example_cell_template').html(),
                    onlineLabel: app.translate([that, app], 'online_label'),
                });

                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); }
                };

                this.listenToOnce(this.currentModal, 'shown', fn_modal_shown_hidden)
                    .listenToOnce(this.currentModal, 'hidden', fn_modal_shown_hidden)
                    .listenToOnce(this.currentModal, 'finish', function (modal, output) {
                        try {
                            if (output.result == 'SUCCESS') {
                                this.source.setRaw(output.data);
                                this.model.set('id', this.source.get('id'));
                                this.editor.setValue(this.source.get('code'));

                                that.currentModal = null;

                                var params = this.model.toJSON();

                                app.router.navigate(
                                    app.router.resolveURL(
                                        app.router.currentModule,
                                        _.extend(
                                            {},
                                            params,
                                            {}
                                        ),
                                        false
                                    ),
                                    { trigger: false, }
                                );
                            }
                        }
                        catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
                    });;

                this.currentModal.show();
            }
            catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
        },
        uploadBtn_click: function (e) {
            var that = this,
                onlineName = this.source.get('nameOnline'),
                onlineUpload = new moment(this.source.get('uploadOnline')).format('YYYY-MM-DD HH:mm:ss'),
                onlineUser = this.source.get('userOnline'),

                fullMessage = (onlineName)
                                ? (((app.translate([this, app], "upload_source_replace_confirm_message")).replace("$1", onlineName, "gi")).replace("$2", onlineUser, "gi")).replace("$3", onlineUpload, "gi")
                                : app.translate([this, app], "upload_source_confirm_message");

                this.currentModal = new Modal.Views.Main({
                    focusOk: false,
                    focusSelector: '#btn-cancel',
                    title: app.translate([this, app], "upload_source_confirm_title"),
                    message: fullMessage,
                    buttons_type: "CONTINUE-CANCEL",
                });

            this.listenToOnce(this.currentModal, "continue", function (modal) {
                try {
                    that.upload();
                    that.currentModal = null;
                }
                catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
            });

            this.currentModal.show();
        },
        runBtn_click: function (e) {
            var that = this;
            this.currentModal = new Modal.Views.Main({
                    focusOk: false,
                    focusSelector: '#btn-cancel',
                    title: app.translate([this, app], "run_modal_confirm_title"),
                    message: app.translate([this, app], "run_modal_confirm_message"),
                    buttons_type: "CONTINUE-CANCEL",
                });

            this.listenToOnce(this.currentModal, "continue", function (modal) {
                try {
                    that.model.set({
                        //Quickly set current lock to update display.
                        currentLock: 'DMTSCRIPTRUN',
                        enqueuedCommand: true,
                        //Quickly set lock to block the screen. Then, lock value
                        //will be updated and handled by locks manager.
                        onlineLocked: true,
                    });

                    Script.run(
                        {
                            params: [
                                { name: 'Id', type: 'INT', value: that.source.get('scriptId') },
                            ],
                            success: function (commandResp) {
                                try {
                                    if (commandResp.statusCode == "OK") {
                                        that.messagesColl.add({
                                            timestamp: new moment().format('YYYY-MM-DD HH:mm:ss'),
                                            message: app.translate([that, app], 'DMT_RUN_COMMAND_SUCCESSFUL'),
                                        });
                                    }
                                    else {
                                        that.messagesColl.add({
                                            timestamp: new moment().format('YYYY-MM-DD HH:mm:ss'),
                                            //Also translate command response text.
                                            message: app.translate([that, app], 'DMT_RUN_COMMAND_ERROR', [app.translate([that, app], commandResp.responseText)]),
                                        });
                                    }

                                    that.$el.find(".messages-container").scrollTop(0);
                                }
                                catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                                finally {
                                    try {
                                        that.currentModal = null;

                                        that.model.set({
                                            //Clear current lock.
                                            currentLock: null,
                                            enqueuedCommand: false,
                                            //Set lock to false.
                                            onlineLocked: false,
                                        });
                                    }
                                    catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                                }
                            },
                            error: function (msg, msgParams) {
                                try {
                                    that.messagesColl.add({
                                        timestamp: new moment().format('YYYY-MM-DD HH:mm:ss'),
                                        message: app.translate([that, app], msg, msgParams),
                                    });

                                    that.$el.find(".messages-container").scrollTop(0);
                                }
                                catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                                finally {
                                    try
                                    {
                                        that.currentModal = null;

                                        that.model.set({
                                            //Clear current lock if transaction failed.
                                            currentLock: null,
                                            enqueuedCommand: false,
                                            //Set lock to false if transaction failed.
                                            onlineLocked: false,
                                        });
                                    }
                                    catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                                }
                            },
                        }
                    );
                }
                catch (error) {
                    try
                    {
                        that.currentModal = null;

                        that.model.set({
                            //Clear current lock if transaction failed.
                            currentLock: null,
                            enqueuedCommand: false,
                            //Set lock to false if transaction failed.
                            onlineLocked: false,
                        });
                    }
                    catch (error2) { console.error((error2.stack) ? error2.stack : new Error(error2).stack); }

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

            this.currentModal.show();
        },
        pauseBtn_click: function (e) {
            var that = this;
            this.currentModal = new Modal.Views.Main({
                    focusOk: false,
                    focusSelector: '#btn-cancel',
                    title: app.translate([this, app], "pause_modal_confirm_title"),
                    message: app.translate([this, app], "pause_modal_confirm_message"),
                    buttons_type: "CONTINUE-CANCEL",
                });

            this.listenToOnce(this.currentModal, "continue", function (modal) {
                try {
                    that.model.set({
                        //Quickly set current lock to update display.
                        currentLock: 'DMTSCRIPTPAUSE',
                        enqueuedCommand: true,
                        //Quickly set lock to block the screen. Then, lock value
                        //will be updated and handled by locks manager.
                        onlineLocked: true,
                    });

                    Script.pause(
                        {
                            params: [
                                { name: 'Id', type: 'INT', value: that.source.get('scriptId') },
                            ],
                            success: function (commandResp) {
                                try {
                                    if (commandResp.statusCode == "OK") {
                                        that.messagesColl.add({
                                            timestamp: new moment().format('YYYY-MM-DD HH:mm:ss'),
                                            message: app.translate([that, app], 'DMT_PAUSE_COMMAND_SUCCESSFUL'),
                                        });
                                    }
                                    else {
                                        that.messagesColl.add({
                                            timestamp: new moment().format('YYYY-MM-DD HH:mm:ss'),
                                            //Also translate command response text.
                                            message: app.translate([that, app], 'DMT_PAUSE_COMMAND_ERROR', [app.translate([that, app], commandResp.responseText)]),
                                        });
                                    }

                                    that.$el.find(".messages-container").scrollTop(0);
                                }
                                catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                                finally {
                                    try {
                                        that.currentModal = null;

                                        that.model.set({
                                            //Clear current lock.
                                            currentLock: null,
                                            enqueuedCommand: false,
                                            //Set lock to false.
                                            onlineLocked: false,
                                        });
                                    }
                                    catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                                }
                            },
                            error: function (msg, msgParams) {
                                try {
                                    that.messagesColl.add({
                                        timestamp: new moment().format('YYYY-MM-DD HH:mm:ss'),
                                        message: app.translate([that, app], msg, msgParams),
                                    });

                                    that.$el.find(".messages-container").scrollTop(0);
                                }
                                catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                                finally {
                                    try {
                                        that.currentModal = null;

                                        that.model.set({
                                            //Clear current lock if transaction failed.
                                            currentLock: null,
                                            enqueuedCommand: false,
                                            //Set lock to false if transaction failed.
                                            onlineLocked: false,
                                        });
                                    }
                                    catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                                }
                            },
                        }
                    );
                }
                catch (error) {
                    try {
                        that.currentModal = null;

                        that.model.set({
                            //Clear current lock if transaction failed.
                            currentLock: null,
                            enqueuedCommand: false,
                            //Set lock to false if transaction failed.
                            onlineLocked: false,
                        });
                    }
                    catch (error2) { console.error((error2.stack) ? error2.stack : new Error(error2).stack); }

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

            this.currentModal.show();
        },
        stopBtn_click: function (e) {
            var that = this;
            this.currentModal = new Modal.Views.Main({
                    focusOk: false,
                    focusSelector: '#btn-cancel',
                    title: app.translate([this, app], "stop_modal_confirm_title"),
                    message: app.translate([this, app], "stop_modal_confirm_message"),
                    buttons_type: "CONTINUE-CANCEL",
                });

            this.listenToOnce(this.currentModal, "continue", function (modal) {
                try {
                    that.model.set({
                        //Quickly set current lock to update display.
                        currentLock: 'DMTSCRIPTSTOP',
                        enqueuedCommand: true,
                        //Quickly set lock to block the screen. Then, lock value
                        //will be updated and handled by locks manager.
                        onlineLocked: true,
                    });

                    Script.stop(
                        {
                            params: [
                                { name: 'Id', type: 'INT', value: that.source.get('scriptId') },
                            ],
                            success: function (commandResp) {
                                try {
                                    if (commandResp.statusCode == "OK") {
                                        that.messagesColl.add({
                                            timestamp: new moment().format('YYYY-MM-DD HH:mm:ss'),
                                            message: app.translate([that, app], 'DMT_STOP_COMMAND_SUCCESSFUL'),
                                        });
                                    }
                                    else {
                                        that.messagesColl.add({
                                            timestamp: new moment().format('YYYY-MM-DD HH:mm:ss'),
                                            //Also translate command response text.
                                            message: app.translate([that, app], 'DMT_STOP_COMMAND_ERROR', [app.translate([that, app], commandResp.responseText)]),
                                        });
                                    }
                                    that.$el.find(".messages-container").scrollTop(0);
                                }
                                catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                                finally {
                                    try {
                                        that.currentModal = null;

                                        that.model.set({
                                            //Clear current lock.
                                            currentLock: null,
                                            enqueuedCommand: false,
                                            //Set lock to false.
                                            onlineLocked: false,
                                        });
                                    }
                                    catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                                }
                            },
                            error: function (msg, msgParams) {
                                try {
                                    that.messagesColl.add({
                                        timestamp: new moment().format('YYYY-MM-DD HH:mm:ss'),
                                        message: app.translate([that, app], msg, msgParams),
                                    });

                                    that.$el.find(".messages-container").scrollTop(0);
                                }
                                catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                                finally {
                                    try {
                                        that.currentModal = null;

                                        that.model.set({
                                            //Clear current lock if transaction failed.
                                            currentLock: null,
                                            enqueuedCommand: false,
                                            //Set lock to false if transaction failed.
                                            onlineLocked: false,
                                        });
                                    }
                                    catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                                }
                            },
                        }
                    );
                }
                catch (error) {
                    try {
                        that.currentModal = null;

                        that.model.set({
                            //Clear current lock if transaction failed.
                            currentLock: null,
                            enqueuedCommand: false,
                            //Set lock to false if transaction failed.
                            onlineLocked: false,
                        });
                    }
                    catch (error2) { console.error((error2.stack) ? error2.stack : new Error(error2).stack); }

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

            this.currentModal.show();
        },
        getLogsBtn_click: function (e) {
            try {
                var that = this;

                ////Set model property to indicates loading state on screen
                //m.set('isLoading', true);

                this.model.set({
                    //Quickly set current lock to update display.
                    currentLock: 'DMTSCRIPTGETLOGS',
                    enqueuedCommand: true,
                    //Quickly set lock to block the screen. Then, lock value
                    //will be updated and handled by locks manager.
                    onlineLocked: true,
                });

                this.source.getLogs({
                    success: function (model, data) {
                        try {
                            var byteString = atob(data);

                            // Convert that text into a byte array.
                            var ab = new ArrayBuffer(byteString.length);

                            var ia = new Uint8Array(ab);

                            for (var i = 0; i < byteString.length; i++)
                                ia[i] = byteString.charCodeAt(i);

                            // Blob for saving.
                            var blob = new Blob([ia], { type: 'application/octet-stream' });

                            // Tell the browser to download the file.
                            saveAs(blob, 'logs.' + that.source.get('scriptName') + '.' + new moment().format('YYYYMMDDHHmmss') + '.zip');
                        }
                        catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                        finally {
                            try {
                                //m.set('isLoading', false);

                                that.model.set({
                                    //Clear current lock if transaction failed.
                                    currentLock: null,
                                    enqueuedCommand: false,
                                    //Set lock to false if transaction failed.
                                    onlineLocked: false,
                                });
                            }
                            catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                        }
                    },
                    error: function (model, msg) {
                        try {
                            that.messagesColl.add({
                                timestamp: new moment().format('YYYY-MM-DD HH:mm:ss'),
                                message: app.translate([that, app], msg),
                            });

                            that.$el.find(".messages-container").scrollTop(0);
                        }
                        catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                        finally {
                            try {
                                //m.set('isLoading', false);

                                that.model.set({
                                    //Clear current lock if transaction failed.
                                    currentLock: null,
                                    enqueuedCommand: false,
                                    //Set lock to false if transaction failed.
                                    onlineLocked: false,
                                });
                            }
                            catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                        }
                    },
                });
            }
            catch (error) {
                try {
                    //m.set('isLoading', false);

                    this.model.set({
                        //Clear current lock if transaction failed.
                        currentLock: null,
                        enqueuedCommand: false,
                        //Set lock to false if transaction failed.
                        onlineLocked: false,
                    });
                }
                catch (error2) { console.error((error2.stack) ? error2.stack : new Error(error2).stack); }

                console.error((error.stack) ? error.stack : new Error(error).stack);
            }
        },
        downloadSourceBtn_click: function (e) {
            try {
                var that = this;

                this.model.set('enqueuedCommand', true);
                this.model.set('downloading', true);

                Script.download(
                    {
                        params: {
                            id: that.source.get('id')
                        },
                        success: function (model, data) {
                            try {
                                // Blob for saving.
                                var blob = new Blob([new Uint8Array(data.Content)], { type: 'application/octet-stream' });

                                // Tell the browser to download the file.
                                saveAs(blob, data.FileName);

                                that.messagesColl.add({
                                    timestamp: new moment().format('YYYY-MM-DD HH:mm:ss'),
                                    message: app.translate([that, app], 'DOWNLOAD_COMMAND_SUCCESSFUL'),
                                });
                                that.$el.find(".messages-container").scrollTop(0);
                                that.model.set('enqueuedCommand', false);
                                that.model.set('downloading', false);
                            }
                            catch (error) {
                                that.model.set('enqueuedCommand', false);
                                that.model.set('downloading', false);
                                console.error((error.stack) ? error.stack : new Error(error).stack);
                            }
                        },
                        error: function (model, errorMsg, errorMsgParams) {
                            try {
                                that.messagesColl.add({
                                    timestamp: new moment().format('YYYY-MM-DD HH:mm:ss'),
                                    message: app.translate([that, app], errorMsg, errorMsgParams),
                                });

                                that.$el.find(".messages-container").scrollTop(0);

                                that.model.set('enqueuedCommand', false);
                                this.model.set('downloading', false);
                            }
                            catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                            finally {
                                try {
                                    that.model.set('enqueuedCommand', false);
                                    that.model.set('downloading', false);
                                }
                                catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                            }
                        },
                    }
                );
            }
            catch (error) {
                this.model.set('enqueuedCommand', false);
                console.error((error.stack) ? error.stack : new Error(error).stack);
            }
        },
        downloadOnlineBtn_click: function (e) {
            try {
                var that = this;

                this.model.set('enqueuedCommand', true);
                this.model.set('downloading', true);

                Script.download(
                    {
                        params: {
                            id: that.source.get('idOnline')
                        },
                        success: function (model, data) {
                            try {
                                // Blob for saving.
                                var blob = new Blob([new Uint8Array(data.Content)], { type: 'application/octet-stream' });

                                // Tell the browser to download the file.
                                saveAs(blob, data.FileName);

                                that.messagesColl.add({
                                    timestamp: new moment().format('YYYY-MM-DD HH:mm:ss'),
                                    message: app.translate([that, app], 'DOWNLOAD_COMMAND_SUCCESSFUL'),
                                });
                                that.$el.find(".messages-container").scrollTop(0);
                                that.model.set('enqueuedCommand', false);
                                that.model.set('downloading', false);
                            }
                            catch (error) {
                                that.model.set('enqueuedCommand', false);
                                that.model.set('downloading', false);
                                console.error((error.stack) ? error.stack : new Error(error).stack);
                            }
                        },
                        error: function (model, errorMsg) {
                            try {
                                that.messagesColl.add({
                                    timestamp: new moment().format('YYYY-MM-DD HH:mm:ss'),
                                    message: app.translate([that, app], errorMsg),
                                });
                                that.$el.find(".messages-container").scrollTop(0);
                                that.model.set('enqueuedCommand', false);
                                that.model.set('downloading', false);
                            }
                            catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                            finally {
                                try {
                                    that.model.set('enqueuedCommand', false);
                                    that.model.set('downloading', false);
                                }
                                catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                            }
                        },
                    }
                );
            }
            catch (error) {
                this.model.set('enqueuedCommand', false);
                console.error((error.stack) ? error.stack : new Error(error).stack);
            }
        },
        source_fetch: function (model, resp, opt) {
            try {
                this.updateViewSourceData(resp);
                this.updateViewOnlineData(resp);

                if (this.model.get('isLoading')) {
                    this.model.set('isLoading', false);
                    this.editor.refresh();
                }
            }
            catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
        },
        source_fetch_online: function (model, resp, opt) {
            try {
                this.updateViewOnlineData(resp);
            }
            catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
        },
        source_change_scriptName: function (model, value, opt) {
            try {
                var that = this,
                    locksCatColl = app.models.locksManager.get('categories'),                    
                    lockNames,
                    locksCatToRemove,
                    scriptName = this.source.get('scriptName').toUpperCase();

                lockNames = _.map(
                    [
                        'DMTSCRIPTDELETE',
                        'DMTSCRIPTGETLOGS',
                        'DMTSCRIPTPAUSE',
                        'DMTSCRIPTSET',
                        'DMTSCRIPTSTOP',
                        'DMTSCRIPTRUN',
                    ],
                    function (obj) { return obj + '-' + scriptName; }
                );

                //Remove lock categories from with name of previous script.
                locksCatToRemove = locksCatColl.find(function (obj) {
                    return obj.get('name').startsWith('DMTSCRIPTSET');
                });

                _.each(locksCatToRemove, function (obj) {
                    locksCatColl.remove(obj);
                });


                //Add lock categories for current script.
                locksCatColl.add(_.map(
                    lockNames, 
                    function (obj) { return { name: obj, }; }
                ));


                //Attach to lock changes
                app.models.locksManager.listenToCategory(
                    this,
                    ['add', 'remove'],
                    lockNames,
                    function (coll, model, opt) {
                        try
                        {
                            var locks = app.models.locksManager.getCategoryLocks(lockNames, that.source.get('agentId'), true);


                            if (locks.length > 0) {
                                //Only use the first because it should have only one lock.
                                //Split lock name to exclude the script name and get only the base lock name.
                                var lockName = locks.models[0].get('name').split('-')[0],
                                    hasSetLock;

                                //Check if lock is a SET lock.
                                hasSetLock = (lockName == 'DMTSCRIPTSET');

                                that.model.set({
                                    currentLock: lockName,
                                    fullLocked: hasSetLock,
                                    onlineLocked: !hasSetLock,
                                });
                            }
                            else {
                                that.model.set({
                                    currentLock: null,
                                    fullLocked: false,
                                    onlineLocked: false,
                                });
                            }
                        }
                        catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                    }
                );
            }
            catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
        },
        uploadFileBtn_click: function (e) {
            try {
                var that = this;

                var filesInput = this.$el.find(".files-input");
                //Max files size before alert
                var fileSizeAlert = 10485760;
                var totalFilesSize = 0;

                //Get the total of the file sizes
                for (var i = 0; i < filesInput[0].files.length; i++) {
                    totalFilesSize += filesInput[0].files[i].size;
                }

                if (totalFilesSize > fileSizeAlert) {
                    this.currentModal = new Modal.Views.Main({
                            focusOk: false,
                            focusSelector: '#btn-cancel',
                            title: app.translate([this, app], "files_exceed_size_modal_confirm_title"),
                            message: app.translate([this, app], "files_exceed_size_modal_confirm_message"),
                            buttons_type: "CONTINUE-CANCEL",
                        });

                    this.listenToOnce(this.currentModal, "continue", function (modal) {
                        try {
                            that.uploadFiles(filesInput);
                            that.currentModal = null;
                        }
                        catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                    });

                    this.currentModal.show();
                }
                else {
                    this.uploadFiles(filesInput);
                }
            }
            catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
        },
        uploadFiles: function (filesInput) {
            try {
                var that = this;
                var review_file_form = this.$el.find(".review-file-form");
                var file_upload_progress = this.$el.find(".file-upload-progress");

                review_file_form.addClass("hide");
                file_upload_progress.removeClass("hide");

                var filesList = filesInput.get(0).files,
                    filesArray = [],
                    filesContent = [],
                    fileNames = [];

                for (var i = 0, len = filesList.length; i < len; i++) {
                    var file = filesList[i];

                    fileNames.push(file.name);
                    filesArray.push(file);
                }


                //Setup file reader
                var reader = new FileReader();

                // Read file into memory as array of bytes
                reader.readAsDataURL(filesArray.shift(0));
                //reader.readAsArrayBuffer(filesArray.shift(0));

                //Empty filesInput
                filesInput.replaceWith(filesInput.val('').clone(true));

                // Handle progress, success, and errors
                reader.onload = function (evt) {
                    //var arrayBuffer = evt.target.result;

                    //filesContent.push(new Uint8Array(arrayBuffer));


                    var fileString = evt.target.result;

                    //Separate the headers from the data
                    var data = fileString.split(',');
                    filesContent.push(data[1]);


                    if (filesArray.length > 0) {
                        reader.readAsDataURL(filesArray.shift(0));
                        //reader.readAsArrayBuffer(filesArray.shift(0));
                    }
                    else {
                        var data = {
                            id: that.source.get("id"),
                            fileNames: fileNames,
                            files: filesContent,
                        };

                        $.ajax({
                            type: 'POST',
                            url: app.foldersRoot + '/app/custom-screens/dmt/api/api.svc/UploadSourceFiles',
                            data: JSON.stringify(data),
                            async: true,
                            //Options to tell jQuery not to process data or worry about content-type.
                            cache: false,
                            contentType: false,
                            processData: false,
                            //Custom XMLHttpRequest to show upload progress.
                            xhr: function () {  
                                var myXhr = $.ajaxSettings.xhr();
                                if (myXhr.upload) { // Check if upload property exists
                                    myXhr.upload.addEventListener(
                                        'progress',
                                        function (e) {
                                            try {
                                                var upload_progress_bar = that.$el.find(".file-upload-progress-bar");
                                                var loaded = e.loaded;
                                                var total = e.total;

                                                var percent = (loaded * 100) / total;
                                                upload_progress_bar.css("width", percent + "%");
                                            }
                                            catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                                        },
                                        false
                                    );
                                }
                                return myXhr;
                            },
                        })
                        .done(function (respString) {
                            try {
                                var resp = JSON.parse(respString);

                                that.$el.find('.file-upload-progress').addClass('hide');

                                if (resp != null && resp.Success == true) {
                                    if (resp.Data.Table)
                                        that.sourceFiles.add(_.map(resp.Data.Table, Screen.Models.SourceFile.prototype.parse));

                                    that.refreshUploadedFiles();


                                    that.$el.find('.add-file-form').removeClass('hide');


                                    //Parse response message. Split it by '|'.
                                    // - The first item will be the message.
                                    // - The rest of the items, if they exist, will be parameter
                                    //to be inserted into the message.
                                    var msgSplit = resp.Message.split('|');

                                    var msg = msgSplit.shift(0),
                                        msgParams = msgSplit;

                                    that.messagesColl.add({
                                        timestamp: new moment().format('YYYY-MM-DD HH:mm:ss'),
                                        message: app.translate([that, app], (!msg) ? 'DMT_FILES_UPLOAD_SUCCESSFUL' : msg, msgParams),
                                    });

                                    that.$el.find(".messages-container").scrollTop(0);
                                }
                                else {
                                    //Parse response message. Split it by '|'.
                                    // - The first item will be the message.
                                    // - The rest of the items, if they exist, will be parameter
                                    //to be inserted into the message.
                                    var msgSplit = resp.Message.split('|');

                                    var msg = msgSplit.shift(0),
                                        msgParams = msgSplit;

                                    that.$el.find(".review-file-form").removeClass("hide");
                                    that.$el.find(".file-upload-progress").addClass("hide");

                                    that.refreshUploadedFiles();

                                    that.messagesColl.add({
                                        timestamp: new moment().format('YYYY-MM-DD HH:mm:ss'),
                                        message: app.translate([that, app], msg, msgParams),
                                    });

                                    that.$el.find(".messages-container").scrollTop(0);
                                }

                                that.refreshUploadedFiles();
                            }
                            catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                        })
                        .fail(function (resp) {
                            try {
                                console.error(resp);

                                that.$el.find(".review-file-form").removeClass("hide");
                                that.$el.find(".file-upload-progress").addClass("hide");

                                that.refreshUploadedFiles();

                                that.messagesColl.add({
                                    timestamp: new moment().format('YYYY-MM-DD HH:mm:ss'),
                                    message: app.translate([that, app], 'INTERNAL_SERVER_ERROR'),
                                });

                                that.$el.find(".messages-container").scrollTop(0);
                            }
                            catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                        });
                    }
                };
                reader.onerror = function errorHandler(evt) {
                    console.error(evt);
                    //if (evt.target.error.name == "NotReadableError") {
                    //    app.views.topMessages.showMessage(app.translate([that, app], 'could_not_read_dwg'), { stay: 5 * 1000, });
                    //}
                };
            }
            catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
        },

        updateViewSourceData: function(resp) {
            if (this.source.get('code'))
                this.editor.setValue(this.source.get('code'));

            this.firstEditorValue = true;

            //Set source files collection
            if (resp.Table1)
                this.sourceFiles.set(_.map(resp.Table1, Screen.Models.SourceFile.prototype.parse));
            else
                this.sourceFiles.reset();

            this.refreshUploadedFiles();
        },
        updateViewOnlineData: function(resp) {
            if (this.source.get('codeOnline'))
                this.onlineCode.setValue(this.source.get('codeOnline'));

            //Set online files collection
            if (resp.Table2)
                this.onlineFiles.set(_.map(resp.Table2, Screen.Models.SourceFile.prototype.parse));
            else
                this.onlineFiles.reset();

            this.refreshUploadedFiles();
        },

        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;
            }
        },
        bindViewScopedEvents: function () {
            //TODO - Put this in events object.
            var that = this;

            this.listeners['beforeunload'] = _.bind(that.window_beforeUnload, that);
            $(window).on('beforeunload', this.listeners['beforeunload']);

            this.listeners['focus'] = _.bind(that.fitToHeight, that);
            window.addEventListener('focus', that.listeners['focus'], false); //Cope with window being resized whilst on another tab

            this.listeners['resize'] = _.debounce(_.bind(that.fitToHeight, that), 250);
            window.addEventListener('resize', that.listeners['resize'], false);

            this.listeners['readystatechange'] = _.bind(that.fitToHeight, that);
            window.addEventListener('readystatechange', that.listeners['readystatechange'], false);

            this.listeners['fullscreenchange'] = _.bind(that.fitToHeight, that);
            document.addEventListener('fullscreenchange', that.listeners['fullscreenchange'], false);
        },
        unbindViewScopedEvents: function () {
            //TODO - Put this in events object.
            $(window).off('beforeunload', this.listeners['beforeunload']);
            window.removeEventListener('focus', this.listeners['focus'], false);
            window.removeEventListener('resize', this.listeners['resize'], false);
            window.removeEventListener('readystatechange', this.listeners['readystatechange'], false);
            document.removeEventListener('fullscreenchange', this.listeners['fullscreenchange'], false);
            this.stopAutoRefresh();
        },
        close: function () {
            this.options.state = app.view_states.closed;

            //If currentModal exists, hide it and erase it.
            if (this.currentModal) {
                this.currentModal.hide();
                this.currentModal = null;
            }

            this.unbindViewScopedEvents(); 

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

            this.bindEvents();
            this.$el.show();
        },
        hide: function () {
            this.options.state = app.view_states.hidden;

            this.$el.hide();
            this.unbind();
            this.stopListening();
        },
        preRender: function () {
            app.models.subnavbar.set("subnavbar", false);
            app.models.subnavbar.setAll(false);
        },
        reRender: function () {
        },
        window_beforeUnload: function (e) {
            try {
                if (this.source.get('modified') == true) {
                    var msg =
                        'Source code may have been changed. If you leave, all unsaved' +
                        'changes will be lost. Continue anyway?';

                    return msg;
                }
            }
            catch (Error) { console.error(Error); }
        },
        tab_changed: function (e) {
            var relatedId = e.relatedTarget.attributes.getNamedItem('data-target').nodeValue
            //var relatedBtns = $(relatedId + '-buttons')
            var targetId = e.target.attributes.getNamedItem('data-target').nodeValue;
            //var targetBtns = $(targetId + '-buttons')

            if (targetId == '#online') {
                this.model.set('currentTab', 'online');
                this.onlineCode.refresh();
            } else {
                this.model.set('currentTab', 'source');
                this.editor.refresh();
            }

            //relatedBtns.hide();
            //targetBtns.show();
        },
    });

    Screen.Models.Source = Backbone.Epoxy.Model.extend({
        defaults: {
            id: null,
            code: null,
            name: null,
            statusCode: null,
            scriptId: null,
            scriptName: null,
            agentId: null,
            agentName: null,
            scanCycleTime: null,
            lastScanCycleTime: null,
            timeout: null,
            lastModificationUser: null,
            lastModificationTimestamp: null,
            equalsOnline: null,

            idOnline: null,
            nameOnline: null,
            codeOnline: null,
            userOnline: null,
            uploadOnline: null,

            scriptStatusCode: null,
            scriptStatusDescription: null,
            scriptErrorStatus: null,
            scriptErrorStatusDescription: null,

            modified: false,
        },


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


            qp.Add('@id', 'VARCHAR', options.params.id);
            qp.Add('@includeFilesMeta', 'INT', 1);
            qp.Add('@timezoneCode', 'VARCHAR', app.models.user.get("timezoneCode"));

            Core.Json.CallProcedure(
                app.DatabaseNames.IH + '.WEB.GetScriptSources',
                qp,
                {
                    onSuccess: function (resp) {
                        try {
                            if ((resp) && (resp.Table) && (resp.Table.length > 0)) {
                                var data = that.parse(resp.Table[0]);

                                if (opt.onlineRefresh) {
                                    var onlineData = _.omit(data, ['id', 'name', 'code', 'scriptId', 'scriptName', 'agentName']);

                                    that.set(onlineData, { from: 'fetch', })
                                        .trigger('fetch-online', that, resp, opt);
                                }
                                else {
                                    that.set(data, { from: 'fetch', })
                                        .trigger('fetch', that, resp, opt);
                                }

                                if (opt.success)
                                    opt.success(that, resp, opt);
                            }
                            else {
                                var errorMsg = ((resp) && (resp.Message))
                                                            ? resp.Message
                                                            : 'SERVER_RESPONSE_NOT_VALID';
                                console.error(errorMsg);

                                if (opt.error)
                                    opt.error(that, errorMsg);
                            }
                        }
                        catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                    },
                    onError: function (errorMsg) {
                        try {
                            console.error(errorMsg);

                            if (opt.error)
                                opt.error(that, errorMsg);
                        }
                        catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                    },
                    Async: options.async,
                    Secured: true,
                },
                app.ConnectionStrings.app
            );

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


            qp.Add('@id', 'VARCHAR', options.params.id);
            qp.Add('@timezoneCode', 'VARCHAR', app.models.user.get("timezoneCode"));

            Core.Json.CallProcedure(
                app.DatabaseNames.IH + '.WEB.OpenDmtSource',
                qp,
                {
                    onSuccess: function (resp) {
                        try {
                            if ((resp) && (resp.Table) && (resp.Table.length > 0)) {
                                var data = that.parse(resp.Table[0]);

                                if (opt.onlineRefresh) {
                                    var onlineData = _.omit(data, ['id', 'name', 'code', 'scriptId', 'scriptName', 'agentName']);

                                    that.set(onlineData, { from: 'fetch', })
                                        .trigger('fetch-online', that, resp, opt);
                                }
                                else {
                                    that.set(data, { from: 'fetch', })
                                        .trigger('fetch', that, resp, opt);
                                }

                                if (opt.success)
                                    opt.success(that, resp, opt);
                            }
                            else {
                                var errorMsg = ((resp) && (resp.Message))
                                                            ? resp.Message
                                                            : 'SERVER_RESPONSE_NOT_VALID';
                                console.error(errorMsg);

                                if (opt.error)
                                    opt.error(that, errorMsg);
                            }
                        }
                        catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                    },
                    onError: function (errorMsg) {
                        try {
                            console.error(errorMsg);

                            if (opt.error)
                                opt.error(that, errorMsg);
                        }
                        catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                    },
                    Async: options.async,
                    Secured: true,
                },
                app.ConnectionStrings.app
            );

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

            qp.Add('@id', 'INT', this.get('scriptId'));

            Core.Json.CallProcedure(
                app.DatabaseNames.IH + '.WEB.GetScriptLogs',
                qp,
                {
                    onSuccess: function (resp) {
                        try {
                            if ((resp) && (resp.Table) && (resp.Table.length > 0)) {
                                if (opt.success)
                                    opt.success(that, resp.Table[0].LogContent);
                            }
                            else {
                                var errorMsg = ((resp) && (resp.Message))
                                                            ? resp.Message
                                                            : 'SERVER_RESPONSE_NOT_VALID';
                                console.error(errorMsg);

                                if (opt.error)
                                    opt.error(that, errorMsg);
                            }
                        }
                        catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                    },
                    onError: function (errorMsg) {
                        try {
                            if (opt.error)
                                opt.error(that, errorMsg);
                        }
                        catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                    },
                    Async: opt.asyc,
                    Secured: true,
                },
                app.ConnectionStrings.app
            );
        },
        parse: function (obj) {
            return {
                id: obj.Id,

                code: obj.Code,
                name: obj.Name,
                statusCode: obj.StatusCode,
                scriptId: obj.ScriptId,
                scriptName: obj.ScriptName,
                agentId: obj.AgentId,
                agentName: obj.AgentName,
                scanCycleTime: obj.ScanCycleTime,
                lastScanCycleTime: obj.LastScanCycleTime,
                timeout: obj.Timeout,
                lastModificationTimestamp: obj.LastModificationTimestampString,
                lastModificationUser: obj.LastModificationUser,
                equalsOnline: obj.EqualsOnline,

                idOnline: obj.SourceIdOnline,
                nameOnline: obj.SourceNameOnline,
                codeOnline: obj.CodeOnline,
                uploadOnline: obj.LastModificationTimestampStringOnline,
                userOnline: obj.LastModificationUserOnline,

                scriptStatusCode: obj.ScriptStatusCode,
                scriptStatusDescription: obj.ScriptStatusDescription,
                scriptErrorStatus: obj.ScriptErrorStatus,
                scriptErrorStatusDescription: (obj.ScriptErrorStatus == 'WARNING') ? app.translate([app], "SCRIPT_ERROR_STATUS_DESCRIPTION_" + (obj.ScriptErrorStatusDescription).toUpperCase()) : obj.ScriptErrorStatusDescription,
            };
        },
        upsert: function (options) {
            var that = this,
                qp = new Core.Database.QueryParameters(),
                opt = _.extend({
                    async: true,
                }, options);

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

            Core.Json.CallProcedure(
                app.DatabaseNames.IH + '.WEB.UpsertDmtSource',
                qp,
                {
                    onSuccess: function (resp) {
                        try {
                            if ((resp) && (resp.Table) && (resp.Table.length > 0)) {
                                if (opt.success)
                                    opt.success(that, resp.Table[0]);
                            }
                            else {
                                var errorMsg = ((resp) && (resp.Message))
                                                            ? resp.Message
                                                            : 'SERVER_RESPONSE_NOT_VALID';
                                console.error(errorMsg);

                                if (opt.error)
                                    opt.error(that, errorMsg);
                            }
                        }
                        catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                    },
                    onError: function (errorMsg) {
                        try {
                            if (opt.error)
                                opt.error(that, errorMsg);
                        }
                        catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                    },
                    Async: opt.asyc,
                    Secured: true,
                },
                app.ConnectionStrings.app
            );
        },
        clone: function (options) {
            var that = this,
                qp = new Core.Database.QueryParameters(),
                opt = _.extend({
                    async: true,
                }, options);

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

            Core.Json.CallProcedure(
                app.DatabaseNames.IH + '.WEB.CloneSource',
                qp,
                {
                    onSuccess: function (resp) {
                        try {
                            if ((resp) && (resp.Table) && (resp.Table.length > 0)) {
                                if (opt.success)
                                    opt.success(that, resp.Table[0]);
                            }
                            else {
                                var errorMsg = ((resp) && (resp.Message))
                                                            ? resp.Message
                                                            : 'SERVER_RESPONSE_NOT_VALID';
                                console.error(errorMsg);

                                if (opt.error)
                                    opt.error(that, errorMsg);
                            }
                        }
                        catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                    },
                    onError: function (errorMsg) {
                        try {
                            if (opt.error)
                                opt.error(that, errorMsg);
                        }
                        catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                    },
                    Async: opt.asyc,
                    Secured: true,
                },
                app.ConnectionStrings.app
            );
        },
        setRaw: function (obj) {
            this.set(this.parse(obj));
        }
    });

    Screen.Models.BrowseModalSource = Screen.Models.Source.extend({
        defaults: _.extend(Screen.Models.Source.prototype.defaults, {
            curSourceId: null,
        }),
    });

    Screen.Collections.Sources = Backbone.Collection.extend({
        model: Screen.Models.BrowseModalSource,

        comparator: function (a, b) {
            var attrsA = a.toJSON(),
                attrsB = b.toJSON();

            if (attrsA.isNew) return -1;
            else if (attrsB.isNew) return 1;
            //Source online always on top
            else if (attrsA.id == attrsA.idOnline) return -1;
            else if (attrsB.id == attrsB.idOnline) return 1;
            else if (attrsA.name < attrsB.name) return -1;
            else if (attrsA.name > attrsB.name) return 1;
            else return 0;
        },

        currentPage: 1,
        fixedParameters: [],
        isFetching: false,
        pageSize: 50,
        transaction_timestamp: null,

        fetch: function (opt) {
            var that = this,
                qp = new Core.Database.QueryParameters(),
                ttimestamp = this.transaction_timestamp = new Date().getTime(),
                options = opt ? _.clone(opt) : {};

            this.isFetching = true;

            if (options.reset) {
                this.resetPagination(true);
            }

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

            if (options.reset) {
                qp.Add('@fromRow', 'INT', 0);
                qp.Add('@rowsToFetch', 'INT', this.pageSize);
            }
            else if (options.refresh) {
                qp.Add('@fromRow', 'INT', 0);
                qp.Add('@rowsToFetch', 'INT', this.currentPage * this.pageSize);
            }
            else {
                qp.Add('@fromRow', 'INT', (this.currentPage - 1) * this.pageSize);
                qp.Add('@rowsToFetch', 'INT', this.pageSize);
            }

            Core.Json.CallProcedure(
                app.DatabaseNames.IH + '.WEB.GetScriptSources',
                qp,
                {
                    onSuccess: function (resp) {
                        try {
                            //checking transaction timestamp
                            if (ttimestamp != that.transaction_timestamp)
                                return;

                            if ((resp) && (resp.Table)) {
                                var records = resp.Table,
                                    newColl;

                                newColl = _.map(records, that.model.prototype.parse);

                                var method = ((options.refresh) || ((options.reset))) ? 'set' : 'add';

                                //checking transaction timestamp
                                if (ttimestamp != that.transaction_timestamp)
                                    return;

                                if (!options.reset) {
                                    //var editingItems = _.where(that.toJSON(), { isNew: true });
                                    var editingItems = _.where(that.toJSON(), { editing: true });

                                    [].push.apply(newColl, editingItems);
                                }

                                if (options.onlineLabel) {
                                    var onlineSource = _.find(newColl, function (source) {
                                        return source.id == source.idOnline;
                                    });
                                    if (onlineSource)
                                        onlineSource.name = onlineSource.name + options.onlineLabel;
                                }

                                that[method](newColl, { from: 'fetch' })
                                    .trigger('fetch', that, records);

                                that.isFetching = false;
                            }
                            else {
                                if ((resp) && (resp.Message))
                                    console.error(resp.Message);
                                else
                                    console.error('Server response not valid.');
                            }
                        }
                        catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                    },
                    Async: options.async,
                    Secured: true,
                },
                app.ConnectionStrings.app
            );

            return this;
        },
        getNextPage: function (options) {
            if (!this.isFetching) {
                this.currentPage++;
                options = (_.isObject(options)) ? options : {};
                this.fetch(options);
            }
        },
        removeItem: function (id, success, error) {
            var model = this.get(id);
            if (model) {
                var that = this;
                var QP = new QueryParameters();

                QP.Add('@Id', 'INT', id);

                Core.Json.CallProcedure(app.DatabaseNames.IH + '.WEB.RemoveSource', QP, {
                    onSuccess: function (data) {
                        try {
                            if (data && data.Table && data.Table.length > 0) {
                                var data = data.Table[0];

                                if (data.Id) {
                                    if (success != null && _.isFunction(success))
                                        success.call(this, that, data.Id);
                                }
                                else {
                                    if (data.Message)
                                        error.call(this, that, data.Message);
                                    else
                                        error.call(this, that);
                                }
                            } else {
                                if (data.Message)
                                    error.call(this, that, data.Message);
                                else
                                    error.call(this, that);
                            }
                        }
                        catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                    },
                    onError: function (data) {
                        try {
                            if (error != null && _.isFunction(error))
                                error.call(this, that);
                        }
                        catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                    },
                    Async: true,
                    Secured: true,
                }, app.ConnectionStrings.app);
            }
        },
        resetPagination: function (force) {
            if (!this.isFetching || force == true) {
                this.currentPage = 1;
            } else {
                _.delay(this.resetPagination, 100);
            }
        },
        upsertItem: function (id, props, success, error) {
            var that = this;
            var qp = new QueryParameters();

            qp.Add('@id', 'INT', id);

            for (var q in props) {
                qp.Add(props[q].name, props[q].type, props[q].value);
            }

            Core.Json.CallProcedure(
                app.DatabaseNames.IH + '.WEB.UpsertDmtSource',
                qp,
                {
                    onSuccess: function (data) {
                        try {
                            if (data && data.Table) {
                                var data = data.Table;
                                var ids = _.pluck(data, 'Id');

                                if (success != null && _.isFunction(success))
                                    success.call(this, that, ids);
                            } else {
                                if (data.Message)
                                    error.call(this, that, data.Message);
                                else
                                    error.call(this, that);
                            }
                        }
                        catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                    },
                    onError: function (data) {
                        try {
                            if (error != null && _.isFunction(error))
                                error.call(this, that);
                        }
                        catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                    },
                    Async: true,
                    Secured: true,
                },
                app.ConnectionStrings.app
            );
        },
    });

    Screen.Models.SourceFile = Backbone.Epoxy.Model.extend({
        defaults: {
            id: null,
            name: null,
            //content: null,
        },

        parse: function (obj) {
            return {
                id: obj.Id,

                //content: obj.Content,
                name: obj.Name,
            };
        },
    });

    Screen.Collections.SourceFiles = Backbone.Collection.extend({
        model: Screen.Models.SourceFile,

        comparator: 'name',

        removeItems: function (options) {
            var that = this,
                opt = _.extend({}, { 
                    params: {},
                }, options);


            callApi(
                'RemoveSourceFiles',
                {
                    id: opt.params.id,
                    fileIds: opt.params.fileIds,
                },
                function (resp, msg, msgParams) {
                    try {
                        if ((resp) && (resp.Data) && (resp.Data.Table) && (resp.Data.Table.length > 0)) {
                            _.each(resp.Data.Table, function (obj) {
                                that.remove(obj.Id);
                            });

                            if (opt.success != null)
                                opt.success(that, resp, msg, msgParams);
                        }
                        else {
                            if (!msg)
                                msg = 'SERVER_RESPONSE_NOT_VALID';

                            console.error(msg);

                            if (opt.error != null)
                                opt.error(that, msg, msgParams);
                        }
                    }
                    catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                },
                function (msg, msgParams) {
                    try {
                        if (opt.error != null)
                            opt.error(that, msg, msgParams);
                    }
                    catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                }
            );
        },
    });

    Screen.Models.Message = Backbone.Epoxy.Model.extend({
        defaults: {
            timestamp: null,
            message: null,
            //content: null,
        },
        idAttribute: 'timestamp',

        parse: function (obj) {
            return {
                timestamp: obj.Timestamp,

                //content: obj.Content,
                message: obj.Message,
            };
        },
    });

    Screen.Collections.Messages = Backbone.Collection.extend({
        model: Screen.Models.Message,

        comparator: function (a, b) {
            var attrsA = a.toJSON(),
                attrsB = b.toJSON();

            if (attrsA.timestamp < attrsB.timestamp) return 1;
            else if (attrsA.timestamp > attrsB.timestamp) return -1;
            else return 0;
        },
    });

    Screen.Models.BrowseModal = Backbone.Epoxy.Model.extend({
        defaults: {
            message: null,

            name: null,
            scriptId: null,
            curSourceId: null,
            sourceId: null,
            idOnline: null,
            nameOnline: null,

            sourceToClone: null,

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

    Screen.Views.BrowseModal = Modal.Views.SimpleModal.extend({
        className: Modal.Views.SimpleModal.prototype.className + ' browse-modal',

        i18n: null,

        items: null,
        sources: null,
        examples: null,

        actionsCellTemplate: null,
        createFromExampleCellTemplate: null,

        sourcesGrid: null,
        examplesGrid: null,

        toCloneSource: null,
        innerModal: null,
        onlineLabel: null,

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

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

            this.items = new Screen.Collections.Sources();
            this.sources = new Screen.Collections.Sources();
            this.examples = new Screen.Collections.Sources();
            
            this.items.fetch(
                {
                    onlineLabel: ' (' + this.onlineLabel + ')',
                    params: [
                        { Name: 'scriptId', Type: 'INT', Value: this.model.get('scriptId') },
                        { Name: 'includeExamples', Type: 'INT', Value: 1 },
                        { Name: 'includeOnline', Type: 'INT', Value: 1 },
                        { Name: 'timezoneCode', Type: 'VARCHAR', Value: app.models.user.get("timezoneCode") },
                    ],
                }
            )

            this.bindEvents();

            return this;
        },
        bindEvents: function () {
            Modal.Views.SimpleModal.prototype.bindEvents.apply(this, arguments);
            this.listenTo(this.items, 'fetch', this.collection_fetch);
        },

        render: function () {
            Modal.Views.SimpleModal.prototype.render.apply(this, arguments);

            var that = this;

            var CustomRow = Backgrid.Row.extend({
                render: function () {
                    Backgrid.Row.prototype.render.call(this)
                    if (this.model.get('statusCode') == "ONLINE") {
                        this.$el.addClass('onlineRow');
                    } else {
                        this.$el.removeClass('onlineRow');
                    }
                    return this
                }
            })

            var sourcesGrid = this.sourcesGrid = new Backgrid.Grid({
                className: 'backgrid table table-hover',
                row: CustomRow,
                columns: [
                    {
                        name: 'name',
                        label: app.translate([this, app], 'name_col_label'),
                        editable: false,
                        sortable: false,
                        cell: Backgrid.StringCell.extend({
                            orderSeparator: '',
                            className: 'string-cell align-center-cell name',
                            enterEditMode: function () {
                                var model = this.model;
                                var column = this.column;

                                var editable = (this.model.get('isNew'));

                                if (editable) {
                                    this.currentEditor = new this.editor({
                                        column: this.column,
                                        model: this.model,
                                        formatter: this.formatter
                                    });

                                    model.trigger("backgrid:edit", model, column, this, this.currentEditor);

                                    // Need to redundantly undelegate events for Firefox
                                    this.undelegateEvents();
                                    this.$el.empty();
                                    this.$el.append(this.currentEditor.$el);
                                    this.currentEditor.render();
                                    this.$el.addClass("editor");

                                    model.trigger("backgrid:editing", model, column, this, this.currentEditor);
                                }
                            },
                        }),
                    },
                    {
                        name: 'lastModificationTimestamp',
                        label: app.translate([this, app], 'timestamp_col_label'),
                        editable: false,
                        sortable: false,
                        cell: Backgrid.Extension.MomentCell.extend({
                            className: 'moment-cell align-center-cell lastModificationTimestamp',
                            modelFormat: 'YYYY-MM-DD HH:mm:ss',
                            displayFormat: 'YYYY-MM-DD HH:mm:ss',
                        }),
                    },
                    {
                        name: 'lastModificationUser',
                        label: app.translate([this, app], 'user_col_label'),
                        editable: false,
                        sortable: false,
                        cell: Backgrid.StringCell.extend({
                            orderSeparator: '',
                            className: 'string-cell align-center-cell',
                        }),
                    },
                    {
                        name: 'specialProperties',
                        label: "",
                        editable: false,
                        sortable: false,
                        cell: Backgrid.Cell.extend({
                            initialize: function () {
                                Backgrid.Cell.prototype.initialize.apply(this, arguments);
                            },
                            render: function () {
                                Backgrid.Cell.prototype.render.apply(this, arguments);

                                var onlineSourceLabel = app.translate([that, app], 'fa_online_source_label');
                                var currentSourceLabel = app.translate([that, app], 'fa_current_source_label');

                                if (this.model.get('id') == this.model.get('idOnline')) {
                                    this.$el.append($('<i class="fa fa-check" title="' + onlineSourceLabel + '" style="font-size:20px;"></i>'));
                                }

                                if (this.model.get('id') == this.model.get('curSourceId')) {
                                    this.$el.append($('<i class="fa fa-pencil" title="' + currentSourceLabel + '" style="font-size:20px;"></i>'));
                                }

                                return this;
                            },
                        }),
                    },
                    {
                        name: "configure",
                        label: "",
                        sortable: false,
                        editable: false,
                        //renderable: (_.isBoolean(ctx.editable)) ? ctx.editable : false,
                        headerCell: Backgrid.HeaderCell.extend({
                            className: "configuretd",
                        }),
                        cell: Backgrid.Cell.extend({
                            template: Handlebars.compile(that.actionsCellTemplate),
                            className: 'actions-cell',
                            events: {
                                'click .btn-cell-action': 'cellActionClick',

                                "click .btn-dropdown-config-option": "configure_dropdown_config_opt_click",
                                "click .btn-dropdown-action-option": "configure_dropdown_action_opt_click",
                            },

                            initialize: function () {
                                Backgrid.Cell.prototype.initialize.apply(this, arguments);

                                this.bindEvents();
                            },
                            render: function () {
                                Backgrid.Cell.prototype.render.apply(this, arguments);
                                this.$el.html(this.template(this.model.toJSON()));

                                if (this.model.get("editing")) {
                                    this.render_newRow();
                                } else {
                                    this.render_actions();
                                }

                                this.delegateEvents();
                                return this;
                            },
                            render_newRow: function () {
                                //Preventing a bug of hiding the modal when
                                //the mouse leaves one of the action buttons
                                this.$el.find('[data-toggle="tooltip"]').on('hidden', function () {
                                    return false;
                                });

                                this.$el.find('[data-toggle="tooltip"]').tooltip();

                                //this.$el.find('.new-row-btns').removeClass('hide');
                                //this.$el.find('.old-row-btns').addClass('hide');

                                return this;
                            },
                            render_actions: function () {
                                this.$el.find("[data-toggle=popover]").popover({
                                    grid: that.sourcesGrid,
                                });

                                //this.$el.find('.new-row-btns').addClass('hide');
                                // this.$el.find('.old-row-btns').removeClass('hide');

                                //if (!ctx.editable) {
                                //  this.$el.find(".configure-source-btn").attr("disabled", true);
                                //}

                                this.checkAnyOptions();
                                return this;
                            },
                            bindEvents: function () {
                                this.listenTo(this.model, 'change:editing change:isLoading change:hasUnsavedClone', this.checkMode);
                            },
                            /////////////////////////////////////////////////////////////////////////////////////
                            checkMode: function () {
                                this.render();
                            },
                            cellActionClick: function (e) {
                                try {
                                    var target = $(e.target);
                                    var actionData = target.closest('[data-cell-action]').data('cell-action');
                                    if (actionData) {
                                        that.actionOnRow(this.model, actionData);
                                    }
                                }
                                catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                            },
                            /////////////////////////////////////////////////////////////////////////////////////
                            checkAnyOptions: function () {
                                var actionSourceBtn = this.$el.find(".action-source-btn");
                                if (this.$el.find(".action-source-dropdown").find("li").length > 0) {
                                    actionSourceBtn.attr("disabled", false);
                                } else {
                                    actionSourceBtn.attr("disabled", true);
                                }
                            },
                            configure_dropdown_config_opt_click: function (e) {
                                e.preventDefault();
                                if (!$(e.target).hasClass("disabled-link")) {
                                    that.configureSource(this, this.model, { option: $(e.target).data("option") });
                                }
                            },
                        }),
                    },
                ],
                collection: this.sources,
            });



            var examplesGrid = this.examplesGrid = new Backgrid.Grid({
                className: 'backgrid table table-hover',
                columns: [
                    {
                        name: 'name',
                        label: app.translate([this, app], 'name_col_label'),
                        editable: false,
                        sortable: false,
                        cell: Backgrid.StringCell.extend({
                            orderSeparator: '',
                            className: 'string-cell align-center-cell',
                            enterEditMode: function () {
                                var model = this.model;
                                var column = this.column;

                                var editable = (this.model.get('isNew'));

                                if (editable) {
                                    this.currentEditor = new this.editor({
                                        column: this.column,
                                        model: this.model,
                                        formatter: this.formatter
                                    });

                                    model.trigger("backgrid:edit", model, column, this, this.currentEditor);

                                    // Need to redundantly undelegate events for Firefox
                                    this.undelegateEvents();
                                    this.$el.empty();
                                    this.$el.append(this.currentEditor.$el);
                                    this.currentEditor.render();
                                    this.$el.addClass("editor");

                                    model.trigger("backgrid:editing", model, column, this, this.currentEditor);
                                }
                            },
                        }),
                    },
                    {
                        name: "configure",
                        label: "",
                        sortable: false,
                        editable: false,
                        //renderable: (_.isBoolean(ctx.editable)) ? ctx.editable : false,
                        //headerCell: Backgrid.HeaderCell.extend({
                        //    className: "configuretd",
                        //}),
                        cell: Backgrid.Cell.extend({
                            template: Handlebars.compile(that.createFromExampleCellTemplate),
                            className: 'actions-cell align-center-cell',
                            events: {
                                'click #createBtn': 'createBtn_click',
                                'click #downloadBtn': 'downloadBtn_click',
                            },

                            initialize: function () {
                                Backgrid.Cell.prototype.initialize.apply(this, arguments);

                                this.bindEvents();
                            },
                            render: function () {
                                Backgrid.Cell.prototype.render.apply(this, arguments);
                                this.$el.html(this.template(this.model.toJSON()));

                                this.$el.find("[data-toggle=popover]").popover({
                                    grid: that.examplesGrid,
                                });

                                //if (this.model.get("editing")) {
                                //    this.render_newRow();
                                //} else {
                                //    this.render_actions();
                                //}

                                //this.delegateEvents();
                                return this;
                            },
                            //render_newRow: function () {
                            //    this.$el.find('[data-toggle="tooltip"]').tooltip();

                            //    return this;
                            //},
                            //render_actions: function () {
                            //    this.$el.find("[data-toggle=popover]").popover();

                            //    //if (!ctx.editable) {
                            //    //  this.$el.find(".configure-source-btn").attr("disabled", true);
                            //    //}

                            //    this.checkAnyOptions();
                            //    return this;
                            //},
                            bindEvents: function () {
                                this.listenTo(this.model, 'change:editing change:isLoading change:hasUnsavedClone', this.checkMode);
                            },
                            ///////////////////////////////////////////////////////////////////////////////////////
                            checkMode: function () {
                                this.render();
                            },
                            createBtn_click: function (e) {
                                try {
                                    //show sources tab
                                    var tabs = that.$el.find('a[data-toggle="tab"]');

                                    tabs.tab();

                                    $(that.$el.find('a[data-target="#sources"]')).tab('show');

                                    //add clone
                                    that.addNewClone(this.model);
                                }
                                catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                            },
                            downloadBtn_click: function (e) {
                                try {
                                    //download example
                                    that.downloadScript(this.model);
                                }
                                catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                            },
                        }),
                    },
                ],
                collection: this.examples,
            });

            this.$el.find('.sources-grid-container').append(sourcesGrid.render().el);
            this.$el.find('.examples-grid-container').append(examplesGrid.render().el);

        },
        configureSource: function (cell, model, data) {
            try {
                var menu_option = (data && data.option) ? data.option : null;

                switch (menu_option.toUpperCase()) {
                    case "OPT-CLONE":
                        this.addNewClone(model);
                        break;
                    case "OPT-DELETE":
                        var that = this;
                        this.innerModal = new Modal.Views.Main({
                                focusOk: false,
                                focusSelector: '#btn-cancel',
                                title: app.translate([this, app], "delete_item_modal_confirm_title"),
                                message: app.translate([this, app], "delete_item_modal_confirm_message"),
                                buttons_type: "CONTINUE-CANCEL",
                            });

                        this.listenToOnce(this.innerModal, "continue", function (modal) {
                            try {
                                that.removeRow(model);
                                that.innerModal = null;
                            }
                            catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                        });

                        this.innerModal.show();
                        break;
                    case "OPT-DOWNLOAD":
                        this.downloadScript(model);
                        break;
                }

            } catch (Error) { }
        },
        addNewClone: function (model) {
            var newCloneId = null;

            //Set preview scripts 'isNew' attribute to false
            this.items.forEach(function (model, index) {
                model.set('isNew', false);
                model.set('hasUnsavedClone', true);
            });

            this.items.unshift({
                id: -1,
                name: model.get('name') + app.translate([this, app], 'copy_label'),
                code: model.get('code'),
                scriptId: this.model.get('scriptId'),
                toCloneId: model.get('id'),

                isNew: true,
                editing: true,
            });

            this.collection_fetch();

            for (var i = 0; i < this.sourcesGrid.body.rows.length; i++) {
                if (this.sourcesGrid.body.rows[i].model.get('id') == -1) {
                    newCloneId = i;
                    break;
                }
            }

            if (newCloneId >= 0)
                this.sourcesGrid.body.rows[newCloneId].cells[0].enterEditMode();

        },
        
        actionOnRow: function (model, action) {
            try {
                switch (action.toUpperCase()) {
                    case 'SAVE':
                        this.saveRow(model);
                        break;
                    case 'DISCARD':
                        this.discardRow(model);
                        break;
                }
            }
            catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
        },
        discardRow: function (m) {
            //to discard just remove the model from the collection.
            m.collection.remove(m);

            //set toCloneSource to null to prevent the deleted source
            //to reapear in case the modal was opened with 'Save As' action
            this.toCloneSource = null;

            //Re-enable clone and create from template options
            this.items.forEach(function (model, index) {
                model.set('hasUnsavedClone', false);
            });

            this.collection_fetch();

        },
        saveRow: function (m) {
            try {
                var that = this;

                //If the model has the property toCloneId, it will call the procedure to clone the item
                if (m.get('toCloneId')) {
                    m.clone(
                        {
                            params: [
                                { Name: 'Id', Type: 'INT', Value: m.get('toCloneId') },
                                { Name: 'Name', Type: 'VARCHAR', Value: m.get('name') },
                                { Name: 'ScriptId', Type: 'VARCHAR', Value: m.get('scriptId') },
                                { Name: 'Code', Type: 'VARCHAR', Value: m.get('code') },
                            ],
                            success: function (ref, resp) {
                                try {
                                    m.set({ id: resp.Id, editing: false, toCloneId: null }, { avoidSync: true });

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

                                    //set toCloneSource to null to prevent the saved source
                                    //to reapear in case the modal was opened with 'Save As' action
                                    that.toCloneSource = null;

                                    //Re-enable clone and create from template options
                                    that.items.forEach(function (model, index) {
                                        model.set('hasUnsavedClone', false);
                                    });

                                    that.collection_fetch();
                                }
                                catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
                            },
                            error: function (coll, msg) {
                                app.views.topMessages.showMessage(app.translate([that, app], msg), { stay: 10 * 1000, });
                                that._refresh();
                            },
                        }
                    );
                }
            }
            catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
        },
        //save: function () {
        //    var that = this,
        //        attrs = this.model.toJSON();

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

        //        try {
        //            this.source.upsert({
        //                params: [
        //                        { Name: '@name', Type: 'VARCHAR', Value: attrs.name, },
        //                        { Name: '@scriptId', Type: 'VARCHAR', Value: attrs.scriptId, },
        //                        { Name: '@returnData', Type: 'BIT', Value: 1 },
        //                ],
        //                success: function (model, resp) {
        //                    try {
        //                        that.model.set('processing', false);
        //                        that.source.set('modified', false);

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

        //                        that.hide();
        //                        //setTimeout(_.bind(that.hide, that), 1500);
        //                    }
        //                    catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
        //                },
        //                error: function (model, errorMsg) {
        //                    try {
        //                        app.views.topMessages.showMessage(app.translate([{ options: { i18n: { 1: that.i18n, }, }, template: 1, }, 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;
        },
        hide: function () {
            if (this.innerModal)
                this.innerModal.hide();

            var result = Modal.Views.SimpleModal.prototype.hide.apply(this, arguments);
            return result;
        },
        removeRow: function (model) {
            var that = this;
            model.collection.removeItem(
                model.get('id')
                , function (coll, data) {
                    app.views.topMessages.showMessage(app.translate([that, app], 'SAVED_CHANGES_SUCCESSFUL'), { stay: 5 * 1000, });

                    that.items.remove(model);

                    that.collection_fetch();
                }
                , function (coll, msg) {
                    app.views.topMessages.showMessage(app.translate([that, app], msg), { stay: 10 * 1000, });
                }
            );
        },
        downloadScript: function (m) {
            try {
                var that = this;

                Script.download(
                    {
                        params: {
                            id: m.get('id')
                        },
                        success: function (model, data) {
                            try {
                                // Blob for saving.
                                var blob = new Blob([new Uint8Array(data.Content)], { type: 'application/octet-stream' });

                                // Tell the browser to download the file.
                                saveAs(blob, data.FileName);
                            }
                            catch (error) {
                                console.error((error.stack) ? error.stack : new Error(error).stack);
                            }
                        },
                        error: function (model, msg) {
                            try {
                                app.views.topMessages.showMessage(app.translate([that, app], msg), { stay: 5 * 1000, });
                            }
                            catch (error) { console.error((error.stack) ? error.stack : new Error(error).stack); }
                        },
                    }
                );
            }
            catch (error) {
                console.error((error.stack) ? error.stack : new Error(error).stack);
            }
        },

        acceptBtn_click: function (e) {
            try {
                this.save();
            }
            catch (e) { console.error((e.stack) ? e.stack : new Error(e).stack); }
        },
        collection_fetch: function (col, resp) {
            var that = this;
            var s = [];
            var e = [];

            this.items.each(
                function (m) {
                    if (m.get('statusCode') == 'EXAMPLE') {
                        e.push(m);
                    }
                    else {
                        s.push(m);
                    }
                }
            );

            s.forEach(function (model, index) {
                model.set('curSourceId', that.model.get('curSourceId'));
            });

            this.sources.set(s);

            this.examples.set(e);

            if (this.toCloneSource && !this.sources.findWhere({ id: -1 })) {
                this.addNewClone(this.toCloneSource);
            }
        },
    });

    //Function to call api of this module.
    function callApi(method, data, success, error) {
        $.ajax(app.foldersRoot + "/app/custom-screens/dmt/api/api.svc/" + method, {
            type: 'POST',
            data: JSON.stringify(data),
            success: function (respString) {
                var resp = JSON.parse(respString);

                if ((resp) && (resp.Success == true)) {
                    if (success) {
                        //Parse response message. Split it by '|'.
                        // - The first item will be the message.
                        // - The rest of the items, if they exist, will be parameter
                        //to be inserted into the message.
                        var msgSplit = resp.Message.split('|');

                        success(resp, msgSplit.shift(0), msgSplit);
                    }
                }
                else {
                    console.error(resp);

                    if (error) {
                        //Parse response message. Split it by '|'.
                        // - The first item will be the message.
                        // - The rest of the items, if they exist, will be parameter
                        //to be inserted into the message.
                        var msgSplit = resp.Message.split('|');

                        error(msgSplit.shift(0), msgSplit);
                    }
                }
            },
            error: function (resp) {
                console.log(resp);

                if (error)
                    error('INTERNAL_SERVER_ERROR', []);
            }
        });
    }
    function parseCommandData(obj) {
        return {
            id: obj.CommandId,
            responseText: obj.CommandResponseText,
            statusCode: obj.CommandStatusCode,
        };
    };

    //Register codemirror helpers
    (function () {
        var WORD = /[\w$]+/, RANGE = 500;

        CodeMirror.registerHelper("hint", "fixed", function (editor, options) {
            var word = options && options.word || WORD;
            var range = options && options.range || RANGE;
            var cur = editor.getCursor(), curLine = editor.getLine(cur.line);
            var end = cur.ch, start = end;
            while (start && word.test(curLine.charAt(start - 1)))--start;
            var curWord = start != end && curLine.slice(start, end);

            var fullList = options && options.list || [];
            var list = [];
            var re = new RegExp(word.source, "g");
            //for (var dir = -1; dir <= 1; dir += 2) {
            //    var line = cur.line, endLine = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir;
            //    for (; line != endLine; line += dir) {
            //        var text = editor.getLine(line), m;
            for (var i = 0, len = fullList.length; i < len; i++) {
                var text = fullList[i];
                //while (m = re.exec(text)) {
                //    if (m[0] === curWord) continue;
                if ((!curWord || text.lastIndexOf(curWord, 0) == 0)) {
                    //seen[m[0]] = true;
                    list.push(text);
                }
                //}
            }

            return { list: list, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end) };

            //var cur = editor.getCursor(),
            //    curLine = editor.getLine(cur.line);
            //var end = cur.ch, start = end;
            //while (start && word.test(curLine.charAt(start - 1)))--start;
            //var curWord = start != end && curLine.slice(start, end);

            //var filterList = [];
            //var fullList = options && options.list || [
            //    "hola",
            //    "chau",
            //];

            //var re = new RegExp(curLine, "g");

            //for (var i = 0, len = fullList.length; i < len; i++)
            //{
            //    var word = fullList[i];

            //    if (m = re.exec(word))
            //        filterList.push(word);
            //}

            //return {
            //    list: filterList,
            //    from: CodeMirror.Pos(cur.line, start),
            //    to: CodeMirror.Pos(cur.line, end),
            //};
        });

        CodeMirror.commands.autocomplete = function (cm) {
            cm.showHint({
                hint: CodeMirror.hint.fixed,
                list: Screen.Config.intellisense,
            });
        };
    })();

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

});
