<commit_before>( function ( $ ) {

    $.widget( 'ui.placepicker', {
        _init: function () {
            var self = this;
                options = self.options,
                text = $.ui.placepicker.regional[ options.region ];

            if ( options.form ) {
                var location = self.isCompleteLocation( options.form );
                if ( location ) {
                    self.setLocation( location );
                }
            }
            if ( options.initialLocation ) {
                if ( !self.isCompleteLocation( options.initialLocation ) ) {
                    throw 'Initial location is not complete.';
                }
                self.setLocation( options.initialLocation );
            }

            // TODO: break out service init
            self.service = {};
            if ( options.map ) { // TODO: move this to _setData?
                self._loadMapService();
                self.lastMapLocation = {
                    latlng: self.service.map.getCenter()
                };
            }
            self._loadGeocoderService();

            var locationText = self.getLocation()
                ? self.getFormattedLocation() : text.noLocationText;

            var uiContainer = ( self.uiContainer = $( '<div />' )
                    .addClass( 'ui-placepicker ui-widget-header' )
                    .appendTo( self.element ) ),

                uiWrapper = ( self.uiWrapper = $( '<div />' )
                    .addClass( 'ui-placepicker-wrapper' )
                    .appendTo( uiContainer ) ),

                uiLocationLabel = ( self.uiLocationLabel = $( '<label />' )
                    .html( text.locationText )
                    .appendTo( uiWrapper ) ),

                uiCurrentLink = ( self.uiCurrentLink = $( '<a />' )
                    .attr( 'href', '#' )
                    .addClass( 'ui-placepicker-currentaddress ui-corner-all ui-state-default' )
                    .hover(
                        function () {
                            $( this ).addClass( 'ui-state-hover' )
                        },
                        function () {
                            $( this ).removeClass( 'ui-state-hover' )
                        } )
                    .click( function ( e ) {
                        e.preventDefault();

		                self._setView( 'search' );
                    } )
                    .appendTo( uiWrapper ) ),

                uiCurrentAddressSpan = ( self.uiCurrentAddressSpan = $( '<span />' )
                    .html( locationText )
                    .appendTo( uiCurrentLink ) ),

                uiCurrentButtonSpan = ( self.uiCurrentButtonSpan = $( '<span />' )
                    .addClass( 'ui-icon ui-icon-home' )
                    .appendTo( uiCurrentLink ) ),

                uiSearcherSpan = ( self.uiSearcherSpan = $( '<span />' )
                    .addClass( 'ui-placepicker-searcher' )
                    .hide()
                    .appendTo( uiWrapper ) ),

                uiSearchInput = ( self.uiSearchInput = $(
                    '<input type="text" value="' + locationText + '" />'
                    )
                    .addClass( 'ui-placepicker-searchinput' )
	                .keypress( function( event ) {
		                if( event.keyCode == 13 ) {
			                event.preventDefault();

			                self.search( this.value, event );
		                }
	                } )
                    .appendTo( uiSearcherSpan ) ),

                uiSearchButton = ( self.uiSearchButton = $( '<button />' )
                    .addClass( 'ui-placepicker-searchbutton' )
                    .html( text.searchText )
                    .click( function ( event ) {
                        event.preventDefault();

                        self.search( uiSearchInput.val(), event );
                    } )
                    .appendTo( uiSearcherSpan ) ),

                uiCancelLink = ( self.uiCancelLink = $( '<a />' )
                    .attr( 'href', '#' )
                    .addClass( 'ui-placepicker-cancel' )
                    .html( text.cancelText )
                    .click( function ( event ) {
		                event.preventDefault();

		                self._trigger( 'cancel', event );

		                if ( options.map ) {
		                    var latlng = ( self.getLocation()
		                        || self.lastMapLocation ).latlng;

		                    self.service.map.hideMarker( 'result' );
		                    self.service.map.panTo( latlng );
		                }

		                self._setView( 'default' );
	                } )
                    .appendTo( uiSearcherSpan ) ),

                uiResult = ( self.uiResult = $( '<div />' )
                    .addClass( 'ui-placepicker-result ui-widget-header' )
                    .hide()
                    .appendTo( uiContainer ) ),

                uiResultLocation = ( self.uiResultLocation = $( '<div />' )
                    .addClass( 'ui-placepicker-result-location' )
                    .appendTo( uiResult ) ),

                uiResultNotice = ( self.uiResultNotice = $( '<div />' )
                    .html( text.noticeIncompleteText )
                    .addClass( 'ui-placepicker-notice' )
                    .appendTo( uiResult ) ),

                uiResultSuccess = ( self.uiResultSuccess = $( '<div />' )
                    .addClass( 'ui-placepicker-result-success' )
                    .appendTo( uiResult ) ),

                uiResultSuccessText = ( self.uiResultSuccessText = $( '<span />' )
                    .html( text.resultText )
                    .appendTo( uiResultSuccess ) ),

                uiResultSaveButton = ( self.uiResultSaveButton = $( '<button />' )
                    .addClass( 'ui-placepicker-save' )
                    .html( text.saveText )
                    .click( function ( event ) {
	                    event.preventDefault();

                        var result = self._getData( 'result' );

                        if ( options.ajax ) {
                            self._ajaxSubmit( options.ajax, result );
                            // TODO: Implement fail/success
                        }

	                    self.setLocation( result );
	                    // TODO: allow massaging of data in this in preparation for form?
	                    self._trigger( 'saved', event, { location: result } );

	                    if ( options.map ) {
	                        self.service.map.hideMarker( 'result' );
	                        self.service.map.showMarker( 'location',
	                            result.latlng );
	                    }

	                    if ( options.form ) {
	                        self._copyObjectToForm( result, options.form );
	                    }

                        var formattedLocation = self.getFormattedLocation();

	                    uiSearchInput.val( formattedLocation );
	                    uiCurrentAddressSpan.html( formattedLocation );
                        self._setView( 'default' );
                    } )
                    .appendTo( uiResultSuccess ) );
        },

        formatLocation: function ( location, fields ) {
            var format_fields = fields || [ 'city', 'province', 'country' ];

            return $.map( $.grep( format_fields, function ( n, i ) {
                return location[ n ] !== undefined;
            } ), function ( n, i ) {
                return location[ n ];
            } ).join( ', ' );
        },

        getFormattedLocation: function () {
            var location = this.getLocation();
            return location ? this.formatLocation( location ) : '';
        },

        getLocation: function () {
            return this._getData( 'location' );
        },
        setLocation: function ( location ) {
            this._setData( 'location', location );
        },

        search: function ( query, event ) {
            var self = this;

            self._setView( 'search' );

            self._trigger( 'search', event, { query: query } );

            this.geocode( { address: query }, function ( response, status ) {
                self._geocodeCallback.apply( self, [ response, status ] );
            } );
        },

        geocode: function ( query, callback ) {
            this.service.geocoder.geocode( query, callback );
        },

        _geocodeCallback: function ( response, status ) {
            if ( status == this.service.geocoder.StatusOK ) {
                var location = this.service.geocoder.locationFromResponse( response );
                this._setData( 'result', location );

                var is_complete = this.isCompleteLocation( location );

                if ( this.options.map ) {
                    this.service.map.panTo( location.latlng );
                    this.service.map.showMarker( 'result', location.latlng );
                }

                this._trigger( 'response', null, {
                    complete: is_complete, location: location } );

                this.uiResultLocation.html( this.formatLocation( location ) );
                this.uiResultSuccess.toggle( is_complete );
                this.uiResultNotice.toggle( !is_complete );
                this.uiResult.show();
            }
        },

        isCompleteLocation: function ( obj ) {
            if ( obj.jquery ) {
                var location = {};
                var full = 1;
                $.each( this.options.formSelectors, function ( field, sel ) {
                    var e = obj.find( sel );
                    if ( !e || !e.val() ) {
                        full = 0;
                        return false;
                    }
                    location[field] = e.val();
                } );

                if ( !full ) {
                    return null;
                }
                $.extend( location, { latlng: {
                    lat: location.lat,
                    lng: location.lng
                } } );
                delete location.lat;
                delete location.lng;

                return location;
            }
            else {
                return ( obj && obj.city && obj.province
                    && obj.country ) ? true : false;
            }
        },

        _setView: function ( view ) {
            switch ( view ) {
                case 'default':
                    this.uiSearcherSpan.hide();
	                this.uiResult.hide();
	                this.uiCurrentLink.show();
                    break;
                case 'search':
                    this.uiResult.hide();
                    this.uiCurrentLink.hide();
	                this.uiSearcherSpan.show();
	                this.uiSearchInput.focus();
	                this.uiSearchInput.select();

	                break;
            }
        },

        _loadMapService: function () {
            var service = this.options.mapService;

            if ( service === undefined ) {
                // TODO: Auto-detect map type
                service = this._firstKey(
                    $.ui.placepicker.services.map );
                if ( !service ) {
                    throw 'No map services were registered.'
                }
            }
            else if ( $.ui.placepicker.services.map[service] === undefined ) {
                throw 'The ' + service + ' map service was not registered.';
            }

            var map = this.options.map;
            this.service.map = new $.ui.placepicker.services.map[service]( {
                placepicker: this,
                map: map
            } );

            this.service.map.createMarker( 'result',
                this.options.resultMarker );
            this.service.map.createMarker( 'location',
                this.options.locationMarker );

            if ( this.getLocation() ) {
                this._initMap();
            }
        },

        _initMap: function () {
            this.service.map.showMarker( 'location', this.getLocation().latlng );
            this.service.map.panTo( this.getLocation().latlng );
        },

        _loadGeocoderService: function () {
            var service = this.options.geocoderService;

            if ( service === undefined ) {
                service =
                    this._firstKey( $.ui.placepicker.services.geocoder );
                if ( !service ) {
                    throw 'No geocoder services were registered.'
                }
            }
            else if ( this.services.geocoder[service] === undefined ) {
                throw 'The ' + service
                    + ' geocoder service was not registered.';
            }

            this.service.geocoder =
                new $.ui.placepicker.services.geocoder[service]( {
                    placepicker: this
                } );
        },

        _firstKey: function ( obj ) {
            var first;
            $.each( obj, function ( index, item ) {
                first = index;
                return false;
            } );
            return first;
        },

        _ajaxSubmit: function ( url, data ) {
            var obj = this._flatten( data );

            $.ajax( {
                type: 'POST',
                url: url,
                data: $.param( obj )
            } );
        },

        _flatten: function ( obj ) {
            var self = this;
            var newobj = {};

            $.each( obj, function ( key, val ) {
                if ( typeof val == 'object' ) {
                    $.extend( newobj, self._flatten( val ) );
                }
                else {
                    newobj[key] = val;
                }
            } );

            return newobj;
        },

        _copyObjectToForm: function ( object, form ) {
            $.each( this._flatten( object ), function ( key, val ) {
                form.find( '[name=' + key + ']' ).val( val );
            } );
        }
    } );

    $.extend( $.ui.placepicker, {
        defaults: {
            region: '',
            initialLocation: '',
            details: null,
            locationMarker: {
                icon: 'images/location.png'
            },
            resultMarker: {
                icon: 'images/result.png'
            },
            formSelectors: {
                city: '[name=city]',
                province: '[name=province]',
                country: '[name=country]',
                lat: '[name=lat]',
                lng: '[name=lng]'
            }
        },
        regional: [],
        services: {}
    } );

    $.ui.placepicker.regional[''] = {
        locationText: 'Location:',
        noLocationText: 'No location entered.',
        noticeIncompleteText: 'The query returned an incomplete result, please try again.',
        resultText: 'Is this the right place?',
        cancelText: 'Cancel',
        searchText: 'Search',
        saveText: 'Save'
    };

} ) ( jQuery );
<commit_msg>Adds some custom formatting to the format functions. Also adds getters.<commit_after>( function ( $ ) {

    $.widget( 'ui.placepicker', {
        _init: function () {
            var self = this;
                options = self.options,
                text = $.ui.placepicker.regional[ options.region ];

            if ( options.form ) {
                var location = self.isCompleteLocation( options.form );
                if ( location ) {
                    self.setLocation( location );
                }
            }
            if ( options.initialLocation ) {
                if ( !self.isCompleteLocation( options.initialLocation ) ) {
                    throw 'Initial location is not complete.';
                }
                self.setLocation( options.initialLocation );
            }

            // TODO: break out service init
            self.service = {};
            if ( options.map ) { // TODO: move this to _setData?
                self._loadMapService();
                self.lastMapLocation = {
                    latlng: self.service.map.getCenter()
                };
            }
            self._loadGeocoderService();

            var locationText = self.getLocation()
                ? self.getFormattedLocation() : text.noLocationText;

            var uiContainer = ( self.uiContainer = $( '<div />' )
                    .addClass( 'ui-placepicker ui-widget-header' )
                    .appendTo( self.element ) ),

                uiWrapper = ( self.uiWrapper = $( '<div />' )
                    .addClass( 'ui-placepicker-wrapper' )
                    .appendTo( uiContainer ) ),

                uiLocationLabel = ( self.uiLocationLabel = $( '<label />' )
                    .html( text.locationText )
                    .appendTo( uiWrapper ) ),

                uiCurrentLink = ( self.uiCurrentLink = $( '<a />' )
                    .attr( 'href', '#' )
                    .addClass( 'ui-placepicker-currentaddress ui-corner-all ui-state-default' )
                    .hover(
                        function () {
                            $( this ).addClass( 'ui-state-hover' )
                        },
                        function () {
                            $( this ).removeClass( 'ui-state-hover' )
                        } )
                    .click( function ( e ) {
                        e.preventDefault();

		                self._setView( 'search' );
                    } )
                    .appendTo( uiWrapper ) ),

                uiCurrentAddressSpan = ( self.uiCurrentAddressSpan = $( '<span />' )
                    .html( locationText )
                    .appendTo( uiCurrentLink ) ),

                uiCurrentButtonSpan = ( self.uiCurrentButtonSpan = $( '<span />' )
                    .addClass( 'ui-icon ui-icon-home' )
                    .appendTo( uiCurrentLink ) ),

                uiSearcherSpan = ( self.uiSearcherSpan = $( '<span />' )
                    .addClass( 'ui-placepicker-searcher' )
                    .hide()
                    .appendTo( uiWrapper ) ),

                uiSearchInput = ( self.uiSearchInput = $(
                    '<input type="text" value="' + locationText + '" />'
                    )
                    .addClass( 'ui-placepicker-searchinput' )
	                .keypress( function( event ) {
		                if( event.keyCode == 13 ) {
			                event.preventDefault();

			                self.search( this.value, event );
		                }
	                } )
                    .appendTo( uiSearcherSpan ) ),

                uiSearchButton = ( self.uiSearchButton = $( '<button />' )
                    .addClass( 'ui-placepicker-searchbutton' )
                    .html( text.searchText )
                    .click( function ( event ) {
                        event.preventDefault();

                        self.search( uiSearchInput.val(), event );
                    } )
                    .appendTo( uiSearcherSpan ) ),

                uiCancelLink = ( self.uiCancelLink = $( '<a />' )
                    .attr( 'href', '#' )
                    .addClass( 'ui-placepicker-cancel' )
                    .html( text.cancelText )
                    .click( function ( event ) {
		                event.preventDefault();

		                self._trigger( 'cancel', event );

		                if ( options.map ) {
		                    var latlng = ( self.getLocation()
		                        || self.lastMapLocation ).latlng;

		                    self.service.map.hideMarker( 'result' );
		                    self.service.map.panTo( latlng );
		                }

		                self._setView( 'default' );
	                } )
                    .appendTo( uiSearcherSpan ) ),

                uiResult = ( self.uiResult = $( '<div />' )
                    .addClass( 'ui-placepicker-result ui-widget-header' )
                    .hide()
                    .appendTo( uiContainer ) ),

                uiResultLocation = ( self.uiResultLocation = $( '<div />' )
                    .addClass( 'ui-placepicker-result-location' )
                    .appendTo( uiResult ) ),

                uiResultNotice = ( self.uiResultNotice = $( '<div />' )
                    .html( text.noticeIncompleteText )
                    .addClass( 'ui-placepicker-notice' )
                    .appendTo( uiResult ) ),

                uiResultSuccess = ( self.uiResultSuccess = $( '<div />' )
                    .addClass( 'ui-placepicker-result-success' )
                    .appendTo( uiResult ) ),

                uiResultSuccessText = ( self.uiResultSuccessText = $( '<span />' )
                    .html( text.resultText )
                    .appendTo( uiResultSuccess ) ),

                uiResultSaveButton = ( self.uiResultSaveButton = $( '<button />' )
                    .addClass( 'ui-placepicker-save' )
                    .html( text.saveText )
                    .click( function ( event ) {
	                    event.preventDefault();

                        var result = self._getData( 'result' );

                        if ( options.ajax ) {
                            self._ajaxSubmit( options.ajax, result );
                            // TODO: Implement fail/success
                        }

	                    self.setLocation( result );
	                    // TODO: allow massaging of data in this in preparation for form?
	                    self._trigger( 'saved', event, { location: result } );

	                    if ( options.map ) {
	                        self.service.map.hideMarker( 'result' );
	                        self.service.map.showMarker( 'location',
	                            result.latlng );
	                    }

	                    if ( options.form ) {
	                        self._copyObjectToForm( result, options.form );
	                    }

                        var formattedLocation = self.getFormattedLocation();

	                    uiSearchInput.val( formattedLocation );
	                    uiCurrentAddressSpan.html( formattedLocation );
                        self._setView( 'default' );
                    } )
                    .appendTo( uiResultSuccess ) );
        },

        formatLocation: function ( location, fields ) {
            var format_fields = fields && fields.length ? fields
                : [ 'city', 'province', 'country' ];

            return $.map( $.grep( format_fields, function ( n, i ) {
                return location[ n ] !== undefined;
            } ), function ( n, i ) {
                return location[ n ];
            } ).join( ', ' );
        },

        getFormattedLocation: function ( fields ) {
            var location = this.getLocation();
            return location ? this.formatLocation( location, fields ) : '';
        },

        getLocation: function () {
            return this._getData( 'location' );
        },
        setLocation: function ( location ) {
            this._setData( 'location', location );
        },

        search: function ( query, event ) {
            var self = this;

            self._setView( 'search' );

            self._trigger( 'search', event, { query: query } );

            this.geocode( { address: query }, function ( response, status ) {
                self._geocodeCallback.apply( self, [ response, status ] );
            } );
        },

        geocode: function ( query, callback ) {
            this.service.geocoder.geocode( query, callback );
        },

        _geocodeCallback: function ( response, status ) {
            if ( status == this.service.geocoder.StatusOK ) {
                var location = this.service.geocoder.locationFromResponse( response );
                this._setData( 'result', location );

                var is_complete = this.isCompleteLocation( location );

                if ( this.options.map ) {
                    this.service.map.panTo( location.latlng );
                    this.service.map.showMarker( 'result', location.latlng );
                }

                this._trigger( 'response', null, {
                    complete: is_complete, location: location } );

                this.uiResultLocation.html( this.formatLocation( location ) );
                this.uiResultSuccess.toggle( is_complete );
                this.uiResultNotice.toggle( !is_complete );
                this.uiResult.show();
            }
        },

        isCompleteLocation: function ( obj ) {
            if ( obj.jquery ) {
                var location = {};
                var full = 1;
                $.each( this.options.formSelectors, function ( field, sel ) {
                    var e = obj.find( sel );
                    if ( !e || !e.val() ) {
                        full = 0;
                        return false;
                    }
                    location[field] = e.val();
                } );

                if ( !full ) {
                    return null;
                }
                $.extend( location, { latlng: {
                    lat: location.lat,
                    lng: location.lng
                } } );
                delete location.lat;
                delete location.lng;

                return location;
            }
            else {
                return ( obj && obj.city && obj.province
                    && obj.country ) ? true : false;
            }
        },

        _setView: function ( view ) {
            switch ( view ) {
                case 'default':
                    this.uiSearcherSpan.hide();
	                this.uiResult.hide();
	                this.uiCurrentLink.show();
                    break;
                case 'search':
                    this.uiResult.hide();
                    this.uiCurrentLink.hide();
	                this.uiSearcherSpan.show();
	                this.uiSearchInput.focus();
	                this.uiSearchInput.select();

	                break;
            }
        },

        _loadMapService: function () {
            var service = this.options.mapService;

            if ( service === undefined ) {
                // TODO: Auto-detect map type
                service = this._firstKey(
                    $.ui.placepicker.services.map );
                if ( !service ) {
                    throw 'No map services were registered.'
                }
            }
            else if ( $.ui.placepicker.services.map[service] === undefined ) {
                throw 'The ' + service + ' map service was not registered.';
            }

            var map = this.options.map;
            this.service.map = new $.ui.placepicker.services.map[service]( {
                placepicker: this,
                map: map
            } );

            this.service.map.createMarker( 'result',
                this.options.resultMarker );
            this.service.map.createMarker( 'location',
                this.options.locationMarker );

            if ( this.getLocation() ) {
                this._initMap();
            }
        },

        _initMap: function () {
            this.service.map.showMarker( 'location', this.getLocation().latlng );
            this.service.map.panTo( this.getLocation().latlng );
        },

        _loadGeocoderService: function () {
            var service = this.options.geocoderService;

            if ( service === undefined ) {
                service =
                    this._firstKey( $.ui.placepicker.services.geocoder );
                if ( !service ) {
                    throw 'No geocoder services were registered.'
                }
            }
            else if ( this.services.geocoder[service] === undefined ) {
                throw 'The ' + service
                    + ' geocoder service was not registered.';
            }

            this.service.geocoder =
                new $.ui.placepicker.services.geocoder[service]( {
                    placepicker: this
                } );
        },

        _firstKey: function ( obj ) {
            var first;
            $.each( obj, function ( index, item ) {
                first = index;
                return false;
            } );
            return first;
        },

        _ajaxSubmit: function ( url, data ) {
            var obj = this._flatten( data );

            $.ajax( {
                type: 'POST',
                url: url,
                data: $.param( obj )
            } );
        },

        _flatten: function ( obj ) {
            var self = this;
            var newobj = {};

            $.each( obj, function ( key, val ) {
                if ( typeof val == 'object' ) {
                    $.extend( newobj, self._flatten( val ) );
                }
                else {
                    newobj[key] = val;
                }
            } );

            return newobj;
        },

        _copyObjectToForm: function ( object, form ) {
            $.each( this._flatten( object ), function ( key, val ) {
                form.find( '[name=' + key + ']' ).val( val );
            } );
        }
    } );

    $.extend( $.ui.placepicker, {
        defaults: {
            region: '',
            initialLocation: '',
            details: null,
            locationMarker: {
                icon: 'images/location.png'
            },
            resultMarker: {
                icon: 'images/result.png'
            },
            formSelectors: {
                city: '[name=city]',
                province: '[name=province]',
                country: '[name=country]',
                lat: '[name=lat]',
                lng: '[name=lng]'
            }
        },
        regional: [],
        services: {},
        getter: 'getFormattedLocation formatLocation'
    } );

    $.ui.placepicker.regional[''] = {
        locationText: 'Location:',
        noLocationText: 'No location entered.',
        noticeIncompleteText: 'The query returned an incomplete result, please try again.',
        resultText: 'Is this the right place?',
        cancelText: 'Cancel',
        searchText: 'Search',
        saveText: 'Save'
    };

} ) ( jQuery );
<|endoftext|>


