<commit_before>/**
 * TabAccordion – An accordion, that may change its display to tabbed content based on the screen width.
 *
 * @author    Thomas Heuer <thomas.heuer@votum.de>
 * @copyright Copyright (c) 2014 Votum GmbH
 *
 * Some nice extra ideas:
 * @todo Measure & set tab content heights individually and remove parent min-height to avoid gaps below tabbed-content
 * @todo Open a section, that was selected through an URL anchor
 * @todo Make all class names configurable from outside of the module scope
 */
define( ['media-query-sync', 'functions'], function( MediaQuerySync, Functions ) {

    /**
     * Display constants; do not edit these – they are only used inside this module and are hardcoded below.
     * @type {Object}
     */
    var display = {
        none: 0,
        accordion: 1,
        tabs: 2
    };

    /**
     * Configuration; may get overwritten through init-method.
     *
     * @type {Object}
     */
    var defaultConfig = {

        /* The selector entries must contain valid CSS selectors to be used in querySelector() */
        selector: {

            /* A CSS selector to identify an area that should be used as accordion/tabbed content. */
            wrapper: '[data-tabaccordion]',

            /* This is a single part/section of the accordion/tabbed content.
             * It must be within `selector.wrapper` */
            section: 'section',

            /* This is the section header – the part which to click on to open a section.
             * It must be within `selector.section` */
            sectionHeading: ':scope > header',

            /* This is a selector to find section contents; you may also set a specific body wrapper here.
             * It must be within `selector.section` */
            sectionBody: ':scope > *:not(header)'
        },

        /* This class name will be added to a new element that is inserted around the section body elements */
        sectionBodyWrapperClass: 'tabaccordion-body-wrapper',

        /* A class name to designate opened sections */
        showSectionClass: 'open',

        /* A class name that is set on the `selector.wrapper` depending on the state of the tabaccordion.
         * These class names should be used to style a specific state/layout.
         * They must be defined here without the leading dot. */
        wrapperTypeClass: {
            accordion: 'accordion',
            tabs: 'tabbed-content'
        },

        /* Specify a class name prefix for a class that indicates the count of child elements in this tabaccordion;
         * Set to an falsy value, if the class should get set */
        wrapperSectionCountClassPrefix: 'tabaccordion-section-count-',

        /**
         * The tabaccordion-types, that define how to display a group of sections on a
         * specified media query breakpoint.
         *
         * New types may be created here according to the pattern below, where:
         * - "type" is a chosen identifier to be set in the data-attribute
         * - "breakpoint" is one of the breakpoints defined in the MediaQuerySync module
         * - "display" is one of the `display` display constants
         *
         * Make sure the breakpoints for each type do not overlap. (e.g. 'md' must only occur once per type)
         *
         * @type {Object} { type: { breakpoint: display } }
         */
        types: {
            'accordion': {
                sm: display.accordion,
                md: display.accordion,
                lg: display.accordion,
                xl: display.accordion,
                xxl: display.accordion
            },
            'accordion-tabs': {
                sm: display.accordion,
                md: display.accordion,
                lg: display.tabs,
                xl: display.tabs,
                xxl: display.tabs
            },
            'accordion-mobile-only': {
                sm: display.accordion,
                md: display.accordion,
                lg: display.none,
                xl: display.none,
                xxl: display.none
            },
            'tabs-only': {
                sm: display.tabs,
                md: display.tabs,
                lg: display.tabs,
                xl: display.tabs,
                xxl: display.tabs
            }
        },

        /**
         * Set the default type.
         * Attention: There is no check if the given key really exists.
         *
         * @type {string} One of the keys of the `types` array.
         */
        defaultType: 'accordion-tabs',

        /* Auto play configuration */
        autoPlay: {
            enabled: false,

            /* How long an item should be visible */
            delay: 4000,

            /* Start over, when the last item was reached? */
            loop: true,

            /* Stop, if the user moves the mouse over the element */
            stopOnHover: true,

            /* Set, which element must be hovered to stop the autoplay;
             * By default this is config.selector.wrapper */
            stopOnHoverSelector: null
        },

        /* callback function on accordion ready */
        onAccordionReady: null

    };

    /**
     * The Accordion object
     *
     * @param {Element} wrapper The wrapper element around the tab-accordion (which already must exist).
     * @param {NodeList} sections The single sections/item of an accordion.
     * @param {String} type One of the keys of the `types` array.
     * @param {Object} userConfig An object with all configuration parameters.
     * @constructor
     */
    function Accordion( wrapper, sections, type, userConfig ) {

        if( !(wrapper instanceof Element) ) {
            throw new Error( 'Accordion: The parameter `wrapper` must be of type Element but was of type `' + (typeof wrapper) + '`.' );
        }
        if( !(sections instanceof NodeList) ) {
            throw new Error( 'Accordion: The parameter `sections` must be of type NodeList but was of type `' + (typeof sections) + '`.' );
        }

        this.config = userConfig;
        this.wrapper = wrapper;
        this.sections = sections;
        /* Get the selected type or the default fallback. */
        this.type = this.config.types[type] ? this.config.types[type] : this.config.types[this.config.defaultType];
        this.length = sections.length;
        this.activeSection = 0;
        this.currentDisplay = display.none;

        /* Get a named reference to this object for usage in event handlers */
        var currentAccordion = this;

        /* Initial setup… */
        var heading, body, thisBodyWrapper;

        /* Add number of sections to the wrapper */
        if( this.config.wrapperSectionCountClassPrefix ) {
            this.wrapper.classList.add( this.config.wrapperSectionCountClassPrefix + this.length );
        }

        /* Only create the wrapper element once and copy it as necessary (see below) */
        var bodyWrapper = document.createElement( 'div' );
        bodyWrapper.className = this.config.sectionBodyWrapperClass;

        /* Iterate over sections */
        for( var i = this.length; i--; ) {

            /* Cache necessary elements */
            heading = currentAccordion.sections[i].querySelector( this.config.selector.sectionHeading );
            body = currentAccordion.sections[i].querySelectorAll( this.config.selector.sectionBody );

            /* Make a copy of the body-wrapper element and wrap the body in it */
            thisBodyWrapper = bodyWrapper.cloneNode( false );

            for( var k = 0, len = body.length; k < len; k++ ) {
                thisBodyWrapper.appendChild( body[k] );
            }

            currentAccordion.sections[i].appendChild( thisBodyWrapper );
            currentAccordion.sections[i].insertBefore( thisBodyWrapper, currentAccordion.sections[i].firstChild );

            /* Setup event handler;
             * The inner function scope is necessary because of the counter, which would be 0 for all events otherwise */
            heading.addEventListener( 'click', (function() {
                var currentSection = currentAccordion.sections[i];
                return function( event ) {
                    if( currentAccordion.currentDisplay === display.accordion && currentSection.classList.contains( currentAccordion.config.showSectionClass ) ) {
                        /* Close this section if was open already */
                        currentAccordion.close( currentSection );
                    }
                    else {
                        /* Or open it, if it was not */
                        currentAccordion.open( currentSection );
                    }
                };
            })() );

        }

        /* Set computed heights as inline styles */
        /** @todo find out why this was necessary */
            //this.setComputedHeights();
        window.addEventListener( 'resize', Functions.debounce( function( event ) {
            currentAccordion.setComputedHeights();
        }, 100 ), false );

        /* Initially set type according to current device state (media query breakpoint) */
        this.updateType( MediaQuerySync.getDeviceState() );

        /* Change the type as necessary when another breakpoint was reached */
        window.addEventListener( 'deviceStateChanged', function( event ) {
            currentAccordion.updateType( event.deviceState );
        }, false );
    }

    /**
     * Iterate over each section to read its real height (the one without a height/max-height value).
     * This computed height is then set as `max-height`.
     */
    Accordion.prototype.setComputedHeights = function() {
        var bodyWrapper, maxHeight = 0, currentMaxHeight, heading;

        /* Remove any styles */
        var classNameTmp = this.wrapper.className;
        //this.wrapper.className = '';
        this.wrapper.classList.remove( this.config.wrapperTypeClass.accordion );
        this.wrapper.classList.remove( this.config.wrapperTypeClass.tabs );

        for( var i = this.length; i--; ) {
            bodyWrapper = this.sections[i].querySelector( '.' + this.config.sectionBodyWrapperClass );

            /* Fall back to no max-height */
            bodyWrapper.style.maxHeight = 'none';

            currentMaxHeight = bodyWrapper.offsetHeight;

            /* Store the biggest height of all sections */
            if( currentMaxHeight > maxHeight ) {
                maxHeight = currentMaxHeight;
            }

            /* Set new max-height */
            bodyWrapper.style.maxHeight = currentMaxHeight + 'px';
            //bodyWrapper.style.maxHeight = (currentMaxHeight + 30) + 'px';
        }

        /* Get example heading height and add it to the overall height */
        heading = this.sections[0].querySelector( this.config.selector.sectionHeading );
        this.wrapper.style.minHeight = maxHeight + 'px';

        /* set original class name */
        this.wrapper.className = classNameTmp;
    };

    /**
     * @param {String} deviceState One of [xs, sm, md, lg]
     */
    Accordion.prototype.updateType = function( deviceState ) {
        if( !this.type[deviceState] ) {
            return;
        }

        this.currentDisplay = this.type[deviceState];

        /* switch to type */
        switch( this.type[deviceState] ) {
            case display.none:
                this.removeTabAccordion();
                break;
            case display.tabs:
                this.makeTabs();
                break;
            case display.accordion:
            default:
                this.makeAccordion();
        }
    };

    /**
     * Open a specific section
     *
     * @param {Element|Number} section
     */
    Accordion.prototype.open = function( section ) {

        /* get a single section from numeric index */
        if( !(section instanceof Element) ) {
            section = this.sections.item( parseInt( section, 10 ) );
        }

        /* close the last section */
        this.close( this.activeSection );

        /* open section */
        section.classList.add( this.config.showSectionClass );

        /* set this section as current */
        this.activeSection = section;
    };

    /**
     * Close all sections or a specific one
     *
     * @param {Element|Number|undefined} section
     */
    Accordion.prototype.close = function( section ) {

        /* close all sections without parameter */
        if( typeof section === 'undefined' ) {
            for( var i = this.length; i--; ) {
                this.close( this.sections[i] );
            }
            return;
        }

        /* get a single section from numeric index */
        if( !(section instanceof Element) ) {
            section = this.sections.item( parseInt( section, 10 ) );
        }

        /* close section */
        section.classList.remove( this.config.showSectionClass );
    };

    /**
     * Remove any classes, switch back to normal panels
     */
    Accordion.prototype.removeTabAccordion = function() {
        this.wrapper.classList.remove( this.config.wrapperTypeClass.tabs );
        this.wrapper.classList.remove( this.config.wrapperTypeClass.accordion );
    };

    /**
     * Turn the current section group into an accordion
     */
    Accordion.prototype.makeAccordion = function() {
        this.wrapper.classList.remove( this.config.wrapperTypeClass.tabs );
        this.wrapper.classList.add( this.config.wrapperTypeClass.accordion );
    };

    /**
     * Turn the current section group into tabbed content
     */
    Accordion.prototype.makeTabs = function() {
        this.wrapper.classList.add( this.config.wrapperTypeClass.tabs );
        this.wrapper.classList.remove( this.config.wrapperTypeClass.accordion );
        this.open( 0 );

        /* run callback onAccordionReady function here */
        if( this.config.onAccordionReady != null ) {
            var _this = this;
            setTimeout( function() {
                _this.config.onAccordionReady();
            }, 100 );
        }
    };

    var module = {
        /**
         * Expose the Accordion object to the parent scope.
         * This can be used to manually create a new accordion instance.
         */
        Accordion: Accordion,

        /**
         * Helper function to set up the dynamic accordions.
         *
         * @param {Object} classNames Custom overwrites for the class names as in the `classes` object.
         * @returns {Array} A list of Accordion objects
         */
        init: function( userConfig ) {

            /* Use user configuration if provided */
            var config = Functions.extend( defaultConfig, userConfig );

            var accordions = [],
                accordionGroups = document.querySelectorAll( config.selector.wrapper ),
                currentAccordion;

            for( var i = accordionGroups.length; i--; ) {
                try {
                    currentAccordion = new Accordion(
                        accordionGroups[i],
                        accordionGroups[i].querySelectorAll( config.selector.section ),
                        accordionGroups[i].dataset.panelGroupType,
                        config
                    );

                    /* Check if explicitly is defined to leave all elements closed by data attribute `data-panel-group-all-closed` */
                    if( typeof accordionGroups[i].dataset.panelGroupAllClosed === 'undefined' ) {

                        /* If not explicitly defined, open one accordion element */
                        var groupOpen = 0;

                        /* Open a specified accordion element if specified by data attribute, e.g. 'data-panel-group-open="2"', else open first element */
                        if( accordionGroups[i].dataset.panelGroupOpen ) {
                            groupOpen = parseInt( accordionGroups[i].dataset.panelGroupOpen, 10 );

                            if( groupOpen >= 1 ) {
                                groupOpen--;
                            }
                            else {
                                groupOpen = 0;
                            }
                        }

                        currentAccordion.open( groupOpen );
                    }

                    /* Autoplay for this tabaccordion */
                    if( config.autoPlay.enabled ) {
                        module.autoPlay( currentAccordion );
                    }

                    /* Store a reference to return */
                    accordions.push( currentAccordion );
                }
                catch( error ) {
                    console.error( error.stack );
                }
            }
            /* Return the list of accordion elements for further manipulation */
            return accordions;
        },

        /**
         * @param {Accordion} accordion
         */
        autoPlay: function( accordion ) {

            var stopOnHoverElements,
                pauseAutoPlay = false,
                isManuallyPaused = false;

            if( accordion.config.autoPlay.stopOnHover ) {
                if( !!accordion.config.autoPlay.stopOnHoverSelector ) {
                    stopOnHoverElements = accordion.wrapper.querySelectorAll( accordion.config.autoPlay.stopOnHoverSelector );
                }
                else {
                    stopOnHoverElements = accordion.wrapper;
                }

                if( stopOnHoverElements instanceof NodeList ) {
                    /* Pause on mouse over */
                    Functions.on( document.body, 'mouseover', stopOnHoverElements, function() {
                        if( !isManuallyPaused ) {
                            pauseAutoPlay = true;
                        }
                    } );

                    /* Autoplay on mouse out */
                    Functions.on( document.body, 'mouseout', stopOnHoverElements, function() {
                        if( !isManuallyPaused ) {
                            pauseAutoPlay = false;
                        }
                    } );
                }
            }

            document.addEventListener( 'tabaccordion-pause', function() {
                isManuallyPaused = true;
                pauseAutoPlay = true;
            }, false );

            document.addEventListener( 'tabaccordion-resume', function() {
                isManuallyPaused = false;
                pauseAutoPlay = false;
            }, false );

            setInterval( function() {
                if( !pauseAutoPlay ) {
                    var currentItem = Array.prototype.indexOf.call( accordion.sections, accordion.activeSection );
                    if( currentItem === accordion.length - 1 ) {
                        accordion.open( 0 );
                    }
                    else {
                        accordion.open( currentItem + 1 );
                    }
                }
            }, accordion.config.autoPlay.delay );

        }

    };

    return module;

} );
<commit_msg>setComputedHeights()<commit_after>/**
 * TabAccordion – An accordion, that may change its display to tabbed content based on the screen width.
 *
 * @author    Thomas Heuer <thomas.heuer@votum.de>
 * @copyright Copyright (c) 2014 Votum GmbH
 *
 * Some nice extra ideas:
 * @todo Measure & set tab content heights individually and remove parent min-height to avoid gaps below tabbed-content
 * @todo Open a section, that was selected through an URL anchor
 * @todo Make all class names configurable from outside of the module scope
 */
