function formatPrice( cost, currencyCode, options = {} ) {
	if ( undefined !== options.precision ) {
		cost = applyPrecision( cost, options.precision );
	}

	return formatCurrency( cost, currencyCode, cost % 1 > 0 ? {} : { precision: 0 } );
}

function formatPrice ( cost , currencyCode , options = { } ) { if ( undefined !== options . precision ) { cost = applyPrecision ( cost , options . precision ) ; } return formatCurrency ( cost , currencyCode , cost % 1 > 0 ? { } : { precision : 0 } ) ; }

Formats price given cost and currency

@param {Number} cost - cost
@param {String} currencyCode - currency code to format with
@param {Object} options - options containing precision
@returns {String} - Returns a formatted price


function getEligibleGSuiteDomain( selectedDomainName, domains ) {
	if ( selectedDomainName && canDomainAddGSuite( selectedDomainName ) ) {
		return selectedDomainName;
	}
	const [ eligibleDomain ] = getGSuiteSupportedDomains( domains );
	return ( eligibleDomain && eligibleDomain.name ) || '';
}

function getEligibleGSuiteDomain ( selectedDomainName , domains ) { if ( selectedDomainName && canDomainAddGSuite ( selectedDomainName ) ) { return selectedDomainName ; } const [ eligibleDomain ] = getGSuiteSupportedDomains ( domains ) ; return ( eligibleDomain && eligibleDomain . name ) || '' ; }

Retrieves the first domain that is eligible to G Suite either from the current selected site or the list of domains.

@param {String} selectedDomainName - domain name for the site currently selected by the user
@param {Array} domains - list of domain objects
@returns {String} - Eligible domain name

Retrieves the first domain that is eligible to G Suite either from the current selected site or the list of domains .


function getGSuiteSupportedDomains( domains ) {
	return domains.filter( function( domain ) {
		const wpcomHosted =
			includes( [ domainTypes.REGISTERED ], domain.type ) &&
			( domain.hasWpcomNameservers || hasGSuite( domain ) );
		const mapped = includes( [ domainTypes.MAPPED ], domain.type );
		const notOtherProvidor =
			domain.googleAppsSubscription && domain.googleAppsSubscription.status !== 'other_provider';
		return ( wpcomHosted || mapped ) && canDomainAddGSuite( domain.name ) && notOtherProvidor;
	} );
}

function getGSuiteSupportedDomains ( domains ) { return domains . filter ( function ( domain ) { const wpcomHosted = includes ( [ domainTypes . REGISTERED ] , domain . type ) && ( domain . hasWpcomNameservers || hasGSuite ( domain ) ) ; const mapped = includes ( [ domainTypes . MAPPED ] , domain . type ) ; const notOtherProvidor = domain . googleAppsSubscription && domain . googleAppsSubscription . status !== 'other_provider' ; return ( wpcomHosted || mapped ) && canDomainAddGSuite ( domain . name ) && notOtherProvidor ; } ) ; }

Filters a list of domains by the domains that eligible for G Suite

@param {Array} domains - list of domain objects
@returns {Array} - Array of G Suite supported domans


function stopMutationObserver() {
	if ( mutationObserver && mutationObserverActive ) {
		activePlaceholders = [];
		activePlaceholderEverDetected = false;
		mutationObserver.disconnect();
		mutationObserverActive = false;
	}
}

function stopMutationObserver ( ) { if ( mutationObserver && mutationObserverActive ) { activePlaceholders = [ ] ; activePlaceholderEverDetected = false ; mutationObserver . disconnect ( ) ; mutationObserverActive = false ; } }

Stop observing the DOM changes and clean up all data structures


function checkActivePlaceholders() {
	const placeholdersCount = activePlaceholders.length;
	debug( `Checking active placeholders. Count: ${ placeholdersCount }` );

	if ( placeholdersCount > 0 ) {
		activePlaceholderEverDetected = true;
	}

	// record event if there ever were any placeholders and if they all just disappeared
	if ( activePlaceholderEverDetected && placeholdersCount === 0 ) {
		// tell tracks to record duration
		const { startTime, trigger } = determineTimingType();
		const duration = Math.round( performance.now() - startTime );
		debug( `Recording placeholder wait. Duration: ${ duration }, Trigger: ${ trigger }` );
		analytics.timing.record( `placeholder-wait.${ trigger }`, duration );
		stopMutationObserver();
	}
}

Check if there are any placeholders on the screen and trigger a timing event when all the placeholders are gone


function getDefaultOptions() {
	const ids = config( 'directly_rtm_widget_ids' );
	const env = config( 'directly_rtm_widget_environment' );

	return {
		id: ids[ env ],
		displayAskQuestion: false,
	};
}

function getDefaultOptions ( ) { const ids = config ( 'directly_rtm_widget_ids' ) ; const env = config ( 'directly_rtm_widget_environment' ) ; return { id : ids [ env ] , displayAskQuestion : false , } ; }

Gets the default set of options to configure the Directly RTM widget.
It's important to keep this config in a getter function, rather than a constant
on the module scope, to prevent import-time errors errors that could crash Calypso.

@see https://cloudup.com/cySVQ9R_O6S for all configuration options

@returns {Object} The default configuration options

Gets the default set of options to configure the Directly RTM widget . It s important to keep this config in a getter function rather than a constant on the module scope to prevent import - time errors errors that could crash Calypso .


function configureGlobals() {
	// Set up the global DirectlyRTM function, required for the RTM widget.
	// This snippet is pasted from Directly's setup code.
	window.DirectlyRTM =
		window.DirectlyRTM ||
		function() {
			( window.DirectlyRTM.cq = window.DirectlyRTM.cq || [] ).push( arguments );
		};
	// Since we can only configure once per pageload, this library only provides a
	// single global configuration.
	window.DirectlyRTM( 'config', getDefaultOptions() );
}

Set up global variables and configuration for the RTM widget

@see https://cloudup.com/cySVQ9R_O6S for the standard setup instructions


function insertDOM() {
	if ( null !== document.getElementById( 'directlyRTMScript' ) ) {
		return;
	}
	const d = document.createElement( 'div' );
	d.id = 'directlyRTMScript';
	d.src = DIRECTLY_ASSETS_BASE_URL;
	document.body.appendChild( d );
}

function insertDOM ( ) { if ( null !== document . getElementById ( 'directlyRTMScript' ) ) { return ; } const d = document . createElement ( 'div' ) ; d . id = 'directlyRTMScript' ; d . src = DIRECTLY_ASSETS_BASE_URL ; document . body . appendChild ( d ) ; }

Inserts a dummy DOM element that the widget uses to calculate the base URL for its assets.

Under standard setup, the RTM widget gathers its base URL by inspecting the src attribute of
the <script> used to load the library. Since we've got a custom setup that doesn't include
a <script> tag, we need to fake this by placing a DOM element with id="directlyRTMScript"
and a src with the appropriate base URL.

@see https://cloudup.com/cySVQ9R_O6S for the standard setup instructions we've modified

Inserts a dummy DOM element that the widget uses to calculate the base URL for its assets .


function loadDirectlyScript() {
	return new Promise( ( resolve, reject ) => {
		loadScript( DIRECTLY_RTM_SCRIPT_URL, function( error ) {
			if ( error ) {
				return reject( new Error( `Failed to load script "${ error.src }".` ) );
			}
			resolve();
		} );
	} );
}

function loadDirectlyScript ( ) { return new Promise ( ( resolve , reject ) => { loadScript ( DIRECTLY_RTM_SCRIPT_URL , function ( error ) { if ( error ) { return reject ( new Error ( ` ${ error . src } ` ) ) ; } resolve ( ) ; } ) ; } ) ; }

Make the request for Directly's remote JavaScript.

@returns {Promise} Promise that resolves after the script loads or fails

Make the request for Directly s remote JavaScript .


function getInitialServerState( initialReducer ) {
	if ( typeof window !== 'object' || ! window.initialReduxState || isSupportSession() ) {
		return null;
	}

	const serverState = deserialize( window.initialReduxState, initialReducer );
	return pick( serverState, Object.keys( window.initialReduxState ) );
}

function getInitialServerState ( initialReducer ) { if ( typeof window !== 'object' || ! window . initialReduxState || isSupportSession ( ) ) { return null ; } const serverState = deserialize ( window . initialReduxState , initialReducer ) ; return pick ( serverState , Object . keys ( window . initialReduxState ) ) ; }

get bootstrapped state from a server-side render

get bootstrapped state from a server - side render


function shouldAddSympathy() {
	// If `force-sympathy` flag is enabled, always clear persistent state.
	if ( config.isEnabled( 'force-sympathy' ) ) {
		return true;
	}

	// If `no-force-sympathy` flag is enabled, never clear persistent state.
	if ( config.isEnabled( 'no-force-sympathy' ) ) {
		return false;
	}

	// Otherwise, in development mode, clear persistent state 25% of the time.
	if ( 'development' === process.env.NODE_ENV && Math.random() < 0.25 ) {
		return true;
	}

	// Otherwise, do not clear persistent state.
	return false;
}

Determines whether to add "sympathy" by randomly clearing out persistent
browser state and loading without it

Can be overridden on the command-line with two flags:
- ENABLE_FEATURES=force-sympathy npm start (always sympathize)
- ENABLE_FEATURES=no-force-sympathy npm start (always prevent sympathy)

If both of these flags are set, then `force-sympathy` takes precedence.

@returns {boolean} Whether to clear persistent state on page load

Determines whether to add sympathy by randomly clearing out persistent browser state and loading without it


function updateProductEdits( edits, productId, doUpdate ) {
	const prevEdits = edits || [];
	let found = false;

	const newEdits = prevEdits.map( productEdits => {
		if ( isEqual( productId, productEdits.productId ) ) {
			found = true;
			return doUpdate( productEdits );
		}
		return productEdits;
	} );

	if ( ! found ) {
		newEdits.push( doUpdate( undefined ) );
	}

	return newEdits;
}

function updateProductEdits ( edits , productId , doUpdate ) { const prevEdits = edits || [ ] ; let found = false ; const newEdits = prevEdits . map ( productEdits => { if ( isEqual ( productId , productEdits . productId ) ) { found = true ; return doUpdate ( productEdits ) ; } return productEdits ; } ) ; if ( ! found ) { newEdits . push ( doUpdate ( undefined ) ) ; } return newEdits ; }

Updates an edits object for a specific product.

The main edits state contains an array of edits object, one for each
product to be edited. The edits object for a product is in this format:

```
{
productId: <Number|Object>,
creates: [ {<new variation object>} ]
updated: [ {<variation props to update>} ]
deletes: [ <variation id> ]
}
```

@param {Object} edits The state at `woocommerce.ui.products.variations.edits`
@param {Number|Object} productId The id of product edits object to be updating.
@param {Function} doUpdate ( productEdits ) Called with previous product edits, takes return as new product edits.
@return {Object} The new, updated product edits to be used in state.

Updates an edits object for a specific product .


function doesNotNeedSandbox( iframe ) {
	const trustedHosts = [
		'spotify.com',
		'kickstarter.com',
		'soundcloud.com',
		'embed.ted.com',
		'player.twitch.tv',
	];

	const hostName = iframe.src && url.parse( iframe.src ).hostname;
	const iframeHost = hostName && hostName.toLowerCase();

	return some( trustedHosts, trustedHost => endsWith( '.' + iframeHost, '.' + trustedHost ) );
}

function doesNotNeedSandbox ( iframe ) { const trustedHosts = [ 'spotify.com' , 'kickstarter.com' , 'soundcloud.com' , 'embed.ted.com' , 'player.twitch.tv' , ] ; const hostName = iframe . src && url . parse ( iframe . src ) . hostname ; const iframeHost = hostName && hostName . toLowerCase ( ) ; return some ( trustedHosts , trustedHost => endsWith ( '.' + iframeHost , '.' + trustedHost ) ) ; }

Internal Dependencies
 Given an iframe, is it okay to have it run without a sandbox?
@param {object} iframe - the iframe to check
@returns {boolean} true/false if we trust the source and we know they don't work in a sandbox

Internal Dependencies Given an iframe is it okay to have it run without a sandbox?


function getEligibleEmailForwardingDomain( selectedDomainName, domains = [] ) {
	const eligibleDomains = getEmailForwardingSupportedDomains( domains );
	let selectedDomain;
	if ( selectedDomainName ) {
		selectedDomain = eligibleDomains.reduce( function( selected, domain ) {
			return domain.name === selectedDomainName ? domain.name : selected;
		}, '' );
	}
	return selectedDomain || ( eligibleDomains.length && eligibleDomains[ 0 ].name ) || '';
}

