<commit_before>
var Q = require('q');
var _ = require('lodash');
var requestor = require('../requestor');
var xml2json = require('xml2json');
var logger = require('../logger');
var globalOptions = require('../options');

function gocdRequestorModule() {

  function baseUrl() {
    var url = globalOptions.get().url;
    if(url && url.indexOf('http') !== 0) {
      url = 'http://' + url;
    }
    return url;
  }

  function getGocdPipelineBasePath (pipeline) {
    return '/go/api/pipelines/' + (pipeline || globalOptions.get().pipeline);
  }

  function getGocdHistoryPath (pipeline) {
    return getGocdPipelineBasePath(pipeline) + '/history';
  }

  function getFromGocd (path) {

    if(path.indexOf('http') === 0) {
      path = path.replace(baseUrl(), '');
    }

    return requestor.getUrl(baseUrl() + path);
  }

  function requestAndXml2Json(path) {
    return getFromGocd(path).then(function (body) {

      var parsedResponse = xml2json.toJson(body, {
        object: true, sanitize: false
      });
      if (parsedResponse.html) {
        console.log('Error requesting', path);
        throw new Error('Unexpected HTML response: ' + body);
      } else {
        return parsedResponse;
      }

    });

  }

  var getPipelineDetails = function(pipelineId, pipelineName) {
    var pipelineDetailsPath = getGocdPipelineBasePath(pipelineName) + '/' + pipelineId + '.xml';
    logger.debug('Requesting pipeline details', pipelineDetailsPath);
    return requestAndXml2Json(pipelineDetailsPath).then(function(json) {
      if(json.pipeline) {
        return json;
      } else {
        logger.error('unexpected data from pipeline details', url, json);
        return undefined;
      }
    });
  };

  var getHistory = function(offset, pipelineName) {

    var historyPagePath = getGocdHistoryPath(pipelineName) + (offset ? '/' + offset : '');
    logger.debug('Requesting history', historyPagePath);

    return getFromGocd(historyPagePath).then(function (body) {
      return JSON.parse(body);
    }).fail(function(error) {
      console.log('ERROR getting Go.CD history', error);
      return undefined;
    });

  };

  var getPipelineNames = function() {
    var pipelinesPath = '/go/api/pipelines.xml';
    logger.debug('Requesting pipeline list', pipelinesPath);

    return requestAndXml2Json(pipelinesPath).then(function (data) {
      data.pipelines.pipeline = [].concat(data.pipelines.pipeline);

      return Q.all(_.map(data.pipelines.pipeline, function(pipeline) {
        return requestAndXml2Json(pipeline.href);
      })).then(function(details) {
        return _.map(details, function(detail) {
            return detail.feed.title;
        });
      });
    });
  };

  return {
    getHistory: getHistory,
    getPipelineDetails: getPipelineDetails,
    getPipelineNames: getPipelineNames
  };
}

var gocdRequestor = gocdRequestorModule();
exports.getHistory = gocdRequestor.getHistory;
exports.getPipelineDetails = gocdRequestor.getPipelineDetails;
exports.getPipelineNames = gocdRequestor.getPipelineNames;
<commit_msg>More error handling<commit_after>
var Q = require('q');
var _ = require('lodash');
var requestor = require('../requestor');
var xml2json = require('xml2json');
var logger = require('../logger');
var globalOptions = require('../options');

function gocdRequestorModule() {

  function baseUrl() {
    var url = globalOptions.get().url;
    if(url && url.indexOf('http') !== 0) {
      url = 'http://' + url;
    }
    return url;
  }

  function getGocdPipelineBasePath (pipeline) {
    return '/go/api/pipelines/' + (pipeline || globalOptions.get().pipeline);
  }

  function getGocdHistoryPath (pipeline) {
    return getGocdPipelineBasePath(pipeline) + '/history';
  }

  function getFromGocd (path) {

    if(path.indexOf('http') === 0) {
      path = path.replace(baseUrl(), '');
    }

    return requestor.getUrl(baseUrl() + path);
  }

  function requestAndXml2Json(path) {
    return getFromGocd(path).then(function (body) {
      var parsedResponse = xml2json.toJson(body, {
        object: true, sanitize: false
      });
      if (parsedResponse.html) {
        console.log('Error requesting', path);
        throw new Error('Unexpected HTML response: ' + body);
      } else {
        return parsedResponse;
      }

    }).fail(function(error) {
      console.log('ERROR reading or parsing Go.CD', error);
      return undefined;
    });

  }

  var getPipelineDetails = function(pipelineId, pipelineName) {
    var pipelineDetailsPath = getGocdPipelineBasePath(pipelineName) + '/' + pipelineId + '.xml';
    logger.debug('Requesting pipeline details', pipelineDetailsPath);
    return requestAndXml2Json(pipelineDetailsPath).then(function(json) {
      if(json.pipeline) {
        return json;
      } else {
        logger.error('unexpected data from pipeline details', url, json);
        return undefined;
      }
    });
  };

  var getHistory = function(offset, pipelineName) {

    var historyPagePath = getGocdHistoryPath(pipelineName) + (offset ? '/' + offset : '');
    logger.debug('Requesting history', historyPagePath);

    return getFromGocd(historyPagePath).then(function (body) {
      return JSON.parse(body);
    }).fail(function(error) {
      console.log('ERROR getting Go.CD history', error);
      return undefined;
    });

  };

  var getPipelineNames = function() {
    var pipelinesPath = '/go/api/pipelines.xml';
    logger.debug('Requesting pipeline list', pipelinesPath);

    return requestAndXml2Json(pipelinesPath).then(function (data) {
      data.pipelines.pipeline = [].concat(data.pipelines.pipeline);

      return Q.all(_.map(data.pipelines.pipeline, function(pipeline) {
        return requestAndXml2Json(pipeline.href);
      })).then(function(details) {
        return _.map(details, function(detail) {
            return detail.feed.title;
        });
      });
    });
  };

  return {
    getHistory: getHistory,
    getPipelineDetails: getPipelineDetails,
    getPipelineNames: getPipelineNames
  };
}

var gocdRequestor = gocdRequestorModule();
exports.getHistory = gocdRequestor.getHistory;
exports.getPipelineDetails = gocdRequestor.getPipelineDetails;
exports.getPipelineNames = gocdRequestor.getPipelineNames;
<|endoftext|>


<commit_before>export const DEFAULT_SOURCES = '';

export const DEFAULT_COLLECTION = 9139487;

export const DEFAULT_COLLECTION_OBJECT_ARRAY = [{ id: DEFAULT_COLLECTION, tags_id: DEFAULT_COLLECTION, label: 'U.S. Top News' }];

export const PICK_COLLECTION = 0;
export const PICK_SOURCE = 1;
export const ADVANCED = 2;
export const STARRED = 3;

export function generateQueryParamString(queries) {
  const collection = queries.map(query => query.collections.map(c => `{"id":${c.id}, "label":"${c.label}"}`));
  const sources = queries.map(query => query.sources.map(c => `{"id":${c.id}}`));
  let urlParamString = queries.map((query, idx) => `{"index":${idx},"q":"${query.q}","startDate":"${query.startDate}","endDate":"${query.endDate}","sources":[${sources[idx]}],"collections":[${collection[idx]}]}`);
  urlParamString = `${urlParamString}`;

  return urlParamString;
}

const MAX_QUERY_LABEL_LENGTH = 60;

export function smartLabelForQuery(query) {
  let smartLabel = query.q;
  if (query.q.length > MAX_QUERY_LABEL_LENGTH) {
    smartLabel = `${smartLabel.substr(0, MAX_QUERY_LABEL_LENGTH)}...`;
  }
  return smartLabel;
}