define( ['media-query-sync', 'functions'], function( MediaQuerySync, Functions ) {

    /**
     * Display constants; do not edit these – they are only used inside this module and are hardcoded below.
     * @type {Object}
     */
    var display = {
        none: 0,
        accordion: 1,
        tabs: 2
    };

    /**
     * Configuration; may get overwritten through init-method.
     *
     * @type {Object}
     */
    var defaultConfig = {

        /* The selector entries must contain valid CSS selectors to be used in querySelector() */
        selector: {

            /* A CSS selector to identify an area that should be used as accordion/tabbed content. */
            wrapper: '[data-tabaccordion]',

            /* This is a single part/section of the accordion/tabbed content.
             * It must be within `selector.wrapper` */
            section: 'section',

            /* This is the section header – the part which to click on to open a section.
             * It must be within `selector.section` */
            sectionHeading: ':scope > header',

            /* This is a selector to find section contents; you may also set a specific body wrapper here.
             * It must be within `selector.section` */
            sectionBody: ':scope > *:not(header)'
        },

        /* This class name will be added to a new element that is inserted around the section body elements */
        sectionBodyWrapperClass: 'tabaccordion-body-wrapper',

        /* A class name to designate opened sections */
        showSectionClass: 'open',

        /* A class name that is set on the `selector.wrapper` depending on the state of the tabaccordion.
         * These class names should be used to style a specific state/layout.
         * They must be defined here without the leading dot. */
        wrapperTypeClass: {
            accordion: 'accordion',
            tabs: 'tabbed-content'
        },

        /* Specify a class name prefix for a class that indicates the count of child elements in this tabaccordion;
         * Set to an falsy value, if the class should get set */
        wrapperSectionCountClassPrefix: 'tabaccordion-section-count-',

        /**
         * The tabaccordion-types, that define how to display a group of sections on a
         * specified media query breakpoint.
         *
         * New types may be created here according to the pattern below, where:
         * - "type" is a chosen identifier to be set in the data-attribute
         * - "breakpoint" is one of the breakpoints defined in the MediaQuerySync module
         * - "display" is one of the `display` display constants
         *
         * Make sure the breakpoints for each type do not overlap. (e.g. 'md' must only occur once per type)
         *
         * @type {Object} { type: { breakpoint: display } }
         */
        types: {
            'accordion': {
                sm: display.accordion,
                md: display.accordion,
                lg: display.accordion,
                xl: display.accordion,
                xxl: display.accordion
            },
            'accordion-tabs': {
                sm: display.accordion,
                md: display.accordion,
                lg: display.tabs,
                xl: display.tabs,
                xxl: display.tabs
            },
            'accordion-mobile-only': {
                sm: display.accordion,
                md: display.accordion,
                lg: display.none,
                xl: display.none,
                xxl: display.none
            },
            'tabs-only': {
                sm: display.tabs,
                md: display.tabs,
                lg: display.tabs,
                xl: display.tabs,
                xxl: display.tabs
            }
        },

        /**
         * Set the default type.
         * Attention: There is no check if the given key really exists.
         *
         * @type {string} One of the keys of the `types` array.
         */
        defaultType: 'accordion-tabs',

        /* Auto play configuration */
        autoPlay: {
            enabled: false,

            /* How long an item should be visible */
            delay: 4000,

            /* Start over, when the last item was reached? */
            loop: true,

            /* Stop, if the user moves the mouse over the element */
            stopOnHover: true,

            /* Set, which element must be hovered to stop the autoplay;
             * By default this is config.selector.wrapper */
            stopOnHoverSelector: null
        },

        /* callback function on accordion ready */
        onAccordionReady: null

    };

    /**
     * The Accordion object
     *
     * @param {Element} wrapper The wrapper element around the tab-accordion (which already must exist).
     * @param {NodeList} sections The single sections/item of an accordion.
     * @param {String} type One of the keys of the `types` array.
     * @param {Object} userConfig An object with all configuration parameters.
     * @constructor
     */
    function Accordion( wrapper, sections, type, userConfig ) {

        if( !(wrapper instanceof Element) ) {
            throw new Error( 'Accordion: The parameter `wrapper` must be of type Element but was of type `' + (typeof wrapper) + '`.' );
        }
        if( !(sections instanceof NodeList) ) {
            throw new Error( 'Accordion: The parameter `sections` must be of type NodeList but was of type `' + (typeof sections) + '`.' );
        }

        this.config = userConfig;
        this.wrapper = wrapper;
        this.sections = sections;
        /* Get the selected type or the default fallback. */
        this.type = this.config.types[type] ? this.config.types[type] : this.config.types[this.config.defaultType];
        this.length = sections.length;
        this.activeSection = 0;
        this.currentDisplay = display.none;

        /* Get a named reference to this object for usage in event handlers */
        var currentAccordion = this;

        /* Initial setup… */
        var heading, body, thisBodyWrapper;

        /* Add number of sections to the wrapper */
        if( this.config.wrapperSectionCountClassPrefix ) {
            this.wrapper.classList.add( this.config.wrapperSectionCountClassPrefix + this.length );
        }

        /* Only create the wrapper element once and copy it as necessary (see below) */
        var bodyWrapper = document.createElement( 'div' );
        bodyWrapper.className = this.config.sectionBodyWrapperClass;

        /* Iterate over sections */
        for( var i = this.length; i--; ) {

            /* Cache necessary elements */
            heading = currentAccordion.sections[i].querySelector( this.config.selector.sectionHeading );
            body = currentAccordion.sections[i].querySelectorAll( this.config.selector.sectionBody );

            /* Make a copy of the body-wrapper element and wrap the body in it */
            thisBodyWrapper = bodyWrapper.cloneNode( false );

            for( var k = 0, len = body.length; k < len; k++ ) {
                thisBodyWrapper.appendChild( body[k] );
            }

            currentAccordion.sections[i].appendChild( thisBodyWrapper );
            currentAccordion.sections[i].insertBefore( thisBodyWrapper, currentAccordion.sections[i].firstChild );

            /* Setup event handler;
             * The inner function scope is necessary because of the counter, which would be 0 for all events otherwise */
            heading.addEventListener( 'click', (function() {
                var currentSection = currentAccordion.sections[i];
                return function( event ) {
                    if( currentAccordion.currentDisplay === display.accordion && currentSection.classList.contains( currentAccordion.config.showSectionClass ) ) {
                        /* Close this section if was open already */
                        currentAccordion.close( currentSection );
                    }
                    else {
                        /* Or open it, if it was not */
                        currentAccordion.open( currentSection );
                    }
                };
            })() );

        }

        /* Set computed heights as inline styles */
        this.setComputedHeights();

        window.addEventListener( 'resize', Functions.debounce( function( event ) {
            currentAccordion.setComputedHeights();
        }, 100 ), false );

        /* Initially set type according to current device state (media query breakpoint) */
        this.updateType( MediaQuerySync.getDeviceState() );

        /* Change the type as necessary when another breakpoint was reached */
        window.addEventListener( 'deviceStateChanged', function( event ) {
            currentAccordion.updateType( event.deviceState );
        }, false );
    }

    /**
     * Iterate over each section to read its real height (the one without a height/max-height value).
     * This computed height is then set as `max-height`.
     */
    Accordion.prototype.setComputedHeights = function() {
        var bodyWrapper, maxHeight = 0, currentMaxHeight, heading;

        /* Remove any styles */
        var classNameTmp = this.wrapper.className;
        //this.wrapper.className = '';
        this.wrapper.classList.remove( this.config.wrapperTypeClass.accordion );
        this.wrapper.classList.remove( this.config.wrapperTypeClass.tabs );

        for( var i = this.length; i--; ) {
            bodyWrapper = this.sections[i].querySelector( '.' + this.config.sectionBodyWrapperClass );

            /* Fall back to no max-height */
            bodyWrapper.style.maxHeight = 'none';

            currentMaxHeight = bodyWrapper.offsetHeight;

            /* Store the biggest height of all sections */
            if( currentMaxHeight > maxHeight ) {
                maxHeight = currentMaxHeight;
            }

            /* Set new max-height */
            bodyWrapper.style.maxHeight = currentMaxHeight + 'px';
            //bodyWrapper.style.maxHeight = (currentMaxHeight + 30) + 'px';
        }

        /* Get example heading height and add it to the overall height */
        heading = this.sections[0].querySelector( this.config.selector.sectionHeading );
        this.wrapper.style.minHeight = maxHeight + 'px';

        /* set original class name */
        this.wrapper.className = classNameTmp;
    };

    /**
     * @param {String} deviceState One of [xs, sm, md, lg]
     */
    Accordion.prototype.updateType = function( deviceState ) {
        if( !this.type[deviceState] ) {
            return;
        }

        this.currentDisplay = this.type[deviceState];

        /* switch to type */
        switch( this.type[deviceState] ) {
            case display.none:
                this.removeTabAccordion();
                break;
            case display.tabs:
                this.makeTabs();
                break;
            case display.accordion:
            default:
                this.makeAccordion();
        }
    };

    /**
     * Open a specific section
     *
     * @param {Element|Number} section
     */
    Accordion.prototype.open = function( section ) {

        /* get a single section from numeric index */
        if( !(section instanceof Element) ) {
            section = this.sections.item( parseInt( section, 10 ) );
        }

        /* close the last section */
        this.close( this.activeSection );

        /* open section */
        section.classList.add( this.config.showSectionClass );

        /* set this section as current */
        this.activeSection = section;
    };

    /**
     * Close all sections or a specific one
     *
     * @param {Element|Number|undefined} section
     */
    Accordion.prototype.close = function( section ) {

        /* close all sections without parameter */
        if( typeof section === 'undefined' ) {
            for( var i = this.length; i--; ) {
                this.close( this.sections[i] );
            }
            return;
        }

        /* get a single section from numeric index */
        if( !(section instanceof Element) ) {
            section = this.sections.item( parseInt( section, 10 ) );
        }

        /* close section */
        section.classList.remove( this.config.showSectionClass );
    };

    /**
     * Remove any classes, switch back to normal panels
     */
    Accordion.prototype.removeTabAccordion = function() {
        this.wrapper.classList.remove( this.config.wrapperTypeClass.tabs );
        this.wrapper.classList.remove( this.config.wrapperTypeClass.accordion );
    };

    /**
     * Turn the current section group into an accordion
     */
    Accordion.prototype.makeAccordion = function() {
        this.wrapper.classList.remove( this.config.wrapperTypeClass.tabs );
        this.wrapper.classList.add( this.config.wrapperTypeClass.accordion );
    };

    /**
     * Turn the current section group into tabbed content
     */
    Accordion.prototype.makeTabs = function() {
        this.wrapper.classList.add( this.config.wrapperTypeClass.tabs );
        this.wrapper.classList.remove( this.config.wrapperTypeClass.accordion );
        this.open( 0 );

        /* run callback onAccordionReady function here */
        if( this.config.onAccordionReady != null ) {
            var _this = this;
            setTimeout( function() {
                _this.config.onAccordionReady();
            }, 100 );
        }
    };

    var module = {
        /**
         * Expose the Accordion object to the parent scope.
         * This can be used to manually create a new accordion instance.
         */
        Accordion: Accordion,

        /**
         * Helper function to set up the dynamic accordions.
         *
         * @param {Object} classNames Custom overwrites for the class names as in the `classes` object.
         * @returns {Array} A list of Accordion objects
         */
        init: function( userConfig ) {

            /* Use user configuration if provided */
            var config = Functions.extend( defaultConfig, userConfig );

            var accordions = [],
                accordionGroups = document.querySelectorAll( config.selector.wrapper ),
                currentAccordion;

            for( var i = accordionGroups.length; i--; ) {
                try {
                    currentAccordion = new Accordion(
                        accordionGroups[i],
                        accordionGroups[i].querySelectorAll( config.selector.section ),
                        accordionGroups[i].dataset.panelGroupType,
                        config
                    );

                    /* Check if explicitly is defined to leave all elements closed by data attribute `data-panel-group-all-closed` */
                    if( typeof accordionGroups[i].dataset.panelGroupAllClosed === 'undefined' ) {

                        /* If not explicitly defined, open one accordion element */
                        var groupOpen = 0;

                        /* Open a specified accordion element if specified by data attribute, e.g. 'data-panel-group-open="2"', else open first element */
                        if( accordionGroups[i].dataset.panelGroupOpen ) {
                            groupOpen = parseInt( accordionGroups[i].dataset.panelGroupOpen, 10 );

                            if( groupOpen >= 1 ) {
                                groupOpen--;
                            }
                            else {
                                groupOpen = 0;
                            }
                        }

                        currentAccordion.open( groupOpen );
                    }

                    /* Autoplay for this tabaccordion */
                    if( config.autoPlay.enabled ) {
                        module.autoPlay( currentAccordion );
                    }

                    /* Store a reference to return */
                    accordions.push( currentAccordion );
                }
                catch( error ) {
                    console.error( error.stack );
                }
            }
            /* Return the list of accordion elements for further manipulation */
            return accordions;
        },

        /**
         * @param {Accordion} accordion
         */
        autoPlay: function( accordion ) {

            var stopOnHoverElements,
                pauseAutoPlay = false,
                isManuallyPaused = false;

            if( accordion.config.autoPlay.stopOnHover ) {
                if( !!accordion.config.autoPlay.stopOnHoverSelector ) {
                    stopOnHoverElements = accordion.wrapper.querySelectorAll( accordion.config.autoPlay.stopOnHoverSelector );
                }
                else {
                    stopOnHoverElements = accordion.wrapper;
                }

                if( stopOnHoverElements instanceof NodeList ) {
                    /* Pause on mouse over */
                    Functions.on( document.body, 'mouseover', stopOnHoverElements, function() {
                        if( !isManuallyPaused ) {
                            pauseAutoPlay = true;
                        }
                    } );

                    /* Autoplay on mouse out */
                    Functions.on( document.body, 'mouseout', stopOnHoverElements, function() {
                        if( !isManuallyPaused ) {
                            pauseAutoPlay = false;
                        }
                    } );
                }
            }

            document.addEventListener( 'tabaccordion-pause', function() {
                isManuallyPaused = true;
                pauseAutoPlay = true;
            }, false );

            document.addEventListener( 'tabaccordion-resume', function() {
                isManuallyPaused = false;
                pauseAutoPlay = false;
            }, false );

            setInterval( function() {
                if( !pauseAutoPlay ) {
                    var currentItem = Array.prototype.indexOf.call( accordion.sections, accordion.activeSection );
                    if( currentItem === accordion.length - 1 ) {
                        accordion.open( 0 );
                    }
                    else {
                        accordion.open( currentItem + 1 );
                    }
                }
            }, accordion.config.autoPlay.delay );

        }

    };

    return module;

} );
<|endoftext|>


