123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236 |
- 'use strict';
-
- import { Dimensions } from 'react-native';
-
- const domMutationObserveScript = `
- var MutationObserver =
- window.MutationObserver || window.WebKitMutationObserver;
- var observer = new MutationObserver(updateSize);
- observer.observe(document, {
- subtree: true,
- attributes: true
- });
- `;
-
- const updateSizeWithMessage = (element, scalesPageToFit) =>
- `
- var scaling = false;
- var zoomedin = false;
- var lastHeight = 0;
- var heightTheSameTimes = 0;
- var maxHeightTheSameTimes = 5;
- var forceRefreshDelay = 1000;
- var forceRefreshTimeout;
- var checkPostMessageTimeout;
-
- function updateSize(event) {
- if (zoomedin || scaling) {
- return;
- }
- if (
- !window.hasOwnProperty('ReactNativeWebView') ||
- !window.ReactNativeWebView.hasOwnProperty('postMessage')
- ) {
- checkPostMessageTimeout = setTimeout(updateSize, 200);
- return;
- }
- clearTimeout(checkPostMessageTimeout);
- height = ${element}.offsetHeight || document.documentElement.offsetHeight;
- width = ${element}.offsetWidth || document.documentElement.offsetWidth;
- var scale = ${scalesPageToFit ? 'screen.width / window.innerWidth' : '1'};
- window.ReactNativeWebView.postMessage(JSON.stringify({ width: Math.min(width, screen.width), height: height * scale }));
-
- // Make additional height checks (required to fix issues wit twitter embeds)
- clearTimeout(forceRefreshTimeout);
- if (lastHeight !== height) {
- heightTheSameTimes = 1;
- } else {
- heightTheSameTimes++;
- }
-
- lastHeight = height;
-
- if (heightTheSameTimes <= maxHeightTheSameTimes) {
- forceRefreshTimeout = setTimeout(
- updateSize,
- heightTheSameTimes * forceRefreshDelay
- );
- }
- }
- `;
-
- const setViewportContent = content => {
- if (!content) {
- return '';
- }
- return `
- var meta = document.createElement("meta");
- meta.setAttribute("name", "viewport");
- meta.setAttribute("content", "${content}");
- document.getElementsByTagName("head")[0].appendChild(meta);
- `;
- };
-
- const detectZoomChanged = `
- var latestTapStamp = 0;
- var lastScale = 1.0;
- var doubleTapDelay = 400;
- function detectZoomChanged() {
- var tempZoomedin = (screen.width / window.innerWidth) > 1;
- tempZoomedin !== zoomedin && window.ReactNativeWebView.postMessage(JSON.stringify({ zoomedin: tempZoomedin }));
- zoomedin = tempZoomedin;
- }
- window.addEventListener('ontouchstart', event => {
- if (event.touches.length === 2) {
- scaling = true;
- }
- })
- window.addEventListener('touchend', event => {
- if(scaling) {
- scaleing = false;
- }
- var tempScale = event.scale;
- tempScale !== lastScale && detectZoomChanged();
- lastScale = tempScale;
- var timeSince = new Date().getTime() - latestTapStamp;
- // double tap
- if(timeSince < 600 && timeSince > 0) {
- zoomedinTimeOut = setTimeout(() => {
- clearTimeout(zoomedinTimeOut);
- detectZoomChanged();
- }, doubleTapDelay);
- }
- latestTapStamp = new Date().getTime();
- });
- `;
-
- const getBaseScript = ({ style, viewportContent, scalesPageToFit, scrollEnabledWithZoomedin }) =>
- `
- ;
- if (!document.getElementById("rnahw-wrapper")) {
- var wrapper = document.createElement('div');
- wrapper.id = 'rnahw-wrapper';
- while (document.body.firstChild instanceof Node) {
- wrapper.appendChild(document.body.firstChild);
- }
- document.body.appendChild(wrapper);
- }
- ${updateSizeWithMessage('wrapper', scalesPageToFit)}
- window.addEventListener('load', updateSize);
- window.addEventListener('resize', updateSize);
- ${domMutationObserveScript}
- ${setViewportContent(viewportContent)}
- ${scrollEnabledWithZoomedin ? detectZoomChanged : ''}
- updateSize();
- `;
-
- const appendFilesToHead = ({ files, script }) =>
- files.reduceRight((combinedScript, file) => {
- const { rel, type, href } = file;
- return `
- var link = document.createElement('link');
- link.rel = '${rel}';
- link.type = '${type}';
- link.href = '${href}';
- document.head.appendChild(link);
- ${combinedScript}
- `;
- }, script);
-
- const screenWidth = Dimensions.get('window').width;
-
- const bodyStyle = `
- body {
- margin: 0;
- padding: 0;
- }
- `;
-
- const appendStylesToHead = ({ style, script }) => {
- const currentStyles = style ? bodyStyle + style : bodyStyle;
- // Escape any single quotes or newlines in the CSS with .replace()
- const escaped = currentStyles.replace(/\'/g, "\\'").replace(/\n/g, '\\n');
- return `
- var styleElement = document.createElement('style');
- styleElement.innerHTML = '${escaped}';
- document.head.appendChild(styleElement);
- ${script}
- `;
- };
-
- const getInjectedSource = ({ html, script }) => `
- ${html}
- <script>
- // prevents code colissions with global scope
- (function() {
- ${script}
- })();
- </script>
- `;
-
- const getScript = ({
- files,
- customStyle,
- customScript,
- style,
- viewportContent,
- scalesPageToFit,
- scrollEnabledWithZoomedin
- }) => {
- let script = getBaseScript({ style, viewportContent, scalesPageToFit, scrollEnabledWithZoomedin });
- script = files && files.length > 0 ? appendFilesToHead({ files, script }) : script;
- script = appendStylesToHead({ style: customStyle, script });
- customScript && (script = customScript + script);
- return script;
- };
-
- export const getWidth = style => {
- return style && style.width ? style.width : screenWidth;
- };
-
- export const isSizeChanged = ({ height, previousHeight, width, previousWidth }) => {
- if (!height || !width) {
- return;
- }
- return height !== previousHeight || width !== previousWidth;
- };
-
- export const reduceData = props => {
- const { source } = props;
- const script = getScript(props);
- const { html, baseUrl } = source;
- if (html) {
- return {
- currentSource: { baseUrl, html: getInjectedSource({ html, script }) }
- };
- } else {
- return {
- currentSource: source,
- script
- };
- }
- };
-
- export const shouldUpdate = ({ prevProps, nextProps }) => {
- if (!(prevProps && nextProps)) {
- return true;
- }
- for (const prop in nextProps) {
- if (nextProps[prop] !== prevProps[prop]) {
- if (typeof nextProps[prop] === 'object' && typeof prevProps[prop] === 'object') {
- if (
- shouldUpdate({
- prevProps: prevProps[prop],
- nextProps: nextProps[prop]
- })
- ) {
- return true;
- }
- } else {
- return true;
- }
- }
- }
- return false;
- };
|