APIs

Show:
/**
 Singleton Screen Layout editor is used to create / edit an existing screen division layout (aka: template > viewers) and when done
 editing, create new ScreenTemplates via ScreenTemplateFactory for both Thumb and main Timeline UIs
 @class ScreenLayoutEditorView
 @constructor
 @return {object} instantiated ScreenLayoutEditorView
 **/
define(['jquery', 'backbone', 'StackView', 'ScreenTemplateFactory'], function ($, Backbone, StackView, ScreenTemplateFactory) {

    /**
     Custom event fired when a screen division / viewer has been removed
     @event VIEWER_REMOVED
     @param {This} caller
     @param {Self} context caller
     @param {Event} rss link
     @static
     @final
     **/
    BB.EVENTS.VIEWER_REMOVED = 'VIEWER_REMOVED';

    BB.SERVICES.SCREEN_LAYOUT_EDITOR_VIEW = 'ScreenLayoutEditorView';

    var ScreenLayoutEditorView = BB.View.extend({

        /**
         Constructor
         @method initialize
         **/
        initialize: function () {
            var self = this;
            BB.comBroker.setService(BB.SERVICES['SCREEN_LAYOUT_EDITOR_VIEW'], self);
            self.RATIO = 4;
            self.m_canvas = undefined;
            self.m_canvasID = undefined;
            self.m_selectedViewerID = undefined;
            self.m_dimensionProps = undefined;

            this.m_property = BB.comBroker.getService(BB.SERVICES['PROPERTIES_VIEW']);
            self.m_property.initPanel(Elements.VIEWER_EDIT_PROPERTIES);

            $(this.el).find('#prev').on('click', function (e) {
                self._deSelectView();
                BB.comBroker.fire(BB.EVENTS.CAMPAIGN_TIMELINE_CHANGED, self);
                return false;
            });

            self._listenAddDivision();
            self._listenRemoveDivision();
            self._listenPushToTopDivision();
            self._listenPushToBottomDivision();
            self._listenSelectNextDivision();

            self.listenTo(self.options.stackView, BB.EVENTS.SELECTED_STACK_VIEW, function (e) {
                if (e == self) {
                    if (self.m_dimensionProps == undefined) {
                        require(['DimensionProps'], function (DimensionProps) {
                            self.m_dimensionProps = new DimensionProps({
                                appendTo: Elements.VIEWER_DIMENSIONS,
                                showAngle: false
                            });
                            $(self.m_dimensionProps).on('changed', function (e) {
                                var props = e.target.getValues();
                                self._updateDimensionsInDB(self.m_canvas.getActiveObject(), props);
                                self._moveViewer(props);
                            });
                            self._render();
                        });
                    } else {
                        self._render();
                    }
                }
            });
        },

        /**
         Load the editor into DOM using the StackView using animation slider
         @method  selectView
         **/
        selectView: function (i_campaign_timeline_id, i_campaign_timeline_board_template_id) {
            var self = this;
            self.m_campaign_timeline_id = i_campaign_timeline_id;
            self.m_campaign_timeline_board_template_id = i_campaign_timeline_board_template_id;
            self.m_global_board_template_id = pepper.getGlobalTemplateIdOfTimeline(i_campaign_timeline_board_template_id);
            self.m_screenProps = pepper.getTemplateViewersScreenProps(self.m_campaign_timeline_id, self.m_campaign_timeline_board_template_id);
            self.m_orientation = BB.comBroker.getService(BB.SERVICES['ORIENTATION_SELECTOR_VIEW']).getOrientation();
            self.m_resolution = BB.comBroker.getService(BB.SERVICES['RESOLUTION_SELECTOR_VIEW']).getResolution();

            self.options.stackView.slideToPage(self, 'right');

            require(['fabric'], function () {
                var w = parseInt(self.m_resolution.split('x')[0]) / self.RATIO;
                var h = parseInt(self.m_resolution.split('x')[1]) / self.RATIO;

                self._canvasFactory(w, h);
                self._listenObjectChanged();
                self._listenObjectsOverlap();
                self._listenBackgroundSelected();
            })
        },

        /**
         On render load default dashboard properties
         @method _render
         **/
        _render: function () {
            var self = this;
            self.m_property.resetPropertiesView();
        },

        /**
         Listen to the addition of a new viewer
         @method (totalViews - i)
         **/
        _listenAddDivision: function () {
            var self = this;
            $(Elements.LAYOUT_EDITOR_ADD_NEW, self.$el).on('click', function () {
                var props = {
                    x: 0,
                    y: 0,
                    w: 100,
                    h: 100
                }
                var board_viewer_id = pepper.createViewer(self.m_global_board_template_id, props);
                var campaign_timeline_chanel_id = pepper.createTimelineChannel(self.m_campaign_timeline_id);
                pepper.assignViewerToTimelineChannel(self.m_campaign_timeline_board_template_id, board_viewer_id, campaign_timeline_chanel_id);

                var viewer = new fabric.Rect({
                    left: 0,
                    top: 0,
                    fill: '#ececec',
                    id: board_viewer_id,
                    hasRotatingPoint: false,
                    borderColor: '#5d5d5d',
                    stroke: 'black',
                    strokeWidth: 1,
                    borderScaleFactor: 0,
                    lineWidth: 1,
                    width: 100,
                    height: 100,
                    cornerColor: 'black',
                    cornerSize: 5,
                    lockRotation: true,
                    transparentCorners: false
                });
                self.m_canvas.add(viewer);

                var props = {
                    x: 0,
                    y: 0,
                    w: viewer.get('width') * self.RATIO,
                    h: viewer.get('height') * self.RATIO
                }
                self._updateDimensionsInDB(viewer, props);
            });
        },

        /**
         Listen to the removal of an existing screen division
         @method _listenRemoveDivision
         **/
        _listenRemoveDivision: function () {
            var self = this;
            $(Elements.LAYOUT_EDITOR_REMOVE, self.$el).on('click', function () {
                if(_.isUndefined(self.m_canvas))
                    return;
                var totalViews = self.m_canvas.getObjects().length;
                if (totalViews < 2) {
                    bootbox.alert($(Elements.MSG_BOOTBOX_AT_LEAST_ONE_DIV).text());
                    return;
                }
                var campaign_timeline_chanel_id = pepper.removeTimelineBoardViewerChannel(self.m_selectedViewerID);
                pepper.removeBoardTemplateViewer(self.m_campaign_timeline_board_template_id, self.m_selectedViewerID);
                pepper.removeChannelFromTimeline(campaign_timeline_chanel_id);
                pepper.removeBlocksFromTimelineChannel(campaign_timeline_chanel_id);
                self.m_canvas.remove(self.m_canvas.getActiveObject());
                self.m_canvas.renderAll();
                /*var viewer = self.m_canvas.item(0);
                var props = {
                    y: viewer.get('top'),
                    x: viewer.get('left'),
                    w: viewer.get('width') * self.RATIO,
                    h: viewer.get('height') * self.RATIO
                };
                self._updateDimensionsInDB(viewer, props);*/
                BB.comBroker.fire(BB.EVENTS.VIEWER_REMOVED, this, this, {
                    campaign_timeline_chanel_id: campaign_timeline_chanel_id
                });
                pepper.announceTemplateViewerEdited(self.m_campaign_timeline_board_template_id);
            });
        },

        /**
         Listen to re-order of screen division, putting selected on top
         @method _listenPushToTopDivision
         **/
        _listenPushToTopDivision: function () {
            var self = this;
            $(Elements.LAYOUT_EDITOR_PUSH_TOP, self.$el).on('click', function () {
                var viewer = self.m_canvas.getActiveObject();
                if (!viewer)
                    return;
                self.m_canvas.bringToFront(viewer);
                self._updateZorder();
            });
        },

        /**
         Listen to re-order of screen division, putting selected at bottom
         @method _listenPushToBottomDivision
         **/
        _listenPushToBottomDivision: function () {
            var self = this;
            $(Elements.LAYOUT_EDITOR_PUSH_BOTTOM, self.$el).on('click', function () {
                var viewer = self.m_canvas.getActiveObject();
                if (!viewer)
                    return;
                self.m_canvas.sendToBack(viewer);
                self._updateZorder();
            });
        },

        /**
         Change the z-order of viewers in pepper
         @method _updateZorder
         **/
        _updateZorder: function () {
            var self = this;
            var viewers = self.m_canvas.getObjects();
            for (var i in viewers){
                // log(viewers[i].get('id') + ' ' + i);
                pepper.updateTemplateViewerOrder(viewers[i].get('id'), i);
            }
            var active = self.m_canvas.getActiveObject();
            self.m_canvas.setActiveObject(active);
        },

        /**
         Listen to selection of next viewer
         @method _listenSelectNextDivision
         **/
        _listenSelectNextDivision: function () {
            var self = this;
            $(Elements.LAYOUT_EDITOR_NEXT, self.$el).on('click', function () {
                var viewer = self.m_canvas.getActiveObject();
                var viewIndex = self.m_canvas.getObjects().indexOf(viewer);
                var totalViews = self.m_canvas.getObjects().length;
                if (viewIndex == totalViews - 1) {
                    self.m_canvas.setActiveObject(self.m_canvas.item(0));
                } else {
                    self.m_canvas.setActiveObject(self.m_canvas.item(viewIndex + 1));
                }
            });
        },

        /**
         Unload the editor from DOM using the StackView animated slider
         @method  selectView
         **/
        _deSelectView: function () {
            var self = this;
            self._destroy();
            self.m_property.resetPropertiesView();
            self.options.stackView.slideToPage(self.options.from, 'left');
        },

        /**
         Create the canvas to render the screen division
         @method _canvasFactory
         @param {Number} i_width
         @param {Number} i_height
         **/
        _canvasFactory: function (i_width, i_height) {
            var self = this;

            var offsetH = i_height / 2;
            var offsetW = (i_width / 2) + 30;
            self.m_canvasID = _.uniqueId('screenLayoutEditorCanvas');
            $('#screenLayoutEditorCanvasWrap').append('' +
                '<div>' +
                '<span align="center">' + self.m_resolution.split('x')[0] + 'px </span>' +
                '<canvas id="' + self.m_canvasID + '" width="' + i_width + 'px" height="' + i_height + 'px" style="border: 1px solid rgb(170, 170, 170);"></canvas>' +
                '<span style="position: relative; top: -' + offsetH + 'px; left: -' + offsetW + 'px;">' + self.m_resolution.split('x')[1] + 'px</span>' +
                '</div>');

            self.m_canvas = new fabric.Canvas(self.m_canvasID);
            self.m_canvas.selection = false;

            var screenTemplateData = {
                orientation: self.m_orientation,
                resolution: self.m_resolution,
                screenProps: self.m_screenProps,
                scale: self.RATIO
            };

            var screenTemplate = new ScreenTemplateFactory({
                i_screenTemplateData: screenTemplateData,
                i_selfDestruct: true,
                i_owner: this});

            var rects = screenTemplate.getDivisions();

            for (var i = 0; i < rects.length; i++) {
                var rectProperties = rects[i];
                var rect = new fabric.Rect({
                    left: rectProperties.x.baseVal.value,
                    top: rectProperties.y.baseVal.value,
                    fill: '#ececec',
                    id: $(rectProperties).data('campaign_timeline_board_viewer_id'),
                    hasRotatingPoint: false,
                    borderColor: '#5d5d5d',
                    stroke: 'black',
                    strokeWidth: 1,
                    borderScaleFactor: 0,
                    lineWidth: 1,
                    width: rectProperties.width.baseVal.value,
                    height: rectProperties.height.baseVal.value,
                    cornerColor: 'black',
                    cornerSize: 5,
                    lockRotation: true,
                    transparentCorners: false
                });
                self.m_canvas.add(rect);

                //rect.on('selected', function () {
                //  log('object selected a rectangle');
                //});
            }

            //self.m_canvas.on('object:moving', function (e) {
            //    log('savings: ' + self.m_global_board_template_id);
            //});

            setTimeout(function () {
                if (!self.m_canvas)
                    return;
                self.m_canvas.setHeight(i_height);
                self.m_canvas.setWidth(i_width);
                self.m_canvas.renderAll();
            }, 500);
        },

        /**
         Listen to changes on selecting the background canvas
         @method _listenBackgroundSelected
         **/
        _listenBackgroundSelected: function () {
            var self = this;
            self.m_bgSelectedHandler = function (e) {
                self.m_property.resetPropertiesView();
            };
            self.m_canvas.on('selection:cleared', self.m_bgSelectedHandler);
        },

        /**
         Listen to changes in viewer overlaps
         @method _listenObjectsOverlap
         **/
        _listenObjectsOverlap: function () {
            var self = this;
            self.m_onOverlap = function (options) {
                options.target.setCoords();
                self.m_canvas.forEachObject(function (obj) {
                    if (obj === options.target) return;
                    obj.setOpacity(options.target.intersectsWithObject(obj) ? 0.5 : 1);
                });
            }

            self.m_canvas.on({
                'object:moving': self.m_onOverlap,
                'object:scaling': self.m_onOverlap,
                'object:rotating': self.m_onOverlap
            });
        },

        /**
         Make sure that at least one screen division is visible within the canvas
         @method _enforceViewerVisible
         **/
        _enforceViewerVisible: function () {
            var self = this;
            var pass = 0;
            var viewer;
            self.m_canvas.forEachObject(function (o) {
                viewer = o;
                if (pass)
                    return;
                if (o.get('left') < (0 - o.get('width')) + 20) {
                } else if (o.get('left') > self.m_canvas.getWidth() - 20) {
                } else if (o.get('top') < (0 - o.get('height') + 20)) {
                } else if (o.get('top') > self.m_canvas.getHeight() - 20) {
                } else {
                    pass = 1;
                }
            });
            if (!pass && viewer) {
                viewer.set({left: 0, top: 0}).setCoords();
                viewer.setCoords();
                self.m_canvas.renderAll();
                bootbox.alert({
                    message: $(Elements.MSG_BOOTBOX_AT_LEAST_ONE_DIV).text(),
                    title: $(Elements.MSG_BOOTBOX_SCREEN_DIV_POS_RESET).text()
                });
                var props = {
                    x: viewer.get('top'),
                    y: viewer.get('left'),
                    w: viewer.get('width') * self.RATIO,
                    h: viewer.get('height') * self.RATIO
                }
                self._updateDimensionsInDB(viewer, props);
            }
        },

        /**
         Enforce minimum x y w h props
         @method self._enforceViewerMinimums(i_viewer);
         @param {Object} i_rect
         **/
        _enforceViewerMinimums: function (i_viewer) {
            var self = this;
            var MIN_SIZE = 50;
            if ((i_viewer.width * self.RATIO) < MIN_SIZE || (i_viewer.height * self.RATIO) < MIN_SIZE) {
                i_viewer.width = MIN_SIZE / self.RATIO;
                i_viewer.height = MIN_SIZE / self.RATIO;
                var props = {
                    x: i_viewer.get('top'),
                    y: i_viewer.get('left'),
                    w: MIN_SIZE,
                    h: MIN_SIZE
                }
                self._updateDimensionsInDB(i_viewer, props);
            }
        },

        /**
         Listen to changes in a viewer changes in cords and update pepper
         @method i_props
         **/
        _listenObjectChanged: function () {
            var self = this;
            self.m_objectMovingHandler = _.debounce(function (e) {
                var o = e.target;
                if (o.width != o.currentWidth || o.height != o.currentHeight) {
                    o.width = o.currentWidth;
                    o.height = o.currentHeight;
                    o.scaleX = 1;
                    o.scaleY = 1;
                }

                self._enforceViewerMinimums(o);
                self._enforceViewerVisible();

                var x = BB.lib.parseToFloatDouble(o.left) * self.RATIO;
                var y = BB.lib.parseToFloatDouble(o.top) * self.RATIO;
                var w = BB.lib.parseToFloatDouble(o.currentWidth) * self.RATIO;
                var h = BB.lib.parseToFloatDouble(o.currentHeight) * self.RATIO;
                // var a = o.get('angle');
                var props = {
                    w: w,
                    h: h,
                    x: x,
                    y: y
                };
                self.m_property.viewPanel(Elements.VIEWER_EDIT_PROPERTIES);
                self.m_dimensionProps.setValues(props);
                self.m_selectedViewerID = o.id;
                self._updateDimensionsInDB(o, props);

            }, 200);

            self.m_canvas.on({
                'object:moving': self.m_objectMovingHandler,
                'object:scaling': self.m_objectMovingHandler,
                'object:selected': self.m_objectMovingHandler,
                'object:modified': self.m_objectMovingHandler
            });
        },

        /**
         Move the object / viewer to new set of coords
         @method _moveViewer
         @param {Object} i_props
         **/
        _moveViewer: function (i_props) {
            var self = this;
            // log('moving viewer');
            var viewer = self.m_canvas.getActiveObject();
            if (viewer) {
                viewer.setWidth(i_props.w / self.RATIO);
                viewer.setHeight(i_props.h / self.RATIO);
                viewer.set('left', i_props.x / self.RATIO);
                viewer.set('top', i_props.y / self.RATIO);

                self._enforceViewerMinimums(viewer);
                self._enforceViewerVisible();

                viewer.setCoords();
                self.m_canvas.renderAll();
            }
        },

        /**
         Update Pepper with latest object dimensions
         @method _updateDimensionsInDB
         @param {Object} i_props
         **/
        _updateDimensionsInDB: function (i_viewer, i_props) {
            var self = this;
            log('Pepper ' +i_viewer.get('id') + ' ' + JSON.stringify(i_props));
            pepper.setBoardTemplateViewer(self.m_campaign_timeline_board_template_id, i_viewer.get('id'), i_props);
            i_viewer.setCoords();
            self.m_canvas.renderAll();
        },

        /**
         One exit UI destroy all members
         @method _destroy
         **/
        _destroy: function () {
            var self = this;
            if (!_.isUndefined(self.m_canvas)){
                self.m_canvas.off('selection:cleared', self.m_bgSelectedHandler);
                self.m_canvas.off({
                    'object:moving': self.m_objectMovingHandler,
                    'object:scaling': self.m_objectMovingHandler,
                    'object:selected': self.m_objectMovingHandler,
                    'object:modified': self.m_objectMovingHandler
                });
                self.m_canvas.off({
                    'object:moving': self.m_onOverlap,
                    'object:scaling': self.m_onOverlap,
                    'object:rotating': self.m_onOverlap
                });
                self.m_canvas.clear().renderAll();
            }
            $('#screenLayoutEditorCanvasWrap').empty()
            self.m_canvasID = undefined;
            self.m_canvas = undefined;
            self.m_campaign_timeline_id = undefined;
            self.m_campaign_timeline_board_template_id = undefined;
            self.m_screenProps = undefined;
            self.m_orientation = undefined;
            self.m_resolution = undefined;
            self.m_global_board_template_id = undefined;
            self.m_selectedViewerID = undefined;
        }
    });

    return ScreenLayoutEditorView;
});