<commit_before>// AdminLTE Gruntfile
module.exports = function (grunt) {

  'use strict';

  grunt.initConfig({
    watch: {
      // If any .less file changes in directory "build/less/" run the "less"-task.
      files: ["build/less/*.less", "build/less/skins/*.less", "dist/js/app.js"],
      tasks: ["less", "uglify"]
    },
    // "less"-task configuration
    // This task will compile all less files upon saving to create both AdminLTE.css and AdminLTE.min.css
    less: {
      // Development not compressed
      development: {
        options: {
          // Whether to compress or not
          compress: false
        },
        files: {
          // compilation.css  :  source.less
          "dist/css/AdminLTE.css": "build/less/AdminLTE.less",
          //Non minified skin files
          "dist/css/skins/skin-blue.css": "build/less/skins/skin-blue.less",
          "dist/css/skins/skin-black.css": "build/less/skins/skin-black.less",
          "dist/css/skins/skin-yellow.css": "build/less/skins/skin-yellow.less",
          "dist/css/skins/skin-green.css": "build/less/skins/skin-green.less",
          "dist/css/skins/skin-red.css": "build/less/skins/skin-red.less",
          "dist/css/skins/skin-purple.css": "build/less/skins/skin-purple.less",
          "dist/css/skins/skin-blue-light.css": "build/less/skins/skin-blue-light.less",
          "dist/css/skins/skin-black-light.css": "build/less/skins/skin-black-light.less",
          "dist/css/skins/skin-yellow-light.css": "build/less/skins/skin-yellow-light.less",
          "dist/css/skins/skin-green-light.css": "build/less/skins/skin-green-light.less",
          "dist/css/skins/skin-red-light.css": "build/less/skins/skin-red-light.less",
          "dist/css/skins/skin-purple-light.css": "build/less/skins/skin-purple-light.less",
          "dist/css/skins/_all-skins.css": "build/less/skins/_all-skins.less"
        }
      },
      // Production compresses version
      production: {
        options: {
          // Whether to compress or not
          compress: true
        },
        files: {
          // compilation.css  :  source.less
          "dist/css/AdminLTE.min.css": "build/less/AdminLTE.less",
          // Skins minified
          "dist/css/skins/skin-blue.min.css": "build/less/skins/skin-blue.less",
          "dist/css/skins/skin-black.min.css": "build/less/skins/skin-black.less",
          "dist/css/skins/skin-yellow.min.css": "build/less/skins/skin-yellow.less",
          "dist/css/skins/skin-green.min.css": "build/less/skins/skin-green.less",
          "dist/css/skins/skin-red.min.css": "build/less/skins/skin-red.less",
          "dist/css/skins/skin-purple.min.css": "build/less/skins/skin-purple.less",
          "dist/css/skins/skin-blue-light.min.css": "build/less/skins/skin-blue-light.less",
          "dist/css/skins/skin-black-light.min.css": "build/less/skins/skin-black-light.less",
          "dist/css/skins/skin-yellow-light.min.css": "build/less/skins/skin-yellow-light.less",
          "dist/css/skins/skin-green-light.min.css": "build/less/skins/skin-green-light.less",
          "dist/css/skins/skin-red-light.min.css": "build/less/skins/skin-red-light.less",
          "dist/css/skins/skin-purple-light.min.css": "build/less/skins/skin-purple-light.less",
          "dist/css/skins/_all-skins.min.css": "build/less/skins/_all-skins.less"
        }
      }
    },
    // Uglify task info. Compress the js files.
    uglify: {
      options: {
        mangle: true,
        preserveComments: 'some'
      },
      my_target: {
        files: {
          'dist/js/app.min.js': ['dist/js/app.js']
        }
      }
    },
    // Build the documentation files
    includes: {
      build: {
        src: ['*.html'], // Source files
        dest: 'documentation/', // Destination directory
        flatten: true,
        cwd: 'documentation/build',
        options: {
          silent: true,
          includePath: 'documentation/build/include'
        }
      }
    },

    // Optimize images
    image: {
      dynamic: {
        files: [{
          expand: true,
          cwd: 'build/img/',
          src: ['**/*.{png,jpg,gif,svg,jpeg}'],
          dest: 'dist/img/'
        }]
      }
    },

    // Validate JS code
    jshint: {
      options: {
        jshintrc: '.jshintrc'
      },
      core: {
        src: 'dist/js/app.js'
      },
      demo: {
        src: 'dist/js/demo.js'
      },
      pages: {
        src: 'dist/js/pages/*.js'
      }
    },

    // Validate CSS files
    csslint: {
      options: {
        csslintrc: 'build/less/.csslintrc'
      },
      dist: [
        'dist/css/AdminLTE.css',
      ]
    },

    // Validate Bootstrap HTML
    bootlint: {
      options: {
        relaxerror: ['W005']
      },
      files: ['pages/**/*.html', '*.html']
    },

    // Delete images in build directory
    // After compressing the images in the build/img dir, there is no need
    // for them
    clean: {
      build: ["build/img/*"]
    }
  });

  // Load all grunt tasks

  // LESS Compiler
  grunt.loadNpmTasks('grunt-contrib-less');
  // Watch File Changes
  grunt.loadNpmTasks('grunt-contrib-watch');
  // Compress JS Files
  grunt.loadNpmTasks('grunt-contrib-uglify');
  // Include Files Within HTML
  grunt.loadNpmTasks('grunt-includes');
  // Optimize images
  grunt.loadNpmTasks('grunt-image');
  // Validate JS code
  grunt.loadNpmTasks('grunt-contrib-jshint');
  // Delete not needed files
  grunt.loadNpmTasks('grunt-contrib-clean');
  // Lint CSS
  grunt.loadNpmTasks('grunt-contrib-csslint');
  // Lint Bootstrap
  grunt.loadNpmTasks('grunt-bootlint');

  // Linting task
  grunt.registerTask('lint', ['jshint', 'csslint', 'bootlint']);

  // The default task (running "grunt" in console) is "watch"
  grunt.registerTask('default', ['watch']);
};
<commit_msg>remove unnecessary coma<commit_after>// AdminLTE Gruntfile
module.exports = function (grunt) {

  'use strict';

  grunt.initConfig({
    watch: {
      // If any .less file changes in directory "build/less/" run the "less"-task.
      files: ["build/less/*.less", "build/less/skins/*.less", "dist/js/app.js"],
      tasks: ["less", "uglify"]
    },
    // "less"-task configuration
    // This task will compile all less files upon saving to create both AdminLTE.css and AdminLTE.min.css
    less: {
      // Development not compressed
      development: {
        options: {
          // Whether to compress or not
          compress: false
        },
        files: {
          // compilation.css  :  source.less
          "dist/css/AdminLTE.css": "build/less/AdminLTE.less",
          //Non minified skin files
          "dist/css/skins/skin-blue.css": "build/less/skins/skin-blue.less",
          "dist/css/skins/skin-black.css": "build/less/skins/skin-black.less",
          "dist/css/skins/skin-yellow.css": "build/less/skins/skin-yellow.less",
          "dist/css/skins/skin-green.css": "build/less/skins/skin-green.less",
          "dist/css/skins/skin-red.css": "build/less/skins/skin-red.less",
          "dist/css/skins/skin-purple.css": "build/less/skins/skin-purple.less",
          "dist/css/skins/skin-blue-light.css": "build/less/skins/skin-blue-light.less",
          "dist/css/skins/skin-black-light.css": "build/less/skins/skin-black-light.less",
          "dist/css/skins/skin-yellow-light.css": "build/less/skins/skin-yellow-light.less",
          "dist/css/skins/skin-green-light.css": "build/less/skins/skin-green-light.less",
          "dist/css/skins/skin-red-light.css": "build/less/skins/skin-red-light.less",
          "dist/css/skins/skin-purple-light.css": "build/less/skins/skin-purple-light.less",
          "dist/css/skins/_all-skins.css": "build/less/skins/_all-skins.less"
        }
      },
      // Production compresses version
      production: {
        options: {
          // Whether to compress or not
          compress: true
        },
        files: {
          // compilation.css  :  source.less
          "dist/css/AdminLTE.min.css": "build/less/AdminLTE.less",
          // Skins minified
          "dist/css/skins/skin-blue.min.css": "build/less/skins/skin-blue.less",
          "dist/css/skins/skin-black.min.css": "build/less/skins/skin-black.less",
          "dist/css/skins/skin-yellow.min.css": "build/less/skins/skin-yellow.less",
          "dist/css/skins/skin-green.min.css": "build/less/skins/skin-green.less",
          "dist/css/skins/skin-red.min.css": "build/less/skins/skin-red.less",
          "dist/css/skins/skin-purple.min.css": "build/less/skins/skin-purple.less",
          "dist/css/skins/skin-blue-light.min.css": "build/less/skins/skin-blue-light.less",
          "dist/css/skins/skin-black-light.min.css": "build/less/skins/skin-black-light.less",
          "dist/css/skins/skin-yellow-light.min.css": "build/less/skins/skin-yellow-light.less",
          "dist/css/skins/skin-green-light.min.css": "build/less/skins/skin-green-light.less",
          "dist/css/skins/skin-red-light.min.css": "build/less/skins/skin-red-light.less",
          "dist/css/skins/skin-purple-light.min.css": "build/less/skins/skin-purple-light.less",
          "dist/css/skins/_all-skins.min.css": "build/less/skins/_all-skins.less"
        }
      }
    },
    // Uglify task info. Compress the js files.
    uglify: {
      options: {
        mangle: true,
        preserveComments: 'some'
      },
      my_target: {
        files: {
          'dist/js/app.min.js': ['dist/js/app.js']
        }
      }
    },
    // Build the documentation files
    includes: {
      build: {
        src: ['*.html'], // Source files
        dest: 'documentation/', // Destination directory
        flatten: true,
        cwd: 'documentation/build',
        options: {
          silent: true,
          includePath: 'documentation/build/include'
        }
      }
    },

    // Optimize images
    image: {
      dynamic: {
        files: [{
          expand: true,
          cwd: 'build/img/',
          src: ['**/*.{png,jpg,gif,svg,jpeg}'],
          dest: 'dist/img/'
        }]
      }
    },

    // Validate JS code
    jshint: {
      options: {
        jshintrc: '.jshintrc'
      },
      core: {
        src: 'dist/js/app.js'
      },
      demo: {
        src: 'dist/js/demo.js'
      },
      pages: {
        src: 'dist/js/pages/*.js'
      }
    },

    // Validate CSS files
    csslint: {
      options: {
        csslintrc: 'build/less/.csslintrc'
      },
      dist: [
        'dist/css/AdminLTE.css'
      ]
    },

    // Validate Bootstrap HTML
    bootlint: {
      options: {
        relaxerror: ['W005']
      },
      files: ['pages/**/*.html', '*.html']
    },

    // Delete images in build directory
    // After compressing the images in the build/img dir, there is no need
    // for them
    clean: {
      build: ["build/img/*"]
    }
  });

  // Load all grunt tasks

  // LESS Compiler
  grunt.loadNpmTasks('grunt-contrib-less');
  // Watch File Changes
  grunt.loadNpmTasks('grunt-contrib-watch');
  // Compress JS Files
  grunt.loadNpmTasks('grunt-contrib-uglify');
  // Include Files Within HTML
  grunt.loadNpmTasks('grunt-includes');
  // Optimize images
  grunt.loadNpmTasks('grunt-image');
  // Validate JS code
  grunt.loadNpmTasks('grunt-contrib-jshint');
  // Delete not needed files
  grunt.loadNpmTasks('grunt-contrib-clean');
  // Lint CSS
  grunt.loadNpmTasks('grunt-contrib-csslint');
  // Lint Bootstrap
  grunt.loadNpmTasks('grunt-bootlint');

  // Linting task
  grunt.registerTask('lint', ['jshint', 'csslint', 'bootlint']);

  // The default task (running "grunt" in console) is "watch"
  grunt.registerTask('default', ['watch']);
};
<|endoftext|>