function getEligibleEmailForwardingDomain ( selectedDomainName , domains = [ ] ) { const eligibleDomains = getEmailForwardingSupportedDomains ( domains ) ; let selectedDomain ; if ( selectedDomainName ) { selectedDomain = eligibleDomains . reduce ( function ( selected , domain ) { return domain . name === selectedDomainName ? domain . name : selected ; } , '' ) ; } return selectedDomain || ( eligibleDomains . length && eligibleDomains [ 0 ] . name ) || '' ; }

Retrieves the first domain that is eligible for Email Forwarding either from the current selected site or the list of domains.

@param {String} selectedDomainName - domain name for the site currently selected by the user
@param {Array} domains - list of domain objects
@returns {String} - Eligible domain name

Retrieves the first domain that is eligible for Email Forwarding either from the current selected site or the list of domains .


function getEmailForwardingSupportedDomains( domains ) {
	return domains.filter( function( domain ) {
		const domainHasGSuite = hasGSuite( domain );
		const wpcomHosted =
			includes( [ domainTypes.REGISTERED ], domain.type ) && domain.hasWpcomNameservers;
		const mapped = includes( [ domainTypes.MAPPED ], domain.type );
		return ( wpcomHosted || mapped ) && ! domainHasGSuite;
	} );
}

function getEmailForwardingSupportedDomains ( domains ) { return domains . filter ( function ( domain ) { const domainHasGSuite = hasGSuite ( domain ) ; const wpcomHosted = includes ( [ domainTypes . REGISTERED ] , domain . type ) && domain . hasWpcomNameservers ; const mapped = includes ( [ domainTypes . MAPPED ] , domain . type ) ; return ( wpcomHosted || mapped ) && ! domainHasGSuite ; } ) ; }

Filters a list of domains by the domains that eligible for Email Forwarding

@param {Array} domains - list of domain objects
@returns {Array} - Array of Email Forwarding supported domans


function setup() {
	const app = express();

	// for nginx
	app.enable( 'trust proxy' );

	app.use( cookieParser() );
	app.use( userAgent.express() );

	if ( 'development' === process.env.NODE_ENV ) {
		// use legacy CSS rebuild system if css-hot-reload is disabled
		if ( ! config.isEnabled( 'css-hot-reload' ) ) {
			// only do `build` upon every request in "development"
			app.use( build() );
		}

		require( 'bundler' )( app );

		// setup logger
		app.use( morgan( 'dev' ) );

		if ( config.isEnabled( 'wpcom-user-bootstrap' ) ) {
			if ( config( 'wordpress_logged_in_cookie' ) ) {
				const username = config( 'wordpress_logged_in_cookie' ).split( '%7C' )[ 0 ];
				console.info( chalk.cyan( '\nYour logged in cookie set to user: ' + username ) );

				app.use( function( req, res, next ) {
					if ( ! req.cookies.wordpress_logged_in ) {
						req.cookies.wordpress_logged_in = config( 'wordpress_logged_in_cookie' );
					}
					next();
				} );
			} else {
				console.info(
					chalk.red(
						'\nYou need to set `wordpress_logged_in_cookie` in secrets.json' +
							' for wpcom-user-bootstrap to work in development.'
					)
				);
			}
		}
	} else {
		// setup logger
		app.use( morgan( 'combined' ) );
	}

	app.use( pwa() );

	// attach the static file server to serve the `public` dir
	app.use( '/calypso', express.static( path.resolve( __dirname, '..', '..', 'public' ) ) );

	// loaded when we detect stats blockers - see lib/analytics/index.js
	app.get( '/nostats.js', function( request, response ) {
		analytics.tracks.recordEvent(
			'calypso_stats_blocked',
			{
				do_not_track: request.headers.dnt,
			},
			request
		);
		response.setHeader( 'content-type', 'application/javascript' );
		response.end( "console.log('Stats are disabled');" );
	} );

	// serve files when not in production so that the source maps work correctly
	if ( 'development' === process.env.NODE_ENV ) {
		app.use( '/assets', express.static( path.resolve( __dirname, '..', '..', 'assets' ) ) );
		app.use( '/client', express.static( path.resolve( __dirname, '..', '..', 'client' ) ) );
	}

	if ( config.isEnabled( 'devdocs' ) ) {
		app.use( require( 'devdocs' )() );
	}

	if ( config.isEnabled( 'desktop' ) ) {
		app.use(
			'/desktop',
			express.static( path.resolve( __dirname, '..', '..', '..', 'public_desktop' ) )
		);
	}

	app.use( require( 'api' )() );

	// attach the pages module
	app.use( pages() );

	return app;
}

Returns the server HTTP request handler "app".
@returns {object} The express app
@api public

Returns the server HTTP request handler app .


function( pluginSlug ) {
		const query = {
			fields: 'icons,banners,compatibility,ratings,-contributors',
			locale: getWporgLocaleCode(),
		};

		pluginSlug = pluginSlug.replace( new RegExp( '.php$' ), '' );

		const baseUrl = 'https://api.wordpress.org/plugins/info/1.0/' + pluginSlug + '.jsonp';

		return new Promise( ( resolve, reject ) => {
			jsonp( baseUrl, query, function( error, data ) {
				if ( error ) {
					debug( 'error downloading plugin details from .org: %s', error );
					reject( error );
					return;
				}

				if ( ! data || ! data.slug ) {
					debug( 'unrecognized format fetching plugin details from .org: %s', data );
					reject( new Error( 'Unrecognized response format' ) );
					return;
				}

				resolve( data );
			} );
		} );
	}

Fetches details for a particular plugin.
@param {string} pluginSlug The plugin identifier.
@returns {Promise} Promise with the plugins details.

Fetches details for a particular plugin .


function( themeId ) {
		const query = {
			action: 'theme_information',
			// Return an `author` object containing `user_nicename` and `display_name` attrs.
			// This is for consistency with WP.com, which always returns the display name as `author`.
			'request[fields][extended_author]': true,
			'request[slug]': themeId,
		};
		return superagent
			.get( WPORG_THEMES_ENDPOINT )
			.set( 'Accept', 'application/json' )
			.query( query )
			.then( ( { body } ) => body );
	}

Get information about a given theme from the WordPress.org API.
If provided with a callback, will call that on succes with an object with theme details.
Otherwise, will return a promise.

@param {string}     themeId  The theme identifier.
@returns {Promise.<Object>}  A promise that returns a `theme` object

Get information about a given theme from the WordPress . org API . If provided with a callback will call that on succes with an object with theme details . Otherwise will return a promise .


function( options = {} ) {
		const { search, page, number } = options;
		const query = {
			action: 'query_themes',
			// Return an `author` object containing `user_nicename` and `display_name` attrs.
			// This is for consistency with WP.com, which always returns the display name as `author`.
			'request[fields][extended_author]': true,
			'request[search]': search,
			'request[page]': page,
			'request[per_page]:': number,
		};

		return superagent
			.get( WPORG_THEMES_ENDPOINT )
			.set( 'Accept', 'application/json' )
			.query( query )
			.then( ( { body } ) => body );
	}

Get information about a given theme from the WordPress.org API.

@param  {Object}        options         Theme query
@param  {String}        options.search  Search string
@param  {Number}        options.number  How many themes to return per page
@param  {Number}        options.page    Which page of matching themes to return
@returns {Promise.<Object>}             A promise that returns an object containing a `themes` array and an `info` object

Get information about a given theme from the WordPress . org API .


function isPlainObject(value) {
  if (!isObject(value)) {
    return false;
  }

  try {
    var _constructor = value.constructor;
    var prototype = _constructor.prototype;
    return _constructor && prototype && hasOwnProperty.call(prototype, 'isPrototypeOf');
  } catch (error) {
    return false;
  }
}

function isPlainObject ( value ) { if ( ! isObject ( value ) ) { return false ; } try { var _constructor = value . constructor ; var prototype = _constructor . prototype ; return _constructor && prototype && hasOwnProperty . call ( prototype , 'isPrototypeOf' ) ; } catch ( error ) { return false ; } }

Check if the given value is a plain object.
@param {*} value - The value to check.
@returns {boolean} Returns `true` if the given value is a plain object, else `false`.

Check if the given value is a plain object .


function toArray(value) {
  return Array.from ? Array.from(value) : slice.call(value);
}

function toArray ( value ) { return Array . from ? Array . from ( value ) : slice . call ( value ) ; }

Convert array-like or iterable object to an array.
@param {*} value - The value to convert.
@returns {Array} Returns a new array.

Convert array - like or iterable object to an array .


function forEach(data, callback) {
  if (data && isFunction(callback)) {
    if (Array.isArray(data) || isNumber(data.length)
    /* array-like */
    ) {
        toArray(data).forEach(function (value, key) {
          callback.call(data, value, key, data);
        });
      } else if (isObject(data)) {
      Object.keys(data).forEach(function (key) {
        callback.call(data, data[key], key, data);
      });
    }
  }

  return data;
}

function
forEach
(
data
,
callback
)
{
if
(
data
&&
isFunction
(
callback
)
)
{
if
(
Array
.
isArray
(
data
)
||
isNumber
(
data
.
length
)
/* array-like */
)
{
toArray
(
data
)
.
forEach
(
function
(
value
,
key
)
{
callback
.
call
(
data
,
value
,
key
,
data
)
;
}
)
;
}
else
if
(
isObject
(
data
)
)
{
Object
.
keys
(
data
)
.
forEach
(
function
(
key
)
{
callback
.
call
(
data
,
data
[
key
]
,
key
,
data
)
;
}
)
;
}
}
return
data
;
}

Iterate the given data.
@param {*} data - The data to iterate.
@param {Function} callback - The process function for each element.
@returns {*} The original data.


function hasClass(element, value) {
  return element.classList ? element.classList.contains(value) : element.className.indexOf(value) > -1;
}

function hasClass ( element , value ) { return element . classList ? element . classList . contains ( value ) : element . className . indexOf ( value ) > - 1 ; }

Check if the given element has a special class.
@param {Element} element - The element to check.
@param {string} value - The class to search.
@returns {boolean} Returns `true` if the special class was found.

Check if the given element has a special class .


function addClass(element, value) {
  if (!value) {
    return;
  }

  if (isNumber(element.length)) {
    forEach(element, function (elem) {
      addClass(elem, value);
    });
    return;
  }

  if (element.classList) {
    element.classList.add(value);
    return;
  }

  var className = element.className.trim();

  if (!className) {
    element.className = value;
  } else if (className.indexOf(value) < 0) {
    element.className = "".concat(className, " ").concat(value);
  }
}

function
addClass
(
element
,
value
)
{
if
(
!
value
)
{
return
;
}
if
(
isNumber
(
element
.
length
)
)
{
forEach
(
element
,
function
(
elem
)
{
addClass
(
elem
,
value
)
;
}
)
;
return
;
}
if
(
element
.
classList
)
{
element
.
classList
.
add
(
value
)
;
return
;
}
var
className
=
element
.
className
.
trim
(
)
;
if
(
!
className
)
{
element
.
className
=
value
;
}
else
if
(
className
.
indexOf
(
value
)
<
0
)
{
element
.
className
=
""
.
concat
(
className
,
" "
)
.
concat
(
value
)
;
}
}

Add classes to the given element.
@param {Element} element - The target element.
@param {string} value - The classes to be added.


function removeClass(element, value) {
  if (!value) {
    return;
  }

  if (isNumber(element.length)) {
    forEach(element, function (elem) {
      removeClass(elem, value);
    });
    return;
  }

  if (element.classList) {
    element.classList.remove(value);
    return;
  }

  if (element.className.indexOf(value) >= 0) {
    element.className = element.className.replace(value, '');
  }
}

function removeClass ( element , value ) { if ( ! value ) { return ; } if ( isNumber ( element . length ) ) { forEach ( element , function ( elem ) { removeClass ( elem , value ) ; } ) ; return ; } if ( element . classList ) { element . classList . remove ( value ) ; return ; } if ( element . className . indexOf ( value ) >= 0 ) { element . className = element . className . replace ( value , '' ) ; } }

Remove classes from the given element.
@param {Element} element - The target element.
@param {string} value - The classes to be removed.


function toggleClass(element, value, added) {
  if (!value) {
    return;
  }

  if (isNumber(element.length)) {
    forEach(element, function (elem) {
      toggleClass(elem, value, added);
    });
    return;
  } // IE10-11 doesn't support the second parameter of `classList.toggle`

  if (added) {
    addClass(element, value);
  } else {
    removeClass(element, value);
  }
}

