APIs

Show:
/**
 This base class for all the Blocks / players which reside on the timeline_channel or inside scenes.
 The base class implements basic timeline and scene interfaces including the management the properties UI.
 @class Block
 @constructor
 @param {string} i_placement indicates if the block is set to exist inside a timeline or inside a scene
 @param {string} i_block_id block / player id, only required if block inserted onto channel_timeline
 @return none
 **/
define(['jquery', 'backbone'], function ($) {

    /**
     Quiet mode, don't announce
     @property BB.CONSTS.NO_NOTIFICATION
     @static
     @final
     @type String
     */
    BB.CONSTS.NO_NOTIFICATION = true;

    /**
     event fires when scene block was changed so scene needs to be re-rendered
     @event Block.SCENE_BLOCK_CHANGE
     @param {this} caller
     @param {String} selected block_id
     **/
    BB.EVENTS.SCENE_BLOCK_CHANGE = 'SCENE_BLOCK_CHANGE';

    /**
     event fires when block border changed so scene needs to be re-rendered
     @event Block.BLOCK_BORDER_CHANGE
     @param {this} caller
     @param {String} selected block_id
     **/
    BB.EVENTS.BLOCK_BORDER_CHANGE = 'BLOCK_BORDER_CHANGE';

    /**
     event fires when scene blocks freshly re-rendered onto the scene canvas so we need to update the UI of ALL blocks
     normally occurs after a Block.SCENE_BLOCK_CHANGE event
     @event Block.SCENE_BLOCKS_RENDERED
     @param {this} caller
     @param {String} selected block_id
     **/
    BB.EVENTS.SCENE_BLOCKS_RENDERED = 'SCENE_BLOCKS_RENDERED';

    /**
     event fires when block is selected
     @event Block.BLOCK_SELECTED
     @param {this} caller
     @param {String} selected block_id
     **/
    BB.EVENTS.BLOCK_SELECTED = 'BLOCK_SELECTED';

    /**
     Custom event fired when a block state changes
     @event LOCK_CHANGED
     @param {This} caller
     @param {Self} context caller
     @param {Event}
     @static
     @final
     **/
    BB.EVENTS.LOCK_CHANGED = 'LOCK_CHANGED';

    var Block = BB.Controller.extend({

        /**
         Constructor
         @method initialize
         **/
        initialize: function (options) {
            var self = this;
            self.m_placement = options.i_placement;
            self.m_block_id = options.i_block_id;
            self.m_sceneID = options.i_scene_player_data_id;
            self.m_blockType = options.blockType;
            self.m_selected = false;
            self.m_zIndex = -1;
            self.m_minSize = {w: 50, h: 50};
            self.m_blockName = BB.PepperHelper.getBlockBoilerplate(self.m_blockType).name;
            self.m_blockAcronym = BB.PepperHelper.getBlockBoilerplate(self.m_blockType).acronym;
            self.m_blockDescription = BB.PepperHelper.getBlockBoilerplate(self.m_blockType).description;
            self.m_blockIcon = BB.PepperHelper.getBlockBoilerplate(self.m_blockType).icon;
            self.m_blockFontAwesome = BB.PepperHelper.getBlockBoilerplate(self.m_blockType).fontAwesome;
            self.m_blockSvg = BB.PepperHelper.getBlockBoilerplate(self.m_blockType).svg;
            self.m_resourceID = undefined;
            self.m_blockProperty = BB.comBroker.getService(BB.SERVICES['BLOCK_PROPERTIES']);

            self._listenAlphaChange();
            self._listenToggleLock();
            self._listenGradientChange();
            self._listenGradientColorPickerClosed();
            self._listenBackgroundStateChange();
            self._listenBorderStateChange();
            self._listenBorderColorChange();
            self._listenBlockSelected();
            self._onBlockLengthChanged();
        },

        /**
         Init the sub properties panel for a block
         @method _initSubPanel
         @param {String} i_panel
         **/
        _initSubPanel: function (i_panel) {
            var self = this;
            self.m_blockProperty.initSubPanel(i_panel);
        },

        /**
         Bring into view a sub properties panel of this block
         @method _viewSubPanel
         @param {String} i_panel
         **/
        _viewSubPanel: function (i_panel) {
            var self = this;
            self.m_blockProperty.viewSubPanel(i_panel);
        },

        /**
         On changes in msdb model updated UI common lock properties
         @method _fabricLock
         **/
        _fabricLock: function () {
            var self = this;
            var domPlayerData = self._getBlockPlayerData();
            var locked = $(domPlayerData).attr('locked');
            if (_.isUndefined(locked) || locked == '0') {
                locked = false;
            } else {
                locked = true;
            }
            self.lockMovementX = locked;
            self.lockMovementY = locked;
            //self.lockScalingX = locked; self.lockScalingY = locked; self.lockUniScaling = locked; self.lockRotation = locked;
            if (!self.m_selected)
                return;
            var dimensionProps = BB.comBroker.getService(BB.SERVICES['DIMENSION_PROPS_LAYOUT']);
            if (_.isUndefined(dimensionProps))
                return;
            dimensionProps.setLock(locked);
        },

        /**
         Toggle lock status
         @method _toggleLock
         **/
        _listenToggleLock: function () {
            var self = this;
            self._toggleLock = function (e) {
                if (!self.m_selected)
                    return;
                self.lockMovementX = e.edata;
                self.lockMovementY = e.edata;
                //self.lockScalingX = e.edata; self.lockScalingY = e.edata; self.lockUniScaling = e.edata; self.lockRotation = e.edata;
                var v = e.edata == true ? 1 : 0;
                var domPlayerData = self._getBlockPlayerData();
                $(domPlayerData).attr('locked', v);
                self._setBlockPlayerData(domPlayerData, BB.CONSTS.NO_NOTIFICATION);
            };
            BB.comBroker.listenWithNamespace(BB.EVENTS.LOCK_CHANGED, self, self._toggleLock);
        },


        /**
         Listen to changes in Alpha UI properties selection and update msdb
         @method _listenAlphaChange
         **/
        _listenAlphaChange: function () {
            var self = this;
            self._alphaChanged = _.debounce(function (e) {
                if (!self.m_selected)
                    return;
                var alpha = e.edata;
                var domPlayerData = self._getBlockPlayerData();
                var data = $(domPlayerData).find('Data').eq(0);
                var xSnippet = $(data).find('Appearance').eq(0);
                $(xSnippet).attr('alpha', alpha);
                self._setBlockPlayerData(domPlayerData);
            }, 100);
            BB.comBroker.listenWithNamespace(BB.EVENTS.ALPHA_CHANGED, self, self._alphaChanged);
        },

        /**
         On changes in msdb model updated UI common alpha properties
         @method _alphaPopulate
         **/
        _alphaPopulate: function () {
            var self = this;
            var domPlayerData = self._getBlockPlayerData();
            var data = $(domPlayerData).find('Data').eq(0);
            var xSnippet = $(data).find('Appearance').eq(0);
            var alpha = $(xSnippet).attr('alpha');
            alpha = parseFloat(alpha) * 100;
            $(Elements.BLOCK_ALPHA_SLIDER).val(alpha);
        },

        /**
         Enable gradient background UI
         @method _enableBgSelection
         **/
        _enableBgSelection: function () {
            var self = this;
            $(Elements.SHOW_BACKGROUND).prop('checked', true);
            $(Elements.BG_COLOR_SOLID_SELECTOR).hide();
            $(Elements.BG_COLOR_GRADIENT_SELECTOR).show();
        },

        /**
         Enable gradient background UI
         @method _enableBgSelection
         **/
        _enableBorderSelection: function () {
            var self = this;
            $(Elements.SHOW_BORDER).prop('checked', true);
            $(Elements.BLOCK_BORDER_WRAP).show();
        },

        /**
         On changes in msdb model updated UI common gradient background properties
         @method _bgPropsPopulate
         **/
        _bgPropsPopulate: function () {
            var self = this;
            var gradient = $(Elements.BG_COLOR_GRADIENT_SELECTOR).data("gradientPicker-sel");
            // gradient.changeFillDirection("top"); /* change direction future support */
            gradient.removeAllPoints();
            var domPlayerData = self._getBlockPlayerData();
            var xSnippet = self._findGradientPoints(domPlayerData);
            if (xSnippet.length > 0) {
                self._enableBgSelection();
                var points = $(xSnippet).find('Point');
                $.each(points, function (i, point) {
                    var pointColor = BB.lib.decimalToHex($(point).attr('color'));
                    var pointMidpoint = (parseInt($(point).attr('midpoint')) / 250);
                    gradient.addPoint(pointMidpoint, pointColor, true);
                });
            } else {
                self._bgPropsUnpopulate();
            }
        },

        /**
         On changes in msdb model updated UI common border properties
         @method _borderPropsPopulate
         **/
        _borderPropsPopulate: function () {
            var self = this;
            var domPlayerData = self._getBlockPlayerData();
            var xSnippet = self._findBorder(domPlayerData);
            if (xSnippet.length > 0) {
                self._enableBorderSelection();
                var color = $(xSnippet).attr('borderColor');
                color = '#' + BB.lib.decimalToHex(color);
                self.m_blockProperty.setBorderBlockPropColorPicker(color);
            } else {
                self._borderPropsUnpopulate();
            }
        },

        /**
         Disable the gradient background UI
         @method _bgPropsUnpopulate
         **/
        _bgPropsUnpopulate: function () {
            var self = this;
            $(Elements.SHOW_BACKGROUND).prop('checked', false);
            $(Elements.BG_COLOR_GRADIENT_SELECTOR).hide();
            $(Elements.BG_COLOR_SOLID_SELECTOR).hide();
            var domPlayerData = self._getBlockPlayerData();
            var gradientPoints = self._findGradientPoints(domPlayerData);
            $(gradientPoints).empty();
        },

        /**
         Disable the border UI
         @method _borderPropsUnpopulate
         **/
        _borderPropsUnpopulate: function () {
            var self = this;
            $(Elements.SHOW_BORDER).prop('checked', false);
            $(Elements.BLOCK_BORDER_WRAP).hide();
            var domPlayerData = self._getBlockPlayerData();
            var border = self._findBorder(domPlayerData);
            $(border).empty();
        },

        /**
         Toggle block background on UI checkbox selection
         @method _toggleBackgroundColorHandler
         @param {event} e
         **/
        _toggleBackgroundColorHandler: function (e) {
            var self = this;
            if (!self.m_selected)
                return;

            var xBgSnippet = undefined;
            var domPlayerData = self._getBlockPlayerData();
            var checked = $(e.target).prop('checked') == true ? 1 : 0;
            if (checked) {
                self._enableBgSelection();
                xBgSnippet = BB.PepperHelper.getCommonBackgroundXML();
                var data = $(domPlayerData).find('Data').eq(0);
                var bgData = $(data).find('Background');
                if (bgData.length > 0 && !_.isUndefined(bgData.replace)) { // ie bug workaround
                    bgData.replace($(xBgSnippet));
                } else {
                    $(data).append($(xBgSnippet));
                }
                var player_data = pepper.xmlToStringIEfix(domPlayerData);
                domPlayerData = $.parseXML(player_data);
                self._setBlockPlayerData(domPlayerData, BB.CONSTS.NO_NOTIFICATION);
                self._bgPropsPopulate();
                //self._announceBlockChanged();
            } else {
                var xSnippet = self._findBackground(domPlayerData);
                $(xSnippet).remove();
                self._bgPropsUnpopulate();
                self._setBlockPlayerData(domPlayerData);
            }
        },

        /**
         Toggle block background on UI checkbox selection
         @method _toggleBackgroundColorHandler
         @param {event} e
         **/
        _toggleBorderHandler: function (e) {
            var self = this;
            if (!self.m_selected)
                return;

            var xBgSnippet = undefined;
            var domPlayerData = self._getBlockPlayerData();
            var checked = $(e.target).prop('checked') == true ? 1 : 0;
            if (checked) {
                self._enableBorderSelection();
                xBgSnippet = BB.PepperHelper.getCommonBorderXML();
                var data = $(domPlayerData).find('Data').eq(0);
                var bgData = self._findBorder(data);
                if (bgData.length > 0 && !_.isUndefined(bgData.replace)) { // ie bug workaround
                    bgData.replace($(xBgSnippet));
                } else {
                    $(data).append($(xBgSnippet));
                }
                var player_data = pepper.xmlToStringIEfix(domPlayerData);
                domPlayerData = $.parseXML(player_data);
                self._setBlockPlayerData(domPlayerData, BB.CONSTS.NO_NOTIFICATION);
                self._borderPropsPopulate();
                //self._announceBlockChanged();
            } else {
                var xSnippet = self._findBorder(domPlayerData);
                $(xSnippet).remove();
                self._borderPropsUnpopulate();
                self._setBlockPlayerData(domPlayerData);
            }
        },

        /**
         Listen to change in background enable / disable states
         @method _listenBackgroundStateChange
         **/
        _listenBackgroundStateChange: function () {
            var self = this;
            self.m_proxyToggleBg = $.proxy(self._toggleBackgroundColorHandler, self);
            self.m_proxyToggleBgKey = _.uniqueId('click.');
            $(Elements.SHOW_BACKGROUND).on(self.m_proxyToggleBgKey, self.m_proxyToggleBg);
        },

        /**
         Listen to change in background enable / disable states
         @method _listenBackgroundStateChange
         **/
        _listenBorderStateChange: function () {
            var self = this;
            self.m_proxyToggleBorder = $.proxy(self._toggleBorderHandler, self);
            self.m_proxyToggleBorderKey = _.uniqueId('click.');
            $(Elements.SHOW_BORDER).on(self.m_proxyToggleBorderKey, self.m_proxyToggleBorder);
        },

        /**
         Update a block's player_data with new gradient background
         @method _listenGradientChange
         **/
        _listenGradientChange: function () {
            var self = this;
            BB.comBroker.listenWithNamespace(BB.EVENTS.GRADIENT_COLOR_CHANGED, self, function (e) {
                if (!self.m_selected)
                    return;
                var points = e.edata.points;
                var styles = e.edata.styles;
                if (points.length == 0)
                    return;
                var domPlayerData = self._getBlockPlayerData();
                var gradientPoints = self._findGradientPoints(domPlayerData);
                $(gradientPoints).empty();
                var pointsXML = "";
                for (var i = 0; i < points.length; ++i) {
                    var pointMidpoint = (parseInt(points[i].position * 250));
                    var color = BB.lib.colorToDecimal(points[i].color);
                    var xPoint = '<Point color="' + color + '" opacity="1" midpoint="' + pointMidpoint + '" />';
                    // log(xPoint);
                    // $(gradientPoints).append(xPoint);
                    pointsXML += xPoint;
                }
                // $(domPlayerData).find('GradientPoints').html(pointsXML);
                var xmlString = (new XMLSerializer()).serializeToString(domPlayerData);
                xmlString = xmlString.replace(/<GradientPoints[ ]*\/>/, '<GradientPoints>' + pointsXML + '</GradientPoints>');
                domPlayerData = $.parseXML(xmlString);
                self._setBlockPlayerData(domPlayerData, BB.CONSTS.NO_NOTIFICATION);
            });
        },

        /**
         Update a block's player_data with new border background
         @method _listenBorderColorChange
         **/
        _listenBorderColorChange: function () {
            var self = this;
            BB.comBroker.listenWithNamespace(BB.EVENTS.BLOCK_BORDER_CHANGE, self, function (e) {
                if (!self.m_selected)
                    return;
                var color = e.edata;
                var domPlayerData = self._getBlockPlayerData();
                var border = self._findBorder(domPlayerData);
                $(border).attr('borderColor', BB.lib.hexToDecimal(color));
                self._setBlockPlayerData(domPlayerData);
            });
        },

        /**
         Update a block's player_data with new gradient background
         @method _listenGradientChange
         **/
        _listenGradientColorPickerClosed: function () {
            var self = this;
            BB.comBroker.listenWithNamespace(BB.EVENTS.GRADIENT_COLOR_CLOSED, self, function (e) {
                if (!self.m_selected)
                    return;
                var domPlayerData = self._getBlockPlayerData();
                self._setBlockPlayerData(domPlayerData);
            });
        },

        /**
         Find the background section in player_data for selected block
         @method _findBackground
         @param  {object} i_domPlayerData
         @return {Xml} xSnippet
         **/
        _findBackground: function (i_domPlayerData) {
            var self = this;
            var xSnippet = $(i_domPlayerData).find('Background');
            return xSnippet;
        },

        /**
         Find the border section in player_data for selected block
         @method _findBorder
         @param  {object} i_domPlayerData
         @return {Xml} xSnippet
         **/
        _findBorder: function (i_domPlayerData) {
            var self = this;
            return $(i_domPlayerData).find('Border');
        },

        /**
         Find the gradient blocks in player_data for selected block
         @method _findGradientPoints
         @param  {object} i_domPlayerData
         @return {Xml} xSnippet
         **/
        _findGradientPoints: function (i_domPlayerData) {
            var self = this;
            var xSnippet = $(i_domPlayerData).find('GradientPoints');
            return xSnippet;
        },

        /**
         Notify this object that it has been selected so it can populate it's own the properties box etc
         The function triggers from the BLOCK_SELECTED event.
         @method _listenBlockSelected
         @return none
         **/
        _listenBlockSelected: function () {
            var self = this;
            BB.comBroker.listenWithNamespace(BB.EVENTS.BLOCK_SELECTED, self, function (e) {
                var blockID = e.edata;
                if (self.m_block_id != blockID) {
                    self.m_selected = false;
                    return;
                }
                self._onBlockSelected();
            });
        },

        /**
         When a block is selected, find out where is it placed (scene/ channel) and change props accordingly
         @method _onBlockSelected
         **/
        _onBlockSelected: function () {
            var self = this;
            self.m_selected = true;
            self.m_blockProperty.viewPanel(Elements.BLOCK_PROPERTIES);
            self._updateTitle();
            self._updateTitleTab();
            self._alphaPopulate();
            self._fabricLock();
            self._borderPropsPopulate();
            self._bgPropsPopulate();

            log('block selected ' + self.m_block_id);

            switch (self.m_placement) {
                case BB.CONSTS.PLACEMENT_CHANNEL:
                {
                    $(Elements.CHANNEL_BLOCK_PROPS).show();
                    $(Elements.SCENE_BLOCK_PROPS).hide();
                    self._updateBlockLength();
                    break;
                }

                case BB.CONSTS.PLACEMENT_SCENE:
                {
                    $(Elements.CHANNEL_BLOCK_PROPS).hide();
                    $(Elements.SCENE_BLOCK_PROPS).show();
                    self._updateBlockDimensions();
                    break;
                }

                case BB.CONSTS.PLACEMENT_IS_SCENE:
                {
                    $(Elements.CHANNEL_BLOCK_PROPS).hide();
                    $(Elements.SCENE_BLOCK_PROPS).hide();
                    self._updateBlockLength();
                    break;
                }
            }

            if (self._loadBlockSpecificProps)
                self._loadBlockSpecificProps();
        },

        /**
         Update the title of the block inside the assigned element.
         @method _updateTitle
         @return none
         **/
        _updateTitle: function () {
            var self = this;
            $(Elements.SELECTED_CHANNEL_RESOURCE_NAME).text(self.m_blockName);
        },

        /**
         Update the title of the selected tab properties element
         @method m_blockAcronym
         **/
        _updateTitleTab: function () {
            var self = this;
            var isVisible = $(Elements.BLOCK_COMMON_SETTINGS_TAB).is(':visible');
            if (isVisible){
                $(Elements.BLOCK_COMMON_SETTINGS_TAB).hide();
                $(Elements.BLOCK_PROPERTIES_TABBER).find('a').eq(0).trigger('click');
            }
            $(Elements.BLOCK_SUBPROPERTIES_TITLE).text(self.m_blockAcronym);
        },

        /**
         Update the length properties of the block with respect to position on the timeline_channel
         @method _updateBlockLength
         @return none
         **/
        _updateBlockLength: function () {
            var self = this;
            var lengthData;

            switch (self.m_placement) {
                case BB.CONSTS.PLACEMENT_CHANNEL:
                {
                    lengthData = pepper.getBlockTimelineChannelBlockLength(self.m_block_id);
                    break;
                }
                case BB.CONSTS.PLACEMENT_IS_SCENE:
                {
                    lengthData = pepper.getSceneDuration(self.m_block_id);
                    break;
                }
            }
            $(Elements.BLOCK_LENGTH_HOURS).val(lengthData.hours).trigger('change');
            $(Elements.BLOCK_LENGTH_MINUTES).val(lengthData.minutes).trigger('change');
            $(Elements.BLOCK_LENGTH_SECONDS).val(lengthData.seconds).trigger('change');
        },

        /**
         Update the position of the block when placed inside a scene
         @method _updateBlockDimensions
         **/
        _updateBlockDimensions: function () {
            var self = this;
            var dimensionProps = BB.comBroker.getService(BB.SERVICES['DIMENSION_PROPS_LAYOUT']);

            //var cW = self['canvasScale'] == 1 ? self.canvas.getWidth() : self.canvas.getWidth() * (1 / self['canvasScale']);
            //var cH = self['canvasScale'] == 1 ? self.canvas.getHeight() : self.canvas.getHeight() * (1 / self['canvasScale']);

            var values = {
                y: self['canvasScale'] == 1 ? self.top : self.top * (1 / self['canvasScale']),
                x: self['canvasScale'] == 1 ? self.left : self.left * (1 / self['canvasScale']),
                w: self['canvasScale'] == 1 ? self.width : self.width * (1 / self['canvasScale']),
                h: self['canvasScale'] == 1 ? self.height : self.height * (1 / self['canvasScale']),
                a: self.angle
            };
            dimensionProps.setValues(values);
        },

        /**
         Take action when block length has changed which is triggered by the BLOCK_LENGTH_CHANGING event
         @method _onBlockLengthChanged
         @return none
         **/
        _onBlockLengthChanged: function () {
            var self = this;

            BB.comBroker.listenWithNamespace(BB.EVENTS.BLOCK_LENGTH_CHANGING, this, function (e) {

                if (self.m_selected) {

                    var hours = $(Elements.BLOCK_LENGTH_HOURS).val();
                    var minutes = $(Elements.BLOCK_LENGTH_MINUTES).val();
                    var seconds = $(Elements.BLOCK_LENGTH_SECONDS).val();

                    switch (e.caller) {
                        case 'blockLengthHours':
                        {
                            hours = e.edata;
                            break;
                        }
                        case 'blockLengthMinutes':
                        {
                            minutes = e.edata;
                            break;
                        }
                        case 'blockLengthSeconds':
                        {
                            seconds = e.edata;
                            break;
                        }
                    }
                    // log('upd: ' + self.m_block_id + ' ' + hours + ' ' + minutes + ' ' + seconds);
                    if (parseInt(hours) == 0 && parseInt(minutes) == 0 && parseInt(seconds) < 5)
                        return;

                    switch (self.m_placement) {
                        case BB.CONSTS.PLACEMENT_CHANNEL:
                        {
                            pepper.setBlockTimelineChannelBlockLength(self.m_block_id, hours, minutes, seconds);
                            break;
                        }
                        case BB.CONSTS.PLACEMENT_IS_SCENE:
                        {
                            log('upd: ' + self.m_block_id + ' ' + hours + ' ' + minutes + ' ' + seconds);
                            pepper.setSceneDuration(self.m_block_id, hours, minutes, seconds);
                            break;
                        }
                    }
                }
            });
        },

        /**
         Announce that this block has changed
         @method _announceBlockChanged
         @param {Number} i_player_data
         **/
        _announceBlockChanged: function () {
            var self = this;
            BB.comBroker.fire(BB.EVENTS['SCENE_BLOCK_CHANGE'], self, null, [self.m_block_id]);
        },

        /**
         Update the msdb for the block with new values inside its player_data
         @method _setBlockPlayerData
         @param {Object} i_xmlDoc the dom object to save to local msdb
         @param {String} [i_noNotify] if set, fire event announcing data saved
         @param {Boolean} [i_xmlIsString] if set, bypass serializeToString since already in string format
         **/
        _setBlockPlayerData: function (i_xmlDoc, i_noNotify, i_xmlIsString) {
            var self = this;
            var player_data;
            if (i_xmlIsString == true) {
                player_data = i_xmlDoc;
            } else {
                player_data = (new XMLSerializer()).serializeToString(i_xmlDoc);
            }

            switch (self.m_placement) {
                case BB.CONSTS.PLACEMENT_CHANNEL:
                {
                    pepper.setCampaignTimelineChannelPlayerRecord(self.m_block_id, 'player_data', player_data);
                    break;
                }
                case BB.CONSTS.PLACEMENT_SCENE:
                {
                    pepper.setScenePlayerdataBlock(self.m_sceneID, self.m_block_id, player_data);
                    if (!i_noNotify)
                        self._announceBlockChanged();
                    break;
                }
                case BB.CONSTS.PLACEMENT_IS_SCENE:
                {
                    pepper.setScenePlayerData(self.m_block_id, player_data);
                    if (!i_noNotify)
                        self._announceBlockChanged();
                    break;
                }
            }
        },


        /**
         Get the XML player data of a block, depending where its placed
         If you like to view XML raw data, be sure to debug domPlayerData.children[0].outerHTML
         @method _getBlockPlayerData
         @return {Object} player data of block (aka player) parsed as DOM
         **/
        _getBlockPlayerData: function () {
            var self = this;
            var recBlock = undefined;

            switch (self.m_placement) {

                case BB.CONSTS.PLACEMENT_CHANNEL:
                {
                    recBlock = pepper.getCampaignTimelineChannelPlayerRecord(self.m_block_id);
                    return $.parseXML(recBlock['player_data']);
                    // to view data debug domPlayerData.children[0].outerHTML
                    break;
                }

                case BB.CONSTS.PLACEMENT_SCENE:
                {
                    return pepper.getScenePlayerdataBlock(self.m_sceneID, self.m_block_id);
                    // to view data debug domPlayerData.children[0].outerHTML
                    break;
                }
            }
        },

        /**
         bug fix: backward comparability with player_data that includes deleted resources
         this was already fixed but we live _selfDestruct for backwards compatability
         @method _selfDestruct
         **/
        _selfDestruct: function () {
            var self = this;
            setTimeout(function () {
                var sceneEditView = BB.comBroker.getService(BB.SERVICES['SCENE_EDIT_VIEW']);
                if (!_.isUndefined(sceneEditView)) {
                    var selectedSceneID = sceneEditView.getSelectedSceneID();
                    pepper.removeScenePlayer(selectedSceneID, self.m_block_id);
                    BB.comBroker.fire(BB.EVENTS.LOAD_SCENE, this, null, selectedSceneID);
                }
            }, 2000);
        },


        /**
         Delete block is a private method that is always called regardless if instance has
         been inherited or not. Used for releasing memory for garbage collector.
         @method _deleteBlock
         @params {Boolean} i_memoryOnly if true only remove from existance but not from msdb
         @return none
         **/
        _deleteBlock: function (i_memoryOnly) {
            var self = this;
            if (!i_memoryOnly)
                pepper.removeBlockFromTimelineChannel(self.m_block_id);
            BB.comBroker.stopListenWithNamespace(BB.EVENTS.BLOCK_SELECTED, self);
            BB.comBroker.stopListenWithNamespace(BB.EVENTS.BLOCK_LENGTH_CHANGING, self);
            BB.comBroker.stopListenWithNamespace(BB.EVENTS.GRADIENT_COLOR_CHANGED, self);
            BB.comBroker.stopListenWithNamespace(BB.EVENTS.GRADIENT_COLOR_CLOSED, self);
            BB.comBroker.stopListenWithNamespace(BB.EVENTS.BLOCK_BORDER_CHANGE, self);
            BB.comBroker.stopListenWithNamespace(BB.EVENTS.ALPHA_CHANGED, self);
            BB.comBroker.stopListenWithNamespace(BB.EVENTS.LOCK_CHANGED, self);
            $(Elements.SHOW_BACKGROUND).off(self.m_proxyToggleBgKey, self.m_proxyToggleBg);
            $(Elements.SHOW_BORDER).off(self.m_proxyToggleBorderKey, self.m_proxyToggleBorder);

            if (self.off != undefined)
                self.off('modified');

            if (self.m_sceneSelectedHandler)
                self.m_canvas.off('object:selected', self.m_sceneSelectedHandler);

            $.each(self, function (k) {
                self[k] = undefined;
            });
        },

        /**
         Fabricate alpha to canvas
         @method _fabricAlpha
         @param {Object} i_domPlayerData
         **/
        _fabricAlpha: function (i_domPlayerData) {
            var self = this;
            var appearance = $(i_domPlayerData).find('Appearance');
            var opacity = $(appearance).attr('alpha');
            self.setOpacity(opacity);
        },

        /**
         Fabricate color points to canvas
         @method _fabricColorPoints
         @param {xml} i_domPlayerData
         **/
        _fabricColorPoints: function (i_domPlayerData) {
            var self = this;
            var gradientPoints = $(i_domPlayerData).find('GradientPoints');
            var points = $(gradientPoints).find('Point');
            var colorStops = {}
            _.each(points, function (point) {
                var color = '#' + BB.lib.decimalToHex($(point).attr('color'));
                var offset = $(point).attr('midpoint');
                offset = offset / 250;
                colorStops[offset] = color;
            });
            return colorStops;
        },

        /**
         Config the fabric block border
         @method _fabricateBorder
         @param {i_options} i_options
         @return {object} object literal
         **/
        _fabricateBorder: function (i_options) {
            var self = this;
            var domPlayerData = self._getBlockPlayerData();
            var border = self._findBorder(domPlayerData);
            var color = border.length == 0 ? 'transparent' : '#' + BB.lib.decimalToHex($(border).attr('borderColor'));
            return _.extend({
                // borderColor: '#5d5d5d',
                stroke: color,
                strokeWidth: 1
            }, i_options);
        },

        /**
         Build the options injected into a newly created fabric object
         @method _fabricateOptions
         @param {Number} i_top
         @param {Number} i_left
         @param {Number} i_width
         @param {Number} i_height
         @param {Number} i_angle
         @return {object} object literal
         **/
        _fabricateOptions: function (i_top, i_left, i_width, i_height, i_angle) {
            var self = this;
            var options = {
                top: i_top,
                left: i_left,
                width: i_width,
                height: i_height,
                angle: i_angle,
                fill: '#ececec',
                hasRotatingPoint: false,
                transparentCorners: false,
                cornerColor: 'black',
                cornerSize: 5,
                lockRotation: true,
                lineWidth: 1
            };

            return self._fabricateBorder(options);
        },

        /**
         Fabricate color points to canvas
         @method _fabricRect
         @param {number} i_width
         @param {number} i_height
         @param {xml} i_domPlayerData
         @return {object} r fabric js rectangular
         **/
        _fabricRect: function (i_width, i_height, i_domPlayerData) {
            var self = this;
            var options = self._fabricateOptions(0, 0, i_width, i_height, 0);
            var r = new fabric.Rect(options);
            r.setGradient('fill', {
                x1: 0 - (i_width / 2),
                y1: 0,
                x2: (i_width / 2),
                y2: 0,
                colorStops: self._fabricColorPoints(i_domPlayerData)
            });
            return r;
        },

        /**
         Convert the block into a fabric js compatible object, called externally on creation of block
         @Override
         @method fabricateBlock
         **/
        fabricateBlock: function (i_canvasScale, i_callback) {
            var self = this;

            var domPlayerData = self._getBlockPlayerData();
            var layout = $(domPlayerData).find('Layout');

            var w = parseInt(layout.attr('width'));
            var h = parseInt(layout.attr('height'));
            var rec = self._fabricRect(w, h, domPlayerData);

            fabric.loadSVGFromString(self.m_blockSvg, function (objects, options) {
                var groupSvg = fabric.util.groupSVGElements(objects, options);
                rec.originX = 'center';
                rec.originY = 'center';
                groupSvg.originX = 'center';
                groupSvg.originY = 'center';

                var o = {
                    left: parseInt(layout.attr('x')),
                    top: parseInt(layout.attr('y')),
                    width: parseInt(layout.attr('width')),
                    height: parseInt(layout.attr('height')),
                    angle: parseInt(layout.attr('rotation')),
                    hasRotatingPoint: false,
                    stroke: 'transparent',
                    cornerColor: 'black',
                    cornerSize: 5,
                    lockRotation: true,
                    transparentCorners: false
                };
                _.extend(self, o);
                self.add(rec);
                self.add(groupSvg);
                self._fabricAlpha(domPlayerData);
                self._fabricLock();
                self['canvasScale'] = i_canvasScale;
                i_callback();
            });
        },

        /**
         Get block data as a json formatted object literal and return to caller
         @method getBlockData
         @return {object} data
         The entire block data members which can be made public
         **/
        getBlockData: function () {
            var self = this;
            var data = {
                blockID: self.m_block_id,
                blockType: self.m_blockType,
                blockName: self.m_blockName,
                blockDescription: self.m_blockDescription,
                blockIcon: self.m_blockIcon,
                blockFontAwesome: self.m_blockFontAwesome,
                blockAcronym: self.m_blockAcronym,
                blockMinWidth: self.m_minSize.w,
                blockMinHeight: self.m_minSize.h,
                blockData: self._getBlockPlayerData()
            };
            return data;
        },

        /**
         Set a block's z-index in case we know it (i.e.: it is going to be a re-render of a previous block that
         was removed from the canvas)
         @method setZindex
         @param {Number} i_index
         **/
        setZindex: function (i_zIndex) {
            var self = this;
            self.m_zIndex = i_zIndex;
        },

        /**
         Get a block's z-index
         @method getZindex
         @param {Number} i_index
         **/
        getZindex: function (i_zIndex) {
            var self = this;
            return self.m_zIndex;
        },

        /**
         Delete block is a public method used as fall back method, if not overridden by inherited instance.
         It is also a semi abstract method, all implementations should go into _deleteBlock();
         @method deleteBlock
         @params {Boolean} i_memoryOnly if true only remove from existance but not from msdb
         @return none
         **/
        deleteBlock: function (i_memoryOnly) {
            /* semi-abstract, overridden, do not modify */
            var self = this;
            self._deleteBlock(i_memoryOnly);
        }
    });

    return Block;
});