<commit_before>import React from 'react';
import { mount } from 'enzyme';
import BackTop from '..';

describe('BackTop', () => {
  beforeAll(() => {
    jest.useFakeTimers();
  });

  afterAll(() => {
    jest.useRealTimers();
  });

  it('should scroll to top after click it', async () => {
    const wrapper = mount(<BackTop visibilityHeight={-1} />);
    document.documentElement.scrollTop = 400;
    // trigger scroll manually
    wrapper.instance().handleScroll();
    jest.runAllTimers();
    wrapper.find('.ant-back-top').simulate('click');
    jest.runAllTimers();
    await new Promise(resolve => setTimeout(resolve, 0));
    expect(Math.abs(Math.round(document.documentElement.scrollTop))).toBe(0);
  });
});
<commit_msg>✅ fix back-top test<commit_after>import React from 'react';
import { mount } from 'enzyme';
import BackTop from '..';

describe('BackTop', () => {
  it('should scroll to top after click it', async () => {
    const wrapper = mount(<BackTop visibilityHeight={-1} />);
    document.documentElement.scrollTop = 400;
    // trigger scroll manually
    wrapper.instance().handleScroll();
    await new Promise(resolve => setTimeout(resolve, 0));
    wrapper.find('.ant-back-top').simulate('click');
    await new Promise(resolve => setTimeout(resolve, 1000));
    expect(Math.abs(Math.round(document.documentElement.scrollTop))).toBe(0);
  });
});
<|endoftext|>


