<commit_before>import Ember from 'ember';

/**
 * @module components
 * @class  sl-menu
 */
export default Ember.Component.extend({

    // -------------------------------------------------------------------------
    // Dependencies

    // -------------------------------------------------------------------------
    // Attributes

    /**
     * Root element HTML tag type
     *
     * @property {Ember.String} tagName
     * @default  "div"
     */
    tagName: 'div',

    /**
     * Class names for the root element
     *
     * @property {Ember.Array} classNames
     */
    classNames: [ 'sl-menu' ],

    // -------------------------------------------------------------------------
    // Actions

    /**
     * Component actions hash
     *
     * @property {Ember.Object} actions
     */
    actions: {

        /**
         * Close all of the sub menus
         *
         * @function actions.closeAll
         * @returns  {void}
         */
        closeAll: function() {
            if ( this.$() ) {
                this.$().removeClass( 'active' );

                if ( this.get( 'isRoot' )) {
                    this.$().removeClass( 'showall' );
                }
            }

            this.set( 'keyHandler', false );

            this.get( 'children' ).forEach( function( item ) {
                item.send( 'closeAll' );
            });

            if ( this.get( 'isRoot' ) ) {
               this.set( 'keyHandler', true );
            }
        },

        /**
         * Cycle rootNode selection forward
         *
         * Only cycles through rootNodes if node was initially selected via keyboard.
         * If "Show All" enabled:
         *     If last rootNode then moves forward to "Show All" option.
         *     If "Show All" option wraps around to first option.
         * If "Show All" disabled:
         *     If last rootNode then wraps around to first option.
         *
         * @function cycleRootSelectionNext
         * @returns  {void}
         */
        cycleRootSelectionNext: function() {
            var currentIndex = this.get( 'currentRootNodeIndex' );

            if ( !this.get( 'keyboardInUse' ) ) {
                return;
            }

            // Whether "Show All" is enabled
            if ( this.get( 'showAllBoolean' ) ) {

                // Not on "Show All"
                if ( null !== currentIndex ) {

                    // Cycling forward, wrapping around last option to first option
                    if ( this.get( 'rootNode.menu.pages.length' ) < currentIndex + 2 ) {
                        this.send( 'showAll' );
                        this.set( 'activeChild', null );

                    // Cycle forward, selecting next rootNode
                    } else {
                        this.childSelected( currentIndex + 2 );
                    }

                // Select first rootNode
                } else {
                    this.childSelected( 1 );
                }

            } else {

                // Cycle forward, wrapping around last option to first option
                if ( this.get( 'rootNode.menu.pages.length' ) < currentIndex + 2 ) {
                    // Select first rootNode
                    this.childSelected( 1 );

                } else {
                    // Cycle forward, selecting next rootNode
                    this.childSelected( currentIndex + 2 );
                }
            }
        },

        /**
         * Cycle rootNode selection backwards
         *
         * Only cycles through rootNodes if node was initially selected via keyboard.
         * If "Show All" enabled:
         *     If first rootNode then wraps around to "Show All" option.
         *     If "Show All" option moves backward to previous option
         * If "Show All" disabled:
         *     If first rootNode then wraps around to last option.
         *
         * @function cycleRootSelectionPrevious
         * @returns  {void}
         */
        cycleRootSelectionPrevious: function() {
            var currentIndex = this.get( 'currentRootNodeIndex' );

            if ( !this.get( 'keyboardInUse' ) ) {
                return;
            }

            // Whether "Show All" is enabled
            if ( this.get( 'showAllBoolean' ) ) {

                // Not on "Show All"
                if ( null !== currentIndex ) {

                    // Cycling backwards, wrapping around first option to last option
                    if ( 0 === currentIndex ) {
                        this.send( 'showAll' );
                        this.set( 'activeChild', null );

                    // Cycle backwards, selecting previous rootNode
                    } else {
                        this.childSelected( currentIndex );
                    }

                // Select last rootNode
                } else {
                    this.childSelected( this.get( 'rootNode.menu.pages' ).length );
                }

            } else {

                // Cycle backwards, wrapping around first option to last option
                if ( 0 === currentIndex ) {
                    // Select last rootNode
                    this.childSelected( this.get( 'rootNode.menu.pages.length' ) );

                } else {
                    // Cycle backward, selecting previous rootNode
                    this.childSelected( currentIndex );
                }
            }
        },

        /**
         * Recursively open sub menus
         *
         * @function actions.drillDown
         * @returns  {void}
         */
        drillDown: function() {
            var child = this.get( 'activeChild' );

            if ( this.get( 'keyHandler' )) {
                if ( child ) {
                    child.set( 'keyHandler', true );
                    this.set( 'keyHandler', false );
                }
            } else if ( child ) {
                child.drillDown();
            }
        },

        /**
         * Send selected action when menu item is selected
         *
         * @function actions.selected
         * @returns  {void}
         */
        selected: function() {
            this.performAction();
        },

        /**
         * Show all of the sub menus
         *
         * @function actions.showAll
         * @returns  {void}
         */
        showAll: function() {
            if ( this.$() ) {
                this.$().addClass( 'active' );

                if ( this.get( 'isRoot' ) ) {
                    this.$().addClass( 'showall' );
                }
            }

            this.get( 'children' ).forEach( function( item ) {
                item.send( 'showAll' );
            });
        }
    },

    // -------------------------------------------------------------------------
    // Events

    /**
     * Method called when menu is clicked
     *
     * @function click
     * @returns  {boolean} false
     */
    click: function() {
        this.performAction();

        return false;
    },

    /**
     * Method triggered on mouseenter event
     *
     * @function mouseEnter
     * @returns  {void}
     */
    mouseEnter: function() {
        var currentActiveRootNodeIndex = this.get( 'currentRootNodeIndex' ),
            query;

        if ( this.get( 'keyboardInUse' ) ) {
            if ( null !== currentActiveRootNodeIndex ) {
                query = 'a:contains("' + this.get( 'rootNode.menu.pages' )[currentActiveRootNodeIndex].label + '")';
                this.$(query).parent().removeClass( 'active' );
            }

            this.set( 'keyboardInUse', false );
        }

        this.$().addClass( 'active' );
    },

    /**
     * Method triggered on mouseleave event
     *
     * @function mouseLeave
     * @returns  {void}
     */
    mouseLeave: function() {
        if ( this.get( 'isRoot' ) ) {
            this.send( 'closeAll' );
        } else if ( !this.get( 'rootNode' ).$().hasClass( 'showall' ) ) {
            this.$().removeClass( 'active' );
        }
    },

    // -------------------------------------------------------------------------
    // Properties

    /**
     * Currently active child
     *
     * @property {number} activeChild
     * @default  null
     */
    activeChild: null,

    /**
     * Embedded Ember View representing the "Show All"
     *
     * @property {Ember.View} AllView
     */
    AllView: Ember.View.extend({

        /**
         * HTML tag name of the root element
         *
         * @property {Ember.String} tagName
         * @default  "li"
         */
        tagName: 'li',

        /**
         * Class names for the AllView view
         *
         * @property {Ember.Array} AllView.classNames
         */
        classNames: [ 'all' ],

        /**
         * Method called on mouseenter event
         *
         * @function AllView.mouseEnter
         * @returns  {void}
         */
        mouseEnter: function() {
            this.send( 'showAll' );
        },

        /**
         * Target pointer to the parent view
         *
         * @function AllView.target
         * @observes parentView
         * @return   {Ember.View}
         */
        target: function() {
            return this.get( 'parentView' );
        }.property( 'parentView' )
    }),

    /**
     * Collection of children items
     *
     * @property {Ember.Array} children
     * @default  null
     */
    children: null,

    /**
     * @property {boolean} isRoot
     * @default  true
     */
    isRoot: true,

    /**
     * @property {Ember.Array} keyEvents
     * @default  null
     */
    keyEvents: null,

    /**
     * @property {boolean} keyHandler
     * @default  false
     */
    keyHandler: false,

    /**
     * Is the menu being interacted with via the keyboard?
     *
     * @property {boolean} keyboardInUse
     * @default  false
     */
    keyboardInUse: false,

    /**
     * Is "Show All" icon and functionalty desired?
     *
     * Is a string representaton of a boolean state
     *
     * @property {string} showAll
     * @default  "false"
     */
    showAll: 'false',

    /**
     * When true, allows key binding to drill down
     *
     * @property {boolean} useDrillDownKey
     * @default  true
     */
    useDrillDownKey: true,

    // -------------------------------------------------------------------------
    // Observers

    /**
     * Initialize children array
     *
     * @function initChildren
     * @observes "init" event
     * @returns  {void}
     */
    initChildren: function() {
        this.set( 'children', Ember.A() );
    }.on( 'init' ),

    /**
     * Initialize keyboard event listeners
     *
     * @function initKeyListeners
     * @observes "didInsertElement" event, keyEvents
     * @returns  {void}
     */
    initKeyListeners: function() {
        var keyEvents = this.get( 'keyEvents' ),
            parent    = this.get( 'parentView' ),
            path      = Ember.A(),
            rootNode  = this,
            self      = this;

        if ( keyEvents ) {
            this.set( 'keyHandler', true );

            keyEvents.on( 'childSelected', function( key ) {
                self.set( 'keyboardInUse', true );
                self.childSelected( key );
            }).on( 'drillDown', function() {
                if ( self.get( 'useDrillDownKey' )) {
                    self.send( 'drillDown' );
                }
            }).on( 'cycleRootSelectionNext', function( event ) {
                self.send( 'cycleRootSelectionNext', event );
            }).on( 'cycleRootSelectionPrevious', function( event ) {
                self.send( 'cycleRootSelectionPrevious', event );
            }).on( 'closeAll', function() {
                self.set( 'keyboardInUse', false );
                self.send( 'closeAll' );
            }).on( 'showAll', function() {
                self.set( 'keyboardInUse', true );
                self.send( 'showAll' );
            });
        }

        // Register child
        if ( typeof parent.registerChild === 'function' ) {
            parent.registerChild( this );
        }

        while( !rootNode.get( 'isRoot' ) ) {
            path.insertAt( 0, rootNode.get( 'menu.label' ) );
            rootNode = rootNode.get( 'parentView' );
        }

        this.setProperties({
            path     : path,
            rootNode : rootNode
        });
    }.observes( 'keyEvents' ).on( 'didInsertElement' ),

    /**
     * Remove bound events and current menu state
     *
     * @function cleanUp
     * @observes "willClearRender" event
     * @returns  {void}
     */
    cleanUp: function() {
        var keyEvents = this.get( 'keyEvents' ),
            parent = this.get( 'parentView' );

        if ( typeof parent.unregisterChild === 'function' ) {
            parent.unregisterChild( this );
        }

        if ( keyEvents ) {
            keyEvents.off( 'childSelected' )
                .off( 'drillDown' )
                .off( 'closeAll' )
                .off( 'showAll' )
                .off( 'cycleRootSelectionNext' )
                .off( 'cycleRootSelectionPrevious' );
        }
    }.on( 'willClearRender' ),

    // -------------------------------------------------------------------------
    // Methods

    /**
     * Activate specified child
     *
     * @function activateChild
     * @param    {mixed} child - Which child to activate
     * @returns  {void}
     */
    activateChild: function( child ) {
        if ( typeof child === 'number' ) {
            child = this.get( 'children' )[ child - 1 ]; // convert to 0 base
        }

        this.get( 'children' ).forEach( function( item ) {
            if ( item === child ) {
                child.performAction();
            } else {
                item.$().removeClass( 'active' );
            }
        }.bind( this ));
    },

    /**
     * Handle child selection event
     *
     * @function childSelected
     * @param    {number} childIndex - Index of the child that is being selected
     * @returns  {void}
     */
    childSelected: function( childIndex ) {
        var child;

        if ( this.get( 'isRoot' ) && this.$().hasClass( 'showall' ) ) {
            this.send( 'closeAll' );
        }

        if ( this.get( 'keyHandler' ) ) {
            this.activateChild( childIndex );
        } else {
            child = this.get( 'activeChild' );

            if ( child ) {
                child.childSelected( childIndex );
            }
        }
    },

    /**
     * Get index of rootNode currently on
     *
     * @function currentRootNodeIndex
     * @returns  {number} - The current root node index
     */
    currentRootNodeIndex: function() {
        var currentIndex = null;

        // Determine index of rootNode currently on
        this.get( 'rootNode.menu.pages' ).forEach( function( item, index ) {
            if ( item.label === this.get( 'activeChild.menu.label' ) ) {
                currentIndex = index;
            }
        }, this );

        return currentIndex;
    }.property().volatile(),

    /**
     * Boolean representation of showAll property
     *
     * @function showAllBoolean
     * @observes showAll
     * @returns  {boolean}
     */
    showAllBoolean: function() {
        return ( 'true' === this.get( 'showAll' ) ) ? true : false;
    }.property( 'showAll' ),

    /**
     * Whether to display the AllView view
     *
     * @function displayShowAll
     * @observes isRoot, showAllBoolean
     * @returns  {boolean}
     */
    displayShowAll: function() {
        return this.get( 'isRoot' ) && this.get( 'showAllBoolean' );
    }.property( 'isRoot', 'showAllBoolean' ),

    /**
     * Send the primary action
     *
     * @function performAction
     * @returns  {void}
     */
    performAction: function() {
        this.$().addClass( 'active' );

        var rootNode = this.get( 'rootNode' ),
            path     = this.get( 'path' ),
            action;

        rootNode.sendAction( 'selectionMade', path );

        if ( this.get( 'menu.pages' ) ) {
            this.showContent();

            if ( !this.get( 'useDrillDownKey' ) ) {
                this.set( 'keyHandler', true );
                this.get( 'parentView' ).set( 'keyHandler', false );
            }
        } else {
            if ( this.get( 'menu.action' )) {
                action = this.get( 'menu.action' );

                if ( typeof action === 'function' ) {
                    action.call( this );
                } else if ( typeof action === 'object' ) {
                    rootNode.sendAction( 'actionInitiated',
                            action.actionName,
                            action.data );
                } else {
                    rootNode.sendAction( 'actionInitiated', action );
                }
            } else if ( this.get( 'menu.route' ) ) {
                rootNode.sendAction( 'changeRoute', this.get( 'menu.route' ) );
            } else if ( this.get( 'menu.link' ) ) {
                window.location.href = this.get( 'menu.link' );
            }

            rootNode.send( 'closeAll' );
        }
    },

    /**
     * Append a child to the children array
     *
     * @function registerChild
     * @param    {mixed} child
     * @returns  {void}
     */
    registerChild: function( child ) {
        this.get( 'children' ).push( child );
    },

    /**
     * Show the active child content of this menu
     *
     * @function showContent
     * @returns  {void}
     */
    showContent: function() {
        this.get( 'parentView' ).set( 'activeChild', this );
    },

    /**
     * Un-register the specified child
     *
     * @function unregisterChild
     * @param    {mixed} child - The child to un-register from this menu
     * @returns  {void}
     */
    unregisterChild: function( child ) {
        if ( child === this.get( 'activeChild' ) ) {
            this.set( 'activeChild', null );
        }

        this.get( 'children' ).removeObject( child );
    }

});
<commit_msg>Closes softlayer/sl-ember-components#258 - Replace bind() call with Ember.run.bind()<commit_after>import Ember from 'ember';