Add or remove classes from the given element.
@param {Element} element - The target element.
@param {string} value - The classes to be toggled.
@param {boolean} added - Add only.

Add or remove classes from the given element .


function getData(element, name) {
  if (isObject(element[name])) {
    return element[name];
  }

  if (element.dataset) {
    return element.dataset[name];
  }

  return element.getAttribute("data-".concat(toParamCase(name)));
}

function getData ( element , name ) { if ( isObject ( element [ name ] ) ) { return element [ name ] ; } if ( element . dataset ) { return element . dataset [ name ] ; } return element . getAttribute ( "data-" . concat ( toParamCase ( name ) ) ) ; }

Get data from the given element.
@param {Element} element - The target element.
@param {string} name - The data key to get.
@returns {string} The data value.


function setData(element, name, data) {
  if (isObject(data)) {
    element[name] = data;
  } else if (element.dataset) {
    element.dataset[name] = data;
  } else {
    element.setAttribute("data-".concat(toParamCase(name)), data);
  }
}

function setData ( element , name , data ) { if ( isObject ( data ) ) { element [ name ] = data ; } else if ( element . dataset ) { element . dataset [ name ] = data ; } else { element . setAttribute ( "data-" . concat ( toParamCase ( name ) ) , data ) ; } }

Set data to the given element.
@param {Element} element - The target element.
@param {string} name - The data key to set.
@param {string} data - The data value.


function removeData(element, name) {
  if (isObject(element[name])) {
    try {
      delete element[name];
    } catch (error) {
      element[name] = undefined;
    }
  } else if (element.dataset) {
    // #128 Safari not allows to delete dataset property
    try {
      delete element.dataset[name];
    } catch (error) {
      element.dataset[name] = undefined;
    }
  } else {
    element.removeAttribute("data-".concat(toParamCase(name)));
  }
}

Remove data from the given element.
@param {Element} element - The target element.
@param {string} name - The data key to remove.


function removeListener(element, type, listener) {
  var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
  var handler = listener;
  type.trim().split(REGEXP_SPACES).forEach(function (event) {
    if (!onceSupported) {
      var listeners = element.listeners;

      if (listeners && listeners[event] && listeners[event][listener]) {
        handler = listeners[event][listener];
        delete listeners[event][listener];

        if (Object.keys(listeners[event]).length === 0) {
          delete listeners[event];
        }

        if (Object.keys(listeners).length === 0) {
          delete element.listeners;
        }
      }
    }

    element.removeEventListener(event, handler, options);
  });
}

function removeListener ( element , type , listener ) { var options = arguments . length > 3 && arguments [ 3 ] !== undefined ? arguments [ 3 ] : { } ; var handler = listener ; type . trim ( ) . split ( REGEXP_SPACES ) . forEach ( function ( event ) { if ( ! onceSupported ) { var listeners = element . listeners ; if ( listeners && listeners [ event ] && listeners [ event ] [ listener ] ) { handler = listeners [ event ] [ listener ] ; delete listeners [ event ] [ listener ] ; if ( Object . keys ( listeners [ event ] ) . length === 0 ) { delete listeners [ event ] ; } if ( Object . keys ( listeners ) . length === 0 ) { delete element . listeners ; } } } element . removeEventListener ( event , handler , options ) ; } ) ; }

Remove event listener from the target element.
@param {Element} element - The event target.
@param {string} type - The event type(s).
@param {Function} listener - The event listener.
@param {Object} options - The event options.

Remove event listener from the target element .


function addListener(element, type, listener) {
  var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
  var _handler = listener;
  type.trim().split(REGEXP_SPACES).forEach(function (event) {
    if (options.once && !onceSupported) {
      var _element$listeners = element.listeners,
          listeners = _element$listeners === void 0 ? {} : _element$listeners;

      _handler = function handler() {
        delete listeners[event][listener];
        element.removeEventListener(event, _handler, options);

        for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
          args[_key2] = arguments[_key2];
        }

        listener.apply(element, args);
      };

      if (!listeners[event]) {
        listeners[event] = {};
      }

      if (listeners[event][listener]) {
        element.removeEventListener(event, listeners[event][listener], options);
      }

      listeners[event][listener] = _handler;
      element.listeners = listeners;
    }

    element.addEventListener(event, _handler, options);
  });
}

function addListener ( element , type , listener ) { var options = arguments . length > 3 && arguments [ 3 ] !== undefined ? arguments [ 3 ] : { } ; var _handler = listener ; type . trim ( ) . split ( REGEXP_SPACES ) . forEach ( function ( event ) { if ( options . once && ! onceSupported ) { var _element$listeners = element . listeners , listeners = _element$listeners === void 0 ? { } : _element$listeners ; _handler = function handler ( ) { delete listeners [ event ] [ listener ] ; element . removeEventListener ( event , _handler , options ) ; for ( var _len2 = arguments . length , args = new Array ( _len2 ) , _key2 = 0 ; _key2 < _len2 ; _key2 ++ ) { args [ _key2 ] = arguments [ _key2 ] ; } listener . apply ( element , args ) ; } ; if ( ! listeners [ event ] ) { listeners [ event ] = { } ; } if ( listeners [ event ] [ listener ] ) { element . removeEventListener ( event , listeners [ event ] [ listener ] , options ) ; } listeners [ event ] [ listener ] = _handler ; element . listeners = listeners ; } element . addEventListener ( event , _handler , options ) ; } ) ; }

Add event listener to the target element.
@param {Element} element - The event target.
@param {string} type - The event type(s).
@param {Function} listener - The event listener.
@param {Object} options - The event options.

Add event listener to the target element .


function dispatchEvent(element, type, data) {
  var event; // Event and CustomEvent on IE9-11 are global objects, not constructors

  if (isFunction(Event) && isFunction(CustomEvent)) {
    event = new CustomEvent(type, {
      detail: data,
      bubbles: true,
      cancelable: true
    });
  } else {
    event = document.createEvent('CustomEvent');
    event.initCustomEvent(type, true, true, data);
  }

  return element.dispatchEvent(event);
}

Dispatch event on the target element.
@param {Element} element - The event target.
@param {string} type - The event type(s).
@param {Object} data - The additional event data.
@returns {boolean} Indicate if the event is default prevented or not.


function getOffset(element) {
  var box = element.getBoundingClientRect();
  return {
    left: box.left + (window.pageXOffset - document.documentElement.clientLeft),
    top: box.top + (window.pageYOffset - document.documentElement.clientTop)
  };
}

function getOffset ( element ) { var box = element . getBoundingClientRect ( ) ; return { left : box . left + ( window . pageXOffset - document . documentElement . clientLeft ) , top : box . top + ( window . pageYOffset - document . documentElement . clientTop ) } ; }

Get the offset base on the document.
@param {Element} element - The target element.
@returns {Object} The offset data.


function addTimestamp(url) {
  var timestamp = "timestamp=".concat(new Date().getTime());
  return url + (url.indexOf('?') === -1 ? '?' : '&') + timestamp;
}

function addTimestamp ( url ) { var timestamp = "timestamp=" . concat ( new Date ( ) . getTime ( ) ) ; return url + ( url . indexOf ( '?' ) === - 1 ? '?' : '&' ) + timestamp ; }

Add timestamp to the given URL.
@param {string} url - The target URL.
@returns {string} The result URL.


function getTransforms(_ref) {
  var rotate = _ref.rotate,
      scaleX = _ref.scaleX,
      scaleY = _ref.scaleY,
      translateX = _ref.translateX,
      translateY = _ref.translateY;
  var values = [];

  if (isNumber(translateX) && translateX !== 0) {
    values.push("translateX(".concat(translateX, "px)"));
  }

  if (isNumber(translateY) && translateY !== 0) {
    values.push("translateY(".concat(translateY, "px)"));
  } // Rotate should come first before scale to match orientation transform

  if (isNumber(rotate) && rotate !== 0) {
    values.push("rotate(".concat(rotate, "deg)"));
  }

  if (isNumber(scaleX) && scaleX !== 1) {
    values.push("scaleX(".concat(scaleX, ")"));
  }

  if (isNumber(scaleY) && scaleY !== 1) {
    values.push("scaleY(".concat(scaleY, ")"));
  }

  var transform = values.length ? values.join(' ') : 'none';
  return {
    WebkitTransform: transform,
    msTransform: transform,
    transform: transform
  };
}

Get transforms base on the given object.
@param {Object} obj - The target object.
@returns {string} A string contains transform values.

Get transforms base on the given object .


function getMaxZoomRatio(pointers) {
  var pointers2 = assign({}, pointers);
  var ratios = [];
  forEach(pointers, function (pointer, pointerId) {
    delete pointers2[pointerId];
    forEach(pointers2, function (pointer2) {
      var x1 = Math.abs(pointer.startX - pointer2.startX);
      var y1 = Math.abs(pointer.startY - pointer2.startY);
      var x2 = Math.abs(pointer.endX - pointer2.endX);
      var y2 = Math.abs(pointer.endY - pointer2.endY);
      var z1 = Math.sqrt(x1 * x1 + y1 * y1);
      var z2 = Math.sqrt(x2 * x2 + y2 * y2);
      var ratio = (z2 - z1) / z1;
      ratios.push(ratio);
    });
  });
  ratios.sort(function (a, b) {
    return Math.abs(a) < Math.abs(b);
  });
  return ratios[0];
}

function getMaxZoomRatio ( pointers ) { var pointers2 = assign ( { } , pointers ) ; var ratios = [ ] ; forEach ( pointers , function ( pointer , pointerId ) { delete pointers2 [ pointerId ] ; forEach ( pointers2 , function ( pointer2 ) { var x1 = Math . abs ( pointer . startX - pointer2 . startX ) ; var y1 = Math . abs ( pointer . startY - pointer2 . startY ) ; var x2 = Math . abs ( pointer . endX - pointer2 . endX ) ; var y2 = Math . abs ( pointer . endY - pointer2 . endY ) ; var z1 = Math . sqrt ( x1 * x1 + y1 * y1 ) ; var z2 = Math . sqrt ( x2 * x2 + y2 * y2 ) ; var ratio = ( z2 - z1 ) / z1 ; ratios . push ( ratio ) ; } ) ; } ) ; ratios . sort ( function ( a , b ) { return Math . abs ( a ) < Math . abs ( b ) ; } ) ; return ratios [ 0 ] ; }

Get the max ratio of a group of pointers.
@param {string} pointers - The target pointers.
@returns {number} The result ratio.

Get the max ratio of a group of pointers .


function getPointer(_ref2, endOnly) {
  var pageX = _ref2.pageX,
      pageY = _ref2.pageY;
  var end = {
    endX: pageX,
    endY: pageY
  };
  return endOnly ? end : assign({
    startX: pageX,
    startY: pageY
  }, end);
}

function getPointer ( _ref2 , endOnly ) { var pageX = _ref2 . pageX , pageY = _ref2 . pageY ; var end = { endX : pageX , endY : pageY } ; return endOnly ? end : assign ( { startX : pageX , startY : pageY } , end ) ; }

Get a pointer from an event object.
@param {Object} event - The target event object.
@param {boolean} endOnly - Indicates if only returns the end point coordinate or not.
@returns {Object} The result pointer contains start and/or end point coordinates.


function getPointersCenter(pointers) {
  var pageX = 0;
  var pageY = 0;
  var count = 0;
  forEach(pointers, function (_ref3) {
    var startX = _ref3.startX,
        startY = _ref3.startY;
    pageX += startX;
    pageY += startY;
    count += 1;
  });
  pageX /= count;
  pageY /= count;
  return {
    pageX: pageX,
    pageY: pageY
  };
}

function getPointersCenter ( pointers ) { var pageX = 0 ; var pageY = 0 ; var count = 0 ; forEach ( pointers , function ( _ref3 ) { var startX = _ref3 . startX , startY = _ref3 . startY ; pageX += startX ; pageY += startY ; count += 1 ; } ) ; pageX /= count ; pageY /= count ; return { pageX : pageX , pageY : pageY } ; }

Get the center point coordinate of a group of pointers.
@param {Object} pointers - The target pointers.
@returns {Object} The center point coordinate.

Get the center point coordinate of a group of pointers .


function getAdjustedSizes(_ref4) // or 'cover'
{
  var aspectRatio = _ref4.aspectRatio,
      height = _ref4.height,
      width = _ref4.width;
  var type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'contain';
  var isValidWidth = isPositiveNumber(width);
  var isValidHeight = isPositiveNumber(height);

  if (isValidWidth && isValidHeight) {
    var adjustedWidth = height * aspectRatio;

    if (type === 'contain' && adjustedWidth > width || type === 'cover' && adjustedWidth < width) {
      height = width / aspectRatio;
    } else {
      width = height * aspectRatio;
    }
  } else if (isValidWidth) {
    height = width / aspectRatio;
  } else if (isValidHeight) {
    width = height * aspectRatio;
  }

  return {
    width: width,
    height: height
  };
}