export function queryPropertyHasChanged(queries, nextQueries, propName) {
  const currentProps = queries.map(q => q[propName]).reduce((allProps, prop) => allProps + prop);
  const nextProps = nextQueries.map(q => q[propName]).reduce((allProps, prop) => allProps + prop);
  const propHasChanged = currentProps !== nextProps;
  return propHasChanged;
}

<commit_msg>Preserve color into URL params for logged in users<commit_after>export const DEFAULT_SOURCES = '';

export const DEFAULT_COLLECTION = 9139487;

export const DEFAULT_COLLECTION_OBJECT_ARRAY = [{ id: DEFAULT_COLLECTION, tags_id: DEFAULT_COLLECTION, label: 'U.S. Top News' }];

export const PICK_COLLECTION = 0;
export const PICK_SOURCE = 1;
export const ADVANCED = 2;
export const STARRED = 3;

export function generateQueryParamString(queries) {
  const collection = queries.map(query => query.collections.map(c => `{"id":${c.id}, "label":"${c.label}"}`));
  const sources = queries.map(query => query.sources.map(c => `{"id":${c.id}}`));
  let urlParamString = queries.map((query, idx) => `{"index":${idx},"q":"${query.q}","color":"${escape(query.color)}","startDate":"${query.startDate}","endDate":"${query.endDate}","sources":[${sources[idx]}],"collections":[${collection[idx]}]}`);
  urlParamString = `${urlParamString}`;

  return urlParamString;
}

const MAX_QUERY_LABEL_LENGTH = 60;

export function smartLabelForQuery(query) {
  let smartLabel = query.q;
  if (query.q.length > MAX_QUERY_LABEL_LENGTH) {
    smartLabel = `${smartLabel.substr(0, MAX_QUERY_LABEL_LENGTH)}...`;
  }
  return smartLabel;
}

export function queryPropertyHasChanged(queries, nextQueries, propName) {
  const currentProps = queries.map(q => q[propName]).reduce((allProps, prop) => allProps + prop);
  const nextProps = nextQueries.map(q => q[propName]).reduce((allProps, prop) => allProps + prop);
  const propHasChanged = currentProps !== nextProps;
  return propHasChanged;
}

<|endoftext|>


<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( this.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>use currentAccordion.config instead of this.config inside the Accordion constructor<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 */
        /** @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;

} );
<|endoftext|>


<commit_before>import assertString from './util/assertString';

/* eslint-disable max-len */
const creditCard = /^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})|62[0-9]{5,}$/;
/* eslint-enable max-len */

export default function isCreditCard(str) {
  assertString(str);
  const sanitized = str.replace(/[^0-9]+/g, '');
  if (!creditCard.test(sanitized)) {
    return false;
  }
  let sum = 0;
  let digit;
  let tmpNum;
  let shouldDouble;
  for (let i = sanitized.length - 1; i >= 0; i--) {
    digit = sanitized.substring(i, (i + 1));
    tmpNum = parseInt(digit, 10);
    if (shouldDouble) {
      tmpNum *= 2;
      if (tmpNum >= 10) {
        sum += ((tmpNum % 10) + 1);
      } else {
        sum += tmpNum;
      }
    } else {
      sum += tmpNum;
    }
    shouldDouble = !shouldDouble;
  }
  return !!((sum % 10) === 0 ? sanitized : false);
}
<commit_msg>Fix Union Pay credit card regex<commit_after>import assertString from './util/assertString';

/* eslint-disable max-len */
const creditCard = /^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})|62[0-9]{14}$/;
/* eslint-enable max-len */

export default function isCreditCard(str) {
  assertString(str);
  const sanitized = str.replace(/[^0-9]+/g, '');
  if (!creditCard.test(sanitized)) {
    return false;
  }
  let sum = 0;
  let digit;
  let tmpNum;
  let shouldDouble;
  for (let i = sanitized.length - 1; i >= 0; i--) {
    digit = sanitized.substring(i, (i + 1));
    tmpNum = parseInt(digit, 10);
    if (shouldDouble) {
      tmpNum *= 2;
      if (tmpNum >= 10) {
        sum += ((tmpNum % 10) + 1);
      } else {
        sum += tmpNum;
      }
    } else {
      sum += tmpNum;
    }
    shouldDouble = !shouldDouble;
  }
  return !!((sum % 10) === 0 ? sanitized : false);
}
<|endoftext|>


<commit_before>console.log('main starting');
var a = require('./a.js');
var b = require('./b.js');
console.log('in main, a.done=%j, b.done=%j', a.done, b.done);
<commit_msg>main.js: export module itself - for inspection<commit_after>console.log('main starting');
var a = require('./a.js');
var b = require('./b.js');
console.log('in main, a.done=%j, b.done=%j', a.done, b.done);

exports.module = module;
<|endoftext|>


<commit_before>const
  fs = require('fs'),
  fse = require('fs-extra'),
  { green } = require('chalk')

const
  appPaths = require('./app-paths')