<commit_before>var gulp = require('gulp');

var files = ['index.js', 'test/*.js', 'gulpfile.js'];

gulp.task('lint', function () {
    var eslint = require('gulp-eslint');
    return gulp.src(files)
        .pipe(eslint())
        .pipe(eslint.format())
        .pipe(eslint.failAfterError());
});

gulp.task('test', function () {
    var mocha = require('gulp-mocha');
    return gulp.src('test/*.js', { read: false })
        .pipe(mocha());
});

gulp.task('default', ['lint', 'test']);

gulp.task('watch', function () {
    gulp.watch(files, ['lint', 'test']);
});
<commit_msg>Remove unnecessary gulpfile.js<commit_after><|endoftext|>


<commit_before>//call packages
var express = require('express');
var app = express();
var bodyParser = require('body-parser');

// configure app to use bodyParser()
// this will get the data from a POST
app.use(bodyParser.urlencoded({ extended: true })); // to support URL-encoded bodies
app.use(bodyParser.json()); // to support JSON-encoded bodies

//import database.js
var db = require('./database.js');

//set port
var port = process.env.PORT || 3030;

//create a new router object
var router = express.Router();

//middleware to use for all requests
router.use(function(req, res, next) {
    console.log('Processing your request...\n');
    next(); // make sure it doesn't stop here and go to the next routes
});

