"use strict"; exports.__esModule = true; exports["default"] = makeAsyncScript; var _react = require("react"); var _propTypes = _interopRequireDefault(require("prop-types")); var _hoistNonReactStatics = _interopRequireDefault(require("hoist-non-react-statics")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } var SCRIPT_MAP = {}; // A counter used to generate a unique id for each component that uses the function var idCount = 0; function makeAsyncScript(getScriptURL, options) { options = options || {}; return function wrapWithAsyncScript(WrappedComponent) { var wrappedComponentName = WrappedComponent.displayName || WrappedComponent.name || "Component"; var AsyncScriptLoader = /*#__PURE__*/ function (_Component) { _inheritsLoose(AsyncScriptLoader, _Component); function AsyncScriptLoader(props, context) { var _this; _this = _Component.call(this, props, context) || this; _this.state = {}; _this.__scriptURL = ""; return _this; } var _proto = AsyncScriptLoader.prototype; _proto.asyncScriptLoaderGetScriptLoaderID = function asyncScriptLoaderGetScriptLoaderID() { if (!this.__scriptLoaderID) { this.__scriptLoaderID = "async-script-loader-" + idCount++; } return this.__scriptLoaderID; }; _proto.setupScriptURL = function setupScriptURL() { this.__scriptURL = typeof getScriptURL === "function" ? getScriptURL() : getScriptURL; return this.__scriptURL; }; _proto.asyncScriptLoaderHandleLoad = function asyncScriptLoaderHandleLoad(state) { var _this2 = this; // use reacts setState callback to fire props.asyncScriptOnLoad with new state/entry this.setState(state, function () { return _this2.props.asyncScriptOnLoad && _this2.props.asyncScriptOnLoad(_this2.state); }); }; _proto.asyncScriptLoaderTriggerOnScriptLoaded = function asyncScriptLoaderTriggerOnScriptLoaded() { var mapEntry = SCRIPT_MAP[this.__scriptURL]; if (!mapEntry || !mapEntry.loaded) { throw new Error("Script is not loaded."); } for (var obsKey in mapEntry.observers) { mapEntry.observers[obsKey](mapEntry); } delete window[options.callbackName]; }; _proto.componentDidMount = function componentDidMount() { var _this3 = this; var scriptURL = this.setupScriptURL(); var key = this.asyncScriptLoaderGetScriptLoaderID(); var _options = options, globalName = _options.globalName, callbackName = _options.callbackName, scriptId = _options.scriptId; // check if global object already attached to window if (globalName && typeof window[globalName] !== "undefined") { SCRIPT_MAP[scriptURL] = { loaded: true, observers: {} }; } // check if script loading already if (SCRIPT_MAP[scriptURL]) { var entry = SCRIPT_MAP[scriptURL]; // if loaded or errored then "finish" if (entry && (entry.loaded || entry.errored)) { this.asyncScriptLoaderHandleLoad(entry); return; } // if still loading then callback to observer queue entry.observers[key] = function (entry) { return _this3.asyncScriptLoaderHandleLoad(entry); }; return; } /* * hasn't started loading * start the "magic" * setup script to load and observers */ var observers = {}; observers[key] = function (entry) { return _this3.asyncScriptLoaderHandleLoad(entry); }; SCRIPT_MAP[scriptURL] = { loaded: false, observers: observers }; var script = document.createElement("script"); script.src = scriptURL; script.async = true; for (var attribute in options.attributes) { script.setAttribute(attribute, options.attributes[attribute]); } if (scriptId) { script.id = scriptId; } var callObserverFuncAndRemoveObserver = function callObserverFuncAndRemoveObserver(func) { if (SCRIPT_MAP[scriptURL]) { var mapEntry = SCRIPT_MAP[scriptURL]; var observersMap = mapEntry.observers; for (var obsKey in observersMap) { if (func(observersMap[obsKey])) { delete observersMap[obsKey]; } } } }; if (callbackName && typeof window !== "undefined") { window[callbackName] = function () { return _this3.asyncScriptLoaderTriggerOnScriptLoaded(); }; } script.onload = function () { var mapEntry = SCRIPT_MAP[scriptURL]; if (mapEntry) { mapEntry.loaded = true; callObserverFuncAndRemoveObserver(function (observer) { if (callbackName) { return false; } observer(mapEntry); return true; }); } }; script.onerror = function () { var mapEntry = SCRIPT_MAP[scriptURL]; if (mapEntry) { mapEntry.errored = true; callObserverFuncAndRemoveObserver(function (observer) { observer(mapEntry); return true; }); } }; document.body.appendChild(script); }; _proto.componentWillUnmount = function componentWillUnmount() { // Remove tag script var scriptURL = this.__scriptURL; if (options.removeOnUnmount === true) { var allScripts = document.getElementsByTagName("script"); for (var i = 0; i < allScripts.length; i += 1) { if (allScripts[i].src.indexOf(scriptURL) > -1) { if (allScripts[i].parentNode) { allScripts[i].parentNode.removeChild(allScripts[i]); } } } } // Clean the observer entry var mapEntry = SCRIPT_MAP[scriptURL]; if (mapEntry) { delete mapEntry.observers[this.asyncScriptLoaderGetScriptLoaderID()]; if (options.removeOnUnmount === true) { delete SCRIPT_MAP[scriptURL]; } } }; _proto.render = function render() { var globalName = options.globalName; // remove asyncScriptOnLoad from childProps var _this$props = this.props, asyncScriptOnLoad = _this$props.asyncScriptOnLoad, forwardedRef = _this$props.forwardedRef, childProps = _objectWithoutPropertiesLoose(_this$props, ["asyncScriptOnLoad", "forwardedRef"]); // eslint-disable-line no-unused-vars if (globalName && typeof window !== "undefined") { childProps[globalName] = typeof window[globalName] !== "undefined" ? window[globalName] : undefined; } childProps.ref = forwardedRef; return (0, _react.createElement)(WrappedComponent, childProps); }; return AsyncScriptLoader; }(_react.Component); // Note the second param "ref" provided by React.forwardRef. // We can pass it along to AsyncScriptLoader as a regular prop, e.g. "forwardedRef" // And it can then be attached to the Component. var ForwardedComponent = (0, _react.forwardRef)(function (props, ref) { return (0, _react.createElement)(AsyncScriptLoader, _extends({}, props, { forwardedRef: ref })); }); ForwardedComponent.displayName = "AsyncScriptLoader(" + wrappedComponentName + ")"; ForwardedComponent.propTypes = { asyncScriptOnLoad: _propTypes["default"].func }; return (0, _hoistNonReactStatics["default"])(ForwardedComponent, WrappedComponent); }; }