module.exports = function legacyValidations (cfg) {
  let file, content, error = false

  file = appPaths.resolve.app(cfg.sourceFiles.indexHtmlTemplate)
  if (!fs.existsSync(file)) {
    console.log('⚠  Missing /src/index.template.html file...')
    console.log()
    error = true
  }
  content = fs.readFileSync(file, 'utf-8')
  if (content.indexOf('<base href') > -1) {
    console.log(`⚠  Your newer Quasar CLI requires a minor change to /src/index.template.html
   Please remove this tag completely:
  <base href="<%= htmlWebpackPlugin.options.appBase %>">
  `)
    console.log()
    error = true
  }

  if (content.indexOf(`chunk.initial ? 'preload' : 'prefetch'`) > -1) {
    console.log(`\n⚠  Your newer Quasar CLI requires a minor change to /src/index.template.html
   Please remove this section completely:

  <% if (!['cordova', 'electron'].includes(htmlWebpackPlugin.options.ctx.modeName) && htmlWebpackPlugin.options.ctx.prod) {
      for (var chunk of webpack.chunks) {
        for (var file of chunk.files) {
          if (file.match(/\.(js|css)$/)) { %>
    <link rel="<%= chunk.initial ? 'preload' : 'prefetch' %>" href="<%= file %>" as="<%= file.match(/\.css$/)? 'style' : 'script' %>">
  <% }}}} %>
  `)
    console.log()
    error = true
  }

  if (content.indexOf('<link rel="manifest"') > -1) {
    console.log(`\n⚠  Your newer Quasar CLI requires a minor change to /src/index.template.html
   Please remove this section completely:

   <% if (htmlWebpackPlugin.options.ctx.mode.pwa) { %>

    .....
   <% } %>
  `)
    console.log()
    error = true
  }

  if (content.indexOf('htmlWebpackPlugin.options.headScripts') > -1) {
    console.log(`\n⚠  Your newer Quasar CLI requires a minor change to /src/index.template.html
   Please remove this section completely:

   <%= htmlWebpackPlugin.options.headScripts %>
  `)
    console.log()
    error = true
  }

  if (content.indexOf('htmlWebpackPlugin.options.bodyScripts') > -1) {
    console.log(`\n⚠  Your newer Quasar CLI requires a minor change to /src/index.template.html
   Please remove this section completely:

   <%= htmlWebpackPlugin.options.bodyScripts %>
  `)
    console.log()
    error = true
  }

  file = appPaths.resolve.app(cfg.sourceFiles.rootComponent)
  content = fs.readFileSync(file, 'utf-8')
  if (content.indexOf('id="q-app"') === -1) {
    console.log(`\n⚠  Your newer Quasar CLI requires a minor change to the root component:
   ${file}

  Please add: id="q-app"
  to the outermost HTML element of the template.

${green('Example:')}
  <template>

      ...

  </template>
`)
    error = true
  }

  if (error) {
    process.exit(1)
  }

  file = appPaths.resolve.app(cfg.sourceFiles.router)
  content = fs.readFileSync(file, 'utf-8')
  if (cfg.ctx.mode.ssr && content.indexOf('export default function') === -1) {
    console.log(`\n⚠  In order to build with SSR mode you need a minor change to the ROUTER file
   This won't break other build modes after you change it.

   ${file}

 You need to have a default export set to "function ({ store })" which returns a new
 instance of Router instead of default exporting the Router instance itself.

${green('OLD WAY:')}
  import Vue from 'vue'
  import VueRouter from 'vue-router'
  import routes from './routes'
  Vue.use(VueRouter)

  // in the new way, we'll wrap the instantiation into:
  // export default function ({ store }) --> store is optional
  const Router = new VueRouter({
    scrollBehavior: () => ({ y: 0 }),
    routes,
    // Leave these as they are and change from quasar.conf.js instead!
    mode: process.env.VUE_ROUTER_MODE,
    base: process.env.VUE_ROUTER_BASE,
  })

  // in the new way, this will be no more
  export default Router

${green('NEW WAY:')}
  import Vue from 'vue'
  import VueRouter from 'vue-router'
  import routes from './routes'
  Vue.use(VueRouter)

  // DO NOT import the store here as you will receive it as
  // parameter in the default exported function:

  export default function (/* { store } */) {
    // IMPORTANT! Instantiate Router inside this function

    const Router = new VueRouter({
      scrollBehavior: () => ({ y: 0 }),
      routes,
      // Leave these as they are and change from quasar.conf.js instead!
      mode: process.env.VUE_ROUTER_MODE,
      base: process.env.VUE_ROUTER_BASE,
    })

    return Router
  }
  `)
    console.log()
    process.exit(1)
  }

  if (cfg.store && cfg.ctx.mode.ssr) {
    file = appPaths.resolve.app(cfg.sourceFiles.store)
    content = fs.readFileSync(file, 'utf-8')
    if (content.indexOf('export default function') === -1) {
      console.log(`\n⚠  In order to build with SSR mode you need a minor change to the STORE file
   This won't break other build modes after you change it.

   ${file}

  You need to have a default export set to "function ()" which returns a new
  instance of Vuex Store instead of default exporting the Store instance itself.

${green('OLD WAY:')}
  import Vue from 'vue'
  import Vuex from 'vuex'
  import example from './module-example'
  Vue.use(Vuex)

  // in the new way, we'll wrap the instantiation into:
  // export default function ()
  const store = new Vuex.Store({
    modules: {
      example
    }
  })

  // in the new way, this will be no more
  export default store

${green('NEW WAY:')}
  import Vue from 'vue'
  import Vuex from 'vuex'
  import example from './module-example'
  Vue.use(Vuex)

  export default function () {
    // IMPORTANT! Instantiate Store inside this function

    const Store = new Vuex.Store({
      modules: {
        example
      }
    })

    return Store
  }
  `)
      console.log()
      process.exit(1)
    }
  }

  file = appPaths.resolve.app('.babelrc')
  if (!fs.existsSync(file)) {
    console.log('⚠  Missing .babelrc file...')
    console.log()
    process.exit(1)
  }
  content = fs.readFileSync(file, 'utf-8')
  if (content.indexOf('"transform-runtime"') > -1) {
    console.log()
    console.log(' ⚠  WARNING')
    console.log(` Your newer Quasar CLI requires a change to .babelrc file.`)
    console.log(` Doing it automatically. Please review the changes.`)
    console.log()

    fse.copySync(
      appPaths.resolve.cli('templates/app/babelrc'),
      appPaths.resolve.app('.babelrc')
    )
  }
}
<commit_msg>fix: bug with q-app detection #150<commit_after>const
  fs = require('fs'),
  fse = require('fs-extra'),
  { green } = require('chalk')

const
  appPaths = require('./app-paths')

