# widget/Widget.js

    import { CONTROLS, MODES } from '../Constants';
    import { DataImage } from '../DataImage';
    import * as THREE from 'three';

    /**
     * @classdesc Widget for controls
     * @constructor
     * @param {HTMLElement} container - A domElement where default control widget will be attached to
     */
    function Widget ( container ) {

        if ( !container ) {

            console.warn( 'PANOLENS.Widget: No container specified' );

        }

        THREE.EventDispatcher.call( this );

        this.DEFAULT_TRANSITION  = 'all 0.27s ease';
        this.TOUCH_ENABLED = !!(( 'ontouchstart' in window ) || window.DocumentTouch && document instanceof DocumentTouch);
        this.PREVENT_EVENT_HANDLER = function ( event ) {
            event.preventDefault();
            event.stopPropagation();
        };

        this.container = container;

        this.barElement = null;
        this.fullscreenElement = null;
        this.videoElement = null;
        this.settingElement = null;

        this.mainMenu = null;

        this.activeMainItem = null;
        this.activeSubMenu = null;
        this.mask = null;

    }

    Widget.prototype = Object.assign( Object.create( THREE.EventDispatcher.prototype ), {

        constructor: Widget,

        /**
         * Add control bar
         * @memberOf Widget
         * @instance
         */
        addControlBar: function () {

            if ( !this.container ) {

                console.warn( 'Widget container not set' );
                return;
            }

            var scope = this, bar, styleTranslate, styleOpacity, gradientStyle;

            gradientStyle = 'linear-gradient(bottom, rgba(0,0,0,0.2), rgba(0,0,0,0))';

            bar = document.createElement( 'div' );
            bar.style.width = '100%';
            bar.style.height = '44px';
            bar.style.float = 'left';
            bar.style.transform = bar.style.webkitTransform = bar.style.msTransform = 'translateY(-100%)';
            bar.style.background = '-webkit-' + gradientStyle;
            bar.style.background = '-moz-' + gradientStyle;
            bar.style.background = '-o-' + gradientStyle;
            bar.style.background = '-ms-' + gradientStyle;
            bar.style.background = gradientStyle;
            bar.style.transition = this.DEFAULT_TRANSITION;
            bar.style.pointerEvents = 'none';
            bar.isHidden = false;
            bar.toggle = function () {
                bar.isHidden = !bar.isHidden;
                styleTranslate = bar.isHidden ? 'translateY(0)' : 'translateY(-100%)';
                styleOpacity = bar.isHidden ? 0 : 1;
                bar.style.transform = bar.style.webkitTransform = bar.style.msTransform = styleTranslate;
                bar.style.opacity = styleOpacity;
            };

            // Menu
            var menu = this.createDefaultMenu();
            this.mainMenu = this.createMainMenu( menu );
            bar.appendChild( this.mainMenu );

            // Mask
            var mask = this.createMask();
            this.mask = mask;
            this.container.appendChild( mask );

            // Dispose
            bar.dispose = function () {

                if ( scope.fullscreenElement ) {

                    bar.removeChild( scope.fullscreenElement );
                    scope.fullscreenElement.dispose();
                    scope.fullscreenElement = null;

                }

                if ( scope.settingElement ) {

                    bar.removeChild( scope.settingElement );
                    scope.settingElement.dispose();
                    scope.settingElement = null;

                }

                if ( scope.videoElement ) {

                    bar.removeChild( scope.videoElement );
                    scope.videoElement.dispose();
                    scope.videoElement = null;

                }

            };

            this.container.appendChild( bar );

            // Mask events
            this.mask.addEventListener( 'mousemove', this.PREVENT_EVENT_HANDLER, true );
            this.mask.addEventListener( 'mouseup', this.PREVENT_EVENT_HANDLER, true );
            this.mask.addEventListener( 'mousedown', this.PREVENT_EVENT_HANDLER, true );
            this.mask.addEventListener( scope.TOUCH_ENABLED ? 'touchend' : 'click', function ( event ) {

                event.preventDefault();
                event.stopPropagation();

                scope.mask.hide();
                scope.settingElement.deactivate();

            }, false );

            // Event listener
            this.addEventListener( 'control-bar-toggle', bar.toggle );

            this.barElement = bar;

        },

        /**
         * Create default menu
         * @memberOf Widget
         * @instance
         */
        createDefaultMenu: function () {

            var scope = this, handler;

            handler = function ( method, data ) {

                return function () {

                    scope.dispatchEvent( {

                        type: 'panolens-viewer-handler',
                        method: method,
                        data: data

                    } );

                };

            };

            return [

                {
                    title: 'Control',
                    subMenu: [
                        {
                            title: this.TOUCH_ENABLED ? 'Touch' : 'Mouse',
                            handler: handler( 'enableControl', CONTROLS.ORBIT )
                        },
                        {
                            title: 'Sensor',
                            handler: handler( 'enableControl', CONTROLS.DEVICEORIENTATION )
                        }
                    ]
                },

                {
                    title: 'Mode',
                    subMenu: [
                        {
                            title: 'Normal',
                            handler: handler( 'disableEffect' )
                        },
                        {
                            title: 'Cardboard',
                            handler: handler( 'enableEffect', MODES.CARDBOARD )
                        },
                        {
                            title: 'Stereoscopic',
                            handler: handler( 'enableEffect', MODES.STEREO )
                        }
                    ]
                }

            ];

        },

        /**
         * Add buttons on top of control bar
         * @param {string} name - The control button name to be created
         * @memberOf Widget
         * @instance
         */
        addControlButton: function ( name ) {

            let element;

            switch( name ) {

            case 'fullscreen':

                element = this.createFullscreenButton();
                this.fullscreenElement = element;

                break;

            case 'setting':

                element = this.createSettingButton();
                this.settingElement = element;

                break;

            case 'video':

                element = this.createVideoControl();
                this.videoElement = element;

                break;

            default:

                return;

            }

            if ( !element ) {

                return;

            }

            this.barElement.appendChild( element );

        },

        /**
         * Create modal mask
         * @memberOf Widget
         * @instance
         */
        createMask: function () {

            const element = document.createElement( 'div' );
            element.style.position = 'absolute';
            element.style.top = 0;
            element.style.left = 0;
            element.style.width = '100%';
            element.style.height = '100%';
            element.style.background = 'transparent';
            element.style.display = 'none';

            element.show = function () {

                this.style.display = 'block';

            };

            element.hide = function () {

                this.style.display = 'none';

            };

            return element;

        },

        /**
         * Create Setting button to toggle menu
         * @memberOf Widget
         * @instance
         */
        createSettingButton: function () {

            let scope = this, item;

            function onTap ( event ) {

                event.preventDefault();
                event.stopPropagation();

                scope.mainMenu.toggle();

                if ( this.activated ) {

                    this.deactivate();

                } else {

                    this.activate();

                }

            }

            item = this.createCustomItem( {

                style: {

                    backgroundImage: 'url("' + DataImage.Setting + '")',
                    webkitTransition: this.DEFAULT_TRANSITION,
                    transition: this.DEFAULT_TRANSITION

                },

                onTap: onTap

            } );

            item.activate = function () {

                this.style.transform = 'rotate3d(0,0,1,90deg)';
                this.activated = true;
                scope.mask.show();

            };

            item.deactivate = function () {

                this.style.transform = 'rotate3d(0,0,0,0)';
                this.activated = false;
                scope.mask.hide();

                if ( scope.mainMenu && scope.mainMenu.visible ) {

                    scope.mainMenu.hide();

                }

                if ( scope.activeSubMenu && scope.activeSubMenu.visible ) {

                    scope.activeSubMenu.hide();

                }

                if ( scope.mainMenu && scope.mainMenu._width ) {

                    scope.mainMenu.changeSize( scope.mainMenu._width );
                    scope.mainMenu.unslideAll();

                }

            };

            item.activated = false;

            return item;

        },

        /**
         * Create Fullscreen button
         * @return {HTMLSpanElement} - The dom element icon for fullscreen
         * @memberOf Widget
         * @instance
         * @fires Widget#panolens-viewer-handler
         */
        createFullscreenButton: function () {

            let scope = this, item, isFullscreen = false, tapSkipped = true, stylesheetId;

            const { container } = this;

            stylesheetId = 'panolens-style-addon';

            // Don't create button if no support
            if ( !document.fullscreenEnabled       &&
    			!document.webkitFullscreenEnabled &&
    			!document.mozFullScreenEnabled    &&
    			!document.msFullscreenEnabled ) {
                return;
            }

            function onTap ( event ) {

                event.preventDefault();
                event.stopPropagation();

                tapSkipped = false;

                if ( !isFullscreen ) {

                    if ( container.requestFullscreen ) { container.requestFullscreen(); }
                    if ( container.msRequestFullscreen ) { container.msRequestFullscreen(); }
                    if ( container.mozRequestFullScreen ) { container.mozRequestFullScreen(); }
                    if ( container.webkitRequestFullscreen ) { container.webkitRequestFullscreen( Element.ALLOW_KEYBOARD_INPUT ); }

                    isFullscreen = true;

                } else {

                    if ( document.exitFullscreen ) { document.exitFullscreen(); }
                    if ( document.msExitFullscreen ) { document.msExitFullscreen(); }
                    if ( document.mozCancelFullScreen ) { document.mozCancelFullScreen(); }
                    if ( document.webkitExitFullscreen ) { document.webkitExitFullscreen( ); }

                    isFullscreen = false;

                }

                this.style.backgroundImage = ( isFullscreen )
                    ? 'url("' + DataImage.FullscreenLeave + '")'
                    : 'url("' + DataImage.FullscreenEnter + '")';

            }

            function onFullScreenChange () {

                if ( tapSkipped ) {

                    isFullscreen = !isFullscreen;

                    item.style.backgroundImage = ( isFullscreen )
                        ? 'url("' + DataImage.FullscreenLeave + '")'
                        : 'url("' + DataImage.FullscreenEnter + '")';

                }

                /**
                 * Viewer handler event
                 * @type {object}
                 * @event Widget#panolens-viewer-handler
                 * @property {string} method - 'onWindowResize' function call on Viewer
                 */
                scope.dispatchEvent( { type: 'panolens-viewer-handler', method: 'onWindowResize' } );

                tapSkipped = true;

            }

            document.addEventListener( 'fullscreenchange', onFullScreenChange, false );
            document.addEventListener( 'webkitfullscreenchange', onFullScreenChange, false );
            document.addEventListener( 'mozfullscreenchange', onFullScreenChange, false );
            document.addEventListener( 'MSFullscreenChange', onFullScreenChange, false );

            item = this.createCustomItem( {

                style: {

                    backgroundImage: 'url("' + DataImage.FullscreenEnter + '")'

                },

                onTap: onTap

            } );

            // Add fullscreen stlye if not exists
            if ( !document.querySelector( stylesheetId ) ) {
                const sheet = document.createElement( 'style' );
                sheet.id = stylesheetId;
                sheet.innerHTML = ':-webkit-full-screen { width: 100% !important; height: 100% !important }';
                document.body.appendChild( sheet );
            }

            return item;

        },

        /**
         * Create video control container
         * @memberOf Widget
         * @instance
         * @return {HTMLSpanElement} - The dom element icon for video control
         */
        createVideoControl: function () {

            const item = document.createElement( 'span' );
            item.style.display = 'none';
            item.show = function () {

                item.style.display = '';

            };

            item.hide = function () {

                item.style.display = 'none';
                item.controlButton.paused = true;
                item.controlButton.update();

            };

            item.controlButton = this.createVideoControlButton();
            item.seekBar = this.createVideoControlSeekbar();

            item.appendChild( item.controlButton );
            item.appendChild( item.seekBar );

            item.dispose = function () {

                item.removeChild( item.controlButton );
                item.removeChild( item.seekBar );

                item.controlButton.dispose();
                item.controlButton = null;

                item.seekBar.dispose();
                item.seekBar = null;

            };

            this.addEventListener( 'video-control-show', item.show );
            this.addEventListener( 'video-control-hide', item.hide );

            return item;

        },

        /**
         * Create video control button
         * @memberOf Widget
         * @instance
         * @return {HTMLSpanElement} - The dom element icon for video control
         * @fires Widget#panolens-viewer-handler
         */
        createVideoControlButton: function () {

            const scope = this;

            function onTap ( event ) {

                event.preventDefault();
                event.stopPropagation();

                /**
                 * Viewer handler event
                 * @type {object}
                 * @event Widget#panolens-viewer-handler
                 * @property {string} method - 'toggleVideoPlay' function call on Viewer
                 */
                scope.dispatchEvent( { type: 'panolens-viewer-handler', method: 'toggleVideoPlay', data: !this.paused } );

                this.paused = !this.paused;

                item.update();

            };

            const item = this.createCustomItem( {

                style: {

                    float: 'left',
                    backgroundImage: 'url("' + DataImage.VideoPlay + '")'

                },

                onTap: onTap

            } );

            item.paused = true;

            item.update = function ( paused ) {

                this.paused = paused !== undefined ? paused : this.paused;

                this.style.backgroundImage = 'url("' + ( this.paused
                    ? DataImage.VideoPlay
                    : DataImage.VideoPause ) + '")';

            };

            return item;

        },

        /**
         * Create video seekbar
         * @memberOf Widget
         * @instance
         * @return {HTMLSpanElement} - The dom element icon for video seekbar
         * @fires Widget#panolens-viewer-handler
         */
        createVideoControlSeekbar: function () {

            let scope = this, item, progressElement, progressElementControl,
                isDragging = false, mouseX, percentageNow, percentageNext;

            progressElement = document.createElement( 'div' );
            progressElement.style.width = '0%';
            progressElement.style.height = '100%';
            progressElement.style.backgroundColor = '#fff';

            progressElementControl = document.createElement( 'div' );
            progressElementControl.style.float = 'right';
            progressElementControl.style.width = '14px';
            progressElementControl.style.height = '14px';
            progressElementControl.style.transform = 'translate(7px, -5px)';
            progressElementControl.style.borderRadius = '50%';
            progressElementControl.style.backgroundColor = '#ddd';

            progressElementControl.addEventListener( 'mousedown', onMouseDown, { passive: true } );
            progressElementControl.addEventListener( 'touchstart', onMouseDown,  { passive: true } );

            function onMouseDown ( event ) {

                event.stopPropagation();

                isDragging = true;

                mouseX = event.clientX || ( event.changedTouches && event.changedTouches[0].clientX );

                percentageNow = parseInt( progressElement.style.width ) / 100;

                addControlListeners();
            }

            function onVideoControlDrag ( event ) {

                if( isDragging ){

                    const clientX = event.clientX || ( event.changedTouches && event.changedTouches[0].clientX );

                    percentageNext = ( clientX - mouseX ) / item.clientWidth;

                    percentageNext = percentageNow + percentageNext;

                    percentageNext = percentageNext > 1 ? 1 : ( ( percentageNext < 0 ) ? 0 : percentageNext );

                    item.setProgress ( percentageNext );

                    /**
                     * Viewer handler event
                     * @type {object}
                     * @event Widget#panolens-viewer-handler
                     * @property {string} method - 'setVideoCurrentTime' function call on Viewer
                     * @property {number} data - Percentage of current video. Range from 0.0 to 1.0
                     */
                    scope.dispatchEvent( { type: 'panolens-viewer-handler', method: 'setVideoCurrentTime', data: percentageNext } );

                }

            }

            function onVideoControlStop ( event ) {

                event.stopPropagation();

                isDragging = false;

                removeControlListeners();

            }

            function addControlListeners () {

                scope.container.addEventListener( 'mousemove', onVideoControlDrag, { passive: true } );
                scope.container.addEventListener( 'mouseup', onVideoControlStop, { passive: true } );
                scope.container.addEventListener( 'touchmove', onVideoControlDrag, { passive: true } );
                scope.container.addEventListener( 'touchend', onVideoControlStop, { passive: true } );

            }

            function removeControlListeners () {

                scope.container.removeEventListener( 'mousemove', onVideoControlDrag, false );
                scope.container.removeEventListener( 'mouseup', onVideoControlStop, false );
                scope.container.removeEventListener( 'touchmove', onVideoControlDrag, false );
                scope.container.removeEventListener( 'touchend', onVideoControlStop, false );

            }

            function onTap ( event ) {

                event.preventDefault();
                event.stopPropagation();

                if ( event.target === progressElementControl ) { return; }

                const percentage = ( event.changedTouches && event.changedTouches.length > 0 )
                    ? ( event.changedTouches[0].pageX - event.target.getBoundingClientRect().left ) / this.clientWidth
                    : event.offsetX / this.clientWidth;

                /**
                 * Viewer handler event
                 * @type {object}
                 * @property {string} method - 'setVideoCurrentTime' function call on Viewer
                 * @property {number} data - Percentage of current video. Range from 0.0 to 1.0
                 */
                scope.dispatchEvent( { type: 'panolens-viewer-handler', method: 'setVideoCurrentTime', data: percentage } );

                item.setProgress( event.offsetX / this.clientWidth );

            };

            function onDispose () {

                removeControlListeners();
                progressElement = null;
                progressElementControl = null;

            }

            progressElement.appendChild( progressElementControl );

            item = this.createCustomItem( {

                style: {

                    float: 'left',
                    width: '30%',
                    height: '4px',
                    marginTop: '20px',
                    backgroundColor: 'rgba(188,188,188,0.8)'

                },

                onTap: onTap,
                onDispose: onDispose

            } );

            item.appendChild( progressElement );

            item.setProgress = function( percentage ) {

                progressElement.style.width = percentage * 100 + '%';

            };

            this.addEventListener( 'video-update', function ( event ) {

                item.setProgress( event.percentage );

            } );

            item.progressElement = progressElement;
            item.progressElementControl = progressElementControl;

            return item;

        },

        /**
         * Create menu item
         * @param  {string} title - Title to display
         * @memberOf Widget
         * @instance
         * @return {HTMLElement} - An anchor tag element
         */
        createMenuItem: function ( title ) {

            const scope = this;
            const item = document.createElement( 'a' );
            item.textContent = title;
            item.style.display = 'block';
            item.style.padding = '10px';
            item.style.textDecoration = 'none';
            item.style.cursor = 'pointer';
            item.style.pointerEvents = 'auto';
            item.style.transition = this.DEFAULT_TRANSITION;

            item.slide = function ( right ) {

                this.style.transform = 'translateX(' + ( right ? '' : '-' ) + '100%)';

            };

            item.unslide = function () {

                this.style.transform = 'translateX(0)';

            };

            item.setIcon = function ( url ) {

                if ( this.icon ) {

                    this.icon.style.backgroundImage = 'url(' + url + ')';

                }

            };

            item.setSelectionTitle = function ( title ) {

                if ( this.selection ) {

                    this.selection.textContent = title;

                }

            };

            item.addSelection = function ( name ) {

                const selection = document.createElement( 'span' );
                selection.style.fontSize = '13px';
                selection.style.fontWeight = '300';
                selection.style.float = 'right';

                this.selection = selection;
                this.setSelectionTitle( name );
                this.appendChild( selection );

                return this;

            };

            item.addIcon = function ( url = DataImage.ChevronRight, left = false, flip = false ) {

                const element = document.createElement( 'span' );
                element.style.float = left ? 'left' : 'right';
                element.style.width = '17px';
                element.style.height = '17px';
                element.style[ 'margin' + ( left ? 'Right' : 'Left' ) ] = '12px';
                element.style.backgroundSize = 'cover';

                if ( flip ) {

                    element.style.transform = 'rotateZ(180deg)';

                }

                this.icon = element;
                this.setIcon( url );
                this.appendChild( element );

                return this;

            };

            item.addSubMenu = function ( title, items ) {

                this.subMenu = scope.createSubMenu( title, items );

                return this;

            };

            item.addEventListener( 'mouseenter', function () {

                this.style.backgroundColor = '#e0e0e0';

            }, false );

            item.addEventListener( 'mouseleave', function () {

                this.style.backgroundColor = '#fafafa';

            }, false );

            return item;

        },

        /**
         * Create menu item header
         * @param  {string} title - Title to display
         * @memberOf Widget
         * @instance
         * @return {HTMLElement} - An anchor tag element
         */
        createMenuItemHeader: function ( title ) {

            const header = this.createMenuItem( title );

            header.style.borderBottom = '1px solid #333';
            header.style.paddingBottom = '15px';

            return header;

        },

        /**
         * Create main menu
         * @param  {array} menus - Menu array list
         * @memberOf Widget
         * @instance
         * @return {HTMLElement} - A span element
         */
        createMainMenu: function ( menus ) {

            let scope = this, menu = this.createMenu();

            menu._width = 200;
            menu.changeSize( menu._width );

            function onTap ( event ) {

                event.preventDefault();
                event.stopPropagation();

                let mainMenu = scope.mainMenu, subMenu = this.subMenu;

                function onNextTick () {

                    mainMenu.changeSize( subMenu.clientWidth );
                    subMenu.show();
                    subMenu.unslideAll();

                }

                mainMenu.hide();
                mainMenu.slideAll();
                mainMenu.parentElement.appendChild( subMenu );

                scope.activeMainItem = this;
                scope.activeSubMenu = subMenu;

                window.requestAnimationFrame( onNextTick );

            };

            for ( var i = 0; i < menus.length; i++ ) {

                var item = menu.addItem( menus[ i ].title );

                item.style.paddingLeft = '20px';

                item.addIcon()
                    .addEventListener( scope.TOUCH_ENABLED ? 'touchend' : 'click', onTap, false );

                if ( menus[ i ].subMenu && menus[ i ].subMenu.length > 0 ) {

                    var title = menus[ i ].subMenu[ 0 ].title;

                    item.addSelection( title )
                        .addSubMenu( menus[ i ].title, menus[ i ].subMenu );

                }

            }

            return menu;

        },

        /**
         * Create sub menu
         * @param {string} title - Sub menu title
         * @param {array} items - Item array list
         * @memberOf Widget
         * @instance
         * @return {HTMLElement} - A span element
         */
        createSubMenu: function ( title, items ) {

            let scope = this, menu, subMenu = this.createMenu();

            subMenu.items = items;
            subMenu.activeItem = null;

            function onTap ( event ) {

                event.preventDefault();
                event.stopPropagation();

                menu = scope.mainMenu;
                menu.changeSize( menu._width );
                menu.unslideAll();
                menu.show();
                subMenu.slideAll( true );
                subMenu.hide();

                if ( this.type !== 'header' ) {

                    subMenu.setActiveItem( this );
                    scope.activeMainItem.setSelectionTitle( this.textContent );

                    if ( this.handler ) { this.handler(); }

                }

            }

            subMenu.addHeader( title ).addIcon( undefined, true, true ).addEventListener( scope.TOUCH_ENABLED ? 'touchend' : 'click', onTap, false );

            for ( let i = 0; i < items.length; i++ ) {

                const item = subMenu.addItem( items[ i ].title );

                item.style.fontWeight = 300;
                item.handler = items[ i ].handler;
                item.addIcon( ' ', true );
                item.addEventListener( scope.TOUCH_ENABLED ? 'touchend' : 'click', onTap, false );

                if ( !subMenu.activeItem ) {

                    subMenu.setActiveItem( item );

                }

            }

            subMenu.slideAll( true );

            return subMenu;

        },

        /**
         * Create general menu
         * @memberOf Widget
         * @instance
         * @return {HTMLElement} - A span element
         */
        createMenu: function () {

            const scope = this;
            const menu = document.createElement( 'span' );
            const style = menu.style;

            style.padding = '5px 0';
            style.position = 'fixed';
            style.bottom = '100%';
            style.right = '14px';
            style.backgroundColor = '#fafafa';
            style.fontFamily = 'Helvetica Neue';
            style.fontSize = '14px';
            style.visibility = 'hidden';
            style.opacity = 0;
            style.boxShadow = '0 0 12pt rgba(0,0,0,0.25)';
            style.borderRadius = '2px';
            style.overflow = 'hidden';
            style.willChange = 'width, height, opacity';
            style.pointerEvents = 'auto';
            style.transition = this.DEFAULT_TRANSITION;

            menu.visible = false;

            menu.changeSize = function ( width, height ) {

                if ( width ) {

                    this.style.width = width + 'px';

                }

                if ( height ) {

                    this.style.height = height + 'px';

                }

            };

            menu.show = function () {

                this.style.opacity = 1;
                this.style.visibility = 'visible';
                this.visible = true;

            };

            menu.hide = function () {

                this.style.opacity = 0;
                this.style.visibility = 'hidden';
                this.visible = false;

            };

            menu.toggle = function () {

                if ( this.visible ) {

                    this.hide();

                } else {

                    this.show();

                }

            };

            menu.slideAll = function ( right ) {

                for ( let i = 0; i < menu.children.length; i++ ){

                    if ( menu.children[ i ].slide ) {

                        menu.children[ i ].slide( right );

                    }

                }

            };

            menu.unslideAll = function () {

                for ( let i = 0; i < menu.children.length; i++ ){

                    if ( menu.children[ i ].unslide ) {

                        menu.children[ i ].unslide();

                    }

                }

            };

            menu.addHeader = function ( title ) {

                const header = scope.createMenuItemHeader( title );
                header.type = 'header';

                this.appendChild( header );

                return header;

            };

            menu.addItem = function ( title ) {

                const item = scope.createMenuItem( title );
                item.type = 'item';

                this.appendChild( item );

                return item;

            };

            menu.setActiveItem = function ( item ) {

                if ( this.activeItem ) {

                    this.activeItem.setIcon( ' ' );

                }

                item.setIcon( DataImage.Check );

                this.activeItem = item;

            };

            menu.addEventListener( 'mousemove', this.PREVENT_EVENT_HANDLER, true );
            menu.addEventListener( 'mouseup', this.PREVENT_EVENT_HANDLER, true );
            menu.addEventListener( 'mousedown', this.PREVENT_EVENT_HANDLER, true );

            return menu;

        },

        /**
         * Create custom item element
         * @memberOf Widget
         * @instance
         * @return {HTMLSpanElement} - The dom element icon
         */
        createCustomItem: function ( options = {} ) {

            const scope = this;
            const item = options.element || document.createElement( 'span' );
            const { onDispose } = options;

            item.style.cursor = 'pointer';
            item.style.float = 'right';
            item.style.width = '44px';
            item.style.height = '100%';
            item.style.backgroundSize = '60%';
            item.style.backgroundRepeat = 'no-repeat';
            item.style.backgroundPosition = 'center';
            item.style.webkitUserSelect =
    		item.style.MozUserSelect =
    		item.style.userSelect = 'none';
            item.style.position = 'relative';
            item.style.pointerEvents = 'auto';

            // White glow on icon
            item.addEventListener( scope.TOUCH_ENABLED ? 'touchstart' : 'mouseenter', function() {
                item.style.filter =
    			item.style.webkitFilter = 'drop-shadow(0 0 5px rgba(255,255,255,1))';
            }, { passive: true });
            item.addEventListener( scope.TOUCH_ENABLED ? 'touchend' : 'mouseleave', function() {
                item.style.filter =
    			item.style.webkitFilter = '';
            }, { passive: true });

            this.mergeStyleOptions( item, options.style );

            if ( options.onTap ) {

                item.addEventListener( scope.TOUCH_ENABLED ? 'touchend' : 'click', options.onTap, false );

            }

            item.dispose = function () {

                item.removeEventListener( scope.TOUCH_ENABLED ? 'touchend' : 'click', options.onTap, false );

                if ( onDispose ) { options.onDispose(); }

            };

            return item;

        },

        /**
         * Merge item css style
         * @param  {HTMLElement} element - The element to be merged with style
         * @param  {object} options - The style options
         * @memberOf Widget
         * @instance
         * @return {HTMLElement} - The same element with merged styles
         */
        mergeStyleOptions: function ( element, options = {} ) {

            for ( let property in options ){

                if ( options.hasOwnProperty( property ) ) {

                    element.style[ property ] = options[ property ];

                }

            }

            return element;

        },

        /**
         * Dispose widgets by detaching dom elements from container
         * @memberOf Widget
         * @instance
         */
        dispose: function () {

            if ( this.barElement ) {
                this.container.removeChild( this.barElement );
                this.barElement.dispose();
                this.barElement = null;

            }

        }

    } );

    export { Widget };