function
getAdjustedSizes
(
_ref4
)
// or 'cover'
{
var
aspectRatio
=
_ref4
.
aspectRatio
,
height
=
_ref4
.
height
,
width
=
_ref4
.
width
;
var
type
=
arguments
.
length
>
1
&&
arguments
[
1
]
!==
undefined
arguments
[
1
]
:
'contain'
;
var
isValidWidth
=
isPositiveNumber
(
width
)
;
var
isValidHeight
=
isPositiveNumber
(
height
)
;
if
(
isValidWidth
&&
isValidHeight
)
{
var
adjustedWidth
=
height
*
aspectRatio
;
if
(
type
===
'contain'
&&
adjustedWidth
>
width
||
type
===
'cover'
&&
adjustedWidth
<
width
)
{
height
=
width
/
aspectRatio
;
}
else
{
width
=
height
*
aspectRatio
;
}
}
else
if
(
isValidWidth
)
{
height
=
width
/
aspectRatio
;
}
else
if
(
isValidHeight
)
{
width
=
height
*
aspectRatio
;
}
return
{
width
:
width
,
height
:
height
}
;
}

Get the max sizes in a rectangle under the given aspect ratio.
@param {Object} data - The original sizes.
@param {string} [type='contain'] - The adjust type.
@returns {Object} The result sizes.

Get the max sizes in a rectangle under the given aspect ratio .


function getRotatedSizes(_ref5) {
  var width = _ref5.width,
      height = _ref5.height,
      degree = _ref5.degree;
  degree = Math.abs(degree) % 180;

  if (degree === 90) {
    return {
      width: height,
      height: width
    };
  }

  var arc = degree % 90 * Math.PI / 180;
  var sinArc = Math.sin(arc);
  var cosArc = Math.cos(arc);
  var newWidth = width * cosArc + height * sinArc;
  var newHeight = width * sinArc + height * cosArc;
  return degree > 90 ? {
    width: newHeight,
    height: newWidth
  } : {
    width: newWidth,
    height: newHeight
  };
}

function getRotatedSizes ( _ref5 ) { var width = _ref5 . width , height = _ref5 . height , degree = _ref5 . degree ; degree = Math . abs ( degree ) % 180 ; if ( degree === 90 ) { return { width : height , height : width } ; } var arc = degree % 90 * Math . PI / 180 ; var sinArc = Math . sin ( arc ) ; var cosArc = Math . cos ( arc ) ; var newWidth = width * cosArc + height * sinArc ; var newHeight = width * sinArc + height * cosArc ; return degree > 90 ? { width : newHeight , height : newWidth } : { width : newWidth , height : newHeight } ; }

Get the new sizes of a rectangle after rotated.
@param {Object} data - The original sizes.
@returns {Object} The result sizes.

Get the new sizes of a rectangle after rotated .


function dataURLToArrayBuffer(dataURL) {
  var base64 = dataURL.replace(REGEXP_DATA_URL_HEAD, '');
  var binary = atob(base64);
  var arrayBuffer = new ArrayBuffer(binary.length);
  var uint8 = new Uint8Array(arrayBuffer);
  forEach(uint8, function (value, i) {
    uint8[i] = binary.charCodeAt(i);
  });
  return arrayBuffer;
}

function dataURLToArrayBuffer ( dataURL ) { var base64 = dataURL . replace ( REGEXP_DATA_URL_HEAD , '' ) ; var binary = atob ( base64 ) ; var arrayBuffer = new ArrayBuffer ( binary . length ) ; var uint8 = new Uint8Array ( arrayBuffer ) ; forEach ( uint8 , function ( value , i ) { uint8 [ i ] = binary . charCodeAt ( i ) ; } ) ; return arrayBuffer ; }

Transform Data URL to array buffer.
@param {string} dataURL - The Data URL to transform.
@returns {ArrayBuffer} The result array buffer.


function arrayBufferToDataURL(arrayBuffer, mimeType) {
  var chunks = []; // Chunk Typed Array for better performance (#435)

  var chunkSize = 8192;
  var uint8 = new Uint8Array(arrayBuffer);

  while (uint8.length > 0) {
    // XXX: Babel's `toConsumableArray` helper will throw error in IE or Safari 9
    // eslint-disable-next-line prefer-spread
    chunks.push(fromCharCode.apply(null, toArray(uint8.subarray(0, chunkSize))));
    uint8 = uint8.subarray(chunkSize);
  }

  return "data:".concat(mimeType, ";base64,").concat(btoa(chunks.join('')));
}

Transform array buffer to Data URL.
@param {ArrayBuffer} arrayBuffer - The array buffer to transform.
@param {string} mimeType - The mime type of the Data URL.
@returns {string} The result Data URL.


function parseOrientation(orientation) {
  var rotate = 0;
  var scaleX = 1;
  var scaleY = 1;

  switch (orientation) {
    // Flip horizontal
    case 2:
      scaleX = -1;
      break;
    // Rotate left 180°

    case 3:
      rotate = -180;
      break;
    // Flip vertical

    case 4:
      scaleY = -1;
      break;
    // Flip vertical and rotate right 90°

    case 5:
      rotate = 90;
      scaleY = -1;
      break;
    // Rotate right 90°

    case 6:
      rotate = 90;
      break;
    // Flip horizontal and rotate right 90°

    case 7:
      rotate = 90;
      scaleX = -1;
      break;
    // Rotate left 90°

    case 8:
      rotate = -90;
      break;

    default:
  }

  return {
    rotate: rotate,
    scaleX: scaleX,
    scaleY: scaleY
  };
}

function
parseOrientation
(
orientation
)
{
var
rotate
=
0
;
var
scaleX
=
1
;
var
scaleY
=
1
;
switch
(
orientation
)
{
// Flip horizontal
case
2
:
scaleX
=
1
;
break
;
// Rotate left 180°
case
3
:
rotate
=
180
;
break
;
// Flip vertical
case
4
:
scaleY
=
1
;
break
;
// Flip vertical and rotate right 90°
case
5
:
rotate
=
90
;
scaleY
=
1
;
break
;
// Rotate right 90°
case
6
:
rotate
=
90
;
break
;
// Flip horizontal and rotate right 90°
case
7
:
rotate
=
90
;
scaleX
=
1
;
break
;
// Rotate left 90°
case
8
:
rotate
=
90
;
break
;
default
:
}
return
{
rotate
:
rotate
,
scaleX
:
scaleX
,
scaleY
:
scaleY
}
;
}

Parse Exif Orientation value.
@param {number} orientation - The orientation to parse.
@returns {Object} The parsed result.


function crop() {
    if (this.ready && !this.cropped && !this.disabled) {
      this.cropped = true;
      this.limitCropBox(true, true);

      if (this.options.modal) {
        addClass(this.dragBox, CLASS_MODAL);
      }

      removeClass(this.cropBox, CLASS_HIDDEN);
      this.setCropBoxData(this.initialCropBoxData);
    }

    return this;
  }

function crop ( ) { if ( this . ready && ! this . cropped && ! this . disabled ) { this . cropped = true ; this . limitCropBox ( true , true ) ; if ( this . options . modal ) { addClass ( this . dragBox , CLASS_MODAL ) ; } removeClass ( this . cropBox , CLASS_HIDDEN ) ; this . setCropBoxData ( this . initialCropBoxData ) ; } return this ; }


function reset() {
    if (this.ready && !this.disabled) {
      this.imageData = assign({}, this.initialImageData);
      this.canvasData = assign({}, this.initialCanvasData);
      this.cropBoxData = assign({}, this.initialCropBoxData);
      this.renderCanvas();

      if (this.cropped) {
        this.renderCropBox();
      }
    }

    return this;
  }

function reset ( ) { if ( this . ready && ! this . disabled ) { this . imageData = assign ( { } , this . initialImageData ) ; this . canvasData = assign ( { } , this . initialCanvasData ) ; this . cropBoxData = assign ( { } , this . initialCropBoxData ) ; this . renderCanvas ( ) ; if ( this . cropped ) { this . renderCropBox ( ) ; } } return this ; }

Reset the image and crop box to their initial states


function clear() {
    if (this.cropped && !this.disabled) {
      assign(this.cropBoxData, {
        left: 0,
        top: 0,
        width: 0,
        height: 0
      });
      this.cropped = false;
      this.renderCropBox();
      this.limitCanvas(true, true); // Render canvas after crop box rendered

      this.renderCanvas();
      removeClass(this.dragBox, CLASS_MODAL);
      addClass(this.cropBox, CLASS_HIDDEN);
    }

    return this;
  }


function replace(url) {
    var hasSameSize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;

    if (!this.disabled && url) {
      if (this.isImg) {
        this.element.src = url;
      }

      if (hasSameSize) {
        this.url = url;
        this.image.src = url;

        if (this.ready) {
          this.viewBoxImage.src = url;
          forEach(this.previews, function (element) {
            element.getElementsByTagName('img')[0].src = url;
          });
        }
      } else {
        if (this.isImg) {
          this.replaced = true;
        }

        this.options.data = null;
        this.uncreate();
        this.load(url);
      }
    }

    return this;
  }

function replace ( url ) { var hasSameSize = arguments . length > 1 && arguments [ 1 ] !== undefined ? arguments [ 1 ] : false ; if ( ! this . disabled && url ) { if ( this . isImg ) { this . element . src = url ; } if ( hasSameSize ) { this . url = url ; this . image . src = url ; if ( this . ready ) { this . viewBoxImage . src = url ; forEach ( this . previews , function ( element ) { element . getElementsByTagName ( 'img' ) [ 0 ] . src = url ; } ) ; } } else { if ( this . isImg ) { this . replaced = true ; } this . options . data = null ; this . uncreate ( ) ; this . load ( url ) ; } } return this ; }

Replace the image's src and rebuild the cropper
@param {string} url - The new URL.
@param {boolean} [hasSameSize] - Indicate if the new image has the same size as the old one.
@returns {Cropper} this

Replace the image s src and rebuild the cropper


function destroy() {
    var element = this.element;

    if (!element[NAMESPACE]) {
      return this;
    }

    element[NAMESPACE] = undefined;

    if (this.isImg && this.replaced) {
      element.src = this.originalUrl;
    }

    this.uncreate();
    return this;
  }

function destroy ( ) { var element = this . element ; if ( ! element [ NAMESPACE ] ) { return this ; } element [ NAMESPACE ] = undefined ; if ( this . isImg && this . replaced ) { element . src = this . originalUrl ; } this . uncreate ( ) ; return this ; }

Destroy the cropper and remove the instance from the image
@returns {Cropper} this


function move(offsetX) {
    var offsetY = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : offsetX;
    var _this$canvasData = this.canvasData,
        left = _this$canvasData.left,
        top = _this$canvasData.top;
    return this.moveTo(isUndefined(offsetX) ? offsetX : left + Number(offsetX), isUndefined(offsetY) ? offsetY : top + Number(offsetY));
  }

function move ( offsetX ) { var offsetY = arguments . length > 1 && arguments [ 1 ] !== undefined ? arguments [ 1 ] : offsetX ; var _this$canvasData = this . canvasData , left = _this$canvasData . left , top = _this$canvasData . top ; return this . moveTo ( isUndefined ( offsetX ) ? offsetX : left + Number ( offsetX ) , isUndefined ( offsetY ) ? offsetY : top + Number ( offsetY ) ) ; }

Move the canvas with relative offsets
@param {number} offsetX - The relative offset distance on the x-axis.
@param {number} [offsetY=offsetX] - The relative offset distance on the y-axis.
@returns {Cropper} this


function moveTo(x) {
    var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : x;
    var canvasData = this.canvasData;
    var changed = false;
    x = Number(x);
    y = Number(y);

    if (this.ready && !this.disabled && this.options.movable) {
      if (isNumber(x)) {
        canvasData.left = x;
        changed = true;
      }

      if (isNumber(y)) {
        canvasData.top = y;
        changed = true;
      }

      if (changed) {
        this.renderCanvas(true);
      }
    }

    return this;
  }

function moveTo ( x ) { var y = arguments . length > 1 && arguments [ 1 ] !== undefined ? arguments [ 1 ] : x ; var canvasData = this . canvasData ; var changed = false ; x = Number ( x ) ; y = Number ( y ) ; if ( this . ready && ! this . disabled && this . options . movable ) { if ( isNumber ( x ) ) { canvasData . left = x ; changed = true ; } if ( isNumber ( y ) ) { canvasData . top = y ; changed = true ; } if ( changed ) { this . renderCanvas ( true ) ; } } return this ; }