//display homepage
app.get('/', function(req, res) {
	res.send('Welcome! Please kindly refer to github' +
			 ' for instructions on how to access this API');
});

//list all items
router.get('/:username/items', function(req, res) {
	db.get_all_items(req.params.username, function(items, err){
		if (err) {
			res.json({'message': err.code});
		} else {
			res.json(items);
		}
	});
});

//add a new item
router.post('/:username/items', function(req, res) {
	if (!req.body.item_name || !req.body.price || !req.body.quantity) {
		res.json({'status': 'error', 'message': 'missing a parameter'});
	} else {
		db.add_item(req, function(items, err){
			if (err) {
				res.json({'message': err.code});
			} else if (items.affectedRows === 1){
				res.json({'message': 'Item has been successfully added!'});
				console.log('Item has been successfully added!');
			} else {
				res.json(items.message);
				console.log('Item has failed to be added');
			}
		});
	}
});

//update the quantity of an existing item by item_id
router.put('/:username/items/:item_id', function(req, res) {
	db.update_item(req, function(items, err) {
		if (err) {
			res.json({'message': err.code});
		} else if (items.affectedRows === 1){
			res.json({'message': 'Item\'s quantity has been successfully updated'});
			console.log('Item\'s quantity has been successfully updated');
		} else {
			res.json(items.message);
			console.log(items.message);
		}
	});
});

//remove an existing item by item_id
router.delete('/:username/items/:item_id', function(req, res) {
	db.delete_item(req, function(items, err) {
		if (err) {
			res.json({'message': err.code});
		} else if (items.affectedRows === 1){
			res.json({'message': 'Item has been successfully removed from database'});
			console.log('Item has been successfully removed from database');
		} else if (items.affectedRows === 0) {
			res.json({'message': 'Item is not found'});
			console.log('Item is not found');
		} else {
			res.json(items.message);
			console.log('Item\'s removal has failed!');
		}
	});
});

//calculate the total items' price by username
router.get('/:username/checkout', function(req, res) {
	db.get_total_cost(req, function(items, err) {
		if (err) {
			res.json({'message': err.code});
		} else {
			res.json(items);
		}
	});
});

//get the total cost after being discounted by coupon code
router.get('/:username/checkout/:coupon', function(req, res) {

	//get discount rate + total cost
	db.get_discount(req, function(items, err) {
		if (err) {
			res.json({'message': err.code});
		} else {
			var after_discount = parseFloat(items["Total Cost"] * (1 - (items["Discount"]/100))).toFixed(2);
			//add after_discount
			items["Total Cost After Discount"] = after_discount;

			res.json(items);
		}
	});

});

//all of routes will be prefixed with /api
app.use('/api', router);

//start the server
app.listen(port);
console.log('Application is listening at port %s', port);<commit_msg>Added % to Discount<commit_after>//call packages
var express = require('express');
var app = express();
var bodyParser = require('body-parser');

// configure app to use bodyParser()
// this will get the data from a POST
app.use(bodyParser.urlencoded({ extended: true })); // to support URL-encoded bodies
app.use(bodyParser.json()); // to support JSON-encoded bodies

//import database.js
var db = require('./database.js');

//set port
var port = process.env.PORT || 3030;

//create a new router object
var router = express.Router();

//middleware to use for all requests
router.use(function(req, res, next) {
    console.log('Processing your request...\n');
    next(); // make sure it doesn't stop here and go to the next routes
});

//display homepage
app.get('/', function(req, res) {
	res.send('Welcome! Please kindly refer to github' +
			 ' for instructions on how to access this API');
});

//list all items
router.get('/:username/items', function(req, res) {
	db.get_all_items(req.params.username, function(items, err){
		if (err) {
			res.json({'message': err.code});
		} else {
			res.json(items);
		}
	});
});

//add a new item
router.post('/:username/items', function(req, res) {
	if (!req.body.item_name || !req.body.price || !req.body.quantity) {
		res.json({'status': 'error', 'message': 'missing a parameter'});
	} else {
		db.add_item(req, function(items, err){
			if (err) {
				res.json({'message': err.code});
			} else if (items.affectedRows === 1){
				res.json({'message': 'Item has been successfully added!'});
				console.log('Item has been successfully added!');
			} else {
				res.json(items.message);
				console.log('Item has failed to be added');
			}
		});
	}
});

//update the quantity of an existing item by item_id
router.put('/:username/items/:item_id', function(req, res) {
	db.update_item(req, function(items, err) {
		if (err) {
			res.json({'message': err.code});
		} else if (items.affectedRows === 1){
			res.json({'message': 'Item\'s quantity has been successfully updated'});
			console.log('Item\'s quantity has been successfully updated');
		} else {
			res.json(items.message);
			console.log(items.message);
		}
	});
});

//remove an existing item by item_id
router.delete('/:username/items/:item_id', function(req, res) {
	db.delete_item(req, function(items, err) {
		if (err) {
			res.json({'message': err.code});
		} else if (items.affectedRows === 1){
			res.json({'message': 'Item has been successfully removed from database'});
			console.log('Item has been successfully removed from database');
		} else if (items.affectedRows === 0) {
			res.json({'message': 'Item is not found'});
			console.log('Item is not found');
		} else {
			res.json(items.message);
			console.log('Item\'s removal has failed!');
		}
	});
});