<commit_before>import React from 'react';
import Moment from 'moment';
import { mount } from 'enzyme';
import MockDate from 'mockdate';
import Calendar from '..';

describe('Calendar', () => {
  it('Calendar should be selectable', () => {
    const onSelect = jest.fn();
    const wrapper = mount(<Calendar onSelect={onSelect} />);
    wrapper
      .find('.ant-fullcalendar-cell')
      .at(0)
      .simulate('click');
    expect(onSelect).toBeCalledWith(expect.anything());
    const value = onSelect.mock.calls[0][0];
    expect(Moment.isMoment(value)).toBe(true);
  });

  it('only Valid range should be selectable', () => {
    const onSelect = jest.fn();
    const validRange = [Moment('2018-02-02'), Moment('2018-02-18')];
    const wrapper = mount(
      <Calendar onSelect={onSelect} validRange={validRange} defaultValue={Moment('2018-02-02')} />,
    );
    wrapper
      .find('[title="February 1, 2018"]')
      .at(0)
      .simulate('click');
    wrapper
      .find('[title="February 2, 2018"]')
      .at(0)
      .simulate('click');
    expect(onSelect.mock.calls.length).toBe(1);
  });

  it('dates other than in valid range should be disabled', () => {
    const onSelect = jest.fn();
    const validRange = [Moment('2018-02-02'), Moment('2018-02-18')];
    const wrapper = mount(
      <Calendar onSelect={onSelect} validRange={validRange} defaultValue={Moment('2018-02-02')} />,
    );
    wrapper
      .find('[title="February 20, 2018"]')
      .at(0)
      .simulate('click');
    const elem = wrapper
      .find('[title="February 20, 2018"]')
      .hasClass('ant-fullcalendar-disabled-cell');
    expect(elem).toEqual(true);
    expect(onSelect.mock.calls.length).toBe(0);
  });

  it('months other than in valid range should be disabled', () => {
    const onSelect = jest.fn();
    const validRange = [Moment('2018-02-02'), Moment('2018-05-18')];
    const wrapper = mount(
      <Calendar
        onSelect={onSelect}
        validRange={validRange}
        defaultValue={Moment('2018-02-02')}
        mode="year"
      />,
    );
    expect(
      wrapper
        .find('[title="Jan"]')
        .at(0)
        .hasClass('ant-fullcalendar-month-panel-cell-disabled'),
    ).toBe(true);
    expect(
      wrapper
        .find('[title="Feb"]')
        .at(0)
        .hasClass('ant-fullcalendar-month-panel-cell-disabled'),
    ).toBe(false);
    expect(
      wrapper
        .find('[title="Jun"]')
        .at(0)
        .hasClass('ant-fullcalendar-month-panel-cell-disabled'),
    ).toBe(true);
    wrapper
      .find('[title="Jan"]')
      .at(0)
      .simulate('click');
    wrapper
      .find('[title="Mar"]')
      .at(0)
      .simulate('click');
    expect(onSelect.mock.calls.length).toBe(1);
  });

  it('months other than in valid range should not be shown in header', () => {
    const validRange = [Moment('2017-02-02'), Moment('2018-05-18')];
    const wrapper = mount(<Calendar validRange={validRange} />);
    wrapper
      .find('.ant-fullcalendar-year-select')
      .hostNodes()
      .simulate('click');
    wrapper
      .find('.ant-select-dropdown-menu-item')
      .first()
      .simulate('click');
    wrapper
      .find('.ant-fullcalendar-month-select')
      .hostNodes()
      .simulate('click');
    // 2 years and 11 months
    expect(wrapper.find('.ant-select-dropdown-menu-item').length).toBe(13);
  });

  it('getDateRange should returns a disabledDate function', () => {
    const validRange = [Moment('2018-02-02'), Moment('2018-05-18')];
    const wrapper = mount(<Calendar validRange={validRange} defaultValue={Moment('2018-02-02')} />);
    const instance = wrapper.instance();
    const disabledDate = instance.getDateRange(validRange);
    expect(disabledDate(Moment('2018-06-02'))).toBe(true);
    expect(disabledDate(Moment('2018-04-02'))).toBe(false);
  });

  it('Calendar should change mode by prop', () => {
    const monthMode = 'month';
    const yearMode = 'year';
    const wrapper = mount(<Calendar />);
    expect(wrapper.state().mode).toEqual(monthMode);
    wrapper.setProps({ mode: 'year' });
    expect(wrapper.state().mode).toEqual(yearMode);
  });

  it('Calendar should switch mode', () => {
    const monthMode = 'month';
    const yearMode = 'year';
    const onPanelChangeStub = jest.fn();
    const wrapper = mount(<Calendar mode={yearMode} onPanelChange={onPanelChangeStub} />);
    expect(wrapper.state().mode).toEqual(yearMode);
    wrapper.setProps({ mode: monthMode });
    expect(wrapper.state().mode).toEqual(monthMode);
    expect(onPanelChangeStub).toHaveBeenCalledTimes(0);
  });

  it('Calendar should support locale', () => {
    MockDate.set(Moment('2018-10-19'));
    // eslint-disable-next-line
    const zhCN = require('../locale/zh_CN').default;
    const wrapper = mount(<Calendar locale={zhCN} />);
    expect(wrapper.render()).toMatchSnapshot();
    MockDate.reset();
  });

  it('should trigger onPanelChange when click last month of date', () => {
    const onPanelChange = jest.fn();
    const date = new Moment('1990-09-03');
    const wrapper = mount(<Calendar onPanelChange={onPanelChange} value={date} />);

    wrapper
      .find('.ant-fullcalendar-cell')
      .at(0)
      .simulate('click');

    expect(onPanelChange).toBeCalled();
    expect(onPanelChange.mock.calls[0][0].month()).toEqual(date.month() - 1);
  });

  it('switch should work correctly without prop mode', () => {
    const onPanelChange = jest.fn();
    const date = new Moment('1990-09-03');
    const wrapper = mount(<Calendar onPanelChange={onPanelChange} value={date} />);
    expect(wrapper).toMatchSnapshot();
    expect(wrapper.state().mode).toBe('month');
    wrapper
      .find('.ant-radio-button-input')
      .at(1)
      .simulate('change');
    expect(wrapper).toMatchSnapshot();
    expect(onPanelChange).toBeCalled();
    expect(onPanelChange.mock.calls[0][1]).toEqual('year');
  });
});
<commit_msg>pref: update selector for more acurrate<commit_after>import React from 'react';
import Moment from 'moment';
import { mount } from 'enzyme';
import MockDate from 'mockdate';
import Calendar from '..';