/**
 * @module components
 * @class  sl-menu
 */
export default Ember.Component.extend({

    // -------------------------------------------------------------------------
    // Dependencies

    // -------------------------------------------------------------------------
    // Attributes

    /**
     * Root element HTML tag type
     *
     * @property {Ember.String} tagName
     * @default  "div"
     */
    tagName: 'div',

    /**
     * Class names for the root element
     *
     * @property {Ember.Array} classNames
     */
    classNames: [ 'sl-menu' ],

    // -------------------------------------------------------------------------
    // Actions

    /**
     * Component actions hash
     *
     * @property {Ember.Object} actions
     */
    actions: {

        /**
         * Close all of the sub menus
         *
         * @function actions.closeAll
         * @returns  {void}
         */
        closeAll: function() {
            if ( this.$() ) {
                this.$().removeClass( 'active' );

                if ( this.get( 'isRoot' )) {
                    this.$().removeClass( 'showall' );
                }
            }

            this.set( 'keyHandler', false );

            this.get( 'children' ).forEach( function( item ) {
                item.send( 'closeAll' );
            });

            if ( this.get( 'isRoot' ) ) {
               this.set( 'keyHandler', true );
            }
        },

        /**
         * Cycle rootNode selection forward
         *
         * Only cycles through rootNodes if node was initially selected via keyboard.
         * If "Show All" enabled:
         *     If last rootNode then moves forward to "Show All" option.
         *     If "Show All" option wraps around to first option.
         * If "Show All" disabled:
         *     If last rootNode then wraps around to first option.
         *
         * @function cycleRootSelectionNext
         * @returns  {void}
         */
        cycleRootSelectionNext: function() {
            var currentIndex = this.get( 'currentRootNodeIndex' );

            if ( !this.get( 'keyboardInUse' ) ) {
                return;
            }

            // Whether "Show All" is enabled
            if ( this.get( 'showAllBoolean' ) ) {

                // Not on "Show All"
                if ( null !== currentIndex ) {

                    // Cycling forward, wrapping around last option to first option
                    if ( this.get( 'rootNode.menu.pages.length' ) < currentIndex + 2 ) {
                        this.send( 'showAll' );
                        this.set( 'activeChild', null );

                    // Cycle forward, selecting next rootNode
                    } else {
                        this.childSelected( currentIndex + 2 );
                    }

                // Select first rootNode
                } else {
                    this.childSelected( 1 );
                }

            } else {

                // Cycle forward, wrapping around last option to first option
                if ( this.get( 'rootNode.menu.pages.length' ) < currentIndex + 2 ) {
                    // Select first rootNode
                    this.childSelected( 1 );

                } else {
                    // Cycle forward, selecting next rootNode
                    this.childSelected( currentIndex + 2 );
                }
            }
        },

        /**
         * Cycle rootNode selection backwards
         *
         * Only cycles through rootNodes if node was initially selected via keyboard.
         * If "Show All" enabled:
         *     If first rootNode then wraps around to "Show All" option.
         *     If "Show All" option moves backward to previous option
         * If "Show All" disabled:
         *     If first rootNode then wraps around to last option.
         *
         * @function cycleRootSelectionPrevious
         * @returns  {void}
         */
        cycleRootSelectionPrevious: function() {
            var currentIndex = this.get( 'currentRootNodeIndex' );

            if ( !this.get( 'keyboardInUse' ) ) {
                return;
            }

            // Whether "Show All" is enabled
            if ( this.get( 'showAllBoolean' ) ) {

                // Not on "Show All"
                if ( null !== currentIndex ) {

                    // Cycling backwards, wrapping around first option to last option
                    if ( 0 === currentIndex ) {
                        this.send( 'showAll' );
                        this.set( 'activeChild', null );

                    // Cycle backwards, selecting previous rootNode
                    } else {
                        this.childSelected( currentIndex );
                    }

                // Select last rootNode
                } else {
                    this.childSelected( this.get( 'rootNode.menu.pages' ).length );
                }

            } else {

                // Cycle backwards, wrapping around first option to last option
                if ( 0 === currentIndex ) {
                    // Select last rootNode
                    this.childSelected( this.get( 'rootNode.menu.pages.length' ) );

                } else {
                    // Cycle backward, selecting previous rootNode
                    this.childSelected( currentIndex );
                }
            }
        },

        /**
         * Recursively open sub menus
         *
         * @function actions.drillDown
         * @returns  {void}
         */
        drillDown: function() {
            var child = this.get( 'activeChild' );

            if ( this.get( 'keyHandler' )) {
                if ( child ) {
                    child.set( 'keyHandler', true );
                    this.set( 'keyHandler', false );
                }
            } else if ( child ) {
                child.drillDown();
            }
        },

        /**
         * Send selected action when menu item is selected
         *
         * @function actions.selected
         * @returns  {void}
         */
        selected: function() {
            this.performAction();
        },

        /**
         * Show all of the sub menus
         *
         * @function actions.showAll
         * @returns  {void}
         */
        showAll: function() {
            if ( this.$() ) {
                this.$().addClass( 'active' );

                if ( this.get( 'isRoot' ) ) {
                    this.$().addClass( 'showall' );
                }
            }

            this.get( 'children' ).forEach( function( item ) {
                item.send( 'showAll' );
            });
        }
    },

    // -------------------------------------------------------------------------
    // Events

    /**
     * Method called when menu is clicked
     *
     * @function click
     * @returns  {boolean} false
     */
    click: function() {
        this.performAction();

        return false;
    },

    /**
     * Method triggered on mouseenter event
     *
     * @function mouseEnter
     * @returns  {void}
     */
    mouseEnter: function() {
        var currentActiveRootNodeIndex = this.get( 'currentRootNodeIndex' ),
            query;

        if ( this.get( 'keyboardInUse' ) ) {
            if ( null !== currentActiveRootNodeIndex ) {
                query = 'a:contains("' + this.get( 'rootNode.menu.pages' )[currentActiveRootNodeIndex].label + '")';
                this.$(query).parent().removeClass( 'active' );
            }

            this.set( 'keyboardInUse', false );
        }

        this.$().addClass( 'active' );
    },

    /**
     * Method triggered on mouseleave event
     *
     * @function mouseLeave
     * @returns  {void}
     */
    mouseLeave: function() {
        if ( this.get( 'isRoot' ) ) {
            this.send( 'closeAll' );
        } else if ( !this.get( 'rootNode' ).$().hasClass( 'showall' ) ) {
            this.$().removeClass( 'active' );
        }
    },

    // -------------------------------------------------------------------------
    // Properties

    /**
     * Currently active child
     *
     * @property {number} activeChild
     * @default  null
     */
    activeChild: null,

    /**
     * Embedded Ember View representing the "Show All"
     *
     * @property {Ember.View} AllView
     */
    AllView: Ember.View.extend({

        /**
         * HTML tag name of the root element
         *
         * @property {Ember.String} tagName
         * @default  "li"
         */
        tagName: 'li',

        /**
         * Class names for the AllView view
         *
         * @property {Ember.Array} AllView.classNames
         */
        classNames: [ 'all' ],

        /**
         * Method called on mouseenter event
         *
         * @function AllView.mouseEnter
         * @returns  {void}
         */
        mouseEnter: function() {
            this.send( 'showAll' );
        },

        /**
         * Target pointer to the parent view
         *
         * @function AllView.target
         * @observes parentView
         * @return   {Ember.View}
         */
        target: function() {
            return this.get( 'parentView' );
        }.property( 'parentView' )
    }),

    /**
     * Collection of children items
     *
     * @property {Ember.Array} children
     * @default  null
     */
    children: null,

    /**
     * @property {boolean} isRoot
     * @default  true
     */
    isRoot: true,

    /**
     * @property {Ember.Array} keyEvents
     * @default  null
     */
    keyEvents: null,

    /**
     * @property {boolean} keyHandler
     * @default  false
     */
    keyHandler: false,

    /**
     * Is the menu being interacted with via the keyboard?
     *
     * @property {boolean} keyboardInUse
     * @default  false
     */
    keyboardInUse: false,

    /**
     * Is "Show All" icon and functionalty desired?
     *
     * Is a string representaton of a boolean state
     *
     * @property {string} showAll
     * @default  "false"
     */
    showAll: 'false',

    /**
     * When true, allows key binding to drill down
     *
     * @property {boolean} useDrillDownKey
     * @default  true
     */
    useDrillDownKey: true,

    // -------------------------------------------------------------------------
    // Observers

    /**
     * Initialize children array
     *
     * @function initChildren
     * @observes "init" event
     * @returns  {void}
     */
    initChildren: function() {
        this.set( 'children', Ember.A() );
    }.on( 'init' ),

    /**
     * Initialize keyboard event listeners
     *
     * @function initKeyListeners
     * @observes "didInsertElement" event, keyEvents
     * @returns  {void}
     */
    initKeyListeners: function() {
        var keyEvents = this.get( 'keyEvents' ),
            parent    = this.get( 'parentView' ),
            path      = Ember.A(),
            rootNode  = this,
            self      = this;

        if ( keyEvents ) {
            this.set( 'keyHandler', true );

            keyEvents.on( 'childSelected', function( key ) {
                self.set( 'keyboardInUse', true );
                self.childSelected( key );
            }).on( 'drillDown', function() {
                if ( self.get( 'useDrillDownKey' )) {
                    self.send( 'drillDown' );
                }
            }).on( 'cycleRootSelectionNext', function( event ) {
                self.send( 'cycleRootSelectionNext', event );
            }).on( 'cycleRootSelectionPrevious', function( event ) {
                self.send( 'cycleRootSelectionPrevious', event );
            }).on( 'closeAll', function() {
                self.set( 'keyboardInUse', false );
                self.send( 'closeAll' );
            }).on( 'showAll', function() {
                self.set( 'keyboardInUse', true );
                self.send( 'showAll' );
            });
        }

        // Register child
        if ( typeof parent.registerChild === 'function' ) {
            parent.registerChild( this );
        }

        while( !rootNode.get( 'isRoot' ) ) {
            path.insertAt( 0, rootNode.get( 'menu.label' ) );
            rootNode = rootNode.get( 'parentView' );
        }

        this.setProperties({
            path     : path,
            rootNode : rootNode
        });
    }.observes( 'keyEvents' ).on( 'didInsertElement' ),

    /**
     * Remove bound events and current menu state
     *
     * @function cleanUp
     * @observes "willClearRender" event
     * @returns  {void}
     */
    cleanUp: function() {
        var keyEvents = this.get( 'keyEvents' ),
            parent = this.get( 'parentView' );

        if ( typeof parent.unregisterChild === 'function' ) {
            parent.unregisterChild( this );
        }

        if ( keyEvents ) {
            keyEvents.off( 'childSelected' )
                .off( 'drillDown' )
                .off( 'closeAll' )
                .off( 'showAll' )
                .off( 'cycleRootSelectionNext' )
                .off( 'cycleRootSelectionPrevious' );
        }
    }.on( 'willClearRender' ),

    // -------------------------------------------------------------------------
    // Methods

    /**
     * Activate specified child
     *
     * @function activateChild
     * @param    {mixed} child - Which child to activate
     * @returns  {void}
     */
    activateChild: function( child ) {
        if ( typeof child === 'number' ) {
            child = this.get( 'children' )[ child - 1 ]; // convert to 0 base
        }

        Ember.run.bind( this, this.get( 'children' ).forEach( function( item ) {
            if ( item === child ) {
                child.performAction();
            } else {
                item.$().removeClass( 'active' );
            }
        }));
    },

    /**
     * Handle child selection event
     *
     * @function childSelected
     * @param    {number} childIndex - Index of the child that is being selected
     * @returns  {void}
     */
    childSelected: function( childIndex ) {
        var child;

        if ( this.get( 'isRoot' ) && this.$().hasClass( 'showall' ) ) {
            this.send( 'closeAll' );
        }

        if ( this.get( 'keyHandler' ) ) {
            this.activateChild( childIndex );
        } else {
            child = this.get( 'activeChild' );

            if ( child ) {
                child.childSelected( childIndex );
            }
        }
    },

    /**
     * Get index of rootNode currently on
     *
     * @function currentRootNodeIndex
     * @returns  {number} - The current root node index
     */
    currentRootNodeIndex: function() {
        var currentIndex = null;

        // Determine index of rootNode currently on
        this.get( 'rootNode.menu.pages' ).forEach( function( item, index ) {
            if ( item.label === this.get( 'activeChild.menu.label' ) ) {
                currentIndex = index;
            }
        }, this );

        return currentIndex;
    }.property().volatile(),

    /**
     * Boolean representation of showAll property
     *
     * @function showAllBoolean
     * @observes showAll
     * @returns  {boolean}
     */
    showAllBoolean: function() {
        return ( 'true' === this.get( 'showAll' ) ) ? true : false;
    }.property( 'showAll' ),

    /**
     * Whether to display the AllView view
     *
     * @function displayShowAll
     * @observes isRoot, showAllBoolean
     * @returns  {boolean}
     */
    displayShowAll: function() {
        return this.get( 'isRoot' ) && this.get( 'showAllBoolean' );
    }.property( 'isRoot', 'showAllBoolean' ),

    /**
     * Send the primary action
     *
     * @function performAction
     * @returns  {void}
     */
    performAction: function() {
        this.$().addClass( 'active' );

        var rootNode = this.get( 'rootNode' ),
            path     = this.get( 'path' ),
            action;

        rootNode.sendAction( 'selectionMade', path );

        if ( this.get( 'menu.pages' ) ) {
            this.showContent();

            if ( !this.get( 'useDrillDownKey' ) ) {
                this.set( 'keyHandler', true );
                this.get( 'parentView' ).set( 'keyHandler', false );
            }
        } else {
            if ( this.get( 'menu.action' )) {
                action = this.get( 'menu.action' );

                if ( typeof action === 'function' ) {
                    action.call( this );
                } else if ( typeof action === 'object' ) {
                    rootNode.sendAction( 'actionInitiated',
                            action.actionName,
                            action.data );
                } else {
                    rootNode.sendAction( 'actionInitiated', action );
                }
            } else if ( this.get( 'menu.route' ) ) {
                rootNode.sendAction( 'changeRoute', this.get( 'menu.route' ) );
            } else if ( this.get( 'menu.link' ) ) {
                window.location.href = this.get( 'menu.link' );
            }

            rootNode.send( 'closeAll' );
        }
    },

    /**
     * Append a child to the children array
     *
     * @function registerChild
     * @param    {mixed} child
     * @returns  {void}
     */
    registerChild: function( child ) {
        this.get( 'children' ).push( child );
    },

    /**
     * Show the active child content of this menu
     *
     * @function showContent
     * @returns  {void}
     */
    showContent: function() {
        this.get( 'parentView' ).set( 'activeChild', this );
    },

    /**
     * Un-register the specified child
     *
     * @function unregisterChild
     * @param    {mixed} child - The child to un-register from this menu
     * @returns  {void}
     */
    unregisterChild: function( child ) {
        if ( child === this.get( 'activeChild' ) ) {
            this.set( 'activeChild', null );
        }

        this.get( 'children' ).removeObject( child );
    }

});
<|endoftext|>