module.exports = function legacyValidations (cfg) {
  let file, content, error = false

  file = appPaths.resolve.app(cfg.sourceFiles.indexHtmlTemplate)
  if (!fs.existsSync(file)) {
    console.log('⚠  Missing /src/index.template.html file...')
    console.log()
    error = true
  }
  content = fs.readFileSync(file, 'utf-8')
  if (content.indexOf('<base href') > -1) {
    console.log(`⚠  Your newer Quasar CLI requires a minor change to /src/index.template.html
   Please remove this tag completely:
  <base href="<%= htmlWebpackPlugin.options.appBase %>">
  `)
    console.log()
    error = true
  }

  if (content.indexOf(`chunk.initial ? 'preload' : 'prefetch'`) > -1) {
    console.log(`\n⚠  Your newer Quasar CLI requires a minor change to /src/index.template.html
   Please remove this section completely:

  <% if (!['cordova', 'electron'].includes(htmlWebpackPlugin.options.ctx.modeName) && htmlWebpackPlugin.options.ctx.prod) {
      for (var chunk of webpack.chunks) {
        for (var file of chunk.files) {
          if (file.match(/\.(js|css)$/)) { %>
    <link rel="<%= chunk.initial ? 'preload' : 'prefetch' %>" href="<%= file %>" as="<%= file.match(/\.css$/)? 'style' : 'script' %>">
  <% }}}} %>
  `)
    console.log()
    error = true
  }

  if (content.indexOf('<link rel="manifest"') > -1) {
    console.log(`\n⚠  Your newer Quasar CLI requires a minor change to /src/index.template.html
   Please remove this section completely:

   <% if (htmlWebpackPlugin.options.ctx.mode.pwa) { %>

    .....
   <% } %>
  `)
    console.log()
    error = true
  }

  if (content.indexOf('htmlWebpackPlugin.options.headScripts') > -1) {
    console.log(`\n⚠  Your newer Quasar CLI requires a minor change to /src/index.template.html
   Please remove this section completely:

   <%= htmlWebpackPlugin.options.headScripts %>
  `)
    console.log()
    error = true
  }

  if (content.indexOf('htmlWebpackPlugin.options.bodyScripts') > -1) {
    console.log(`\n⚠  Your newer Quasar CLI requires a minor change to /src/index.template.html
   Please remove this section completely:

   <%= htmlWebpackPlugin.options.bodyScripts %>
  `)
    console.log()
    error = true
  }

  file = appPaths.resolve.app(cfg.sourceFiles.rootComponent)
  content = fs.readFileSync(file, 'utf-8')
  if (content.indexOf('id="q-app"') === -1 && content.indexOf('#q-app') === -1) {
    console.log(`\n⚠  Your newer Quasar CLI requires a minor change to the root component:
   ${file}

  Please add: id="q-app" (or write #q-app if using Pug)
  to the outermost HTML element of the template.

${green('Example:')}
  <template>

      ...

  </template>
`)
    error = true
  }

  if (error) {
    process.exit(1)
  }

  file = appPaths.resolve.app(cfg.sourceFiles.router)
  content = fs.readFileSync(file, 'utf-8')
  if (cfg.ctx.mode.ssr && content.indexOf('export default function') === -1) {
    console.log(`\n⚠  In order to build with SSR mode you need a minor change to the ROUTER file
   This won't break other build modes after you change it.

   ${file}

 You need to have a default export set to "function ({ store })" which returns a new
 instance of Router instead of default exporting the Router instance itself.

${green('OLD WAY:')}
  import Vue from 'vue'
  import VueRouter from 'vue-router'
  import routes from './routes'
  Vue.use(VueRouter)

  // in the new way, we'll wrap the instantiation into:
  // export default function ({ store }) --> store is optional
  const Router = new VueRouter({
    scrollBehavior: () => ({ y: 0 }),
    routes,
    // Leave these as they are and change from quasar.conf.js instead!
    mode: process.env.VUE_ROUTER_MODE,
    base: process.env.VUE_ROUTER_BASE,
  })

  // in the new way, this will be no more
  export default Router

${green('NEW WAY:')}
  import Vue from 'vue'
  import VueRouter from 'vue-router'
  import routes from './routes'
  Vue.use(VueRouter)

  // DO NOT import the store here as you will receive it as
  // parameter in the default exported function:

  export default function (/* { store } */) {
    // IMPORTANT! Instantiate Router inside this function

    const Router = new VueRouter({
      scrollBehavior: () => ({ y: 0 }),
      routes,
      // Leave these as they are and change from quasar.conf.js instead!
      mode: process.env.VUE_ROUTER_MODE,
      base: process.env.VUE_ROUTER_BASE,
    })

    return Router
  }
  `)
    console.log()
    process.exit(1)
  }

  if (cfg.store && cfg.ctx.mode.ssr) {
    file = appPaths.resolve.app(cfg.sourceFiles.store)
    content = fs.readFileSync(file, 'utf-8')
    if (content.indexOf('export default function') === -1) {
      console.log(`\n⚠  In order to build with SSR mode you need a minor change to the STORE file
   This won't break other build modes after you change it.

   ${file}

  You need to have a default export set to "function ()" which returns a new
  instance of Vuex Store instead of default exporting the Store instance itself.

${green('OLD WAY:')}
  import Vue from 'vue'
  import Vuex from 'vuex'
  import example from './module-example'
  Vue.use(Vuex)

  // in the new way, we'll wrap the instantiation into:
  // export default function ()
  const store = new Vuex.Store({
    modules: {
      example
    }
  })

  // in the new way, this will be no more
  export default store

${green('NEW WAY:')}
  import Vue from 'vue'
  import Vuex from 'vuex'
  import example from './module-example'
  Vue.use(Vuex)

  export default function () {
    // IMPORTANT! Instantiate Store inside this function

    const Store = new Vuex.Store({
      modules: {
        example
      }
    })

    return Store
  }
  `)
      console.log()
      process.exit(1)
    }
  }

  file = appPaths.resolve.app('.babelrc')
  if (!fs.existsSync(file)) {
    console.log('⚠  Missing .babelrc file...')
    console.log()
    process.exit(1)
  }
  content = fs.readFileSync(file, 'utf-8')
  if (content.indexOf('"transform-runtime"') > -1) {
    console.log()
    console.log(' ⚠  WARNING')
    console.log(` Your newer Quasar CLI requires a change to .babelrc file.`)
    console.log(` Doing it automatically. Please review the changes.`)
    console.log()

    fse.copySync(
      appPaths.resolve.cli('templates/app/babelrc'),
      appPaths.resolve.app('.babelrc')
    )
  }
}
<|endoftext|>


<commit_before>var app = require('../../src/app');
var request = require('supertest')(app);
var async = require('async');
var status = require('http-status-codes');
var _ = require('lodash');

var chai = require('chai');
var expect = chai.expect;

var utils = require('./testUtils');
var testScouts = require('./testScouts');

describe('using preference and assignments', function () {
  var generatedUsers, generatedScouts, generatedBadges, generatedOfferings,
    events, purchasables;
  var preferences = {};
  var badId = utils.badId;

  before(function (done) {
    utils.dropDb(done);
  });

  before(function (done) {
    this.timeout(10000);
    utils.generateTokens(['admin', 'teacher', 'coordinator', 'coordinator2'], function (err, generatedTokens) {
      if (err) return done(err);
      generatedUsers = generatedTokens;
      return done();
    });
  });

  before(function (done) {
    utils.createEvents(generatedUsers.admin.token, function (err, items) {
      if (err) return done(err);
      events = items;
      return done();
    });
  });

  before(function (done) {
    utils.createBadges(generatedUsers.admin.token, function (err, badges) {
      if (err) return done(err);
      generatedBadges = badges;
      return done();
    });
  });

  before(function (done) {
    var defaultPostData = {
      price: 10,
      periods: [1, 2, 3],
      duration: 1
    };

    utils.createOfferingsForEvent(events[0], generatedBadges, defaultPostData, generatedUsers.admin.token, function (err, offerings) {
      if (err) return done(err);
      generatedOfferings = offerings;
      return done();
    });
  });

  before(function (done) {
    utils.createScoutsForUser(generatedUsers.coordinator, testScouts(5), generatedUsers.coordinator.token, function (err, scouts) {
      if (err) return done(err);
      generatedScouts = scouts;
      return done();
    });
  });

  before(function (done) {
    utils.createPurchasablesForEvent(events[0].id, function (err, items) {
      if (err) return done(err);
      purchasables = items;
      return done();
    });
  });

  before(function (done) {
    var postData = [{
      offering: generatedOfferings[0].id,
      rank: 1
    }, {
      offering: generatedOfferings[1].id,
      rank: 2
    }];

    async.forEachOfSeries(generatedScouts, function (scout, index, cb) {
      var registrationId;

      async.series([
        function (next) {
          request.post('/api/scouts/' + scout.id + '/registrations')
            .set('Authorization', generatedUsers.coordinator.token)
            .send({ event_id: events[0].id })
            .expect(status.CREATED)
            .end(function (err, res) {
              if (err) return done(err);
              registrationId = res.body.registration.id;
              return next();
            });
        },
        function (next) {
          request.post('/api/scouts/' + scout.id + '/registrations/' + registrationId + '/purchases')
            .set('Authorization', generatedUsers.coordinator.token)
            .send({
              purchasable: purchasables[0].id,
              quantity: 2,
              size: 'l'
            })
            .expect(status.CREATED, next);
        },
        function (next) {
          request.post('/api/scouts/' + scout.id + '/registrations/' + registrationId + '/preferences')
            .set('Authorization', generatedUsers.admin.token)
            .send(postData)
            .expect(status.CREATED)
            .end(function (err, res) {
              if (err) return done(err);
              preferences[registrationId] = res.body.registration.preferences;
              return next();
            });
        },
        function (next) {
          var postData = [{
            periods: [1],
            offering: generatedOfferings[0].id
          }, {
            periods: [2, 3],
            offering: generatedOfferings[1].id
          }];

          request.post('/api/scouts/' + scout.id + '/registrations/' + registrationId + '/assignments')
            .set('Authorization', generatedUsers.admin.token)
            .send(postData)
            .expect(status.CREATED, next);
        }
      ], cb);
    }, done);
  });

  after(function (done) {
    utils.dropDb(done);
  });

  describe('getting scouts that are registered for an event', function () {

    it('should get all registrations for an event', function (done) {
      request.get('/api/events/' + events[0].id + '/registrations')
        .set('Authorization', generatedUsers.admin.token)
        .expect(status.OK)
        .end(function (err, res) {
          if (err) return done(err);
          _.forEach(res.body, function (registration) {
            expect(registration.scout_id).to.exist;
            expect(registration.scout).to.exist;
            expect(registration.scout.firstname).to.exist;
            expect(registration.scout.lastname).to.exist;
            expect(registration.scout.troop).to.exist;
            expect(registration.preferences).to.have.lengthOf(2);
            expect(registration.assignments).to.have.lengthOf(2);
            expect(registration.purchases).to.have.lengthOf(1);

            _.forEach(registration.preferences, function (preference) {
              expect(preference.badge.name).to.exist;
              expect(preference.details.rank).to.exist;
            });

            _.forEach(registration.purchases, function (purchase) {
              expect(purchase.item).to.exist;
              expect(purchase.price).to.exist;
              expect(purchase.details.quantity).to.exist;
              expect(purchase.details.size).to.exist;
            });

            _.forEach(registration.assignments, function (assignment) {
              expect(assignment.badge.name).to.exist;
              expect(assignment.details.periods).to.exist;
              expect(assignment.price).to.exist;
            });
          });
          return done();
        });
    });

    it('should allow teachers to see registrations', function (done) {
      request.get('/api/events/' + events[0].id + '/registrations')
        .set('Authorization', generatedUsers.teacher.token)
        .expect(status.OK, done);
    });

    it('should not allow coordinators to see registrations', function (done) {
      request.get('/api/events/' + events[0].id + '/registrations')
        .set('Authorization', generatedUsers.coordinator.token)
        .expect(status.UNAUTHORIZED, done);
    });

    it('should fail gracefully for a bad id', function (done) {
      request.get('/api/events/' + badId + '/registrations')
        .set('Authorization', generatedUsers.teacher.token)
        .expect(status.OK, done);
    });
  });

});<commit_msg>Tests for expected result<commit_after>var app = require('../../src/app');
var request = require('supertest')(app);
var async = require('async');
var status = require('http-status-codes');
var _ = require('lodash');

