//SCREEN-BOILERPLATE

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

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

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

  "js/codemirror-5.22.0/lib/codemirror", 
  "js/ih-runtime-lib",
  "custom-screens/screen-builder/ih-lib",
  "custom-screens/screen-builder/sb-environment",
  "highcharts",
  "moment",
  "modules/dateTimeControl",

  //others
  "js/codemirror-5.22.0/mode/css/css",
  "js/codemirror-5.22.0/mode/javascript/javascript",
  "js/codemirror-5.22.0/mode/xml/xml",
  "js/codemirror-5.22.0/mode/htmlmixed/htmlmixed",
  "custom-screens/screen-builder/editor/api/js/jquery-ui.min",
  "custom-screens/screen-builder/editor/api/js/nicEdit",
  "custom-screens/screen-builder/editor/api/js/interact",
  //"custom-screens/screen-builder/editor/api/js/interact.min",
],

function (app, T, Modal, CodeMirror, IHRuntime, DataAPI, SBEnvironment, Highcharts, moment, DatetimeControl
) {

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

    ScreenBuilder.Models.Main = Backbone.Model.extend({
        defaults: {
            fullscreen: false,
            guidsParsed: false,

            omitTags: ["script", "style", "link", "noscript", "meta", "title"],
        }
    });

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

    ScreenBuilder.Views.Main = Backbone.View.extend({
        template: "editor"
        , id: "editor"
        , title: "Screen 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
        , initialize: function () {
            this.options.state = app.view_states.loading;
            this.options.onappend = (_.isFunction(this.options.onappend)) ? this.options.onappend : function () { };

            var screenId = null; 
            if (this.options.viewParams) {
                screenId = (this.options.viewParams.id) ? this.options.viewParams.id : null; 
            }

            this.options.environment = null; 

            this.options.listeners = {};
            this.options.editor = null;
            this.options.firstEditorValue = true; 
            this.options.frameScroll = {
                top: 0,
                left: 0, 
            }; 

            var model = new ScreenBuilder.Models.Main({
                id: screenId, 
            });

            this.model = model;

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

        events: {
            "click .btn-expand-frame": "expandFrame",
            "click .btn-collapse-frame": "collapseFrame",
            "click .btn-add-image": "openAddImageSelector",
            "click .btn-add-tag": "openAddTagSelector",
            "click .btn-add-chart": "openChartBuilder",
            "click .btn-add-text": "openAddText",
            "click .btn-open-preview": "openPreviewWindow",
        }, 

        render: function (container) {
            var that = this;
            var thatContainer = (container != null && container != undefined) ? container : this.options.container;
            this.options.MYREFERENCES = {};

            //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/screen-builder/editor/";

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

                    //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")
                    //end
                    //appending view to the main container 

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

                    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.cssLoaded(); 
                        }
                        else {
                            setTimeout(_.bind(checkCssLoadedfn, that), 500);
                        }
                    };

                    that.options.environment = new SBEnvironment.Class({
                        frame: that.$el.find('.preview-frame'),
                        screenId: that.model.get("id"),
                        mode: SBEnvironment.Enums.Modes.Editor, 
                    }); 

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

                    setTimeout(_.bind(checkCssLoadedfn, that), 1000);
                }, true, customPath); 
            }, customPath, "main_template");
        }
        , cssLoaded: function () {
            var that = this;
            that.$el.find(".preview-frame").load(_.bind(that.frameLoaded, that));
            that.readFromFile(this.model.get("id"), true, true, true, function () {
                that.options.editor.clearHistory(); 
            });
        }
        , instanceCodeMirror: function () {
            var that = this;

            CodeMirror.defineExtension("autoFormatRange", function (from, to) {
                var cm = this;
                var outer = cm.getMode(), text = cm.getRange(from, to).split("\n");
                var state = CodeMirror.copyState(outer, cm.getTokenAt(from).state);
                var tabSize = cm.getOption("tabSize");

                var out = "", lines = 0, atSol = from.ch == 0;
                function newline() {
                    out += "\n";
                    atSol = true;
                    ++lines;
                }

                for (var i = 0; i < text.length; ++i) {
                    var stream = new CodeMirror.StringStream(text[i], tabSize);
                    while (!stream.eol()) {
                        var inner = CodeMirror.innerMode(outer, state);
                        var style = outer.token(stream, state), cur = stream.current();
                        stream.start = stream.pos;
                        if (!atSol || /\S/.test(cur)) {
                            out += cur;
                            atSol = false;
                        }
                        if (!atSol && inner.mode.newlineAfterToken &&
                            inner.mode.newlineAfterToken(style, cur, stream.string.slice(stream.pos) || text[i + 1] || "", inner.state))
                            newline();
                    }
                    if (!stream.pos && outer.blankLine) outer.blankLine(state);
                    if (!atSol) newline();
                }

                cm.operation(function () {
                    cm.replaceRange(out, from, to);
                    for (var cur = from.line + 1, end = from.line + lines; cur <= end; ++cur)
                        cm.indentLine(cur, "smart");
                });
            });

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

            var mixedMode = {
                name: "htmlmixed",
                scriptTypes: [{
                    matches: /\/x-handlebars-template|\/x-mustache/i,
                    mode: null
                },
                {
                    matches: /(text|application)\/(x-)?vb(a|script)/i,
                    mode: "vbscript"
                }]
            };

            this.options.editor = null;

            this.options.editor = CodeMirror.fromTextArea(that.$el.find(".editor-container")[0], {
                lineNumbers: true,
                mode: "htmlmixed",
                //viewportMargin: Infinity, 
            });

            CodeMirror.commands.save = function (a, b, c) {
            }; 

            this.options.editor.on("changes", _.bind(that.editorChanged, that));
            this.options.editor.on("changes", _.bind(that.editorSetValue, that));

            this.enableEditorScreen(true); 
        }
        , enableEditorScreen: function (v) {
            if ((_.isBoolean(v) && v == true) || !_.isBoolean(v)) {                
                this.$el.find(".btn-preview-toolbar").attr("disabled", false);
            } else if (v == false) {
                this.$el.find(".btn-preview-toolbar").attr("disabled", true);
            }
        }
        , refreshFrame: function (path) {
            var that = this; 
            var frame = that.$el.find(".preview-frame");

            //the guids will be erased
            this.model.set("guidsParsed", false);

            //adding milliseconds time to avoid cache.
            var originalPath = path; 
            path = path + "?v=" + new Date().getTime().toString();

            var cw = $(frame[0].contentWindow);

            //trying to keep scroll pos
            //this.options.frameScroll.top = cw.scrollTop();
            //this.options.frameScroll.left = cw.scrollLeft();    

            frame[0].contentWindow.location.replace(path);
            //frame.attr("src", path);
            //frame[0].contentWindow.location.reload(true);
        }
        , frameLoaded: function(){
            var that = this; 
            var frame = this.$el.find(".preview-frame");
            var cw = $(frame[0].contentWindow);
            cw.scrollTop(this.options.frameScroll.top).scrollLeft(this.options.frameScroll.left);

            //that.injectEnvironmentToFrame();
            that.options.environment.injectEnvironmentToFrame();
            that.injectEditionEnvironmentToFrame();
        }
        , injectEditionEnvironmentToFrame: function () {
            try{
                var that = this; 
                //applies several tweaks to make the iframe editable (with drag and drop), zoomed out when collapse, etc.
                var frame = this.$el.find(".preview-frame");
                //frame.hide(); 
                var contents = frame.contents();
                var head = contents.find("head"); 
                var body = contents.find("body"); 

                var zoomValue = "0.5"; 
                head.append(
                    ('<style type="text/css">.editor-editr-zoomed-out{-moz-transform: scale({zoomValue}, {zoomValue});-webkit-transform: scale({zoomValue}, {zoomValue});' +
                    '-o-transform: scale({zoomValue}, {zoomValue});-ms-transform: scale({zoomValue}, {zoomValue});transform: scale({zoomValue}, {zoomValue});' +
                    '-moz-transform-origin: top left;-webkit-transform-origin: top left;-o-transform-origin: top left;' +
                    '-ms-transform-origin: top left;transform-origin: top left;};</style>').replace(/{zoomValue}/g, zoomValue)
                );

                head.append(
                    ('<style type="text/css">' +
                    '.editor-editr-draggable-elem { cursor: move; }' +
                    '</style>')
                );

                that.injectDraggable(frame);
                that.injectResizable(frame);
                that.injectInlineToolbar(frame); 

                that.$el.find(".btn-preview-toolbar").filter("*[data-toggle='tooltip']").each(function(){
                    $(this).tooltip({
                        placement: $(this).data("placement") ? $(this).data("placement") : 'left',
                    });
                }); 

                this.checkFrameZoom();
                //frame.show();
            } catch (error) {
                console.log("Error while trying to load frame environment. Exception: [" + error.toString() + "]");
            }
        }
        , injectInlineToolbar: function (frame) {
            var that = this;

            var customPath = "/app/custom-screens/screen-builder/editor/";

            T.render.call(this, this.template, function (toolbar_template) {

                //var toolbar_template = Handlebars.compile(
                //    this.$el.find("#screen_builder_editr_inline_toolbar_template").html()
                //);
                //var $toolbarTemplate = $("<div/>").html(toolbar_template).contents(); 
                //frame.contents().find("body").children(":not(script, style)").append($toolbarTemplate);

                frame.contents().find("body").children(":not(" + that.model.get("omitTags").join(', ') + ")").each(function (inx, elem) {
                    elem = $(elem);
                    var opts = {
                        hasLink: (typeof elem.attr("data-link") !== typeof undefined && elem.attr("data-link") !== false)
                    };

                    elem.append($("<div/>").html(toolbar_template(opts)).contents());
                });

                //frame.contents().find("body").find(".editor-editr-inline-toolbar-btn").tooltip({
                //});

                frame.contents().find("body").find(".editor-editr-inline-toolbar-btn").click(function (e) {
                    var target = $(e.target).closest("[data-editor-editr-toolbar-btn-action]");
                    var parent = target.closest('[data-editor-elem-guid]');
                    var action = target.data("editor-editr-toolbar-btn-action");
                    action = _.isString(action) ? action : "";

                    switch (action.toUpperCase()) {
                        case "REMOVE":
                            that.inlineToolbarRemove(parent);
                            break;
                        case "LINK":
                            that.inlineToolbarAddLink(parent);
                            break;
                        case "UNLINK":
                            that.inlineToolbarRemoveLink(parent);
                            break;
                        case "EDIT":
                            that.inlineToolbarEdit(parent);
                            break;
                        case "MOVE-UP":
                            that.inlineToolbarMoveDirection(parent, "UP");
                            break;
                        case "MOVE-DOWN":
                            that.inlineToolbarMoveDirection(parent, "DOWN");
                            break;
                    }
                });

            }, customPath, "screen_builder_editr_inline_toolbar_template");
        }
        , inlineToolbarRemove: function (elem) {
            var elem_editor_guid = elem.data("editor-elem-guid");
            var editor = this.options.editor; 
            var edtr = this.getEdtr();

            if (elem_editor_guid) {
                var interactive_elem = edtr.find('*[data-editor-elem-guid="' + elem_editor_guid.toString() + '"]');
                if (interactive_elem.length > 0) {
                    interactive_elem.remove();
                    elem.remove(); 
                    editor.setValue(this.parseEdtrHtml(edtr));
                }
            } else { console.log("Error while trying to remove element. Element guid was not found."); }
        }
        , inlineToolbarAddLink: function (elem) {
            var that = this; 
            var elem_editor_guid = elem.data("editor-elem-guid");
            var editor = this.options.editor;
            var edtr = this.getEdtr();

            if (elem_editor_guid) {
                var interactive_elem = edtr.find('*[data-editor-elem-guid="' + elem_editor_guid.toString() + '"]');
                if (interactive_elem.length > 0) {

                    var modal = new ScreenBuilder.Views.AddLinkSelectorModal({
                        id: this.model.get("id"),
                        events: _.extend(ScreenBuilder.Views.AddLinkSelectorModal.prototype.events, {
                        }),
                    });

                    modal.on("linkSelected", function (type, value) {
                        that.once("fileWritten", function (args) {
                            that.readFromFile(that.model.get("id"), true, true, true);
                        });
                        interactive_elem = that.addLink(interactive_elem, type, value);
                        editor.setValue(that.parseEdtrHtml(edtr));
                    });

                    modal.show();
                }
            } else { console.log("Error while trying to add link to element. Element guid was not found."); }
        }
        , addLink: function (elem, type, link) {
            var value = "";
            switch (type) {
                case "URL":
                    value = ("url: {{link}}");
                    break;
                case "TRENDINGVIEW":
                    value = ("trending: {{link}}");
                    break;
                case "SYSTEMSCREEN":
                    value = ("system: {{link}}");
                    break;
            }

            value = value.replace("{{link}}", link);
            elem.attr("data-link", value);

            return elem;
        }
        , inlineToolbarRemoveLink: function (elem) {
            var that = this;
            var elem_editor_guid = elem.data("editor-elem-guid");
            var editor = this.options.editor;
            var edtr = this.getEdtr();

            if (elem_editor_guid) {
                var interactive_elem = edtr.find('*[data-editor-elem-guid="' + elem_editor_guid.toString() + '"]');
                if (interactive_elem.length > 0) {
                    that.once("fileWritten", function (args) {
                        that.readFromFile(that.model.get("id"), true, true, true);
                    });

                    interactive_elem = that.removeLink(interactive_elem);
                    editor.setValue(that.parseEdtrHtml(edtr));
                }
            } else { console.log("Error while trying to add link to element. Element guid was not found."); }
        }
        , removeLink: function (elem) {
            elem.removeAttr("data-link"); 
            return elem; 
        }
        , inlineToolbarEdit: function (elem) {
            var that = this;
            var elem_editor_guid = elem.data("editor-elem-guid");
            var editor = this.options.editor;
            var edtr = this.getEdtr();

            if (elem_editor_guid) {
                var interactive_elem = edtr.find('*[data-editor-elem-guid="' + elem_editor_guid.toString() + '"]');
                if (interactive_elem.length > 0) {
                    var type = that.getElementEditorType(interactive_elem); 
                    switch (type) {
                        case "TAGVIEWER":
                            //make edit mode
                            that.openAddTagSelector("EDIT", interactive_elem, edtr); 
                            break;
                        case "IMAGEVIEWER":
                            //make edit mode
                            that.openAddImageSelector("EDIT", interactive_elem, edtr); 
                            break;
                        case "CHARTVIEWER":
                            //make edit mode
                            break;
                        case "TEXT":
                            //make edit mode
                            that.openAddText("EDIT", interactive_elem, edtr); 
                            break; 
                    }
                }
            } else { console.log("Error while trying to add link to element. Element guid was not found."); }
        }
        , inlineToolbarMoveDirection: function (elem, direction) {
            try {
                var that = this;
                direction = (_.isString(direction)) ? direction.toUpperCase() : "UP"; 
                var lh = that.parseZIndexOnElements();
                var highest = lh.high; 
                var lowest = lh.low; 

                var elem_editor_guid = elem.data("editor-elem-guid");
                var editor = this.options.editor; 
                var edtr = lh.edtr; 

                if (elem_editor_guid) {
                    var interactive_elem = edtr.find('*[data-editor-elem-guid="' + elem_editor_guid.toString() + '"]');
                    if (interactive_elem.length > 0) {
                        
                        var currentzindex = parseInt(that.getElementZIndex(interactive_elem), 10); 
                        var zIndexedEdtr = edtr.find("body").children(":not(" + that.model.get("omitTags").join(', ') + ")").filter(function (inx, e) {
                            return that.getElementZIndex(e) !== "auto" && (_.isNumber(that.getElementZIndex(e)) || (_.isString(that.getElementZIndex(e)) && that.getElementZIndex(e).trim().length > 0))
                                && (
                                    (direction == "DOWN" && (parseInt(that.getElementZIndex(e), 10) < currentzindex))
                                    || (direction == "UP" && (parseInt(that.getElementZIndex(e), 10) > currentzindex))
                                )
                                && $(e).data("editor-elem-guid") != elem_editor_guid.toString();
                        }); 

                        if (direction == "UP"){
                            zIndexedEdtr.each(function(inx, elem){
                                $elem = $(elem); 
                                $elem.css({ "z-index": (parseInt(that.getElementZIndex($elem), 10) - 1).toString() }); 
                            }); 

                            interactive_elem.css({ "z-index": highest }); 
                            elem.css({ "z-index": highest }); 
                        }else if (direction == "DOWN"){
                            zIndexedEdtr.each(function(inx, elem){
                                $elem = $(elem); 
                                $elem.css({ "z-index": (parseInt(that.getElementZIndex($elem), 10) + 1).toString() }); 
                            }); 

                            interactive_elem.css({ "z-index": lowest }); 
                            elem.css({ "z-index": lowest }); 
                        }
                        
                        editor.setValue(this.parseEdtrHtml(edtr));
                    }
                } else { console.log("Error while trying to remove element. Element guid was not found."); }
            } catch (error) { console.log("Error while trying to move object up or down. Error: " + error.toString()); }
        }
        , getElementEditorType: function (elem) {
            var type = "UNKNOWN";
            var sbType = ""; 
            //sbType = (_.isString(elem.data("sb-type")) && elem.data("sb-type").length > 0) ? elem.data("sb-type") : elem.attr("data-sb-type");
            //sbType = (_.isString(sbType)) ? sbType.toUpperCase() : ""; 

            if (sbType == "TAGVIEWER" || elem.is("tagviewer")) {
                type = "TAGVIEWER";
            } else if (sbType == "CHARTVIEWER" || elem.is("chartviewer")) {
                type = "CHARTVIEWER";
            } else if (sbType == "IMAGEVIEWER" || elem.is("imageviewer")) {
                type = "IMAGEVIEWER";
            } else if (elem.is("span")) {
                type = "TEXT";
            }

            return type; 
        }
        , getElementZIndex: function (elem) {
            try {
                elem = elem.css ? elem : $(elem);

                var zindex = parseInt(elem.css("z-index"), 10);
                zindex = ((!isNaN(zindex)) && _.isNumber(zindex)) ? zindex : elem[0].style.zIndex; 

                return zindex; 
            } catch (error) {
                return ""; 
            }
        }
        , parseZIndexOnElements: function () {
            var that = this; 
            var editor = this.options.editor;
            var frame = this.$el.find(".preview-frame");
            var edtr = this.getEdtr();
            var frame_contents = frame.contents();

            var zIndexed = edtr.find("body").children(":not(" + that.model.get("omitTags").join(', ') + ")").filter(function (inx, e) {
                var zindex = that.getElementZIndex(e);
                try{
                    return _.isNumber(zindex) || (_.isString(zindex) && zindex !== "auto" && zindex.trim().length > 0 && _.isNumber(parseInt(zindex, 10)));
                } catch (error) { return false;  }
            }); 

            var notZIndexed = edtr.find("body").children(":not(" + that.model.get("omitTags").join(', ') + ")").filter(function (inx, e) {
                var zindex = that.getElementZIndex(e);
                return zindex == "auto" || (!_.isNumber(zindex) && (!_.isString(zindex) || zindex.trim().length < 1 || !_.isNumber(parseInt(zindex, 10))));
            });

            var highestZIndex = -Infinity, lowestZIndex = +Infinity; 
            zIndexed.each(function (inx, elem) {
                var zindex = that.getElementZIndex(elem);
                if (zindex > highestZIndex) highestZIndex = zindex; 
                if (zindex < lowestZIndex) lowestZIndex = zindex;
            });

            highestZIndex = highestZIndex == -Infinity ? 0 : highestZIndex; 
            lowestZIndex = lowestZIndex == +Infinity ? 1 : lowestZIndex; 

            notZIndexed.each(function (inx, elem) {
                $(elem).css({ "z-index": ++highestZIndex }); 
            }); 

            return { low: lowestZIndex, high: highestZIndex, edtr: edtr };
        }
        , injectDraggable: function (frame) {
            var that = this;

            this.options.preview_draggable =
            frame.contents().find("body").children(":not(" + that.model.get("omitTags").join(', ') + ")").draggable({
                addClasses: false,
                helper: "original", 
                start: this.objectDragStart, 
                stop: this.objectDragStop,
            });

            this.checkPreviewDraggable();
        }
        , injectResizable: function (frame) {
            var that = this;

            //var elems = frame.contents().find("body").children(":not(" + that.model.get("omitTags").concat(["img", "input", "select"]).join(', ') + ")");

            //elems.each(function (i, elem) {
            //    interact()
            //        .resizable({
            //            preserveAspectRatio: false,
            //            edges: { left: true, right: true, bottom: true, top: true }
            //        }); 
            //}); 

            //interact()
            //    .resizable({
            //        preserveAspectRatio: false,
            //        edges: { left: true, right: true, bottom: true, top: true }
            //    }); 

            this.options.preview_resizeable =
            frame.contents().find("body").children(":not(" + that.model.get("omitTags").concat(["img", "input", "select"]).join(', ') + ")").resizable({
                addClasses: false,
                stop: this.objectResizeStop, 
            });

            this.checkPreviewResizeable(); 
        }
        , checkPreviewDraggable: function () {
            var fullscreen = this.model.get("fullscreen");
            if (this.options.preview_draggable) {
                this.options.preview_draggable.draggable({
                    disabled: !fullscreen,
                }); 
            }
        }
        , checkPreviewResizeable: function () {
            var fullscreen = this.model.get("fullscreen"); 
            if (this.options.preview_resizeable) {
                this.options.preview_resizeable.resizable({
                    disabled: !fullscreen,
                });
            }
        }
        , objectDragStart: function (e, obj) {
            obj.helper.css({ position: "absolute" }); 
        }
        , objectDragStop: function (e, obj) {
            try {
                var editor = this.options.editor;
                var frame = this.$el.find(".preview-frame");
                var helper = obj.helper; 
                var elem_editor_guid = helper.data("editor-elem-guid");

                var edtr = this.getEdtr();

                if (elem_editor_guid) {
                    var interactive_elem = edtr.find('*[data-editor-elem-guid="' + elem_editor_guid.toString() + '"]');
                    if (interactive_elem.length > 0) {
                        interactive_elem.css({ left: obj.position.left, top: obj.position.top, position: "absolute"});

                        editor.setValue(this.parseEdtrHtml(edtr));
                    }
                } else { console.log(""); }
            } catch (error) { console.log("Error on drag stop: " + error.toString()); }
        }
        , objectResizeStop: function (e, obj) {
            try {
                var editor = this.options.editor;
                var frame = this.$el.find(".preview-frame");
                var helper = obj.helper;
                var elem_editor_guid = helper.data("editor-elem-guid");

                var edtr = this.getEdtr();

                if (elem_editor_guid) {
                    var interactive_elem = edtr.find('*[data-editor-elem-guid="' + elem_editor_guid.toString() + '"]');
                    if (interactive_elem.length > 0) {
                        interactive_elem.css({ height: obj.size.height, width: obj.size.width });

                        editor.setValue(this.parseEdtrHtml(edtr));
                    }
                } else { console.log(""); }
            } catch (error) { console.log("Error on drag stop: " + error.toString()); }
        }
        , injectScript: function (frame, src, dependencies) {
            var that = this; 
            try {
                dependencies = (dependencies) ? dependencies : []; 
                frame = frame.contentWindow ? frame : frame.length > 0 && frame[0] && frame[0].contentWindow ? frame[0] : null;
                if (frame) {
                    if (this.checkScriptDependencies(frame, dependencies)) {
                        var script = frame.contentWindow.document.createElement("script");
                        script.type = "text/javascript";
                        script.src = src;
                        frame.contentWindow.document.body.appendChild(script);
                    } else {
                        setTimeout(function () {
                            that.injectScript.call(that, frame, src, dependencies);
                        }, 500); 
                    }
                }
            } catch (error) {
                console.log("Error while trying to inject script [src=" + src.toString() + "] ."); 
            }
        }
        , injectStyle: function (frame, src) {
            var that = this;
            try {
                frame = frame.contentWindow ? frame : frame.length > 0 && frame[0] && frame[0].contentWindow ? frame[0] : null;
                if (frame) {
                    var style = frame.contentWindow.document.createElement("link");
                    style.type = "text/css";
                    style.href = src;
                    style.rel = "stylesheet"; 
                    frame.contentWindow.document.head.appendChild(style);
                }
            } catch (error) {
                console.log("Error while trying to inject style [src=" + src.toString() + "] .");
            }
        }
        , checkScriptDependencies: function (frame, dependencies) {
            var ret = true; 
            try {
                dependencies = _.isArray(dependencies) ? dependencies : [dependencies];
                frame = frame.contentWindow ? frame : frame.length > 0 && frame[0] && frame[0].contentWindow ? frame[0] : null;
            
                if (frame) {
                    for (var i = 0, len = dependencies.length; i < len; i++) {
                        if (!_.isArray(dependencies[i])) {
                            if (!frame.contentWindow[dependencies[i]]) ret = false;
                        } else {
                            var arr = dependencies[i]; 
                            if (!frame.contentWindow[arr[0]][arr[1]]) ret = false;
                        }
                    }
                }
            } catch (error) {
                ret = false; 
            }
            return ret; 
        }
        , checkFrameZoom: function () {
            var that = this; 
            var editor = this.$el.find(".editor-section");
            var frame = this.$el.find(".preview-section");
            var contents = this.$el.find(".preview-frame").contents(); 
            var fullscreen = this.model.get("fullscreen"); 

            if (!fullscreen) {
                contents.find("html").addClass("editor-editr-zoomed-out");
            } else if (fullscreen) {
                contents.find("html").removeClass("editor-editr-zoomed-out");
            }

            this.editorOnFrameExpandCollapse(); 
        }
        , expandFrame: function(){
            var editor = this.$el.find(".editor-section");
            var frame = this.$el.find(".preview-section");

            editor.addClass("hide section-collapsed").removeClass("section-normal");
            frame.addClass("fullscreen section-expanded").removeClass("section-normal");

            this.$el.find(".btn-expand-frame").addClass("hide");
            this.$el.find(".btn-collapse-frame").removeClass("hide");

            this.model.set("fullscreen", true); 
        }
        , collapseFrame: function () {
            var editor = this.$el.find(".editor-section");
            var frame = this.$el.find(".preview-section");

            editor.removeClass("hide section-collapsed").addClass("section-normal");
            frame.removeClass("fullscreen section-expanded").addClass("section-normal");

            this.$el.find(".btn-expand-frame").removeClass("hide");
            this.$el.find(".btn-collapse-frame").addClass("hide");

            this.model.set("fullscreen", false);
        }
        , editorOnFrameExpandCollapse: function () {
            try{
                var fullscreen = this.model.get("fullscreen");

                //was expanded
                if (fullscreen) {
                    this.parseEditorGuidsOnElements(); 
                } else {
                    //was collapsed
                    //this.options.editor.refresh();
                    this.removeEditorGuids();
                }
            } catch (error) { console.log("Error ") }
        }
        , getEdtr: function () {
            var editorValue = this.options.editor.getValue();
            //editorValue = editorValue.replace("<html>", "").replace("\n</html>", "").replace("</html>", "");

            //var originalEditorValue = editorValue; 

            //var lastIndexOf = -1; 
            //var indexOf = editorValue.indexOf("<script", lastIndexOf + 1);
            //while (indexOf != -1) {
            //    var nextChar = editorValue.indexOf(">", indexOf);
            //    if (nextChar != -1) {
            //        nextChar += 1; 
            //        editorValue = [editorValue.slice(0, nextChar), "<!-- <![CDATA[", editorValue.slice(nextChar)].join('');
            //    }
            //    lastIndexOf = indexOf;
            //    indexOf = editorValue.indexOf("<script", lastIndexOf + 1);
            //}

            //var lastIndexOf = -1;
            //var indexOf = editorValue.indexOf("<style", lastIndexOf + 1);
            //while (indexOf != -1) {
            //    var nextChar = editorValue.indexOf(">", indexOf);
            //    if (nextChar != -1) {
            //        nextChar += 1;
            //        editorValue = [editorValue.slice(0, nextChar), "<!-- <![CDATA[", editorValue.slice(nextChar)].join('');
            //    }
            //    lastIndexOf = indexOf;
            //    indexOf = editorValue.indexOf("<style", lastIndexOf + 1);
            //}

            //editorValue = editorValue.replace(/<\/script>/ig, "]]> --></script>");
            //editorValue = editorValue.replace(/<\/style>/ig, "]]> --></style>");

            ////editorValue = originalEditorValue; 
            //editorValue = editorValue.replace(/<script/ig, "<edtr-code");
            //editorValue = editorValue.replace(/script>/ig, "edtr-code>");
            //editorValue = editorValue.replace(/<style/ig, "<edtr-css");
            //editorValue = editorValue.replace(/style>/ig, "edtr-css>");

            //var impl = (new DOMParser).parseFromString(editorValue, mime_type);
            var doc = document.implementation.createHTMLDocument(''); 
            var body = doc.body; 
            body.innerHTML = editorValue; 

            return $(doc);
            //return $('<html/>').html(editorValue);
        }
        , parseEdtrHtml: function (edtr) {
            //var html = "<html>\n" + edtr.html().trim() + "\n</html>";
            var html = edtr[0].body.innerHTML;

            //html = html.replace(/\/\/<!-- <!\[CDATA\[/g, "");
            //html = html.replace(/\/\/\]\] -->/g, "");

            //html = html.replace(/edtr-code/g, "script");
            //html = html.replace(/edtr-css/g, "style");

            return html; 
        }
        , parseEditorGuidsOnElements: function () {
            try {
                var that = this;
                if (!this.model.get("guidsParsed")) {
                    var editor = this.options.editor;
                    var frame = this.$el.find(".preview-frame");
                    //old method
                    //var edtr = $('<html/>').html(editor.getValue().replace("<html>", "").replace("\n</html>", ""));
                    var edtr = this.getEdtr(); 
                    var frame_contents = frame.contents();

                    var guids = {};
                    edtr.find("body").children(":not(" + that.model.get("omitTags").join(', ') + ")").each(function (inx, elem) {
                        var $elem = $(elem);
                        guids[inx] = that.guid();
                        $elem.attr("data-editor-elem-guid", guids[inx]);
                    });

                    frame_contents.find("body").children(":not(" + that.model.get("omitTags").join(', ') + ")").each(function (inx, elem) {
                        var $elem = $(elem);
                        //$elem.data("editor-elem-guid", guids[inx]);
                        $elem.attr("data-editor-elem-guid", guids[inx]);

                        $elem.addClass("editor-editr-draggable-elem editor-editr-elem");
                    });

                    this.model.set("guidsParsed", true);

                    //old method
                    //editor.setValue("<html>\n" + edtr.html().trim() + "\n</html>");
                    editor.setValue(this.parseEdtrHtml(edtr));
                }
            } catch (error) { }
        }
        , removeEditorGuids: function () {
            try {
                var that = this;

                if (this.model.get("guidsParsed")) {
                    var editor = this.options.editor;
                    var frame = this.$el.find(".preview-frame");
                    var edtr = this.getEdtr(); 
                    var frame_contents = frame.contents();

                    edtr.find("body").children().removeAttr("data-editor-elem-guid");
                    frame_contents.find("body").children().removeAttr("data-editor-elem-guid")
                    .removeClass("editor-editr-draggable-elem editor-editr-elem");

                    this.model.set("guidsParsed", false);
                    editor.setValue(this.parseEdtrHtml(edtr)); 
                }
            } catch (error) { }
        }
        , guid: function(){
            function s4() {
                return Math.floor((1 + Math.random()) * 0x10000)
                    .toString(16)
                    .substring(1);
            }
            return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
                s4() + '-' + s4() + s4() + s4();
        }
        , readFromFile: function (screenId, refreshFrame, refreshCode, forceOnFullscreen, callback) {
            var that = this;
            forceOnFullscreen = _.isBoolean(forceOnFullscreen) ? forceOnFullscreen : false;
            refreshCode = _.isBoolean(refreshCode) ? refreshCode : false;
            refreshFrame = _.isBoolean(refreshFrame) ? refreshFrame : true;

            this.readFile(screenId, function (status, response) {
                if (refreshCode && !that.options.editor) {
                    that.instanceCodeMirror();
                }

                if (response.Success) {
                    if (refreshCode && (!that.model.get("fullscreen") || forceOnFullscreen)) that.options.editor.setValue(response.Data.Contents);
                    if (refreshFrame && (!that.model.get("fullscreen") || forceOnFullscreen)) that.refreshFrame(response.Data.Path);
                }

                if (callback && _.isFunction(callback)) {
                    callback.call(that); 
                }
            });
        }
        , readFile: function (screenId, callback) {
            var that = this; 
            this.callAPI({ action: "read", screenId: screenId }, callback);
        }
        , escapeHTML: function escapeHtml(string) {
            var entityMap = {
                "&": "&amp;",
                "<": "&lt;",
                ">": "&gt;",
                '"': '&quot;',
                "'": '&#39;',
                "/": '&#x2F;'
            };

            return String(string).replace(/[&<>"'\/]/g, function (s) {
                return entityMap[s];
            });
        }
        , writeFile: function (screenId, contents, callback) {
            var that = this;
            this.callAPI({
                action: "write", screenId: screenId, contents: contents
            }, function () {
                this.trigger("fileWritten", arguments);

                if (callback && _.isFunction(callback)) {
                    callback.apply(this, arguments); 
                }
            });
        }
        , callAPI: function (data, callback) {
            var that = this; 
            $.ajax(app.foldersRoot + "/app/custom-screens/screen-builder/" + this.template + "/api/api.aspx", {
                type: 'POST',
                data: data,
                success: function (res) {
                    if (callback && _.isFunction(callback)) {
                        callback.call(that, true, res);
                    }
                },
                error: function (res) {
                    console.log("error");
                    if (callback && _.isFunction(callback)) {
                        callback.call(that, false, res);
                    }
                }
            });
        }
        , editorChanged: _.debounce(function(instance, changes){
            var that = this; 
            var editor = this.options.editor;

            if (this.options.firstEditorValue) {
                this.options.firstEditorValue = false;
                return; 
            }

            if ((changes.length == 1 && changes[0].origin == "setValue") || this.model.get("fullscreen")) {
                return; 
            }

            var contents = editor.getValue(); 
            this.writeFile(that.model.get("id"), contents, function (status, response) {
                that.readFromFile(that.model.get("id"), true, false);
            }); 
        }, 500)
        , editorSetValue: function (instance, changes) {
            var that = this; 
            if (changes.length == 1 && changes[0].origin == "setValue") {
                if (!this.model.get("fullscreen")) {
                    var totalLines = this.options.editor.lineCount();
                    this.options.editor.autoFormatRange({ line: 0, ch: 0 }, { line: totalLines });
                } else {
                    this.editorSetValueFullscreen(instance, changes); 
                }
            }
        }
        , editorSetValueFullscreen: _.debounce(function () {
            var that = this; 
            var editor = this.options.editor;

            var totalLines = this.options.editor.lineCount();
            this.options.editor.autoFormatRange({ line: 0, ch: 0 }, { line: totalLines });

            var edtr = this.getEdtr();
            edtr.find("body").children().removeAttr("data-editor-elem-guid");
            var contents = (this.parseEdtrHtml(edtr));

            this.writeFile(that.model.get("id"), contents, function () {
            }); 
        }, 1000)
        , openAddImageSelector: function (action, srcElem, ctx) {
            var that = this; 
            var modal = new ScreenBuilder.Views.AddImageSelectorModal({
                id: this.model.get("id"), 
                events: _.extend(ScreenBuilder.Views.AddImageSelectorModal.prototype.events, {
                }),
                mode: (action == "EDIT") ? "EDIT" : "CREATE",
            });

            modal.on("imagesSelected", function (imgArr) {
                that.once("fileWritten", function (args) {
                    that.readFromFile(that.model.get("id"), true, true, true);
                }); 
                that.addImages(imgArr, srcElem, ctx);
            }); 

            modal.show();
        }
        , addImages: function (img, srcElem, ctx) { //img or imgArr
            var img = _.isArray(img) ? img : [img]; 
            var editor = this.options.editor;
            var frame = this.$el.find(".preview-frame");
            var edtr = this.getEdtr();

            if (srcElem && img && img.length > 0) {
                srcElem.find("img").attr("src", img[0]); 
                editor.setValue(this.parseEdtrHtml(ctx));
            } else {
                var imgs = [];
                _.each(img, function (src) {
                    //var $imgWrapper = $("<div data-sb-type='imageViewer' style='display:inline-block;position:absolute;' />");
                    var $imgWrapper = $("<imageViewer style='display:inline-block;position:absolute;' />");
                    var $img = $("<img style='width:100%;height:100%' />");
                    $imgWrapper.append($img);

                    $img.attr("src", src);
                    imgs.push($imgWrapper);
                });

                edtr.find("body").append(imgs);
                editor.setValue(this.parseEdtrHtml(edtr));
            }
        }
        , openAddTagSelector: function (action, srcElem, ctx) {
            var that = this;
            var modal = new ScreenBuilder.Views.AddTagSelectorModal({
                id: this.model.get("id"),
                events: _.extend(ScreenBuilder.Views.AddTagSelectorModal.prototype.events, {
                }),
                mode: (action == "EDIT") ? "EDIT" : "CREATE", 
            });

            modal.on("tagsSelected", function (tagsArr) {
                that.once("fileWritten", function (args) {
                    that.readFromFile(that.model.get("id"), true, true, true);
                });
                that.addTags(tagsArr, srcElem, ctx);
            });

            modal.show();
        }
        , addTags: function (tags, srcElem, ctx) {
            tags = _.isArray(tags) ? tags : [tags];
            var editor = this.options.editor;
            var frame = this.$el.find(".preview-frame");
            var edtr = this.getEdtr();

            if (srcElem && tags && tags.length > 0) {
                srcElem.attr("data-label", tags[0].name.toString());
                srcElem.attr("data-tagname", tags[0].agentName.toString() + '.' + tags[0].name.toString());
                editor.setValue(this.parseEdtrHtml(ctx));
            } else {
                var tagViewers = [];
                _.each(tags, function (tag) {
                    //var tagViewer = $("<div data-sb-type='tagViewer' data-label='" + tag.name.toString() + "' data-tagid='" + tag.id.toString() + "'></div>");
                    var tagViewer = $("<tagViewer data-label='" + tag.name.toString() + "' data-tagname='" + tag.agentName.toString() + '.' + tag.name.toString() + "'></tagViewer>");
                    tagViewers.push(tagViewer);
                    edtr.find("body").append(tagViewer);
                });

                //edtr.find("body").append(tagViewers);
                editor.setValue(this.parseEdtrHtml(edtr));
            }
        }
        , openChartBuilder: function () {
            var that = this;
            var modal = new ScreenBuilder.Views.ChartBuilderModal({
                id: this.model.get("id"),
                events: _.extend(ScreenBuilder.Views.ChartBuilderModal.prototype.events, {
                }),
            });

            modal.on("chartSelected", function (properties) {
                that.once("fileWritten", function (args) {
                    that.readFromFile(that.model.get("id"), true, true, true);
                });
                that.addChart(properties);
            });

            modal.show();
        }
        , addChart: function (properties) {
            var editor = this.options.editor;
            var frame = this.$el.find(".preview-frame");
            var edtr = this.getEdtr();
            
            //var _template = "<div data-sb-type='chartViewer' data-title='{{title}}' data-tagids='{{tagIds}}' data-labels='{{labels}}' data-from='{{from}}' data-to='{{to}}'></div>";
            var _template = "<chartViewer data-title='{{title}}' data-tagnames='{{tagNames}}' data-labels='{{labels}}' data-from='{{from}}' data-to='{{to}}'></chartViewer>";
            
            var tags = _.isArray(properties.tags) ? properties.tags : [properties.tags];
            var labels = _.pluck(tags, "name");
            var from = properties.from;
            var to = properties.to; 
            var title = properties.title; 

            var chartViewer = $(
                (_template).replace("{{tagNames}}", (_.map(tags, function (t) { return t.agentName.toString() + '.' + t.name.toString(); })).join(",")).replace("{{labels}}", labels.join(","))
                .replace("{{from}}", from).replace("{{to}}", to).replace("{{title}}", title)
            );
            edtr.find("body").append(chartViewer);

            //edtr.find("body").append(tagViewers);
            editor.setValue(this.parseEdtrHtml(edtr));
        }
        , openAddText: function (action, srcElem, ctx) {
            var that = this;
            var content = (action == "EDIT" && srcElem && srcElem.html) ? srcElem.html() : null;

            var modal = new ScreenBuilder.Views.AddTextModal({
                id: this.model.get("id"),
                events: _.extend(ScreenBuilder.Views.AddTextModal.prototype.events, {
                }),
                content: _.isString(content) ? content : null, 
            });

            modal.on("textSelected", function (elem) {
                that.once("fileWritten", function (args) {
                    that.readFromFile(that.model.get("id"), true, true, true);
                });
                that.addText(elem, srcElem, ctx);
            });

            modal.show();
        }
        , addText: function (elem, srcElem, ctx) {
            var editor = this.options.editor;
            var frame = this.$el.find(".preview-frame");
            var edtr = this.getEdtr();

            if (srcElem) {
                srcElem.html(elem.html());
                editor.setValue(this.parseEdtrHtml(ctx));
            } else {
                elem.css({ "position": "absolute" });
                edtr.find("body").append(elem);
                editor.setValue(this.parseEdtrHtml(edtr));
            }
        }
        , openPreviewWindow: function () {
            try {
                var url = app.router.resolveURL("viewer", { id: this.model.get("id") }, false);
                url = app.root + "/" + url; 
                window.open(url, "_blank"); 
            } catch (error) { console.log("Error"); }
        }
        , 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;
            }
        }
        , fitToHeight: function () {
            this.$el.find(".resize-height-to-fit").height($(window).height() - 80);
        }
        , refresh: function () {
            try {
                
            } catch (Error) { }
        }
        , 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);
            this.listenTo(this.model, "change:fullscreen", this.checkPreviewDraggable);
            this.listenTo(this.model, "change:fullscreen", this.checkPreviewResizeable);
            //this.listenTo(this.model, "change:fullscreen", this.editorOnFrameExpandCollapse);
        }
        , bindViewScopedEvents: function () {
            var that = this;

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

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

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

            this.options.listeners['fullscreenchange'] = _.bind(that.fitToHeight, that);
            document.addEventListener('fullscreenchange', that.options.listeners['fullscreenchange'], false);

            //this.$el.find(".preview-frame").load(_.bind(that.frameLoaded, that));
        }
        , unbindViewScopedEvents: function () {
            if (this.options.tagViewers) this.options.tagViewers.destroy(); 
            window.removeEventListener('focus', this.options.listeners['focus'], false);
            window.removeEventListener('resize', this.options.listeners['resize'], false);
            window.removeEventListener('readystatechange', this.options.listeners['readystatechange'], false);
            document.removeEventListener('fullscreenchange', this.options.listeners['fullscreenchange'], false);
            this.$el.find(".preview-frame").off("load"); 
        }
        , close: function () {
            this.options.state = app.view_states.closed;
            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 () {
        }
    });

    ScreenBuilder.Models.AddImageSelectorModal = Backbone.Epoxy.Model.extend({
        defaults: {
            id: null, //screen_id
            files: [],
            filesBinding: [],
            mode: "CREATE",
        },

        initialize: function () {
            var opts = { };
            this.options = _.extend(this.options ? this.options : {}, opts);
        },

        fetch: function (options) {
            var that = this;
            that.refreshUploadedFiles(); 
        },

        refreshUploadedFiles: function () {
            var that = this;
            that.callAPI({
                action: 'GET_UPLOADED_FILES',
                screenId: that.get("id"),
                extensions: "jpg, jpeg, gif, png, apng, svg, bmp, ico", 
            }, function (status, response) {
                if (status == true && response && response.Data && response.Data.Files) {
                    var files = response.Data.Files;
                    var filesBinding = _.map(files, function (value, key) {
                        return { label: key, value: key }
                    });

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

        callAPI: function (data, callback) {
            var that = this;
            $.ajax(app.foldersRoot + "/app/custom-screens/screen-builder/configuration/screen-builder/api/api.aspx", {
                type: 'POST',
                data: data,
                success: function (res) {
                    if (callback && _.isFunction(callback)) {
                        callback.call(that, true, res);
                    }
                },
                error: function (res) {
                    console.log("error");
                    if (callback && _.isFunction(callback)) {
                        callback.call(that, false, res);
                    }
                }
            });
        }, 

        computeds: {
        }
    });

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

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

            if (this.options.viewParams) {
                
            }

            this.options.viewModels = {
            };

            this.options._isRendered = false;

            this.model = new ScreenBuilder.Models.AddImageSelectorModal({
                id: (this.options.id) ? this.options.id : null,
                mode: (this.options.mode) ? this.options.mode : null,
            });

            this.bindEvents();
        },
        events: {
            'click .close': function (event) {
                event.preventDefault();

                this.trigger('cancel');

                if (this.options.content && this.options.content.trigger) {
                    this.options.content.trigger('cancel', this);
                }
            },
            "click .btn-cancel": function (event) {
                event.preventDefault();

                this.trigger('cancel');

                if (this.options.content && this.options.content.trigger) {
                    this.options.content.trigger('cancel', this);
                }
            },
            "click .btn-add": "addImage",
            "change .files-select": "imagesSelectedChanged"
        },

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

            //the screens have a custompath, so it has to be specified in the customPath variable that is
            //then sent to the template loader.
            var customPath = "/app/custom-screens/screen-builder/editor/";

            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({
                        mode: that.model.get("mode"),
                    }));

                    that.applyBindings();

                }, true, customPath);
            }, customPath, "screen_builder_add_image_selector");

            that.options._isRendered = true;
            that.refresh();
        }
        , imagesSelectedChanged: function () {
            try {
                var that = this;
                var filesSelect = that.$el.find(".files-select");
                var val = filesSelect.val();
                var mode = that.model.get("mode");

                if (val.length > 1 && mode == "EDIT") {
                    filesSelect.find(':selected:not(.selected)').prop('selected', false);
                } else {
                    filesSelect.find(':selected').addClass('selected');
                    filesSelect.find(':not(:selected)').removeClass('selected');
                }
            } catch (error) { console.log("Error: " + error.toString()); }
        }
        , addImage: function () {
            var that = this;
            var filesSelect = this.$el.find(".files-select");
            var val = filesSelect.val(); 

            this.trigger("imagesSelected", val);

            this.hide(); 
        }
        , refresh: function () {
            try {
                this.model.fetch();
            } catch (Error) { }
        }
        , _startAutoRefresh: function () {
            try {
                if (this.options.MYREFERENCES.autoRefresh.toid != null) {
                    clearTimeout(this.options.MYREFERENCES.autoRefresh.toid);
                    this.options.MYREFERENCES.autoRefresh.toid = null;
                }

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

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

            this.refresh();

            if (this.options.MYREFERENCES.autoRefresh.enabled == true) {
                this.options.MYREFERENCES.autoRefresh.toid = setTimeout(_.bind(this._autoRefresh, this), 3000);
            }
        }
        , _stopAutoRefresh: function () {
            if (this.options.MYREFERENCES.autoRefresh.toid != null) {
                clearTimeout(this.options.MYREFERENCES.autoRefresh.toid);
                this.options.MYREFERENCES.autoRefresh.toid = null;
            }
            this.options.MYREFERENCES.autoRefresh.enabled = false;
        }
        , show: function () {

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

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

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

            $backdrop = $('.modal-backdrop');

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

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

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

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

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

            return this;

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

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

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

                self.trigger('hidden');

                self.close();
            });

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

            //rendering as soon as getting data in the model
            //this.listenTo(this.model, "change", this.render);
        }
        , close: function () {
            this._stopAutoRefresh();
            this.remove();
            this.unbind();
        }
        , preRender: function () {
        }
        , reRender: function () {
        }
    });

    ScreenBuilder.Models.AddLinkSelectorModal = Backbone.Epoxy.Model.extend({
        defaults: {
            id: null, //screen_id
            linkType: "systemScreen",

            screens: [],
            trendingViews: [], 
        },
        initialize: function () {
            var opts = {};
            this.options = _.extend(this.options ? this.options : {}, opts);
        },
        fetch: function (options) {
            var that = this;
        },
        fetchSystemScreens: function (opt) {
            var options = {
                method: "set",
                async: true,
                callback: null,
            };

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

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

            var items = [];
            var children = {};

            Core.Json.CallProcedure(app.DatabaseNames.System + ".SBuilder.GetSystemScreens", QP, {
                onSuccess: function (data) {
                    if (data && data.Table) {
                        var parameters = data.Table1; 
                        var data = data.Table;
                        var root = _.where(data, { ParentId: null }); 

                        var mapItems = function(tt, set, params){
                            return _.map(tt, function(t){
                                var children = _.where(set, { ParentId: t.Id });
                                if (children.length > 0) {
                                    children = mapItems(children, set, params); 
                                } else {
                                    children = null; 
                                }

                                var route = t.Route;
                                if (_.isString(route)) {
                                    var prs = {};
                                    _.each(_.where(params, { MenuId: t.RawId }), function (p) {
                                        prs[p.Name] = p.Value; 
                                    });

                                    route = app.router.resolveURL(route, prs, false, true); 
                                }

                                return {
                                    id: t.Id,
                                    name: t.Name,
                                    route: route,
                                    children: children, 
                                }; 
                            }); 
                        }; 

                        items = mapItems(root, data, parameters); 
                    }

                    that.set("screens", items);

                    if (options.callback != null && _.isFunction(options.callback))
                        options.callback.call(this, that);
                },
                Async: options.async,
                Secured: true,
            }, app.ConnectionStrings.app);
        },
        fetchTrendingViews: function (opt) {
            var options = {
                method: "set",
                async: true,
                callback: null,
            };

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

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

            var items = [];
            var children = {};

            Core.Json.CallProcedure(app.DatabaseNames.System + ".SBuilder.GetTrendingViews", QP, {
                onSuccess: function (data) {
                    if (data && data.Table) {
                        var data = data.Table;
                        var root = _.where(data, { ChartViewGroupId: null });

                        var mapItems = function (tt, set) {
                            return _.map(tt, function (t) {
                                var children = _.where(set, { ChartViewGroupId: t.Id });
                                if (children.length > 0) {
                                    children = mapItems(children);
                                } else {
                                    children = null;
                                }

                                return {
                                    id: t.Id,
                                    name: t.Name,
                                    children: children,
                                };
                            });
                        };

                        items = mapItems(root, data);
                    }

                    that.set("trendingViews", items);

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

    ScreenBuilder.Views.AddLinkSelectorModal = Backbone.Epoxy.View.extend({
        template: "editor"
        , id: "editor"
        , title: ""
        , className: "modal hide fade modal-wider"
        //default not cacheable, change this if you want the view to be cacheable
        // if the view is set as cacheable should also have a refresh method to reset the view without erasing the DOM.
        , isCacheable: true
        , initialize: function () {
            this.options.MYREFERENCES = {
                autoRefresh: {
                    enabled: true,
                    toid: null
                },
            };

            if (this.options.viewParams) {

            }

            this.options.viewModels = {
            };

            this.options._isRendered = false;

            this.model = new ScreenBuilder.Models.AddLinkSelectorModal({
                id: (this.options.id) ? this.options.id : null,
            });

            this.bindEvents();
        },
        events: {
            'click .close': function (event) {
                event.preventDefault();

                this.trigger('cancel');

                if (this.options.content && this.options.content.trigger) {
                    this.options.content.trigger('cancel', this);
                }
            },
            "click .btn-cancel": function (event) {
                event.preventDefault();

                this.trigger('cancel');

                if (this.options.content && this.options.content.trigger) {
                    this.options.content.trigger('cancel', this);
                }
            },
            "click .btn-add": "addLink",
        },

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

            //the screens have a custompath, so it has to be specified in the customPath variable that is
            //then sent to the template loader.
            var customPath = "/app/custom-screens/screen-builder/editor/";

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

                    that.refresh();
                }, true, customPath);
            }, customPath, "screen_builder_add_link_selector");

            that.options._isRendered = true;
        }
        , addLink: function () {
            var that = this;
            var linkType = this.model.get("linkType").toUpperCase();

            var value = null; 
            switch (linkType) {
                case "URL":
                    value = this.$el.find(".external-url-input").val(); 
                    break;
                case "SYSTEMSCREEN":
                    value = this.$el.find(".system-screens-select").val();
                    value = (_.isArray(value) && value.length > 0) ? value[0] : null;
                    break;
                case "TRENDINGVIEW":
                    value = this.$el.find(".trending-views-select").val();
                    value = (_.isArray(value) && value.length > 0) ? value[0] : null;
                    break;
            }

            this.trigger("linkSelected", linkType, value);

            this.hide();
        }
        , refresh: function () {
            try {
                var that = this; 
                
                //fetching system screens
                this.model.fetchSystemScreens({
                    callback: function () {
                        that.renderSystemScreensView();
                    }
                });

                //fetching trending views
                this.model.fetchTrendingViews({
                    callback: function () {
                        that.renderTrendingViewsView();
                    }
                });
            } catch (Error) { }
        }
        , renderSystemScreensView: function () {
            var template = Handlebars.compile(this.$el.find(".system-screens-select-template").html());
            var rendered = template(this.model.toJSON());
            this.$el.find(".system-screen-settings").html(rendered);
        }
        , renderTrendingViewsView: function () {
            var template = Handlebars.compile(this.$el.find(".trending-views-select-template").html());
            var rendered = template(this.model.toJSON());
            this.$el.find(".trending-view-settings").html(rendered);
        }
        , _startAutoRefresh: function () {
            try {
                if (this.options.MYREFERENCES.autoRefresh.toid != null) {
                    clearTimeout(this.options.MYREFERENCES.autoRefresh.toid);
                    this.options.MYREFERENCES.autoRefresh.toid = null;
                }

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

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

            this.refresh();

            if (this.options.MYREFERENCES.autoRefresh.enabled == true) {
                this.options.MYREFERENCES.autoRefresh.toid = setTimeout(_.bind(this._autoRefresh, this), 3000);
            }
        }
        , _stopAutoRefresh: function () {
            if (this.options.MYREFERENCES.autoRefresh.toid != null) {
                clearTimeout(this.options.MYREFERENCES.autoRefresh.toid);
                this.options.MYREFERENCES.autoRefresh.toid = null;
            }
            this.options.MYREFERENCES.autoRefresh.enabled = false;
        }
        , show: function () {

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

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

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

            $backdrop = $('.modal-backdrop');

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

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

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

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

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

            return this;

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

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

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

                self.trigger('hidden');

                self.close();
            });

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

            //rendering as soon as getting data in the model
            //this.listenTo(this.model, "change", this.render);
        }
        , close: function () {
            this._stopAutoRefresh();
            this.remove();
            this.unbind();
        }
        , preRender: function () {
        }
        , reRender: function () {
        }
    });

    //ScreenBuilder.Models.TagSelectorAgent = Backbone.Model.extend({
    //    defaults: {
    //        id: null,
    //        parent: null,
    //        name: null,
    //        type: null,
    //        children: null, 
    //    },
    //    initialize: function () {
    //        this.attributes.children = new ScreenBuilder.Collections.TagSelectorAgents(); 
    //    },
    //    fetch: function (opt) {
    //        var options = {
    //            method: "set",
    //            async: true,
    //            callback: null,
    //        };

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

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

    //        var items = [];
    //        var children = {};

    //        Core.Json.CallProcedure(app.DatabaseNames.System + ".SBuilder.GetAgents", QP, {
    //            onSuccess: function (data) {
    //                if (Core.Object.Eval(data, "Table")) {
    //                    var data = data.Table;
    //                    var root = _.where(data, { Parent: null });

    //                    var pushTo = items;
    //                    for (var i = 0; i < root.length; i++) {
    //                        var itemData = data[i];

    //                        items.push({
    //                            id: itemData.Id,
    //                            parent: itemData.Parent,
    //                            name: itemData.Name,
    //                            type: itemData.Type,
    //                        });

    //                        children[itemData.Id] = _.where(data, { Parent: itemData.Id });
    //                    }
    //                }

    //                var newItems = _.where(that.toJSON(), { isNew: true });
    //                [].push.apply(items, newItems);

    //                that[options.method](items, { from: "fetch" });
    //                that.each(function (i) {
    //                    var cc = children[i.get("id")] ? childre[i.get("id")] : [];
    //                    i.get("children").set(cc);
    //                });

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

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

    //ScreenBuilder.Collections.TagSelectorAgents = Backbone.Collection.extend({
    //    model: ScreenBuilder.Models.TagSelectorAgent, 
    //}); 

    ScreenBuilder.Models.AddTagSelectorModal = Backbone.Epoxy.Model.extend({
        defaults: {
            id: null, //screen_id
            searchQuery: null,
            mode: "CREATE",

            agents: [],
            tags: [], 
        },
        initialize: function () {
            var opts = {};
            this.options = _.extend(this.options ? this.options : {}, opts);
        },
        fetchTags: function (opt, search) {
            var options = {
                method: "set",
                async: true,
                callback: null,
            };

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

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

            QP.Add("searchQuery", "VARCHAR", (_.isString(search) && search.trim() != '') ? search : null); 

            var items = [];
            var children = {};

            Core.Json.CallProcedure(app.DatabaseNames.System + ".SBuilder.GetTags", QP, {
                onSuccess: function (data) {
                    if (data && data.Table) {
                        var data = data.Table;
                        var agents = _.uniq(_.pluck(data, "AgentName")); 

                        _.each(agents, function (ag) {
                            var item = {
                                name: ag,
                                tags: _.map(_.where(data, { AgentName: ag }), function(tag){
                                    return {
                                        id: tag.Id, 
                                        name: tag.Name, 
                                        type: tag.DataType, 
                                    }; 
                                }),  
                            }; 

                            items.push(item);
                        }); 
                    }

                    that.set("agents", items, { from: "fetch" });
                    that.set("tags", _.map(data, function (t) { return { id: t.Id, name: t.Name, type: t.DataType, agentName: t.AgentName } }));

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

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

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

            if (this.options.viewParams) {
            }

            this.options.viewModels = {
            };

            this.options._isRendered = false;

            this.model = new ScreenBuilder.Models.AddTagSelectorModal({
                id: (this.options.id) ? this.options.id : null,
                mode: (this.options.mode) ? this.options.mode : null,
            });

            this.bindEvents();
        },
        events: {
            'click .close': function (event) {
                event.preventDefault();

                this.trigger('cancel');

                if (this.options.content && this.options.content.trigger) {
                    this.options.content.trigger('cancel', this);
                }
            },
            "click .btn-cancel": function (event) {
                event.preventDefault();

                this.trigger('cancel');

                if (this.options.content && this.options.content.trigger) {
                    this.options.content.trigger('cancel', this);
                }
            },
            "click .btn-add": "addTag",
        },

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

            //the screens have a custompath, so it has to be specified in the customPath variable that is
            //then sent to the template loader.
            var customPath = "/app/custom-screens/screen-builder/editor/";

            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({
                        mode: that.model.get("mode"), 
                    }));

                    that.applyBindings();

                    that.refresh();
                }, true, customPath);
            }, customPath, "screen_builder_add_tag_selector");

            that.options._isRendered = true;
        }
        , tagsSelectedChanged: function(){
            try {
                var that = this; 
                var tagsSelect = that.$el.find(".tags-select");
                var val = tagsSelect.val();
                var mode = that.model.get("mode"); 

                if (val.length > 1 && mode == "EDIT") {
                    tagsSelect.find(':selected:not(.selected)').prop('selected', false);
                } else {
                    tagsSelect.find(':selected').addClass('selected');
                    tagsSelect.find(':not(:selected)').removeClass('selected');
                }
            } catch (error) { console.log("Error: " + error.toString()); }
        }
        , addTag: function () {
            var that = this;
            var tagsSelect = this.$el.find(".tags-select");
            var val = tagsSelect.val();

            var tags = that.model.get("tags"); 
            var vals = []; 
            _.each(val, function (v) {
                vals.push(_.findWhere(tags, { id: parseInt(v, 10) })); 
            }); 

            this.trigger("tagsSelected", vals);

            this.hide();
        }
        , refresh: function () {
            try {
                var that = this;

                this.model.fetchTags({
                    callback: function () {
                        that.renderTagList(); 
                    }
                }, this.model.get("searchQuery"));
            } catch (Error) { }
        }
        , renderTagList: function () {
            var that = this; 
            var template = Handlebars.compile(this.$el.find(".tags-multiple-select-template").html());
            var rendered = template(this.model.toJSON());
            this.$el.find(".tags-select-container").html(rendered);
            this.$el.find(".tags-select").on("change", _.bind(that.tagsSelectedChanged, that));
        }
        , _startAutoRefresh: function () {
            try {
                if (this.options.MYREFERENCES.autoRefresh.toid != null) {
                    clearTimeout(this.options.MYREFERENCES.autoRefresh.toid);
                    this.options.MYREFERENCES.autoRefresh.toid = null;
                }

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

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

            this.refresh();

            if (this.options.MYREFERENCES.autoRefresh.enabled == true) {
                this.options.MYREFERENCES.autoRefresh.toid = setTimeout(_.bind(this._autoRefresh, this), 3000);
            }
        }
        , _stopAutoRefresh: function () {
            if (this.options.MYREFERENCES.autoRefresh.toid != null) {
                clearTimeout(this.options.MYREFERENCES.autoRefresh.toid);
                this.options.MYREFERENCES.autoRefresh.toid = null;
            }
            this.options.MYREFERENCES.autoRefresh.enabled = false;
        }
        , show: function () {

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

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

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

            $backdrop = $('.modal-backdrop');

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

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

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

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

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

            return this;

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

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

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

                self.trigger('hidden');

                self.close();
            });

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

            //rendering as soon as getting data in the model
            this.listenTo(this.model, "change:searchQuery", this.refresh);
        }
        , close: function () {
            this._stopAutoRefresh();
            this.remove();
            this.unbind();
        }
        , preRender: function () {
        }
        , reRender: function () {
        }
    });

    ScreenBuilder.Models.ChartBuilderModal = Backbone.Epoxy.Model.extend({
        defaults: {
            id: null, //screen_id
            searchQuery: null,
            agents: [],
            tags: [],

            chartTitle: null,
            from: null,
            to: null,
            mode: "CREATE", 
        },
        initialize: function () {
            var opts = {};
            this.options = _.extend(this.options ? this.options : {}, opts);

            var toDate = new Date();
            var fromDate = new Date(toDate - 60000 * 5);

            this.set({
                from: new moment(fromDate).format("YYYY-MM-DD HH:mm:ss"),
                to: new moment(toDate).format("YYYY-MM-DD HH:mm:ss"),
            }, { silent: true });
        },
        fetchTags: function (opt, search) {
            var options = {
                method: "set",
                async: true,
                callback: null,
            };

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

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

            QP.Add("searchQuery", "VARCHAR", (_.isString(search) && search.trim() != '') ? search : null);

            var items = [];
            var children = {};

            Core.Json.CallProcedure(app.DatabaseNames.System + ".SBuilder.GetTags", QP, {
                onSuccess: function (data) {
                    if (data && data.Table) {
                        var data = data.Table;
                        var agents = _.uniq(_.pluck(data, "AgentName"));

                        _.each(agents, function (ag) {
                            var item = {
                                name: ag,
                                tags: _.map(_.where(data, { AgentName: ag }), function (tag) {
                                    return {
                                        id: tag.Id,
                                        name: tag.Name,
                                        type: tag.DataType,
                                    };
                                }),
                            };

                            items.push(item);
                        });
                    }

                    that.set("agents", items, { from: "fetch" });
                    that.set("tags", _.map(data, function (t) { return { id: t.Id, name: t.Name, type: t.DataType, agentName: t.AgentName } }));

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

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

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

            if (this.options.viewParams) {
            }

            this.model = new ScreenBuilder.Models.ChartBuilderModal({
                id: (this.options.id) ? this.options.id : null,
            });

            this.options.viewModels = {
                fromControl: new DatetimeControl.Model({
                    datetime: this.model.get("from"), 
                }),
                toControl: new DatetimeControl.Model({
                    datetime: this.model.get("to"), 
                }),
            };

            this.options._isRendered = false;

            this.bindEvents();
        },
        events: {
            'click .close': function (event) {
                event.preventDefault();

                this.trigger('cancel');

                if (this.options.content && this.options.content.trigger) {
                    this.options.content.trigger('cancel', this);
                }
            },
            "click .btn-cancel": function (event) {
                event.preventDefault();

                this.trigger('cancel');

                if (this.options.content && this.options.content.trigger) {
                    this.options.content.trigger('cancel', this);
                }
            },
            "click .btn-add": "addChart",
        },

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

            //the screens have a custompath, so it has to be specified in the customPath variable that is
            //then sent to the template loader.
            var customPath = "/app/custom-screens/screen-builder/editor/";

            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({
                        mode: that.model.get("mode"), 
                    }));

                    var dc = new DatetimeControl.Views.Main({
                        model: that.options.viewModels.fromControl,
                    });
                    dc.render(that.$el.find(".from-picker"));

                    var dc = new DatetimeControl.Views.Main({
                        model: that.options.viewModels.toControl,
                    });
                    dc.render(that.$el.find(".to-picker"));

                    that.applyBindings();

                    that.refresh();
                }, true, customPath);
            }, customPath, "screen_builder_chart_builder_modal");

            that.options._isRendered = true;
        }
        , addChart: function () {
            var that = this;
            var tagsSelect = this.$el.find(".tags-select");
            var val = tagsSelect.val();

            var tags = that.model.get("tags");
            var vals = [];
            _.each(val, function (v) {
                vals.push(_.findWhere(tags, { id: parseInt(v, 10) }));
            });

            var properties = {
                title: this.model.get("chartTitle"),
                from: this.model.get("from"),
                to: this.model.get("to"),
                tags: vals, 
            }; 

            this.trigger("chartSelected", properties);

            this.hide();
        }
        , refresh: function () {
            try {
                var that = this;

                this.model.fetchTags({
                    callback: function () {
                        that.renderTagList();
                    }
                }, this.model.get("searchQuery"));
            } catch (Error) { }
        }
        , renderTagList: function () {
            var template = Handlebars.compile(this.$el.find(".tags-multiple-select-template").html());
            var rendered = template(this.model.toJSON());
            this.$el.find(".tags-select-container").html(rendered);
        }
        , _startAutoRefresh: function () {
            try {
                if (this.options.MYREFERENCES.autoRefresh.toid != null) {
                    clearTimeout(this.options.MYREFERENCES.autoRefresh.toid);
                    this.options.MYREFERENCES.autoRefresh.toid = null;
                }

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

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

            this.refresh();

            if (this.options.MYREFERENCES.autoRefresh.enabled == true) {
                this.options.MYREFERENCES.autoRefresh.toid = setTimeout(_.bind(this._autoRefresh, this), 3000);
            }
        }
        , _stopAutoRefresh: function () {
            if (this.options.MYREFERENCES.autoRefresh.toid != null) {
                clearTimeout(this.options.MYREFERENCES.autoRefresh.toid);
                this.options.MYREFERENCES.autoRefresh.toid = null;
            }
            this.options.MYREFERENCES.autoRefresh.enabled = false;
        }
        , show: function () {

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

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

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

            $backdrop = $('.modal-backdrop');

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

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

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

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

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

            return this;

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

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

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

                self.trigger('hidden');

                self.close();
            });

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

            //rendering as soon as getting data in the model
            this.listenTo(this.model, "change:searchQuery", this.refresh);

            this.listenTo(this.options.viewModels.fromControl, "change:datetime", this.fromToModelChanged);
            this.listenTo(this.options.viewModels.toControl, "change:datetime", this.fromToModelChanged);
        }
        , fromToModelChanged: function (a, b, c) {
            this.model.set({
                from: new moment(this.options.viewModels.fromControl.get("datetime")).format("YYYY-MM-DD HH:mm:ss"),
                to: new moment(this.options.viewModels.toControl.get("datetime")).format("YYYY-MM-DD HH:mm:ss"),
            });
        }
        , close: function () {
            this._stopAutoRefresh();
            this.remove();
            this.unbind();
        }
        , preRender: function () {
        }
        , reRender: function () {
        }
    });

    ScreenBuilder.Models.AddTextModal = Backbone.Model.extend({
        defaults: {
            id: null, //screen_id
            content: null, 
        },
        initialize: function () {
            var opts = {};
            this.options = _.extend(this.options ? this.options : {}, opts);
        },
        fetch: function (options) {
            var that = this;
        },
    });

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

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

            if (this.options.viewParams) {

            }

            this.options.viewModels = {
            };

            this.options.nicEditor = null; 
            this.options._isRendered = false;

            this.model = new ScreenBuilder.Models.AddTextModal({
                id: (this.options.id) ? this.options.id : null,
                content: (this.options.content) ? this.options.content : null,
            });

            this.bindEvents();
        },
        events: {
            'click .close': function (event) {
                event.preventDefault();

                this.trigger('cancel');

                if (this.options.content && this.options.content.trigger) {
                    this.options.content.trigger('cancel', this);
                }
            },
            "click .btn-cancel": function (event) {
                event.preventDefault();

                this.trigger('cancel');

                if (this.options.content && this.options.content.trigger) {
                    this.options.content.trigger('cancel', this);
                }
            },
            "click .btn-add": "addText",
        },

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

            //the screens have a custompath, so it has to be specified in the customPath variable that is
            //then sent to the template loader.
            var customPath = "/app/custom-screens/screen-builder/editor/";

            T.render.call(this, this.template, function (tmp) {
                if (!that.options.i18n) that.options.i18n = {};
                app.getI18NJed(that, that.template, function (i18nJED) {

                    //storing internationalization data
                    that.options.i18n[that.template] = i18nJED;

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

                    that.applyBindings();

                    var content = that.$el.find(".content-textarea");

                    //content.on("keyup", _.bind(that.checkContent, that)); 
                    //var editor = new MediumEditor(content.get(0));

                    that.on("shown", function () {
                        that.options.nicEditor =
                        new window.nicEditor({
                            iconsPath: app.foldersRoot + "/app/custom-screens/screen-builder/editor/api/js/nicEditorIcons.gif",
                            buttonList: [
                                'fontSize',
                                'fontFamily',
                                'fontFormat',
                                'bold',
                                'italic',
                                'underline',
                                'left',
                                'center',
                                'right',
                                'justify',
                                'ol',
                                'ul',
                                'subscript',
                                'superscript',
                                'strikethrough',
                                'removeformat',
                                'indent',
                                'outdent',
                                'hr',
                                'forecolor',
                                'bgcolor',
                            ], 
                        }).panelInstance(content.get(0));

                        textContent = _.isString(that.model.get("content")) ? that.model.get("content") : "";
                        that.options.nicEditor.nicInstances[0].setContent(textContent);
                        that.$el.find(".nicEdit-main").focus(); 
                    }); 

                    that.options._isRendered = true;
                    that.options.rendering = false;
                }, true, customPath);
            }, customPath, "screen_builder_add_text");

            that.options.rendering = true; 
            that.refresh();
        }
        , checkContent: function () {
            var that = this; 
            var content = that.$el.find(".content-textarea");
            //var val = $(content.html());

            var txt = content.text(); 
            var val = $('<div>').html($("<span>").html(txt)); 
            val.find("span").css({ "font-weight": "bold" });

            content.html(val.html()); 
        }
        , addText: function () {
            var that = this;
            //var content = that.$el.find(".content-textarea"); 
            //var val = $("<span>").html(content.html());
            var content = that.options.nicEditor.nicInstances[0].getContent(); 
            var val = $("<span>").html(content);

            this.trigger("textSelected", val);

            this.hide();
        }
        , refresh: function () {
            try {
                this.model.fetch();
            } catch (Error) { }
        }
        , _startAutoRefresh: function () {
            try {
                if (this.options.MYREFERENCES.autoRefresh.toid != null) {
                    clearTimeout(this.options.MYREFERENCES.autoRefresh.toid);
                    this.options.MYREFERENCES.autoRefresh.toid = null;
                }

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

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

            this.refresh();

            if (this.options.MYREFERENCES.autoRefresh.enabled == true) {
                this.options.MYREFERENCES.autoRefresh.toid = setTimeout(_.bind(this._autoRefresh, this), 3000);
            }
        }
        , _stopAutoRefresh: function () {
            if (this.options.MYREFERENCES.autoRefresh.toid != null) {
                clearTimeout(this.options.MYREFERENCES.autoRefresh.toid);
                this.options.MYREFERENCES.autoRefresh.toid = null;
            }
            this.options.MYREFERENCES.autoRefresh.enabled = false;
        }
        , show: function () {

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

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

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

            $el.on("shown", function () {
                self.trigger("shown"); 
            }); 

            $backdrop = $('.modal-backdrop');

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

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

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

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

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

            return this;

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

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

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

                self.trigger('hidden');

                self.close();
            });

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

            //rendering as soon as getting data in the model
            //this.listenTo(this.model, "change", this.render);
        }
        , close: function () {
            this._stopAutoRefresh();
            this.remove();
            this.unbind();
        }
        , preRender: function () {
        }
        , reRender: function () {
        }
    });

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

});