describe('Calendar', () => {
  it('Calendar should be selectable', () => {
    const onSelect = jest.fn();
    const wrapper = mount(<Calendar onSelect={onSelect} />);
    wrapper
      .find('.ant-fullcalendar-cell')
      .at(0)
      .simulate('click');
    expect(onSelect).toBeCalledWith(expect.anything());
    const value = onSelect.mock.calls[0][0];
    expect(Moment.isMoment(value)).toBe(true);
  });

  it('only Valid range should be selectable', () => {
    const onSelect = jest.fn();
    const validRange = [Moment('2018-02-02'), Moment('2018-02-18')];
    const wrapper = mount(
      <Calendar onSelect={onSelect} validRange={validRange} defaultValue={Moment('2018-02-02')} />,
    );
    wrapper
      .find('[title="February 1, 2018"]')
      .at(0)
      .simulate('click');
    wrapper
      .find('[title="February 2, 2018"]')
      .at(0)
      .simulate('click');
    expect(onSelect.mock.calls.length).toBe(1);
  });

  it('dates other than in valid range should be disabled', () => {
    const onSelect = jest.fn();
    const validRange = [Moment('2018-02-02'), Moment('2018-02-18')];
    const wrapper = mount(
      <Calendar onSelect={onSelect} validRange={validRange} defaultValue={Moment('2018-02-02')} />,
    );
    wrapper
      .find('[title="February 20, 2018"]')
      .at(0)
      .simulate('click');
    const elem = wrapper
      .find('[title="February 20, 2018"]')
      .hasClass('ant-fullcalendar-disabled-cell');
    expect(elem).toEqual(true);
    expect(onSelect.mock.calls.length).toBe(0);
  });

  it('months other than in valid range should be disabled', () => {
    const onSelect = jest.fn();
    const validRange = [Moment('2018-02-02'), Moment('2018-05-18')];
    const wrapper = mount(
      <Calendar
        onSelect={onSelect}
        validRange={validRange}
        defaultValue={Moment('2018-02-02')}
        mode="year"
      />,
    );
    expect(
      wrapper
        .find('[title="Jan"]')
        .at(0)
        .hasClass('ant-fullcalendar-month-panel-cell-disabled'),
    ).toBe(true);
    expect(
      wrapper
        .find('[title="Feb"]')
        .at(0)
        .hasClass('ant-fullcalendar-month-panel-cell-disabled'),
    ).toBe(false);
    expect(
      wrapper
        .find('[title="Jun"]')
        .at(0)
        .hasClass('ant-fullcalendar-month-panel-cell-disabled'),
    ).toBe(true);
    wrapper
      .find('[title="Jan"]')
      .at(0)
      .simulate('click');
    wrapper
      .find('[title="Mar"]')
      .at(0)
      .simulate('click');
    expect(onSelect.mock.calls.length).toBe(1);
  });

  it('months other than in valid range should not be shown in header', () => {
    const validRange = [Moment('2017-02-02'), Moment('2018-05-18')];
    const wrapper = mount(<Calendar validRange={validRange} />);
    wrapper
      .find('.ant-fullcalendar-year-select')
      .hostNodes()
      .simulate('click');
    wrapper
      .find('.ant-select-dropdown-menu-item')
      .first()
      .simulate('click');
    wrapper
      .find('.ant-fullcalendar-month-select')
      .hostNodes()
      .simulate('click');
    // 2 years and 11 months
    expect(wrapper.find('.ant-select-dropdown-menu-item').length).toBe(13);
  });

  it('getDateRange should returns a disabledDate function', () => {
    const validRange = [Moment('2018-02-02'), Moment('2018-05-18')];
    const wrapper = mount(<Calendar validRange={validRange} defaultValue={Moment('2018-02-02')} />);
    const instance = wrapper.instance();
    const disabledDate = instance.getDateRange(validRange);
    expect(disabledDate(Moment('2018-06-02'))).toBe(true);
    expect(disabledDate(Moment('2018-04-02'))).toBe(false);
  });

  it('Calendar should change mode by prop', () => {
    const monthMode = 'month';
    const yearMode = 'year';
    const wrapper = mount(<Calendar />);
    expect(wrapper.state().mode).toEqual(monthMode);
    wrapper.setProps({ mode: 'year' });
    expect(wrapper.state().mode).toEqual(yearMode);
  });

  it('Calendar should switch mode', () => {
    const monthMode = 'month';
    const yearMode = 'year';
    const onPanelChangeStub = jest.fn();
    const wrapper = mount(<Calendar mode={yearMode} onPanelChange={onPanelChangeStub} />);
    expect(wrapper.state().mode).toEqual(yearMode);
    wrapper.setProps({ mode: monthMode });
    expect(wrapper.state().mode).toEqual(monthMode);
    expect(onPanelChangeStub).toHaveBeenCalledTimes(0);
  });

  it('Calendar should support locale', () => {
    MockDate.set(Moment('2018-10-19'));
    // eslint-disable-next-line
    const zhCN = require('../locale/zh_CN').default;
    const wrapper = mount(<Calendar locale={zhCN} />);
    expect(wrapper.render()).toMatchSnapshot();
    MockDate.reset();
  });

  it('should trigger onPanelChange when click last month of date', () => {
    const onPanelChange = jest.fn();
    const date = new Moment('1990-09-03');
    const wrapper = mount(<Calendar onPanelChange={onPanelChange} value={date} />);

    wrapper
      .find('.ant-fullcalendar-cell')
      .at(0)
      .simulate('click');

    expect(onPanelChange).toBeCalled();
    expect(onPanelChange.mock.calls[0][0].month()).toEqual(date.month() - 1);
  });

  it('switch should work correctly without prop mode', () => {
    const onPanelChange = jest.fn();
    const date = new Moment('1990-09-03');
    const wrapper = mount(<Calendar onPanelChange={onPanelChange} value={date} />);
    expect(wrapper).toMatchSnapshot();
    expect(wrapper.state().mode).toBe('month');
    wrapper.find('.ant-radio-button-input[value="year"]').simulate('change');
    expect(wrapper).toMatchSnapshot();
    expect(onPanelChange).toBeCalled();
    expect(onPanelChange.mock.calls[0][1]).toEqual('year');
  });
});
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import { Router } from 'routes';
import { Autobind } from 'es-decorators';

// Redux
import { connect } from 'react-redux';

// Services
import AreasService from 'services/AreasService';
import UserService from 'services/UserService';
import DatasetService from 'services/DatasetService';

// components
import CustomSelect from 'components/ui/CustomSelect';
import Spinner from 'components/ui/Spinner';

class SubscribeToDatasetModal extends React.Component {

  constructor(props) {
    super(props);

    this.state = {
      datasets: [],
      areaOptions: [],
      loadingAreaOptions: false,
      loadingDatasets: false,
      selectedArea: null,
      loading: false,
      saved: false,
      name: ''
    };

    // Services
    this.areasService = new AreasService({ apiURL: process.env.WRI_API_URL });
    this.userService = new UserService({ apiURL: process.env.WRI_API_URL });
    this.datasetService = new DatasetService(null, { apiURL: process.env.WRI_API_URL });
  }