<commit_before>import Ember from 'ember';

// these are not currently editable in Ember
const transitioningInClass  = 'ember-transitioning-in';
const transitioningOutClass = 'ember-transitioning-out';

export default Ember.Mixin.create({

  classNameBindings: ['_active','_disabled','_transitioningIn','_transitioningOut'],
  linkSelector: 'a.ember-view',

  initChildLinkViews: Ember.on('init', function(){
    this.set('childLinkViews', Ember.A());
  }),

  buildChildLinkViews: Ember.on('didRender', function(){
    Ember.run.schedule('afterRender', this, function(){
      let childLinkSelector = this.get('linkSelector');
      let childLinkElements = this.$(childLinkSelector);
      let applicationContainer = Ember.getOwner(this).application.__container__;
      let viewRegistry = applicationContainer.lookup('-view-registry:main') || Ember.View.views;

      let childLinkViews = childLinkElements.toArray().map(
        view => viewRegistry[view.id]
      );

      this.set('childLinkViews', Ember.A(childLinkViews));
    });
  }),

  _transitioningIn: Ember.computed('childLinkViews.@each.transitioningIn', function(){
    if (this.get('childLinkViews').isAny('transitioningIn')) {
      return transitioningInClass;
    }
  }),

  _transitioningOut: Ember.computed('childLinkViews.@each.transitioningOut', function(){
    if (this.get('childLinkViews').isAny('transitioningOut')) {
      return transitioningOutClass;
    }
  }),

  hasActiveLinks: Ember.computed('childLinkViews.@each.active', function(){
    return this.get('childLinkViews').isAny('active');
  }),

  activeClass: Ember.computed('childLinkViews.@each.active', function(){
    let activeLink = this.get('childLinkViews').findBy('active');
    return (activeLink ? activeLink.get('active') : 'active');
  }),

  _active: Ember.computed('hasActiveLinks', 'activeClass', function(){
    return (this.get('hasActiveLinks') ? this.get('activeClass') : false);
  }),

  allLinksDisabled: Ember.computed('childLinkViews.@each.disabled', function(){
    return this.get('childLinkViews').isEvery('disabled');
  }),

  disabledClass: Ember.computed('childLinkViews.@each.disabled', function(){
    let disabledLink = this.get('childLinkViews').findBy('disabled');
    return (disabledLink ? disabledLink.get('disabled') : 'disabled');
  }),

  _disabled: Ember.computed('allLinksDisabled', 'disabledClass', function(){
    return (this.get('allLinksDisabled') ? this.get('disabledClass') : false);
  })

});
<commit_msg>Fix beta builds<commit_after>import Ember from 'ember';