Move the canvas to an absolute point
@param {number} x - The x-axis coordinate.
@param {number} [y=x] - The y-axis coordinate.
@returns {Cropper} this


function zoom(ratio, _originalEvent) {
    var canvasData = this.canvasData;
    ratio = Number(ratio);

    if (ratio < 0) {
      ratio = 1 / (1 - ratio);
    } else {
      ratio = 1 + ratio;
    }

    return this.zoomTo(canvasData.width * ratio / canvasData.naturalWidth, null, _originalEvent);
  }

function zoom ( ratio , _originalEvent ) { var canvasData = this . canvasData ; ratio = Number ( ratio ) ; if ( ratio < 0 ) { ratio = 1 / ( 1 - ratio ) ; } else { ratio = 1 + ratio ; } return this . zoomTo ( canvasData . width * ratio / canvasData . naturalWidth , null , _originalEvent ) ; }

Zoom the canvas with a relative ratio
@param {number} ratio - The target ratio.
@param {Event} _originalEvent - The original event if any.
@returns {Cropper} this


function rotateTo(degree) {
    degree = Number(degree);

    if (isNumber(degree) && this.ready && !this.disabled && this.options.rotatable) {
      this.imageData.rotate = degree % 360;
      this.renderCanvas(true, true);
    }

    return this;
  }

function rotateTo ( degree ) { degree = Number ( degree ) ; if ( isNumber ( degree ) && this . ready && ! this . disabled && this . options . rotatable ) { this . imageData . rotate = degree % 360 ; this . renderCanvas ( true , true ) ; } return this ; }

Rotate the canvas to an absolute degree
@param {number} degree - The rotate degree.
@returns {Cropper} this


function scaleX(_scaleX) {
    var scaleY = this.imageData.scaleY;
    return this.scale(_scaleX, isNumber(scaleY) ? scaleY : 1);
  }

function scaleX ( _scaleX ) { var scaleY = this . imageData . scaleY ; return this . scale ( _scaleX , isNumber ( scaleY ) ? scaleY : 1 ) ; }

Scale the image on the x-axis.
@param {number} scaleX - The scale ratio on the x-axis.
@returns {Cropper} this


function scaleY(_scaleY) {
    var scaleX = this.imageData.scaleX;
    return this.scale(isNumber(scaleX) ? scaleX : 1, _scaleY);
  }

function scaleY ( _scaleY ) { var scaleX = this . imageData . scaleX ; return this . scale ( isNumber ( scaleX ) ? scaleX : 1 , _scaleY ) ; }

Scale the image on the y-axis.
@param {number} scaleY - The scale ratio on the y-axis.
@returns {Cropper} this


function scale(scaleX) {
    var scaleY = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : scaleX;
    var imageData = this.imageData;
    var transformed = false;
    scaleX = Number(scaleX);
    scaleY = Number(scaleY);

    if (this.ready && !this.disabled && this.options.scalable) {
      if (isNumber(scaleX)) {
        imageData.scaleX = scaleX;
        transformed = true;
      }

      if (isNumber(scaleY)) {
        imageData.scaleY = scaleY;
        transformed = true;
      }

      if (transformed) {
        this.renderCanvas(true, true);
      }
    }

    return this;
  }

function scale ( scaleX ) { var scaleY = arguments . length > 1 && arguments [ 1 ] !== undefined ? arguments [ 1 ] : scaleX ; var imageData = this . imageData ; var transformed = false ; scaleX = Number ( scaleX ) ; scaleY = Number ( scaleY ) ; if ( this . ready && ! this . disabled && this . options . scalable ) { if ( isNumber ( scaleX ) ) { imageData . scaleX = scaleX ; transformed = true ; } if ( isNumber ( scaleY ) ) { imageData . scaleY = scaleY ; transformed = true ; } if ( transformed ) { this . renderCanvas ( true , true ) ; } } return this ; }

Scale the image
@param {number} scaleX - The scale ratio on the x-axis.
@param {number} [scaleY=scaleX] - The scale ratio on the y-axis.
@returns {Cropper} this


function getCanvasData() {
    var canvasData = this.canvasData;
    var data = {};

    if (this.ready) {
      forEach(['left', 'top', 'width', 'height', 'naturalWidth', 'naturalHeight'], function (n) {
        data[n] = canvasData[n];
      });
    }

    return data;
  }

function getCanvasData ( ) { var canvasData = this . canvasData ; var data = { } ; if ( this . ready ) { forEach ( [ 'left' , 'top' , 'width' , 'height' , 'naturalWidth' , 'naturalHeight' ] , function ( n ) { data [ n ] = canvasData [ n ] ; } ) ; } return data ; }

Get the canvas position and size data.
@returns {Object} The result canvas data.


function setCanvasData(data) {
    var canvasData = this.canvasData;
    var aspectRatio = canvasData.aspectRatio;

    if (this.ready && !this.disabled && isPlainObject(data)) {
      if (isNumber(data.left)) {
        canvasData.left = data.left;
      }

      if (isNumber(data.top)) {
        canvasData.top = data.top;
      }

      if (isNumber(data.width)) {
        canvasData.width = data.width;
        canvasData.height = data.width / aspectRatio;
      } else if (isNumber(data.height)) {
        canvasData.height = data.height;
        canvasData.width = data.height * aspectRatio;
      }

      this.renderCanvas(true);
    }

    return this;
  }

function setCanvasData ( data ) { var canvasData = this . canvasData ; var aspectRatio = canvasData . aspectRatio ; if ( this . ready && ! this . disabled && isPlainObject ( data ) ) { if ( isNumber ( data . left ) ) { canvasData . left = data . left ; } if ( isNumber ( data . top ) ) { canvasData . top = data . top ; } if ( isNumber ( data . width ) ) { canvasData . width = data . width ; canvasData . height = data . width / aspectRatio ; } else if ( isNumber ( data . height ) ) { canvasData . height = data . height ; canvasData . width = data . height * aspectRatio ; } this . renderCanvas ( true ) ; } return this ; }

Set the canvas position and size with new data.
@param {Object} data - The new canvas data.
@returns {Cropper} this

Set the canvas position and size with new data .


function setAspectRatio(aspectRatio) {
    var options = this.options;

    if (!this.disabled && !isUndefined(aspectRatio)) {
      // 0 -> NaN
      options.aspectRatio = Math.max(0, aspectRatio) || NaN;

      if (this.ready) {
        this.initCropBox();

        if (this.cropped) {
          this.renderCropBox();
        }
      }
    }

    return this;
  }

function
setAspectRatio
(
aspectRatio
)
{
var
options
=
this
.
options
;
if
(
!
this
.
disabled
&&
!
isUndefined
(
aspectRatio
)
)
{
// 0 -> NaN
options
.
aspectRatio
=
Math
.
max
(
0
,
aspectRatio
)
||
;
if
(
this
.
ready
)
{
this
.
initCropBox
(
)
;
if
(
this
.
cropped
)
{
this
.
renderCropBox
(
)
;
}
}
}
return
this
;
}

Change the aspect ratio of the crop box.
@param {number} aspectRatio - The new aspect ratio.
@returns {Cropper} this

Change the aspect ratio of the crop box .


function setDragMode(mode) {
    var options = this.options,
        dragBox = this.dragBox,
        face = this.face;

    if (this.ready && !this.disabled) {
      var croppable = mode === DRAG_MODE_CROP;
      var movable = options.movable && mode === DRAG_MODE_MOVE;
      mode = croppable || movable ? mode : DRAG_MODE_NONE;
      options.dragMode = mode;
      setData(dragBox, DATA_ACTION, mode);
      toggleClass(dragBox, CLASS_CROP, croppable);
      toggleClass(dragBox, CLASS_MOVE, movable);

      if (!options.cropBoxMovable) {
        // Sync drag mode to crop box when it is not movable
        setData(face, DATA_ACTION, mode);
        toggleClass(face, CLASS_CROP, croppable);
        toggleClass(face, CLASS_MOVE, movable);
      }
    }

    return this;
  }

Change the drag mode.
@param {string} mode - The new drag mode.
@returns {Cropper} this


function(event, func) {
    this._events = this._events || {};
    this._events[event] = this._events[event] || [];
    this._events[event].push(func);
  }

function ( event , func ) { this . _events = this . _events || { } ; this . _events [ event ] = this . _events [ event ] || [ ] ; this . _events [ event ] . push ( func ) ; }

Add custom event listener
@param  {String} event Event type
@param  {Function} func   Callback
@return {Void}


function(event, func) {
    this._events = this._events || {};
    if (event in this._events === false) return;
    this._events[event].splice(this._events[event].indexOf(func), 1);
  }

function ( event , func ) { this . _events = this . _events || { } ; if ( event in this . _events === false ) return ; this . _events [ event ] . splice ( this . _events [ event ] . indexOf ( func ) , 1 ) ; }

Remove custom event listener
@param  {String} event Event type
@param  {Function} func   Callback
@return {Void}


function appendItem(item, parent, custom) {
  if (item.parentNode) {
    if (!item.parentNode.parentNode) {
      parent.appendChild(item.parentNode);
    }
  } else {
    parent.appendChild(item);
  }

  util.removeClass(item, "excluded");
  if (!custom) {
    item.innerHTML = item.textContent;
  }
}

function appendItem ( item , parent , custom ) { if ( item . parentNode ) { if ( ! item . parentNode . parentNode ) { parent . appendChild ( item . parentNode ) ; } } else { parent . appendChild ( item ) ; } util . removeClass ( item , "excluded" ) ; if ( ! custom ) { item . innerHTML = item . textContent ; } }

Append an item to the list
@param  {Object} item
@param  {Object} custom
@return {Void}


function() {
  if (this.items.length) {
    var f = document.createDocumentFragment();

    if (this.config.pagination) {
      var pages = this.pages.slice(0, this.pageIndex);

      util.each(pages, function(i, items) {
        util.each(items, function(j, item) {
          appendItem(item, f, this.customOption);
        }, this);
      }, this);
    } else {
      util.each(this.items, function(i, item) {
        appendItem(item, f, this.customOption);
      }, this);
    }

    if (f.childElementCount) {
      util.removeClass(this.items[this.navIndex], "active");
      this.navIndex = f.querySelector(".selectr-option").idx;
      util.addClass(this.items[this.navIndex], "active");
    }

    this.tree.appendChild(f);
  }
}

function ( ) { if ( this . items . length ) { var f = document . createDocumentFragment ( ) ; if ( this . config . pagination ) { var pages = this . pages . slice ( 0 , this . pageIndex ) ; util . each ( pages , function ( i , items ) { util . each ( items , function ( j , item ) { appendItem ( item , f , this . customOption ) ; } , this ) ; } , this ) ; } else { util . each ( this . items , function ( i , item ) { appendItem ( item , f , this . customOption ) ; } , this ) ; } if ( f . childElementCount ) { util . removeClass ( this . items [ this . navIndex ] , "active" ) ; this . navIndex = f . querySelector ( ".selectr-option" ) . idx ; util . addClass ( this . items [ this . navIndex ] , "active" ) ; } this . tree . appendChild ( f ) ; } }


function(option, data) {
  data = data || option;
  var content = this.customOption ? this.config.renderOption(data) : option.textContent;
  var opt = util.createElement("li", {
    class: "selectr-option",
    html: content,
    role: "treeitem",
    "aria-selected": false
  });

  opt.idx = option.idx;

  this.items.push(opt);

  if (option.defaultSelected) {
    this.defaultSelected.push(option.idx);
  }

  if (option.disabled) {
    opt.disabled = true;
    util.addClass(opt, "disabled");
  }

  return opt;
}

function ( option , data ) { data = data || option ; var content = this . customOption ? this . config . renderOption ( data ) : option . textContent ; var opt = util . createElement ( "li" , { class : "selectr-option" , html : content , role : "treeitem" , "aria-selected" : false } ) ; opt . idx = option . idx ; this . items . push ( opt ) ; if ( option . defaultSelected ) { this . defaultSelected . push ( option . idx ) ; } if ( option . disabled ) { opt . disabled = true ; util . addClass ( opt , "disabled" ) ; } return opt ; }

Build a list item from the HTMLOptionElement
@param  {int} i      HTMLOptionElement index
@param  {HTMLOptionElement} option
@param  {bool} group  Has parent optgroup
@return {void}