  componentDidMount() {
    this.loadAreas();
    if (this.props.showDatasetSelector) {
      this.loadDatasets();
    }
  }

  @Autobind
  onChangeSelectedArea(value) {
    this.setState({
      selectedArea: value
    });
  }

  @Autobind
  onChangeSelectedDataset(value) {
    this.setState({
      selectedDataset: value
    });
  }

  loadAreas() {
    this.setState({
      loadingAreaOptions: true
    });
    this.areasService.fetchCountries().then((response) => {
      this.setState({
        areaOptions: response.data,
        loadingAreaOptions: false
      });
    });
  }

  loadDatasets() {
    this.datasetService.getAllDatasets().then((response) => {
      this.setState({
        loadingDatasets: false,
        datasets: response.map(val => ({ label: val.attributes.name, value: val.attributes.name }))
      });
    }).catch(err => console.log(err));
  }

  @Autobind
  handleSubscribe() {
    const { selectedArea, name } = this.state;
    const { dataset, user } = this.props;

    if (selectedArea) {
      this.setState({
        loading: true
      });
      this.userService.createSubscriptionToDataset(dataset.id, selectedArea.value, user, name)
        .then(() => {
          this.setState({
            loading: false,
            saved: true
          });
        })
        .catch(err => this.setState({ error: err, loading: false }));
    }
  }

  @Autobind
  handleGoToMySubscriptions() {
    this.setState({
      saved: false
    });
    this.props.toggleModal(false);
    Router.pushRoute('myrw', { tab: 'subscriptions' });
  }

  @Autobind
  handleNameChange(event) {
    this.setState({
      name: event.target.value
    });
  }

  @Autobind
  handleCancel() {
    this.setState({
      saved: false
    });
    this.props.toggleModal(false);
  }

  render() {
    const {
      areaOptions,
      loadingAreaOptions,
      selectedArea,
      loading,
      saved,
      name,
      datasets,
      loadingDatasets,
      selectedDataset
    } = this.state;
    const { dataset, showDatasetSelector } = this.props;
    let headerText;
    if (saved) {
      headerText = 'Subscription saved!';
    } else if (dataset) {
      headerText = `Subscribe to ${dataset.attributes.name}`;
    } else {
      headerText = 'Subscribe to alerts';
    }
    const paragraphText = saved ?
      'Your subscription was successfully created. Please check your email address to confirm it' :
      'Please enter a name and select an area for the subscription';

    return (

{headerText}

{paragraphText}

        {!saved &&

Subscription name

              <input value={name} onChange={this.handleNameChange} />

              <Spinner isLoading={loadingAreaOptions || loadingDatasets || loading} className="-light -small" />
              <CustomSelect
                placeholder="Select area"
                options={areaOptions}
                onValueChange={this.onChangeSelectedArea}
                allowNonLeafSelection={false}
                value={selectedArea && selectedArea.value}
              />
              {showDatasetSelector &&
                <CustomSelect
                  placeholder="Select a dataset"
                  options={datasets}
                  onValueChange={this.onChangeSelectedDataset}
                  allowNonLeafSelection={false}
                  value={selectedDataset && selectedDataset.value}
                />
              }

        }

        {saved &&

        }

        {!saved &&

            <button className="c-btn -primary" onClick={this.handleSubscribe}>
              Subscribe
            </button>
            <button className="c-btn -secondary" onClick={this.handleCancel}>
              Cancel
            </button>

        }

        {saved &&

            <button className="c-btn -secondary" onClick={this.handleCancel}>
              Ok
            </button>
            <button className="c-btn -primary" onClick={this.handleGoToMySubscriptions}>
              Check my subscriptions
            </button>

        }

    );
  }
}

SubscribeToDatasetModal.propTypes = {
  toggleModal: PropTypes.func.isRequired,
  dataset: PropTypes.object,
  showDatasetSelector: PropTypes.bool.isRequired,
  // Store
  user: PropTypes.object.isRequired
};

const mapStateToProps = state => ({
  user: state.user
});

export default connect(mapStateToProps, null)(SubscribeToDatasetModal);
<commit_msg>use selected dataset from selector<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import { Router } from 'routes';
import { Autobind } from 'es-decorators';

// Redux
import { connect } from 'react-redux';

// Services
import AreasService from 'services/AreasService';
import UserService from 'services/UserService';
import DatasetService from 'services/DatasetService';

// components
import CustomSelect from 'components/ui/CustomSelect';
import Spinner from 'components/ui/Spinner';

class SubscribeToDatasetModal extends React.Component {

  constructor(props) {
    super(props);

    this.state = {
      datasets: [],
      areaOptions: [],
      loadingAreaOptions: false,
      loadingDatasets: false,
      selectedArea: null,
      loading: false,
      saved: false,
      name: ''
    };

    // Services
    this.areasService = new AreasService({ apiURL: process.env.WRI_API_URL });
    this.userService = new UserService({ apiURL: process.env.WRI_API_URL });
    this.datasetService = new DatasetService(null, { apiURL: process.env.WRI_API_URL });
  }

  componentDidMount() {
    this.loadAreas();
    if (this.props.showDatasetSelector) {
      this.loadDatasets();
    }
  }

  @Autobind
  onChangeSelectedArea(value) {
    this.setState({
      selectedArea: value
    });
  }

  @Autobind
  onChangeSelectedDataset(value) {
    this.setState({
      selectedDataset: value
    });
  }

  loadAreas() {
    this.setState({
      loadingAreaOptions: true
    });
    this.areasService.fetchCountries().then((response) => {
      this.setState({
        areaOptions: response.data,
        loadingAreaOptions: false
      });
    });
  }

  loadDatasets() {
    this.datasetService.getAllDatasets().then((response) => {
      this.setState({
        loadingDatasets: false,
        datasets: response.map(val => (
          { label: val.attributes.name, value: val.attributes.name, id: val.id }))
      });
    }).catch(err => console.log(err));
  }

  @Autobind
  handleSubscribe() {
    const { selectedArea, name, selectedDataset } = this.state;
    const { dataset, user } = this.props;
    const datasetId = dataset ? dataset.id : selectedDataset.id;

    if (selectedArea) {
      this.setState({
        loading: true
      });
      this.userService.createSubscriptionToDataset(datasetId, selectedArea.value, user, name)
        .then(() => {
          this.setState({
            loading: false,
            saved: true
          });
        })
        .catch(err => this.setState({ error: err, loading: false }));
    }
  }

  @Autobind
  handleGoToMySubscriptions() {
    this.setState({
      saved: false
    });
    this.props.toggleModal(false);
    Router.pushRoute('myrw', { tab: 'subscriptions' });
  }

  @Autobind
  handleNameChange(event) {
    this.setState({
      name: event.target.value
    });
  }

  @Autobind
  handleCancel() {
    this.setState({
      saved: false
    });
    this.props.toggleModal(false);
  }

  render() {
    const {
      areaOptions,
      loadingAreaOptions,
      selectedArea,
      loading,
      saved,
      name,
      datasets,
      loadingDatasets,
      selectedDataset
    } = this.state;
    const { dataset, showDatasetSelector } = this.props;
    let headerText;
    if (saved) {
      headerText = 'Subscription saved!';
    } else if (dataset) {
      headerText = `Subscribe to ${dataset.attributes.name}`;
    } else {
      headerText = 'Subscribe to alerts';
    }
    const paragraphText = saved ?
      'Your subscription was successfully created. Please check your email address to confirm it' :
      'Please enter a name and select an area for the subscription';

    return (

{headerText}

{paragraphText}

        {!saved &&

Subscription name

              <input value={name} onChange={this.handleNameChange} />

              <Spinner isLoading={loadingAreaOptions || loadingDatasets || loading} className="-light -small" />
              <CustomSelect
                placeholder="Select area"
                options={areaOptions}
                onValueChange={this.onChangeSelectedArea}
                allowNonLeafSelection={false}
                value={selectedArea && selectedArea.value}
              />
              {showDatasetSelector &&
                <CustomSelect
                  placeholder="Select a dataset"
                  options={datasets}
                  onValueChange={this.onChangeSelectedDataset}
                  allowNonLeafSelection={false}
                  value={selectedDataset && selectedDataset.value}
                />
              }

        }

        {saved &&

        }

        {!saved &&

            <button className="c-btn -primary" onClick={this.handleSubscribe}>
              Subscribe
            </button>
            <button className="c-btn -secondary" onClick={this.handleCancel}>
              Cancel
            </button>

        }

        {saved &&

            <button className="c-btn -secondary" onClick={this.handleCancel}>
              Ok
            </button>
            <button className="c-btn -primary" onClick={this.handleGoToMySubscriptions}>
              Check my subscriptions
            </button>

        }

    );
  }
}

SubscribeToDatasetModal.propTypes = {
  toggleModal: PropTypes.func.isRequired,
  dataset: PropTypes.object,
  showDatasetSelector: PropTypes.bool.isRequired,
  // Store
  user: PropTypes.object.isRequired
};

const mapStateToProps = state => ({
  user: state.user
});

export default connect(mapStateToProps, null)(SubscribeToDatasetModal);
<|endoftext|>


<commit_before>#!/usr/bin/env -S node --no-warnings
const prettyCLI = require('@tryghost/pretty-cli');
const ui = require('@tryghost/pretty-cli').ui;
const _ = require('lodash');
const chalk = require('chalk');
const gscan = require('../lib');

const options = {
    format: 'cli'
};

let levels;