var chai = require('chai');
var expect = chai.expect;

var utils = require('./testUtils');
var testScouts = require('./testScouts');

describe.only('using preference and assignments', function () {
  var generatedUsers, generatedScouts, generatedBadges, generatedOfferings,
    events, purchasables;
  var preferences = {};
  var badId = utils.badId;

  before(function (done) {
    utils.dropDb(done);
  });

  before(function (done) {
    this.timeout(10000);
    utils.generateTokens(['admin', 'teacher', 'coordinator', 'coordinator2'], function (err, generatedTokens) {
      if (err) return done(err);
      generatedUsers = generatedTokens;
      return done();
    });
  });

  before(function (done) {
    utils.createEvents(generatedUsers.admin.token, function (err, items) {
      if (err) return done(err);
      events = items;
      return done();
    });
  });

  before(function (done) {
    utils.createBadges(generatedUsers.admin.token, function (err, badges) {
      if (err) return done(err);
      generatedBadges = badges;
      return done();
    });
  });

  before(function (done) {
    var defaultPostData = {
      price: 10,
      periods: [1, 2, 3],
      duration: 1
    };

    utils.createOfferingsForEvent(events[0], generatedBadges, defaultPostData, generatedUsers.admin.token, function (err, offerings) {
      if (err) return done(err);
      generatedOfferings = offerings;
      return done();
    });
  });

  before(function (done) {
    utils.createScoutsForUser(generatedUsers.coordinator, testScouts(5), generatedUsers.coordinator.token, function (err, scouts) {
      if (err) return done(err);
      generatedScouts = scouts;
      return done();
    });
  });

  before(function (done) {
    utils.createScoutsForUser(generatedUsers.coordinator2, testScouts(5), generatedUsers.coordinator2.token, function (err, scouts) {
      if (err) return done(err);
      return done();
    });
  });

  before(function (done) {
    utils.createPurchasablesForEvent(events[0].id, function (err, items) {
      if (err) return done(err);
      purchasables = items;
      return done();
    });
  });

  before(function (done) {
    var postData = [{
      offering: generatedOfferings[0].id,
      rank: 1
    }, {
      offering: generatedOfferings[1].id,
      rank: 2
    }];

    async.forEachOfSeries(generatedScouts, function (scout, index, cb) {
      var registrationId;

      async.series([
        function (next) {
          request.post('/api/scouts/' + scout.id + '/registrations')
            .set('Authorization', generatedUsers.coordinator.token)
            .send({ event_id: events[0].id })
            .expect(status.CREATED)
            .end(function (err, res) {
              if (err) return done(err);
              registrationId = res.body.registration.id;
              return next();
            });
        },
        function (next) {
          request.post('/api/scouts/' + scout.id + '/registrations/' + registrationId + '/purchases')
            .set('Authorization', generatedUsers.coordinator.token)
            .send({
              purchasable: purchasables[0].id,
              quantity: 2,
              size: 'l'
            })
            .expect(status.CREATED, next);
        },
        function (next) {
          request.post('/api/scouts/' + scout.id + '/registrations/' + registrationId + '/preferences')
            .set('Authorization', generatedUsers.admin.token)
            .send(postData)
            .expect(status.CREATED)
            .end(function (err, res) {
              if (err) return done(err);
              preferences[registrationId] = res.body.registration.preferences;
              return next();
            });
        },
        function (next) {
          var postData = [{
            periods: [1],
            offering: generatedOfferings[0].id
          }, {
            periods: [2, 3],
            offering: generatedOfferings[1].id
          }];

          request.post('/api/scouts/' + scout.id + '/registrations/' + registrationId + '/assignments')
            .set('Authorization', generatedUsers.admin.token)
            .send(postData)
            .expect(status.CREATED, next);
        }
      ], cb);
    }, done);
  });

  after(function (done) {
    utils.dropDb(done);
  });

  describe('getting scouts that are registered for an event', function () {

    it('should get all registrations for an event', function (done) {
      request.get('/api/events/' + events[0].id + '/registrations')
        .set('Authorization', generatedUsers.admin.token)
        .expect(status.OK)
        .end(function (err, res) {
          if (err) return done(err);
          _.forEach(res.body, function (registration) {
            expect(registration.scout_id).to.exist;
            expect(registration.scout).to.exist;
            expect(registration.scout.firstname).to.exist;
            expect(registration.scout.lastname).to.exist;
            expect(registration.scout.troop).to.exist;
            expect(registration.preferences).to.have.lengthOf(2);
            expect(registration.assignments).to.have.lengthOf(2);
            expect(registration.purchases).to.have.lengthOf(1);

            _.forEach(registration.preferences, function (preference) {
              expect(preference.badge.name).to.exist;
              expect(preference.details.rank).to.exist;
            });

            _.forEach(registration.purchases, function (purchase) {
              expect(purchase.item).to.exist;
              expect(purchase.price).to.exist;
              expect(purchase.details.quantity).to.exist;
              expect(purchase.details.size).to.exist;
            });

            _.forEach(registration.assignments, function (assignment) {
              expect(assignment.badge.name).to.exist;
              expect(assignment.details.periods).to.exist;
              expect(assignment.price).to.exist;
            });
          });
          return done();
        });
    });

    it('should allow teachers to see registrations', function (done) {
      request.get('/api/events/' + events[0].id + '/registrations')
        .set('Authorization', generatedUsers.teacher.token)
        .expect(status.OK, done);
    });

    it('should not allow coordinators to see registrations', function (done) {
      request.get('/api/events/' + events[0].id + '/registrations')
        .set('Authorization', generatedUsers.coordinator.token)
        .expect(status.UNAUTHORIZED, done);
    });

    it('should fail gracefully for a bad id', function (done) {
      request.get('/api/events/' + badId + '/registrations')
        .set('Authorization', generatedUsers.teacher.token)
        .expect(status.OK, done);
    });
  });

  describe('getting all scouts and all registrations', function (done) {
    it('should get all scouts of the site', function (done) {
      request.get('/api/scouts')
        .set('Authorization', generatedUsers.admin.token)
        .expect(status.OK)
        .end(function (err, res) {
          if (err) return done(err);
          var scouts = res.body;
          expect(scouts).to.have.lengthOf(10);
          _.forEach(scouts, function (scout) {
            expect(scout.firstname).to.exist;
            expect(scout.lastname).to.exist;
            expect(scout.troop).to.exist;
            expect(scout.emergency_name).to.exist;
            expect(scout.emergency_phone).to.exist;
            expect(scout.emergency_relation).to.exist;
            expect(scout.notes).to.exist;
            expect(scout.registrations).to.exist;
            expect(scout.user).to.exist;
          });
          return done();
        });
    });

    it('should get some details of the registration', function (done) {
      request.get('/api/scouts')
        .set('Authorization', generatedUsers.admin.token)
        .expect(status.OK)
        .end(function (err, res) {
          if (err) return done(err);
          var scouts = res.body;
          expect(scouts).to.have.lengthOf(10);
          _.forEach(scouts, function (scout) {
            _.forEach(scout.registrations, function (registration) {
              expect(registration.registration_id).to.exist;
              expect(registration.event_id).to.exist;
              expect(registration.assignments).to.exist;
              expect(registration.purchases).to.exist;
            });
          });
          return done();
        });
    });

    it('should get some details of the user', function (done) {
      request.get('/api/scouts')
        .set('Authorization', generatedUsers.admin.token)
        .expect(status.OK)
        .end(function (err, res) {
          if (err) return done(err);
          var scouts = res.body;
          expect(scouts).to.have.lengthOf(10);
          _.forEach(scouts, function (scout) {
            expect(scout.user.name).to.exist;
            expect(scout.user.email).to.exist;
            expect(scout.user.user_id).to.exist;
          });
          return done();
        });
    });

    it('should allow teachers to get a list of scouts', function (done) {
      request.get('/api/scouts')
        .set('Authorization', generatedUsers.teacher.token)
        .expect(status.OK, done);
    });

    it('should not allow coordinators to access', function (done) {
      request.get('/api/scouts')
        .set('Authorization', generatedUsers.coordinator.token)
        .expect(status.UNAUTHORIZED, done);
    })
  });
});<|endoftext|>


