UNPKG

ashish-sdk

Version:
1,436 lines (1,356 loc) 385 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.tsembed = {})); }(this, (function (exports) { 'use strict'; /** * Copyright (c) 2022 * * Common utility functions for ThoughtSpot Visual Embed SDK * * @summary Utils * @author Ayon Ghosh <ayon.ghosh@thoughtspot.com> */ /** * Construct a runtime filters query string from the given filters. * Refer to the following docs for more details on runtime filter syntax: * https://cloud-docs.thoughtspot.com/admin/ts-cloud/apply-runtime-filter.html * https://cloud-docs.thoughtspot.com/admin/ts-cloud/runtime-filter-operators.html * @param runtimeFilters */ const getFilterQuery = (runtimeFilters) => { if (runtimeFilters && runtimeFilters.length) { const filters = runtimeFilters.map((filter, valueIndex) => { const index = valueIndex + 1; const filterExpr = []; filterExpr.push(`col${index}=${filter.columnName}`); filterExpr.push(`op${index}=${filter.operator}`); filterExpr.push(filter.values.map((value) => `val${index}=${value}`).join('&')); return filterExpr.join('&'); }); return `${filters.join('&')}`; } return null; }; /** * Convert a value to a string representation to be sent as a query * parameter to the ThoughtSpot app. * @param value Any parameter value */ const serializeParam = (value) => { // do not serialize primitive types if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { return value; } return JSON.stringify(value); }; /** * Convert a value to a string: * in case of an array, we convert it to CSV. * in case of any other type, we directly return the value. * @param value */ const paramToString = (value) => Array.isArray(value) ? value.join(',') : value; /** * Return a query param string composed from the given params object * @param queryParams */ const getQueryParamString = (queryParams, shouldSerializeParamValues = false) => { const qp = []; const params = Object.keys(queryParams); params.forEach((key) => { const val = queryParams[key]; if (val !== undefined) { const serializedValue = shouldSerializeParamValues ? serializeParam(val) : paramToString(val); qp.push(`${key}=${serializedValue}`); } }); if (qp.length) { return qp.join('&'); } return null; }; /** * Get a string representation of a dimension value in CSS * If numeric, it is considered in pixels. * @param value */ const getCssDimension = (value) => { if (typeof value === 'number') { return `${value}px`; } return value; }; /** * Append a string to a URL's hash fragment * @param url A URL * @param stringToAppend The string to append to the URL hash */ const appendToUrlHash = (url, stringToAppend) => { let outputUrl = url; const encStringToAppend = encodeURIComponent(stringToAppend); if (url.indexOf('#') >= 0) { outputUrl = `${outputUrl}${encStringToAppend}`; } else { outputUrl = `${outputUrl}#${encStringToAppend}`; } return outputUrl; }; const getEncodedQueryParamsString = (queryString) => { if (!queryString) { return queryString; } return btoa(queryString) .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=+$/, ''); }; const getOffsetTop = (element) => { const rect = element.getBoundingClientRect(); return rect.top + window.scrollY; }; /** * Copyright (c) 2022 * * TypeScript type definitions for ThoughtSpot Visual Embed SDK * * @summary Type definitions for Embed SDK * @author Ayon Ghosh <ayon.ghosh@thoughtspot.com> */ (function (AuthType) { /** * No authentication. Use this only for testing purposes. */ AuthType["None"] = "None"; /** * SSO using SAML */ AuthType["SSO"] = "SSO_SAML"; /** * SSO using OIDC */ AuthType["OIDC"] = "SSO_OIDC"; /** * Trusted authentication server */ AuthType["AuthServer"] = "AuthServer"; /** * Use the ThoughtSpot login API to authenticate to the cluster directly. * * Warning: This feature is primarily intended for developer testing. It is * strongly advised not to use this authentication method in production. */ AuthType["Basic"] = "Basic"; })(exports.AuthType || (exports.AuthType = {})); (function (RuntimeFilterOp) { /** * Equals */ RuntimeFilterOp["EQ"] = "EQ"; /** * Does not equal */ RuntimeFilterOp["NE"] = "NE"; /** * Less than */ RuntimeFilterOp["LT"] = "LT"; /** * Less than or equal to */ RuntimeFilterOp["LE"] = "LE"; /** * Greater than */ RuntimeFilterOp["GT"] = "GT"; /** * Greater than or equal to */ RuntimeFilterOp["GE"] = "GE"; /** * Contains */ RuntimeFilterOp["CONTAINS"] = "CONTAINS"; /** * Begins with */ RuntimeFilterOp["BEGINS_WITH"] = "BEGINS_WITH"; /** * Ends with */ RuntimeFilterOp["ENDS_WITH"] = "ENDS_WITH"; /** * Between, inclusive of higher value */ RuntimeFilterOp["BW_INC_MAX"] = "BW_INC_MAX"; /** * Between, inclusive of lower value */ RuntimeFilterOp["BW_INC_MIN"] = "BW_INC_MIN"; /** * Between, inclusive of both higher and lower value */ RuntimeFilterOp["BW_INC"] = "BW_INC"; /** * Between, non-inclusive */ RuntimeFilterOp["BW"] = "BW"; /** * Is included in this list of values */ RuntimeFilterOp["IN"] = "IN"; })(exports.RuntimeFilterOp || (exports.RuntimeFilterOp = {})); (function (EmbedEvent) { /** * Rendering has initialized. * @return timestamp - The timestamp when the event was generated. */ EmbedEvent["Init"] = "init"; /** * Authentication has either succeeded or failed. * @return isLoggedIn - A Boolean specifying whether authentication was successful. */ EmbedEvent["AuthInit"] = "authInit"; /** * The embed object container has loaded. * @return timestamp - The timestamp when the event was generated. */ EmbedEvent["Load"] = "load"; /** * Data pertaining to answer or Liveboard is received * @return data - The answer or Liveboard data */ EmbedEvent["Data"] = "data"; /** * Search/answer/Liveboard filters have been applied/updated * @hidden */ EmbedEvent["FiltersChanged"] = "filtersChanged"; /** * Search query has been updated */ EmbedEvent["QueryChanged"] = "queryChanged"; /** * A drill down operation has been performed. * @return additionalFilters - Any additional filters applied * @return drillDownColumns - The columns on which drill down was performed * @return nonFilteredColumns - The columns that were not filtered */ EmbedEvent["Drilldown"] = "drillDown"; /** * One or more data sources have been selected. * @return dataSourceIds - the list of data sources */ EmbedEvent["DataSourceSelected"] = "dataSourceSelected"; /** * One or more data columns have been selected. * @return columnIds - the list of columns * @version SDK: 1.9.0 | ThoughtSpot: 8.2.0.cl */ EmbedEvent["AddRemoveColumns"] = "addRemoveColumns"; /** * A custom action has been triggered * @return actionId - The id of the custom action * @return data - The answer or Liveboard data */ EmbedEvent["CustomAction"] = "customAction"; /** * A click has been triggered on table/chart * @return ContextMenuInputPoints - data point that is double clicked * @version 1.10.0 */ EmbedEvent["VizPointClick"] = "vizPointClick"; /** * A double click has been triggered on table/chart * @return ContextMenuInputPoints - data point that is double clicked * @version 1.5.0 or later */ EmbedEvent["VizPointDoubleClick"] = "vizPointDoubleClick"; /** * Event troggered when rendering a chart, call the supplied * callback with the config overrides. * @version 1.10.0 */ EmbedEvent["GetVizConfigOverrides"] = "getVizConfigOverrides"; /** * An error has occurred. * @return error - An error object or message */ EmbedEvent["Error"] = "Error"; /** * The embedded object has sent an alert * @return alert - An alert object */ EmbedEvent["Alert"] = "alert"; /** * The ThoughtSpot auth session has expired. */ EmbedEvent["AuthExpire"] = "ThoughtspotAuthExpired"; /** * The height of the embedded Liveboard or visualization has been computed. * @return data - The height of the embedded Liveboard or visualization * @hidden */ EmbedEvent["EmbedHeight"] = "EMBED_HEIGHT"; /** * The center of visible iframe viewport is calculated. * @return data - The center of the visible Iframe viewport. * @hidden */ EmbedEvent["EmbedIframeCenter"] = "EmbedIframeCenter"; /** * Detects the route change. */ EmbedEvent["RouteChange"] = "ROUTE_CHANGE"; /** * The v1 event type for Data * @hidden */ EmbedEvent["V1Data"] = "exportVizDataToParent"; /** * Emitted when the embed does not have cookie access. This * happens on Safari where third-party cookies are blocked by default. * * @version 1.1.0 or later */ EmbedEvent["NoCookieAccess"] = "noCookieAccess"; /** * Emitted when SAML is complete * @private * @hidden */ EmbedEvent["SAMLComplete"] = "samlComplete"; /** * Emitted when any modal is opened in the app * @version 1.6.0 or later */ EmbedEvent["DialogOpen"] = "dialog-open"; /** * Emitted when any modal is closed in the app * @version 1.6.0 or later */ EmbedEvent["DialogClose"] = "dialog-close"; })(exports.EmbedEvent || (exports.EmbedEvent = {})); (function (HostEvent) { /** * Trigger a search * @param dataSourceIds - The list of data source GUIDs * @param searchQuery - The search query */ HostEvent["Search"] = "search"; /** * Trigger a drill on certain points by certain column * @param points - an object containing selectedPoints/clickedPoints * eg. { selectedPoints: []} * @param columnGuid - a string guid of the column to drill by. This is optional, * if not provided it will auto drill by the configured column. \ * @version 1.5.0 or later */ HostEvent["DrillDown"] = "triggerDrillDown"; /** * Apply filters * @hidden */ HostEvent["Filter"] = "filter"; /** * Reload the answer or visualization * @hidden */ HostEvent["Reload"] = "reload"; /** * Set the visible visualizations on a Liveboard. * @param - an array of ids of visualizations to show, the ids not passed * will be hidden. * @version 1.6.0 or later */ HostEvent["SetVisibleVizs"] = "SetPinboardVisibleVizs"; /** * Update the runtime filters. The runtime filters passed here are extended * on to the existing runtime filters if they exist. * @param - {@link RuntimeFilter}[] an array of {@link RuntimeFilter} Types. * @version 1.8.0 or later */ HostEvent["UpdateRuntimeFilters"] = "UpdateRuntimeFilters"; /** * Highlight the point in the chart defined by the InputPoint passed. * @verion 1.10.0 */ HostEvent["HighlightPoint"] = "HighlightPoint"; })(exports.HostEvent || (exports.HostEvent = {})); (function (DataSourceVisualMode) { /** * Data source panel is hidden. */ DataSourceVisualMode["Hidden"] = "hide"; /** * Data source panel is collapsed, but the user can manually expand it. */ DataSourceVisualMode["Collapsed"] = "collapse"; /** * Data source panel is expanded, but the user can manually collapse it. */ DataSourceVisualMode["Expanded"] = "expand"; })(exports.DataSourceVisualMode || (exports.DataSourceVisualMode = {})); /** * The query params passed down to the embedded ThoughtSpot app * containing configuration and/or visual information. */ // eslint-disable-next-line no-shadow var Param; (function (Param) { Param["DataSources"] = "dataSources"; Param["DataSourceMode"] = "dataSourceMode"; Param["DisableActions"] = "disableAction"; Param["DisableActionReason"] = "disableHint"; Param["ForceTable"] = "forceTable"; Param["preventLiveboardFilterRemoval"] = "preventPinboardFilterRemoval"; Param["SearchQuery"] = "searchQuery"; Param["HideActions"] = "hideAction"; Param["HideObjects"] = "hideObjects"; Param["HostAppUrl"] = "hostAppUrl"; Param["EnableVizTransformations"] = "enableVizTransform"; Param["EnableSearchAssist"] = "enableSearchAssist"; Param["HideResult"] = "hideResult"; Param["UseLastSelectedDataSource"] = "useLastSelectedSources"; Param["Tag"] = "tag"; Param["searchTokenString"] = "searchTokenString"; Param["executeSearch"] = "executeSearch"; Param["fullHeight"] = "isFullHeightPinboard"; Param["livedBoardEmbed"] = "isLiveboardEmbed"; Param["searchEmbed"] = "isSearchEmbed"; Param["Version"] = "sdkVersion"; Param["ViewPortHeight"] = "viewPortHeight"; Param["ViewPortWidth"] = "viewPortWidth"; Param["VisibleActions"] = "visibleAction"; Param["CustomCSSUrl"] = "customCssUrl"; })(Param || (Param = {})); (function (Action) { Action["Save"] = "save"; Action["Update"] = "update"; Action["SaveUntitled"] = "saveUntitled"; Action["SaveAsView"] = "saveAsView"; Action["MakeACopy"] = "makeACopy"; Action["EditACopy"] = "editACopy"; Action["CopyLink"] = "embedDocument"; Action["ResetLayout"] = "resetLayout"; Action["Schedule"] = "subscription"; Action["SchedulesList"] = "schedule-list"; Action["Share"] = "share"; Action["AddFilter"] = "addFilter"; Action["ConfigureFilter"] = "configureFilter"; Action["AddFormula"] = "addFormula"; Action["SearchOnTop"] = "searchOnTop"; Action["SpotIQAnalyze"] = "spotIQAnalyze"; Action["ExplainInsight"] = "explainInsight"; Action["SpotIQFollow"] = "spotIQFollow"; Action["ShareViz"] = "shareViz"; Action["ReplaySearch"] = "replaySearch"; Action["ShowUnderlyingData"] = "showUnderlyingData"; Action["Download"] = "download"; Action["DownloadAsPdf"] = "downloadAsPdf"; Action["DownloadAsCsv"] = "downloadAsCSV"; Action["DownloadAsXlsx"] = "downloadAsXLSX"; Action["DownloadTrace"] = "downloadTrace"; Action["ExportTML"] = "exportTSL"; Action["ImportTML"] = "importTSL"; Action["UpdateTML"] = "updateTSL"; Action["EditTML"] = "editTSL"; Action["Present"] = "present"; Action["ToggleSize"] = "toggleSize"; Action["Edit"] = "edit"; Action["EditTitle"] = "editTitle"; Action["Remove"] = "delete"; Action["Ungroup"] = "ungroup"; Action["Describe"] = "describe"; Action["Relate"] = "relate"; Action["CustomizeHeadlines"] = "customizeHeadlines"; /** * @hidden */ Action["PinboardInfo"] = "pinboardInfo"; Action["LiveboardInfo"] = "pinboardInfo"; Action["SendAnswerFeedback"] = "sendFeedback"; Action["DownloadEmbraceQueries"] = "downloadEmbraceQueries"; Action["Pin"] = "pin"; Action["AnalysisInfo"] = "analysisInfo"; Action["Subscription"] = "subscription"; Action["Explore"] = "explore"; Action["DrillInclude"] = "context-menu-item-include"; Action["DrillExclude"] = "context-menu-item-exclude"; Action["CopyToClipboard"] = "context-menu-item-copy-to-clipboard"; Action["CopyAndEdit"] = "context-menu-item-copy-and-edit"; Action["DrillEdit"] = "context-menu-item-edit"; Action["EditMeasure"] = "context-menu-item-edit-measure"; Action["Separator"] = "context-menu-item-separator"; Action["DrillDown"] = "DRILL"; Action["RequestAccess"] = "requestAccess"; Action["QueryDetailsButtons"] = "queryDetailsButtons"; /** * @version SDK: 1.8.0 | ThoughtSpot: 8.1.0.cl */ Action["Monitor"] = "createMonitor"; /** * @version SDK: 1.8.0 | ThoughtSpot: 8.1.0.cl */ Action["AnswerDelete"] = "onDeleteAnswer"; /** * @version SDK: 1.8.0 | ThoughtSpot: 8.1.0.cl */ Action["AnswerChartSwitcher"] = "answerChartSwitcher"; /** * @version SDK: 1.8.0 | ThoughtSpot: 8.1.0.cl */ Action["AddToFavorites"] = "addToFavorites"; /** * @version SDK: 1.8.0 | ThoughtSpot: 8.1.0.cl */ Action["EditDetails"] = "editDetails"; })(exports.Action || (exports.Action = {})); // eslint-disable-next-line no-shadow var OperationType; (function (OperationType) { OperationType["GetChartWithData"] = "GetChartWithData"; OperationType["GetTableWithHeadlineData"] = "GetTableWithHeadlineData"; })(OperationType || (OperationType = {})); const ERROR_MESSAGE = { INVALID_THOUGHTSPOT_HOST: 'Error parsing ThoughtSpot host. Please provide a valid URL.', LIVEBOARD_VIZ_ID_VALIDATION: 'Please provide either liveboardId or pinboardId', }; /** * Copyright (c) 2022 * * Utilities related to reading configuration objects * * @summary Config-related utils * @author Ayon Ghosh <ayon.ghosh@thoughtspot.com> */ const urlRegex = new RegExp([ '(^(https?:)//)?', '(([^:/?#]*)(?::([0-9]+))?)', '(/{0,1}[^?#]*)', '(\\?[^#]*|)', '(#.*|)$', // hash ].join('')); /** * Parse and construct the ThoughtSpot hostname or IP address * from the embed configuration object. * @param config */ const getThoughtSpotHost = (config) => { const urlParts = config.thoughtSpotHost.match(urlRegex); if (!urlParts) { throw new Error(ERROR_MESSAGE.INVALID_THOUGHTSPOT_HOST); } const protocol = urlParts[2] || window.location.protocol; const host = urlParts[3]; let path = urlParts[6]; // Lose the trailing / if any if (path.charAt(path.length - 1) === '/') { path = path.substring(0, path.length - 1); } // const urlParams = urlParts[7]; // const hash = urlParts[8]; return `${protocol}//${host}${path}`; }; const getV2BasePath = (config) => { if (config.basepath) { return config.basepath; } const tsHost = getThoughtSpotHost(config); // This is to handle when e2e's. Search is run on pods for comp-blink-test-pipeline // with baseUrl=https://localhost:8443. // This is to handle when the developer is developing in their local environment. if (tsHost.includes('://localhost') && !tsHost.includes(':8443')) { return ''; } return 'v2'; }; /** * It is a good idea to keep URLs under 2000 chars. * If this is ever breached, since we pass view configuration through * URL params, we would like to log a warning. * Reference: https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers */ const URL_MAX_LENGTH = 2000; /** * The default CSS dimensions of the embedded app */ const DEFAULT_EMBED_WIDTH = '100%'; const DEFAULT_EMBED_HEIGHT = '100%'; var Config = { DEBUG: false, LIB_VERSION: '2.41.0' }; // since es6 imports are static and we run unit tests from the console, window won't be defined when importing this file var window$1; if (typeof(window) === 'undefined') { var loc = { hostname: '' }; window$1 = { navigator: { userAgent: '' }, document: { location: loc, referrer: '' }, screen: { width: 0, height: 0 }, location: loc }; } else { window$1 = window; } /* * Saved references to long variable names, so that closure compiler can * minimize file size. */ var ArrayProto = Array.prototype; var FuncProto = Function.prototype; var ObjProto = Object.prototype; var slice = ArrayProto.slice; var toString = ObjProto.toString; var hasOwnProperty = ObjProto.hasOwnProperty; var windowConsole = window$1.console; var navigator$1 = window$1.navigator; var document$1 = window$1.document; var windowOpera = window$1.opera; var screen = window$1.screen; var userAgent = navigator$1.userAgent; var nativeBind = FuncProto.bind; var nativeForEach = ArrayProto.forEach; var nativeIndexOf = ArrayProto.indexOf; var nativeMap = ArrayProto.map; var nativeIsArray = Array.isArray; var breaker = {}; var _ = { trim: function(str) { // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/Trim#Polyfill return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); } }; // Console override var console$1 = { /** @type {function(...*)} */ log: function() { if (Config.DEBUG && !_.isUndefined(windowConsole) && windowConsole) { try { windowConsole.log.apply(windowConsole, arguments); } catch (err) { _.each(arguments, function(arg) { windowConsole.log(arg); }); } } }, /** @type {function(...*)} */ warn: function() { if (Config.DEBUG && !_.isUndefined(windowConsole) && windowConsole) { var args = ['Mixpanel warning:'].concat(_.toArray(arguments)); try { windowConsole.warn.apply(windowConsole, args); } catch (err) { _.each(args, function(arg) { windowConsole.warn(arg); }); } } }, /** @type {function(...*)} */ error: function() { if (Config.DEBUG && !_.isUndefined(windowConsole) && windowConsole) { var args = ['Mixpanel error:'].concat(_.toArray(arguments)); try { windowConsole.error.apply(windowConsole, args); } catch (err) { _.each(args, function(arg) { windowConsole.error(arg); }); } } }, /** @type {function(...*)} */ critical: function() { if (!_.isUndefined(windowConsole) && windowConsole) { var args = ['Mixpanel error:'].concat(_.toArray(arguments)); try { windowConsole.error.apply(windowConsole, args); } catch (err) { _.each(args, function(arg) { windowConsole.error(arg); }); } } } }; var log_func_with_prefix = function(func, prefix) { return function() { arguments[0] = '[' + prefix + '] ' + arguments[0]; return func.apply(console$1, arguments); }; }; var console_with_prefix = function(prefix) { return { log: log_func_with_prefix(console$1.log, prefix), error: log_func_with_prefix(console$1.error, prefix), critical: log_func_with_prefix(console$1.critical, prefix) }; }; // UNDERSCORE // Embed part of the Underscore Library _.bind = function(func, context) { var args, bound; if (nativeBind && func.bind === nativeBind) { return nativeBind.apply(func, slice.call(arguments, 1)); } if (!_.isFunction(func)) { throw new TypeError(); } args = slice.call(arguments, 2); bound = function() { if (!(this instanceof bound)) { return func.apply(context, args.concat(slice.call(arguments))); } var ctor = {}; ctor.prototype = func.prototype; var self = new ctor(); ctor.prototype = null; var result = func.apply(self, args.concat(slice.call(arguments))); if (Object(result) === result) { return result; } return self; }; return bound; }; _.bind_instance_methods = function(obj) { for (var func in obj) { if (typeof(obj[func]) === 'function') { obj[func] = _.bind(obj[func], obj); } } }; /** * @param {*=} obj * @param {function(...*)=} iterator * @param {Object=} context */ _.each = function(obj, iterator, context) { if (obj === null || obj === undefined) { return; } if (nativeForEach && obj.forEach === nativeForEach) { obj.forEach(iterator, context); } else if (obj.length === +obj.length) { for (var i = 0, l = obj.length; i < l; i++) { if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) { return; } } } else { for (var key in obj) { if (hasOwnProperty.call(obj, key)) { if (iterator.call(context, obj[key], key, obj) === breaker) { return; } } } } }; _.escapeHTML = function(s) { var escaped = s; if (escaped && _.isString(escaped)) { escaped = escaped .replace(/&/g, '&amp;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') .replace(/"/g, '&quot;') .replace(/'/g, '&#039;'); } return escaped; }; _.extend = function(obj) { _.each(slice.call(arguments, 1), function(source) { for (var prop in source) { if (source[prop] !== void 0) { obj[prop] = source[prop]; } } }); return obj; }; _.isArray = nativeIsArray || function(obj) { return toString.call(obj) === '[object Array]'; }; // from a comment on http://dbj.org/dbj/?p=286 // fails on only one very rare and deliberate custom object: // var bomb = { toString : undefined, valueOf: function(o) { return "function BOMBA!"; }}; _.isFunction = function(f) { try { return /^\s*\bfunction\b/.test(f); } catch (x) { return false; } }; _.isArguments = function(obj) { return !!(obj && hasOwnProperty.call(obj, 'callee')); }; _.toArray = function(iterable) { if (!iterable) { return []; } if (iterable.toArray) { return iterable.toArray(); } if (_.isArray(iterable)) { return slice.call(iterable); } if (_.isArguments(iterable)) { return slice.call(iterable); } return _.values(iterable); }; _.map = function(arr, callback, context) { if (nativeMap && arr.map === nativeMap) { return arr.map(callback, context); } else { var results = []; _.each(arr, function(item) { results.push(callback.call(context, item)); }); return results; } }; _.keys = function(obj) { var results = []; if (obj === null) { return results; } _.each(obj, function(value, key) { results[results.length] = key; }); return results; }; _.values = function(obj) { var results = []; if (obj === null) { return results; } _.each(obj, function(value) { results[results.length] = value; }); return results; }; _.include = function(obj, target) { var found = false; if (obj === null) { return found; } if (nativeIndexOf && obj.indexOf === nativeIndexOf) { return obj.indexOf(target) != -1; } _.each(obj, function(value) { if (found || (found = (value === target))) { return breaker; } }); return found; }; _.includes = function(str, needle) { return str.indexOf(needle) !== -1; }; // Underscore Addons _.inherit = function(subclass, superclass) { subclass.prototype = new superclass(); subclass.prototype.constructor = subclass; subclass.superclass = superclass.prototype; return subclass; }; _.isObject = function(obj) { return (obj === Object(obj) && !_.isArray(obj)); }; _.isEmptyObject = function(obj) { if (_.isObject(obj)) { for (var key in obj) { if (hasOwnProperty.call(obj, key)) { return false; } } return true; } return false; }; _.isUndefined = function(obj) { return obj === void 0; }; _.isString = function(obj) { return toString.call(obj) == '[object String]'; }; _.isDate = function(obj) { return toString.call(obj) == '[object Date]'; }; _.isNumber = function(obj) { return toString.call(obj) == '[object Number]'; }; _.isElement = function(obj) { return !!(obj && obj.nodeType === 1); }; _.encodeDates = function(obj) { _.each(obj, function(v, k) { if (_.isDate(v)) { obj[k] = _.formatDate(v); } else if (_.isObject(v)) { obj[k] = _.encodeDates(v); // recurse } }); return obj; }; _.timestamp = function() { Date.now = Date.now || function() { return +new Date; }; return Date.now(); }; _.formatDate = function(d) { // YYYY-MM-DDTHH:MM:SS in UTC function pad(n) { return n < 10 ? '0' + n : n; } return d.getUTCFullYear() + '-' + pad(d.getUTCMonth() + 1) + '-' + pad(d.getUTCDate()) + 'T' + pad(d.getUTCHours()) + ':' + pad(d.getUTCMinutes()) + ':' + pad(d.getUTCSeconds()); }; _.safewrap = function(f) { return function() { try { return f.apply(this, arguments); } catch (e) { console$1.critical('Implementation error. Please turn on debug and contact support@mixpanel.com.'); if (Config.DEBUG){ console$1.critical(e); } } }; }; _.safewrap_class = function(klass, functions) { for (var i = 0; i < functions.length; i++) { klass.prototype[functions[i]] = _.safewrap(klass.prototype[functions[i]]); } }; _.safewrap_instance_methods = function(obj) { for (var func in obj) { if (typeof(obj[func]) === 'function') { obj[func] = _.safewrap(obj[func]); } } }; _.strip_empty_properties = function(p) { var ret = {}; _.each(p, function(v, k) { if (_.isString(v) && v.length > 0) { ret[k] = v; } }); return ret; }; /* * this function returns a copy of object after truncating it. If * passed an Array or Object it will iterate through obj and * truncate all the values recursively. */ _.truncate = function(obj, length) { var ret; if (typeof(obj) === 'string') { ret = obj.slice(0, length); } else if (_.isArray(obj)) { ret = []; _.each(obj, function(val) { ret.push(_.truncate(val, length)); }); } else if (_.isObject(obj)) { ret = {}; _.each(obj, function(val, key) { ret[key] = _.truncate(val, length); }); } else { ret = obj; } return ret; }; _.JSONEncode = (function() { return function(mixed_val) { var value = mixed_val; var quote = function(string) { var escapable = /[\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; // eslint-disable-line no-control-regex var meta = { // table of character substitutions '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"': '\\"', '\\': '\\\\' }; escapable.lastIndex = 0; return escapable.test(string) ? '"' + string.replace(escapable, function(a) { var c = meta[a]; return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); }) + '"' : '"' + string + '"'; }; var str = function(key, holder) { var gap = ''; var indent = ' '; var i = 0; // The loop counter. var k = ''; // The member key. var v = ''; // The member value. var length = 0; var mind = gap; var partial = []; var value = holder[key]; // If the value has a toJSON method, call it to obtain a replacement value. if (value && typeof value === 'object' && typeof value.toJSON === 'function') { value = value.toJSON(key); } // What happens next depends on the value's type. switch (typeof value) { case 'string': return quote(value); case 'number': // JSON numbers must be finite. Encode non-finite numbers as null. return isFinite(value) ? String(value) : 'null'; case 'boolean': case 'null': // If the value is a boolean or null, convert it to a string. Note: // typeof null does not produce 'null'. The case is included here in // the remote chance that this gets fixed someday. return String(value); case 'object': // If the type is 'object', we might be dealing with an object or an array or // null. // Due to a specification blunder in ECMAScript, typeof null is 'object', // so watch out for that case. if (!value) { return 'null'; } // Make an array to hold the partial results of stringifying this object value. gap += indent; partial = []; // Is the value an array? if (toString.apply(value) === '[object Array]') { // The value is an array. Stringify every element. Use null as a placeholder // for non-JSON values. length = value.length; for (i = 0; i < length; i += 1) { partial[i] = str(i, value) || 'null'; } // Join all of the elements together, separated with commas, and wrap them in // brackets. v = partial.length === 0 ? '[]' : gap ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : '[' + partial.join(',') + ']'; gap = mind; return v; } // Iterate through all of the keys in the object. for (k in value) { if (hasOwnProperty.call(value, k)) { v = str(k, value); if (v) { partial.push(quote(k) + (gap ? ': ' : ':') + v); } } } // Join all of the member texts together, separated with commas, // and wrap them in braces. v = partial.length === 0 ? '{}' : gap ? '{' + partial.join(',') + '' + mind + '}' : '{' + partial.join(',') + '}'; gap = mind; return v; } }; // Make a fake root object containing our value under the key of ''. // Return the result of stringifying the value. return str('', { '': value }); }; })(); /** * From https://github.com/douglascrockford/JSON-js/blob/master/json_parse.js * Slightly modified to throw a real Error rather than a POJO */ _.JSONDecode = (function() { var at, // The index of the current character ch, // The current character escapee = { '"': '"', '\\': '\\', '/': '/', 'b': '\b', 'f': '\f', 'n': '\n', 'r': '\r', 't': '\t' }, text, error = function(m) { var e = new SyntaxError(m); e.at = at; e.text = text; throw e; }, next = function(c) { // If a c parameter is provided, verify that it matches the current character. if (c && c !== ch) { error('Expected \'' + c + '\' instead of \'' + ch + '\''); } // Get the next character. When there are no more characters, // return the empty string. ch = text.charAt(at); at += 1; return ch; }, number = function() { // Parse a number value. var number, string = ''; if (ch === '-') { string = '-'; next('-'); } while (ch >= '0' && ch <= '9') { string += ch; next(); } if (ch === '.') { string += '.'; while (next() && ch >= '0' && ch <= '9') { string += ch; } } if (ch === 'e' || ch === 'E') { string += ch; next(); if (ch === '-' || ch === '+') { string += ch; next(); } while (ch >= '0' && ch <= '9') { string += ch; next(); } } number = +string; if (!isFinite(number)) { error('Bad number'); } else { return number; } }, string = function() { // Parse a string value. var hex, i, string = '', uffff; // When parsing for string values, we must look for " and \ characters. if (ch === '"') { while (next()) { if (ch === '"') { next(); return string; } if (ch === '\\') { next(); if (ch === 'u') { uffff = 0; for (i = 0; i < 4; i += 1) { hex = parseInt(next(), 16); if (!isFinite(hex)) { break; } uffff = uffff * 16 + hex; } string += String.fromCharCode(uffff); } else if (typeof escapee[ch] === 'string') { string += escapee[ch]; } else { break; } } else { string += ch; } } } error('Bad string'); }, white = function() { // Skip whitespace. while (ch && ch <= ' ') { next(); } }, word = function() { // true, false, or null. switch (ch) { case 't': next('t'); next('r'); next('u'); next('e'); return true; case 'f': next('f'); next('a'); next('l'); next('s'); next('e'); return false; case 'n': next('n'); next('u'); next('l'); next('l'); return null; } error('Unexpected "' + ch + '"'); }, value, // Placeholder for the value function. array = function() { // Parse an array value. var array = []; if (ch === '[') { next('['); white(); if (ch === ']') { next(']'); return array; // empty array } while (ch) { array.push(value()); white(); if (ch === ']') { next(']'); return array; } next(','); white(); } } error('Bad array'); }, object = function() { // Parse an object value. var key, object = {}; if (ch === '{') { next('{'); white(); if (ch === '}') { next('}'); return object; // empty object } while (ch) { key = string(); white(); next(':'); if (Object.hasOwnProperty.call(object, key)) { error('Duplicate key "' + key + '"'); } object[key] = value(); white(); if (ch === '}') { next('}'); return object; } next(','); white(); } } error('Bad object'); }; value = function() { // Parse a JSON value. It could be an object, an array, a string, // a number, or a word. white(); switch (ch) { case '{': return object(); case '[': return array(); case '"': return string(); case '-': return number(); default: return ch >= '0' && ch <= '9' ? number() : word(); } }; // Return the json_parse function. It will have access to all of the // above functions and variables. return function(source) { var result; text = source; at = 0; ch = ' '; result = value(); white(); if (ch) { error('Syntax error'); } return result; }; })(); _.base64Encode = function(data) { var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, ac = 0, enc = '', tmp_arr = []; if (!data) { return data; } data = _.utf8Encode(data); do { // pack three octets into four hexets o1 = data.charCodeAt(i++); o2 = data.charCodeAt(i++); o3 = data.charCodeAt(i++); bits = o1 << 16 | o2 << 8 | o3; h1 = bits >> 18 & 0x3f; h2 = bits >> 12 & 0x3f; h3 = bits >> 6 & 0x3f; h4 = bits & 0x3f; // use hexets to index into b64, and append result to encoded string tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4); } while (i < data.length); enc = tmp_arr.join(''); switch (data.length % 3) { case 1: enc = enc.slice(0, -2) + '=='; break; case 2: enc = enc.slice(0, -1) + '='; break; } return enc; }; _.utf8Encode = function(string) { string = (string + '').replace(/\r\n/g, '\n').replace(/\r/g, '\n'); var utftext = '', start, end; var stringl = 0, n;