// these are not currently editable in Ember
const transitioningInClass  = 'ember-transitioning-in';
const transitioningOutClass = 'ember-transitioning-out';

export default Ember.Mixin.create({

  classNameBindings: ['_active','_disabled','_transitioningIn','_transitioningOut'],
  linkSelector: 'a.ember-view',

  init() {
    this._super(...arguments);

    this.set('childLinkViews',  Ember.A([]));
  },

  buildChildLinkViews: Ember.on('didReceiveAttrs', function(){
    Ember.run.scheduleOnce('afterRender', this, function(){
      let childLinkSelector = this.get('linkSelector');
      let childLinkElements = this.$(childLinkSelector);
      let applicationContainer = Ember.getOwner(this).application.__container__;
      let viewRegistry = applicationContainer.lookup('-view-registry:main');

      let childLinkViews = childLinkElements.toArray().map(
        view => viewRegistry[view.id]
      );

      this.set('childLinkViews', Ember.A(childLinkViews));
    });
  }),

  _transitioningIn: Ember.computed('childLinkViews.@each.transitioningIn', function(){
    if (this.get('childLinkViews').isAny('transitioningIn')) {
      return transitioningInClass;
    }
  }),

  _transitioningOut: Ember.computed('childLinkViews.@each.transitioningOut', function(){
    if (this.get('childLinkViews').isAny('transitioningOut')) {
      return transitioningOutClass;
    }
  }),

  hasActiveLinks: Ember.computed('childLinkViews.@each.active', function(){
    return this.get('childLinkViews').isAny('active');
  }),

  activeClass: Ember.computed('childLinkViews.@each.active', function(){
    let activeLink = this.get('childLinkViews').findBy('active');
    return (activeLink ? activeLink.get('active') : 'active');
  }),

  _active: Ember.computed('hasActiveLinks', 'activeClass', function(){
    return (this.get('hasActiveLinks') ? this.get('activeClass') : false);
  }),

  allLinksDisabled: Ember.computed('childLinkViews.@each.disabled', function(){
    return this.get('childLinkViews').isEvery('disabled');
  }),

  disabledClass: Ember.computed('childLinkViews.@each.disabled', function(){
    let disabledLink = this.get('childLinkViews').findBy('disabled');
    return (disabledLink ? disabledLink.get('disabled') : 'disabled');
  }),

  _disabled: Ember.computed('allLinksDisabled', 'disabledClass', function(){
    return (this.get('allLinksDisabled') ? this.get('disabledClass') : false);
  })

});
<|endoftext|>


<commit_before>import Service from '@ember/service';
import { computed, get, getProperties, set } from '@ember/object';
import { getOwner } from '@ember/application';
import isMobile from 'ismobilejs';

export default Service.extend({

  fastboot: computed(function() {
    return getOwner(this).lookup('service:fastboot');
  }),

  init() {
    this._super(...arguments);

    let queries = [];
    if (get(this, 'fastboot.isFastBoot')) {
      const headers = get(this, 'fastboot.request.headers');
      const userAgent = get(headers, 'user-agent');
      if (userAgent) {
        queries = getProperties(
          isMobile(userAgent),
          ['any', 'phone', 'tablet', 'apple', 'android', 'amazon', 'windows', 'seven_inch', 'other']
        );
      }
    } else {
      queries = isMobile;
    }

    for (let media in queries) {
      set(this, media, queries[media]);
    }
  }
});<commit_msg>Fix accessing the user agent string in newer FastBoot versions<commit_after>import Service from '@ember/service';
import { computed, get, getProperties, set } from '@ember/object';
import { getOwner } from '@ember/application';
import { isBlank } from '@ember/utils';
import isMobile from 'ismobilejs';

export default Service.extend({

  fastboot: computed(function() {
    return getOwner(this).lookup('service:fastboot');
  }),

  init() {
    this._super(...arguments);

    let queries = [];
    if (get(this, 'fastboot.isFastBoot')) {
      const headers = get(this, 'fastboot.request.headers');
      let userAgent = get(headers, 'headers.user-agent')[0];

      if (isBlank(userAgent)) { return; }

      queries = getProperties(
        isMobile(userAgent),
        ['any', 'phone', 'tablet', 'apple', 'android', 'amazon', 'windows', 'seven_inch', 'other']
      );
    } else {
      queries = isMobile;
    }

    for (let media in queries) {
      set(this, media, queries[media]);
    }
  }
});<|endoftext|>


<commit_before>/**
  @module ember-flexberry-data
*/

import Ember from 'ember';
import NumberTransform from 'ember-data/-private/transforms/number';

/**
  Transformation for model's attributes defined as DS.attr
  with type 'decimal'.
  It extends number transformation from ember data.
  @class DecimalTransform
  @extends DS.NumberTransform
  @example
  ```
  import DS from 'ember-data';
  export default DS.Model.extend({
    name: DS.attr('string');
    decimalNumber: DS.attr('decimal')
  });
  ```
*/
export default NumberTransform.extend({
  /**
    Deserializes serialized attribute value.
   */
  deserialize(serialized) {
    return Number(serialized.toString().replace(',', '.'));
  },

  /**
    Serializes deserialized attribute value.
   */
  serialize(deserialized) {
    return Number(deserialized.toString().replace(',', '.'));
  }
});
<commit_msg>Fix `decimal` transform<commit_after>/**
  @module ember-flexberry-data
*/

import Ember from 'ember';
import NumberTransform from 'ember-data/-private/transforms/number';

/**
  Transformation for model's attributes defined as DS.attr
  with type 'decimal'.
  It extends number transformation from ember data.
  @class DecimalTransform
  @extends DS.NumberTransform
  @example
  ```
  import DS from 'ember-data';
  export default DS.Model.extend({
    name: DS.attr('string');
    decimalNumber: DS.attr('decimal')
  });
  ```
*/
export default NumberTransform.extend({
  /**
    Deserializes serialized attribute value.
   */
  deserialize(serialized) {
    return Ember.isEmpty(serialized) ? null : this._super(serialized.toString().replace(',', '.'));
  },

  /**
    Serializes deserialized attribute value.
   */
  serialize(deserialized) {
    return Ember.isEmpty(deserialized) ? null : this._super(deserialized.toString().replace(',', '.'));
  }
});
<|endoftext|>


<commit_before>var _ = require('underscore');
var CoreView = require('backbone/core-view');
var createTextLabelsTabPane = require('../../components/tab-pane/create-text-labels-tab-pane');
var TabPaneTemplate = require('./layer-tab-pane.tpl');
var Header = require('./layer-header-view.js');
var LayerContentAnalysesView = require('./layer-content-views/layer-content-analyses-view');
var StyleView = require('../style/style-view');
var InfowindowsView = require('./layer-content-views/infowindows-view');
var TablesCollection = require('../../data/tables-collection');
var AnalysisSourceOptionsModel = require('./layer-content-views/analyses/analysis-source-options-model');
var AnalysisFormsCollection = require('./layer-content-views/analyses/analysis-forms-collection');
var DataView = require('./layer-content-views/data/data-view');