prettyCLI
    .configure({
        name: 'gscan'
    })
    .groupOrder([
        'Sources:',
        'Utilities:',
        'Commands:',
        'Arguments:',
        'Required Options:',
        'Options:',
        'Global Options:'
    ])
    .positional('<themePath>', {
        paramsDesc: 'Theme folder or .zip file path',
        mustExist: true
    })
    .boolean('-z, --zip', {
        desc: 'Theme path points to a zip file'
    })
    .boolean('-1, --v1', {
        desc: 'Check theme for Ghost 1.0 compatibility'
    })
    .boolean('-2, --v2', {
        desc: 'Check theme for Ghost 2.0 compatibility'
    })
    .boolean('-3, --v3', {
        desc: 'Check theme for Ghost 3.0 compatibility'
    })
    .boolean('-c, --canary', {
        desc: 'Check theme for upcoming Ghost version compatibility'
    })
    .boolean('-f, --fatal', {
        desc: 'Only show fatal errors that prevent upgrading Ghost'
    })
    .boolean('--verbose', {
        desc: 'Output check details'
    })
    .parseAndExit()
    .then((argv) => {
        if (argv.v1) {
            options.checkVersion = 'v1';
        } else if (argv.v2) {
            options.checkVersion = 'v2';
        } else if (argv.v3) {
            options.checkVersion = 'v3';
        } else if (argv.canary) {
            options.checkVersion = 'canary';
        } else {
            // CASE: set default value
            options.checkVersion = 'latest';
        }

        options.verbose = argv.verbose;
        options.onlyFatalErrors = argv.fatal;

        if (options.onlyFatalErrors) {
            ui.log(chalk.bold('\nChecking theme compatibility (fatal issues only)...'));
        } else {
            ui.log(chalk.bold('\nChecking theme compatibility...'));
        }

        if (argv.zip) {
            gscan.checkZip(argv.themePath, options)
                .then(theme => outputResults(theme, options))
                .catch((error) => {
                    ui.log(error);
                });
        } else {
            gscan.check(argv.themePath, options)
                .then(theme => outputResults(theme, options))
                .catch(function ENOTDIRPredicate(err) {
                    return err.code === 'ENOTDIR';
                }, function (err) {
                    ui.log(err.message);
                    ui.log('Did you mean to add the -z flag to read a zip file?');
                });
        }
    });

levels = {
    error: chalk.red,
    warning: chalk.yellow,
    recommendation: chalk.yellow,
    feature: chalk.green
};

function outputResult(result) {
    ui.log(levels[result.level](`- ${_.capitalize(result.level)}:`), result.rule);

    if (options.verbose) {
        ui.log(`${chalk.bold('\nDetails:')} ${result.details}`);
    }

    if (result.failures && result.failures.length) {
        if (options.verbose) {
            ui.log(''); // extra line-break
            ui.log(`${chalk.bold('Files:')}`);
            result.failures.forEach((failure) => {
                ui.log(`${failure.ref} - ${failure.message}`);
            });
        } else {
            ui.log(`${chalk.bold('Files:')} ${_.map(result.failures, 'ref')}`);
        }
    }

    ui.log(''); // extra line-break
}

function getSummary(theme, options) {
    let summaryText = '';
    const errorCount = theme.results.error.length;
    const warnCount = theme.results.warning.length;
    const pluralize = require('pluralize');
    const checkSymbol = '\u2713';

    if (errorCount === 0 && warnCount === 0) {
        if (options.onlyFatalErrors) {
            summaryText = `${chalk.green(checkSymbol)} Your theme has no fatal compatibility issues with Ghost ${theme.checkedVersion}`;
        } else {
            summaryText = `${chalk.green(checkSymbol)} Your theme is compatible with Ghost ${theme.checkedVersion}`;
        }
    } else {
        summaryText = `Your theme has`;

        if (!_.isEmpty(theme.results.error)) {
            summaryText += chalk.red.bold(` ${pluralize('error', theme.results.error.length, true)}`);
        }

        if (!_.isEmpty(theme.results.error) && !_.isEmpty(theme.results.warning)) {
            summaryText += ' and';
        }

        if (!_.isEmpty(theme.results.warning)) {
            summaryText += chalk.yellow.bold(` ${pluralize('warning', theme.results.warning.length, true)}`);
        }

        summaryText += '!';

        // NOTE: had to subtract the number of 'invisible' formating symbols
        //       needs update if formatting above changes
        const hiddenSymbols = 38;
        summaryText += '\n' + _.repeat('-', (summaryText.length - hiddenSymbols));
    }

    return summaryText;
}

function outputResults(theme, options) {
    try {
        theme = gscan.format(theme, options);
    } catch (err) {
        ui.log.error('Error formating result, some results may be missing.');
        ui.log.error(err);
    }

    let errorCount = theme.results.error.length;

    ui.log('\n' + getSummary(theme, options));

    if (!_.isEmpty(theme.results.error)) {
        ui.log(chalk.red.bold('\nErrors'));
        ui.log(chalk.red.bold('------'));
        ui.log(chalk.red('Important to fix, functionality may be degraded.\n'));

        _.each(theme.results.error, rule => outputResult(rule, options));
    }

    if (!_.isEmpty(theme.results.warning)) {
        ui.log(chalk.yellow.bold('\nWarnings'));
        ui.log(chalk.yellow.bold('--------'));

        _.each(theme.results.warning, rule => outputResult(rule, options));
    }

    if (!_.isEmpty(theme.results.recommendation)) {
        ui.log(chalk.yellow.bold('\nRecommendations'));
        ui.log(chalk.yellow.bold('---------------'));

        _.each(theme.results.recommendation, rule => outputResult(rule, options));
    }

    ui.log(`\nGet more help at ${chalk.cyan.underline('https://ghost.org/docs/api/handlebars-themes/')}`);
    ui.log(`You can also check theme compatibility at ${chalk.cyan.underline('https://gscan.ghost.org/')}`);

    // The CLI feature is mainly used to run gscan programatically in tests within themes.
    // Exiting with error code for warnings, causes the test to fail, even tho a theme
    // upload via Ghost Admin would be possible without showing errors/warning.
    // See also here: https://github.com/TryGhost/Blog/pull/41#issuecomment-484525754
    // TODO: make failing for warnings configurable by e. g. passing an option, so we can
    // disable it for the usage with tests
    if (errorCount > 0) {
        process.exit(1);
    } else {
        process.exit(0);
    }
}
<commit_msg>OS-agnostic warning suppression<commit_after>#!/usr/bin/env node

// Remove all Node warnings before doing anything else
process.removeAllListeners('warning');

const prettyCLI = require('@tryghost/pretty-cli');
const ui = require('@tryghost/pretty-cli').ui;
const _ = require('lodash');
const chalk = require('chalk');
const gscan = require('../lib');

const options = {
    format: 'cli'
};

let levels;

prettyCLI
    .configure({
        name: 'gscan'
    })
    .groupOrder([
        'Sources:',
        'Utilities:',
        'Commands:',
        'Arguments:',
        'Required Options:',
        'Options:',
        'Global Options:'
    ])
    .positional('<themePath>', {
        paramsDesc: 'Theme folder or .zip file path',
        mustExist: true
    })
    .boolean('-z, --zip', {
        desc: 'Theme path points to a zip file'
    })
    .boolean('-1, --v1', {
        desc: 'Check theme for Ghost 1.0 compatibility'
    })
    .boolean('-2, --v2', {
        desc: 'Check theme for Ghost 2.0 compatibility'
    })
    .boolean('-3, --v3', {
        desc: 'Check theme for Ghost 3.0 compatibility'
    })
    .boolean('-c, --canary', {
        desc: 'Check theme for upcoming Ghost version compatibility'
    })
    .boolean('-f, --fatal', {
        desc: 'Only show fatal errors that prevent upgrading Ghost'
    })
    .boolean('--verbose', {
        desc: 'Output check details'
    })
    .parseAndExit()
    .then((argv) => {
        if (argv.v1) {
            options.checkVersion = 'v1';
        } else if (argv.v2) {
            options.checkVersion = 'v2';
        } else if (argv.v3) {
            options.checkVersion = 'v3';
        } else if (argv.canary) {
            options.checkVersion = 'canary';
        } else {
            // CASE: set default value
            options.checkVersion = 'latest';
        }

        options.verbose = argv.verbose;
        options.onlyFatalErrors = argv.fatal;

        if (options.onlyFatalErrors) {
            ui.log(chalk.bold('\nChecking theme compatibility (fatal issues only)...'));
        } else {
            ui.log(chalk.bold('\nChecking theme compatibility...'));
        }

        if (argv.zip) {
            gscan.checkZip(argv.themePath, options)
                .then(theme => outputResults(theme, options))
                .catch((error) => {
                    ui.log(error);
                });
        } else {
            gscan.check(argv.themePath, options)
                .then(theme => outputResults(theme, options))
                .catch(function ENOTDIRPredicate(err) {
                    return err.code === 'ENOTDIR';
                }, function (err) {
                    ui.log(err.message);
                    ui.log('Did you mean to add the -z flag to read a zip file?');
                });
        }
    });

levels = {
    error: chalk.red,
    warning: chalk.yellow,
    recommendation: chalk.yellow,
    feature: chalk.green
};

function outputResult(result) {
    ui.log(levels[result.level](`- ${_.capitalize(result.level)}:`), result.rule);

    if (options.verbose) {
        ui.log(`${chalk.bold('\nDetails:')} ${result.details}`);
    }

    if (result.failures && result.failures.length) {
        if (options.verbose) {
            ui.log(''); // extra line-break
            ui.log(`${chalk.bold('Files:')}`);
            result.failures.forEach((failure) => {
                ui.log(`${failure.ref} - ${failure.message}`);
            });
        } else {
            ui.log(`${chalk.bold('Files:')} ${_.map(result.failures, 'ref')}`);
        }
    }

    ui.log(''); // extra line-break
}

