//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)

function SelectText(element) {
    var doc = document
        , text = doc.getElementById(element)
        , range, selection
    ;
    if (doc.body.createTextRange) {
        range = document.body.createTextRange();
        range.moveToElementText(text);
        range.select();
    } else if (window.getSelection) {
        selection = window.getSelection();
        range = document.createRange();
        range.selectNodeContents(text);
        selection.removeAllRanges();
        selection.addRange(range);
    }
}

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

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

  'assets/libs/js/codemirror/lib/codemirror',
  'assets/libs/js/codemirror/addon/selection/active-line',
  'assets/libs/js/codemirror/addon/lint/lint',
  'assets/libs/js/codemirror/addon/hint/show-hint',
  'assets/libs/js/codemirror/addon/mode/simple',
  //'custom-screens/IHConfiguration/dot-programs/codemirror-dot',
],

function (app, T, CodeMirror) {
    var functions = [
            "and()",
            "jump",
            "or()",
            "sum()",
            "timer()",
            "truncate()",
            "xor()",
        ],
        variablesInput = [
            "DI1", "DI2", "DI3", "DI4", "DI5", "DI6", "DI7",
            "AI1", "AI2",
        ],
        variablesOutput = [
            "A1", "A2", "A3", "A4",
            "D1", "D2", "D3", "D4", "D5", "D6",
        ];

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

        var compilationErrors;

        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.defineSimpleMode("dot", {
            // The start state contains the rules that are intially used
            start: [
              // The regex matches the token, the token property contains the type
              //{ regex: /"(?:[^\\]|\\.)*?"/, token: "string" },
              { regex: /^(A|D|AI|DI|)[0-9]+?/, token: "dot-variable" },
              //{ regex: /([0-9a-zA-Z]+)(\()(.+)(\))/, token: ["dot-variable", , "dot-variable", null, "dot-variable", ] },
              // You can match multiple tokens at once. Note that the captured
              // groups must span the whole string in this case
              //{regex: /(function)(\s+)([a-z$][\w$]*)/, token: ["keyword", null, "variable-2"]},
              // Rules are matched in the order in which they appear, so there is
              // no ambiguity between this one and the one above
              //{regex: /(?:function|var|return|if|for|while|else|do|this)\b/, token: "keyword"},
              //{regex: /true|false|null|undefined/, token: "atom"},
              //{regex: /0x[a-f\d]+|[-+]?(?:\.\d+|\d+\.?\d*)(?:e[-+]?\d+)?/i, token: "number"},
              //{regex: /\/\/.*/, token: "comment"},
              //{regex: /\/(?:[^\\]|\\.)*?\//, token: "variable-3"},
              // A next property will cause the mode to move to a different state
              //{regex: /\/\*/, token: "comment", next: "comment"},
              //{regex: /[-+\/*=<>!]+/, token: "operator"},
              // indent and dedent properties guide autoindentation
              //{regex: /[\{\[\(]/, indent: true},
              //{regex: /[\}\]\)]/, dedent: true},
              //{regex: /[a-z$][\w$]*/, token: "variable"},
              // You can embed other modes with the mode property. This rule
              // causes all code between << and >> to be highlighted with the XML
              // mode.
              //{regex: /<</, token: "meta", mode: {spec: "xml", end: />>/}}
            ],
            // The multi-line comment state.
            comment: [
              { regex: /.*?\*\//, token: "comment", next: "start" },
              { regex: /.*/, token: "comment" }
            ],
            // The meta property contains global information about the mode. It
            // can contain properties like lineComment, which are supported by
            // all modes, and also directives like dontIndentStates, which are
            // specific to simple modes.
            meta: {
                dontIndentStates: ["comment"],
                lineComment: "//"
            }
        });

        CodeMirror.commands.autocomplete = function (cm) {
            cm.showHint({
                hint: CodeMirror.hint.fixed,
                list: [].concat(functions, variablesInput, variablesOutput),
            });
        }
    })();

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

    //Screen.Models.Dot = Backbone.Epoxy.Model.extend({
    //    defaults: {
    //        id: 0,
    //        name: '',
    //        friendlyName: '',
    //        description: '',
    //        //productTypeId: 0,
    //        //productTypeId: 0,


    //        value: 0,
    //        label: '',
    //    },
    //});

    //Screen.Collections.AuditMessages = Backbone.Collection.extend({
    //    model: Screen.Models.Dot,
    //});

    Screen.Models.DotProgram = Backbone.Epoxy.Model.extend({
        defaults: {
            id: null,
            name: null,
            description: null,
            sourceCode: null,
        },

        fetch: function (options) {
            var that = this,
                opt = _.extend(
                    { async: true, error: function () { }, success: function () { }, params: {}, },
                    options
                ),
                qp = new QueryParameters(),
                ttimestamp = this.transaction_timestamp = new Date().getTime();

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


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

                            if ((resp) && (resp.Table) && (resp.Table.length > 0)) {
                                var obj = that.parse(resp.Table[0]);

                                that.set(obj, { from: 'fetch', })
                                    .trigger('fetch', that, resp, options);

                                opt.success(that, resp, options);
                            }
                            else {
                                var msg = (resp.Message) ? resp.Message : 'SERVER_RESPONSE_NOT_VALID';

                                opt.error(that, msg);
                                console.error(new Error(msg).stack);
                            }
                        }
                        catch (Error) { console.error(Error.stack); }
                    },
                    onError: function (msg) {
                        try {
                            opt.error(msg);
                            console.error(new Error(msg).stack);
                        }
                        catch (Error) { console.error(Error.stack); }
                    },
                    Async: opt.async,
                },
                app.ConnectionStrings.app
            );

            return this;
        },
        parse: function (obj) {
            return {
                id: obj.Id,
                name: obj.Name,
                description: obj.Description,
                sourceCode: obj.SourceCode,
            };
        },
    });

    Screen.Models.Main = Backbone.Epoxy.Model.extend({
        defaults: {
            dotProgramId: null,
            dotPrograms: [],
            //dotId: 0,
            //dots: [],

            //hasData: false,
            //isLoading: true,
            isLoadingDotProgram: false,
            hasDataDotProgram: false,
        },
        computeds: {
            hasDotPrograms: { 
                deps: ["dotPrograms"],
                get: function (value) {
                    return ((value) && (value.length > 0));
                },
            },
            //hasDots: { 
            //    deps: ["dots"],
            //    get: function (dots) {
            //        return dots.length != 0;
            //    },
            //},
        },

        fetchDotPrograms: function () {
            var that = this;

            Core.Json.CallProcedure(
                app.DatabaseNames.IH + ".WEB.GetDotProgramsMeta",
                null,
                {
                    onSuccess: function (resp) {
                        try {
                            if ((resp) && (resp.Table)) {
                                var records = resp.Table;

                                var newItems = _.map(records, function (obj) {
                                    return {
                                        value: obj.Id,
                                        label: obj.Name,
                                    };
                                });

                                that.set("dotPrograms", newItems);
                            }
                            else {
                                if ((resp) && (resp.Message))
                                    console.error(new Error(resp.Message).stack);
                                else
                                    console.error(new Error("Server response not valid.").stack);
                            }
                        }
                        catch (Error) { console.error(Error.stack); }
                    },
                    onFailure: function (resp) {
                        console.error(resp);
                    },
                    Async: false,
                },
                app.ConnectionStrings.app
            );

            return this;
        },
        //fetchDots: function () {
        //    var that = this;

        //    Core.Json.CallProcedure(
        //        app.DatabaseNames.IH + ".WEB.GetAllDots",
        //        null,
        //        {
        //            onSuccess: function (resp) {
        //                try {
        //                    if ((resp) && (resp.Table)) {
        //                        var records = resp.Table;

        //                        var newItems = _.map(records, function (obj) {
        //                            return {
        //                                id: obj.Id,
        //                                name: obj.Name,
        //                                friendlyName: obj.FriendlyName,
        //                                description: obj.Description,
        //                                //productTypeId: obj.LocationId,
        //                                //productTypeId: obj.AgentId,

        //                                value: obj.Id,
        //                                label: obj.Name,
        //                            };
        //                        });

        //                        that.set("dots", newItems);
        //                    }
        //                    else {
        //                        if ((resp) && (resp.Message))
        //                            console.error(new Error(resp.Message).stack);
        //                        else
        //                            console.error(new Error("Server response not valid.").stack);
        //                    }
        //                }
        //                catch (Error) { console.error(Error.stack); }
        //            },
        //            onFailure: function (resp) {
        //                console.error(resp);
        //            },
        //            Async: false,
        //        },
        //        app.ConnectionStrings.app
        //    );

        //    return this;
        //},
    });

    //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 'dot-programs';
        } catch (Error) { }
    }

    Screen.Views.Main = Backbone.Epoxy.View.extend({
        template: 'dot-programs',
        id: 'dot-programs',
        title: 'Dot Programs',
        //default not cacheable, change this if you want the view to be cacheable
        // if the view is set as cacheable should also have a fetch method to reset the view without erasing the DOM.
        isCacheable: false,
        events: function () {
            return {
                'click #compileBtn': this.compileBtn_click,
            };
        },
        bindings: 'data-bind',
        autoRefresh: null,
        viewParams: null,

        codeEditor: null,
        currentCompilation: null,
        dotProgram: null,

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

            this.autoRefresh = {
                enabled: false,
                toid: null,
                every: 10 * 1000,
            };

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

            this.dotProgram = new Screen.Models.DotProgram();

            this.bindingSources = {
                dotProgram: this.dotProgram,
            };


            this.model.fetchDotPrograms();
            //.fetchDots();


            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:dotProgramId', this.model_change_pickers)
                .listenTo(this.dotProgram, 'fetch', this.dotProgram_fetch);
        },
        callDotProgramCompilerApi: function (action, data, success, error) {
            data = (data != null) ? data : {};

            if (action != null && action != undefined) _.extend(data, { action: action });

            $.ajax(app.foldersRoot + '/app/custom-screens/IHConfiguration/dot-programs/api/dotProgramCompiler.aspx', {
                type: 'POST',
                data: data,
                //dataType: 'json',
                success: success,
                error: error
            });
        },
        parseEditorErrors: function (text) {
            var found = [];

            if (this.currentCompilation) {
                for (var i = 0, len = this.currentCompilation.Errors.length; i < len; i++) {
                    var comError = this.currentCompilation.Errors[i];

                    found.push({
                        from: CodeMirror.Pos(comError.Line, 1),
                        to: CodeMirror.Pos(comError.Line, 1),
                        message: comError.Message,
                        severity: "error",
                    });
                }

                //found.push({
                //    from: CodeMirror.Pos(0, 1),
                //    to: CodeMirror.Pos(0, 10),
                //    message: "Warning 1",
                //    severity: "warning",
                //});
            }

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

            this.refreshDotProgram(opt);

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

                this.autoRefresh.toid = setTimeout(
                    function () { that._refresh(); },
                    this.autoRefresh.every
                );
            }
        },
        refresh: function (viewParams) {
            var fixedParams = _.extend({}, Screen.Models.Main.prototype.defaults, viewParams);

            if (fixedParams.dotProgramId)
                fixedParams.dotProgramId = parseInt(fixedParams.dotProgramId, 10);

            this.model.set(fixedParams);
        },
        refreshDotProgram: function (options) {
            var attrs = this.model.toJSON({ computed: true, }),
                opt = _.extend({}, options, { params: {}, });

            if (opt.refresh == true)
                this.model.set('isLoadingDotProgram', true);

            this.dotProgram.fetch(_.extend(opt, {
                params: {
                    id: attrs.dotProgramId,
                },
            }));
        },
        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/IHConfiguration/dot-programs/';

            T.render.call(this, this.template, function (tmp) {
                //getInternationalizationData
                if (!that.options.i18n) that.options.i18n = {};
                app.getI18NJed(that, that.template, function (i18nJED) {
                    //storing internationalization data
                    that.options.i18n[that.template] = i18nJED;
                    that.$el.html(tmp({
                        functions: functions,
                        variablesInput: variablesInput,
                        variablesOutput: variablesOutput,
                    }));
                    that.applyBindings();
                    that.append(thatContainer, that.$el);

                    var checkCssLoadedfn = function () {
                        var ss = document.styleSheets,
                            csslook = [
                                'assets/libs/js/codemirror/lib/codemirror.css',
                                'assets/libs/js/codemirror/addon/lint/lint.css',
                                'assets/libs/js/codemirror/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.codeEditor = CodeMirror(
                                that.$el.find('#code')[0],
                                {
                                    lineNumbers: true,
                                    mode: "dot",
                                    extraKeys: { "Ctrl-Space": "autocomplete" },
                                    gutters: ["CodeMirror-lint-markers"],
                                    lint: {
                                        getAnnotations: _.bind(that.parseEditorErrors, that),
                                    },
                                    lintOnChange: false,
                                    styleActiveLine: true,
                                    //value: 'D1 = D2 && D3' +
                                    //        '\njump(D1;"test")' +
                                    //        '\nD3 = D4 && D5' +
                                    //        '\n::test' +
                                    //        '\nD1 = D2 && D3'
                                }
                            );
                        }
                        else {
                            setTimeout(checkCssLoadedfn, 500);
                        }
                    };

                    setTimeout(checkCssLoadedfn, 1000);

                }, true, customPath);
            }, customPath, 'main_template');
        },
        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/IHConfiguration/' + 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;

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

                            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/lib/codemirror.css',
                                        'assets/libs/js/codemirror/addon/lint/lint.css',
                                        'assets/libs/js/codemirror/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.codeEditor = CodeMirror(
                                        that.$el.find('#code')[0],
                                        {
                                            lineNumbers: true,
                                            mode: "dot",
                                            extraKeys: { "Ctrl-Space": "autocomplete" },
                                            gutters: ["CodeMirror-lint-markers"],
                                            lint: {
                                                getAnnotations: _.bind(that.parseEditorErrors, that),
                                            },
                                            lintOnChange: false,
                                            styleActiveLine: true,
                                            value: that.dotProgram.get('sourceCode'),
                                            //value: 'D1 = D2 && D3' +
                                            //        '\njump(D1;"test")' +
                                            //        '\nD3 = D4 && D5' +
                                            //        '\n::test' +
                                            //        '\nD1 = D2 && D3'
                                        }
                                    );
                                }
                                else {
                                    setTimeout(checkCssLoadedfn, 500);
                                }
                            };

                            setTimeout(checkCssLoadedfn, 1000);


                            //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.
                            var fixedParams = _.extend({}, viewParams);

                            //if (fixedParams.search) 
                            //    fixedParams.search = (fixedParams.search != '-') ? fixedParams.search.replace('plus', '+') : '';

                            that.model.set(fixedParams);

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

                            that._refresh({
                                refresh: true,
                                params: _.extend(
                                    {},
                                    params,
                                    {}
                                ),
                            });
                        },
                        true,
                        customPath
                    );
                },
                customPath
            );
        },
        startAutoRefresh: function () {
            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(); },
                        1
                    );
                }
            } catch (Error) { console.error(Error.stack); }
        },
        stopAutoRefresh: function () {
            if (this.autoRefresh.toid != null) {
                clearTimeout(this.autoRefresh.toid);
                this.autoRefresh.toid = null;
            }
            this.autoRefresh.enabled = false;
        },

        compileBtn_click: function (e) {
            try {
                var that = this;

                this.callDotProgramCompilerApi(
                    'COMPILE',
                    {
                        sourceCode: JSONEncode({
                            Code: this.codeEditor.getValue(),
                        }),
                    },
                    function (resp) {
                        try {
                            if ((resp) && (resp.Success == true) && (resp.Data)) {
                                var data = resp.Data;

                                that.currentCompilation = data;

                                that.codeEditor.performLint();


                                var byteCodesStr = '';//'Bytes code:\r\n';
                                //var byteCodesStr = 'char myArray[] = {';
                                var framesStr = '';


                                if (that.currentCompilation.Errors.length == 0) {
                                    for (var i = 0, len = data.BytesCode.length; i < len; i++) {
                                        byteCodesStr += '[' + i + '] = ' + data.BytesCode[i] + '\r\n';
                                        //byteCodesStr += '\r\n' + data.BytesCode[i] + ((i != data.BytesCode.length - 1) ? ',' : '');
                                    }

                                    for (var i = 0, len = data.Frames.length; i < len; i++) {
                                        framesStr += data.Frames[i] + '\r\n';
                                    }

                                    //byteCodesStr += '\r\n};'
                                }

                                $('#bytesCode').val(byteCodesStr);
                                $('#frames').val(framesStr);
                            }
                            else {
                                if ((resp) && (resp.Message))
                                    console.error(resp.Message);
                                else
                                    console.error('Server response not valid.');
                            }
                        }
                        catch (Error) { console.error(Error.stack); }
                    }
                )
            }
            catch (Error) { console.error(Error.stack); }
        },
        model_change_pickers: function (model) {
            try {
                var params = this.model.toJSON();

                app.router.navigate(
                    app.router.resolveURL(
                        app.router.currentModule,
                        _.extend(
                            {},
                            params,
                            {
                            }
                        ),
                        false
                    ),
                    { trigger: false, }
                );

                this._refresh(_.extend(
                    {},
                    params,
                    {
                    }
                ));
            }
            catch (Error) { console.error(Error.stack); }
        },
        dotProgram_fetch: function (model, resp) {
            try {
                this.codeEditor.getDoc().setValue(this.dotProgram.get('sourceCode'));
            }
            catch (Error) { console.error(Error.stack); }
        },

        append: function (container, el) {
            el = (el != null && el != undefined) ? el : this.$el;

            if (this.options.state == app.view_states.loading
                || this.options.state == app.view_states.shown) {
                //appending view to the main container and set state to shown

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

                this.options.onappend(this);
            }

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

            if (this.options.state == app.view_states.closed) {
                //return without appending.
                return;
            }
        },
        bindViewScopedEvents: function () {
            var that = this;
        },
        unbindViewScopedEvents: function () {

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

            this.stopAutoRefresh();

            this.closeSubviews();
            this.remove();
            this.unbindViewScopedEvents();
            this.unbind();
        },
        closeSubviews: function () {
            _.each(this.subviews, function (sview) {
                sview.close();
            });
        },
        show: function () {
            this.options.state = app.view_states.shown;

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

            this.hideSubviews();

            this.$el.hide();
            this.unbind();
            this.stopListening();
        },
        hideSubviews: function () {
            _.each(this.subviews, function (sview) {
                sview.hide();
            });
        },
        preRender: function () {
            app.models.subnavbar.set("subnavbar", false);
        },
        reRender: function (viewParams) {
            try {
                this.refresh(viewParams);
            } catch (Error) { }
        },
        startAutoRefresh: function () {
            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(); },
                        1
                    );
                }
            } catch (Error) { console.error(Error.stack); }
        },
        stopAutoRefresh: function () {
            if (this.autoRefresh.toid != null) {
                clearTimeout(this.autoRefresh.toid);
                this.autoRefresh.toid = null;
            }
            this.autoRefresh.enabled = false;
        },
    });

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

});