module.exports = CoreView.extend({
  events: {
    'click .js-back': '_onClickBack'
  },

  options: {
    analysisPayload: null // id or a new analysis node attrs (may not be complete)
  },

  initialize: function (opts) {
    if (!opts.analysisDefinitionNodesCollection) throw new Error('analysisDefinitionNodesCollection is required');
    if (!opts.layerDefinitionsCollection) throw new Error('layerDefinitionsCollection is required');
    if (!opts.layerDefinitionModel) throw new Error('layerDefinitionModel is required');
    if (!opts.stackLayoutModel) throw new Error('StackLayoutModel is required');
    if (!opts.modals) throw new Error('modals is required');
    if (!opts.configModel) throw new Error('configModel is required');
    if (!opts.editorModel) throw new Error('editorModel is required');
    if (!opts.analysis) throw new Error('analysis is required');

    this.analysisDefinitionNodesCollection = opts.analysisDefinitionNodesCollection;
    this.layerDefinitionModel = opts.layerDefinitionModel;
    this.layerDefinitionsCollection = opts.layerDefinitionsCollection;
    this.stackLayoutModel = opts.stackLayoutModel;
    this.modals = opts.modals;
    this._configModel = opts.configModel;
    this._editorModel = opts.editorModel;
    this.analysis = opts.analysis;

    this._editorModel.on('change:edition', this._changeStyle, this);
    this.add_related_model(this._editorModel);
  },

  render: function () {
    this._unbindEvents();

    var self = this;
    var analysisPayload = this.options.analysisPayload;

    var tabPaneTabs = [{
      label: _t('editor.layers.menu-tab-pane-labels.data'),
      name: 'data',
      selected: !analysisPayload,
      createContentView: function () {
        return new DataView({
          className: 'Editor-content',
          layerDefinitionModel: self.layerDefinitionModel,
          configModel: self._configModel,
          modals: self.modals,
          editorModel: self._editorModel
        });
      }
    }, {
      label: _t('editor.layers.menu-tab-pane-labels.analyses'),
      name: 'analyses',
      selected: !!analysisPayload,
      createContentView: function () {
        var analysisSourceOptionsModel = new AnalysisSourceOptionsModel(null, {
          analysisDefinitionNodesCollection: self.analysisDefinitionNodesCollection,
          layerDefinitionsCollection: self.layerDefinitionsCollection,
          tablesCollection: new TablesCollection(null, {
            configModel: self._configModel
          })
        });
        analysisSourceOptionsModel.fetch();

        var analysisFormsCollection = new AnalysisFormsCollection(null, {
          layerDefinitionModel: self.layerDefinitionModel,
          analysisSourceOptionsModel: analysisSourceOptionsModel
        });
        analysisFormsCollection.resetByLayerDefinition();

        // e.g. when selected from layers view
        var selectedNodeId;
        if (_.isString(analysisPayload)) {
          selectedNodeId = analysisPayload;
        }

        // payload passed after continue when an option was selected in add-analysis-view
        if (_.isObject(analysisPayload)) {
          selectedNodeId = analysisPayload.id;
          analysisFormsCollection.addHead(analysisPayload);
        }

        return new LayerContentAnalysesView({
          className: 'Editor-content',
          analysis: self.analysis,
          analysisFormsCollection: analysisFormsCollection,
          layerDefinitionModel: self.layerDefinitionModel,
          selectedNodeId: selectedNodeId
        });
      }
    }, {
      label: _t('editor.layers.menu-tab-pane-labels.style'),
      name: 'style',
      createContentView: function () {
        var lastLayerNodeModel = self.layerDefinitionModel.getAnalysisDefinitionNodeModel();
        return new StyleView({
          className: 'Editor-content',
          configModel: self._configModel,
          analysisDefinitionsCollection: self.analysisDefinitionsCollection,
          querySchemaModel: lastLayerNodeModel.querySchemaModel,
          layerDefinitionsCollection: self.layerDefinitionsCollection,
          layerDefinitionModel: self.layerDefinitionModel,
          editorModel: self._editorModel,
          modals: self.modals
        });
      }
    }, {
      label: _t('editor.layers.menu-tab-pane-labels.infowindow'),
      name: 'infowindow',
      createContentView: function () {
        return new InfowindowsView({
          className: 'Editor-content',
          layerDefinitionModel: self.layerDefinitionModel,
          querySchemaModel: self.layerDefinitionModel.getAnalysisDefinitionNodeModel().querySchemaModel,
          configModel: self._configModel,
          editorModel: self._editorModel
        });
      }
    }, {
      label: _t('editor.layers.menu-tab-pane-labels.legends'),
      name: 'legends',
      createContentView: function () {
        return new CoreView({
          className: 'Editor-content'
        });
      }
    }];

    var header = new Header({
      layerDefinitionModel: this.layerDefinitionModel,
      editorModel: this._editorModel
    });

    this.addView(header);
    this.$el.append(header.render().$el);

    var tabPaneOptions = {
      tabPaneOptions: {
        template: TabPaneTemplate,
        tabPaneItemOptions: {
          tagName: 'li',
          className: 'CDB-NavMenu-item'
        }
      },
      tabPaneItemLabelOptions: {
        tagName: 'button',
        className: 'CDB-NavMenu-link u-upperCase'
      }
    };

    this._layerTabPaneView = createTextLabelsTabPane(tabPaneTabs, tabPaneOptions);
    this._bindEvents();
    this.$el.append(this._layerTabPaneView.render().$el);
    this.addView(this._layerTabPaneView);
    return this;
  },

  _unbindEvents: function () {
    if (this._layerTabPaneView && this._layerTabPaneView.collection) {
      this._layerTabPaneView.collection.off('change:selected', this._quitEditing, this);
    }
  },

  _bindEvents: function () {
    this._layerTabPaneView.collection.on('change:selected', this._quitEditing, this);
    this.add_related_model(this._layerTabPaneView.collection);
  },

  _onClickBack: function () {
    this._editorModel.set({ edition: false });
    this.stackLayoutModel.prevStep('layers');
  },

  _changeStyle: function () {
    this._layerTabPaneView.changeStyleMenu(this._editorModel);
  },

  _quitEditing: function () {
    if (this._layerTabPaneView.getSelectedTabPaneName() !== 'style') {
      this._editorModel.set({ edition: false });
    }
  }
});
<commit_msg>Make sure to always have a selected node for analyses<commit_after>var _ = require('underscore');
var CoreView = require('backbone/core-view');
var createTextLabelsTabPane = require('../../components/tab-pane/create-text-labels-tab-pane');
var TabPaneTemplate = require('./layer-tab-pane.tpl');
var Header = require('./layer-header-view.js');
var LayerContentAnalysesView = require('./layer-content-views/layer-content-analyses-view');
var StyleView = require('../style/style-view');
var InfowindowsView = require('./layer-content-views/infowindows-view');
var TablesCollection = require('../../data/tables-collection');
var AnalysisSourceOptionsModel = require('./layer-content-views/analyses/analysis-source-options-model');
var AnalysisFormsCollection = require('./layer-content-views/analyses/analysis-forms-collection');
var DataView = require('./layer-content-views/data/data-view');

module.exports = CoreView.extend({
  events: {
    'click .js-back': '_onClickBack'
  },

  options: {
    analysisPayload: null // id or a new analysis node attrs (may not be complete)
  },

  initialize: function (opts) {
    if (!opts.analysisDefinitionNodesCollection) throw new Error('analysisDefinitionNodesCollection is required');
    if (!opts.layerDefinitionsCollection) throw new Error('layerDefinitionsCollection is required');
    if (!opts.layerDefinitionModel) throw new Error('layerDefinitionModel is required');
    if (!opts.stackLayoutModel) throw new Error('StackLayoutModel is required');
    if (!opts.modals) throw new Error('modals is required');
    if (!opts.configModel) throw new Error('configModel is required');
    if (!opts.editorModel) throw new Error('editorModel is required');
    if (!opts.analysis) throw new Error('analysis is required');

    this.analysisDefinitionNodesCollection = opts.analysisDefinitionNodesCollection;
    this.layerDefinitionModel = opts.layerDefinitionModel;
    this.layerDefinitionsCollection = opts.layerDefinitionsCollection;
    this.stackLayoutModel = opts.stackLayoutModel;
    this.modals = opts.modals;
    this._configModel = opts.configModel;
    this._editorModel = opts.editorModel;
    this.analysis = opts.analysis;

    this._editorModel.on('change:edition', this._changeStyle, this);
    this.add_related_model(this._editorModel);
  },

  render: function () {
    this._unbindEvents();

    var self = this;
    var analysisPayload = this.options.analysisPayload;

    var tabPaneTabs = [{
      label: _t('editor.layers.menu-tab-pane-labels.data'),
      name: 'data',
      selected: !analysisPayload,
      createContentView: function () {
        return new DataView({
          className: 'Editor-content',
          layerDefinitionModel: self.layerDefinitionModel,
          configModel: self._configModel,
          modals: self.modals,
          editorModel: self._editorModel
        });
      }
    }, {
      label: _t('editor.layers.menu-tab-pane-labels.analyses'),
      name: 'analyses',
      selected: !!analysisPayload,
      createContentView: function () {
        var analysisSourceOptionsModel = new AnalysisSourceOptionsModel(null, {
          analysisDefinitionNodesCollection: self.analysisDefinitionNodesCollection,
          layerDefinitionsCollection: self.layerDefinitionsCollection,
          tablesCollection: new TablesCollection(null, {
            configModel: self._configModel
          })
        });
        analysisSourceOptionsModel.fetch();

        var analysisFormsCollection = new AnalysisFormsCollection(null, {
          layerDefinitionModel: self.layerDefinitionModel,
          analysisSourceOptionsModel: analysisSourceOptionsModel
        });
        analysisFormsCollection.resetByLayerDefinition();

        // e.g. when selected from layers view
        var selectedNodeId;
        if (_.isString(analysisPayload)) {
          selectedNodeId = analysisPayload;
        } else if (_.isObject(analysisPayload)) {
          // payload passed after continue when an option was selected in add-analysis-view
          selectedNodeId = analysisPayload.id;
          analysisFormsCollection.addHead(analysisPayload);
        } else {
          selectedNodeId = self.layerDefinitionModel.get('source');
        }

        return new LayerContentAnalysesView({
          className: 'Editor-content',
          analysis: self.analysis,
          analysisFormsCollection: analysisFormsCollection,
          layerDefinitionModel: self.layerDefinitionModel,
          selectedNodeId: selectedNodeId
        });
      }
    }, {
      label: _t('editor.layers.menu-tab-pane-labels.style'),
      name: 'style',
      createContentView: function () {
        var lastLayerNodeModel = self.layerDefinitionModel.getAnalysisDefinitionNodeModel();
        return new StyleView({
          className: 'Editor-content',
          configModel: self._configModel,
          analysisDefinitionsCollection: self.analysisDefinitionsCollection,
          querySchemaModel: lastLayerNodeModel.querySchemaModel,
          layerDefinitionsCollection: self.layerDefinitionsCollection,
          layerDefinitionModel: self.layerDefinitionModel,
          editorModel: self._editorModel,
          modals: self.modals
        });
      }
    }, {
      label: _t('editor.layers.menu-tab-pane-labels.infowindow'),
      name: 'infowindow',
      createContentView: function () {
        return new InfowindowsView({
          className: 'Editor-content',
          layerDefinitionModel: self.layerDefinitionModel,
          querySchemaModel: self.layerDefinitionModel.getAnalysisDefinitionNodeModel().querySchemaModel,
          configModel: self._configModel,
          editorModel: self._editorModel
        });
      }
    }, {
      label: _t('editor.layers.menu-tab-pane-labels.legends'),
      name: 'legends',
      createContentView: function () {
        return new CoreView({
          className: 'Editor-content'
        });
      }
    }];

    var header = new Header({
      layerDefinitionModel: this.layerDefinitionModel,
      editorModel: this._editorModel
    });

    this.addView(header);
    this.$el.append(header.render().$el);

    var tabPaneOptions = {
      tabPaneOptions: {
        template: TabPaneTemplate,
        tabPaneItemOptions: {
          tagName: 'li',
          className: 'CDB-NavMenu-item'
        }
      },
      tabPaneItemLabelOptions: {
        tagName: 'button',
        className: 'CDB-NavMenu-link u-upperCase'
      }
    };

    this._layerTabPaneView = createTextLabelsTabPane(tabPaneTabs, tabPaneOptions);
    this._bindEvents();
    this.$el.append(this._layerTabPaneView.render().$el);
    this.addView(this._layerTabPaneView);
    return this;
  },

  _unbindEvents: function () {
    if (this._layerTabPaneView && this._layerTabPaneView.collection) {
      this._layerTabPaneView.collection.off('change:selected', this._quitEditing, this);
    }
  },

  _bindEvents: function () {
    this._layerTabPaneView.collection.on('change:selected', this._quitEditing, this);
    this.add_related_model(this._layerTabPaneView.collection);
  },

  _onClickBack: function () {
    this._editorModel.set({ edition: false });
    this.stackLayoutModel.prevStep('layers');
  },

  _changeStyle: function () {
    this._layerTabPaneView.changeStyleMenu(this._editorModel);
  },

  _quitEditing: function () {
    if (this._layerTabPaneView.getSelectedTabPaneName() !== 'style') {
      this._editorModel.set({ edition: false });
    }
  }
});
<|endoftext|>


<commit_before>var queue = require('queue-async');
var _ = require('underscore');

