UNPKG

flopflip

Version:

A feature toggle wrapper to use LaunchDarkly with React Redux

1,860 lines (1,562 loc) 86 kB
import { bindActionCreators, combineReducers } from 'redux'; import React, { Component } from 'react'; import propTypes from 'prop-types'; import { connect } from 'react-redux'; var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; function createCommonjsModule(fn, module) { return module = { exports: {} }, fn(module, module.exports), module.exports; } var base64 = createCommonjsModule(function (module, exports) { (function () { var object = exports; // #8: web workers var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; function InvalidCharacterError(message) { this.message = message; } InvalidCharacterError.prototype = new Error; InvalidCharacterError.prototype.name = 'InvalidCharacterError'; // encoder // [https://gist.github.com/999166] by [https://github.com/nignag] object.btoa || ( object.btoa = function (input) { var str = String(input); for ( // initialize result and counter var block, charCode, idx = 0, map = chars, output = ''; // if the next str index does not exist: // change the mapping table to "=" // check if d has no fractional digits str.charAt(idx | 0) || (map = '=', idx % 1); // "8 - idx % 1 * 8" generates the sequence 2, 4, 6, 8 output += map.charAt(63 & block >> 8 - idx % 1 * 8) ) { charCode = str.charCodeAt(idx += 3/4); if (charCode > 0xFF) { throw new InvalidCharacterError("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range."); } block = block << 8 | charCode; } return output; }); // decoder // [https://gist.github.com/1020396] by [https://github.com/atk] object.atob || ( object.atob = function (input) { var str = String(input).replace(/=+$/, ''); if (str.length % 4 == 1) { throw new InvalidCharacterError("'atob' failed: The string to be decoded is not correctly encoded."); } for ( // initialize result and counters var bc = 0, bs, buffer, idx = 0, output = ''; // get next character buffer = str.charAt(idx++); // character found in table? initialize bit storage and add its ascii value; ~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer, // and if not first of each 4 characters, // convert the first 8 bits to one ascii character bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0 ) { // try to find character in table (0-63, not found => -1) buffer = chars.indexOf(buffer); } return output; }); }()); }); // See http://ecmanaut.blogspot.com/2006/07/encoding-decoding-utf8-in-javascript.html function btoa(s) { return base64.btoa(unescape(encodeURIComponent(s))); } function base64URLEncode(s) { return btoa(s) .replace(/=/g, '') .replace(/\+/g, '-') .replace(/\//g, '_'); } function clone(obj) { return JSON.parse(JSON.stringify(obj)); } function modifications(oldObj, newObj) { var mods = {}; if (!oldObj || !newObj) { return {}; } for (var prop in oldObj) { if (oldObj.hasOwnProperty(prop)) { if (newObj[prop] !== oldObj[prop]) { mods[prop] = {previous: oldObj[prop], current: newObj[prop]}; } } } return mods; } var utils = { btoa: btoa, base64URLEncode: base64URLEncode, clone: clone, modifications: modifications }; function EventProcessor(eventsUrl) { var processor = {}; var queue = []; var initialFlush = true; processor.enqueue = function(event) { queue.push(event); }; processor.flush = function(user, sync) { var maxLength = 2000 - eventsUrl.length; var data = []; if (!user) { if (initialFlush) { console && console.warn && console.warn('Be sure to call `identify` in the LaunchDarkly client: http://docs.launchdarkly.com/docs/running-an-ab-test#include-the-client-side-snippet'); } return false; } initialFlush = false; while (maxLength > 0 && queue.length > 0) { var event = queue.pop(); event.user = user; maxLength = maxLength - utils.base64URLEncode(JSON.stringify(event)).length; // If we are over the max size, put this one back on the queue // to try in the next round, unless this event alone is larger // than the limit, in which case, screw it, and try it anyway. if (maxLength < 0 && data.length > 0) { queue.push(event); } else { data.push(event); } } if (data.length > 0) { var src = eventsUrl + '?d=' + utils.base64URLEncode(JSON.stringify(data)); //Detect browser support for CORS if ('withCredentials' in new XMLHttpRequest()) { /* supports cross-domain requests */ var xhr = new XMLHttpRequest(); xhr.open('GET', src, !sync); xhr.send(); } else { var img = new Image(); img.src = src; } } // if the queue is not empty, call settimeout to flush it again // with a 0 timeout (stack-less recursion) // Or, just recursively call flush_queue with the remaining elements // if we're doing this on unload if (queue.length > 0) { if (sync) { processor.flush(user, sync); } else { setTimeout(function() { processor.flush(user); }, 0); } } return false; }; return processor; } var EventProcessor_1 = EventProcessor; function EventEmitter() { var emitter = {}; var events = {}; emitter.on = function(event, handler, context) { events[event] = events[event] || []; events[event] = events[event].concat({handler: handler, context: context}); }; emitter.off = function(event, handler, context) { if (!events[event]) { return; } for (var i = 0; i < events[event].length ; i++) { if (events[event][i].handler === handler && events[event][i].context === context) { events[event] = events[event].slice(0, i).concat(events[event].slice(i + 1)); } } }; emitter.emit = function(event) { if (!events[event]) { return; } for (var i = 0; i < events[event].length; i++) { events[event][i].handler.apply(events[event][i].context, Array.prototype.slice.call(arguments, 1)); } }; return emitter; } var EventEmitter_1 = EventEmitter; var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g; var index$1 = function (str) { if (typeof str !== 'string') { throw new TypeError('Expected a string'); } return str.replace(matchOperatorsRe, '\\$&'); }; function doesUrlMatch(matcher, href, search, hash) { var canonicalUrl = href.replace(search, '').replace(hash, ''); var regex; var testUrl; switch (matcher.kind) { case 'exact': testUrl = href; regex = new RegExp('^' + index$1(matcher.url) + '/?$'); break; case 'canonical': testUrl = canonicalUrl; regex = new RegExp('^' + index$1(matcher.url) + '/?$'); break; case 'substring': testUrl = canonicalUrl; regex = new RegExp('.*' + index$1(matcher.substring) + '.*$'); break; case 'regex': testUrl = canonicalUrl; regex = new RegExp(matcher.pattern); break; default: return false; } return regex.test(testUrl); } function findGoalsForClick(event, clickGoals) { var matches = []; for (var i = 0; i < clickGoals.length; i++) { var target = event.target; var goal = clickGoals[i]; var selector = goal.selector; var elements = document.querySelectorAll(selector); while (target && elements.length > 0) { for (var j = 0; j < elements.length; j++) { if (target === elements[j]) matches.push(goal); } target = target.parentNode; } } return matches; } function GoalTracker(goals, onEvent) { var tracker = {}; var goals = goals; var listenerFn = null; var clickGoals = []; for (var i = 0; i < goals.length; i++) { var goal = goals[i]; var urls = goal.urls || []; for (var j = 0; j < urls.length; j++) { if (doesUrlMatch(urls[j], location.href, location.search, location.hash)) { if (goal.kind === 'pageview') { onEvent('pageview', goal); } else { clickGoals.push(goal); onEvent('click_pageview', goal); } break; } } } if (clickGoals.length > 0) { listenerFn = function(event) { var goals = findGoalsForClick(event, clickGoals); for (var i = 0; i < goals.length; i++) { onEvent('click', goals[i]); } }; document.addEventListener('click', listenerFn); } tracker.dispose = function() { document.removeEventListener('click', listenerFn); }; return tracker; } var GoalTracker_1 = GoalTracker; function Stream(url, environment) { var stream = {}; var url = url + '/ping/' + environment; var es = null; stream.connect = function(onPing) { if (typeof EventSource !== 'undefined') { es = new window.EventSource(url); es.addEventListener('ping', onPing); } }; stream.disconnect = function() { es && es.close(); }; stream.isConnected = function() { return es && (es.readyState === EventSource.OPEN || es.readyState === EventSource.CONNECTING); }; return stream; } var Stream_1 = Stream; var json = 'application/json'; function fetchJSON(endpoint, callback) { var xhr = new XMLHttpRequest(); xhr.addEventListener('load', function() { if (xhr.status === 200 && xhr.getResponseHeader('Content-type') === json) { callback(null, JSON.parse(xhr.responseText)); } else { callback(xhr.statusText); } }); xhr.addEventListener('error', function() { callback(xhr.statusText); }); xhr.open('GET', endpoint); xhr.send(); return xhr; } var flagSettingsRequest; var lastFlagSettingsCallback; function Requestor(baseUrl, environment) { var requestor = {}; requestor.fetchFlagSettings = function(user, hash, callback) { var data = utils.base64URLEncode(JSON.stringify(user)); var endpoint = [baseUrl, '/sdk/eval/', environment, '/users/', data, hash ? '?h=' + hash : ''].join(''); var cb; var wrappedCallback = (function(currentCallback) { return function() { currentCallback.apply(null, arguments); flagSettingsRequest = null; lastFlagSettingsCallback = null; }; })(callback); if (flagSettingsRequest) { flagSettingsRequest.abort(); cb = (function(prevCallback) { return function() { prevCallback && prevCallback.apply(null, arguments); wrappedCallback.apply(null, arguments); }; })(lastFlagSettingsCallback); } else { cb = wrappedCallback; } lastFlagSettingsCallback = cb; flagSettingsRequest = fetchJSON(endpoint, cb); }; requestor.fetchGoals = function(callback) { var endpoint = [baseUrl, '/sdk/goals/', environment].join(''); fetchJSON(endpoint, callback); }; return requestor; } var Requestor_1 = Requestor; function sanitizeUser(u) { var sane = utils.clone(u); if (sane.key) { sane.key = sane.key.toString(); } return sane; } function Identity(initialUser, onChange) { var ident = {}; var user; ident.setUser = function(u) { user = sanitizeUser(u); onChange(utils.clone(user)); }; ident.getUser = function() { return utils.clone(user); }; if (initialUser) { ident.setUser(initialUser); } return ident; } var Identity_1 = Identity; var messages ={ invalidKey: function() { return 'Event key must be a string'; }, unknownCustomEventKey: function(key) { return 'Custom event "' + key + '" does not exist' } }; var index = createCommonjsModule(function (module) { var flags = {}; var environment; var events; var requestor; var stream; var emitter; var hash; var ident; var baseUrl; var eventsUrl; var streamUrl; var goalTracker; var useLocalStorage; var goals; var readyEvent = 'ready'; var changeEvent = 'change'; var flushInterval = 2000; var seenRequests = {}; function sendIdentifyEvent(user) { events.enqueue({ kind: 'identify', key: user.key, user: user, creationDate: (new Date()).getTime() }); } function sendFlagEvent(key, value, defaultValue) { var user = ident.getUser(); var cacheKey = JSON.stringify(value) + (user && user.key ? user.key : '') + key; var now = new Date(); var cached = seenRequests[cacheKey]; if (cached && (now - cached) < 300000 /* five minutes, in ms */) { return; } seenRequests[cacheKey] = now; events.enqueue({ kind: 'feature', key: key, user: user, value: value, 'default': defaultValue, creationDate: now.getTime() }); } function sendGoalEvent(kind, goal) { var event = { kind: kind, key: goal.key, data: null, url: window.location.href, creationDate: (new Date()).getTime() }; if (kind === 'click') { event.selector = goal.selector; } return events.enqueue(event); } function identify(user, hash, onDone) { ident.setUser(user); requestor.fetchFlagSettings(ident.getUser(), hash, function(err, settings) { if (err) { console.warn('Error fetching flag settings: ', err); } if (settings) { updateSettings(settings); } onDone && onDone(); }); } function variation(key, defaultValue) { var value; if (flags && flags.hasOwnProperty(key)) { value = flags[key] === null ? defaultValue : flags[key]; } else { value = defaultValue; } sendFlagEvent(key, value, defaultValue); return value; } function allFlags() { var results = {}; if (!flags) { return results; } for (var key in flags) { if (flags.hasOwnProperty(key)) { results[key] = variation(key, null); } } return results; } function customEventExists(key) { if (!goals || goals.length === 0) { return false; } for (var i=0 ; i < goals.length ; i++) { if (goals[i].kind === 'custom' && goals[i].key === key) { return true; } } return false; } function track(key, data) { if (typeof key !== 'string') { throw messages.invalidKey(); } // Validate key if we have goals if (!!goals && !customEventExists(key)) { console.warn(messages.unknownCustomEventKey(key)); } events.enqueue({ kind: 'custom', key: key, data: data, url: window.location.href, creationDate: (new Date()).getTime() }); } function connectStream() { stream.connect(function() { requestor.fetchFlagSettings(ident.getUser(), hash, function(err, settings) { if (err) { console.warn('Error fetching flag settings: ', err); } updateSettings(settings); }); }); } function updateSettings(settings) { var changes; var keys; if (!settings) { return; } changes = utils.modifications(flags, settings); keys = Object.keys(changes); flags = settings; if (useLocalStorage) { localStorage.setItem(lsKey(environment, ident.getUser()), JSON.stringify(flags)); } if (keys.length > 0) { keys.forEach(function(key) { emitter.emit(changeEvent + ':' + key, changes[key].current, changes[key].previous); }); emitter.emit(changeEvent, changes); keys.forEach(function(key) { sendFlagEvent(key, changes[key].current); }); } } function on(event, handler, context) { if (event.substr(0, changeEvent.length) === changeEvent) { if (!stream.isConnected()) { connectStream(); } emitter.on.apply(emitter, [event, handler, context]); } else { emitter.on.apply(emitter, Array.prototype.slice.call(arguments)); } } function off() { emitter.off.apply(emitter, Array.prototype.slice.call(arguments)); } function handleMessage(event) { if (event.origin !== baseUrl) { return; } if (event.data.type === 'SYN') { window.editorClientBaseUrl = baseUrl; var editorTag = document.createElement('script'); editorTag.type = 'text/javascript'; editorTag.async = true; editorTag.src = baseUrl + event.data.editorClientUrl; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(editorTag, s); } } var client = { identify: identify, variation: variation, track: track, on: on, off: off, allFlags: allFlags }; function lsKey(env, user) { var uKey = ''; if (user && user.key) { uKey = user.key; } return 'ld:' + env + ':' + uKey; } function initialize(env, user, options) { var localStorageKey; options = options || {}; environment = env; flags = typeof(options.bootstrap) === 'object' ? options.bootstrap : {}; hash = options.hash; baseUrl = options.baseUrl || 'https://app.launchdarkly.com'; eventsUrl = options.eventsUrl || 'https://events.launchdarkly.com'; streamUrl = options.streamUrl || 'https://clientstream.launchdarkly.com'; stream = Stream_1(streamUrl, environment); events = EventProcessor_1(eventsUrl + '/a/' + environment + '.gif'); emitter = EventEmitter_1(); ident = Identity_1(user, sendIdentifyEvent); requestor = Requestor_1(baseUrl, environment); localStorageKey = lsKey(environment, ident.getUser()); if (typeof options.bootstrap === 'object') { // Emitting the event here will happen before the consumer // can register a listener, so defer to next tick. setTimeout(function() { emitter.emit(readyEvent); }, 0); } else if (typeof(options.bootstrap) === 'string' && options.bootstrap.toUpperCase() === 'LOCALSTORAGE' && typeof(Storage) !== 'undefined') { useLocalStorage = true; // check if localstorage data is corrupted, if so clear it try { flags = JSON.parse(localStorage.getItem(localStorageKey)); } catch (error) { localStorage.setItem(localStorageKey, null); } if (flags === null) { requestor.fetchFlagSettings(ident.getUser(), hash, function(err, settings) { if (err) { console.warn('Error fetching flag settings: ', err); } flags = settings; settings && localStorage.setItem(localStorageKey, JSON.stringify(flags)); emitter.emit(readyEvent); }); } else { // We're reading the flags from local storage. Signal that we're ready, // then update localStorage for the next page load. We won't signal changes or update // the in-memory flags unless you subscribe for changes setTimeout(function() { emitter.emit(readyEvent); }, 0); requestor.fetchFlagSettings(ident.getUser(), hash, function(err, settings) { if (err) { console.warn('Error fetching flag settings: ', err); } settings && localStorage.setItem(localStorageKey, JSON.stringify(settings)); }); } } else { requestor.fetchFlagSettings(ident.getUser(), hash, function(err, settings) { if (err) { console.warn('Error fetching flag settings: ', err); } flags = settings; emitter.emit(readyEvent); }); } requestor.fetchGoals(function(err, g) { if (err) { console.warn('Error fetching goals: ', err); } if (g && g.length > 0) { goals = g; goalTracker = GoalTracker_1(goals, sendGoalEvent); } }); function start() { setTimeout(function tick() { events.flush(ident.getUser()); setTimeout(tick, flushInterval); }, flushInterval); } if (document.readyState !== 'complete') { window.addEventListener('load', start); } else { start(); } window.addEventListener('beforeunload', function() { events.flush(ident.getUser(), true); }); function refreshGoalTracker() { if (goalTracker) { goalTracker.dispose(); } if (goals && goals.length) { goalTracker = GoalTracker_1(goals, sendGoalEvent); } } if (goals && goals.length > 0) { if (!!(window.history && history.pushState)) { window.addEventListener('popstate', refreshGoalTracker); } else { window.addEventListener('hashchange', refreshGoalTracker); } } window.addEventListener('message', handleMessage); return client; } module.exports = { initialize: initialize }; { module.exports.version = '1.0.0'; } }); /** * lodash (Custom Build) <https://lodash.com/> * Build: `lodash modularize exports="npm" -o ./` * Copyright jQuery Foundation and other contributors <https://jquery.org/> * Released under MIT license <https://lodash.com/license> * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE> * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors */ /** Used as references for various `Number` constants. */ var INFINITY = 1 / 0; /** `Object#toString` result references. */ var symbolTag = '[object Symbol]'; /** Used to match words composed of alphanumeric characters. */ var reAsciiWord = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g; /** Used to match Latin Unicode letters (excluding mathematical operators). */ var reLatin = /[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g; /** Used to compose unicode character classes. */ var rsAstralRange = '\\ud800-\\udfff'; var rsComboMarksRange = '\\u0300-\\u036f\\ufe20-\\ufe23'; var rsComboSymbolsRange = '\\u20d0-\\u20f0'; var rsDingbatRange = '\\u2700-\\u27bf'; var rsLowerRange = 'a-z\\xdf-\\xf6\\xf8-\\xff'; var rsMathOpRange = '\\xac\\xb1\\xd7\\xf7'; var rsNonCharRange = '\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf'; var rsPunctuationRange = '\\u2000-\\u206f'; var rsSpaceRange = ' \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000'; var rsUpperRange = 'A-Z\\xc0-\\xd6\\xd8-\\xde'; var rsVarRange = '\\ufe0e\\ufe0f'; var rsBreakRange = rsMathOpRange + rsNonCharRange + rsPunctuationRange + rsSpaceRange; /** Used to compose unicode capture groups. */ var rsApos = "['\u2019]"; var rsAstral = '[' + rsAstralRange + ']'; var rsBreak = '[' + rsBreakRange + ']'; var rsCombo = '[' + rsComboMarksRange + rsComboSymbolsRange + ']'; var rsDigits = '\\d+'; var rsDingbat = '[' + rsDingbatRange + ']'; var rsLower = '[' + rsLowerRange + ']'; var rsMisc = '[^' + rsAstralRange + rsBreakRange + rsDigits + rsDingbatRange + rsLowerRange + rsUpperRange + ']'; var rsFitz = '\\ud83c[\\udffb-\\udfff]'; var rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')'; var rsNonAstral = '[^' + rsAstralRange + ']'; var rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}'; var rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]'; var rsUpper = '[' + rsUpperRange + ']'; var rsZWJ = '\\u200d'; /** Used to compose unicode regexes. */ var rsLowerMisc = '(?:' + rsLower + '|' + rsMisc + ')'; var rsUpperMisc = '(?:' + rsUpper + '|' + rsMisc + ')'; var rsOptLowerContr = '(?:' + rsApos + '(?:d|ll|m|re|s|t|ve))?'; var rsOptUpperContr = '(?:' + rsApos + '(?:D|LL|M|RE|S|T|VE))?'; var reOptMod = rsModifier + '?'; var rsOptVar = '[' + rsVarRange + ']?'; var rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*'; var rsSeq = rsOptVar + reOptMod + rsOptJoin; var rsEmoji = '(?:' + [rsDingbat, rsRegional, rsSurrPair].join('|') + ')' + rsSeq; var rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')'; /** Used to match apostrophes. */ var reApos = RegExp(rsApos, 'g'); /** * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols). */ var reComboMark = RegExp(rsCombo, 'g'); /** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */ var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g'); /** Used to match complex or compound words. */ var reUnicodeWord = RegExp([ rsUpper + '?' + rsLower + '+' + rsOptLowerContr + '(?=' + [rsBreak, rsUpper, '$'].join('|') + ')', rsUpperMisc + '+' + rsOptUpperContr + '(?=' + [rsBreak, rsUpper + rsLowerMisc, '$'].join('|') + ')', rsUpper + '?' + rsLowerMisc + '+' + rsOptLowerContr, rsUpper + '+' + rsOptUpperContr, rsDigits, rsEmoji ].join('|'), 'g'); /** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */ var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange + rsComboMarksRange + rsComboSymbolsRange + rsVarRange + ']'); /** Used to detect strings that need a more robust regexp to match words. */ var reHasUnicodeWord = /[a-z][A-Z]|[A-Z]{2,}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/; /** Used to map Latin Unicode letters to basic Latin letters. */ var deburredLetters = { // Latin-1 Supplement block. '\xc0': 'A', '\xc1': 'A', '\xc2': 'A', '\xc3': 'A', '\xc4': 'A', '\xc5': 'A', '\xe0': 'a', '\xe1': 'a', '\xe2': 'a', '\xe3': 'a', '\xe4': 'a', '\xe5': 'a', '\xc7': 'C', '\xe7': 'c', '\xd0': 'D', '\xf0': 'd', '\xc8': 'E', '\xc9': 'E', '\xca': 'E', '\xcb': 'E', '\xe8': 'e', '\xe9': 'e', '\xea': 'e', '\xeb': 'e', '\xcc': 'I', '\xcd': 'I', '\xce': 'I', '\xcf': 'I', '\xec': 'i', '\xed': 'i', '\xee': 'i', '\xef': 'i', '\xd1': 'N', '\xf1': 'n', '\xd2': 'O', '\xd3': 'O', '\xd4': 'O', '\xd5': 'O', '\xd6': 'O', '\xd8': 'O', '\xf2': 'o', '\xf3': 'o', '\xf4': 'o', '\xf5': 'o', '\xf6': 'o', '\xf8': 'o', '\xd9': 'U', '\xda': 'U', '\xdb': 'U', '\xdc': 'U', '\xf9': 'u', '\xfa': 'u', '\xfb': 'u', '\xfc': 'u', '\xdd': 'Y', '\xfd': 'y', '\xff': 'y', '\xc6': 'Ae', '\xe6': 'ae', '\xde': 'Th', '\xfe': 'th', '\xdf': 'ss', // Latin Extended-A block. '\u0100': 'A', '\u0102': 'A', '\u0104': 'A', '\u0101': 'a', '\u0103': 'a', '\u0105': 'a', '\u0106': 'C', '\u0108': 'C', '\u010a': 'C', '\u010c': 'C', '\u0107': 'c', '\u0109': 'c', '\u010b': 'c', '\u010d': 'c', '\u010e': 'D', '\u0110': 'D', '\u010f': 'd', '\u0111': 'd', '\u0112': 'E', '\u0114': 'E', '\u0116': 'E', '\u0118': 'E', '\u011a': 'E', '\u0113': 'e', '\u0115': 'e', '\u0117': 'e', '\u0119': 'e', '\u011b': 'e', '\u011c': 'G', '\u011e': 'G', '\u0120': 'G', '\u0122': 'G', '\u011d': 'g', '\u011f': 'g', '\u0121': 'g', '\u0123': 'g', '\u0124': 'H', '\u0126': 'H', '\u0125': 'h', '\u0127': 'h', '\u0128': 'I', '\u012a': 'I', '\u012c': 'I', '\u012e': 'I', '\u0130': 'I', '\u0129': 'i', '\u012b': 'i', '\u012d': 'i', '\u012f': 'i', '\u0131': 'i', '\u0134': 'J', '\u0135': 'j', '\u0136': 'K', '\u0137': 'k', '\u0138': 'k', '\u0139': 'L', '\u013b': 'L', '\u013d': 'L', '\u013f': 'L', '\u0141': 'L', '\u013a': 'l', '\u013c': 'l', '\u013e': 'l', '\u0140': 'l', '\u0142': 'l', '\u0143': 'N', '\u0145': 'N', '\u0147': 'N', '\u014a': 'N', '\u0144': 'n', '\u0146': 'n', '\u0148': 'n', '\u014b': 'n', '\u014c': 'O', '\u014e': 'O', '\u0150': 'O', '\u014d': 'o', '\u014f': 'o', '\u0151': 'o', '\u0154': 'R', '\u0156': 'R', '\u0158': 'R', '\u0155': 'r', '\u0157': 'r', '\u0159': 'r', '\u015a': 'S', '\u015c': 'S', '\u015e': 'S', '\u0160': 'S', '\u015b': 's', '\u015d': 's', '\u015f': 's', '\u0161': 's', '\u0162': 'T', '\u0164': 'T', '\u0166': 'T', '\u0163': 't', '\u0165': 't', '\u0167': 't', '\u0168': 'U', '\u016a': 'U', '\u016c': 'U', '\u016e': 'U', '\u0170': 'U', '\u0172': 'U', '\u0169': 'u', '\u016b': 'u', '\u016d': 'u', '\u016f': 'u', '\u0171': 'u', '\u0173': 'u', '\u0174': 'W', '\u0175': 'w', '\u0176': 'Y', '\u0177': 'y', '\u0178': 'Y', '\u0179': 'Z', '\u017b': 'Z', '\u017d': 'Z', '\u017a': 'z', '\u017c': 'z', '\u017e': 'z', '\u0132': 'IJ', '\u0133': 'ij', '\u0152': 'Oe', '\u0153': 'oe', '\u0149': "'n", '\u017f': 'ss' }; /** Detect free variable `global` from Node.js. */ var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal; /** Detect free variable `self`. */ var freeSelf = typeof self == 'object' && self && self.Object === Object && self; /** Used as a reference to the global object. */ var root = freeGlobal || freeSelf || Function('return this')(); /** * A specialized version of `_.reduce` for arrays without support for * iteratee shorthands. * * @private * @param {Array} [array] The array to iterate over. * @param {Function} iteratee The function invoked per iteration. * @param {*} [accumulator] The initial value. * @param {boolean} [initAccum] Specify using the first element of `array` as * the initial value. * @returns {*} Returns the accumulated value. */ function arrayReduce(array, iteratee, accumulator, initAccum) { var index = -1, length = array ? array.length : 0; if (initAccum && length) { accumulator = array[++index]; } while (++index < length) { accumulator = iteratee(accumulator, array[index], index, array); } return accumulator; } /** * Converts an ASCII `string` to an array. * * @private * @param {string} string The string to convert. * @returns {Array} Returns the converted array. */ function asciiToArray(string) { return string.split(''); } /** * Splits an ASCII `string` into an array of its words. * * @private * @param {string} The string to inspect. * @returns {Array} Returns the words of `string`. */ function asciiWords(string) { return string.match(reAsciiWord) || []; } /** * The base implementation of `_.propertyOf` without support for deep paths. * * @private * @param {Object} object The object to query. * @returns {Function} Returns the new accessor function. */ function basePropertyOf(object) { return function(key) { return object == null ? undefined : object[key]; }; } /** * Used by `_.deburr` to convert Latin-1 Supplement and Latin Extended-A * letters to basic Latin letters. * * @private * @param {string} letter The matched letter to deburr. * @returns {string} Returns the deburred letter. */ var deburrLetter = basePropertyOf(deburredLetters); /** * Checks if `string` contains Unicode symbols. * * @private * @param {string} string The string to inspect. * @returns {boolean} Returns `true` if a symbol is found, else `false`. */ function hasUnicode(string) { return reHasUnicode.test(string); } /** * Checks if `string` contains a word composed of Unicode symbols. * * @private * @param {string} string The string to inspect. * @returns {boolean} Returns `true` if a word is found, else `false`. */ function hasUnicodeWord(string) { return reHasUnicodeWord.test(string); } /** * Converts `string` to an array. * * @private * @param {string} string The string to convert. * @returns {Array} Returns the converted array. */ function stringToArray(string) { return hasUnicode(string) ? unicodeToArray(string) : asciiToArray(string); } /** * Converts a Unicode `string` to an array. * * @private * @param {string} string The string to convert. * @returns {Array} Returns the converted array. */ function unicodeToArray(string) { return string.match(reUnicode) || []; } /** * Splits a Unicode `string` into an array of its words. * * @private * @param {string} The string to inspect. * @returns {Array} Returns the words of `string`. */ function unicodeWords(string) { return string.match(reUnicodeWord) || []; } /** Used for built-in method references. */ var objectProto = Object.prototype; /** * Used to resolve the * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) * of values. */ var objectToString = objectProto.toString; /** Built-in value references. */ var Symbol$1 = root.Symbol; /** Used to convert symbols to primitives and strings. */ var symbolProto = Symbol$1 ? Symbol$1.prototype : undefined; var symbolToString = symbolProto ? symbolProto.toString : undefined; /** * The base implementation of `_.slice` without an iteratee call guard. * * @private * @param {Array} array The array to slice. * @param {number} [start=0] The start position. * @param {number} [end=array.length] The end position. * @returns {Array} Returns the slice of `array`. */ function baseSlice(array, start, end) { var index = -1, length = array.length; if (start < 0) { start = -start > length ? 0 : (length + start); } end = end > length ? length : end; if (end < 0) { end += length; } length = start > end ? 0 : ((end - start) >>> 0); start >>>= 0; var result = Array(length); while (++index < length) { result[index] = array[index + start]; } return result; } /** * The base implementation of `_.toString` which doesn't convert nullish * values to empty strings. * * @private * @param {*} value The value to process. * @returns {string} Returns the string. */ function baseToString(value) { // Exit early for strings to avoid a performance hit in some environments. if (typeof value == 'string') { return value; } if (isSymbol(value)) { return symbolToString ? symbolToString.call(value) : ''; } var result = (value + ''); return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; } /** * Casts `array` to a slice if it's needed. * * @private * @param {Array} array The array to inspect. * @param {number} start The start position. * @param {number} [end=array.length] The end position. * @returns {Array} Returns the cast slice. */ function castSlice(array, start, end) { var length = array.length; end = end === undefined ? length : end; return (!start && end >= length) ? array : baseSlice(array, start, end); } /** * Creates a function like `_.lowerFirst`. * * @private * @param {string} methodName The name of the `String` case method to use. * @returns {Function} Returns the new case function. */ function createCaseFirst(methodName) { return function(string) { string = toString(string); var strSymbols = hasUnicode(string) ? stringToArray(string) : undefined; var chr = strSymbols ? strSymbols[0] : string.charAt(0); var trailing = strSymbols ? castSlice(strSymbols, 1).join('') : string.slice(1); return chr[methodName]() + trailing; }; } /** * Creates a function like `_.camelCase`. * * @private * @param {Function} callback The function to combine each word. * @returns {Function} Returns the new compounder function. */ function createCompounder(callback) { return function(string) { return arrayReduce(words(deburr(string).replace(reApos, '')), callback, ''); }; } /** * Checks if `value` is object-like. A value is object-like if it's not `null` * and has a `typeof` result of "object". * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is object-like, else `false`. * @example * * _.isObjectLike({}); * // => true * * _.isObjectLike([1, 2, 3]); * // => true * * _.isObjectLike(_.noop); * // => false * * _.isObjectLike(null); * // => false */ function isObjectLike(value) { return !!value && typeof value == 'object'; } /** * Checks if `value` is classified as a `Symbol` primitive or object. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. * @example * * _.isSymbol(Symbol.iterator); * // => true * * _.isSymbol('abc'); * // => false */ function isSymbol(value) { return typeof value == 'symbol' || (isObjectLike(value) && objectToString.call(value) == symbolTag); } /** * Converts `value` to a string. An empty string is returned for `null` * and `undefined` values. The sign of `-0` is preserved. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to process. * @returns {string} Returns the string. * @example * * _.toString(null); * // => '' * * _.toString(-0); * // => '-0' * * _.toString([1, 2, 3]); * // => '1,2,3' */ function toString(value) { return value == null ? '' : baseToString(value); } /** * Converts `string` to [camel case](https://en.wikipedia.org/wiki/CamelCase). * * @static * @memberOf _ * @since 3.0.0 * @category String * @param {string} [string=''] The string to convert. * @returns {string} Returns the camel cased string. * @example * * _.camelCase('Foo Bar'); * // => 'fooBar' * * _.camelCase('--foo-bar--'); * // => 'fooBar' * * _.camelCase('__FOO_BAR__'); * // => 'fooBar' */ var camelCase = createCompounder(function(result, word, index) { word = word.toLowerCase(); return result + (index ? capitalize(word) : word); }); /** * Converts the first character of `string` to upper case and the remaining * to lower case. * * @static * @memberOf _ * @since 3.0.0 * @category String * @param {string} [string=''] The string to capitalize. * @returns {string} Returns the capitalized string. * @example * * _.capitalize('FRED'); * // => 'Fred' */ function capitalize(string) { return upperFirst(toString(string).toLowerCase()); } /** * Deburrs `string` by converting * [Latin-1 Supplement](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table) * and [Latin Extended-A](https://en.wikipedia.org/wiki/Latin_Extended-A) * letters to basic Latin letters and removing * [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). * * @static * @memberOf _ * @since 3.0.0 * @category String * @param {string} [string=''] The string to deburr. * @returns {string} Returns the deburred string. * @example * * _.deburr('déjà vu'); * // => 'deja vu' */ function deburr(string) { string = toString(string); return string && string.replace(reLatin, deburrLetter).replace(reComboMark, ''); } /** * Converts the first character of `string` to upper case. * * @static * @memberOf _ * @since 4.0.0 * @category String * @param {string} [string=''] The string to convert. * @returns {string} Returns the converted string. * @example * * _.upperFirst('fred'); * // => 'Fred' * * _.upperFirst('FRED'); * // => 'FRED' */ var upperFirst = createCaseFirst('toUpperCase'); /** * Splits `string` into an array of its words. * * @static * @memberOf _ * @since 3.0.0 * @category String * @param {string} [string=''] The string to inspect. * @param {RegExp|string} [pattern] The pattern to match words. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. * @returns {Array} Returns the words of `string`. * @example * * _.words('fred, barney, & pebbles'); * // => ['fred', 'barney', 'pebbles'] * * _.words('fred, barney, & pebbles', /[^, ]+/g); * // => ['fred', 'barney', '&', 'pebbles'] */ function words(string, pattern, guard) { string = toString(string); pattern = guard ? undefined : pattern; if (pattern === undefined) { return hasUnicodeWord(string) ? unicodeWords(string) : asciiWords(string); } return string.match(pattern) || []; } var index$3 = camelCase; /** * To avoid having to rely on node-uuid and crypto. * * Credit: https://gist.github.com/antonioaguilar/6135f84658328d399ed656ba3169e558 */ var uuid = (function () { var date = new Date().getTime(); var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { var random = (date + Math.random() * 16) % 16 | 0; date = Math.floor(date / 16); return (c === 'x' ? random : random & 0x3 | 0x8).toString(16); }); return uuid; }); var classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; var createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var defineProperty = function (obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }; var _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; }; var inherits = function (subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }; var possibleConstructorReturn = function (self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }; var slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); var createAnonymousUser = function createAnonymousUser() { return { key: uuid() }; }; var normalizeFlag = function normalizeFlag(flagName, flagValue) { return [index$3(flagName), // Multivariate flags contain a string or `null` - `false` seems // more natural. flagValue === null ? false : flagValue]; }; var camelCaseFlags = function camelCaseFlags(rawFlags) { return Object.entries(rawFlags).reduce(function (camelCasedFlags, _ref) { var _ref2 = slicedToArray(_ref, 2), flagName = _ref2[0], flagValue = _ref2[1]; var _normalizeFlag = normalizeFlag(flagName, flagValue), _normalizeFlag2 = slicedToArray(_normalizeFlag, 2), normalzedFlagName = _normalizeFlag2[0], normalzedFlagValue = _normalizeFlag2[1]; // Can't return expression as it is the assigned value camelCasedFlags[normalzedFlagName] = normalzedFlagValue; return camelCasedFlags; }, {}); }; var initialize = function initialize(_ref3) { var clientSideId = _ref3.clientSideId, user = _ref3.user; return index.initialize(clientSideId, user || createAnonymousUser()); }; var flagUpdates = function flagUpdates(_ref4) { var rawFlags = _ref4.rawFlags, client = _ref4.client, updateFlags = _ref4.updateFlags; var _loop = function _loop(flagName) { if (Object.prototype.hasOwnProperty.call(rawFlags, flagName)) { client.on('change:' + flagName, function (flagValue) { var _normalizeFlag3 = normalizeFlag(flagName, flagValue), _normalizeFlag4 = slicedToArray(_normalizeFlag3, 2), normalzedFlagName = _normalizeFlag4[0], normalzedFlagValue = _normalizeFlag4[1]; updateFlags(defineProperty({}, normalzedFlagName, normalzedFlagValue)); }); } }; // Dispatch whenever configured flag value changes for (var flagName in rawFlags) { _loop(flagName); } }; var listen = function listen(_ref5) { var client = _ref5.client, updateFlags = _ref5.updateFlags, updateStatus = _ref5.updateStatus; client.on('ready', function () { updateStatus({ isReady: true }); var rawFlags = client.allFlags(); var camelCasedFlags = camelCaseFlags(rawFlags); updateFlags(camelCasedFlags); flagUpdates({ rawFlags: rawFlags, client: client, updateFlags: updateFlags }); }); }; // Actions var UPDATE_FLAGS = '@flopflip/flags/update'; var initialState = {}; // Reducer function reducer() { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : initialState; var action = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; switch (action.type) { case UPDATE_FLAGS: return _extends({}, state, action.payload); default: return state; } } // Action Creators var update = function update(flags) { return { type: UPDATE_FLAGS, payload: flags }; }; // Actions var UPDATE_STATUS = '@flopflip/status/update'; var initialState$1 = { isReady: false }; // Reducer function reducer$1() { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : initialState$1; var action = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; switch (action.type) { case UPDATE_STATUS: return _extends({}, state, { isReady: action.payload.isReady }); default: return state; } } // Action Creators var update$1 = function update(status) { return { type: UPDATE_STATUS, payload: status }; }; var flopflipReducer = combineReducers({ flags: reducer, status: reducer$1 }); function createFlopFlipEnhancer(clientSideId, user) { var client = initialize({ clientSideId: clientSideId, user: user }); return function (next) { return function () { var store = next.apply(undefined, arguments); listen({ client: client, updateFlags: store.dispatch(update), updateStatus: store.dispatch(update$1) }); return store; }; }; } var STATE_SLICE = '@flopflip'; var FeatureToggled = function (_React$Component) { inherits(FeatureToggled, _React$Component); function FeatureToggled() { classCallCheck(this, FeatureToggled); return possibleConstructorReturn(this, (FeatureToggled.__proto__ || Object.getPrototypeOf(FeatureToggled)).apply(this, arguments)); } createClass(FeatureToggled, [{ key: 'render', value: function render() { if (this.props.isFeatureEnabled) { return this.props.children; } return this.props.untoggledComponent; } }]); return FeatureToggled; }(React.Component); FeatureToggled.defaultProps = { untoggledComponent: null }; var featureToggled = connect(function (state, ownProps) { return { isFeatureEnabled: Boolean(state[STATE_SLICE].flags[ownProps.flag]) }; })(FeatureToggled); /** * Copyright (c) 2013-present, 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 this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * * @typechecks * */ /*eslint-disable no-self-compare */ /** * Copyright 2015, Yahoo! Inc. * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var index$5 = createCommonjsModule(function (module, exports) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var createChangeEmitter = exports.createChangeEmitter = function createChangeEmitter() { var currentListeners = []; var nextListeners = currentListeners; function ensureCanMutateNextListeners() { if (nextListeners === currentListeners) { nextListeners = currentListeners.slice(); } } function listen(listener) { if (typeof listener !== 'function') { throw new Error('Expected listener to be a function.'); } var isSubscribed = true; ensureCanMutateNextListeners(); nextListeners.push(listener); return function () { if (!isSubscribed) { return; } isSubscribed = false; ensureCanMutateNextListeners(); var index = nextListeners.indexOf(listener); nextListeners.splice(index, 1); }; } function emit() { currentListeners = nextListeners; var listeners = currentListeners; for (var i = 0; i < listeners.length; i++) { listeners[i].apply(listeners, arguments); } } return { listen: listen, emit: emit }; }; }); var ponyfill = createCommonjsModule(function (module, exports) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports['default'] = symbolObservablePonyfill; function symbolObservablePonyfill(root) { var result; var _Symbol = root.Symbol; if (typeof _Symbol === 'function') { if (_Symbol.observable) { result = _Symbol.observable; } else { result = _Symbol('observable'); _Symbol.observable = result; } } else { result = '@@observable'; } return result; } }); var index$8 = createCommonjsModule(function (module, exports) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _ponyfill2 = _interopRequireDefault(ponyfill); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var root; /* global window */ if (typeof self !== 'undefined') { root =