function(item) {
  var that = this,
      r;

  var docFrag = document.createDocumentFragment();
  var option = this.options[item.idx];
  var data = this.data ? this.data[item.idx] : option;
  var content = this.customSelected ? this.config.renderSelection(data) : option.textContent;

  var tag = util.createElement("li", {
    class: "selectr-tag",
    html: content
  });
  var btn = util.createElement("button", {
    class: "selectr-tag-remove",
    type: "button"
  });

  tag.appendChild(btn);

  // Set property to check against later
  tag.idx = item.idx;
  tag.tag = option.value;

  this.tags.push(tag);

  if (this.config.sortSelected) {

    var tags = this.tags.slice();

    // Deal with values that contain numbers
    r = function(val, arr) {
      val.replace(/(\d+)|(\D+)/g, function(that, $1, $2) {
        arr.push([$1 || Infinity, $2 || ""]);
      });
    };

    tags.sort(function(a, b) {
      var x = [],
          y = [],
          ac, bc;
      if (that.config.sortSelected === true) {
        ac = a.tag;
        bc = b.tag;
      } else if (that.config.sortSelected === 'text') {
        ac = a.textContent;
        bc = b.textContent;
      }

      r(ac, x);
      r(bc, y);

      while (x.length && y.length) {
        var ax = x.shift();
        var by = y.shift();
        var nn = (ax[0] - by[0]) || ax[1].localeCompare(by[1]);
        if (nn) return nn;
      }

      return x.length - y.length;
    });

    util.each(tags, function(i, tg) {
      docFrag.appendChild(tg);
    });

    this.label.innerHTML = "";

  } else {
    docFrag.appendChild(tag);
  }

  if (this.config.taggable) {
    this.label.insertBefore(docFrag, this.input.parentNode);
  } else {
    this.label.appendChild(docFrag);
  }
}


function(item) {
  var tag = false;

  util.each(this.tags, function(i, t) {
    if (t.idx === item.idx) {
      tag = t;
    }
  }, this);

  if (tag) {
    this.label.removeChild(tag);
    this.tags.splice(this.tags.indexOf(tag), 1);
  }
}

function ( item ) { var tag = false ; util . each ( this . tags , function ( i , t ) { if ( t . idx === item . idx ) { tag = t ; } } , this ) ; if ( tag ) { this . label . removeChild ( tag ) ; this . tags . splice ( this . tags . indexOf ( tag ) , 1 ) ; } }

Remove a tag
@param  {HTMLElement} item
@return {void}


function() {
  var tree = this.tree;
  var scrollTop = tree.scrollTop;
  var scrollHeight = tree.scrollHeight;
  var offsetHeight = tree.offsetHeight;
  var atBottom = scrollTop >= (scrollHeight - offsetHeight);

  if ((atBottom && this.pageIndex < this.pages.length)) {
    var f = document.createDocumentFragment();

    util.each(this.pages[this.pageIndex], function(i, item) {
      appendItem(item, f, this.customOption);
    }, this);

    tree.appendChild(f);

    this.pageIndex++;

    this.emit("selectr.paginate", {
      items: this.items.length,
      total: this.data.length,
      page: this.pageIndex,
      pages: this.pages.length
    });
  }
}

function ( ) { var tree = this . tree ; var scrollTop = tree . scrollTop ; var scrollHeight = tree . scrollHeight ; var offsetHeight = tree . offsetHeight ; var atBottom = scrollTop >= ( scrollHeight - offsetHeight ) ; if ( ( atBottom && this . pageIndex < this . pages . length ) ) { var f = document . createDocumentFragment ( ) ; util . each ( this . pages [ this . pageIndex ] , function ( i , item ) { appendItem ( item , f , this . customOption ) ; } , this ) ; tree . appendChild ( f ) ; this . pageIndex ++ ; this . emit ( "selectr.paginate" , { items : this . items . length , total : this . data . length , page : this . pageIndex , pages : this . pages . length } ) ; } }

Load the next page of items
@return {void}


function() {
  if (this.config.searchable || this.config.taggable) {
    this.input.value = null;
    this.searching = false;
    if (this.config.searchable) {
      util.removeClass(this.inputContainer, "active");
    }

    if (util.hasClass(this.container, "notice")) {
      util.removeClass(this.container, "notice");
      util.addClass(this.container, "open");
      this.input.focus();
    }

    util.each(this.items, function(i, item) {
      // Items that didn't match need the class
      // removing to make them visible again
      util.removeClass(item, "excluded");
      // Remove the span element for underlining matched items
      if (!this.customOption) {
        item.innerHTML = item.textContent;
      }
    }, this);
  }
}


function(query, option) {
  var result = new RegExp(query, "i").exec(option.textContent);
  if (result) {
    return option.textContent.replace(result[0], "" + result[0] + "");
  }
  return false;
}

function
(
query
,
option
)
{
var
result
=
new
RegExp
(
query
,
"i"
)
.
exec
(
option
.
textContent
)
;
if
(
result
)
{
return
option
.
textContent
.
replace
(
result
[
0
]
,
""
+
result
[
0
]
+
""
)
;
}
return
false
;
}

Query matching for searches
@param  {string} query
@param  {HTMLOptionElement} option
@return {bool}


function TreePath(container, root) {
  if (container) {
    this.root = root;
    this.path = document.createElement('div');
    this.path.className = 'jsoneditor-treepath';
    this.path.setAttribute('tabindex',0);
    this.contentMenuClicked;
    container.appendChild(this.path);
    this.reset();
  }
}

function TreePath ( container , root ) { if ( container ) { this . root = root ; this . path = document . createElement ( 'div' ) ; this . path . className = 'jsoneditor-treepath' ; this . path . setAttribute ( 'tabindex' , 0 ) ; this . contentMenuClicked ; container . appendChild ( this . path ) ; this . reset ( ) ; } }

Creates a component that visualize path selection in tree based editors
@param {HTMLElement} container
@param {HTMLElement} root
@constructor


function ModeSwitcher(container, modes, current, onSwitch) {
  // available modes
  var availableModes = {
    code: {
      'text': translate('modeCodeText'),
      'title': translate('modeCodeTitle'),
      'click': function () {
        onSwitch('code')
      }
    },
    form: {
      'text': translate('modeFormText'),
      'title': translate('modeFormTitle'),
      'click': function () {
        onSwitch('form');
      }
    },
    text: {
      'text': translate('modeTextText'),
      'title': translate('modeTextTitle'),
      'click': function () {
        onSwitch('text');
      }
    },
    tree: {
      'text': translate('modeTreeText'),
      'title': translate('modeTreeTitle'),
      'click': function () {
        onSwitch('tree');
      }
    },
    view: {
      'text': translate('modeViewText'),
      'title': translate('modeViewTitle'),
      'click': function () {
        onSwitch('view');
      }
    }
  };

  // list the selected modes
  var items = [];
  for (var i = 0; i < modes.length; i++) {
    var mode = modes[i];
    var item = availableModes[mode];
    if (!item) {
      throw new Error('Unknown mode "' + mode + '"');
    }

    item.className = 'jsoneditor-type-modes' + ((current == mode) ? ' jsoneditor-selected' : '');
    items.push(item);
  }

  // retrieve the title of current mode
  var currentMode = availableModes[current];
  if (!currentMode) {
    throw new Error('Unknown mode "' + current + '"');
  }
  var currentTitle = currentMode.text;

  // create the html element
  var box = document.createElement('button');
  box.type = 'button';
  box.className = 'jsoneditor-modes jsoneditor-separator';
  box.innerHTML = currentTitle + ' ▾';
  box.title = 'Switch editor mode';
  box.onclick = function () {
    var menu = new ContextMenu(items);
    menu.show(box, container);
  };

  var frame = document.createElement('div');
  frame.className = 'jsoneditor-modes';
  frame.style.position = 'relative';
  frame.appendChild(box);

  container.appendChild(frame);

  this.dom = {
    container: container,
    box: box,
    frame: frame
  };
}

function
ModeSwitcher
(
container
,
modes
,
current
,
onSwitch
)
{
// available modes
var
availableModes
=
{
code
:
{
'text'
:
translate
(
'modeCodeText'
)
,
'title'
:
translate
(
'modeCodeTitle'
)
,
'click'
:
function
(
)
{
onSwitch
(
'code'
)
}
}
,
form
:
{
'text'
:
translate
(
'modeFormText'
)
,
'title'
:
translate
(
'modeFormTitle'
)
,
'click'
:
function
(
)
{
onSwitch
(
'form'
)
;
}
}
,
text
:
{
'text'
:
translate
(
'modeTextText'
)
,
'title'
:
translate
(
'modeTextTitle'
)
,
'click'
:
function
(
)
{
onSwitch
(
'text'
)
;
}
}
,
tree
:
{
'text'
:
translate
(
'modeTreeText'
)
,
'title'
:
translate
(
'modeTreeTitle'
)
,
'click'
:
function
(
)
{
onSwitch
(
'tree'
)
;
}
}
,
view
:
{
'text'
:
translate
(
'modeViewText'
)
,
'title'
:
translate
(
'modeViewTitle'
)
,
'click'
:
function
(
)
{
onSwitch
(
'view'
)
;
}
}
}
;
// list the selected modes
var
items
=
[
]
;
for
(
var
i
=
0
;
i
<
modes
.
length
;
i
++
)
{
var
mode
=
modes
[
i
]
;
var
item
=
availableModes
[
mode
]
;
if
(
!
item
)
{
throw
new
Error
(
'Unknown mode "'
+
mode
+
'"'
)
;
}
item
.
className
=
'jsoneditor-type-modes'
+
(
(
current
==
mode
)
' jsoneditor-selected'
:
''
)
;
items
.
push
(
item
)
;
}
// retrieve the title of current mode
var
currentMode
=
availableModes
[
current
]
;
if
(
!
currentMode
)
{
throw
new
Error
(
'Unknown mode "'
+
current
+
'"'
)
;
}
var
currentTitle
=
currentMode
.
text
;
// create the html element
var
box
=
document
.
createElement
(
'button'
)
;
box
.
type
=
'button'
;
box
.
className
=
'jsoneditor-modes jsoneditor-separator'
;
box
.
innerHTML
=
currentTitle
+
' ▾'
;
box
.
title
=
'Switch editor mode'
;
box
.
onclick
=
function
(
)
{
var
menu
=
new
ContextMenu
(
items
)
;
menu
.
show
(
box
,
container
)
;
}
;
var
frame
=
document
.
createElement
(
'div'
)
;
frame
.
className
=
'jsoneditor-modes'
;
frame
.
style
.
position
=
'relative'
;
frame
.
appendChild
(
box
)
;
container
.
appendChild
(
frame
)
;
this
.
dom
=
{
container
:
container
,
box
:
box
,
frame
:
frame
}
;
}

Create a select box to be used in the editor menu's, which allows to switch mode
@param {HTMLElement} container
@param {String[]} modes  Available modes: 'code', 'form', 'text', 'tree', 'view'
@param {String} current  Available modes: 'code', 'form', 'text', 'tree', 'view'
@param {function(mode: string)} onSwitch  Callback invoked on switch
@constructor

Create a select box to be used in the editor menu s which allows to switch mode


function lastNonWhitespace () {
    var p = chars.length - 1;

    while (p >= 0) {
      var pp = chars[p];
      if (!isWhiteSpace(pp)) {
        return pp;
      }
      p--;
    }

    return '';
  }

function lastNonWhitespace ( ) { var p = chars . length - 1 ; while ( p >= 0 ) { var pp = chars [ p ] ; if ( ! isWhiteSpace ( pp ) ) { return pp ; } p -- ; } return '' ; }

get the last parsed non-whitespace character

get the last parsed non - whitespace character


function nextNonWhiteSpace() {
    var iNext = i + 1;
    while (iNext < jsString.length && isWhiteSpace(jsString[iNext])) {
      iNext++;
    }

    return jsString[iNext];
  }

function nextNonWhiteSpace ( ) { var iNext = i + 1 ; while ( iNext < jsString . length && isWhiteSpace ( jsString [ iNext ] ) ) { iNext ++ ; } return jsString [ iNext ] ; }

get at the first next non-white space character

get at the first next non - white space character


function parseString(endQuote) {
    chars.push('"');
    i++;
    var c = curr();
    while (i < jsString.length && c !== endQuote) {
      if (c === '"' && prev() !== '\\') {
        // unescaped double quote, escape it
        chars.push('\\"');
      }
      else if (controlChars.hasOwnProperty(c)) {
        // replace unescaped control characters with escaped ones
        chars.push(controlChars[c])
      }
      else if (c === '\\') {
        // remove the escape character when followed by a single quote ', not needed
        i++;
        c = curr();
        if (c !== '\'') {
          chars.push('\\');
        }
        chars.push(c);
      }
      else {
        // regular character
        chars.push(c);
      }

      i++;
      c = curr();
    }
    if (c === endQuote) {
      chars.push('"');
      i++;
    }
  }