<commit_before>/* global atom */

// Dependencies
var helpers = require('atom-linter')
var linter = require('./utils/linter')

module.exports = function () {
  var self = this

  return {
    grammarScopes: self.scope,
    scope: 'file',
    lintOnFly: true,
    regex:
      '^.*?:(?<line>[0-9]+):(?<col>[0-9]+):' +
      '((?<message>.*?(?=\\())((?<error>.+undefined.*?)|(?<warning>.*?)))$',
    lint: function (textEditor) {
      var filePath = textEditor.getPath()
      var fileContent = textEditor.getText()
      var settings = self.cache.get(filePath)

      // Sane check
      if (!settings || !settings.args || !settings.styleObj) {
        atom.notifications.addWarning('Something went wrong internally.', {
          detail: 'No sweat, just re-open this file and this annoying warning shouldn\'t appear anymore',
          dismissable: true
        })

        return []
      }

      var boundLinter = linter.bind({
        regex: this.regex,
        file: {
          path: filePath,
          content: fileContent
        }
      })

      var execPath = settings.styleObj.execPath
      var args = settings.args.concat(['-'])
      var opts = { stdin: fileContent, throwOnStdErr: false }

      return helpers.execNode(execPath, args, opts)
      .then(boundLinter)
      .catch(function (err) {
        atom.notifications.addError('Couldn\'t execute linter bin', {
          detail: err.message,
          dismissable: true
        })
      })
    }
  }
}
<commit_msg>pass the settings of the package to the boundLinter<commit_after>/* global atom */

// Dependencies
var helpers = require('atom-linter')
var linter = require('./utils/linter')

module.exports = function () {
  var self = this

  return {
    grammarScopes: self.scope,
    scope: 'file',
    lintOnFly: true,
    regex:
      '^.*?:(?<line>[0-9]+):(?<col>[0-9]+):' +
      '((?<message>.*?(?=\\())((?<error>.+undefined.*?)|(?<warning>.*?)))$',
    lint: function (textEditor) {
      var filePath = textEditor.getPath()
      var fileContent = textEditor.getText()
      var settings = self.cache.get(filePath)
      var config = self.cache.get('config')

      // Sane check
      if (!settings || !settings.args || !settings.styleObj) {
        atom.notifications.addWarning('Something went wrong internally.', {
          detail: 'No sweat, just re-open this file and this annoying warning shouldn\'t appear anymore',
          dismissable: true
        })

        return []
      }

      var boundLinter = linter.bind({
        regex: this.regex,
        file: {
          path: filePath,
          content: fileContent
        },
        config: config
      })

      var execPath = settings.styleObj.execPath
      var args = settings.args.concat(['-'])
      var opts = { stdin: fileContent, throwOnStdErr: false }

      return helpers.execNode(execPath, args, opts)
      .then(boundLinter)
      .catch(function (err) {
        atom.notifications.addError('Couldn\'t execute linter bin', {
          detail: err.message,
          dismissable: true
        })
      })
    }
  }
}
<|endoftext|>


<commit_before>module.exports = {
    temperature: function (temp) {
        var text;

        if (temp > 80) {
            text = "Your coffee is way to hot. Blow before drinking!";
        }

        if (temp >= 60 && temp <= 80) {
            text = "Your coffee is perfect to drink.";
        }

        if (temp < 60) {
            text = "Your coffee becomes Iced Coffee.";
        }

        return ":coffee: " + text;
    }
};
<commit_msg>Update notifications<commit_after>module.exports = {
    temperature: function (temp) {
        var text;

        if (temp > 80) {
            text = "Your coffee is way to hot. Blow before drinking! Or wait for my cue.";
        }

        if (temp >= 60 && temp <= 80) {
            text = "Your coffee is perfect to drink.";
        }

        if (temp < 60) {
            text = "Your coffee becomes Iced Coffee. You should finish it.";
        }

        if (temp < 40) {
            text = "Your coffee has been declared dead. Make a new one.";
        }

        return ":coffee: " + text;
    }
};
<|endoftext|>


<commit_before>import React, { PropTypes } from 'react';
import Heading from './Heading.js';
import moment from 'moment-jalaali';
import { PersianNumber } from 'react-persian';
import Day from './Day.js';

export default class Calendar extends React.Component {
    static propsTypes = {
        startDayOfWeek: PropTypes.number,
        onSelect: PropTypes.func.isRequired,
        max: PropTypes.oneOfType([
            PropTypes.object,
            PropTypes.string
        ]),
        min: PropTypes.oneOfType([
            PropTypes.object,
            PropTypes.string
        ]),
        prevMonthElement: PropTypes.element.isRequired,
        nextMonthElement: PropTypes.element.isRequired,
    };

    componentWillMount() {
        let month;
        if (this.props.month) {
            month = this.props.month;
        } else {
            month = moment();
        }

        month.locale('fa');

        this.state = {
            month
        };
    }

    nextMonth() {
        const { month } = this.state;
        month.add(1, 'months')
        this.forceUpdate();
    }

    prevMonth() {
        const { month } = this.state;
        month.subtract(1, 'months')
        this.forceUpdate();
    }

