(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i 0) {
if (response.data.data.length === 1) {
// Auto-select single pixel
var pixelId = response.data.data[0].id;
var pixelName = response.data.data[0].name || '';
_this.saveSettingsFBL4B(pixelId, businessId, pixelName);
} else {
// Multiple pixels — store on config for renderConnectedState to use
window.fbl4bConfig.pendingPixels = response.data.data;
// Force re-render with meaningful state change
_this.setState({fbl4bPixels: response.data.data});
}
} else {
// Store empty array so renderConnectedState shows "no pixels" state
window.fbl4bConfig.pendingPixels = [];
_this.setState({fbl4bPixels: []});
}
},
error: function(jqXHR, textStatus, errorThrown) {
_this.consoleLog('FBL4B: Failed to fetch pixels: ' + errorThrown);
// Store empty array so renderConnectedState shows "no pixels" state
window.fbl4bConfig.pendingPixels = [];
_this.setState({fbl4bPixels: []});
}
});
},
/**
* Clear all FBL4B stored data when user disconnects.
* Calls the delete endpoint and reloads the page.
*/
clearFBL4BData: function clearFBL4BData() {
var _this = this;
jQuery.ajax({
type: 'post',
url: window.fbl4bConfig.deleteConfigKeys,
success: function onSuccess(data, _textStatus, _jqXHR) {
// Clear local config
window.fbl4bConfig.businessId = null;
window.fbl4bConfig.pixelId = null;
window.fbl4bConfig.installed = false;
// Reload page to show fresh onboarding state
window.location.reload();
},
error: function() {
_this.consoleLog('FBL4B: Failed to clear stored data');
// Still reload to show onboarding
window.location.reload();
}
});
},
/**
* Save settings for FBL4B flow.
*
* @param pixelId - The pixel ID to save
* @param businessId - The client_business_id to save
*/
saveSettingsFBL4B: function saveSettingsFBL4B(pixelId, businessId, pixelName) {
var _this = this;
jQuery.ajax({
type: 'post',
url: window.fbl4bConfig.setSaveSettingsRoute,
data: ajaxParam({
pixelId: pixelId || '',
businessId: businessId || '',
pixelName: pixelName || '',
}),
success: function onSuccess(data, _textStatus, _jqXHR) {
var response = data;
if (response.success) {
window.fbl4bConfig.businessId = businessId;
window.fbl4bConfig.pixelId = pixelId;
window.fbl4bConfig.pixelName = pixelName || '';
if (pixelId) {
// Pixel selected — update config and re-render inline
window.fbl4bConfig.installed = true;
window.fbl4bConfig.pendingPixels = null;
_this.setState({pixelId: pixelId, installed: 'true', businessId: businessId});
_this.showEventsManagerSection(pixelId);
// Hide Ads sections for FBL4B (they require MBE scopes)
_this.hideAdsPlugin();
} else {
}
} else {
}
},
error: function () {
_this.consoleLog('FBL4B: There was a problem saving the settings');
_this.showFBL4BNotice('Failed to save connection. Please try again.', 'error');
}
});
},
/**
* Reload the FBL4B iframe with updated parameters.
* Called after settings are saved to show the "connected" state.
* Uses displayBusinessId (the id field) which is what the iframe expects.
*/
reloadFBL4BIframe: function reloadFBL4BIframe(pixelId, displayBusinessId) {
var _this = this;
var baseUrl = window.fbl4bConfig.iframeUrl;
var params = 'app_id=' + window.fbl4bConfig.appId +
'&config_id=' + window.fbl4bConfig.configId +
'&installed=true' +
'&version=' + window.fbl4bConfig.version;
if (displayBusinessId) {
params += '&business_id=' + displayBusinessId;
}
if (pixelId) {
params += '&pixel_id=' + pixelId;
}
var newIframeUrl = baseUrl + params;
// Update the iframe src
jQuery('#fbe-iframe iframe').attr('src', newIframeUrl);
},
saveSettings: function saveSettings( pixelId, accessToken, externalBusinessId ){
var _this = this;
if(!pixelId){
console.error('Meta Business Extension Error: got no pixel_id');
return;
}
if(!accessToken){
console.error('Meta Business Extension Error: got no access token');
return;
}
if(!externalBusinessId){
console.error('Meta Business Extension Error: got no external business id');
return;
}
jQuery.ajax({
type: 'post',
url: window.facebookBusinessExtensionConfig.setSaveSettingsRoute,
async : false,
data: ajaxParam({
pixelId: pixelId,
accessToken: accessToken,
externalBusinessId: externalBusinessId,
}),
success: function onSuccess(data, _textStatus, _jqXHR) {
var response = data;
let msg = '';
if (response.success) {
_this.setState({pixelId: pixelId});
_this.showEventsManagerSection(pixelId);
msg = "The Meta Pixel with ID: " + pixelId + " is now installed on your website.";
} else {
msg = "There was a problem saving the pixel. Please try again";
}
},
error: function () {
console.error('There was a problem saving the pixel with id', pixelId);
}
});
},
deleteFBAssets: function deleteFBAssets() {
var _this = this;
jQuery.ajax({
type: 'delete',
url: window.facebookBusinessExtensionConfig.deleteConfigKeys,
success: function onSuccess(data, _textStatus, _jqXHR) {
let msg = '';
if(data.success) {
msg = data.message;
_this.hideEventsManagerSection();
}else {
msg = data.error_message;
}
_this.setState({installed: 'false'});
},
error: function() {
console.error('There was a problem deleting the connection, Please try again.');
}
});
},
showAdsPlugin: function showAdsPlugin() {
jQuery("#meta-ads-plugin").show();
},
hideAdsPlugin: function hideAdsPlugin() {
jQuery("#meta-ads-plugin").hide();
},
componentDidMount: function componentDidMount() {
this.bindMessageEvents();
// For FBL4B: If we have an access token but missing business_id or pixel_id,
// fetch the missing data before rendering the iframe
if (this.isFBL4BEnabled()) {
this.initFBL4BData();
// Show the cancel link after 5s delay — only if still in loading state
setTimeout(function() {
var cancelLink = document.getElementById('fbl4b-cancel-link');
if (cancelLink) {
cancelLink.style.display = 'block';
}
}, 5000);
// Handle inline pixel selection dropdown changes
jQuery(document).on('change', '#fbl4b-pixel-select-inline', function() {
var btn = jQuery('#fbl4b-select-pixel-inline-btn');
if (jQuery(this).val()) {
btn.prop('disabled', false).removeClass('fbl4b-btn-disabled');
} else {
btn.prop('disabled', true).addClass('fbl4b-btn-disabled');
}
});
}
},
/**
* Initialize FBL4B data on page load.
* Always validates the access token by calling /me API.
* If token is revoked (user disconnected), clears all FBL4B data.
*/
initFBL4BData: function initFBL4BData() {
var _this = this;
var config = window.fbl4bConfig;
// Check if FBL4B is connected (installed flag comes from connectionType)
if (!config.installed) {
return;
}
// Always validate the access token by calling /me API
_this.validateFBL4BConnection(function(isValid) {
if (!isValid) {
_this.consoleLog("FBL4B: Access token is invalid/revoked, clearing stored data...");
_this.clearFBL4BData();
return;
}
// If we already have a pixel selected, verify it's still accessible
if (config.pixelId) {
_this.verifyPixelValidity(config.businessId, config.pixelId);
return;
}
// If we have business_id but no pixel_id, fetch pixels
// This only happens if user abandoned pixel selection
if (config.businessId && !config.pixelId) {
_this.fetchPixelsForBusiness(config.businessId);
return;
}
// If we're missing business_id entirely, fetch it first
if (!config.businessId) {
_this.fetchPixelIdAndSave();
}
});
},
/**
* Validate the FBL4B connection by calling /me API.
* If the access token is revoked, the API will return an error.
*
* @param callback - Callback function that receives a boolean (valid/invalid)
*/
validateFBL4BConnection: function validateFBL4BConnection(callback) {
var _this = this;
jQuery.ajax({
type: 'post',
url: window.fbl4bConfig.validateTokenRoute,
timeout: 30000,
success: function onSuccess(response) {
if (response.success && response.data && response.data.valid) {
// Check if businessId is missing from stored config
var storedBusinessId = window.fbl4bConfig.businessId;
if (!storedBusinessId || storedBusinessId === '') {
window.fbl4bConfig.businessId = response.data.client_business_id;
// Save businessId to backend (partial update), then continue validation
_this.saveSettingsFBL4B(null, response.data.client_business_id);
}
callback(true);
} else {
callback(false);
}
},
error: function(jqXHR, textStatus, errorThrown) {
_this.consoleLog("FBL4B: Token validation failed - " + textStatus);
_this.consoleLog('FBL4B: Token validation error: ' + errorThrown);
callback(false);
}
});
},
/**
* Verify the stored pixel is still accessible via the business's pixel list.
* If the pixel was removed from the connected app on Meta's side,
* it won't appear in the fetch results. Disconnects if no pixels remain,
* otherwise prompts re-selection.
*/
verifyPixelValidity: function verifyPixelStillAccessible(businessId, pixelId) {
var _this = this;
if (!businessId) {
return;
}
jQuery.ajax({
type: 'post',
url: window.fbl4bConfig.fetchPixelsRoute,
data: { businessId: businessId },
timeout: 15000,
success: function(response) {
if (response.success && response.data && response.data.data) {
var pixels = response.data.data;
var found = pixels.some(function(p) { return p.id === pixelId; });
if (!found) {
_this.clearStoredPixel();
window.fbl4bConfig.pixelId = '';
window.fbl4bConfig.pixelName = '';
window.fbl4bConfig.pendingPixels = pixels;
_this.setState({ fbl4bPixelId: '' });
}
} else if (!response.success && response.data && response.data.code === 'no_pixels') {
_this.clearFBL4BData();
}
},
error: function() {
}
});
},
/**
* Save the businessId to backend and reload page.
* Used when businessId was missing from stored config but token is valid.
*/
saveBusinessIdAndReload: function saveBusinessIdAndReload(businessId) {
var _this = this;
var config = window.fbl4bConfig;
jQuery.ajax({
type: 'post',
url: config.setSaveSettingsRoute,
data: {
pixelId: config.pixelId,
businessId: businessId
},
success: function onSuccess(data, _textStatus, _jqXHR) {
window.location.reload();
},
error: function() {
// Continue anyway with updated local config
}
});
},
/**
* Render the connected state UI (plugin-side, not iframe).
* Shows Business ID, Pixel ID with Reconnect and Disconnect options.
*/
renderConnectedState: function renderConnectedState(config, pixels) {
var _this = this;
var disconnectUrl = FBL4B_DISCONNECT_URL + '?business_id=' + (config.businessId || '');
var hasPixel = config.pixelId && config.pixelId !== '';
var needsPixelSelection = !hasPixel && pixels && pixels.length > 0;
var noPixelsAvailable = !hasPixel && pixels && pixels.length === 0;
// Hide the fbe-iframe and render connected state in a sibling container
var iframeEl = document.getElementById('fbe-iframe');
if (iframeEl) {
iframeEl.style.display = 'none';
// Hide the back button if it exists
var backBtnEl = document.getElementById('fbl4b-back-btn');
if (backBtnEl) { backBtnEl.style.display = 'none'; }
// Hide the upgrade banner if it exists (server-rendered, stays after JS transition)
var upgradeBanner = document.querySelector('.fbl4b-upgrade-notice');
if (upgradeBanner) { upgradeBanner.style.display = 'none'; }
// Create or reuse a sibling container for the connected state
var connectedEl = document.getElementById('fbl4b-connected');
if (!connectedEl) {
connectedEl = document.createElement('div');
connectedEl.id = 'fbl4b-connected';
iframeEl.parentNode.insertBefore(connectedEl, iframeEl);
}
connectedEl.style.display = '';
// Render connected state into the sibling container
ReactDOM.render(this._buildConnectedState(config, pixels, hasPixel, needsPixelSelection, noPixelsAvailable, disconnectUrl), connectedEl);
return React.createElement('div', {style: {display: 'none'}});
}
return this._buildConnectedState(config, pixels, hasPixel, needsPixelSelection, noPixelsAvailable, disconnectUrl);
},
_buildConnectedState: function _buildConnectedState(config, pixels, hasPixel, needsPixelSelection, noPixelsAvailable, disconnectUrl) {
var _this = this;
return React.createElement(
'div',
{className: 'fbl4b-connected-container'},
// Header
React.createElement(
'div',
{className: 'fbl4b-connected-header'},
React.createElement(
'div',
{className: 'fbl4b-connected-header-left'},
React.createElement('span', {
className: 'fbl4b-connected-logo',
dangerouslySetInnerHTML: {__html: ''}
}),
React.createElement('span', {className: 'fbl4b-connected-logo-text'}, 'Meta'),
React.createElement('span', {className: 'fbl4b-connected-title'}, 'Your Business is Connected to Meta'),
React.createElement(
'span',
{className: hasPixel ? 'fbl4b-connected-badge' : 'fbl4b-connected-badge fbl4b-connected-badge-setup'},
hasPixel ? '✓ Active' : '⚠ Setup Required'
)
)
),
// Connection details
React.createElement(
'div',
{className: 'fbl4b-connected-details'},
// Business ID row
React.createElement(
'div',
{className: 'fbl4b-connected-row fbl4b-connected-row-border'},
React.createElement(
'div',
{className: 'fbl4b-connected-label'},
React.createElement('span', null, 'Business ID')
),
React.createElement('span', {className: 'fbl4b-connected-value'}, config.businessId)
),
// Pixel row — shows ID when selected, or inline selection when pending
React.createElement(
'div',
{className: needsPixelSelection ? 'fbl4b-connected-row fbl4b-pixel-row-selection' : 'fbl4b-connected-row'},
React.createElement(
'div',
{className: 'fbl4b-connected-label'},
React.createElement('span', null, 'Meta Pixel'),
!hasPixel ? React.createElement(
'span',
{className: 'fbl4b-selection-badge'},
'⚠ Selection required'
) : null
),
hasPixel
? React.createElement('span', {className: 'fbl4b-connected-value'},
config.pixelName ? config.pixelName + ' (' + config.pixelId + ')' : config.pixelId)
: needsPixelSelection
? React.createElement(
'div',
{className: 'fbl4b-pixel-inline-select'},
React.createElement(
'select',
{
id: 'fbl4b-pixel-select-inline',
className: 'fbl4b-pixel-select',
onChange: function(e) {
var btn = document.getElementById('fbl4b-select-pixel-inline-btn');
var selectEl = e.target;
if (e.target.value) {
if (btn) {
btn.disabled = false;
btn.className = 'fbl4b-btn-confirm';
}
selectEl.style.borderColor = '#d1d5db';
selectEl.style.color = '#1c2b33';
} else {
if (btn) {
btn.disabled = true;
btn.className = 'fbl4b-btn-confirm fbl4b-btn-disabled';
}
selectEl.style.borderColor = '#dc2626';
selectEl.style.color = '#dc2626';
}
}
},
React.createElement('option', {value: ''}, 'Select Pixel'),
pixels.map(function(pixel) {
return React.createElement('option', {key: pixel.id, value: pixel.id, 'data-name': pixel.name || ''}, pixel.name + ' (' + pixel.id + ')');
})
),
React.createElement(
'button',
{
id: 'fbl4b-select-pixel-inline-btn',
className: 'fbl4b-btn-confirm fbl4b-btn-disabled',
onClick: function() {
var selectEl = document.getElementById('fbl4b-pixel-select-inline');
var selectedPixelId = selectEl ? selectEl.value : '';
if (selectedPixelId) {
var selectedOption = selectEl.options[selectEl.selectedIndex];
var selectedPixelName = selectedOption ? selectedOption.getAttribute('data-name') || '' : '';
_this.saveSettingsFBL4B(selectedPixelId, config.businessId, selectedPixelName);
}
}
},
'Confirm'
)
)
: null
),
// Helper text below pixel row
needsPixelSelection ? React.createElement(
'p',
{className: 'fbl4b-pixel-helper-text'},
'Select a pixel to use on this WordPress site to start tracking conversions.'
) : null,
// No pixels alert
noPixelsAvailable ? React.createElement(
'div',
{className: 'fbl4b-pixel-alert', style: {marginTop: '8px'}},
React.createElement('p', {className: 'fbl4b-pixel-alert-text'},
'⚠️ No Meta Pixels were found for your business. Please create a pixel in ',
React.createElement('a', {href: 'https://business.facebook.com/events_manager', target: '_blank', className: 'fbl4b-link'}, 'Events Manager'),
', then click Refresh.'
),
React.createElement(
'button',
{
onClick: function() {
_this.fetchPixelsForBusiness(config.businessId);
},
className: 'fbl4b-btn-primary',
style: {marginRight: '10px'}
},
'Refresh Pixels'
)
) : null
),
// Connection Settings accordion — collapses Reconnect & Disconnect
React.createElement(
'div',
{className: 'fbl4b-connected-section-last fbl4b-connection-settings'},
React.createElement(
'div',
{
className: 'fbl4b-connection-settings-header',
onClick: function() {
_this.setState({showConnectionSettings: !_this.state.showConnectionSettings});
}
},
React.createElement('span', {className: 'fbl4b-connection-settings-title'}, 'Connection Settings'),
React.createElement('span', {className: 'fbl4b-connection-settings-arrow'}, _this.state.showConnectionSettings ? '▴' : '▾')
),
_this.state.showConnectionSettings ? React.createElement(
'div',
{className: 'fbl4b-connection-settings-body'},
// Reconnect — only when fully connected
hasPixel ? React.createElement(
'div',
{className: 'fbl4b-connection-settings-item'},
React.createElement('h4', {className: 'fbl4b-connected-section-title'}, 'Reconnect'),
React.createElement('p', {className: 'fbl4b-connected-section-desc'},
'Re-run the connection flow to update your linked assets.'
),
!_this.state.showReconnectConfirm
? React.createElement(
'button',
{
onClick: function() {
_this.setState({showReconnectConfirm: true});
},
className: 'fbl4b-btn-primary'
},
'Reconnect'
)
: React.createElement(
'div',
{className: 'fbl4b-reconnect-confirm'},
React.createElement('p', {style: {margin: '0 0 12px 0', fontSize: '14px', color: '#1c2b33', lineHeight: '1.5'}},
'This will start a new authentication flow. Your current pixel configuration will be preserved until you complete the new connection.'
),
React.createElement(
'div',
{style: {display: 'flex', gap: '8px'}},
React.createElement(
'button',
{
onClick: function() {
_this.setState({showReconnectConfirm: false, showReconnectIframe: true});
},
className: 'fbl4b-btn-primary'
},
'Continue'
),
React.createElement(
'button',
{
onClick: function() {
_this.setState({showReconnectConfirm: false});
},
className: 'fbl4b-btn-secondary'
},
'Cancel'
)
)
)
) : null,
// Disconnect
React.createElement(
'div',
{className: 'fbl4b-connection-settings-item'},
React.createElement('h4', {className: 'fbl4b-connected-section-title'}, 'Disconnect'),
React.createElement(
'p',
{className: 'fbl4b-connected-section-desc'},
'This will remove the connection between this WordPress site and your Meta Business Portfolio. Your Pixel and other assets will remain accessible in ',
React.createElement('a', {href: FBL4B_BUSINESS_MANAGER_URL + '?business_id=' + (config.businessId || ''), target: '_blank', className: 'fbl4b-link'}, 'Meta Business Manager'),
'.'
),
!_this.state.showDisconnectWarning
? React.createElement(
'button',
{
onClick: function() {
_this.setState({showDisconnectWarning: true});
},
className: 'fbl4b-btn-danger'
},
'Disconnect from Meta'
)
: React.createElement(
'div',
{className: 'fbl4b-reconnect-confirm'},
React.createElement('p', {className: 'fbl4b-disconnect-warning-title'}, '⚠️ To complete disconnection:'),
React.createElement(
'ol',
{className: 'fbl4b-disconnect-warning-steps'},
React.createElement('li', null, 'Click "Continue" below to open Meta Business Settings'),
React.createElement('li', null, 'Find and remove this WordPress integration'),
React.createElement('li', null, 'Return here and refresh this page')
),
React.createElement(
'div',
{style: {display: 'flex', gap: '8px', marginTop: '12px'}},
React.createElement(
'button',
{
onClick: function() {
window.open(disconnectUrl, '_blank');
},
className: 'fbl4b-btn-danger'
},
'Continue to Meta Business Settings'
),
React.createElement(
'button',
{
onClick: function() {
_this.setState({showDisconnectWarning: false});
},
className: 'fbl4b-btn-secondary'
},
'Cancel'
)
)
)
)
) : null
)
);
},
/**
* Show a WordPress-style admin notice for FBL4B events.
* @param {string} message - The message to display
* @param {string} type - 'error', 'success', or 'warning'
*/
showFBL4BNotice: function showFBL4BNotice(message, type) {
// Remove any existing FBL4B notices
var existing = document.querySelectorAll('.fbl4b-notice');
existing.forEach(function(el) { el.remove(); });
var notice = document.createElement('div');
notice.className = 'fbl4b-notice fbl4b-notice-' + type;
var messageEl = document.createElement('p');
messageEl.textContent = message;
notice.appendChild(messageEl);
var dismissBtn = document.createElement('button');
dismissBtn.className = 'fbl4b-notice-dismiss';
dismissBtn.textContent = '×';
dismissBtn.onclick = function() { notice.remove(); };
notice.appendChild(dismissBtn);
// Insert before the iframe container
var iframeContainer = document.getElementById('fbe-iframe');
if (iframeContainer && iframeContainer.parentNode) {
iframeContainer.parentNode.insertBefore(notice, iframeContainer);
}
// Auto-dismiss after 10 seconds
setTimeout(function() {
if (notice.parentNode) { notice.remove(); }
}, 10000);
},
consoleLog: function consoleLog(message) {
var debug = this.isFBL4BEnabled()
? window.fbl4bConfig.debug
: window.facebookBusinessExtensionConfig.debug;
if(debug) {
console.log(message);
}
},
/**
* Build query params for FBL4B iframe.
* FBL4B uses a simplified set of params compared to MBE.
* When we have pixelId, include business_id and pixel_id.
*/
queryParamsFBL4B: function queryParamsFBL4B() {
var config = window.fbl4bConfig;
// app_id and config_id are already in the iframe URL from PHP
var params = '&version=' + config.version;
if (config.pixelId) {
params += '&installed=true';
if (config.businessId) {
params += '&business_id=' + config.businessId;
}
params += '&pixel_id=' + config.pixelId;
} else {
params += '&installed=false';
}
return params;
},
/**
* Build query params for legacy MBE iframe.
*/
queryParams: function queryParams() {
return 'app_id='+window.facebookBusinessExtensionConfig.appId +
'&timezone='+window.facebookBusinessExtensionConfig.timeZone+
'&external_business_id='+window.facebookBusinessExtensionConfig.externalBusinessId+
'&installed='+this.state.installed+
'&system_user_name='+window.facebookBusinessExtensionConfig.systemUserName+
'&business_vertical='+window.facebookBusinessExtensionConfig.businessVertical+
'&version='+window.facebookBusinessExtensionConfig.version+
'¤cy='+ window.facebookBusinessExtensionConfig.currency +
'&business_name='+ window.facebookBusinessExtensionConfig.businessName +
'&channel=' + window.facebookBusinessExtensionConfig.channel +
'&hide_create_ad_button=' + true;
},
render: function render() {
var _this = this;
try {
// For FBL4B: Check if we need to show pixel selection instead of iframe
if (_this.isFBL4BEnabled()) {
var config = window.fbl4bConfig;
// Debug: Log all config values
// If we have access token but missing pixel_id, check if we have
// pending pixels to show for selection. If pendingPixels is set,
// fall through to the connected state which renders the pixel selection UI.
// If still fetching (pendingPixels not set), show loading placeholder.
if (config.installed && !config.pixelId) {
// If pendingPixels is set (including empty array), the pixel fetch
// is complete — fall through to renderConnectedState which handles
// pixel selection, no-pixels, and connected states.
if (config.pendingPixels !== undefined && config.pendingPixels !== null) {
return _this.renderConnectedState(config, config.pendingPixels);
}
// Still fetching pixels — show loading placeholder
return React.createElement(
'div',
{
id: 'fbl4b-loading',
className: 'fbl4b-loading-container'
},
React.createElement('p', {className: 'fbl4b-loading-text'},
'Configuring your Meta Business connection...'
),
React.createElement(
'div',
{
id: 'fbl4b-cancel-link',
style: {display: 'none'}
},
React.createElement(
'button',
{
onClick: function() {
if (confirm('This will disconnect your Meta Business connection. Continue?')) {
_this.clearFBL4BData();
}
},
className: 'fbl4b-link',
style: {background: 'none', border: 'none', cursor: 'pointer', marginTop: '12px', fontSize: '13px'}
},
'Cancel and start over'
)
)
);
}
// If user clicked Reconnect, show the iframe for re-authentication
if (_this.state.showReconnectIframe) {
// Show the main container and hide the connected state
var iframeEl = document.getElementById('fbe-iframe');
var connectedEl = document.getElementById('fbl4b-connected');
if (iframeEl) { iframeEl.style.display = ''; }
if (connectedEl) { connectedEl.style.display = 'none'; }
var iframeUrl = config.iframeUrl;
// iframeUrl already includes app_id and config_id from PHP
var reconnectParams = '&version=' + config.version + '&installed=false';
// Render back button outside fbe-iframe
var backBtnEl = document.getElementById('fbl4b-back-btn');
if (!backBtnEl) {
backBtnEl = document.createElement('div');
backBtnEl.id = 'fbl4b-back-btn';
backBtnEl.style.margin = '20px 20px 12px 0';
iframeEl.parentNode.insertBefore(backBtnEl, iframeEl);
}
backBtnEl.style.display = '';
ReactDOM.render(
React.createElement(
'button',
{
onClick: function() {
_this.setState({showReconnectIframe: false});
},
className: 'fbl4b-btn-secondary'
},
'← Back to Connection Status'
),
backBtnEl
);
// Render just the iframe inside fbe-iframe
return React.createElement(
'iframe',
{
src: iframeUrl + reconnectParams,
className: 'fbl4b-iframe-full'
}
);
}
// Connected state — show when we have businessId (with or without pixel)
if (config.businessId) {
var pendingPixels = config.pendingPixels || null;
return _this.renderConnectedState(config, pendingPixels);
}
// Onboarding state - show iframe for initial setup
var iframeUrl = config.iframeUrl;
var queryParams = _this.queryParamsFBL4B();
// If upgrading from MBE, show a back button to return to MBE connected state
if (config.upgradeFromMBE) {
return React.createElement(
'div',
null,
React.createElement(
'button',
{
onClick: function() {
// Clear upgrade flag and redirect back without the param
var url = new URL(window.location.href);
url.searchParams.delete('upgrade_to_fbl4b');
window.location.href = url.toString();
},
style: { marginBottom: '16px' },
className: 'fbl4b-btn-secondary'
},
'← Back to Current Connection'
),
React.createElement(
'iframe',
{
src: iframeUrl + queryParams,
className: 'fbl4b-iframe-full'
}
)
);
}
return React.createElement(
'iframe',
{
src: iframeUrl + queryParams,
className: 'fbl4b-iframe-full'
}
);
}
// Legacy MBE flow
var iframeUrl = window.facebookBusinessExtensionConfig.fbeLoginUrl;
var queryParams = _this.queryParams();
return React.createElement(
'iframe',
{
src: iframeUrl + queryParams,
className: 'fbl4b-iframe-full'
}
);
} catch (err) {
console.error(err);
}
},
hideEventsManagerSection: function hideEventsManagerSection() {
jQuery(".events-manager-wrapper").hide();
jQuery('#ad-creation-plugin').hide();
jQuery('#ad-insights-plugin').hide();
jQuery("#fb-adv-conf").hide();
jQuery(".events-manager-wrapper input#pixel-id").val('');
},
showEventsManagerSection: function showEventsManagerSection(pixelId) {
jQuery(".events-manager-wrapper").show();
jQuery('#ad-creation-plugin').show();
jQuery('#ad-insights-plugin').show();
jQuery("#fb-adv-conf").show();
jQuery(".events-manager-wrapper input#pixel-id").val(pixelId);
// Update Events Manager link with the correct pixel ID
var sanitizedPixelId = String(pixelId).replace(/[^0-9]/g, '');
jQuery(".meta-event-manager a").attr("href",
"https://business.facebook.com/events_manager2/list/pixel/" + sanitizedPixelId
);
}
});
// Render
ReactDOM.render(
React.createElement(FBEFlowContainer, null),
document.getElementById('fbe-iframe')
);
// Code to display the above container.
var displayFBModal = function displayFBModal() {
if (FBUtils.isIE()) {
IEOverlay().render();
}
};
(function main() {
// Logic for when to display the container.
if (document.readyState === 'interactive') {
// in case the document is already rendered
displayFBModal();
} else if (document.addEventListener) {
// modern browsers
document.addEventListener('DOMContentLoaded', displayFBModal);
} else {
document.attachEvent('onreadystatechange', function () {
// IE <= 8
if (document.readyState === 'complete') {
displayFBModal();
}
});
}
})();
},{"./IEOverlay":1,"./Modal":2,"./utils":191,"react":190,"react-dom":35}],4:[function(require,module,exports){
(function (process){(function (){
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
'use strict';
var _assign = require('object-assign');
// -- Inlined from fbjs --
var emptyObject = {};
if (process.env.NODE_ENV !== 'production') {
Object.freeze(emptyObject);
}
var validateFormat = function validateFormat(format) {};
if (process.env.NODE_ENV !== 'production') {
validateFormat = function validateFormat(format) {
if (format === undefined) {
throw new Error('invariant requires an error message argument');
}
};
}
function _invariant(condition, format, a, b, c, d, e, f) {
validateFormat(format);
if (!condition) {
var error;
if (format === undefined) {
error = new Error('Minified exception occurred; use the non-minified dev environment ' + 'for the full error message and additional helpful warnings.');
} else {
var args = [a, b, c, d, e, f];
var argIndex = 0;
error = new Error(format.replace(/%s/g, function () {
return args[argIndex++];
}));
error.name = 'Invariant Violation';
}
error.framesToPop = 1; // we don't care about invariant's own frame
throw error;
}
}
var warning = function(){};
if (process.env.NODE_ENV !== 'production') {
var printWarning = function printWarning(format) {
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
var argIndex = 0;
var message = 'Warning: ' + format.replace(/%s/g, function () {
return args[argIndex++];
});
if (typeof console !== 'undefined') {
console.error(message);
}
try {
// --- Welcome to debugging React ---
// This error was thrown as a convenience so that you can use this stack
// to find the callsite that caused this warning to fire.
throw new Error(message);
} catch (x) {}
};
warning = function warning(condition, format) {
if (format === undefined) {
throw new Error('`warning(condition, format, ...args)` requires a warning ' + 'message argument');
}
if (format.indexOf('Failed Composite propType: ') === 0) {
return; // Ignore CompositeComponent proptype check.
}
if (!condition) {
for (var _len2 = arguments.length, args = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
args[_key2 - 2] = arguments[_key2];
}
printWarning.apply(undefined, [format].concat(args));
}
};
}
// /-- Inlined from fbjs --
var MIXINS_KEY = 'mixins';
// Helper function to allow the creation of anonymous functions which do not
// have .name set to the name of the variable being assigned to.
function identity(fn) {
return fn;
}
var ReactPropTypeLocationNames;
if (process.env.NODE_ENV !== 'production') {
ReactPropTypeLocationNames = {
prop: 'prop',
context: 'context',
childContext: 'child context'
};
} else {
ReactPropTypeLocationNames = {};
}
function factory(ReactComponent, isValidElement, ReactNoopUpdateQueue) {
/**
* Policies that describe methods in `ReactClassInterface`.
*/
var injectedMixins = [];
/**
* Composite components are higher-level components that compose other composite
* or host components.
*
* To create a new type of `ReactClass`, pass a specification of
* your new class to `React.createClass`. The only requirement of your class
* specification is that you implement a `render` method.
*
* var MyComponent = React.createClass({
* render: function() {
* return
Hello World
;
* }
* });
*
* The class specification supports a specific protocol of methods that have
* special meaning (e.g. `render`). See `ReactClassInterface` for
* more the comprehensive protocol. Any other properties and methods in the
* class specification will be available on the prototype.
*
* @interface ReactClassInterface
* @internal
*/
var ReactClassInterface = {
/**
* An array of Mixin objects to include when defining your component.
*
* @type {array}
* @optional
*/
mixins: 'DEFINE_MANY',
/**
* An object containing properties and methods that should be defined on
* the component's constructor instead of its prototype (static methods).
*
* @type {object}
* @optional
*/
statics: 'DEFINE_MANY',
/**
* Definition of prop types for this component.
*
* @type {object}
* @optional
*/
propTypes: 'DEFINE_MANY',
/**
* Definition of context types for this component.
*
* @type {object}
* @optional
*/
contextTypes: 'DEFINE_MANY',
/**
* Definition of context types this component sets for its children.
*
* @type {object}
* @optional
*/
childContextTypes: 'DEFINE_MANY',
// ==== Definition methods ====
/**
* Invoked when the component is mounted. Values in the mapping will be set on
* `this.props` if that prop is not specified (i.e. using an `in` check).
*
* This method is invoked before `getInitialState` and therefore cannot rely
* on `this.state` or use `this.setState`.
*
* @return {object}
* @optional
*/
getDefaultProps: 'DEFINE_MANY_MERGED',
/**
* Invoked once before the component is mounted. The return value will be used
* as the initial value of `this.state`.
*
* getInitialState: function() {
* return {
* isOn: false,
* fooBaz: new BazFoo()
* }
* }
*
* @return {object}
* @optional
*/
getInitialState: 'DEFINE_MANY_MERGED',
/**
* @return {object}
* @optional
*/
getChildContext: 'DEFINE_MANY_MERGED',
/**
* Uses props from `this.props` and state from `this.state` to render the
* structure of the component.
*
* No guarantees are made about when or how often this method is invoked, so
* it must not have side effects.
*
* render: function() {
* var name = this.props.name;
* return
Hello, {name}!
;
* }
*
* @return {ReactComponent}
* @required
*/
render: 'DEFINE_ONCE',
// ==== Delegate methods ====
/**
* Invoked when the component is initially created and about to be mounted.
* This may have side effects, but any external subscriptions or data created
* by this method must be cleaned up in `componentWillUnmount`.
*
* @optional
*/
componentWillMount: 'DEFINE_MANY',
/**
* Invoked when the component has been mounted and has a DOM representation.
* However, there is no guarantee that the DOM node is in the document.
*
* Use this as an opportunity to operate on the DOM when the component has
* been mounted (initialized and rendered) for the first time.
*
* @param {DOMElement} rootNode DOM element representing the component.
* @optional
*/
componentDidMount: 'DEFINE_MANY',
/**
* Invoked before the component receives new props.
*
* Use this as an opportunity to react to a prop transition by updating the
* state using `this.setState`. Current props are accessed via `this.props`.
*
* componentWillReceiveProps: function(nextProps, nextContext) {
* this.setState({
* likesIncreasing: nextProps.likeCount > this.props.likeCount
* });
* }
*
* NOTE: There is no equivalent `componentWillReceiveState`. An incoming prop
* transition may cause a state change, but the opposite is not true. If you
* need it, you are probably looking for `componentWillUpdate`.
*
* @param {object} nextProps
* @optional
*/
componentWillReceiveProps: 'DEFINE_MANY',
/**
* Invoked while deciding if the component should be updated as a result of
* receiving new props, state and/or context.
*
* Use this as an opportunity to `return false` when you're certain that the
* transition to the new props/state/context will not require a component
* update.
*
* shouldComponentUpdate: function(nextProps, nextState, nextContext) {
* return !equal(nextProps, this.props) ||
* !equal(nextState, this.state) ||
* !equal(nextContext, this.context);
* }
*
* @param {object} nextProps
* @param {?object} nextState
* @param {?object} nextContext
* @return {boolean} True if the component should update.
* @optional
*/
shouldComponentUpdate: 'DEFINE_ONCE',
/**
* Invoked when the component is about to update due to a transition from
* `this.props`, `this.state` and `this.context` to `nextProps`, `nextState`
* and `nextContext`.
*
* Use this as an opportunity to perform preparation before an update occurs.
*
* NOTE: You **cannot** use `this.setState()` in this method.
*
* @param {object} nextProps
* @param {?object} nextState
* @param {?object} nextContext
* @param {ReactReconcileTransaction} transaction
* @optional
*/
componentWillUpdate: 'DEFINE_MANY',
/**
* Invoked when the component's DOM representation has been updated.
*
* Use this as an opportunity to operate on the DOM when the component has
* been updated.
*
* @param {object} prevProps
* @param {?object} prevState
* @param {?object} prevContext
* @param {DOMElement} rootNode DOM element representing the component.
* @optional
*/
componentDidUpdate: 'DEFINE_MANY',
/**
* Invoked when the component is about to be removed from its parent and have
* its DOM representation destroyed.
*
* Use this as an opportunity to deallocate any external resources.
*
* NOTE: There is no `componentDidUnmount` since your component will have been
* destroyed by that point.
*
* @optional
*/
componentWillUnmount: 'DEFINE_MANY',
/**
* Replacement for (deprecated) `componentWillMount`.
*
* @optional
*/
UNSAFE_componentWillMount: 'DEFINE_MANY',
/**
* Replacement for (deprecated) `componentWillReceiveProps`.
*
* @optional
*/
UNSAFE_componentWillReceiveProps: 'DEFINE_MANY',
/**
* Replacement for (deprecated) `componentWillUpdate`.
*
* @optional
*/
UNSAFE_componentWillUpdate: 'DEFINE_MANY',
// ==== Advanced methods ====
/**
* Updates the component's currently mounted DOM representation.
*
* By default, this implements React's rendering and reconciliation algorithm.
* Sophisticated clients may wish to override this.
*
* @param {ReactReconcileTransaction} transaction
* @internal
* @overridable
*/
updateComponent: 'OVERRIDE_BASE'
};
/**
* Similar to ReactClassInterface but for static methods.
*/
var ReactClassStaticInterface = {
/**
* This method is invoked after a component is instantiated and when it
* receives new props. Return an object to update state in response to
* prop changes. Return null to indicate no change to state.
*
* If an object is returned, its keys will be merged into the existing state.
*
* @return {object || null}
* @optional
*/
getDerivedStateFromProps: 'DEFINE_MANY_MERGED'
};
/**
* Mapping from class specification keys to special processing functions.
*
* Although these are declared like instance properties in the specification
* when defining classes using `React.createClass`, they are actually static
* and are accessible on the constructor instead of the prototype. Despite
* being static, they must be defined outside of the "statics" key under
* which all other static methods are defined.
*/
var RESERVED_SPEC_KEYS = {
displayName: function(Constructor, displayName) {
Constructor.displayName = displayName;
},
mixins: function(Constructor, mixins) {
if (mixins) {
for (var i = 0; i < mixins.length; i++) {
mixSpecIntoComponent(Constructor, mixins[i]);
}
}
},
childContextTypes: function(Constructor, childContextTypes) {
if (process.env.NODE_ENV !== 'production') {
validateTypeDef(Constructor, childContextTypes, 'childContext');
}
Constructor.childContextTypes = _assign(
{},
Constructor.childContextTypes,
childContextTypes
);
},
contextTypes: function(Constructor, contextTypes) {
if (process.env.NODE_ENV !== 'production') {
validateTypeDef(Constructor, contextTypes, 'context');
}
Constructor.contextTypes = _assign(
{},
Constructor.contextTypes,
contextTypes
);
},
/**
* Special case getDefaultProps which should move into statics but requires
* automatic merging.
*/
getDefaultProps: function(Constructor, getDefaultProps) {
if (Constructor.getDefaultProps) {
Constructor.getDefaultProps = createMergedResultFunction(
Constructor.getDefaultProps,
getDefaultProps
);
} else {
Constructor.getDefaultProps = getDefaultProps;
}
},
propTypes: function(Constructor, propTypes) {
if (process.env.NODE_ENV !== 'production') {
validateTypeDef(Constructor, propTypes, 'prop');
}
Constructor.propTypes = _assign({}, Constructor.propTypes, propTypes);
},
statics: function(Constructor, statics) {
mixStaticSpecIntoComponent(Constructor, statics);
},
autobind: function() {}
};
function validateTypeDef(Constructor, typeDef, location) {
for (var propName in typeDef) {
if (typeDef.hasOwnProperty(propName)) {
// use a warning instead of an _invariant so components
// don't show up in prod but only in __DEV__
if (process.env.NODE_ENV !== 'production') {
warning(
typeof typeDef[propName] === 'function',
'%s: %s type `%s` is invalid; it must be a function, usually from ' +
'React.PropTypes.',
Constructor.displayName || 'ReactClass',
ReactPropTypeLocationNames[location],
propName
);
}
}
}
}
function validateMethodOverride(isAlreadyDefined, name) {
var specPolicy = ReactClassInterface.hasOwnProperty(name)
? ReactClassInterface[name]
: null;
// Disallow overriding of base class methods unless explicitly allowed.
if (ReactClassMixin.hasOwnProperty(name)) {
_invariant(
specPolicy === 'OVERRIDE_BASE',
'ReactClassInterface: You are attempting to override ' +
'`%s` from your class specification. Ensure that your method names ' +
'do not overlap with React methods.',
name
);
}
// Disallow defining methods more than once unless explicitly allowed.
if (isAlreadyDefined) {
_invariant(
specPolicy === 'DEFINE_MANY' || specPolicy === 'DEFINE_MANY_MERGED',
'ReactClassInterface: You are attempting to define ' +
'`%s` on your component more than once. This conflict may be due ' +
'to a mixin.',
name
);
}
}
/**
* Mixin helper which handles policy validation and reserved
* specification keys when building React classes.
*/
function mixSpecIntoComponent(Constructor, spec) {
if (!spec) {
if (process.env.NODE_ENV !== 'production') {
var typeofSpec = typeof spec;
var isMixinValid = typeofSpec === 'object' && spec !== null;
if (process.env.NODE_ENV !== 'production') {
warning(
isMixinValid,
"%s: You're attempting to include a mixin that is either null " +
'or not an object. Check the mixins included by the component, ' +
'as well as any mixins they include themselves. ' +
'Expected object but got %s.',
Constructor.displayName || 'ReactClass',
spec === null ? null : typeofSpec
);
}
}
return;
}
_invariant(
typeof spec !== 'function',
"ReactClass: You're attempting to " +
'use a component class or function as a mixin. Instead, just use a ' +
'regular object.'
);
_invariant(
!isValidElement(spec),
"ReactClass: You're attempting to " +
'use a component as a mixin. Instead, just use a regular object.'
);
var proto = Constructor.prototype;
var autoBindPairs = proto.__reactAutoBindPairs;
// By handling mixins before any other properties, we ensure the same
// chaining order is applied to methods with DEFINE_MANY policy, whether
// mixins are listed before or after these methods in the spec.
if (spec.hasOwnProperty(MIXINS_KEY)) {
RESERVED_SPEC_KEYS.mixins(Constructor, spec.mixins);
}
for (var name in spec) {
if (!spec.hasOwnProperty(name)) {
continue;
}
if (name === MIXINS_KEY) {
// We have already handled mixins in a special case above.
continue;
}
var property = spec[name];
var isAlreadyDefined = proto.hasOwnProperty(name);
validateMethodOverride(isAlreadyDefined, name);
if (RESERVED_SPEC_KEYS.hasOwnProperty(name)) {
RESERVED_SPEC_KEYS[name](Constructor, property);
} else {
// Setup methods on prototype:
// The following member methods should not be automatically bound:
// 1. Expected ReactClass methods (in the "interface").
// 2. Overridden methods (that were mixed in).
var isReactClassMethod = ReactClassInterface.hasOwnProperty(name);
var isFunction = typeof property === 'function';
var shouldAutoBind =
isFunction &&
!isReactClassMethod &&
!isAlreadyDefined &&
spec.autobind !== false;
if (shouldAutoBind) {
autoBindPairs.push(name, property);
proto[name] = property;
} else {
if (isAlreadyDefined) {
var specPolicy = ReactClassInterface[name];
// These cases should already be caught by validateMethodOverride.
_invariant(
isReactClassMethod &&
(specPolicy === 'DEFINE_MANY_MERGED' ||
specPolicy === 'DEFINE_MANY'),
'ReactClass: Unexpected spec policy %s for key %s ' +
'when mixing in component specs.',
specPolicy,
name
);
// For methods which are defined more than once, call the existing
// methods before calling the new property, merging if appropriate.
if (specPolicy === 'DEFINE_MANY_MERGED') {
proto[name] = createMergedResultFunction(proto[name], property);
} else if (specPolicy === 'DEFINE_MANY') {
proto[name] = createChainedFunction(proto[name], property);
}
} else {
proto[name] = property;
if (process.env.NODE_ENV !== 'production') {
// Add verbose displayName to the function, which helps when looking
// at profiling tools.
if (typeof property === 'function' && spec.displayName) {
proto[name].displayName = spec.displayName + '_' + name;
}
}
}
}
}
}
}
function mixStaticSpecIntoComponent(Constructor, statics) {
if (!statics) {
return;
}
for (var name in statics) {
var property = statics[name];
if (!statics.hasOwnProperty(name)) {
continue;
}
var isReserved = name in RESERVED_SPEC_KEYS;
_invariant(
!isReserved,
'ReactClass: You are attempting to define a reserved ' +
'property, `%s`, that shouldn\'t be on the "statics" key. Define it ' +
'as an instance property instead; it will still be accessible on the ' +
'constructor.',
name
);
var isAlreadyDefined = name in Constructor;
if (isAlreadyDefined) {
var specPolicy = ReactClassStaticInterface.hasOwnProperty(name)
? ReactClassStaticInterface[name]
: null;
_invariant(
specPolicy === 'DEFINE_MANY_MERGED',
'ReactClass: You are attempting to define ' +
'`%s` on your component more than once. This conflict may be ' +
'due to a mixin.',
name
);
Constructor[name] = createMergedResultFunction(Constructor[name], property);
return;
}
Constructor[name] = property;
}
}
/**
* Merge two objects, but throw if both contain the same key.
*
* @param {object} one The first object, which is mutated.
* @param {object} two The second object
* @return {object} one after it has been mutated to contain everything in two.
*/
function mergeIntoWithNoDuplicateKeys(one, two) {
_invariant(
one && two && typeof one === 'object' && typeof two === 'object',
'mergeIntoWithNoDuplicateKeys(): Cannot merge non-objects.'
);
for (var key in two) {
if (two.hasOwnProperty(key)) {
_invariant(
one[key] === undefined,
'mergeIntoWithNoDuplicateKeys(): ' +
'Tried to merge two objects with the same key: `%s`. This conflict ' +
'may be due to a mixin; in particular, this may be caused by two ' +
'getInitialState() or getDefaultProps() methods returning objects ' +
'with clashing keys.',
key
);
one[key] = two[key];
}
}
return one;
}
/**
* Creates a function that invokes two functions and merges their return values.
*
* @param {function} one Function to invoke first.
* @param {function} two Function to invoke second.
* @return {function} Function that invokes the two argument functions.
* @private
*/
function createMergedResultFunction(one, two) {
return function mergedResult() {
var a = one.apply(this, arguments);
var b = two.apply(this, arguments);
if (a == null) {
return b;
} else if (b == null) {
return a;
}
var c = {};
mergeIntoWithNoDuplicateKeys(c, a);
mergeIntoWithNoDuplicateKeys(c, b);
return c;
};
}
/**
* Creates a function that invokes two functions and ignores their return vales.
*
* @param {function} one Function to invoke first.
* @param {function} two Function to invoke second.
* @return {function} Function that invokes the two argument functions.
* @private
*/
function createChainedFunction(one, two) {
return function chainedFunction() {
one.apply(this, arguments);
two.apply(this, arguments);
};
}
/**
* Binds a method to the component.
*
* @param {object} component Component whose method is going to be bound.
* @param {function} method Method to be bound.
* @return {function} The bound method.
*/
function bindAutoBindMethod(component, method) {
var boundMethod = method.bind(component);
if (process.env.NODE_ENV !== 'production') {
boundMethod.__reactBoundContext = component;
boundMethod.__reactBoundMethod = method;
boundMethod.__reactBoundArguments = null;
var componentName = component.constructor.displayName;
var _bind = boundMethod.bind;
boundMethod.bind = function(newThis) {
for (
var _len = arguments.length,
args = Array(_len > 1 ? _len - 1 : 0),
_key = 1;
_key < _len;
_key++
) {
args[_key - 1] = arguments[_key];
}
// User is trying to bind() an autobound method; we effectively will
// ignore the value of "this" that the user is trying to use, so
// let's warn.
if (newThis !== component && newThis !== null) {
if (process.env.NODE_ENV !== 'production') {
warning(
false,
'bind(): React component methods may only be bound to the ' +
'component instance. See %s',
componentName
);
}
} else if (!args.length) {
if (process.env.NODE_ENV !== 'production') {
warning(
false,
'bind(): You are binding a component method to the component. ' +
'React does this for you automatically in a high-performance ' +
'way, so you can safely remove this call. See %s',
componentName
);
}
return boundMethod;
}
var reboundMethod = _bind.apply(boundMethod, arguments);
reboundMethod.__reactBoundContext = component;
reboundMethod.__reactBoundMethod = method;
reboundMethod.__reactBoundArguments = args;
return reboundMethod;
};
}
return boundMethod;
}
/**
* Binds all auto-bound methods in a component.
*
* @param {object} component Component whose method is going to be bound.
*/
function bindAutoBindMethods(component) {
var pairs = component.__reactAutoBindPairs;
for (var i = 0; i < pairs.length; i += 2) {
var autoBindKey = pairs[i];
var method = pairs[i + 1];
component[autoBindKey] = bindAutoBindMethod(component, method);
}
}
var IsMountedPreMixin = {
componentDidMount: function() {
this.__isMounted = true;
}
};
var IsMountedPostMixin = {
componentWillUnmount: function() {
this.__isMounted = false;
}
};
/**
* Add more to the ReactClass base class. These are all legacy features and
* therefore not already part of the modern ReactComponent.
*/
var ReactClassMixin = {
/**
* TODO: This will be deprecated because state should always keep a consistent
* type signature and the only use case for this, is to avoid that.
*/
replaceState: function(newState, callback) {
this.updater.enqueueReplaceState(this, newState, callback);
},
/**
* Checks whether or not this composite component is mounted.
* @return {boolean} True if mounted, false otherwise.
* @protected
* @final
*/
isMounted: function() {
if (process.env.NODE_ENV !== 'production') {
warning(
this.__didWarnIsMounted,
'%s: isMounted is deprecated. Instead, make sure to clean up ' +
'subscriptions and pending requests in componentWillUnmount to ' +
'prevent memory leaks.',
(this.constructor && this.constructor.displayName) ||
this.name ||
'Component'
);
this.__didWarnIsMounted = true;
}
return !!this.__isMounted;
}
};
var ReactClassComponent = function() {};
_assign(
ReactClassComponent.prototype,
ReactComponent.prototype,
ReactClassMixin
);
/**
* Creates a composite component class given a class specification.
* See https://facebook.github.io/react/docs/top-level-api.html#react.createclass
*
* @param {object} spec Class specification (which must define `render`).
* @return {function} Component constructor function.
* @public
*/
function createClass(spec) {
// To keep our warnings more understandable, we'll use a little hack here to
// ensure that Constructor.name !== 'Constructor'. This makes sure we don't
// unnecessarily identify a class without displayName as 'Constructor'.
var Constructor = identity(function(props, context, updater) {
// This constructor gets overridden by mocks. The argument is used
// by mocks to assert on what gets mounted.
if (process.env.NODE_ENV !== 'production') {
warning(
this instanceof Constructor,
'Something is calling a React component directly. Use a factory or ' +
'JSX instead. See: https://fb.me/react-legacyfactory'
);
}
// Wire up auto-binding
if (this.__reactAutoBindPairs.length) {
bindAutoBindMethods(this);
}
this.props = props;
this.context = context;
this.refs = emptyObject;
this.updater = updater || ReactNoopUpdateQueue;
this.state = null;
// ReactClasses doesn't have constructors. Instead, they use the
// getInitialState and componentWillMount methods for initialization.
var initialState = this.getInitialState ? this.getInitialState() : null;
if (process.env.NODE_ENV !== 'production') {
// We allow auto-mocks to proceed as if they're returning null.
if (
initialState === undefined &&
this.getInitialState._isMockFunction
) {
// This is probably bad practice. Consider warning here and
// deprecating this convenience.
initialState = null;
}
}
_invariant(
typeof initialState === 'object' && !Array.isArray(initialState),
'%s.getInitialState(): must return an object or null',
Constructor.displayName || 'ReactCompositeComponent'
);
this.state = initialState;
});
Constructor.prototype = new ReactClassComponent();
Constructor.prototype.constructor = Constructor;
Constructor.prototype.__reactAutoBindPairs = [];
injectedMixins.forEach(mixSpecIntoComponent.bind(null, Constructor));
mixSpecIntoComponent(Constructor, IsMountedPreMixin);
mixSpecIntoComponent(Constructor, spec);
mixSpecIntoComponent(Constructor, IsMountedPostMixin);
// Initialize the defaultProps property after all mixins have been merged.
if (Constructor.getDefaultProps) {
Constructor.defaultProps = Constructor.getDefaultProps();
}
if (process.env.NODE_ENV !== 'production') {
// This is a tag to indicate that the use of these method names is ok,
// since it's used with createClass. If it's not, then it's likely a
// mistake so we'll warn you to use the static property, property
// initializer or constructor respectively.
if (Constructor.getDefaultProps) {
Constructor.getDefaultProps.isReactClassApproved = {};
}
if (Constructor.prototype.getInitialState) {
Constructor.prototype.getInitialState.isReactClassApproved = {};
}
}
_invariant(
Constructor.prototype.render,
'createClass(...): Class specification must implement a `render` method.'
);
if (process.env.NODE_ENV !== 'production') {
warning(
!Constructor.prototype.componentShouldUpdate,
'%s has a method called ' +
'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' +
'The name is phrased as a question because the function is ' +
'expected to return a value.',
spec.displayName || 'A component'
);
warning(
!Constructor.prototype.componentWillRecieveProps,
'%s has a method called ' +
'componentWillRecieveProps(). Did you mean componentWillReceiveProps()?',
spec.displayName || 'A component'
);
warning(
!Constructor.prototype.UNSAFE_componentWillRecieveProps,
'%s has a method called UNSAFE_componentWillRecieveProps(). ' +
'Did you mean UNSAFE_componentWillReceiveProps()?',
spec.displayName || 'A component'
);
}
// Reduce time spent doing lookups by setting these on the prototype.
for (var methodName in ReactClassInterface) {
if (!Constructor.prototype[methodName]) {
Constructor.prototype[methodName] = null;
}
}
return Constructor;
}
return createClass;
}
module.exports = factory;
}).call(this)}).call(this,require('_process'))
},{"_process":29,"object-assign":28}],5:[function(require,module,exports){
(function (process){(function (){
'use strict';
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @typechecks
*/
var emptyFunction = require('./emptyFunction');
/**
* Upstream version of event listener. Does not take into account specific
* nature of platform.
*/
var EventListener = {
/**
* Listen to DOM events during the bubble phase.
*
* @param {DOMEventTarget} target DOM element to register listener on.
* @param {string} eventType Event type, e.g. 'click' or 'mouseover'.
* @param {function} callback Callback function.
* @return {object} Object with a `remove` method.
*/
listen: function listen(target, eventType, callback) {
if (target.addEventListener) {
target.addEventListener(eventType, callback, false);
return {
remove: function remove() {
target.removeEventListener(eventType, callback, false);
}
};
} else if (target.attachEvent) {
target.attachEvent('on' + eventType, callback);
return {
remove: function remove() {
target.detachEvent('on' + eventType, callback);
}
};
}
},
/**
* Listen to DOM events during the capture phase.
*
* @param {DOMEventTarget} target DOM element to register listener on.
* @param {string} eventType Event type, e.g. 'click' or 'mouseover'.
* @param {function} callback Callback function.
* @return {object} Object with a `remove` method.
*/
capture: function capture(target, eventType, callback) {
if (target.addEventListener) {
target.addEventListener(eventType, callback, true);
return {
remove: function remove() {
target.removeEventListener(eventType, callback, true);
}
};
} else {
if (process.env.NODE_ENV !== 'production') {
console.error('Attempted to listen to events during the capture phase on a ' + 'browser that does not support the capture phase. Your application ' + 'will not receive some events.');
}
return {
remove: emptyFunction
};
}
},
registerDefault: function registerDefault() {}
};
module.exports = EventListener;
}).call(this)}).call(this,require('_process'))
},{"./emptyFunction":12,"_process":29}],6:[function(require,module,exports){
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
'use strict';
var canUseDOM = !!(typeof window !== 'undefined' && window.document && window.document.createElement);
/**
* Simple, lightweight module assisting with the detection and context of
* Worker. Helps avoid circular dependencies and allows code to reason about
* whether or not they are in a Worker, even if they never include the main
* `ReactWorker` dependency.
*/
var ExecutionEnvironment = {
canUseDOM: canUseDOM,
canUseWorkers: typeof Worker !== 'undefined',
canUseEventListeners: canUseDOM && !!(window.addEventListener || window.attachEvent),
canUseViewport: canUseDOM && !!window.screen,
isInWorker: !canUseDOM // For now, this is true - might change in the future.
};
module.exports = ExecutionEnvironment;
},{}],7:[function(require,module,exports){
"use strict";
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @typechecks
*/
var _hyphenPattern = /-(.)/g;
/**
* Camelcases a hyphenated string, for example:
*
* > camelize('background-color')
* < "backgroundColor"
*
* @param {string} string
* @return {string}
*/
function camelize(string) {
return string.replace(_hyphenPattern, function (_, character) {
return character.toUpperCase();
});
}
module.exports = camelize;
},{}],8:[function(require,module,exports){
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @typechecks
*/
'use strict';
var camelize = require('./camelize');
var msPattern = /^-ms-/;
/**
* Camelcases a hyphenated CSS property name, for example:
*
* > camelizeStyleName('background-color')
* < "backgroundColor"
* > camelizeStyleName('-moz-transition')
* < "MozTransition"
* > camelizeStyleName('-ms-transition')
* < "msTransition"
*
* As Andi Smith suggests
* (http://www.andismith.com/blog/2012/02/modernizr-prefixed/), an `-ms` prefix
* is converted to lowercase `ms`.
*
* @param {string} string
* @return {string}
*/
function camelizeStyleName(string) {
return camelize(string.replace(msPattern, 'ms-'));
}
module.exports = camelizeStyleName;
},{"./camelize":7}],9:[function(require,module,exports){
'use strict';
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
*/
var isTextNode = require('./isTextNode');
/*eslint-disable no-bitwise */
/**
* Checks if a given DOM node contains or is another DOM node.
*/
function containsNode(outerNode, innerNode) {
if (!outerNode || !innerNode) {
return false;
} else if (outerNode === innerNode) {
return true;
} else if (isTextNode(outerNode)) {
return false;
} else if (isTextNode(innerNode)) {
return containsNode(outerNode, innerNode.parentNode);
} else if ('contains' in outerNode) {
return outerNode.contains(innerNode);
} else if (outerNode.compareDocumentPosition) {
return !!(outerNode.compareDocumentPosition(innerNode) & 16);
} else {
return false;
}
}
module.exports = containsNode;
},{"./isTextNode":22}],10:[function(require,module,exports){
(function (process){(function (){
'use strict';
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @typechecks
*/
var invariant = require('./invariant');
/**
* Convert array-like objects to arrays.
*
* This API assumes the caller knows the contents of the data type. For less
* well defined inputs use createArrayFromMixed.
*
* @param {object|function|filelist} obj
* @return {array}
*/
function toArray(obj) {
var length = obj.length;
// Some browsers builtin objects can report typeof 'function' (e.g. NodeList
// in old versions of Safari).
!(!Array.isArray(obj) && (typeof obj === 'object' || typeof obj === 'function')) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'toArray: Array-like object expected') : invariant(false) : void 0;
!(typeof length === 'number') ? process.env.NODE_ENV !== 'production' ? invariant(false, 'toArray: Object needs a length property') : invariant(false) : void 0;
!(length === 0 || length - 1 in obj) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'toArray: Object should have keys for indices') : invariant(false) : void 0;
!(typeof obj.callee !== 'function') ? process.env.NODE_ENV !== 'production' ? invariant(false, 'toArray: Object can\'t be `arguments`. Use rest params ' + '(function(...args) {}) or Array.from() instead.') : invariant(false) : void 0;
// Old IE doesn't give collections access to hasOwnProperty. Assume inputs
// without method will throw during the slice call and skip straight to the
// fallback.
if (obj.hasOwnProperty) {
try {
return Array.prototype.slice.call(obj);
} catch (e) {
// IE < 9 does not support Array#slice on collections objects
}
}
// Fall back to copying key by key. This assumes all keys have a value,
// so will not preserve sparsely populated inputs.
var ret = Array(length);
for (var ii = 0; ii < length; ii++) {
ret[ii] = obj[ii];
}
return ret;
}
/**
* Perform a heuristic test to determine if an object is "array-like".
*
* A monk asked Joshu, a Zen master, "Has a dog Buddha nature?"
* Joshu replied: "Mu."
*
* This function determines if its argument has "array nature": it returns
* true if the argument is an actual array, an `arguments' object, or an
* HTMLCollection (e.g. node.childNodes or node.getElementsByTagName()).
*
* It will return false for other array-like objects like Filelist.
*
* @param {*} obj
* @return {boolean}
*/
function hasArrayNature(obj) {
return (
// not null/false
!!obj && (
// arrays are objects, NodeLists are functions in Safari
typeof obj == 'object' || typeof obj == 'function') &&
// quacks like an array
'length' in obj &&
// not window
!('setInterval' in obj) &&
// no DOM node should be considered an array-like
// a 'select' element has 'length' and 'item' properties on IE8
typeof obj.nodeType != 'number' && (
// a real array
Array.isArray(obj) ||
// arguments
'callee' in obj ||
// HTMLCollection/NodeList
'item' in obj)
);
}
/**
* Ensure that the argument is an array by wrapping it in an array if it is not.
* Creates a copy of the argument if it is already an array.
*
* This is mostly useful idiomatically:
*
* var createArrayFromMixed = require('createArrayFromMixed');
*
* function takesOneOrMoreThings(things) {
* things = createArrayFromMixed(things);
* ...
* }
*
* This allows you to treat `things' as an array, but accept scalars in the API.
*
* If you need to convert an array-like object, like `arguments`, into an array
* use toArray instead.
*
* @param {*} obj
* @return {array}
*/
function createArrayFromMixed(obj) {
if (!hasArrayNature(obj)) {
return [obj];
} else if (Array.isArray(obj)) {
return obj.slice();
} else {
return toArray(obj);
}
}
module.exports = createArrayFromMixed;
}).call(this)}).call(this,require('_process'))
},{"./invariant":20,"_process":29}],11:[function(require,module,exports){
(function (process){(function (){
'use strict';
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @typechecks
*/
/*eslint-disable fb-www/unsafe-html*/
var ExecutionEnvironment = require('./ExecutionEnvironment');
var createArrayFromMixed = require('./createArrayFromMixed');
var getMarkupWrap = require('./getMarkupWrap');
var invariant = require('./invariant');
/**
* Dummy container used to render all markup.
*/
var dummyNode = ExecutionEnvironment.canUseDOM ? document.createElement('div') : null;
/**
* Pattern used by `getNodeName`.
*/
var nodeNamePattern = /^\s*<(\w+)/;
/**
* Extracts the `nodeName` of the first element in a string of markup.
*
* @param {string} markup String of markup.
* @return {?string} Node name of the supplied markup.
*/
function getNodeName(markup) {
var nodeNameMatch = markup.match(nodeNamePattern);
return nodeNameMatch && nodeNameMatch[1].toLowerCase();
}
/**
* Creates an array containing the nodes rendered from the supplied markup. The
* optionally supplied `handleScript` function will be invoked once for each
*