function parseKey() {
    var specialValues = ['null', 'true', 'false'];
    var key = '';
    var c = curr();

    var regexp = /[a-zA-Z_$\d]/; // letter, number, underscore, dollar character
    while (regexp.test(c)) {
      key += c;
      i++;
      c = curr();
    }

    if (specialValues.indexOf(key) === -1) {
      chars.push('"' + key + '"');
    }
    else {
      chars.push(key);
    }
  }


function _positionForIndex(index) {
    var textTillIndex = el.value.substring(0,index);
    var row = (textTillIndex.match(/\n/g) || []).length + 1;
    var col = textTillIndex.length - textTillIndex.lastIndexOf("\n");

    return {
      row: row,
      column: col
    }
  }

function _positionForIndex ( index ) { var textTillIndex = el . value . substring ( 0 , index ) ; var row = ( textTillIndex . match ( / \n / g ) || [ ] ) . length + 1 ; var col = textTillIndex . length - textTillIndex . lastIndexOf ( "\n" ) ; return { row : row , column : col } }

Returns textarea row and column position for certain index
@param {Number} index text index
@returns {{row: Number, col: Number}}


function () {
      var scrollTop = content.scrollTop;
      var diff = (finalScrollTop - scrollTop);
      if (Math.abs(diff) > 3) {
        content.scrollTop += diff / 3;
        editor.animateCallback = callback;
        editor.animateTimeout = setTimeout(animate, 50);
      }
      else {
        // finished
        if (callback) {
          callback(true);
        }
        content.scrollTop = finalScrollTop;
        delete editor.animateTimeout;
        delete editor.animateCallback;
      }
    }

function
(
)
{
var
scrollTop
=
content
.
scrollTop
;
var
diff
=
(
finalScrollTop
scrollTop
)
;
if
(
Math
.
abs
(
diff
)
>
3
)
{
content
.
scrollTop
+=
diff
/
3
;
editor
.
animateCallback
=
callback
;
editor
.
animateTimeout
=
setTimeout
(
animate
,
50
)
;
}
else
{
// finished
if
(
callback
)
{
callback
(
true
)
;
}
content
.
scrollTop
=
finalScrollTop
;
delete
editor
.
animateTimeout
;
delete
editor
.
animateCallback
;
}
}


function (event) {
    var target = event.target;
    if ((target !== absoluteAnchor) && !util.isChildOf(target, absoluteAnchor)) {
      destroy();
    }
  }

function ( event ) { var target = event . target ; if ( ( target !== absoluteAnchor ) && ! util . isChildOf ( target , absoluteAnchor ) ) { destroy ( ) ; } }


function render(element) {
  connectDevtools(DesktopRenderer);

  ROOT_NODE = createElement('ROOT');
  const container = ROOT_NODE;

  // Returns the current fiber (flushed fiber)
  const node = DesktopRenderer.createContainer(ROOT_NODE);

  // Schedules a top level update with current fiber and a priority level (depending upon the context)
  DesktopRenderer.updateContainer(element, node, null);
  //ROOT_NODE.render();
}


function whitespace(rule, value, source, errors, options) {
  if (/^\s+$/.test(value) || value === '') {
    errors.push(util.format(options.messages.whitespace, rule.fullField));
  }
}

function whitespace ( rule , value , source , errors , options ) { if ( / ^\s+$ / . test ( value ) || value === '' ) { errors . push ( util . format ( options . messages . whitespace , rule . fullField ) ) ; } }

Rule for validating whitespace.

@param rule The validation rule.
@param value The value of the field on the source object.
@param source The source object being validated.
@param errors An array of errors that this rule may add
validation errors to.
@param options The validation options.
@param options.messages The validation messages.


function enumerable(rule, value, source, errors, options) {
  rule[ENUM] = Array.isArray(rule[ENUM]) ? rule[ENUM] : [];
  if (rule[ENUM].indexOf(value) === -1) {
    errors.push(util.format(options.messages[ENUM], rule.fullField, rule[ENUM].join(', ')));
  }
}

function
enumerable
(
rule
,
value
,
source
,
errors
,
options
)
{
rule
[
ENUM
]
=
Array
.
isArray
(
rule
[
ENUM
]
)
rule
[
ENUM
]
:
[
]
;
if
(
rule
[
ENUM
]
.
indexOf
(
value
)
===
1
)
{
errors
.
push
(
util
.
format
(
options
.
messages
[
ENUM
]
,
rule
.
fullField
,
rule
[
ENUM
]
.
join
(
', '
)
)
)
;
}
}

Rule for validating a value exists in an enumerable list.

@param rule The validation rule.
@param value The value of the field on the source object.
@param source The source object being validated.
@param errors An array of errors that this rule may add
validation errors to.
@param options The validation options.
@param options.messages The validation messages.

Rule for validating a value exists in an enumerable list .


function range(rule, value, source, errors, options) {
  const len = typeof rule.len === 'number';
  const min = typeof rule.min === 'number';
  const max = typeof rule.max === 'number';
  // 正则匹配码点范围从U+010000一直到U+10FFFF的文字（补充平面Supplementary Plane）
  const spRegexp = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
  let val = value;
  let key = null;
  const num = typeof (value) === 'number';
  const str = typeof (value) === 'string';
  const arr = Array.isArray(value);
  if (num) {
    key = 'number';
  } else if (str) {
    key = 'string';
  } else if (arr) {
    key = 'array';
  }
  // if the value is not of a supported type for range validation
  // the validation rule rule should use the
  // type property to also test for a particular type
  if (!key) {
    return false;
  }
  if (arr) {
    val = value.length;
  }
  if (str) {
    // 处理码点大于U+010000的文字length属性不准确的bug，如"𠮷𠮷𠮷".lenght !== 3
    val = value.replace(spRegexp, '_').length;
  }
  if (len) {
    if (val !== rule.len) {
      errors.push(util.format(options.messages[key].len, rule.fullField, rule.len));
    }
  } else if (min && !max && val < rule.min) {
    errors.push(util.format(options.messages[key].min, rule.fullField, rule.min));
  } else if (max && !min && val > rule.max) {
    errors.push(util.format(options.messages[key].max, rule.fullField, rule.max));
  } else if (min && max && (val < rule.min || val > rule.max)) {
    errors.push(util.format(options.messages[key].range,
      rule.fullField, rule.min, rule.max));
  }
}

Rule for validating minimum and maximum allowed values.

@param rule The validation rule.
@param value The value of the field on the source object.
@param source The source object being validated.
@param errors An array of errors that this rule may add
validation errors to.
@param options The validation options.
@param options.messages The validation messages.

Rule for validating minimum and maximum allowed values .


function required(rule, value, source, errors, options, type) {
  if (rule.required &&
    (!source.hasOwnProperty(rule.field) || util.isEmptyValue(value, type || rule.type))) {
    errors.push(util.format(options.messages.required, rule.fullField));
  }
}

function required ( rule , value , source , errors , options , type ) { if ( rule . required && ( ! source . hasOwnProperty ( rule . field ) || util . isEmptyValue ( value , type || rule . type ) ) ) { errors . push ( util . format ( options . messages . required , rule . fullField ) ) ; } }

Rule for validating required fields.

@param rule The validation rule.
@param value The value of the field on the source object.
@param source The source object being validated.
@param errors An array of errors that this rule may add
validation errors to.
@param options The validation options.
@param options.messages The validation messages.


function string(rule, value, callback, source, options) {
  const errors = [];
  const validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field));
  if (validate) {
    if (isEmptyValue(value, 'string') && !rule.required) {
      return callback();
    }
    rules.required(rule, value, source, errors, options, 'string');
    if (!isEmptyValue(value, 'string')) {
      rules.type(rule, value, source, errors, options);
      rules.range(rule, value, source, errors, options);
      rules.pattern(rule, value, source, errors, options);
      if (rule.whitespace === true) {
        rules.whitespace(rule, value, source, errors, options);
      }
    }
  }
  callback(errors);
}

function string ( rule , value , callback , source , options ) { const errors = [ ] ; const validate = rule . required || ( ! rule . required && source . hasOwnProperty ( rule . field ) ) ; if ( validate ) { if ( isEmptyValue ( value , 'string' ) && ! rule . required ) { return callback ( ) ; } rules . required ( rule , value , source , errors , options , 'string' ) ; if ( ! isEmptyValue ( value , 'string' ) ) { rules . type ( rule , value , source , errors , options ) ; rules . range ( rule , value , source , errors , options ) ; rules . pattern ( rule , value , source , errors , options ) ; if ( rule . whitespace === true ) { rules . whitespace ( rule , value , source , errors , options ) ; } } } callback ( errors ) ; }

Performs validation for string types.

@param rule The validation rule.
@param value The value of the field on the source object.
@param callback The callback function.
@param source The source object being validated.
@param options The validation options.
@param options.messages The validation messages.


function regexp(rule, value, callback, source, options) {
  const errors = [];
  const validate = rule.required || (!rule.required && source.hasOwnProperty(rule.field));
  if (validate) {
    if (isEmptyValue(value) && !rule.required) {
      return callback();
    }
    rules.required(rule, value, source, errors, options);
    if (!isEmptyValue(value)) {
      rules.type(rule, value, source, errors, options);
    }
  }
  callback(errors);
}

function regexp ( rule , value , callback , source , options ) { const errors = [ ] ; const validate = rule . required || ( ! rule . required && source . hasOwnProperty ( rule . field ) ) ; if ( validate ) { if ( isEmptyValue ( value ) && ! rule . required ) { return callback ( ) ; } rules . required ( rule , value , source , errors , options ) ; if ( ! isEmptyValue ( value ) ) { rules . type ( rule , value , source , errors , options ) ; } } callback ( errors ) ; }

Validates the regular expression type.

@param rule The validation rule.
@param value The value of the field on the source object.
@param callback The callback function.
@param source The source object being validated.
@param options The validation options.
@param options.messages The validation messages.


function pattern(rule, value, source, errors, options) {
  if (rule.pattern) {
    if (rule.pattern instanceof RegExp) {
      // if a RegExp instance is passed, reset `lastIndex` in case its `global`
      // flag is accidentally set to `true`, which in a validation scenario
      // is not necessary and the result might be misleading
      rule.pattern.lastIndex = 0;
      if (!rule.pattern.test(value)) {
        errors.push(util.format(options.messages.pattern.mismatch,
          rule.fullField, value, rule.pattern));
      }
    } else if (typeof rule.pattern === 'string') {
      const _pattern = new RegExp(rule.pattern);
      if (!_pattern.test(value)) {
        errors.push(util.format(options.messages.pattern.mismatch,
          rule.fullField, value, rule.pattern));
      }
    }
  }
}

Rule for validating a regular expression pattern.

@param rule The validation rule.
@param value The value of the field on the source object.
@param source The source object being validated.
@param errors An array of errors that this rule may add
validation errors to.
@param options The validation options.
@param options.messages The validation messages.

Rule for validating a regular expression pattern .


function type(rule, value, source, errors, options) {
  if (rule.required && value === undefined) {
    required(rule, value, source, errors, options);
    return;
  }
  const custom = ['integer', 'float', 'array', 'regexp', 'object',
    'method', 'email', 'number', 'date', 'url', 'hex'];
  const ruleType = rule.type;
  if (custom.indexOf(ruleType) > -1) {
    if (!types[ruleType](value)) {
      errors.push(util.format(options.messages.types[ruleType], rule.fullField, rule.type));
    }
    // straight typeof check
  } else if (ruleType && typeof (value) !== rule.type) {
    errors.push(util.format(options.messages.types[ruleType], rule.fullField, rule.type));
  }
}

function
type
(
rule
,
value
,
source
,
errors
,
options
)
{
if
(
rule
.
required
&&
value
===
undefined
)
{
required
(
rule
,
value
,
source
,
errors
,
options
)
;
return
;
}
const
custom
=
[
'integer'
,
'float'
,
'array'
,
'regexp'
,
'object'
,
'method'
,
'email'
,
'number'
,
'date'
,
'url'
,
'hex'
]
;
const
ruleType
=
rule
.
type
;
if
(
custom
.
indexOf
(
ruleType
)
>
1
)
{
if
(
!
types
[
ruleType
]
(
value
)
)
{
errors
.
push
(
util
.
format
(
options
.
messages
.
types
[
ruleType
]
,
rule
.
fullField
,
rule
.
type
)
)
;
}
// straight typeof check
}
else
if
(
ruleType
&&
typeof
(
value
)
!==
rule
.
type
)
{
errors
.
push
(
util
.
format
(
options
.
messages
.
types
[
ruleType
]
,
rule
.
fullField
,
rule
.
type
)
)
;
}
}