//calculate the total items' price by username
router.get('/:username/checkout', function(req, res) {
	db.get_total_cost(req, function(items, err) {
		if (err) {
			res.json({'message': err.code});
		} else {
			res.json(items);
		}
	});
});

//get the total cost after being discounted by coupon code
router.get('/:username/checkout/:coupon', function(req, res) {

	//get discount rate + total cost
	db.get_discount(req, function(items, err) {
		if (err) {
			res.json({'message': err.code});
		} else {
			var after_discount = parseFloat(items["Total Cost"] * (1 - (items["Discount"]/100))).toFixed(2);

			//add discount rate and total cost after discount
			items["Discount"] = items["Discount"].toString() + "%";
			items["Total Cost After Discount"] = after_discount;

			res.json(items);
		}
	});

});

//all of routes will be prefixed with /api
app.use('/api', router);

//start the server
app.listen(port);
console.log('Application is listening at port %s', port);<|endoftext|>


<commit_before>(function( jQuery ) {

var ralpha = /alpha\([^)]*\)/,
	ropacity = /opacity=([^)]*)/,
	rdashAlpha = /-([a-z])/ig,
	rupper = /([A-Z])/g,
	rnumpx = /^-?\d+(?:px)?$/i,
	rnum = /^-?\d/,

	cssShow = { position: "absolute", visibility: "hidden", display: "block" },
	cssWidth = [ "Left", "Right" ],
	cssHeight = [ "Top", "Bottom" ],
	curCSS,

	// cache check for defaultView.getComputedStyle
	getComputedStyle = document.defaultView && document.defaultView.getComputedStyle,

	fcamelCase = function( all, letter ) {
		return letter.toUpperCase();
	};

jQuery.fn.css = function( name, value ) {
	return jQuery.access( this, name, value, true, function( elem, name, value ) {
		return jQuery.css( elem, name, value );
	});
};

jQuery.extend({
	// Add in style property hooks for overriding the default
	// behavior of getting and setting a style property
	cssHooks: {
		opacity: {
			get: function( elem ) {
				// We should always get a number back from opacity
				var ret = curCSS( elem, "opacity", "opacity" );
				return ret === "" ? "1" : ret;
			}
		}
	},

	// Exclude the following css properties to add px
	cssNumber: {
		"zIndex": true,
		"fontWeight": true,
		"opacity": true,
		"zoom": true,
		"lineHeight": true
	},

	// Add in properties whose names you wish to fix before
	// setting or getting the value
	cssProps: {
		// normalize float css property
		"float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat"
	},

	// Get and set the style property on a DOM Node
	style: function( elem, name, value, extra ) {
		// Don't set styles on text and comment nodes
		if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
			return undefined;
		}

		// Make sure that we're working with the right name
		var ret, origName = name.replace( rdashAlpha, fcamelCase ),
			style = elem.style, hooks = jQuery.cssHooks[ origName ];

		name = jQuery.cssProps[ origName ] || origName;

		// Check if we're setting a value
		if ( value !== undefined ) {
			// If a number was passed in, add 'px' to the (except for certain CSS properties)
			if ( typeof value === "number" && !jQuery.cssNumber[ origName ] ) {
				value += "px";
			}

			// If a hook was provided, use that value, otherwise just set the specified value
			if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value )) !== undefined ) {
				style[ name ] = value;
			}

		} else {
			// If a hook was provided get the non-computed value from there
			if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
				return ret;
			}

			// Otherwise just get the value from the style object
			return style[ name ];
		}
	},

	css: function( elem, name, value, extra ) {
		// Make sure that we're working with the right name
		var ret, origName = name.replace( rdashAlpha, fcamelCase ),
			hooks = jQuery.cssHooks[ origName ];

		name = jQuery.cssProps[ origName ] || origName;

		// Check if we're setting a value, just use jQuery.style (DEPRECATED)
		if ( value !== undefined ) {
			jQuery.style( elem, name, value );

		// If a hook was provided get the computed value from there
		} else if ( hooks && "get" in hooks && (ret = hooks.get( elem, true, extra )) !== undefined ) {
			return ret;

		// Otherwise, if a way to get the computed value exists, use that
		} else if ( curCSS ) {
			return curCSS( elem, name, origName );
		}
	},

	// A method for quickly swapping in/out CSS properties to get correct calculations
	swap: function( elem, options, callback ) {
		var old = {};

		// Remember the old values, and insert the new ones
		for ( var name in options ) {
			old[ name ] = elem.style[ name ];
			elem.style[ name ] = options[ name ];
		}

		callback.call( elem );

		// Revert the old values
		for ( name in options ) {
			elem.style[ name ] = old[ name ];
		}
	}
});

jQuery.each(["height", "width"], function( i, name ) {
	jQuery.cssHooks[ name ] = {
		get: function( elem, computed, extra ) {
			var val;

			if ( computed ) {
				if ( elem.offsetWidth !== 0 ) {
					val = getWH( elem, name, extra );

				} else {
					jQuery.swap( elem, cssShow, function() {
						val = getWH( elem, name, extra );
					});
				}

				return val + "px";
			}
		},

		set: function( elem, value ) {
			// ignore negative width and height values #1599
			value = parseFloat(value);

			if ( value >= 0 ) {
				return value + "px";
			}
		}
	};
});

if ( !jQuery.support.opacity ) {
	jQuery.cssHooks.opacity = {
		get: function( elem, computed ) {
			// IE uses filters for opacity
			return ropacity.test((computed ? elem.currentStyle.filter : elem.style.filter) || "") ?
				(parseFloat(RegExp.$1) / 100) + "" :
				"1";
		},

		set: function( elem, value ) {
			var style = elem.style;

			// IE has trouble with opacity if it does not have layout
			// Force it by setting the zoom level
			style.zoom = 1;

			// Set the alpha filter to set the opacity
			var opacity = parseInt( value, 10 ) + "" === "NaN" ?
				"" :
				"alpha(opacity=" + value * 100 + ")";

			var filter = style.filter || elem.currentStyle.filter || "";

			style.filter = ralpha.test(filter) ?
				filter.replace(ralpha, opacity) :
				opacity;
		}
	};
}

if ( getComputedStyle ) {
	curCSS = function( elem, newName, name ) {
		var ret, defaultView, computedStyle;

		name = name.replace( rupper, "-$1" ).toLowerCase();

		if ( !(defaultView = elem.ownerDocument.defaultView) ) {
			return undefined;
		}

		if ( (computedStyle = defaultView.getComputedStyle( elem, null )) ) {
			ret = computedStyle.getPropertyValue( name );
		}

		return ret;
	};

} else if ( document.documentElement.currentStyle ) {
	curCSS = function( elem, name ) {
		var left, rsLeft, ret = elem.currentStyle[ name ], style = elem.style;

		// From the awesome hack by Dean Edwards
		// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291

		// If we're not dealing with a regular pixel number
		// but a number that has a weird ending, we need to convert it to pixels
		if ( !rnumpx.test( ret ) && rnum.test( ret ) ) {
			// Remember the original values
			left = style.left;
			rsLeft = elem.runtimeStyle.left;

			// Put in the new values to get a computed value out
			elem.runtimeStyle.left = elem.currentStyle.left;
			style.left = name === "fontSize" ? "1em" : (ret || 0);
			ret = style.pixelLeft + "px";

			// Revert the changed values
			style.left = left;
			elem.runtimeStyle.left = rsLeft;
		}

		return ret;
	};
}

function getWH( elem, name, extra ) {
	var which = name === "width" ? cssWidth : cssHeight,
		val = name === "width" ? elem.offsetWidth : elem.offsetHeight;

	if ( extra === "border" ) {
		return val;
	}

	jQuery.each( which, function() {
		if ( !extra ) {
			val -= parseFloat(jQuery.css( elem, "padding" + this )) || 0;
		}

		if ( extra === "margin" ) {
			val += parseFloat(jQuery.css( elem, "margin" + this )) || 0;

		} else {
			val -= parseFloat(jQuery.css( elem, "border" + this + "Width" )) || 0;
		}
	});

	return val;
}

if ( jQuery.expr && jQuery.expr.filters ) {
	jQuery.expr.filters.hidden = function( elem ) {
		var width = elem.offsetWidth, height = elem.offsetHeight,
			skip = elem.nodeName.toLowerCase() === "tr";

		return width === 0 && height === 0 && !skip ?
			true :
			width > 0 && height > 0 && !skip ?
				false :
				(elem.style.display || jQuery.css( elem, "display" )) === "none";
	};

	jQuery.expr.filters.visible = function( elem ) {
		return !jQuery.expr.filters.hidden( elem );
	};
}

})( jQuery );
<commit_msg>Make sure that empty height/width values are still set.<commit_after>(function( jQuery ) {

var ralpha = /alpha\([^)]*\)/,
	ropacity = /opacity=([^)]*)/,
	rdashAlpha = /-([a-z])/ig,
	rupper = /([A-Z])/g,
	rnumpx = /^-?\d+(?:px)?$/i,
	rnum = /^-?\d/,

	cssShow = { position: "absolute", visibility: "hidden", display: "block" },
	cssWidth = [ "Left", "Right" ],
	cssHeight = [ "Top", "Bottom" ],
	curCSS,

	// cache check for defaultView.getComputedStyle
	getComputedStyle = document.defaultView && document.defaultView.getComputedStyle,

	fcamelCase = function( all, letter ) {
		return letter.toUpperCase();
	};

jQuery.fn.css = function( name, value ) {
	return jQuery.access( this, name, value, true, function( elem, name, value ) {
		return jQuery.css( elem, name, value );
	});
};