    handleClick(day) {
        const state = this.state;
        if (day.format('jMM') !== state.month.format('jMM')) {
            state.month = day.clone();
        }

        this.setState({
            ...state,
            selectedDay: day
        });

        this.props.onSelect(day);
    }

    render() {
        const { month, selectedDay } = this.state;
        const { prevMonthElement, nextMonthElement, min, max } = this.props;
        const days = [];

        const current = month.clone().startOf('jMonth');
        const end = month.clone().endOf('jMonth');

        // Set start to the first day of week in the last month
        current.subtract((current.day() + 1) % 7, 'days');

        // Set end to the last day of week in the next month
        end.add(6 - (end.day() + 1) % 7, 'days');

        while (current.isBefore(end)) {
            days.push(current.clone());
            current.add(1, 'days');
        }

        return

            <Heading month={month}
                     onPrev={this.prevMonth.bind(this)}
                     onNext={this.nextMonth.bind(this)}
                     prevMonthElement={prevMonthElement}
                     nextMonthElement={nextMonthElement}/>

                {
                    dayOfWeekNames.map((name, key) =>
{name}
)
                }

                {
                    days.map((day, key) => {
                        const isCurrentMonth = day.format('jMM') === month.format('jMM');
                        const isDisabled = (min ? day.isBefore(min) : false) || (max ? day.isAfter(max) : false);

                        return <Day
                            key={key}
                            selected={day.isSame(selectedDay, 'day')}
                            handleClick={this.handleClick.bind(this, day)}
                            day={day}
                            isDisabled={isDisabled}
                            isCurrentMonth={isCurrentMonth}/>;
                    })
                }

;
    }
}

// Load Persian localisation
moment.loadPersian();

// Day of week names for use in date-picker heading
const dayOfWeekNames = [
    'ش',
    'ی',
    'د',
    'س',
    'چ',
    'پ',
    'ج'
];
<commit_msg>fix: Fix lint errors<commit_after>import React, { PropTypes } from 'react';
import Heading from './Heading.js';
import moment from 'moment-jalaali';
import Day from './Day.js';

// Load Persian localisation
moment.loadPersian();

// Day of week names for use in date-picker heading
const dayOfWeekNames = [
  'ش',
  'ی',
  'د',
  'س',
  'چ',
  'پ',
  'ج'
];

export default class Calendar extends React.Component {
  static propTypes = {
    startDayOfWeek: PropTypes.number,
    onSelect: PropTypes.func.isRequired,
    month: PropTypes.object,
    max: PropTypes.oneOfType([
      PropTypes.object,
      PropTypes.string
    ]),
    min: PropTypes.oneOfType([
      PropTypes.object,
      PropTypes.string
    ]),
    prevMonthElement: PropTypes.element.isRequired,
    nextMonthElement: PropTypes.element.isRequired
  };

  componentWillMount() {
    let month;
    if (this.props.month) {
      month = this.props.month;
    } else {
      month = moment();
    }

    month.locale('fa');

    this.state = {
      month
    };
  }

  nextMonth() {
    const { month } = this.state;
    month.add(1, 'months');
    this.forceUpdate();
  }

  prevMonth() {
    const { month } = this.state;
    month.subtract(1, 'months');
    this.forceUpdate();
  }

  handleClick(day) {
    const state = this.state;
    if (day.format('jMM') !== state.month.format('jMM')) {
      state.month = day.clone();
    }

    this.setState({
      ...state,
      selectedDay: day
    });

    this.props.onSelect(day);
  }

  render() {
    const { month, selectedDay } = this.state;
    const { prevMonthElement, nextMonthElement, min, max } = this.props;
    const days = [];

    const current = month.clone().startOf('jMonth');
    const end = month.clone().endOf('jMonth');

    // Set start to the first day of week in the last month
    current.subtract((current.day() + 1) % 7, 'days');

    // Set end to the last day of week in the next month
    end.add(6 - (end.day() + 1) % 7, 'days');

    while (current.isBefore(end)) {
      days.push(current.clone());
      current.add(1, 'days');
    }

    return (

      <Heading month={month}
               onPrev={this.prevMonth.bind(this)}
               onNext={this.nextMonth.bind(this)}
               prevMonthElement={prevMonthElement}
               nextMonthElement={nextMonthElement}/>

        {
          dayOfWeekNames.map((name, key) =>
{name}
)
        }

        {
          days.map((day, key) => {
            const isCurrentMonth = day.format('jMM') === month.format('jMM');
            const isDisabled = (min ? day.isBefore(min) : false) || (max ? day.isAfter(max) : false);

            return (<Day
              key={key}
              selected={day.isSame(selectedDay, 'day')}
              handleClick={this.handleClick.bind(this, day)}
              day={day}
              isDisabled={isDisabled}
              isCurrentMonth={isCurrentMonth}/>);
          })
        }

);
  }
}
<|endoftext|>


<commit_before>/**
 * Created by tomascaslo on 10/13/15.
 */
import EventEmitter from 'events';

const TIME = {
  SECOND_TENTHS_PER_SECOND: 10,
  SECONDS_PER_MINUTE: 60,
  SECOND_TENTHS_PER_MINUTE: 600,
  MINUTES_PER_HOUR: 60,
  SECONDS_PER_HOUR: 3600,
  SECOND_TENTHS_PER_HOUR: 36000,
  HOURS_PER_DAY: 24,

  SECOND_TENTHS_POSITION: 0,
  SECONDS_POSITION: 1,
  MINUTES_POSITION: 2,
  HOURS_POSITION: 3,
  DAYS_POSITION: 4,

  SECOND_TENTHS: 'secondTenths',
  SECONDS: 'seconds',
  MINUTES: 'minutes',
  HOURS: 'hours',
  DAYS: 'days',

  unitsInMilliseconds: {
    secondTenths: 100,
    seconds: 1000,
    minutes: 60000,
    hours: 3600000,
    days: 86400000,
  },

};

const STATUS = {
  NOT_STARTED: 'Not started',
  RUNNING: 'Running',
  PAUSED: 'Paused',
  STOPPED: 'Stopped',
};

let initialTime, endTime, currentTime;

class ThunderTimer extends EventEmitter {

  constructor(options = new Object()) {
    super();

    if (options.separator && options.separator === '.') {
      throw new Error('Separator can not be a dot.');
    }

    this.timeArray = options.initialTime || [0, 0, 0, 0, 0];
    this.isCountDown = options.isCountDown || false;
    this.thunder = null;
    this.isRunning = false;
    this.separator = options.separator || ':';
    this.status = 'Not started.';
  }

  start(updateTimeInterval = TIME.unitsInMilliseconds.secondTenths) {
    if (!this.thunder) {
      this._setIsRunning(true);
      this._setStatus(STATUS.RUNNING);
      initialTime = Date.now();

      this.thunder = setInterval(this._updateTime.bind(this), updateTimeInterval);
    }
  }

  pause() {
    this._setIsRunning(false);
    this._setStatus(STATUS.PAUSED);

    return this.timeArray;
  }

  stop() {
    this._setIsRunning(false);
    this._setStatus(STATUS.STOPPED);
    clearInterval(this.thunder);

    return this.timeArray;
  }

  getTime(pretty = false) {
    return pretty ? this._prettify() : this.timeArray.reverse().join(this.separator);
  }

  getStatus(status) {
    return this.status;
  }

  _setIsRunning(isRunning) {
    this.isRunning = isRunning;
  }

  _setStatus(status) {
    this.status = status;
  }