function MapConfigOverviewsAdapter(overviewsMetadataBackend, filterStatsBackend) {
    this.overviewsMetadataBackend = overviewsMetadataBackend;
    this.filterStatsBackend = filterStatsBackend;
}

module.exports = MapConfigOverviewsAdapter;

MapConfigOverviewsAdapter.prototype.getMapConfig = function (user, requestMapConfig, params, context, callback) {
    var self = this;

    var layers = requestMapConfig.layers;
    var analysesResults = context.analysesResults;

    if (!layers || layers.length === 0) {
        return callback(null, requestMapConfig);
    }

    var augmentLayersQueue = queue(layers.length);

    layers.forEach(function (layer) {
        augmentLayersQueue.defer(
            augmentLayer,
            self.overviewsMetadataBackend,
            self.filterStatsBackend,
            user,
            layer,
            analysesResults
        );
    });

    augmentLayersQueue.awaitAll(function layersAugmentQueueFinish (err, layers) {
        if (err) {
            return callback(err);
        }

        if (!layers || layers.length === 0) {
            return callback(new Error('Missing layers array from layergroup config'));
        }

        requestMapConfig.layers = layers;

        return callback(null, requestMapConfig);
    });

};

function augmentLayer(overviewsMetadataBackend, filterStatsBackend, user, layer, analysesResults, done) {
    if (layer.type !== 'mapnik' && layer.type !== 'cartodb') {
        return done(null, layer);
    }

    overviewsMetadataBackend.getOverviewsMetadata(user, layer.options.sql, function (err, metadata) {
        if (err) {
            return done(err, layer);
        }

        var query_rewrite_data = { overviews: metadata };

        if (layer.options.source && analysesResults && !layer.options.sql_wrap) {
            var sourceId = layer.options.source.id;

            query_rewrite_data.filters = getFilters(analysesResults, sourceId);
            query_rewrite_data.unfiltered_query = getUnfilteredQuery(analysesResults, sourceId);
        }

        var filters = query_rewrite_data.filters;

        if (!filters) {
            if (!_.isEmpty(metadata)) {
                layer = Object.assign({}, layer);
                layer.options = Object.assign({}, layer.options, { query_rewrite_data });
            }

            return done(null, layer);
        }

        var unfiltered_query = query_rewrite_data.unfiltered_query;

        filterStatsBackend.getFilterStats(user, unfiltered_query, filters, function (err, stats) {
            if (err) {
                return done(null, layer);
            }

            query_rewrite_data.filter_stats = stats;

            if (!_.isEmpty(metadata)) {
                layer = Object.assign({}, layer);
                layer.options = Object.assign({}, layer.options, { query_rewrite_data });
            }

            return done(null, layer);
        });
    });
}

function getRootNode (analysesResults, sourceId) {
    var node = _.find(analysesResults, function (a) {
        return a.rootNode.params.id === sourceId;
    });

    return node ? node.rootNode : undefined;
}

function getUnfilteredQuery (analysesResults, sourceId) {
    var node = getRootNode(analysesResults, sourceId);

    if (node) {
        var filters = node.getFilters();
        var filters_disabler = Object.keys(filters).reduce(function (disabler, filter_id) {
            disabler[filter_id] = false;
            return disabler;
        }, {});

        return node.getQuery(filters_disabler);
    }
}

function getFilters (analysesResults, sourceId) {
    var node = getRootNode(analysesResults, sourceId);

    if (node) {
        return node.getFilters();
    }
}
<commit_msg>CamelCase<commit_after>var queue = require('queue-async');
var _ = require('underscore');

function MapConfigOverviewsAdapter(overviewsMetadataBackend, filterStatsBackend) {
    this.overviewsMetadataBackend = overviewsMetadataBackend;
    this.filterStatsBackend = filterStatsBackend;
}

module.exports = MapConfigOverviewsAdapter;

MapConfigOverviewsAdapter.prototype.getMapConfig = function (user, requestMapConfig, params, context, callback) {
    var self = this;

    var layers = requestMapConfig.layers;
    var analysesResults = context.analysesResults;

    if (!layers || layers.length === 0) {
        return callback(null, requestMapConfig);
    }

    var augmentLayersQueue = queue(layers.length);

    layers.forEach(function (layer) {
        augmentLayersQueue.defer(
            augmentLayer,
            self.overviewsMetadataBackend,
            self.filterStatsBackend,
            user,
            layer,
            analysesResults
        );
    });

    augmentLayersQueue.awaitAll(function layersAugmentQueueFinish (err, layers) {
        if (err) {
            return callback(err);
        }

        if (!layers || layers.length === 0) {
            return callback(new Error('Missing layers array from layergroup config'));
        }

        requestMapConfig.layers = layers;

        return callback(null, requestMapConfig);
    });

};

function augmentLayer(overviewsMetadataBackend, filterStatsBackend, user, layer, analysesResults, done) {
    if (layer.type !== 'mapnik' && layer.type !== 'cartodb') {
        return done(null, layer);
    }

    overviewsMetadataBackend.getOverviewsMetadata(user, layer.options.sql, function (err, metadata) {
        if (err) {
            return done(err, layer);
        }

        var queryRewriteData = { overviews: metadata };

        if (layer.options.source && analysesResults && !layer.options.sql_wrap) {
            var sourceId = layer.options.source.id;

            queryRewriteData.filters = getFilters(analysesResults, sourceId);
            queryRewriteData.unfiltered_query = getUnfilteredQuery(analysesResults, sourceId);
        }

        var filters = queryRewriteData.filters;

        if (!filters) {
            if (!_.isEmpty(metadata)) {
                layer = Object.assign({}, layer);
                layer.options = Object.assign({}, layer.options, { queryRewriteData });
            }

            return done(null, layer);
        }

        var unfilteredQuery = queryRewriteData.unfiltered_query;

        filterStatsBackend.getFilterStats(user, unfilteredQuery, filters, function (err, stats) {
            if (err) {
                return done(null, layer);
            }

            queryRewriteData.filter_stats = stats;

            if (!_.isEmpty(metadata)) {
                layer = Object.assign({}, layer);
                layer.options = Object.assign({}, layer.options, { queryRewriteData });
            }

            return done(null, layer);
        });
    });
}

function getRootNode (analysesResults, sourceId) {
    var node = _.find(analysesResults, function (a) {
        return a.rootNode.params.id === sourceId;
    });

    return node ? node.rootNode : undefined;
}

function getUnfilteredQuery (analysesResults, sourceId) {
    var node = getRootNode(analysesResults, sourceId);

    if (node) {
        var filters = node.getFilters();
        var filters_disabler = Object.keys(filters).reduce(function (disabler, filter_id) {
            disabler[filter_id] = false;
            return disabler;
        }, {});

        return node.getQuery(filters_disabler);
    }
}