jQuery.extend({
	// Add in style property hooks for overriding the default
	// behavior of getting and setting a style property
	cssHooks: {
		opacity: {
			get: function( elem ) {
				// We should always get a number back from opacity
				var ret = curCSS( elem, "opacity", "opacity" );
				return ret === "" ? "1" : ret;
			}
		}
	},

	// Exclude the following css properties to add px
	cssNumber: {
		"zIndex": true,
		"fontWeight": true,
		"opacity": true,
		"zoom": true,
		"lineHeight": true
	},

	// Add in properties whose names you wish to fix before
	// setting or getting the value
	cssProps: {
		// normalize float css property
		"float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat"
	},

	// Get and set the style property on a DOM Node
	style: function( elem, name, value, extra ) {
		// Don't set styles on text and comment nodes
		if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
			return undefined;
		}

		// Make sure that we're working with the right name
		var ret, origName = name.replace( rdashAlpha, fcamelCase ),
			style = elem.style, hooks = jQuery.cssHooks[ origName ];

		name = jQuery.cssProps[ origName ] || origName;

		// Check if we're setting a value
		if ( value !== undefined ) {
			// If a number was passed in, add 'px' to the (except for certain CSS properties)
			if ( typeof value === "number" && !jQuery.cssNumber[ origName ] ) {
				value += "px";
			}

			// If a hook was provided, use that value, otherwise just set the specified value
			if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value )) !== undefined ) {
				style[ name ] = value;
			}

		} else {
			// If a hook was provided get the non-computed value from there
			if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
				return ret;
			}

			// Otherwise just get the value from the style object
			return style[ name ];
		}
	},

	css: function( elem, name, value, extra ) {
		// Make sure that we're working with the right name
		var ret, origName = name.replace( rdashAlpha, fcamelCase ),
			hooks = jQuery.cssHooks[ origName ];

		name = jQuery.cssProps[ origName ] || origName;

		// Check if we're setting a value, just use jQuery.style (DEPRECATED)
		if ( value !== undefined ) {
			jQuery.style( elem, name, value );

		// If a hook was provided get the computed value from there
		} else if ( hooks && "get" in hooks && (ret = hooks.get( elem, true, extra )) !== undefined ) {
			return ret;

		// Otherwise, if a way to get the computed value exists, use that
		} else if ( curCSS ) {
			return curCSS( elem, name, origName );
		}
	},

	// A method for quickly swapping in/out CSS properties to get correct calculations
	swap: function( elem, options, callback ) {
		var old = {};

		// Remember the old values, and insert the new ones
		for ( var name in options ) {
			old[ name ] = elem.style[ name ];
			elem.style[ name ] = options[ name ];
		}

		callback.call( elem );

		// Revert the old values
		for ( name in options ) {
			elem.style[ name ] = old[ name ];
		}
	}
});

jQuery.each(["height", "width"], function( i, name ) {
	jQuery.cssHooks[ name ] = {
		get: function( elem, computed, extra ) {
			var val;

			if ( computed ) {
				if ( elem.offsetWidth !== 0 ) {
					val = getWH( elem, name, extra );

				} else {
					jQuery.swap( elem, cssShow, function() {
						val = getWH( elem, name, extra );
					});
				}

				return val + "px";
			}
		},

		set: function( elem, value ) {
			if ( value !== "" ) {
				// ignore negative width and height values #1599
				value = parseFloat(value);

				if ( value >= 0 ) {
					return value + "px";
				}

			} else {
				return value;
			}
		}
	};
});

if ( !jQuery.support.opacity ) {
	jQuery.cssHooks.opacity = {
		get: function( elem, computed ) {
			// IE uses filters for opacity
			return ropacity.test((computed ? elem.currentStyle.filter : elem.style.filter) || "") ?
				(parseFloat(RegExp.$1) / 100) + "" :
				"1";
		},

		set: function( elem, value ) {
			var style = elem.style;

			// IE has trouble with opacity if it does not have layout
			// Force it by setting the zoom level
			style.zoom = 1;

			// Set the alpha filter to set the opacity
			var opacity = parseInt( value, 10 ) + "" === "NaN" ?
				"" :
				"alpha(opacity=" + value * 100 + ")";

			var filter = style.filter || elem.currentStyle.filter || "";

			style.filter = ralpha.test(filter) ?
				filter.replace(ralpha, opacity) :
				opacity;
		}
	};
}

if ( getComputedStyle ) {
	curCSS = function( elem, newName, name ) {
		var ret, defaultView, computedStyle;

		name = name.replace( rupper, "-$1" ).toLowerCase();

		if ( !(defaultView = elem.ownerDocument.defaultView) ) {
			return undefined;
		}

		if ( (computedStyle = defaultView.getComputedStyle( elem, null )) ) {
			ret = computedStyle.getPropertyValue( name );
		}

		return ret;
	};

} else if ( document.documentElement.currentStyle ) {
	curCSS = function( elem, name ) {
		var left, rsLeft, ret = elem.currentStyle[ name ], style = elem.style;

		// From the awesome hack by Dean Edwards
		// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291

		// If we're not dealing with a regular pixel number
		// but a number that has a weird ending, we need to convert it to pixels
		if ( !rnumpx.test( ret ) && rnum.test( ret ) ) {
			// Remember the original values
			left = style.left;
			rsLeft = elem.runtimeStyle.left;

			// Put in the new values to get a computed value out
			elem.runtimeStyle.left = elem.currentStyle.left;
			style.left = name === "fontSize" ? "1em" : (ret || 0);
			ret = style.pixelLeft + "px";

			// Revert the changed values
			style.left = left;
			elem.runtimeStyle.left = rsLeft;
		}

		return ret;
	};
}

function getWH( elem, name, extra ) {
	var which = name === "width" ? cssWidth : cssHeight,
		val = name === "width" ? elem.offsetWidth : elem.offsetHeight;

	if ( extra === "border" ) {
		return val;
	}

	jQuery.each( which, function() {
		if ( !extra ) {
			val -= parseFloat(jQuery.css( elem, "padding" + this )) || 0;
		}

		if ( extra === "margin" ) {
			val += parseFloat(jQuery.css( elem, "margin" + this )) || 0;

		} else {
			val -= parseFloat(jQuery.css( elem, "border" + this + "Width" )) || 0;
		}
	});

	return val;
}

if ( jQuery.expr && jQuery.expr.filters ) {
	jQuery.expr.filters.hidden = function( elem ) {
		var width = elem.offsetWidth, height = elem.offsetHeight,
			skip = elem.nodeName.toLowerCase() === "tr";

		return width === 0 && height === 0 && !skip ?
			true :
			width > 0 && height > 0 && !skip ?
				false :
				(elem.style.display || jQuery.css( elem, "display" )) === "none";
	};

	jQuery.expr.filters.visible = function( elem ) {
		return !jQuery.expr.filters.hidden( elem );
	};
}

})( jQuery );
<|endoftext|>


<commit_before>const express = require('express')
const path = require('path')
const logger = require('morgan')
const cookieParser = require('cookie-parser')
const bodyParser = require('body-parser')
const passport = require('passport')
const Strategy = require('passport-twitter').Strategy
const mongoose = require('mongoose')
const debug = require('debug')('homeless-alarm:server')
const Chance = require('chance')
require('dotenv').config()

const chance = new Chance()

const uri = process.env.MONGODB_URI
mongoose.connect(uri)

const db = mongoose.connection
db.on('error', (error) => {
  debug(`connection error: ${error}`)
})
db.once('open', () => {
  debug('mongolab connection opened')
})

// see: https://github.com/passport/express-4.x-twitter-example
passport.use(new Strategy(
  {
    consumerKey: process.env.TWITTER_CONSUMER_KEY,
    consumerSecret: process.env.TWITTER_CONSUMER_SECRET,
    callbackUrl: process.env.TWITTER_CALLBACK_URL,
  },
  (token, tokenSecret, profile, callback) => (callback(null, profile))
))

passport.serializeUser((user, callback) => (callback(null, user)))

passport.deserializeUser((obj, callback) => (callback(null, obj)))

const app = express()
const server = require('http').Server(app)
const io = require('socket.io')(server)

app.use((req, res, next) => {
  res.io = io
  next()
})

const emitHashtag = () => {
  io.emit('socket-to-me', chance.hashtag())
}

setInterval(emitHashtag, 30000)

// view engine setup
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'pug')

// uncomment after placing your favicon in /public
// app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')))
app.use(logger('dev'))
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }))
app.use(cookieParser())
app.use(express.static(path.join(__dirname, 'public')))
app.use(express.static(path.join(__dirname, '../node_modules')))

app.use(require('express-session')({
  secret: process.env.EXPRESS_SESSION_SECRET,
  resave: true,
  saveUninitialized: true,
}))

app.use(passport.initialize())
app.use(passport.session())

const routes = require('./routes/index')
const profile = require('./routes/profile')
const featureToggles = require('./routes/feature-toggles')
const login = require('./routes/login')
const logout = require('./routes/logout')
const weather = require('./routes/weather')
const socketToMe = require('./routes/socket-to-me')

app.use('/', routes)
app.use('/profile', profile)
app.use('/v1/feature-toggles', featureToggles)
app.use('/login', login)
app.use('/logout', logout)
app.use('/v1/weather', weather)
app.use('/socket-to-me', socketToMe)

// todo: move into separate files
app.get('/auth/twitter', passport.authenticate('twitter'))

app.get('/auth/twitter/callback',
  passport.authenticate('twitter', { failureRedirect: '/login' }),
  (req, res) => {
    res.redirect('/')
  })
// /todo: move into separate files

const ensureLoggedIn = require('connect-ensure-login').ensureLoggedIn()
app.get('/status', ensureLoggedIn, require('express-status-monitor')())
app.use(require('express-favicon-short-circuit'))

// catch 404 and forward to error handler
app.use((req, res, next) => {
  const err = new Error('Not Found')
  err.status = 404
  next(err)
})

// error handlers w/ stacktraces in development
app.use((err, req, res, next) => {
  res.status(err.status || 500)
  res.render('error', {
    message: err.message,
    error: (app.get('env') === 'development') ? err : {},
  })
})