  _updateTime() {
    currentTime = Date.now() - initialTime;

    this.timeArray[4] = Math.floor(currentTime / TIME.unitsInMilliseconds.days);
    this.timeArray[3] = Math.floor((currentTime % TIME.unitsInMilliseconds.days) / TIME.unitsInMilliseconds.hours);
    this.timeArray[2] = Math.floor((currentTime % TIME.unitsInMilliseconds.hours) / TIME.unitsInMilliseconds.minutes);
    this.timeArray[1] = Math.floor((currentTime % TIME.unitsInMilliseconds.minutes) / TIME.unitsInMilliseconds.seconds);
    this.timeArray[0] = (currentTime % TIME.unitsInMilliseconds.seconds) / TIME.unitsInMilliseconds.secondTenths;
  }

  _prettify() {
    return this.timeArray[TIME.SECONDS_POSITION] + ' ' + TIME.SECONDS + ', ' +
            this.timeArray[TIME.MINUTES_POSITION] + ' ' + TIME.MINUTES + ', ' +
            this.timeArray[TIME.HOURS_POSITION] + ' ' + TIME.HOURS + ', ' +
            this.timeArray[TIME.DAYS_POSITION] + ' ' + TIME.DAYS;
  }

}

module.exports = ThunderTimer;<commit_msg>add in code doc and a little refactor<commit_after>/**
 * @file ThunderTimer class implementation. ThunderTimer creates a timer that can be managed with methods like ThunderTimer.start(),
 * ThunderTimer.pause(), ThunderTimer.stop(), among others.
 * @author Tomás Castro
 * @version 0.0.1
 */
import EventEmitter from 'events';

const TIME = {
  SECOND_TENTHS_PER_SECOND: 10,
  SECONDS_PER_MINUTE: 60,
  SECOND_TENTHS_PER_MINUTE: 600,
  MINUTES_PER_HOUR: 60,
  SECONDS_PER_HOUR: 3600,
  SECOND_TENTHS_PER_HOUR: 36000,
  HOURS_PER_DAY: 24,

  SECOND_TENTHS_POSITION: 0,
  SECONDS_POSITION: 1,
  MINUTES_POSITION: 2,
  HOURS_POSITION: 3,
  DAYS_POSITION: 4,

  SECOND_TENTHS: 'secondTenths',
  SECONDS: 'seconds',
  MINUTES: 'minutes',
  HOURS: 'hours',
  DAYS: 'days',

  unitsInMilliseconds: {
    secondTenths: 100,
    seconds: 1000,
    minutes: 60000,
    hours: 3600000,
    days: 86400000,
  },

};

const STATUS = {
  NOT_STARTED: 'Not started',
  RUNNING: 'Running',
  PAUSED: 'Paused',
  STOPPED: 'Stopped',
};

let initialTime;
let endTime;
let currentTime;

/**
 * ThunderTimer class.
 *
 * @extends EventEmitter
 * @class
 * @property {number[]} timeArray=[0, 0, 0, 0, 0] - Array of length 5 representing the current time of a timer.
 * @property {boolean}  isCountDown=false         - Sets if the timer is a countdown.
 * @property {Object}   thunder=null              - Holds the setInterval function.
 * @property {boolean}  isRunning=false           - Represents the running status of a timer.
 * @property {Object}   separator=:               - Sets the type of separator for the prettified time.
 * @property {string}   status=Not started.       - Descriptor of the current status of the timer.
 * @public
 *
 */
class ThunderTimer extends EventEmitter {

  /**
   * ThunderTimer Constructor.
   * @constructor
   * @param {Object}   [options={}]                        - Timer options.
   * @param {number[]} [options.timeArray=[0, 0, 0, 0, 0]] - Array of length 5 representing the current time of a timer.
   * @param {boolean}  [options.isCountDown=false]         - Sets if the timer is a countdown.
   * @param {Object}   [options.separator=:]               - Sets the type of separator for the prettified time.
   */
  constructor(options = {}) {
    super();

    if (options.separator && options.separator === '.') {
      throw new Error('Separator can not be a dot.');
    }

    this.timeArray = options.initialTime || [0, 0, 0, 0, 0];
    this.isCountDown = options.isCountDown || false;
    this.separator = options.separator || ':';
    this.thunder = null;
    this.isRunning = false;
    this.status = 'Not started.';
  }

  /**
   * Starts a timer.
   * @param {number} [updateTimeInterval=TIME.unitsInMilliseconds.secondTenths] - Interval at which the timer is updated. (in milliseconds)
   */
  start(updateTimeInterval = TIME.unitsInMilliseconds.secondTenths) {
    if (!this.thunder) {
      this._setIsRunning(true);
      this._setStatus(STATUS.RUNNING);
      initialTime = Date.now();

      this.thunder = setInterval(this._updateTime.bind(this), updateTimeInterval);
    }
  }

  /**
   * Pauses a timer.
   * @returns {number[]}
   */
  pause() {
    this._setIsRunning(false);
    this._setStatus(STATUS.PAUSED);

    return this.timeArray;
  }

  /**
   * Stops a timer.
   * @returns {*|number[]}
   */
  stop() {
    this._setIsRunning(false);
    this._setStatus(STATUS.STOPPED);
    clearInterval(this.thunder);

    return this.timeArray;
  }

  /**
   * Returns a representation of the arrayTime of timer.
   * @param {boolean} [pretty=false] - If set to true timer is prettified and returned as String.
   * @returns {string|Array}
   */
  getTime(pretty = false) {
    return pretty ? this._prettify() : this.timeArray.reverse().join(this.separator);
  }

  /**
   * Returns the current status descriptor of timer.
   * @returns {string}
   */
  getStatus() {
    return this.status;
  }

  /**
   * Sets the value for isRunning of timer.
   * @param {boolean} isRunning - Represents the running state of the timer.
   * @private
   */
  _setIsRunning(isRunning) {
    this.isRunning = isRunning;
  }

  /**
   * Returns the current running status of timer.
   * @returns {boolean}
   * @private
   */
  _getIsRunning() {
    return this.isRunning;
  }

  /**
   * Sets the status of timer.
   * @param {string} status - Status descriptor of timer.
   * @private
   */
  _setStatus(status) {
    this.status = status;
  }

  /**
   * Updates arrayTime.
   * @private
   */
  _updateTime() {
    currentTime = Date.now() - initialTime;

    this.timeArray[4] = Math.floor(currentTime / TIME.unitsInMilliseconds.days);
    this.timeArray[3] = Math.floor((currentTime % TIME.unitsInMilliseconds.days) / TIME.unitsInMilliseconds.hours);
    this.timeArray[2] = Math.floor((currentTime % TIME.unitsInMilliseconds.hours) / TIME.unitsInMilliseconds.minutes);
    this.timeArray[1] = Math.floor((currentTime % TIME.unitsInMilliseconds.minutes) / TIME.unitsInMilliseconds.seconds);
    this.timeArray[0] = (currentTime % TIME.unitsInMilliseconds.seconds) / TIME.unitsInMilliseconds.secondTenths;
  }

  /**
   * Prettifies timer.
   * @returns {string}
   * @private
   */
  _prettify() {
    return this.timeArray[TIME.SECONDS_POSITION] + ' ' + TIME.SECONDS + ', ' +
            this.timeArray[TIME.MINUTES_POSITION] + ' ' + TIME.MINUTES + ', ' +
            this.timeArray[TIME.HOURS_POSITION] + ' ' + TIME.HOURS + ', ' +
            this.timeArray[TIME.DAYS_POSITION] + ' ' + TIME.DAYS;
  }

}

module.exports = ThunderTimer;<|endoftext|>