function getSummary(theme, options) {
    let summaryText = '';
    const errorCount = theme.results.error.length;
    const warnCount = theme.results.warning.length;
    const pluralize = require('pluralize');
    const checkSymbol = '\u2713';

    if (errorCount === 0 && warnCount === 0) {
        if (options.onlyFatalErrors) {
            summaryText = `${chalk.green(checkSymbol)} Your theme has no fatal compatibility issues with Ghost ${theme.checkedVersion}`;
        } else {
            summaryText = `${chalk.green(checkSymbol)} Your theme is compatible with Ghost ${theme.checkedVersion}`;
        }
    } else {
        summaryText = `Your theme has`;

        if (!_.isEmpty(theme.results.error)) {
            summaryText += chalk.red.bold(` ${pluralize('error', theme.results.error.length, true)}`);
        }

        if (!_.isEmpty(theme.results.error) && !_.isEmpty(theme.results.warning)) {
            summaryText += ' and';
        }

        if (!_.isEmpty(theme.results.warning)) {
            summaryText += chalk.yellow.bold(` ${pluralize('warning', theme.results.warning.length, true)}`);
        }

        summaryText += '!';

        // NOTE: had to subtract the number of 'invisible' formating symbols
        //       needs update if formatting above changes
        const hiddenSymbols = 38;
        summaryText += '\n' + _.repeat('-', (summaryText.length - hiddenSymbols));
    }

    return summaryText;
}

function outputResults(theme, options) {
    try {
        theme = gscan.format(theme, options);
    } catch (err) {
        ui.log.error('Error formating result, some results may be missing.');
        ui.log.error(err);
    }

    let errorCount = theme.results.error.length;

    ui.log('\n' + getSummary(theme, options));

    if (!_.isEmpty(theme.results.error)) {
        ui.log(chalk.red.bold('\nErrors'));
        ui.log(chalk.red.bold('------'));
        ui.log(chalk.red('Important to fix, functionality may be degraded.\n'));

        _.each(theme.results.error, rule => outputResult(rule, options));
    }

    if (!_.isEmpty(theme.results.warning)) {
        ui.log(chalk.yellow.bold('\nWarnings'));
        ui.log(chalk.yellow.bold('--------'));

        _.each(theme.results.warning, rule => outputResult(rule, options));
    }

    if (!_.isEmpty(theme.results.recommendation)) {
        ui.log(chalk.yellow.bold('\nRecommendations'));
        ui.log(chalk.yellow.bold('---------------'));

        _.each(theme.results.recommendation, rule => outputResult(rule, options));
    }

    ui.log(`\nGet more help at ${chalk.cyan.underline('https://ghost.org/docs/api/handlebars-themes/')}`);
    ui.log(`You can also check theme compatibility at ${chalk.cyan.underline('https://gscan.ghost.org/')}`);

    // The CLI feature is mainly used to run gscan programatically in tests within themes.
    // Exiting with error code for warnings, causes the test to fail, even tho a theme
    // upload via Ghost Admin would be possible without showing errors/warning.
    // See also here: https://github.com/TryGhost/Blog/pull/41#issuecomment-484525754
    // TODO: make failing for warnings configurable by e. g. passing an option, so we can
    // disable it for the usage with tests
    if (errorCount > 0) {
        process.exit(1);
    } else {
        process.exit(0);
    }
}
<|endoftext|>


<commit_before>goog.provide('Main');
// Core
goog.require('Blockly');
// Blocks
goog.require('Blockly.Constants.Logic');
goog.require('Blockly.Constants.Loops');
goog.require('Blockly.Constants.Math');
goog.require('Blockly.Constants.Text');
goog.require('Blockly.Constants.Lists');
goog.require('Blockly.Constants.Colour');
goog.require('Blockly.Constants.Variables');
goog.require('Blockly.Constants.VariablesDynamic');
goog.require('Blockly.Blocks.procedures');

Main.init = function() {
  Blockly.inject('blocklyDiv', {
    'toolbox': document.getElementById('toolbox')
  });
};
window.addEventListener('load', Main.init);
<commit_msg>Fix compile test.<commit_after>goog.provide('Main');
// Core
goog.require('Blockly');
goog.require('Blockly.FieldDropdown');
goog.require('Blockly.FieldNumber');
goog.require('Blockly.FieldTextInput');
goog.require('Blockly.FieldVariable');
// Blocks
goog.require('Blockly.Constants.Logic');
goog.require('Blockly.Constants.Loops');
goog.require('Blockly.Constants.Math');
goog.require('Blockly.Constants.Text');
goog.require('Blockly.Constants.Lists');
goog.require('Blockly.Constants.Colour');
goog.require('Blockly.Constants.Variables');
goog.require('Blockly.Constants.VariablesDynamic');
goog.require('Blockly.Blocks.procedures');

Main.init = function() {
  Blockly.inject('blocklyDiv', {
    'toolbox': document.getElementById('toolbox')
  });
};
window.addEventListener('load', Main.init);
<|endoftext|>


<commit_before>#!/usr/bin/env node

var argv = require('minimist')(process.argv.slice(2));
var fs = require('fs');
var path = require('path');
var lib = require('../lib');
var config = {};

if (argv._.length === 0) {
  console.error('Usage: project-badge [-b badgefile] [options]');
  process.exit(1);
}

var outstream = fs.createWriteStream(path.join(process.cwd(), argv._[0]));

if (argv.b) {
  var configData = fs.readFileSync(path.join(process.cwd(), argv.b));
  config = JSON.parse(configData.toString());
}

for (var i in argv) {
  if (argv.hasOwnProperty(i) && i !== 'b' && i !== '_')
    config[i] = argv[i];
}

lib.outputToStream(config.type, config, outstream);
<commit_msg>use resolve instead of join<commit_after>#!/usr/bin/env node

var argv = require('minimist')(process.argv.slice(2));
var fs = require('fs');
var path = require('path');
var lib = require('../lib');
var config = {};

if (argv._.length === 0) {
  console.error('Usage: project-badge [-b badgefile] [options]');
  process.exit(1);
}

var outstream = fs.createWriteStream(path.resolve(process.cwd(), argv._[0]));

if (argv.b) {
  var configData = fs.readFileSync(path.resolve(process.cwd(), argv.b));
  config = JSON.parse(configData.toString());
}

for (var i in argv) {
  if (argv.hasOwnProperty(i) && i !== 'b' && i !== '_')
    config[i] = argv[i];
}

lib.outputToStream(config.type, config, outstream);
<|endoftext|>


<commit_before>/**
 * CLI for scaffolding new components
 */

const path = require('path');
const fs = require('fs-extra');
const program = require('commander');
const Cli = require('../internals/cli/cli.js');

program
    .command('new <component> [subComponent...]')
    .description('Creates a new component')
    .option('-c, --class', 'Create class component')
    .option('-p, --pure', 'Create pure component')
    .option('-s, --scss', 'Create scss file')
    .option('-t, --test', 'Create test.js file')
    .option('-d, --data', 'Create data json file')
    .option('-i, --index', 'Create index.js file')
    .option('-C, --componentsFolder <cFolder>', 'Change components folder')
    .option('-I, --append-index', 'Append to index.js')
    .option('-S, --append-scss', 'Append to index.scss')
    .action((component, subComponents, options) => {
        let overrides = {}
        if(options.class) {overrides.createClass = false;}
        if(options.pure) {overrides.createPure = true;}
        if(options.scss) {overrides.createScss = false;}
        if(options.test) {overrides.createTest = false;}
        if(options.index) {overrides.createIndex = false;}
        if(options.appendIndex) {overrides.updateIndexJs = false;}
        if(options.appendScss) {overrides.updateIndexScss = false;}
        if(options.componentsFolder) {overrides.componentsFolder = options.componentsFolder}

        if(subComponents.length) {
            overrides.updateIndexScss = false;
            overrides.updateIndexJs = false;
        }

        const config = require('../internals/config.js')(overrides);
        const rootFolder = path.join(
            config.rootFolder,
            config.appFolder,
            config.componentsFolder
        )
        const paths = [rootFolder, component, ...subComponents];
        const componentPath = path.join(
            rootFolder,
            component,
            ...subComponents
        );

        const cli = new Cli(config);
        if(!cli.validatePath(componentPath)) {
            throw new Error(`Component already exists at: ${componentPath}`);
        }

        cli.createComponent(componentPath, paths[paths.length-1]);
    });

program.command('delete <componentName> [subComponent...]')
    .option('-C, --componentsFolder <cFolder>', 'Change components folder')
    .action((component, subComponents, options) => {
        let overrides = {}
        if(options.componentsFolder) {overrides.componentsFolder = options.componentsFolder}

        const config = require('../internals/config.js')(overrides);
        const rootFolder = path.join(
            config.rootFolder,
            config.appFolder,
            config.componentsFolder
        )
        const paths = [rootFolder, component, ...subComponents];
        const componentPath = path.join(
            rootFolder,
            component,
            ...subComponents
        );

        const cli = new Cli(config);
        if(cli.validatePath(componentPath)) {
            throw new Error(`Component doesn't exist at: ${componentPath}`);
        }

        cli.deleteComponent(componentPath, paths[paths.length-1]);
    });

program.command('publish [componentName]')
    .action(() => {
        const overrides = {
            rootFolder: process.cwd()
        };
        require('babel-register');
        const config = require('../internals/config.js')(overrides);
        const cli = new Cli(config);
        const outputPath = path.join(
            config.rootFolder,
            config.outputPathHtmlFolder
        );
        cli.publishComponents(outputPath);
    })