module.exports = { app, server }
<commit_msg>add on connection listener<commit_after>const express = require('express')
const path = require('path')
const logger = require('morgan')
const cookieParser = require('cookie-parser')
const bodyParser = require('body-parser')
const passport = require('passport')
const Strategy = require('passport-twitter').Strategy
const mongoose = require('mongoose')
const debug = require('debug')('homeless-alarm:server')
const Chance = require('chance')
require('dotenv').config()

const chance = new Chance()

const uri = process.env.MONGODB_URI
mongoose.connect(uri)

const db = mongoose.connection
db.on('error', (error) => {
  debug(`connection error: ${error}`)
})
db.once('open', () => {
  debug('mongolab connection opened')
})

// see: https://github.com/passport/express-4.x-twitter-example
passport.use(new Strategy(
  {
    consumerKey: process.env.TWITTER_CONSUMER_KEY,
    consumerSecret: process.env.TWITTER_CONSUMER_SECRET,
    callbackUrl: process.env.TWITTER_CALLBACK_URL,
  },
  (token, tokenSecret, profile, callback) => (callback(null, profile))
))

passport.serializeUser((user, callback) => (callback(null, user)))

passport.deserializeUser((obj, callback) => (callback(null, obj)))

const app = express()
const server = require('http').Server(app)
const io = require('socket.io')(server)

io.on('connection', () => {
  debug('socket.io connected')
})

app.use((req, res, next) => {
  res.io = io
  next()
})

const emitHashtag = () => {
  io.emit('socket-to-me', chance.hashtag())
}

setInterval(emitHashtag, 30000)

// view engine setup
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'pug')

// uncomment after placing your favicon in /public
// app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')))
app.use(logger('dev'))
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }))
app.use(cookieParser())
app.use(express.static(path.join(__dirname, 'public')))
app.use(express.static(path.join(__dirname, '../node_modules')))

app.use(require('express-session')({
  secret: process.env.EXPRESS_SESSION_SECRET,
  resave: true,
  saveUninitialized: true,
}))

app.use(passport.initialize())
app.use(passport.session())

const routes = require('./routes/index')
const profile = require('./routes/profile')
const featureToggles = require('./routes/feature-toggles')
const login = require('./routes/login')
const logout = require('./routes/logout')
const weather = require('./routes/weather')
const socketToMe = require('./routes/socket-to-me')

app.use('/', routes)
app.use('/profile', profile)
app.use('/v1/feature-toggles', featureToggles)
app.use('/login', login)
app.use('/logout', logout)
app.use('/v1/weather', weather)
app.use('/socket-to-me', socketToMe)

// todo: move into separate files
app.get('/auth/twitter', passport.authenticate('twitter'))

app.get('/auth/twitter/callback',
  passport.authenticate('twitter', { failureRedirect: '/login' }),
  (req, res) => {
    res.redirect('/')
  })
// /todo: move into separate files

const ensureLoggedIn = require('connect-ensure-login').ensureLoggedIn()
app.get('/status', ensureLoggedIn, require('express-status-monitor')())
app.use(require('express-favicon-short-circuit'))

// catch 404 and forward to error handler
app.use((req, res, next) => {
  const err = new Error('Not Found')
  err.status = 404
  next(err)
})

// error handlers w/ stacktraces in development
app.use((err, req, res, next) => {
  res.status(err.status || 500)
  res.render('error', {
    message: err.message,
    error: (app.get('env') === 'development') ? err : {},
  })
})

module.exports = { app, server }
<|endoftext|>


<commit_before>function indentRecursive(node, indentLevel = 0) {
  node.each && node.each((child, i) => {
    if (!child.raws.before || child.raws.before.includes('\n')) {
      child.raws.before = `${ node.type === 'rule' || i === 0 ? '\n' : '\n\n'}${'  '.repeat(indentLevel)}`
    }
    child.raws.after = `\n${'  '.repeat(indentLevel)}`
    indentRecursive(child, indentLevel + 1)
  })
}

export default function formatNodes(root) {
  indentRecursive(root)
  root.first.raws.before = ''
}
<commit_msg>Fix formatting<commit_after>function indentRecursive(node, indent = 0) {
  node.each &&
    node.each((child, i) => {
      if (!child.raws.before || child.raws.before.includes('\n')) {
        child.raws.before = `\n${node.type !== 'rule' && i > 0 ? '\n' : ''}${'  '.repeat(indent)}`
      }
      child.raws.after = `\n${'  '.repeat(indent)}`
      indentRecursive(child, indent + 1)
    })
}

export default function formatNodes(root) {
  indentRecursive(root)
  root.first.raws.before = ''
}
<|endoftext|>


<commit_before>import autoprefixer from 'gulp-autoprefixer';
import chalk from 'chalk';
import glob from 'glob';
import gulp from 'gulp';
import nib from 'nib';
import size from 'gulp-size';
import sourcemaps from 'gulp-sourcemaps';
import stylus from 'gulp-stylus';
import lost from 'lost';
import postcss from 'postcss';

var uiComponentsPath = getStyleImportPath(),
    importPaths = ['nib', uiComponentsPath + '/helpers/*.styl', uiComponentsPath + '/settings/*.styl'];

/**
 * Compile stylus source to CSS
 */
gulp.task('styles', function gulpStylus() {

    return gulp
        .src('src/stylesheets/*.styl')
        .pipe(sourcemaps.init())
        .pipe(stylus({
            use: nib(),
            import: importPaths,
            include: uiComponentsPath,
            compress: false
        }))
        .pipe(postcss([
          lost(),
          autoprefixer('last 2 versions')
        ]))
        .pipe(sourcemaps.write('.'))
        .pipe(gulp.dest('src/stylesheets'))
        .pipe(size());
});

/**
 * Compile stylus source to minified CSS in dist/
 */
gulp.task('styles:dist', function gulpStylusMin() {

    return gulp
        .src('src/stylesheets/*.styl')
        .pipe(stylus({
            use: nib(),
            import: importPaths,
            include: uiComponentsPath,
            compress: true
        }))
        .pipe(postcss([
          lost(),
          autoprefixer('last 2 versions')
        ]))
        .pipe(gulp.dest('dist/stylesheets'))
        .pipe(size());
});

/**
 * [getStyleImportPath description]
 * @return {[type]} [description]
 */
function getStyleImportPath() {
    var packageDir = getPackageDir(require(process.cwd() + '/package.json')),
        dir = glob.sync(process.cwd() + '/' + packageDir + '/apsis/ui-components*/');

    if (!dir.length) {
        console.log('');
        console.log(chalk.bold.red('>>>>> You have not installed Apsis stylus helpers. <<<<<<'));
        console.log(chalk.yellow('If you need them, run jspm install apsis:styles.'));
        console.log('');
    }

    return dir;
}

function getPackageDir(pkgInfo) {
    var packageDir = 'jspm_packages';

    if ( !!pkgInfo.jspm ) {
        if ( !!pkgInfo.jspm.directories && !!pkgInfo.jspm.directories.packages ) {
            packageDir = pkgInfo.jspm.directories.packages;
        }
    } else if ( !!pkgInfo.directories && !!pkgInfo.directories.packages ) {
        packageDir = pkgInfo.directories.packages;
    } else {
        throw new Error('Can\t find package.json');
    }

    return packageDir;
}
<commit_msg>fix typo<commit_after>import autoprefixer from 'gulp-autoprefixer';
import chalk from 'chalk';
import glob from 'glob';
import gulp from 'gulp';
import nib from 'nib';
import size from 'gulp-size';
import sourcemaps from 'gulp-sourcemaps';
import stylus from 'gulp-stylus';
import lost from 'lost';
import postcss from 'gulp-postcss';

var uiComponentsPath = getStyleImportPath(),
    importPaths = ['nib', uiComponentsPath + '/helpers/*.styl', uiComponentsPath + '/settings/*.styl'];

/**
 * Compile stylus source to CSS
 */
gulp.task('styles', function gulpStylus() {

    return gulp
        .src('src/stylesheets/*.styl')
        .pipe(sourcemaps.init())
        .pipe(stylus({
            use: nib(),
            import: importPaths,
            include: uiComponentsPath,
            compress: false
        }))
        .pipe(postcss([
          lost(),
          autoprefixer('last 2 versions')
        ]))
        .pipe(sourcemaps.write('.'))
        .pipe(gulp.dest('src/stylesheets'))
        .pipe(size());
});

/**
 * Compile stylus source to minified CSS in dist/
 */
gulp.task('styles:dist', function gulpStylusMin() {

    return gulp
        .src('src/stylesheets/*.styl')
        .pipe(stylus({
            use: nib(),
            import: importPaths,
            include: uiComponentsPath,
            compress: true
        }))
        .pipe(postcss([
          lost(),
          autoprefixer('last 2 versions')
        ]))
        .pipe(gulp.dest('dist/stylesheets'))
        .pipe(size());
});

/**
 * [getStyleImportPath description]
 * @return {[type]} [description]
 */
function getStyleImportPath() {
    var packageDir = getPackageDir(require(process.cwd() + '/package.json')),
        dir = glob.sync(process.cwd() + '/' + packageDir + '/apsis/ui-components*/');

    if (!dir.length) {
        console.log('');
        console.log(chalk.bold.red('>>>>> You have not installed Apsis stylus helpers. <<<<<<'));
        console.log(chalk.yellow('If you need them, run jspm install apsis:styles.'));
        console.log('');
    }

    return dir;
}

function getPackageDir(pkgInfo) {
    var packageDir = 'jspm_packages';

    if ( !!pkgInfo.jspm ) {
        if ( !!pkgInfo.jspm.directories && !!pkgInfo.jspm.directories.packages ) {
            packageDir = pkgInfo.jspm.directories.packages;
        }
    } else if ( !!pkgInfo.directories && !!pkgInfo.directories.packages ) {
        packageDir = pkgInfo.directories.packages;
    } else {
        throw new Error('Can\t find package.json');
    }

    return packageDir;
}
<|endoftext|>