function getFilters (analysesResults, sourceId) {
    var node = getRootNode(analysesResults, sourceId);

    if (node) {
        return node.getFilters();
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2012 Vanderbilt University, All rights reserved.
 *
 * Author: Miklos Maroti
 */

define([ "gmeassert", "loader" ], function (ASSERT, loader) {
	"use strict";

	var nodeCount = 0;

	var Node = function (parent) {
		ASSERT(typeof relid === "string");
		ASSERT(parent === null || parent instanceof Node);

		Object.defineProperties(this, {
			parent: {
				value: parent,
				enumerable: false,	// to allow serialization
				writable: false
			},
			nodeid: {
				value: ++nodeCount,
				enumerable: true,
				writable: false
			}
		});
	};

	Object.defineProperties(Node.prototype, {
		path: {
			get: function () {
				return this.parent === null ? "" : this.parent.getPath() + "."
						+ this.relid;
			}
		},
		root: {
			get: function () {
				var node = this;
				while( node.parent !== null ) {
					node = node.parent;
				}
				return node;
			}
		},
		name: {
			get: function() {
				var data = this.data || {name: "unknown"};
				return data.name;
			}
		}
	});

	var updateNode = function(node, hash) {
		ASSERT(node.data);

		// we bail out early, so we do not traverse the whole tree
		if( node.data.hash === hash ) {
			return;
		}

	};

	// ----------------- Public Interface -----------------

	return {
	};
});
<commit_msg>treeview update<commit_after>/*
 * Copyright (C) 2012 Vanderbilt University, All rights reserved.
 *
 * Author: Miklos Maroti
 */

define([ "gmeassert", "loader" ], function(ASSERT, loader) {
	"use strict";

	var Node = function(parent) {
		ASSERT(parent === null || parent instanceof Node);

		Object.defineProperties(this, {
			parent : {
				value : parent,
				enumerable : false, // to allow serialization
				writable : false
			}
		});
	};

	Object.defineProperties(Node.prototype, {
		expand : {
			value : function() {
				if (this.children)
					return;

				this.children = {};

			}
		}
	});

	var updateNode = function(node, hash) {
		ASSERT(node.data);

		// if nothing changed, then stop traversing
		if (node.data.hash === hash) {
			return;
		}

		loader.load(hash, function(new_data) {
			var relid;
			var new_children = new_data.children || {};

			for (relid in node.children) {
				if (!(relid in new_children)) {
					// emit event
					delete node.children[relid];
				}
			}

			for (relid in new_children) {
				if (relid in node.children) {
					updateNode(node.children[relid], new_children[relid]);
				}
			}
		});
	};

	// ----------------- node -----------------

	var nodeMethods = {

		/**
		 * Returns the relid of this node.
		 *
		 * @return the relid of this node.
		 */
		get relid() {
			ASSERT(this.parent !== undefined);
			if (this.parent === null) {
				return "";
			}

			ASSERT(this.parent.children);
			for ( var relid in this.parent.children) {
				if (this.parent.children[relid] === this)
					return relid;
			}

			ASSERT(false);
		},

		/**
		 * Returns the path of this object by walking to the root of the
		 * tree and querying the relid.
		 *
		 * @return the path of this node
		 */
		get path() {
			return this.parent === null ? "" : this.parent.getPath() + "/"
					+ this.relid;
		},

		get : function() {
			}
		},

		/**
		 * Returns true if this node is expanded, that is if we track its children.
		 *
		 * @return true if this node is expanded
		 */
		get isExpanded() {
			return !!this.children;
		},

		get name() {
			var data = this.data || {};
			var attr = data.attributes || {};
			return attr.name || "unknown";
		},

		/**
		 * Called just after a new child node is added to an expanded parent
		 * because of a database update.
		 *
		 * @param node
		 *            the new child node
		 */
		onAdded : function() {
			console.log("node added " + this.path);
		},

		/**
		 * Called just before a child node and all its children are removed from
		 * an expanded parent because of a database update. This call is not
		 * recursive, so it is not called for the children of the removed node.
		 *
		 * @param node
		 *            the node that is being removed
		 */
		onRemoved : function() {
			console.log("node removed " + this.path);
		},

		/**
		 * This event is fired when the name of a node has changed because of a
		 * database update.
		 *
		 * @param node
		 *            the node whose name has changed
		 */
		onChanged : function() {
			console.log("node changed " + this.path + " with name "
					+ this.name);
		},

		expand : function() {
		}

	};

	// ----------------- tree -----------------

	var treeProto = {

		/**
		 * This method must be called from the GUI when we want to load the
		 * children of the current node. The onNodeAdded event will be called
		 * for each new child once they are loaded from the database.
		 *
		 * @param node
		 *            the node to be expanded
		 */
		expandNode : function(node) {
			ASSERT(node.tree === this);
		}
	};

	var createTree = function() {
		var tree = Object.create(treeProto);

		/**
		 * We create an event handler node object whose instances will be the
		 * contained nodes.
		 */
		tree.nodeEvents = Object.create(nodeMethods);

		tree.root = Object.create(tree.handler);

		return tree;
	};

	// ----------------- Public Interface -----------------

	return {
		create : function() {
			return new Tree();
		}
	};
});
<|endoftext|>


<commit_before>import * as ct from 'countries-and-timezones'
import * as R from 'ramda'
import * as Yup from 'yup'

import Autocomplete from 'src/components/inputs/formik/Autocomplete.js'
import { getTzLabels } from 'src/utils/timezones'

const getFields = (getData, names, onChange, auxElements = []) => {
  return R.filter(
    it => R.includes(it.name, names),
    allFields(getData, onChange, auxElements)
  )
}

const allFields = (getData, onChange, auxElements = []) => {
  const getView = (data, code, compare) => it => {
    if (!data) return ''

    return R.compose(
      R.prop(code),
      R.find(R.propEq(compare ?? 'code', it))
    )(data)
  }

  const displayCodeArray = data => it => {
    if (!it) return it

    return R.compose(R.join(', '), R.map(getView(data, 'code')))(it)
  }

  const overridenMachines = R.map(override => override.machine, auxElements)

  const suggestionFilter = it =>
    R.differenceWith((x, y) => x.deviceId === y, it, overridenMachines)

  const machineData = getData(['machines'])
  const countryData = getData(['countries'])
  const currencyData = getData(['currencies'])
  const languageData = getData(['languages'])
  const cryptoData = getData(['cryptoCurrencies'])
  const timezonesData = R.values(ct.getAllTimezones())

  const tzLabels = getTzLabels(timezonesData)

  const findSuggestion = it => {
    const machine = R.find(R.propEq('deviceId', it.machine))(machineData)
    return machine ? [machine] : []
  }

  return [
    {
      name: 'machine',
      width: 200,
      size: 'sm',
      view: getView(machineData, 'name', 'deviceId'),
      input: Autocomplete,
      inputProps: {
        options: it =>
          R.concat(findSuggestion(it))(suggestionFilter(machineData)),
        valueProp: 'deviceId',
        labelProp: 'name'
      }
    },
    {
      name: 'country',
      width: 200,
      size: 'sm',
      view: getView(countryData, 'display'),
      input: Autocomplete,
      inputProps: {
        options: countryData,
        valueProp: 'code',
        labelProp: 'display'
      }
    },
    {
      name: 'fiatCurrency',
      width: 150,
      size: 'sm',
      view: getView(currencyData, 'code'),
      input: Autocomplete,
      inputProps: {
        options: currencyData,
        valueProp: 'code',
        labelProp: 'code'
      }
    },
    {
      name: 'languages',
      width: 200,
      size: 'sm',
      view: displayCodeArray(languageData),
      input: Autocomplete,
      inputProps: {
        options: languageData,
        valueProp: 'code',
        labelProp: 'display',
        multiple: true
      }
    },
    {
      name: 'cryptoCurrencies',
      width: 170,
      size: 'sm',
      view: displayCodeArray(cryptoData),
      input: Autocomplete,
      inputProps: {
        options: cryptoData,
        valueProp: 'code',
        labelProp: 'code',
        multiple: true,
        optionsLimit: null,
        onChange
      }
    },
    {
      name: 'timezone',
      width: 320,
      size: 'sm',
      view: getView(tzLabels, 'label'),
      input: Autocomplete,
      inputProps: {
        options: tzLabels,
        valueProp: 'code',
        labelProp: 'label'
      }
    }
  ]
}

const mainFields = (auxData, configureCoin) => {
  const getData = R.path(R.__, auxData)

  return getFields(
    getData,
    ['country', 'fiatCurrency', 'languages', 'cryptoCurrencies', 'timezone'],
    configureCoin
  )
}

const overrides = (auxData, auxElements, configureCoin) => {
  const getData = R.path(R.__, auxData)

  return getFields(
    getData,
    ['machine', 'country', 'languages', 'cryptoCurrencies'],
    configureCoin,
    auxElements
  )
}

const LocaleSchema = Yup.object().shape({
  country: Yup.string()
    .label('Country')
    .required(),
  fiatCurrency: Yup.string()
    .label('Fiat Currency')
    .required(),
  languages: Yup.array()
    .label('Languages')
    .required()
    .min(1)
    .max(4),
  cryptoCurrencies: Yup.array()
    .label('Crypto Currencies')
    .required()
    .min(1),
  timezone: Yup.string()
    .label('Timezone')
    .required()
})

const OverridesSchema = Yup.object().shape({
  machine: Yup.string()
    .label('Machine')
    .required(),
  country: Yup.string()
    .label('Country')
    .required(),
  languages: Yup.array()
    .label('Languages')
    .required()
    .min(1),
  cryptoCurrencies: Yup.array()
    .label('Crypto Currencies')
    .required()
    .min(1)
})

const localeDefaults = {
  country: '',
  fiatCurrency: '',
  languages: [],
  cryptoCurrencies: [],
  timezone: ''
}

const overridesDefaults = {
  machine: '',
  country: '',
  languages: [],
  cryptoCurrencies: []
}

export {
  mainFields,
  overrides,
  LocaleSchema,
  OverridesSchema,
  localeDefaults,
  overridesDefaults
}
<commit_msg>feat: add config migration<commit_after>import * as ct from 'countries-and-timezones'
import * as R from 'ramda'
import * as Yup from 'yup'

import Autocomplete from 'src/components/inputs/formik/Autocomplete.js'
import { getTzLabels } from 'src/utils/timezones'

const getFields = (getData, names, onChange, auxElements = []) => {
  return R.filter(
    it => R.includes(it.name, names),
    allFields(getData, onChange, auxElements)
  )
}

const allFields = (getData, onChange, auxElements = []) => {
  const getView = (data, code, compare) => it => {
    if (!data) return ''

    return R.compose(
      R.prop(code),
      R.find(R.propEq(compare ?? 'code', it))
    )(data)
  }

  const displayCodeArray = data => it => {
    if (!it) return it

    return R.compose(R.join(', '), R.map(getView(data, 'code')))(it)
  }

  const overridenMachines = R.map(override => override.machine, auxElements)

  const suggestionFilter = it =>
    R.differenceWith((x, y) => x.deviceId === y, it, overridenMachines)

  const machineData = getData(['machines'])
  const countryData = getData(['countries'])
  const currencyData = getData(['currencies'])
  const languageData = getData(['languages'])
  const cryptoData = getData(['cryptoCurrencies'])
  const timezonesData = R.values(ct.getAllTimezones())

  const tzLabels = getTzLabels(timezonesData)

  console.log(tzLabels)

  const findSuggestion = it => {
    const machine = R.find(R.propEq('deviceId', it.machine))(machineData)
    return machine ? [machine] : []
  }

  return [
    {
      name: 'machine',
      width: 200,
      size: 'sm',
      view: getView(machineData, 'name', 'deviceId'),
      input: Autocomplete,
      inputProps: {
        options: it =>
          R.concat(findSuggestion(it))(suggestionFilter(machineData)),
        valueProp: 'deviceId',
        labelProp: 'name'
      }
    },
    {
      name: 'country',
      width: 200,
      size: 'sm',
      view: getView(countryData, 'display'),
      input: Autocomplete,
      inputProps: {
        options: countryData,
        valueProp: 'code',
        labelProp: 'display'
      }
    },
    {
      name: 'fiatCurrency',
      width: 150,
      size: 'sm',
      view: getView(currencyData, 'code'),
      input: Autocomplete,
      inputProps: {
        options: currencyData,
        valueProp: 'code',
        labelProp: 'code'
      }
    },
    {
      name: 'languages',
      width: 200,
      size: 'sm',
      view: displayCodeArray(languageData),
      input: Autocomplete,
      inputProps: {
        options: languageData,
        valueProp: 'code',
        labelProp: 'display',
        multiple: true
      }
    },
    {
      name: 'cryptoCurrencies',
      width: 170,
      size: 'sm',
      view: displayCodeArray(cryptoData),
      input: Autocomplete,
      inputProps: {
        options: cryptoData,
        valueProp: 'code',
        labelProp: 'code',
        multiple: true,
        optionsLimit: null,
        onChange
      }
    },
    {
      name: 'timezone',
      width: 320,
      size: 'sm',
      view: getView(tzLabels, 'label'),
      input: Autocomplete,
      inputProps: {
        options: tzLabels,
        valueProp: 'code',
        labelProp: 'label'
      }
    }
  ]
}

const mainFields = (auxData, configureCoin) => {
  const getData = R.path(R.__, auxData)

  return getFields(
    getData,
    ['country', 'fiatCurrency', 'languages', 'cryptoCurrencies', 'timezone'],
    configureCoin
  )
}

const overrides = (auxData, auxElements, configureCoin) => {
  const getData = R.path(R.__, auxData)

  return getFields(
    getData,
    ['machine', 'country', 'languages', 'cryptoCurrencies'],
    configureCoin,
    auxElements
  )
}

const LocaleSchema = Yup.object().shape({
  country: Yup.string()
    .label('Country')
    .required(),
  fiatCurrency: Yup.string()
    .label('Fiat Currency')
    .required(),
  languages: Yup.array()
    .label('Languages')
    .required()
    .min(1)
    .max(4),
  cryptoCurrencies: Yup.array()
    .label('Crypto Currencies')
    .required()
    .min(1),
  timezone: Yup.string()
    .label('Timezone')
    .required()
})

const OverridesSchema = Yup.object().shape({
  machine: Yup.string()
    .label('Machine')
    .required(),
  country: Yup.string()
    .label('Country')
    .required(),
  languages: Yup.array()
    .label('Languages')
    .required()
    .min(1),
  cryptoCurrencies: Yup.array()
    .label('Crypto Currencies')
    .required()
    .min(1)
})

const localeDefaults = {
  country: '',
  fiatCurrency: '',
  languages: [],
  cryptoCurrencies: [],
  timezone: ''
}

const overridesDefaults = {
  machine: '',
  country: '',
  languages: [],
  cryptoCurrencies: []
}

export {
  mainFields,
  overrides,
  LocaleSchema,
  OverridesSchema,
  localeDefaults,
  overridesDefaults
}
<|endoftext|>


<commit_before>/**
 * Returns an Express Router with bindings for the Admin UI static resources,
 * i.e files, less and browserified scripts.
 *
 * Should be included before other middleware (e.g. session management,
 * logging, etc) for reduced overhead.
 */

var browserify = require('./browserify');
var express = require('express');
var glob = require('glob');
var less = require('less-middleware');
var path = require('path');
var minify = require('express-minify');
var router = express.Router();

/* Browersify all Tiny-MCE React-based plugins */

var mapPlugin = function(file, pluckNameReg) {
	var nameMatch = pluckNameReg.exec(file);
	if (!nameMatch) throw new Error('Unexpected name format for TinyMCE plugin ' + file);
	return {
		file: path.relative(__dirname, file).replace(/\\/g, '/'),
		fileRoot: '/' + path.relative(__dirname + '/../', file).replace(/\\/g, '/'),
		path: nameMatch[1],
		pathRoot: '/' + path.relative(__dirname + '/../', nameMatch[1]).replace(/\\/g, '/'),
		pathRootNoPublic: path.normalize('/' + path.relative(__dirname + '/../', nameMatch[1]).replace('public' + path.sep, '')).replace(/\\/g, '/'),
		name: nameMatch[2],
		filename: nameMatch[3]
	};
};

var basePluginFolder = '/../public/js/lib/tinymce/plugins/';
var pluckReactPluginName = /(.*tinymce\/plugins\/([^\/]+)\/)(renderPlugin)\.js$/i;
var tinyReactPlugins = glob.sync(__dirname + basePluginFolder + '**/renderPlugin.js')
	.map(function(file) { return mapPlugin(file, pluckReactPluginName); });

/* Browserify all Tiny-MCE plugins - plugin js vs. any React-based renderPlugin js */
var pluckPluginName = /(.*tinymce\/plugins\/([^\/]+)\/)([^\.]+)\.js$/i;
var tinyPlugins = glob.sync(__dirname + basePluginFolder + '**/plugin.js')
	.map(function(file) { return mapPlugin(file, pluckPluginName); });

// var allTinyPlugins = tinyPlugins.slice();
// Array.prototype.push.apply(allTinyPlugins, tinyReactPlugins);

/* Prepare browserify bundles */

var bundles = {
	fields: browserify('fields.js', 'FieldTypes'),
	home: browserify('views/home.js'),
	item: browserify('views/item.js'),
	list: browserify('views/list.js')
};
tinyReactPlugins.forEach(function(p) { bundles[p.name] = browserify(p.file); });

router.prebuild = function() {
	bundles.fields.build();
	bundles.home.build();
	bundles.item.build();
	bundles.list.build();
};
tinyReactPlugins.forEach(function(p) { router.prebuild[p.name] = bundles[p.name].build(); });

/* Prepare LESS options */

var reactSelectPath = path.join(path.dirname(require.resolve('react-select')), '..');

var lessOptions = {
	render: {
		modifyVars: {
			reactSelectPath: JSON.stringify(reactSelectPath)
		}
	}
};

/* Configure router */
// router.use('/styles', less(__dirname + '../../public/styles', lessOptions));
router.use('/styles', less(path.resolve(__dirname + '/../public/styles'), lessOptions));
// router.use(express.static(__dirname + '../../public'));
router.use(express.static(path.resolve(__dirname + '/../public')));
/* TinyMCE plugin Skins */
router.use('/js/lib/tinymce/skins', less(__dirname + '/../public/js/lib/tinymce/skins', lessOptions));
router.use('/js/lib/tinymce/skins/', express.static(__dirname + '/../public/js/lib/tinymce/skins'));
router.use('/styles/elemental', less(__dirname + '/../../node_modules/elemental/less', lessOptions));
router.use('/styles/elemental/', express.static(__dirname + '/../../node_modules/elemental/less'));
router.use('/public/images/', express.static(__dirname + '/../../node_modules/elemental/public/images'));
router.get('/js/fields.js', bundles.fields.serve);
router.get('/js/home.js', bundles.home.serve);
router.get('/js/item.js', bundles.item.serve);
router.get('/js/list.js', bundles.list.serve);
tinyPlugins.forEach(function(p) {
	if (p.name !== 'cloudinarybrowser') {
		router.get(p.pathRootNoPublic + '/', minify());
		router.use(p.pathRootNoPublic + '/' + p.filename + '.min.js', express.static(path.resolve(__dirname + '/' + p.file)));
	} else {
		//cloudinarybrowser functions as two plugins
		router.get(p.pathRootNoPublic + 'images/', minify());
		router.use(p.pathRootNoPublic + 'images/' + p.filename + '.min.js', express.static(path.resolve(__dirname + '/' + p.file)));
		router.get(p.pathRootNoPublic + 'files/', minify());
		router.use(p.pathRootNoPublic + 'files/' + p.filename + '.min.js', express.static(path.resolve(__dirname + '/' + p.file)));
	}
});
tinyReactPlugins.forEach(function(p) {
	router.get('/js/tiny-mce-plugins/' + p.name + '.js', bundles[p.name].serve);
	router.use('/styles/tiny-mce-plugins/' + p.name, less(p.path, lessOptions));
});
router.use('/styles/tiny-mce-plugins/', express.static(__dirname + basePluginFolder));

module.exports = router;
<commit_msg>Update elemental less for node 8<commit_after>/**
 * Returns an Express Router with bindings for the Admin UI static resources,
 * i.e files, less and browserified scripts.
 *
 * Should be included before other middleware (e.g. session management,
 * logging, etc) for reduced overhead.
 */

var browserify = require('./browserify');
var express = require('express');
var glob = require('glob');
var less = require('less-middleware');
var path = require('path');
var minify = require('express-minify');
var router = express.Router();

/* Browersify all Tiny-MCE React-based plugins */

var mapPlugin = function(file, pluckNameReg) {
	var nameMatch = pluckNameReg.exec(file);
	if (!nameMatch) throw new Error('Unexpected name format for TinyMCE plugin ' + file);
	return {
		file: path.relative(__dirname, file).replace(/\\/g, '/'),
		fileRoot: '/' + path.relative(__dirname + '/../', file).replace(/\\/g, '/'),
		path: nameMatch[1],
		pathRoot: '/' + path.relative(__dirname + '/../', nameMatch[1]).replace(/\\/g, '/'),
		pathRootNoPublic: path.normalize('/' + path.relative(__dirname + '/../', nameMatch[1]).replace('public' + path.sep, '')).replace(/\\/g, '/'),
		name: nameMatch[2],
		filename: nameMatch[3]
	};
};

var basePluginFolder = '/../public/js/lib/tinymce/plugins/';
var pluckReactPluginName = /(.*tinymce\/plugins\/([^\/]+)\/)(renderPlugin)\.js$/i;
var tinyReactPlugins = glob.sync(__dirname + basePluginFolder + '**/renderPlugin.js')
	.map(function(file) { return mapPlugin(file, pluckReactPluginName); });

/* Browserify all Tiny-MCE plugins - plugin js vs. any React-based renderPlugin js */
var pluckPluginName = /(.*tinymce\/plugins\/([^\/]+)\/)([^\.]+)\.js$/i;
var tinyPlugins = glob.sync(__dirname + basePluginFolder + '**/plugin.js')
	.map(function(file) { return mapPlugin(file, pluckPluginName); });

// var allTinyPlugins = tinyPlugins.slice();
// Array.prototype.push.apply(allTinyPlugins, tinyReactPlugins);

/* Prepare browserify bundles */

var bundles = {
	fields: browserify('fields.js', 'FieldTypes'),
	home: browserify('views/home.js'),
	item: browserify('views/item.js'),
	list: browserify('views/list.js')
};
tinyReactPlugins.forEach(function(p) { bundles[p.name] = browserify(p.file); });

router.prebuild = function() {
	bundles.fields.build();
	bundles.home.build();
	bundles.item.build();
	bundles.list.build();
};
tinyReactPlugins.forEach(function(p) { router.prebuild[p.name] = bundles[p.name].build(); });

/* Prepare LESS options */

var reactSelectPath = path.join(path.dirname(require.resolve('react-select')), '..');

var lessOptions = {
	render: {
		modifyVars: {
			reactSelectPath: JSON.stringify(reactSelectPath)
		}
	}
};

/* Configure router */
// router.use('/styles', less(__dirname + '../../public/styles', lessOptions));
router.use('/styles', less(path.resolve(__dirname + '/../public/styles'), lessOptions));
// router.use(express.static(__dirname + '../../public'));
router.use(express.static(path.resolve(__dirname + '/../public')));
/* TinyMCE plugin Skins */
router.use('/js/lib/tinymce/skins', less(__dirname + '/../public/js/lib/tinymce/skins', lessOptions));
router.use('/js/lib/tinymce/skins/', express.static(__dirname + '/../public/js/lib/tinymce/skins'));
const elementalLessPath = path.join(__dirname, '../../../', 'elemental/less')
router.use('/styles/elemental', less(elementalLessPath, lessOptions));
router.use('/styles/elemental/', express.static(elementalLessPath));
router.use('/public/images/', express.static(__dirname + '/../../node_modules/elemental/public/images'));
router.get('/js/fields.js', bundles.fields.serve);
router.get('/js/home.js', bundles.home.serve);
router.get('/js/item.js', bundles.item.serve);
router.get('/js/list.js', bundles.list.serve);
tinyPlugins.forEach(function(p) {
	if (p.name !== 'cloudinarybrowser') {
		router.get(p.pathRootNoPublic + '/', minify());
		router.use(p.pathRootNoPublic + '/' + p.filename + '.min.js', express.static(path.resolve(__dirname + '/' + p.file)));
	} else {
		//cloudinarybrowser functions as two plugins
		router.get(p.pathRootNoPublic + 'images/', minify());
		router.use(p.pathRootNoPublic + 'images/' + p.filename + '.min.js', express.static(path.resolve(__dirname + '/' + p.file)));
		router.get(p.pathRootNoPublic + 'files/', minify());
		router.use(p.pathRootNoPublic + 'files/' + p.filename + '.min.js', express.static(path.resolve(__dirname + '/' + p.file)));
	}
});
tinyReactPlugins.forEach(function(p) {
	router.get('/js/tiny-mce-plugins/' + p.name + '.js', bundles[p.name].serve);
	router.use('/styles/tiny-mce-plugins/' + p.name, less(p.path, lessOptions));
});
router.use('/styles/tiny-mce-plugins/', express.static(__dirname + basePluginFolder));

module.exports = router;
<|endoftext|>