/* global wpforms_gutenberg_form_selector */
/* jshint es3: false, esversion: 6 */
/**
* @param wpforms_gutenberg_form_selector.route_namespace
* @param strings.theme_name
* @param strings.theme_delete
* @param strings.theme_delete_title
* @param strings.theme_delete_confirm
* @param strings.theme_delete_cant_undone
* @param strings.theme_delete_yes
* @param strings.theme_copy
* @param strings.theme_custom
* @param strings.theme_noname
* @param strings.button_background
* @param strings.button_text
* @param strings.field_label
* @param strings.field_sublabel
* @param strings.field_border
*/
/**
* Gutenberg editor block.
*
* Themes panel module.
*
* @since 1.8.8
*/
export default ( function( document, window, $ ) {
/**
* WP core components.
*
* @since 1.8.8
*/
const { PanelBody, ColorIndicator, TextControl, Button } = wp.components;
const { __experimentalRadio: Radio, __experimentalRadioGroup: RadioGroup } = wp.components;
/**
* Localized data aliases.
*
* @since 1.8.8
*/
const { isPro, isLicenseActive, strings, route_namespace: routeNamespace } = wpforms_gutenberg_form_selector;
/**
* Form selector common module.
*
* @since 1.8.8
*
* @type {Object}
*/
let formSelectorCommon = null;
/**
* Runtime state.
*
* @since 1.8.8
*
* @type {Object}
*/
const state = {};
/**
* Themes data.
*
* @since 1.8.8
*
* @type {Object}
*/
const themesData = {
wpforms: null,
custom: null,
};
/**
* Enabled themes.
*
* @since 1.8.8
*
* @type {Object}
*/
let enabledThemes = null;
/**
* Elements holder.
*
* @since 1.8.8
*
* @type {Object}
*/
const el = {};
/**
* Public functions and properties.
*
* @since 1.8.8
*
* @type {Object}
*/
const app = {
/**
* Initialize panel.
*
* @since 1.8.8
*/
init() {
el.$window = $( window );
app.fetchThemesData();
$( app.ready );
},
/**
* Document ready.
*
* @since 1.8.8
*/
ready() {
app.events();
},
/**
* Events.
*
* @since 1.8.8
*/
events() {
wp.data.subscribe( function() { // eslint-disable-line complexity
const isSavingPost = wp.data.select( 'core/editor' )?.isSavingPost();
const isAutosavingPost = wp.data.select( 'core/editor' )?.isAutosavingPost();
const isSavingWidget = wp.data.select( 'core/edit-widgets' )?.isSavingWidgetAreas();
const currentPost = wp.data.select( 'core/editor' )?.getCurrentPost();
const isBlockOrTemplate = currentPost?.type?.includes( 'wp_template' ) || currentPost?.type?.includes( 'wp_block' );
if ( ( ! isSavingPost && ! isSavingWidget && ! isBlockOrTemplate ) || isAutosavingPost ) {
return;
}
if ( isBlockOrTemplate ) {
// Delay saving if this is FSE for better performance.
_.debounce( app.saveCustomThemes, 500 )();
return;
}
app.saveCustomThemes();
} );
},
/**
* Get all themes data.
*
* @since 1.8.8
*
* @return {Object} Themes data.
*/
getAllThemes() {
return { ...( themesData.custom || {} ), ...( themesData.wpforms || {} ) };
},
/**
* Get theme data.
*
* @since 1.8.8
*
* @param {string} slug Theme slug.
*
* @return {Object|null} Theme settings.
*/
getTheme( slug ) {
return app.getAllThemes()[ slug ] || null;
},
/**
* Get enabled themes data.
*
* @since 1.8.8
*
* @return {Object} Themes data.
*/
getEnabledThemes() {
if ( enabledThemes ) {
return enabledThemes;
}
const allThemes = app.getAllThemes();
if ( isPro && isLicenseActive ) {
return allThemes;
}
enabledThemes = Object.keys( allThemes ).reduce( ( acc, key ) => {
if ( allThemes[ key ].settings?.fieldSize && ! allThemes[ key ].disabled ) {
acc[ key ] = allThemes[ key ];
}
return acc;
}, {} );
return enabledThemes;
},
/**
* Update enabled themes.
*
* @since 1.8.8
*
* @param {string} slug Theme slug.
* @param {Object} theme Theme settings.
*/
updateEnabledThemes( slug, theme ) {
if ( ! enabledThemes ) {
return;
}
enabledThemes = {
...enabledThemes,
[ slug ]: theme,
};
},
/**
* Whether the theme is disabled.
*
* @since 1.8.8
*
* @param {string} slug Theme slug.
*
* @return {boolean} True if the theme is disabled.
*/
isDisabledTheme( slug ) {
return ! app.getEnabledThemes()?.[ slug ];
},
/**
* Whether the theme is one of the WPForms themes.
*
* @since 1.8.8
*
* @param {string} slug Theme slug.
*
* @return {boolean} True if the theme is one of the WPForms themes.
*/
isWPFormsTheme( slug ) {
return Boolean( themesData.wpforms[ slug ]?.settings );
},
/**
* Fetch themes data from API.
*
* @since 1.8.8
*/
fetchThemesData() {
// If a fetch is already in progress, exit the function.
if ( state.isFetchingThemes || themesData.wpforms ) {
return;
}
// Set the flag to true indicating a fetch is in progress.
state.isFetchingThemes = true;
try {
// Fetch themes data.
wp.apiFetch( {
path: routeNamespace + 'themes/',
method: 'GET',
cache: 'no-cache',
} )
.then( ( response ) => {
themesData.wpforms = response.wpforms || {};
themesData.custom = response.custom || {};
} )
.catch( ( error ) => {
// eslint-disable-next-line no-console
console.error( error?.message );
} )
.finally( () => {
state.isFetchingThemes = false;
} );
} catch ( error ) {
// eslint-disable-next-line no-console
console.error( error );
}
},
/**
* Save custom themes.
*
* @since 1.8.8
*/
saveCustomThemes() {
// Custom themes do not exist.
if ( state.isSavingThemes || ! themesData.custom ) {
return;
}
// Set the flag to true indicating a saving is in progress.
state.isSavingThemes = true;
try {
// Save themes.
wp.apiFetch( {
path: routeNamespace + 'themes/custom/',
method: 'POST',
data: { customThemes: themesData.custom },
} )
.then( ( response ) => {
if ( ! response?.result ) {
// eslint-disable-next-line no-console
console.log( response?.error );
}
} )
.catch( ( error ) => {
// eslint-disable-next-line no-console
console.error( error?.message );
} )
.finally( () => {
state.isSavingThemes = false;
} );
} catch ( error ) {
// eslint-disable-next-line no-console
console.error( error );
}
},
/**
* Get the current style attributes state.
*
* @since 1.8.8
*
* @param {Object} props Block properties.
*
* @return {boolean} Whether the custom theme is created.
*/
getCurrentStyleAttributes( props ) {
const defaultAttributes = Object.keys( themesData.wpforms.default?.settings );
const currentStyleAttributes = {};
for ( const key in defaultAttributes ) {
const attr = defaultAttributes[ key ];
currentStyleAttributes[ attr ] = props.attributes[ attr ] ?? '';
}
return currentStyleAttributes;
},
/**
* Maybe create custom theme.
*
* @since 1.8.8
*
* @param {Object} props Block properties.
*
* @return {boolean} Whether the custom theme is created.
*/
maybeCreateCustomTheme( props ) { // eslint-disable-line complexity
const currentStyles = app.getCurrentStyleAttributes( props );
const isWPFormsTheme = !! themesData.wpforms[ props.attributes.theme ];
const isCustomTheme = !! themesData.custom[ props.attributes.theme ];
let migrateToCustomTheme = false;
// It is one of the default themes without any changes.
if (
isWPFormsTheme &&
JSON.stringify( themesData.wpforms[ props.attributes.theme ]?.settings ) === JSON.stringify( currentStyles )
) {
return false;
}
const prevAttributes = formSelectorCommon.getBlockRuntimeStateVar( props.clientId, 'prevAttributesState' );
// It is a block added in FS 1.0, so it doesn't have a theme.
// The `prevAttributes` is `undefined` means that we are in the first render of the existing block.
if ( props.attributes.theme === 'default' && props.attributes.themeName === '' && ! prevAttributes ) {
migrateToCustomTheme = true;
}
// It is a modified default theme OR unknown custom theme.
if ( isWPFormsTheme || ! isCustomTheme || migrateToCustomTheme ) {
app.createCustomTheme( props, currentStyles, migrateToCustomTheme );
}
return true;
},
/**
* Create custom theme.
*
* @since 1.8.8
*
* @param {Object} props Block properties.
* @param {Object} currentStyles Current style settings.
* @param {boolean} migrateToCustomTheme Whether it is needed to migrate to custom theme.
*
* @return {boolean} Whether the custom theme is created.
*/
createCustomTheme( props, currentStyles = null, migrateToCustomTheme = false ) { // eslint-disable-line complexity
let counter = 0;
let themeSlug = props.attributes.theme;
const baseTheme = app.getTheme( props.attributes.theme ) || themesData.wpforms.default;
let themeName = baseTheme.name;
themesData.custom = themesData.custom || {};
if ( migrateToCustomTheme ) {
themeSlug = 'custom';
themeName = strings.theme_custom;
}
// Determine the theme slug and the number of copies.
do {
counter++;
themeSlug = themeSlug + '-copy-' + counter;
} while ( themesData.custom[ themeSlug ] && counter < 10000 );
const copyStr = counter < 2 ? strings.theme_copy : strings.theme_copy + ' ' + counter;
themeName += ' (' + copyStr + ')';
// The first migrated Custom Theme should be without `(Copy)` suffix.
themeName = migrateToCustomTheme && counter < 2 ? strings.theme_custom : themeName;
// Add the new custom theme.
themesData.custom[ themeSlug ] = {
name: themeName,
settings: currentStyles || app.getCurrentStyleAttributes( props ),
};
app.updateEnabledThemes( themeSlug, themesData.custom[ themeSlug ] );
// Update the block attributes with the new custom theme settings.
props.setAttributes( {
theme: themeSlug,
themeName,
} );
return true;
},
/**
* Maybe create custom theme by given attributes.
*
* @since 1.8.8
*
* @param {Object} attributes Block properties.
*
* @return {string} New theme's slug.
*/
maybeCreateCustomThemeFromAttributes( attributes ) { // eslint-disable-line complexity
const newThemeSlug = attributes.theme;
const existingTheme = app.getTheme( attributes.theme );
const keys = Object.keys( attributes );
let isExistingTheme = Boolean( existingTheme?.settings );
// Check if the theme already exists and has the same settings.
if ( isExistingTheme ) {
for ( const i in keys ) {
const key = keys[ i ];
if ( ! existingTheme.settings[ key ] || existingTheme.settings[ key ] !== attributes[ key ] ) {
isExistingTheme = false;
break;
}
}
}
// The theme exists and has the same settings.
if ( isExistingTheme ) {
return newThemeSlug;
}
// The theme doesn't exist.
// Normalize the attributes to the default theme settings.
const defaultAttributes = Object.keys( themesData.wpforms.default.settings );
const newSettings = {};
for ( const i in defaultAttributes ) {
const attr = defaultAttributes[ i ];
newSettings[ attr ] = attributes[ attr ] ?? '';
}
// Create a new custom theme.
themesData.custom[ newThemeSlug ] = {
name: attributes.themeName ?? strings.theme_custom,
settings: newSettings,
};
app.updateEnabledThemes( newThemeSlug, themesData.custom[ newThemeSlug ] );
return newThemeSlug;
},
/**
* Update custom theme.
*
* @since 1.8.8
*
* @param {string} attribute Attribute name.
* @param {string} value New attribute value.
* @param {Object} props Block properties.
*/
updateCustomThemeAttribute( attribute, value, props ) { // eslint-disable-line complexity
const themeSlug = props.attributes.theme;
// Skip if it is one of the WPForms themes OR the attribute is not in the theme settings.
if (
themesData.wpforms[ themeSlug ] ||
(
attribute !== 'themeName' &&
! themesData.wpforms.default.settings[ attribute ]
)
) {
return;
}
// Skip if the custom theme doesn't exist.
// It should never happen, only in some unique circumstances.
if ( ! themesData.custom[ themeSlug ] ) {
return;
}
// Update theme data.
if ( attribute === 'themeName' ) {
themesData.custom[ themeSlug ].name = value;
} else {
themesData.custom[ themeSlug ].settings = themesData.custom[ themeSlug ].settings || themesData.wpforms.default.settings;
themesData.custom[ themeSlug ].settings[ attribute ] = value;
}
// Trigger event for developers.
el.$window.trigger( 'wpformsFormSelectorUpdateTheme', [ themeSlug, themesData.custom[ themeSlug ], props ] );
},
/**
* Get Themes panel JSX code.
*
* @since 1.8.8
*
* @param {Object} props Block properties.
* @param {Object} formSelectorCommonModule Common module.
* @param {Object} stockPhotosModule StockPhotos module.
*
* @return {Object} Themes panel JSX code.
*/
getThemesPanel( props, formSelectorCommonModule, stockPhotosModule ) {
// Store common module in app.
formSelectorCommon = formSelectorCommonModule;
state.stockPhotos = stockPhotosModule;
// If there are no themes data, it is necessary to fetch it firstly.
if ( ! themesData.wpforms ) {
app.fetchThemesData();
// Return empty JSX code.
return ( <>> );
}
// Get event handlers.
const handlers = app.getEventHandlers( props );
const showCustomThemeOptions = formSelectorCommonModule.isFullStylingEnabled() && app.maybeCreateCustomTheme( props );
const checked = formSelectorCommonModule.isFullStylingEnabled() ? props.attributes.theme : 'classic';
const isLeadFormsEnabled = formSelectorCommonModule.isLeadFormsEnabled( formSelectorCommonModule.getBlockContainer( props ) );
const displayLeadFormNotice = isLeadFormsEnabled ? 'block' : 'none';
const modernNoticeStyles = displayLeadFormNotice === 'block' ? { display: 'none' } : {};
let classes = formSelectorCommon.getPanelClass( props );
classes += isLeadFormsEnabled ? ' wpforms-lead-forms-enabled' : '';
classes += app.isMac() ? ' wpforms-is-mac' : '';
return (
{ strings.use_modern_notice_head }
{ strings.use_modern_notice_text } { strings.learn_more }
{ strings.lead_forms_panel_notice_head }
{ strings.lead_forms_panel_notice_text }
handlers.selectTheme( value ) }
>
{ app.getThemesItemsJSX( props ) }
{ showCustomThemeOptions && (
<>
handlers.changeThemeName( value ) }
/>
{ strings.theme_delete }
>
) }
);
},
/**
* Get the Themes panel items JSX code.
*
* @since 1.8.8
*
* @param {Object} props Block properties.
*
* @return {Array} Themes items JSX code.
*/
getThemesItemsJSX( props ) { // eslint-disable-line complexity
const allThemesData = app.getAllThemes();
if ( ! allThemesData ) {
return [];
}
const itemsJsx = [];
const themes = Object.keys( allThemesData );
let theme, firstThemeSlug;
// Display the current custom theme on the top of the list.
if ( ! app.isWPFormsTheme( props.attributes.theme ) ) {
firstThemeSlug = props.attributes.theme;
itemsJsx.push(
app.getThemesItemJSX(
props.attributes.theme,
app.getTheme( props.attributes.theme )
)
);
}
for ( const key in themes ) {
const slug = themes[ key ];
// Skip the first theme.
if ( firstThemeSlug && firstThemeSlug === slug ) {
continue;
}
// Ensure that all the theme settings are present.
theme = { ...allThemesData.default, ...( allThemesData[ slug ] || {} ) };
theme.settings = { ...allThemesData.default.settings, ...( theme.settings || {} ) };
itemsJsx.push( app.getThemesItemJSX( slug, theme ) );
}
return itemsJsx;
},
/**
* Get the Themes panel's single item JSX code.
*
* @since 1.8.8
*
* @param {string} slug Theme slug.
* @param {Object} theme Theme data.
*
* @return {Object|null} Themes panel single item JSX code.
*/
getThemesItemJSX( slug, theme ) {
if ( ! theme ) {
return null;
}
const title = theme.name?.length > 0 ? theme.name : strings.theme_noname;
return (
);
},
/**
* Set block theme.
*
* @since 1.8.8
*
* @param {Object} props Block properties.
* @param {string} themeSlug The theme slug.
*
* @return {boolean} True on success.
*/
setBlockTheme( props, themeSlug ) {
if ( app.maybeDisplayUpgradeModal( themeSlug ) ) {
return false;
}
const theme = app.getTheme( themeSlug );
if ( ! theme?.settings ) {
return false;
}
const attributes = Object.keys( theme.settings );
const block = formSelectorCommon.getBlockContainer( props );
const container = block.querySelector( `#wpforms-${ props.attributes.formId }` );
// Overwrite block attributes with the new theme settings.
// It is needed to rely on the theme settings only.
const newProps = { ...props, attributes: { ...props.attributes, ...theme.settings } };
// Update the preview with the new theme settings.
for ( const key in attributes ) {
const attr = attributes[ key ];
theme.settings[ attr ] = theme.settings[ attr ] === '0' ? '0px' : theme.settings[ attr ];
formSelectorCommon.updatePreviewCSSVarValue(
attr,
theme.settings[ attr ],
container,
newProps
);
}
// Prepare the new attributes to be set.
const setAttributes = {
theme: themeSlug,
themeName: theme.name,
...theme.settings,
};
if ( props.setAttributes ) {
// Update the block attributes with the new theme settings.
props.setAttributes( setAttributes );
}
// Trigger event for developers.
el.$window.trigger( 'wpformsFormSelectorSetTheme', [ block, themeSlug, props ] );
return true;
},
/**
* Maybe display upgrades modal in Lite.
*
* @since 1.8.8
*
* @param {string} themeSlug The theme slug.
*
* @return {boolean} True if modal was displayed.
*/
maybeDisplayUpgradeModal( themeSlug ) {
if ( ! app.isDisabledTheme( themeSlug ) ) {
return false;
}
if ( ! isPro ) {
formSelectorCommon.education.showProModal( 'themes', strings.themes );
return true;
}
if ( ! isLicenseActive ) {
formSelectorCommon.education.showLicenseModal( 'themes', strings.themes, 'select-theme' );
return true;
}
return false;
},
/**
* Get themes panel event handlers.
*
* @since 1.8.8
*
* @param {Object} props Block properties.
*
* @type {Object}
*/
getEventHandlers( props ) { // eslint-disable-line max-lines-per-function
const commonHandlers = formSelectorCommon.getSettingsFieldsHandlers( props );
const handlers = {
/**
* Select theme event handler.
*
* @since 1.8.8
*
* @param {string} value New attribute value.
*/
selectTheme( value ) {
if ( ! app.setBlockTheme( props, value ) ) {
return;
}
// Maybe open Stock Photo installation window.
state?.stockPhotos?.onSelectTheme( value, props, app, commonHandlers );
const block = formSelectorCommon.getBlockContainer( props );
formSelectorCommon.setTriggerServerRender( false );
commonHandlers.updateCopyPasteContent();
// Trigger event for developers.
el.$window.trigger( 'wpformsFormSelectorSelectTheme', [ block, props, value ] );
},
/**
* Change theme name event handler.
*
* @since 1.8.8
*
* @param {string} value New attribute value.
*/
changeThemeName( value ) {
formSelectorCommon.setTriggerServerRender( false );
props.setAttributes( { themeName: value } );
app.updateCustomThemeAttribute( 'themeName', value, props );
},
/**
* Delete theme event handler.
*
* @since 1.8.8
*/
deleteTheme() {
const deleteThemeSlug = props.attributes.theme;
// Remove theme from the theme storage.
delete themesData.custom[ deleteThemeSlug ];
// Open the confirmation modal window.
app.deleteThemeModal( props, deleteThemeSlug, handlers );
},
};
return handlers;
},
/**
* Open the theme delete confirmation modal window.
*
* @since 1.8.8
*
* @param {Object} props Block properties.
* @param {string} deleteThemeSlug Theme slug.
* @param {Object} handlers Block event handlers.
*/
deleteThemeModal( props, deleteThemeSlug, handlers ) {
const confirm = strings.theme_delete_confirm.replace( '%1$s', `${ props.attributes.themeName } ` );
const content = `
${ confirm } ${ strings.theme_delete_cant_undone }
`;
$.confirm( {
title: strings.theme_delete_title,
content,
icon: 'wpforms-exclamation-circle',
type: 'red',
buttons: {
confirm: {
text: strings.theme_delete_yes,
btnClass: 'btn-confirm',
keys: [ 'enter' ],
action() {
// Switch to the default theme.
handlers.selectTheme( 'default' );
// Trigger event for developers.
el.$window.trigger( 'wpformsFormSelectorDeleteTheme', [ deleteThemeSlug, props ] );
},
},
cancel: {
text: strings.cancel,
keys: [ 'esc' ],
},
},
} );
},
/**
* Determine if the user is on a Mac.
*
* @return {boolean} True if the user is on a Mac.
*/
isMac() {
return navigator.userAgent.includes( 'Macintosh' );
},
};
app.init();
// Provide access to public functions/properties.
return app;
}( document, window, jQuery ) );
Oh hey, it looks like you're working on a Contact page.
$primary: #9ebaa0; //
$secondary: #aa9d88; //
$background: #738e96; //
$text: #fff; //
$menu_hover: #9ebaa0;
$menu_hover_text: #fff;
$header_background: #738e96; //
$header_text: #fff; //
$subsection_background: #627c83; //
$subsection_background_hover: #627c83; //
$subsection_text: #d5dde0; //
$active_menu_text: #fff;
$active_submenu_text: #fff;
$text_shadow: 1;
@import "../_admin.scss";
Qatar's schedule for World Cup qualification - Top Lad
Skip to content
The epidemic has caused a delay in the Qatar 2022 World Cup qualification process, but it will start up again in September 2021. All six regions will host World Cup qualifiers over the course of the following ten months in preparation for the 32-team championship that will take place in November and December 2022.
Where is World Cup qualifying for 2022?
AFC (Asia), CAF (Africa), CONCACAF (North America, Central America, and the Caribbean), CONMEBOL (South America), OFC (Oceania), and UEFA are among the six regions (Europe). Qatar, which will host the 2022 World Cup, has already qualified. For the remaining 31 berths, the following regions will compete.
How many teams from each area advance to the Qatar World Cup in 2022?
Teams will compete for the positions open in their regions. 13 teams can proceed in UEFA Europe qualifying, while Africa receives five spots. Three clubs from Concacaf qualifying advance, while four teams each from AFC Asia and Conmebol South America do. In two playoff matches involving one club each from Concacaf, the AFC, Conmebol, and Oceania, the final two teams will qualify in June 2022.
What is the UEFA Europe World Cup qualifying schedule for 2022?
With 10 groups, all 55 European countries started qualifying in March 2021. To make up for the missed World Cup qualifying dates from June 2021, the teams will play a number of games in September, October, and November (the new date for Euro 2020). The World Cup is open to the group winners.
Portugal, Serbia, Ireland, Luxembourg, and Azerbaijan make up Group A.
Spain, Sweden, Greece, Georgia, and Kosovo are in Group B.
Italy, Switzerland, Northern Ireland, Bulgaria, and Lithuania make up Group C.
Group D consists of France, Kazakhstan, Ukraine, Finland, Bosnia and Herzegovina, and
Belgian, Welsh, Czech, Belarusian, and Estonian members of Group E
Denmark, Austria, Scotland, Israel, the Faroe Islands, and Moldova make up Group F.
Netherlands, Turkey, Montenegro, Latvia, Gibraltar, and Group G
Croatia, Slovakia, Russia, Slovenia, Cyprus, and Malta make up Group H.
England, Poland, Hungary, Albania, Andorra, and San Marino make up Group I.
Germany, Romania, Iceland, North Macedonia, Armenia, Liechtenstein are members of Group J.
MD 1-3 Match Dates: March 24-31, 2021
4-6 MD: 1-8 September 2021
MD 7-8: 8–12 October 2021
November 11–16, 2021, MD 9–10
In the 2020–21 UEFA Nations League, the 10 runners-up and the top two group winners will compete in playoffs to determine the final three spots.
What Concacaf World Cup qualifiers are scheduled for 2022?
35 soccer nations from North America, the Caribbean, and Central America make up CONCACAF.
The top five countries, Costa Rica, Honduras, Jamaica, Mexico, and the United States, automatically advanced to the eighth-team competition.
Nations ranked 6-35 in the FIFA global ranking were divided into six groups for the first round of qualifying, and the group winners proceeded to the next round.
El Salvador, Antigua and Barbuda, Grenada, Montserrat, and the US Virgin Islands make up Group A.
Canada, Suriname, Bermuda, the Cayman Islands, and Aruba make up Group B.
Group C: British Virgin Islands, Curaçao, Guatemala, St. Vincent and the Grenadines, and
Barbados, Dominica, Panama, the Dominican Republic, and Anguilla make up Group D.
Group E: Turks and Caicos Islands, Haiti, Nicaragua, Belize, and St. Lucia (withdrew).
Trinidad and Tobago, St. Kitts and Nevis, Guyana, Puerto Rico, and the Bahamas make up Group F.
The three champions—Canada, El Salvador, and Panama—moved on to the third round after those six competed in a home-and-away elimination round.
El Salvador defeated Saint Kitts & Nevis.
Canada defeated Haiti.
Curacao lost to Panama.
There will be a total of 14 home and away games amongst the top eight teams. The top three teams advance automatically, with the fourth-place team competing in the intercontinental playoff in June 2022 against Asia or Oceania.
Schedule for American World Cup qualifying: El Salvador on September 2
Canada on September 5 in Nashville
8 September in Honduras
Jamaica on October 7
10 October in Panama
Costa Rica on October 13
Mexico on November 12
16 November in Jamaica
vs. El Salvador, January 27
Canada on January 30
2.02.2019 vs. Honduras
24 March in Mexico
27 March, vs Panama
30 March in Costa Rica