program.command('scaffold [outputPath]')
    .option('--componentName <componentName>')
    .option('--containerName <containerName>')
    .action((outputPath, options) => {
        const config = require('../internals/config.js')();
        const cli = new Cli(config);
        const rootFolder = path.join(
            config.rootFolder,
            config.appFolder
        )

        const componentName = options.componentName || 'Hero';
        const componentPath = path.join(rootFolder, config.componentsFolder, componentName);
        if(!cli.validatePath(componentPath)) {
            throw new Error(`Component already exists at: ${componentPath}`);
        }
        cli.createComponent(componentPath, componentName);

        const containerName = options.containerName || 'PageHome';
        const containerPath = path.join(rootFolder, config.containersFolder, containerName);
        cli.componentsFolder = path.join(rootFolder, config.containersFolder);
        cli.createComponent(containerPath, containerName);
        const container = path.join(containerPath, `${containerName}.js`);
        cli.scaffoldComponent(container, containerName, componentName);

        const rcFilePath = path.join(process.cwd(), 'internals', '.frontendrc');
        let rcFileContent = fs.readFileSync(rcFilePath, 'utf8');
        if(outputPath) {
            rcFileContent = rcFileContent.replace(
                '"outputPath": "dist/static",',
                `"outputPath": "${outputPath}",`
            )
        }
        fs.writeFileSync(path.join(process.cwd(), '.frontendrc'), rcFileContent);

        if(outputPath) {
            const packageJson = path.join(process.cwd(), 'package.json');
            let packageJsonContent = fs.readFileSync(packageJson, 'utf8').replace(
                /\.\/dist\/raw/g,
                `${outputPath}/raw`
            );
            fs.writeFileSync(packageJson, packageJsonContent);
        }
    })

program.parse(process.argv);
<commit_msg>Rename default container component name<commit_after>/**
 * CLI for scaffolding new components
 */

const path = require('path');
const fs = require('fs-extra');
const program = require('commander');
const Cli = require('../internals/cli/cli.js');

program
    .command('new <component> [subComponent...]')
    .description('Creates a new component')
    .option('-c, --class', 'Create class component')
    .option('-p, --pure', 'Create pure component')
    .option('-s, --scss', 'Create scss file')
    .option('-t, --test', 'Create test.js file')
    .option('-d, --data', 'Create data json file')
    .option('-i, --index', 'Create index.js file')
    .option('-C, --componentsFolder <cFolder>', 'Change components folder')
    .option('-I, --append-index', 'Append to index.js')
    .option('-S, --append-scss', 'Append to index.scss')
    .action((component, subComponents, options) => {
        let overrides = {}
        if(options.class) {overrides.createClass = false;}
        if(options.pure) {overrides.createPure = true;}
        if(options.scss) {overrides.createScss = false;}
        if(options.test) {overrides.createTest = false;}
        if(options.index) {overrides.createIndex = false;}
        if(options.appendIndex) {overrides.updateIndexJs = false;}
        if(options.appendScss) {overrides.updateIndexScss = false;}
        if(options.componentsFolder) {overrides.componentsFolder = options.componentsFolder}

        if(subComponents.length) {
            overrides.updateIndexScss = false;
            overrides.updateIndexJs = false;
        }

        const config = require('../internals/config.js')(overrides);
        const rootFolder = path.join(
            config.rootFolder,
            config.appFolder,
            config.componentsFolder
        )
        const paths = [rootFolder, component, ...subComponents];
        const componentPath = path.join(
            rootFolder,
            component,
            ...subComponents
        );

        const cli = new Cli(config);
        if(!cli.validatePath(componentPath)) {
            throw new Error(`Component already exists at: ${componentPath}`);
        }

        cli.createComponent(componentPath, paths[paths.length-1]);
    });

program.command('delete <componentName> [subComponent...]')
    .option('-C, --componentsFolder <cFolder>', 'Change components folder')
    .action((component, subComponents, options) => {
        let overrides = {}
        if(options.componentsFolder) {overrides.componentsFolder = options.componentsFolder}

        const config = require('../internals/config.js')(overrides);
        const rootFolder = path.join(
            config.rootFolder,
            config.appFolder,
            config.componentsFolder
        )
        const paths = [rootFolder, component, ...subComponents];
        const componentPath = path.join(
            rootFolder,
            component,
            ...subComponents
        );

        const cli = new Cli(config);
        if(cli.validatePath(componentPath)) {
            throw new Error(`Component doesn't exist at: ${componentPath}`);
        }

        cli.deleteComponent(componentPath, paths[paths.length-1]);
    });

program.command('publish [componentName]')
    .action(() => {
        const overrides = {
            rootFolder: process.cwd()
        };
        require('babel-register');
        const config = require('../internals/config.js')(overrides);
        const cli = new Cli(config);
        const outputPath = path.join(
            config.rootFolder,
            config.outputPathHtmlFolder
        );
        cli.publishComponents(outputPath);
    })

program.command('scaffold [outputPath]')
    .option('--componentName <componentName>')
    .option('--containerName <containerName>')
    .action((outputPath, options) => {
        const config = require('../internals/config.js')();
        const cli = new Cli(config);
        const rootFolder = path.join(
            config.rootFolder,
            config.appFolder
        )

        const componentName = options.componentName || 'Hero';
        const componentPath = path.join(rootFolder, config.componentsFolder, componentName);
        if(!cli.validatePath(componentPath)) {
            throw new Error(`Component already exists at: ${componentPath}`);
        }
        cli.createComponent(componentPath, componentName);

        const containerName = options.containerName || 'HomePage';
        const containerPath = path.join(rootFolder, config.containersFolder, containerName);
        cli.componentsFolder = path.join(rootFolder, config.containersFolder);
        cli.createComponent(containerPath, containerName);
        const container = path.join(containerPath, `${containerName}.js`);
        cli.scaffoldComponent(container, containerName, componentName);

        const rcFilePath = path.join(process.cwd(), 'internals', '.frontendrc');
        let rcFileContent = fs.readFileSync(rcFilePath, 'utf8');
        if(outputPath) {
            rcFileContent = rcFileContent.replace(
                '"outputPath": "dist/static",',
                `"outputPath": "${outputPath}",`
            )
        }
        fs.writeFileSync(path.join(process.cwd(), '.frontendrc'), rcFileContent);

        if(outputPath) {
            const packageJson = path.join(process.cwd(), 'package.json');
            let packageJsonContent = fs.readFileSync(packageJson, 'utf8').replace(
                /\.\/dist\/raw/g,
                `${outputPath}/raw`
            );
            fs.writeFileSync(packageJson, packageJsonContent);
        }
    })

program.parse(process.argv);
<|endoftext|>


<commit_before>#! /usr/bin/env node

const commander = require('commander')
const program = require('../lib/program')
const showWinnerImage = require('../lib/modules/show-winner-image')
const API_URL = process.env.API_URL
const PROFILE_URL = process.env.PROFILE_URL
const url = require('url')

commander
  .usage('--url [EVENT_URL] --meetup-name [MEETUP_NAME] --event-id [EVENT_ID]')
  .option('-m, --meetup-name <MEETUP_NAME>', `meetup's name. For example: banodejs`) // eslint-disable-line quotes
  .option('-e, --event-id <EVENT_ID>', `event's id. For example: 231888421`) // eslint-disable-line quotes
  .option('-u, --url <EVENT_URL>', `event's url. For example:`) // eslint-disable-line quotes
  .parse(process.argv)

if (!commander.url && (!commander.meetupName || !commander.eventId)) {
  commander.help()
}

if (commander.url) {
  const path = url.parse(commander.url).path
  const pathSlices = path.split('/')
  commander.eventId = pathSlices[pathSlices.length - 2]
  commander.meetupName = pathSlices[pathSlices.length - 4]
}

program(API_URL, PROFILE_URL, commander.meetupName, commander.eventId)
  .then(winner => {
    showWinnerImage(winner)
      .catch(error => {
        console.log(`There was an error when we tried to show the winner's image`) // eslint-disable-line quotes
        console.log(`Error description: ${error.message}`) // eslint-disable-line quotes
      })
      .then(winner => console.log(JSON.stringify(winner, null, 2)))
  })
  .catch(error => {
    console.error(error)

    process.exit(1)
  })
<commit_msg>docs(CLI): Add documentation link<commit_after>#! /usr/bin/env node

const commander = require('commander')
const program = require('../lib/program')
const showWinnerImage = require('../lib/modules/show-winner-image')
const API_URL = process.env.API_URL
const PROFILE_URL = process.env.PROFILE_URL
const url = require('url')

commander
  .usage('--url [EVENT_URL] --meetup-name [MEETUP_NAME] --event-id [EVENT_ID]')
  .option('-m, --meetup-name <MEETUP_NAME>', `meetup's name. For example: banodejs`) // eslint-disable-line quotes
  .option('-e, --event-id <EVENT_ID>', `event's id. For example: 231888421`) // eslint-disable-line quotes
  .option('-u, --url <EVENT_URL>', `event's url. For example: www.meetup.com/es-ES/banodejs/events/231097952`) // eslint-disable-line quotes
  .parse(process.argv)

if (!commander.url && (!commander.meetupName || !commander.eventId)) {
  commander.help()
}

if (commander.url) {
  const path = url.parse(commander.url).path
  const pathSlices = path.split('/')
  commander.eventId = pathSlices[pathSlices.length - 2]
  commander.meetupName = pathSlices[pathSlices.length - 4]
}

program(API_URL, PROFILE_URL, commander.meetupName, commander.eventId)
  .then(winner => {
    showWinnerImage(winner)
      .catch(error => {
        console.log(`There was an error when we tried to show the winner's image`) // eslint-disable-line quotes
        console.log(`Error description: ${error.message}`) // eslint-disable-line quotes
      })
      .then(winner => console.log(JSON.stringify(winner, null, 2)))
  })
  .catch(error => {
    console.error(error)

    process.exit(1)
  })
<|endoftext|>