Rule for validating the type of a value.

@param rule The validation rule.
@param value The value of the field on the source object.
@param source The source object being validated.
@param errors An array of errors that this rule may add
validation errors to.
@param options The validation options.
@param options.messages The validation messages.

Rule for validating the type of a value .


function getStyleComputedProperty(element, property) {
    if (element.nodeType !== 1) {
      return [];
    } // NOTE: 1 DOM access here

    var window = element.ownerDocument.defaultView;
    var css = window.getComputedStyle(element, null);
    return property ? css[property] : css;
  }

function
getStyleComputedProperty
(
element
,
property
)
{
if
(
element
.
nodeType
!==
1
)
{
return
[
]
;
}
// NOTE: 1 DOM access here
var
window
=
element
.
ownerDocument
.
defaultView
;
var
css
=
window
.
getComputedStyle
(
element
,
)
;
return
property
css
[
property
]
:
css
;
}

Get CSS computed property of the given element
@method
@memberof Popper.Utils
@argument {Eement} element
@argument {String} property


function getBoundingClientRect(element) {
    var rect = {}; // IE10 10 FIX: Please, don't ask, the element isn't
    // considered in DOM in some circumstances...
    // This isn't reproducible in IE10 compatibility mode of IE11

    try {
      if (isIE(10)) {
        rect = element.getBoundingClientRect();
        var scrollTop = getScroll(element, 'top');
        var scrollLeft = getScroll(element, 'left');
        rect.top += scrollTop;
        rect.left += scrollLeft;
        rect.bottom += scrollTop;
        rect.right += scrollLeft;
      } else {
        rect = element.getBoundingClientRect();
      }
    } catch (e) {}

    var result = {
      left: rect.left,
      top: rect.top,
      width: rect.right - rect.left,
      height: rect.bottom - rect.top
    }; // subtract scrollbar size from sizes

    var sizes = element.nodeName === 'HTML' ? getWindowSizes(element.ownerDocument) : {};
    var width = sizes.width || element.clientWidth || result.right - result.left;
    var height = sizes.height || element.clientHeight || result.bottom - result.top;
    var horizScrollbar = element.offsetWidth - width;
    var vertScrollbar = element.offsetHeight - height; // if an hypothetical scrollbar is detected, we must be sure it's not a `border`
    // we make this check conditional for performance reasons

    if (horizScrollbar || vertScrollbar) {
      var styles = getStyleComputedProperty(element);
      horizScrollbar -= getBordersSize(styles, 'x');
      vertScrollbar -= getBordersSize(styles, 'y');
      result.width -= horizScrollbar;
      result.height -= vertScrollbar;
    }

    return getClientRect(result);
  }

Get bounding client rect of given element
@method
@memberof Popper.Utils
@param {HTMLElement} element
@return {Object} client rect


function isFixed(element) {
    var nodeName = element.nodeName;

    if (nodeName === 'BODY' || nodeName === 'HTML') {
      return false;
    }

    if (getStyleComputedProperty(element, 'position') === 'fixed') {
      return true;
    }

    var parentNode = getParentNode(element);

    if (!parentNode) {
      return false;
    }

    return isFixed(parentNode);
  }

function isFixed ( element ) { var nodeName = element . nodeName ; if ( nodeName === 'BODY' || nodeName === 'HTML' ) { return false ; } if ( getStyleComputedProperty ( element , 'position' ) === 'fixed' ) { return true ; } var parentNode = getParentNode ( element ) ; if ( ! parentNode ) { return false ; } return isFixed ( parentNode ) ; }

Check if the given element is fixed or is inside a fixed parent
@method
@memberof Popper.Utils
@argument {Element} element
@argument {Element} customContainer
@returns {Boolean} answer to "isFixed?"


function getBoundaries(popper, reference, padding, boundariesElement) {
    var fixedPosition = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; // NOTE: 1 DOM access here

    var boundaries = {
      top: 0,
      left: 0
    };
    var offsetParent = fixedPosition ? getFixedPositionOffsetParent(popper) : findCommonOffsetParent(popper, reference); // Handle viewport case

    if (boundariesElement === 'viewport') {
      boundaries = getViewportOffsetRectRelativeToArtbitraryNode(offsetParent, fixedPosition);
    } else {
      // Handle other cases based on DOM element used as boundaries
      var boundariesNode = void 0;

      if (boundariesElement === 'scrollParent') {
        boundariesNode = getScrollParent(getParentNode(reference));

        if (boundariesNode.nodeName === 'BODY') {
          boundariesNode = popper.ownerDocument.documentElement;
        }
      } else if (boundariesElement === 'window') {
        boundariesNode = popper.ownerDocument.documentElement;
      } else {
        boundariesNode = boundariesElement;
      }

      var offsets = getOffsetRectRelativeToArbitraryNode(boundariesNode, offsetParent, fixedPosition); // In case of HTML, we need a different computation

      if (boundariesNode.nodeName === 'HTML' && !isFixed(offsetParent)) {
        var _getWindowSizes = getWindowSizes(popper.ownerDocument),
            height = _getWindowSizes.height,
            width = _getWindowSizes.width;

        boundaries.top += offsets.top - offsets.marginTop;
        boundaries.bottom = height + offsets.top;
        boundaries.left += offsets.left - offsets.marginLeft;
        boundaries.right = width + offsets.left;
      } else {
        // for all the other DOM elements, this one is good
        boundaries = offsets;
      }
    } // Add paddings

    padding = padding || 0;
    var isPaddingNumber = typeof padding === 'number';
    boundaries.left += isPaddingNumber ? padding : padding.left || 0;
    boundaries.top += isPaddingNumber ? padding : padding.top || 0;
    boundaries.right -= isPaddingNumber ? padding : padding.right || 0;
    boundaries.bottom -= isPaddingNumber ? padding : padding.bottom || 0;
    return boundaries;
  }

Computed the boundaries limits and return them
@method
@memberof Popper.Utils
@param {HTMLElement} popper
@param {HTMLElement} reference
@param {number} padding
@param {HTMLElement} boundariesElement - Element used to define the boundaries
@param {Boolean} fixedPosition - Is in fixed position mode
@returns {Object} Coordinates of the boundaries


function isModifierEnabled(modifiers, modifierName) {
    return modifiers.some(function (_ref) {
      var name = _ref.name,
          enabled = _ref.enabled;
      return enabled && name === modifierName;
    });
  }

function isModifierEnabled ( modifiers , modifierName ) { return modifiers . some ( function ( _ref ) { var name = _ref . name , enabled = _ref . enabled ; return enabled && name === modifierName ; } ) ; }

Helper used to know if the given modifier is enabled.
@method
@memberof Popper.Utils
@returns {Boolean}

Helper used to know if the given modifier is enabled .


function Popper(reference, popper) {
      var _this = this;

      var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
      classCallCheck(this, Popper);

      this.scheduleUpdate = function () {
        return requestAnimationFrame(_this.update);
      }; // make update() debounced, so that it only runs at most once-per-tick

      this.update = debounce(this.update.bind(this)); // with {} we create a new object with the options inside it

      this.options = _extends({}, Popper.Defaults, options); // init state

      this.state = {
        isDestroyed: false,
        isCreated: false,
        scrollParents: []
      }; // get reference and popper elements (allow jQuery wrappers)

      this.reference = reference && reference.jquery ? reference[0] : reference;
      this.popper = popper && popper.jquery ? popper[0] : popper; // Deep merge modifiers options

      this.options.modifiers = {};
      Object.keys(_extends({}, Popper.Defaults.modifiers, options.modifiers)).forEach(function (name) {
        _this.options.modifiers[name] = _extends({}, Popper.Defaults.modifiers[name] || {}, options.modifiers ? options.modifiers[name] : {});
      }); // Refactoring modifiers' list (Object => Array)

      this.modifiers = Object.keys(this.options.modifiers).map(function (name) {
        return _extends({
          name: name
        }, _this.options.modifiers[name]);
      }) // sort the modifiers by order
      .sort(function (a, b) {
        return a.order - b.order;
      }); // modifiers have the ability to execute arbitrary code when Popper.js get inited
      // such code is executed in the same order of its modifier
      // they could add new properties to their options configuration
      // BE AWARE: don't add options to `options.modifiers.name` but to `modifierOptions`!

      this.modifiers.forEach(function (modifierOptions) {
        if (modifierOptions.enabled && isFunction(modifierOptions.onLoad)) {
          modifierOptions.onLoad(_this.reference, _this.popper, _this.options, modifierOptions, _this.state);
        }
      }); // fire the first update to position the popper in the right place

      this.update();
      var eventsEnabled = this.options.eventsEnabled;

      if (eventsEnabled) {
        // setup event listeners, they will take care of update the position in specific situations
        this.enableEventListeners();
      }

      this.state.eventsEnabled = eventsEnabled;
    }

Creates a new Popper.js instance.
@class Popper
@param {HTMLElement|referenceObject} reference - The reference element used to position the popper
@param {HTMLElement} popper - The HTML element used as the popper
@param {Object} options - Your custom options to override the ones defined in [Defaults](#defaults)
@return {Object} instance - The generated Popper.js instance


function writeOutputHTML(dir, html) {
  const outputPath = pathJoin(dir, 'index.html');
  mkdirp.sync(path.dirname(outputPath));
  writeFileSync(outputPath, serialize(html));
}

function writeOutputHTML ( dir , html ) { const outputPath = pathJoin ( dir , 'index.html' ) ; mkdirp . sync ( path . dirname ( outputPath ) ) ; writeFileSync ( outputPath , serialize ( html ) ) ; }

Writes given HTML AST to output index.html

Writes given HTML AST to output index . html


function getOutputHTML(dir) {
  if (existsSync(`${dir}/index.html`)) {
    return readHTML(`${dir}/index.html`);
  }

  const outputHTML = readHTML(inputIndexHTMLPath);
  const scripts = queryAll(outputHTML, predicates.hasTagName('script'));
  const moduleScripts = scripts.filter(script => getAttribute(script, 'type') === 'module');
  moduleScripts.forEach(moduleScript => {
    remove(moduleScript);
  });

  writeOutputHTML(dir, outputHTML);
  return outputHTML;
}

function getOutputHTML ( dir ) { if ( existsSync ( ` ${ dir } ` ) ) { return readHTML ( ` ${ dir } ` ) ; } const outputHTML = readHTML ( inputIndexHTMLPath ) ; const scripts = queryAll ( outputHTML , predicates . hasTagName ( 'script' ) ) ; const moduleScripts = scripts . filter ( script => getAttribute ( script , 'type' ) === 'module' ) ; moduleScripts . forEach ( moduleScript => { remove ( moduleScript ) ; } ) ; writeOutputHTML ( dir , outputHTML ) ; return outputHTML ; }

Gets the output HTML as AST. Rollup might run with multiple outputs, but there should be only one index.html output. Therefore we check if it wasn't already created before. If there is no index.html yet, we copy over the input index.html with the original module scripts removed.

Gets the output HTML as AST . Rollup might run with multiple outputs but there should be only one index . html output . Therefore we check if it wasn t already created before . If there is no index . html yet we copy over the input index . html with the original module scripts removed .


function copyPolyfills(pluginConfig, outputConfig) {
  if (
    !pluginConfig.polyfillDynamicImports &&
    !pluginConfig.polyfillWebcomponents &&
    !pluginConfig.polyfillBabel
  ) {
    return;
  }
  const polyfillsDir = `${outputConfig.dir.replace('/legacy', '')}/polyfills`;
  mkdirp.sync(polyfillsDir);

  if (pluginConfig.polyfillDynamicImports) {
    copyFileSync(
      path.resolve(pathJoin(__dirname, '../../src/dynamic-import-polyfill.js')),
      `${polyfillsDir}/dynamic-import-polyfill.js`,
    );
  }

  if (pluginConfig.polyfillWebcomponents) {
    copyFileSync(
      require.resolve('@webcomponents/webcomponentsjs/webcomponents-bundle.js'),
      `${polyfillsDir}/webcomponent-polyfills.js`,
    );
  }

  if (pluginConfig.polyfillBabel) {
    copyFileSync(
      require.resolve('@babel/polyfill/browser.js'),
      `${polyfillsDir}/babel-polyfills.js`,
    );
  }
}

Copy required polyfills from node_modules to the build directory