UNPKG

react-native-webview-invoke

Version:

Invoke functions between React Native and WebView directly

398 lines (320 loc) 9.78 kB
'use strict'; function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function (obj) { return typeof obj; }; } else { _typeof = function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function createEventBus() { var listeners = { send: [], receive: [], ready: [] }; function addEventListener(name, cb) { if (name in listeners) { var fns = listeners[name]; if (fns.indexOf(cb) < 0) { fns.push(cb); } } } function removeEventListener(name, cb) { if (name in listeners) { var fns = listeners[name]; var idx = fns.indexOf(cb); if (idx >= 0) { fns.splice(idx, 1); } } } function emitEvent(name, event) { if (name in listeners) { listeners[name].forEach(function (fn) { return fn(event); }); } } return { addEventListener: addEventListener, removeEventListener: removeEventListener, emitEvent: emitEvent }; } var SYNC_COMMAND = 'RNWV:sync'; var STATUS_SUCCESS = 'success'; var STATUS_FAIL = 'fail'; var _count = 0; var Deferred = function Deferred() { var _this = this; _classCallCheck(this, Deferred); this.promise = new Promise(function (resolve, reject) { _this.resolve = resolve; _this.reject = reject; }); }; function getTransactionKey(data) { return "".concat(data.command, "(").concat(data.id, ")"); } function createPayload(command, data) { return { id: _count++, command: command, data: data, reply: false, status: STATUS_SUCCESS }; } function createMessager(sendHandler) { var needWait = []; var eventBus = createEventBus(); var transactions = {}; var callbacks = {}; // var fn = {}; // all other side functions function isConnect() { return !needWait; } function bind(name) { return function () { for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } return send(name, args); }; } function define(name, func) { callbacks[name] = function (args) { return func.apply(void 0, _toConsumableArray(args)); }; !needWait && sync(); return { define: define, bind: bind }; } /** sender parts */ function sender(data) { var force = data.command === SYNC_COMMAND; // force send the message when the message is the sync message if (!force && needWait) { needWait.push(data); } else { sendHandler(data); } eventBus.emitEvent('send', data); } function initialize() { if (needWait) { var waiting = needWait; needWait = null; waiting.forEach(function (payload) { sender(payload); }); eventBus.emitEvent('ready'); } } function send(command, data) { var payload = createPayload(command, data); var defer = new Deferred(); transactions[getTransactionKey(payload)] = defer; sender(payload); return defer.promise; } function reply(data, result, status) { data.reply = true; data.data = result; data.status = status; sender(data); } /** listener parts */ function listener(data) { if (data.reply) { var key = getTransactionKey(data); if (transactions[key]) { if (data.status === STATUS_FAIL) { transactions[key].reject(data.data); } else { transactions[key].resolve(data.data); } } } else { if (callbacks[data.command]) { var result = callbacks[data.command](data.data); if (result && result.then) { result.then(function (d) { reply(data, d, STATUS_SUCCESS); })["catch"](function (e) { reply(data, e, STATUS_FAIL); }); } else { reply(data, result, STATUS_SUCCESS); } } else { reply(data, "function ".concat(data.command, " is not defined"), STATUS_FAIL); } } eventBus.emitEvent('receive', data); } var __sync = bind(SYNC_COMMAND); function _sync() { var defines = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; defines.filter(function (d) { return !fn[d]; }).map(function (d) { fn[d] = bind(d); }); initialize(); return Object.keys(callbacks); } define(SYNC_COMMAND, _sync); function sync() { __sync(Object.keys(callbacks)).then(_sync); } return { bind: bind, define: define, listener: listener, ready: sync, fn: fn, addEventListener: eventBus.addEventListener, removeEventListener: eventBus.removeEventListener, isConnect: isConnect }; } var _postMessage = null; var isBrowser = typeof window !== 'undefined'; var _createMessager = createMessager(function (data) { return isBrowser && _postMessage && _postMessage(JSON.stringify(data)); }), bind = _createMessager.bind, define = _createMessager.define, listener = _createMessager.listener, ready = _createMessager.ready, fn = _createMessager.fn, addEventListener = _createMessager.addEventListener, removeEventListener = _createMessager.removeEventListener, isConnect = _createMessager.isConnect; /*Handle only the messages which are of structure rn-webview-invoke. Ignore messages which came from other sources. Ex: In case of vimeo player, vimeo player will throw some self messages. These messages need to be ignored*/ var handleMessage = function handleMessage(message) { var jsonMessage = undefined; if (_typeof(message) === 'object') { jsonMessage = message; } else if (typeof message === 'string') { try { jsonMessage = JSON.parse(message); } catch (error) {} } if (jsonMessage && (jsonMessage.command || jsonMessage.reply)) { listener(jsonMessage); } }; if (isBrowser) { // react-native var originalPostMessage = window.originalPostMessage; if (originalPostMessage) { _postMessage = function _postMessage() { var _window; return (_window = window).postMessage.apply(_window, arguments); }; ready(); } else { var descriptor = { get: function get() { return originalPostMessage; }, set: function set(value) { originalPostMessage = value; if (originalPostMessage) { _postMessage = function _postMessage() { var _window2; return (_window2 = window).postMessage.apply(_window2, arguments); }; setTimeout(ready, 50); } } }; Object.defineProperty(window, 'originalPostMessage', descriptor); } // react-native-webview var ReactNativeWebView = window.ReactNativeWebView; if (ReactNativeWebView) { _postMessage = function _postMessage() { var _window$ReactNativeWe; return (_window$ReactNativeWe = window.ReactNativeWebView).postMessage.apply(_window$ReactNativeWe, arguments); }; ready(); } else { var _descriptor = { get: function get() { return ReactNativeWebView; }, set: function set(value) { ReactNativeWebView = value; if (ReactNativeWebView) { _postMessage = function _postMessage() { var _window$ReactNativeWe2; return (_window$ReactNativeWe2 = window.ReactNativeWebView).postMessage.apply(_window$ReactNativeWe2, arguments); }; setTimeout(ready, 50); } } }; Object.defineProperty(window, 'ReactNativeWebView', _descriptor); } // onMessage react native window.document.addEventListener('message', function (e) { return originalPostMessage && handleMessage(e.data); }); // onMessage react-native-webview window.addEventListener('message', function (e) { return ReactNativeWebView && handleMessage(e.data); }); // onMessage react-native-webview with android window.document.addEventListener('message', function (e) { return ReactNativeWebView && handleMessage(e.data); }); } var browser = { bind: bind, define: define, fn: fn, addEventListener: addEventListener, removeEventListener: removeEventListener, isConnect: isConnect }; module.exports = browser;