/* HTML escaping and shallow-equals implementations are the same as React's (on purpose.) Therefore, it has the following Copyright and Licensing: Copyright 2013-2014, Facebook, Inc. All rights reserved. This source code is licensed under the BSD-style license found in the LICENSE file in the root directory of React's source tree. */ import invariant from 'invariant'; import {intlConfigPropTypes} from './types'; const intlConfigPropNames = Object.keys(intlConfigPropTypes); const ESCAPED_CHARS = { '&': '&', '>': '>', '<': '<', '"': '"', "'": ''', }; const UNSAFE_CHARS_REGEX = /[&><"']/g; export function escape(str) { return ('' + str).replace(UNSAFE_CHARS_REGEX, match => ESCAPED_CHARS[match]); } export function filterProps(props, whitelist, defaults = {}) { return whitelist.reduce((filtered, name) => { if (props.hasOwnProperty(name)) { filtered[name] = props[name]; } else if (defaults.hasOwnProperty(name)) { filtered[name] = defaults[name]; } return filtered; }, {}); } export function invariantIntlContext({intl} = {}) { invariant( intl, '[React Intl] Could not find required `intl` object. ' + ' needs to exist in the component ancestry.' ); } export function shallowEquals(objA, objB) { if (objA === objB) { return true; } if ( typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null ) { return false; } let keysA = Object.keys(objA); let keysB = Object.keys(objB); if (keysA.length !== keysB.length) { return false; } // Test for A's keys different from B. let bHasOwnProperty = Object.prototype.hasOwnProperty.bind(objB); for (let i = 0; i < keysA.length; i++) { if (!bHasOwnProperty(keysA[i]) || objA[keysA[i]] !== objB[keysA[i]]) { return false; } } return true; } export function shouldIntlComponentUpdate( {props, state, context = {}}, nextProps, nextState, nextContext = {} ) { const {intl = {}} = context; const {intl: nextIntl = {}} = nextContext; return ( !shallowEquals(nextProps, props) || !shallowEquals(nextState, state) || !( nextIntl === intl || shallowEquals( filterProps(nextIntl, intlConfigPropNames), filterProps(intl, intlConfigPropNames) ) ) ); } export function createError(message, exception) { const eMsg = exception ? `\n${exception}` : ''; return `[React Intl] ${message}${eMsg}`; } export function defaultErrorHandler(error) { if (process.env.NODE_ENV !== 'production') { console.error(error); } }