paypal-checkout
Version:
PayPal Checkout components, for integrating checkout products.
382 lines (281 loc) • 9.61 kB
JavaScript
/* @flow */
import { info } from 'beaver-logger/client';
import { ZalgoPromise } from 'zalgo-promise/src';
import type { CrossDomainWindowType } from 'cross-domain-utils/src';
import { isDevice } from 'belter/src';
import { LANG_TO_DEFAULT_COUNTRY, LOCALE } from '../constants';
import type { LocaleType } from '../types';
import { config } from '../config';
import { memoize } from './util';
function isDocumentReady() : boolean {
// eslint-disable-next-line compat/compat
return Boolean(document.body) && document.readyState === 'complete';
}
export const documentReady : ZalgoPromise<void> = new ZalgoPromise(resolve => {
if (isDocumentReady()) {
return resolve();
}
const interval = setInterval(() => {
if (isDocumentReady()) {
clearInterval(interval);
return resolve();
}
}, 10);
});
export const documentBody : ZalgoPromise<HTMLElement> = documentReady.then(() => {
if (document.body) {
return document.body;
}
throw new Error('Document ready but document.body not present');
});
export function loadScript(src : string, timeout : number = 0, attrs : Object = {}) : ZalgoPromise<void> {
return new ZalgoPromise((resolve, reject) => {
const script = document.createElement('script');
script.addEventListener('load', () => {
resolve();
});
// For Internet explorer 8 support
script.onreadystatechange = function scriptOnReadyStateChange() {
if (this.readyState === 'complete' || this.readyState === 'loaded') {
resolve();
}
};
const scriptLoadError = new Error('script_loading_error');
script.addEventListener('error', () => {
return reject(scriptLoadError);
});
if (timeout) {
setTimeout(() => {
return reject(new Error('script_loading_timed_out'));
}, timeout);
}
for (const attr of Object.keys(attrs)) {
script.setAttribute(attr, attrs[attr]);
}
script.setAttribute('src', src);
const head = document.getElementsByTagName('head')[0];
head.appendChild(script);
});
}
export function isNodeList(nodes : mixed) : boolean {
const result = Object.prototype.toString.call(nodes);
if (result === '[object HTMLCollection]' || result === '[object NodeList]') {
return true;
}
return false;
}
export function isElement(item : mixed) : boolean {
return item instanceof HTMLElement;
}
export function getElement(item : mixed) : ?HTMLElement {
if (!item) {
return;
}
if (item instanceof HTMLElement) {
return item;
}
if (typeof item === 'string') {
if (document.querySelector) {
const result = document.querySelector(item);
if (result) {
return result;
}
}
return document.getElementById(item);
}
}
export function getElements(collection : $ReadOnlyArray<mixed> | NodeList<HTMLElement> | HTMLCollection<HTMLElement> | HTMLElement | string) : $ReadOnlyArray<HTMLElement> {
if (!collection) {
return [];
}
if (collection instanceof HTMLElement || typeof collection === 'string') {
const element = getElement(collection);
if (element) {
return [ element ];
}
return [];
}
if (Array.isArray(collection) || collection instanceof NodeList || collection instanceof HTMLCollection) {
const result = [];
for (let i = 0; i < collection.length; i++) {
const el = getElement(collection[i]);
if (el) {
result.push(el);
}
}
return result;
}
return [];
}
export function onDocumentReady(method : () => void) : ZalgoPromise<void> {
return documentReady.then(method);
}
export const parseQuery = memoize((queryString : string) : Object => {
const params = {};
if (!queryString) {
return params;
}
if (queryString.indexOf('=') === -1) {
return params;
}
for (let pair of queryString.split('&')) {
pair = pair.split('=');
if (pair[0] && pair[1]) {
params[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
}
}
return params;
});
export function getQueryParam(name : string) : string {
return parseQuery(window.location.search.slice(1))[name];
}
export function urlWillRedirectPage(url : string) : boolean {
if (url.indexOf('#') === -1) {
return true;
}
if (url.indexOf('#') === 0) {
return false;
}
if (url.split('#')[0] === window.location.href.split('#')[0]) {
return false;
}
return true;
}
export function extendUrl(url : string, params : { [key : string] : string } = {}) : string {
const hasHash = url.indexOf('#') > 0;
let [ serverUrl, hash ] = url.split('#');
if (hash && !serverUrl) {
[ serverUrl, hash ] = [ `#${ hash }`, '' ];
}
const [ originalUrl, originalQueryString ] = serverUrl.split('?');
if (originalQueryString) {
const originalQuery = parseQuery(originalQueryString);
for (const key in originalQuery) {
if (!params.hasOwnProperty(key)) {
params[key] = originalQuery[key];
}
}
}
const newQueryString = Object.keys(params).filter(key => key && params[key]).sort().map(key => {
return `${ encodeURIComponent(key) }=${ encodeURIComponent(params[key]) }`;
}).join('&');
let newUrl = originalUrl;
if (newQueryString) {
newUrl = `${ newUrl }?${ newQueryString }`;
}
if (hasHash) {
newUrl = `${ newUrl }#${ hash || '' }`;
}
return newUrl;
}
export function redirect(win : CrossDomainWindowType = window, url : string) : ZalgoPromise<void> {
return new ZalgoPromise(resolve => {
info(`redirect`, { url });
setTimeout(() => {
win.location = url;
if (!urlWillRedirectPage(url)) {
resolve();
}
}, 1);
});
}
export function hasMetaViewPort() : boolean {
const meta = document.querySelector('meta[name=viewport]');
if (isDevice() && window.screen.width < 660 && !meta) {
return false;
}
return true;
}
export function getBrowserLocales() : $ReadOnlyArray<string> {
const nav = window.navigator;
const locales = nav.languages
? Array.prototype.slice.apply(nav.languages)
: [];
if (nav.language) {
locales.push(nav.language);
}
if (nav.userLanguage) {
locales.push(nav.userLanguage);
}
return locales;
}
export function normalizeLocale(locale : string) : ?LocaleType {
if (locale && locale.match(/^[a-z]{2}[-_][A-Z]{2}$/)) {
const [ lang, country ] = locale.split(/[-_]/);
if (LOCALE[country] && LOCALE[country].indexOf(lang) !== -1) {
return { country, lang };
}
}
}
export function normalizeLang(lang : string) : ?LocaleType {
if (lang && lang.match(/^[a-z]{2}$/)) {
if (LANG_TO_DEFAULT_COUNTRY[lang]) {
return { country: LANG_TO_DEFAULT_COUNTRY[lang], lang };
}
}
}
export const getBrowserLocale = memoize(() : LocaleType => {
const locales = getBrowserLocales();
for (const locale of locales) {
let loc = normalizeLocale(locale);
if (loc) {
info('better_browser_locale_full');
return loc;
}
loc = normalizeLang(locale);
if (loc) {
info('better_browser_locale_lang');
return loc;
}
}
return config.defaultLocale;
});
export function isElementVisible(el : HTMLElement) : boolean {
return Boolean(el.offsetWidth || el.offsetHeight || el.getClientRects().length);
}
export const enablePerformance = memoize(() : boolean => {
return Boolean(
window.performance &&
performance.now &&
performance.timing &&
performance.timing.connectEnd &&
performance.timing.navigationStart &&
(Math.abs(performance.now() - Date.now()) > 1000) &&
(performance.now() - (performance.timing.connectEnd - performance.timing.navigationStart)) > 0
);
});
export function getPageRenderTime() : ZalgoPromise<?number> {
return documentReady.then(() => {
if (!enablePerformance()) {
return;
}
const timing = window.performance.timing;
if (timing.connectEnd && timing.domInteractive) {
return timing.domInteractive - timing.connectEnd;
}
});
}
export function getResourceLoadTime(url : string) : ?number {
if (!enablePerformance()) {
return;
}
if (!window.performance || typeof window.performance.getEntries !== 'function') {
return;
}
const entries = window.performance.getEntries(); // eslint-disable-line compat/compat
for (let i = 0; i < entries.length; i++) {
const entry = entries[i];
if (entry && entry.name === url && entry.duration && entry.duration >= 0 && entry.duration <= 60000) {
return Math.floor(entry.duration);
}
}
}
export function htmlEncode(html : string = '') : string {
return html.toString()
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/\//g, '/');
}