diff --git a/CHANGELOG.md b/CHANGELOG.md index 77d9633a..cad748e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ## Unreleased +* `productId` is no longer a required field for `Revenue` logged via `logRevenueV2`. +* Track raw user agent string for backend filtering. + ### 3.0.1 (June 22, 2016) * Update README with link to our [Google Tag Manager integration demo app](https://github.com/amplitude/GTM-Web-Demo). diff --git a/README.md b/README.md index 228eb85b..307a7810 100644 --- a/README.md +++ b/README.md @@ -270,7 +270,7 @@ var revenue = new amplitude.Revenue().setProductId('com.company.productId').setP amplitude.getInstance().logRevenueV2(revenue); ``` -`productId` and `price` are required fields. `quantity` defaults to 1 if not specified. Each field has a corresponding `set` method (for example `setProductId`, `setQuantity`, etc). This table describes the different fields available: +`price` is a required field. `quantity` defaults to 1 if not specified. Each field has a corresponding `set` method (for example `setProductId`, `setQuantity`, etc). This table describes the different fields available: | Name | Type | Description | default | |--------------------|------------|----------------------------------------------------------------------------------------------------------|---------| diff --git a/amplitude.js b/amplitude.js index 1bc84035..088f5181 100644 --- a/amplitude.js +++ b/amplitude.js @@ -522,6 +522,8 @@ var AmplitudeClient = function AmplitudeClient(instanceName) { this._newSession = false; this._sequenceNumber = 0; this._sessionId = null; + + this._userAgent = (navigator && navigator.userAgent) || null; }; AmplitudeClient.prototype.Identify = Identify; @@ -1279,7 +1281,8 @@ AmplitudeClient.prototype._logEvent = function _logEvent(eventType, eventPropert version: version }, sequence_number: sequenceNumber, // for ordering events and identifys - groups: utils.truncate(utils.validateGroups(groups)) + groups: utils.truncate(utils.validateGroups(groups)), + user_agent: this._userAgent // country: null }; @@ -3813,14 +3816,14 @@ var utils = require('./utils'); /* * Wrapper for logging Revenue data. Revenue objects get passed to amplitude.logRevenueV2 to send to Amplitude servers. - * Note: productId and price are required fields. If quantity is not specified, then defaults to 1. + * Note: price is the only required field. If quantity is not specified, then defaults to 1. */ /** * Revenue API - instance constructor. Revenue objects are a wrapper for revenue data. * Each method updates a revenue property in the Revenue object, and returns the same Revenue object, * allowing you to chain multiple method calls together. - * Note: productId and price are required fields to log revenue events. + * Note: price is a required field to log revenue events. * If quantity is not specified then defaults to 1. * See [Readme]{@link https://github.com/amplitude/Amplitude-Javascript#tracking-revenue} for more information * about logging Revenue. @@ -3830,17 +3833,17 @@ var utils = require('./utils'); */ var Revenue = function Revenue() { // required fields - this._productId = null; - this._quantity = 1; this._price = null; // optional fields + this._productId = null; + this._quantity = 1; this._revenueType = null; this._properties = null; }; /** - * Set a value for the product identifer. This field is required for all revenue being logged. + * Set a value for the product identifer. * @public * @param {string} productId - The value for the product identifier. Empty and invalid strings are ignored. * @return {Revenue} Returns the same Revenue object, allowing you to chain multiple method calls together. @@ -3932,10 +3935,6 @@ Revenue.prototype.setEventProperties = function setEventProperties(eventProperti * @private */ Revenue.prototype._isValidRevenue = function _isValidRevenue() { - if (type(this._productId) !== 'string' || utils.isEmptyString(this._productId)) { - utils.log('Invalid revenue, need to set productId field'); - return false; - } if (type(this._price) !== 'number') { utils.log('Invalid revenue, need to set price field'); return false; diff --git a/amplitude.min.js b/amplitude.min.js index 9afbe8c5..1c6328d8 100644 --- a/amplitude.min.js +++ b/amplitude.min.js @@ -1,3 +1,3 @@ -(function umd(require){if("object"==typeof exports){module.exports=require("1")}else if("function"==typeof define&&define.amd){define(function(){return require("1")})}else{this["amplitude"]=require("1")}})(function outer(modules,cache,entries){var global=function(){return this}();function require(name,jumped){if(cache[name])return cache[name].exports;if(modules[name])return call(name,require);throw new Error('cannot find module "'+name+'"')}function call(id,require){var m=cache[id]={exports:{}};var mod=modules[id];var name=mod[2];var fn=mod[0];fn.call(m.exports,function(req){var dep=modules[id][1][req];return require(dep?dep:req)},m,m.exports,outer,modules,cache,entries);if(name)cache[name]=cache[id];return cache[id].exports}for(var id in entries){if(entries[id]){global[entries[id]]=require(id)}else{require(id)}}require.duo=true;require.cache=cache;require.modules=modules;return require}({1:[function(require,module,exports){var Amplitude=require("./amplitude");var old=window.amplitude||{};var newInstance=new Amplitude;newInstance._q=old._q||[];for(var instance in old._iq){if(old._iq.hasOwnProperty(instance)){newInstance.getInstance(instance)._q=old._iq[instance]._q||[]}}module.exports=newInstance},{"./amplitude":2}],2:[function(require,module,exports){var AmplitudeClient=require("./amplitude-client");var Constants=require("./constants");var Identify=require("./identify");var object=require("object");var Revenue=require("./revenue");var type=require("./type");var utils=require("./utils");var version=require("./version");var DEFAULT_OPTIONS=require("./options");var Amplitude=function Amplitude(){this.options=object.merge({},DEFAULT_OPTIONS);this._q=[];this._instances={}};Amplitude.prototype.Identify=Identify;Amplitude.prototype.Revenue=Revenue;Amplitude.prototype.getInstance=function getInstance(instance){instance=utils.isEmptyString(instance)?Constants.DEFAULT_INSTANCE:instance.toLowerCase();var client=this._instances[instance];if(client===undefined){client=new AmplitudeClient(instance);this._instances[instance]=client}return client};Amplitude.prototype.init=function init(apiKey,opt_userId,opt_config,opt_callback){this.getInstance().init(apiKey,opt_userId,opt_config,function(instance){this.options=instance.options;if(type(opt_callback)==="function"){opt_callback(instance)}}.bind(this))};Amplitude.prototype.runQueuedFunctions=function(){for(var i=0;ithis.options.sessionTimeout){this._newSession=true;this._sessionId=now;if(this.options.includeUtm){this._initUtmData()}if(this.options.includeReferrer){this._saveReferrer(this._getReferrer())}}this._lastEventTime=now;_saveCookieData(this);if(this.options.saveEvents){this._unsentEvents=this._loadSavedUnsentEvents(this.options.unsentKey);this._unsentIdentifys=this._loadSavedUnsentEvents(this.options.unsentIdentifyKey);for(var i=0;i0){options[key]=inputValue}};for(var key in config){if(config.hasOwnProperty(key)){parseValidateAndLoad(key)}}};AmplitudeClient.prototype.runQueuedFunctions=function(){for(var i=0;i=this.options.eventUploadThreshold){this.sendEvents(callback);return true}if(!this._updateScheduled){this._updateScheduled=true;setTimeout(function(){this._updateScheduled=false;this.sendEvents()}.bind(this),this.options.eventUploadPeriodMillis)}return false};AmplitudeClient.prototype._getFromStorage=function _getFromStorage(storage,key){return storage.getItem(key+this._storageSuffix)};AmplitudeClient.prototype._setInStorage=function _setInStorage(storage,key,value){storage.setItem(key+this._storageSuffix,value)};var _upgradeCookeData=function _upgradeCookeData(scope){var cookieData=scope.cookieStorage.get(scope.options.cookieName);if(type(cookieData)==="object"&&cookieData.deviceId&&cookieData.sessionId&&cookieData.lastEventTime){return}var _getAndRemoveFromLocalStorage=function _getAndRemoveFromLocalStorage(key){var value=localStorage.getItem(key);localStorage.removeItem(key);return value};var apiKeySuffix=type(scope.options.apiKey)==="string"&&"_"+scope.options.apiKey.slice(0,6)||"";var localStorageDeviceId=_getAndRemoveFromLocalStorage(Constants.DEVICE_ID+apiKeySuffix);var localStorageUserId=_getAndRemoveFromLocalStorage(Constants.USER_ID+apiKeySuffix);var localStorageOptOut=_getAndRemoveFromLocalStorage(Constants.OPT_OUT+apiKeySuffix);if(localStorageOptOut!==null&&localStorageOptOut!==undefined){localStorageOptOut=String(localStorageOptOut)==="true"}var localStorageSessionId=parseInt(_getAndRemoveFromLocalStorage(Constants.SESSION_ID));var localStorageLastEventTime=parseInt(_getAndRemoveFromLocalStorage(Constants.LAST_EVENT_TIME));var localStorageEventId=parseInt(_getAndRemoveFromLocalStorage(Constants.LAST_EVENT_ID));var localStorageIdentifyId=parseInt(_getAndRemoveFromLocalStorage(Constants.LAST_IDENTIFY_ID));var localStorageSequenceNumber=parseInt(_getAndRemoveFromLocalStorage(Constants.LAST_SEQUENCE_NUMBER));var _getFromCookie=function _getFromCookie(key){return type(cookieData)==="object"&&cookieData[key]};scope.options.deviceId=_getFromCookie("deviceId")||localStorageDeviceId;scope.options.userId=_getFromCookie("userId")||localStorageUserId;scope._sessionId=_getFromCookie("sessionId")||localStorageSessionId||scope._sessionId;scope._lastEventTime=_getFromCookie("lastEventTime")||localStorageLastEventTime||scope._lastEventTime;scope._eventId=_getFromCookie("eventId")||localStorageEventId||scope._eventId;scope._identifyId=_getFromCookie("identifyId")||localStorageIdentifyId||scope._identifyId;scope._sequenceNumber=_getFromCookie("sequenceNumber")||localStorageSequenceNumber||scope._sequenceNumber;scope.options.optOut=localStorageOptOut||false;if(cookieData&&cookieData.optOut!==undefined&&cookieData.optOut!==null){scope.options.optOut=String(cookieData.optOut)==="true"}_saveCookieData(scope)};var _loadCookieData=function _loadCookieData(scope){var cookieData=scope.cookieStorage.get(scope.options.cookieName+scope._storageSuffix);if(type(cookieData)==="object"){if(cookieData.deviceId){scope.options.deviceId=cookieData.deviceId}if(cookieData.userId){scope.options.userId=cookieData.userId}if(cookieData.optOut!==null&&cookieData.optOut!==undefined){scope.options.optOut=cookieData.optOut}if(cookieData.sessionId){scope._sessionId=parseInt(cookieData.sessionId)}if(cookieData.lastEventTime){scope._lastEventTime=parseInt(cookieData.lastEventTime)}if(cookieData.eventId){scope._eventId=parseInt(cookieData.eventId)}if(cookieData.identifyId){scope._identifyId=parseInt(cookieData.identifyId)}if(cookieData.sequenceNumber){scope._sequenceNumber=parseInt(cookieData.sequenceNumber)}}};var _saveCookieData=function _saveCookieData(scope){scope.cookieStorage.set(scope.options.cookieName+scope._storageSuffix,{deviceId:scope.options.deviceId,userId:scope.options.userId,optOut:scope.options.optOut,sessionId:scope._sessionId,lastEventTime:scope._lastEventTime,eventId:scope._eventId,identifyId:scope._identifyId,sequenceNumber:scope._sequenceNumber})};AmplitudeClient.prototype._initUtmData=function _initUtmData(queryParams,cookieParams){queryParams=queryParams||location.search;cookieParams=cookieParams||this.cookieStorage.get("__utmz");var utmProperties=getUtmData(cookieParams,queryParams);_sendUserPropertiesOncePerSession(this,Constants.UTM_PROPERTIES,utmProperties)};var _sendUserPropertiesOncePerSession=function _sendUserPropertiesOncePerSession(scope,storageKey,userProperties){if(type(userProperties)!=="object"||Object.keys(userProperties).length===0){return}var identify=new Identify;for(var key in userProperties){if(userProperties.hasOwnProperty(key)){identify.setOnce("initial_"+key,userProperties[key])}}var hasSessionStorage=utils.sessionStorageEnabled();if(hasSessionStorage&&!scope._getFromStorage(sessionStorage,storageKey)||!hasSessionStorage){for(var property in userProperties){if(userProperties.hasOwnProperty(property)){identify.set(property,userProperties[property])}}if(hasSessionStorage){scope._setInStorage(sessionStorage,storageKey,JSON.stringify(userProperties))}}scope.identify(identify)};AmplitudeClient.prototype._getReferrer=function _getReferrer(){return document.referrer};AmplitudeClient.prototype._getReferringDomain=function _getReferringDomain(referrer){if(utils.isEmptyString(referrer)){return null}var parts=referrer.split("/");if(parts.length>=3){return parts[2]}return null};AmplitudeClient.prototype._saveReferrer=function _saveReferrer(referrer){if(utils.isEmptyString(referrer)){return}var referrerInfo={referrer:referrer,referring_domain:this._getReferringDomain(referrer)};_sendUserPropertiesOncePerSession(this,Constants.REFERRER,referrerInfo)};AmplitudeClient.prototype.saveEvents=function saveEvents(){try{this._setInStorage(localStorage,this.options.unsentKey,JSON.stringify(this._unsentEvents))}catch(e){}try{this._setInStorage(localStorage,this.options.unsentIdentifyKey,JSON.stringify(this._unsentIdentifys))}catch(e){}};AmplitudeClient.prototype.setDomain=function setDomain(domain){if(!utils.validateInput(domain,"domain","string")){return}try{this.cookieStorage.options({domain:domain});this.options.domain=this.cookieStorage.options().domain;_loadCookieData(this);_saveCookieData(this)}catch(e){utils.log(e)}};AmplitudeClient.prototype.setUserId=function setUserId(userId){try{this.options.userId=userId!==undefined&&userId!==null&&""+userId||null;_saveCookieData(this)}catch(e){utils.log(e)}};AmplitudeClient.prototype.setGroup=function(groupType,groupName){if(!this._apiKeySet("setGroup()")||!utils.validateInput(groupType,"groupType","string")||utils.isEmptyString(groupType)){return}var groups={};groups[groupType]=groupName;var identify=(new Identify).set(groupType,groupName);this._logEvent(Constants.IDENTIFY_EVENT,null,null,identify.userPropertiesOperations,groups,null)};AmplitudeClient.prototype.setOptOut=function setOptOut(enable){if(!utils.validateInput(enable,"enable","boolean")){return}try{this.options.optOut=enable;_saveCookieData(this)}catch(e){utils.log(e)}};AmplitudeClient.prototype.regenerateDeviceId=function regenerateDeviceId(){this.setDeviceId(UUID()+"R")};AmplitudeClient.prototype.setDeviceId=function setDeviceId(deviceId){if(!utils.validateInput(deviceId,"deviceId","string")){return}try{if(!utils.isEmptyString(deviceId)){this.options.deviceId=""+deviceId;_saveCookieData(this)}}catch(e){utils.log(e)}};AmplitudeClient.prototype.setUserProperties=function setUserProperties(userProperties){if(!this._apiKeySet("setUserProperties()")||!utils.validateInput(userProperties,"userProperties","object")){return}var identify=new Identify;for(var property in userProperties){if(userProperties.hasOwnProperty(property)){identify.set(property,userProperties[property])}}this.identify(identify)};AmplitudeClient.prototype.clearUserProperties=function clearUserProperties(){if(!this._apiKeySet("clearUserProperties()")){return}var identify=new Identify;identify.clearAll();this.identify(identify)};var _convertProxyObjectToRealObject=function _convertProxyObjectToRealObject(instance,proxy){for(var i=0;i0){return this._logEvent(Constants.IDENTIFY_EVENT,null,null,identify_obj.userPropertiesOperations,null,opt_callback)}}else{utils.log("Invalid identify input type. Expected Identify object but saw "+type(identify_obj))}if(type(opt_callback)==="function"){opt_callback(0,"No request sent")}};AmplitudeClient.prototype.setVersionName=function setVersionName(versionName){if(!utils.validateInput(versionName,"versionName","string")){return}this.options.versionName=versionName};AmplitudeClient.prototype._logEvent=function _logEvent(eventType,eventProperties,apiProperties,userProperties,groups,callback){_loadCookieData(this);if(!eventType||this.options.optOut){if(type(callback)==="function"){callback(0,"No request sent")}return}try{var eventId;if(eventType===Constants.IDENTIFY_EVENT){eventId=this.nextIdentifyId()}else{eventId=this.nextEventId()}var sequenceNumber=this.nextSequenceNumber();var eventTime=(new Date).getTime();if(!this._sessionId||!this._lastEventTime||eventTime-this._lastEventTime>this.options.sessionTimeout){this._sessionId=eventTime}this._lastEventTime=eventTime;_saveCookieData(this);userProperties=userProperties||{};apiProperties=apiProperties||{};eventProperties=eventProperties||{};groups=groups||{};var event={device_id:this.options.deviceId,user_id:this.options.userId,timestamp:eventTime,event_id:eventId,session_id:this._sessionId||-1,event_type:eventType,version_name:this.options.versionName||null,platform:this.options.platform,os_name:this._ua.browser.name||null,os_version:this._ua.browser.major||null,device_model:this._ua.os.name||null,language:this.options.language,api_properties:apiProperties,event_properties:utils.truncate(utils.validateProperties(eventProperties)),user_properties:utils.truncate(utils.validateProperties(userProperties)),uuid:UUID(),library:{name:"amplitude-js",version:version},sequence_number:sequenceNumber,groups:utils.truncate(utils.validateGroups(groups))};if(eventType===Constants.IDENTIFY_EVENT){this._unsentIdentifys.push(event);this._limitEventsQueued(this._unsentIdentifys)}else{this._unsentEvents.push(event);this._limitEventsQueued(this._unsentEvents)}if(this.options.saveEvents){this.saveEvents()}if(!this._sendEventsIfReady(callback)&&type(callback)==="function"){callback(0,"No request sent")}return eventId}catch(e){utils.log(e)}};AmplitudeClient.prototype._limitEventsQueued=function _limitEventsQueued(queue){if(queue.length>this.options.savedMaxCount){queue.splice(0,queue.length-this.options.savedMaxCount)}};AmplitudeClient.prototype.logEvent=function logEvent(eventType,eventProperties,opt_callback){if(!this._apiKeySet("logEvent()")||!utils.validateInput(eventType,"eventType","string")||utils.isEmptyString(eventType)){if(type(opt_callback)==="function"){opt_callback(0,"No request sent")}return-1}return this._logEvent(eventType,eventProperties,null,null,null,opt_callback)};AmplitudeClient.prototype.logEventWithGroups=function(eventType,eventProperties,groups,opt_callback){if(!this._apiKeySet("logEventWithGroup()")||!utils.validateInput(eventType,"eventType","string")){if(type(opt_callback)==="function"){opt_callback(0,"No request sent")}return-1}return this._logEvent(eventType,eventProperties,null,null,groups,opt_callback)};var _isNumber=function _isNumber(n){return!isNaN(parseFloat(n))&&isFinite(n)};AmplitudeClient.prototype.logRevenueV2=function logRevenueV2(revenue_obj){if(!this._apiKeySet("logRevenueV2()")){return}if(type(revenue_obj)==="object"&&revenue_obj.hasOwnProperty("_q")){revenue_obj=_convertProxyObjectToRealObject(new Revenue,revenue_obj)}if(revenue_obj instanceof Revenue){if(revenue_obj&&revenue_obj._isValidRevenue()){return this.logEvent(Constants.REVENUE_EVENT,revenue_obj._toJSONObject())}}else{utils.log("Invalid revenue input type. Expected Revenue object but saw "+type(revenue_obj))}};AmplitudeClient.prototype.logRevenue=function logRevenue(price,quantity,product){if(!this._apiKeySet("logRevenue()")||!_isNumber(price)||quantity!==undefined&&!_isNumber(quantity)){return-1}return this._logEvent(Constants.REVENUE_EVENT,{},{productId:product,special:"revenue_amount",quantity:quantity||1,price:price},null,null,null)};AmplitudeClient.prototype.removeEvents=function removeEvents(maxEventId,maxIdentifyId){_removeEvents(this,"_unsentEvents",maxEventId);_removeEvents(this,"_unsentIdentifys",maxIdentifyId)};var _removeEvents=function _removeEvents(scope,eventQueue,maxId){if(maxId<0){return}var filteredEvents=[];for(var i=0;imaxId){filteredEvents.push(scope[eventQueue][i])}}scope[eventQueue]=filteredEvents};AmplitudeClient.prototype.sendEvents=function sendEvents(callback){if(!this._apiKeySet("sendEvents()")||this._sending||this.options.optOut||this._unsentCount()===0){if(type(callback)==="function"){callback(0,"No request sent")}return}this._sending=true;var url=("https:"===window.location.protocol?"https":"http")+"://"+this.options.apiEndpoint+"/";var numEvents=Math.min(this._unsentCount(),this.options.uploadBatchSize);var mergedEvents=this._mergeEventsAndIdentifys(numEvents);var maxEventId=mergedEvents.maxEventId;var maxIdentifyId=mergedEvents.maxIdentifyId;var events=JSON.stringify(mergedEvents.eventsToSend);var uploadTime=(new Date).getTime();var data={client:this.options.apiKey,e:events,v:Constants.API_VERSION,upload_time:uploadTime,checksum:md5(Constants.API_VERSION+this.options.apiKey+events+uploadTime)};var scope=this;new Request(url,data).send(function(status,response){scope._sending=false;try{if(status===200&&response==="success"){scope.removeEvents(maxEventId,maxIdentifyId);if(scope.options.saveEvents){scope.saveEvents()}if(!scope._sendEventsIfReady(callback)&&type(callback)==="function"){callback(status,response)}}else if(status===413){if(scope.options.uploadBatchSize===1){scope.removeEvents(maxEventId,maxIdentifyId)}scope.options.uploadBatchSize=Math.ceil(numEvents/2);scope.sendEvents(callback)}else if(type(callback)==="function"){callback(status,response)}}catch(e){}})};AmplitudeClient.prototype._mergeEventsAndIdentifys=function _mergeEventsAndIdentifys(numEvents){var eventsToSend=[];var eventIndex=0;var maxEventId=-1;var identifyIndex=0;var maxIdentifyId=-1;while(eventsToSend.length=this._unsentIdentifys.length;var noEvents=eventIndex>=this._unsentEvents.length;if(noEvents&&noIdentifys){utils.log("Merging Events and Identifys, less events and identifys than expected");break}else if(noIdentifys){event=this._unsentEvents[eventIndex++];maxEventId=event.event_id}else if(noEvents){event=this._unsentIdentifys[identifyIndex++];maxIdentifyId=event.event_id}else{if(!("sequence_number"in this._unsentEvents[eventIndex])||this._unsentEvents[eventIndex].sequence_number>2;enc2=(chr1&3)<<4|chr2>>4;enc3=(chr2&15)<<2|chr3>>6; -enc4=chr3&63;if(isNaN(chr2)){enc3=enc4=64}else if(isNaN(chr3)){enc4=64}output=output+Base64._keyStr.charAt(enc1)+Base64._keyStr.charAt(enc2)+Base64._keyStr.charAt(enc3)+Base64._keyStr.charAt(enc4)}return output},decode:function(input){try{if(window.btoa&&window.atob){return decodeURIComponent(escape(window.atob(input)))}}catch(e){}return Base64._decode(input)},_decode:function(input){var output="";var chr1,chr2,chr3;var enc1,enc2,enc3,enc4;var i=0;input=input.replace(/[^A-Za-z0-9\+\/\=]/g,"");while(i>4;chr2=(enc2&15)<<4|enc3>>2;chr3=(enc3&3)<<6|enc4;output=output+String.fromCharCode(chr1);if(enc3!==64){output=output+String.fromCharCode(chr2)}if(enc4!==64){output=output+String.fromCharCode(chr3)}}output=UTF8.decode(output);return output}};module.exports=Base64},{"./utf8":23}],23:[function(require,module,exports){var UTF8={encode:function(s){var utftext="";for(var n=0;n127&&c<2048){utftext+=String.fromCharCode(c>>6|192);utftext+=String.fromCharCode(c&63|128)}else{utftext+=String.fromCharCode(c>>12|224);utftext+=String.fromCharCode(c>>6&63|128);utftext+=String.fromCharCode(c&63|128)}}return utftext},decode:function(utftext){var s="";var i=0;var c=0,c1=0,c2=0;while(i191&&c<224){c1=utftext.charCodeAt(i+1);s+=String.fromCharCode((c&31)<<6|c1&63);i+=2}else{c1=utftext.charCodeAt(i+1);c2=utftext.charCodeAt(i+2);s+=String.fromCharCode((c&15)<<12|(c1&63)<<6|c2&63);i+=3}}return s}};module.exports=UTF8},{}],14:[function(require,module,exports){var json=window.JSON||{};var stringify=json.stringify;var parse=json.parse;module.exports=parse&&stringify?JSON:require("json-fallback")},{"json-fallback":24}],24:[function(require,module,exports){(function(){"use strict";var JSON=module.exports={};function f(n){return n<10?"0"+n:n}if(typeof Date.prototype.toJSON!=="function"){Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){return this.valueOf()}}var cx,escapable,gap,indent,meta,rep;function quote(string){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+'"'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==="object"&&typeof value.toJSON==="function"){value=value.toJSON(key)}if(typeof rep==="function"){value=rep.call(holder,key,value)}switch(typeof value){case"string":return quote(value);case"number":return isFinite(value)?String(value):"null";case"boolean":case"null":return String(value);case"object":if(!value){return"null"}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==="[object Array]"){length=value.length;for(i=0;iconstants.MAX_STRING_LENGTH?value.substring(0,constants.MAX_STRING_LENGTH):value}return value};var validateInput=function validateInput(input,name,expectedType){if(type(input)!==expectedType){log("Invalid "+name+" input type. Expected "+expectedType+" but received "+type(input));return false}return true};var validateProperties=function validateProperties(properties){var propsType=type(properties);if(propsType!=="object"){log("Error: invalid event properties format. Expecting Javascript object, received "+propsType+", ignoring");return{}}var copy={};for(var property in properties){if(!properties.hasOwnProperty(property)){continue}var key=property;var keyType=type(key);if(keyType!=="string"){key=String(key);log("WARNING: Non-string property key, received type "+keyType+', coercing to string "'+key+'"')}var value=validatePropertyValue(key,properties[property]);if(value===null){continue}copy[key]=value}return copy};var invalidValueTypes=["null","nan","undefined","function","arguments","regexp","element"];var validatePropertyValue=function validatePropertyValue(key,value){var valueType=type(value);if(invalidValueTypes.indexOf(valueType)!==-1){log('WARNING: Property key "'+key+'" with invalid value type '+valueType+", ignoring");value=null}else if(valueType==="error"){value=String(value);log('WARNING: Property key "'+key+'" with value type error, coercing to '+value)}else if(valueType==="array"){var arrayCopy=[];for(var i=0;i0){if(!this.userPropertiesOperations.hasOwnProperty(AMP_OP_CLEAR_ALL)){utils.log("Need to send $clearAll on its own Identify object without any other operations, skipping $clearAll")}return this}this.userPropertiesOperations[AMP_OP_CLEAR_ALL]="-";return this};Identify.prototype.prepend=function(property,value){this._addOperation(AMP_OP_PREPEND,property,value);return this};Identify.prototype.set=function(property,value){this._addOperation(AMP_OP_SET,property,value);return this};Identify.prototype.setOnce=function(property,value){this._addOperation(AMP_OP_SET_ONCE,property,value);return this};Identify.prototype.unset=function(property){this._addOperation(AMP_OP_UNSET,property,"-");return this};Identify.prototype._addOperation=function(operation,property,value){if(this.userPropertiesOperations.hasOwnProperty(AMP_OP_CLEAR_ALL)){utils.log("This identify already contains a $clearAll operation, skipping operation "+operation);return}if(this.properties.indexOf(property)!==-1){utils.log('User property "'+property+'" already used in this identify, skipping operation '+operation);return}if(!this.userPropertiesOperations.hasOwnProperty(operation)){this.userPropertiesOperations[operation]={}}this.userPropertiesOperations[operation][property]=value;this.properties.push(property)};module.exports=Identify},{"./type":8,"./utils":9}],16:[function(require,module,exports){(function($){"use strict";function safe_add(x,y){var lsw=(x&65535)+(y&65535),msw=(x>>16)+(y>>16)+(lsw>>16);return msw<<16|lsw&65535}function bit_rol(num,cnt){return num<>>32-cnt}function md5_cmn(q,a,b,x,s,t){return safe_add(bit_rol(safe_add(safe_add(a,q),safe_add(x,t)),s),b)}function md5_ff(a,b,c,d,x,s,t){return md5_cmn(b&c|~b&d,a,b,x,s,t)}function md5_gg(a,b,c,d,x,s,t){return md5_cmn(b&d|c&~d,a,b,x,s,t)}function md5_hh(a,b,c,d,x,s,t){return md5_cmn(b^c^d,a,b,x,s,t)}function md5_ii(a,b,c,d,x,s,t){return md5_cmn(c^(b|~d),a,b,x,s,t)}function binl_md5(x,len){x[len>>5]|=128<>>9<<4)+14]=len;var i,olda,oldb,oldc,oldd,a=1732584193,b=-271733879,c=-1732584194,d=271733878;for(i=0;i>5]>>>i%32&255)}return output}function rstr2binl(input){var i,output=[];output[(input.length>>2)-1]=undefined;for(i=0;i>5]|=(input.charCodeAt(i/8)&255)<16){bkey=binl_md5(bkey,key.length*8)}for(i=0;i<16;i+=1){ipad[i]=bkey[i]^909522486;opad[i]=bkey[i]^1549556828}hash=binl_md5(ipad.concat(rstr2binl(data)),512+data.length*8);return binl2rstr(binl_md5(opad.concat(hash),512+128))}function rstr2hex(input){var hex_tab="0123456789abcdef",output="",x,i;for(i=0;i>>4&15)+hex_tab.charAt(x&15)}return output}function str2rstr_utf8(input){return unescape(encodeURIComponent(input))}function raw_md5(s){return rstr_md5(str2rstr_utf8(s))}function hex_md5(s){return rstr2hex(raw_md5(s))}function raw_hmac_md5(k,d){return rstr_hmac_md5(str2rstr_utf8(k),str2rstr_utf8(d))}function hex_hmac_md5(k,d){return rstr2hex(raw_hmac_md5(k,d))}function md5(string,key,raw){if(!key){if(!raw){return hex_md5(string)}return raw_md5(string)}if(!raw){return hex_hmac_md5(key,string)}return raw_hmac_md5(key,string)}if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports){exports=module.exports=md5}exports.md5=md5}else{if(typeof define==="function"&&define.amd){define(function(){return md5})}else{$.md5=md5}}})(this)},{}],6:[function(require,module,exports){var has=Object.prototype.hasOwnProperty;exports.keys=Object.keys||function(obj){var keys=[];for(var key in obj){if(has.call(obj,key)){keys.push(key)}}return keys};exports.values=function(obj){var vals=[];for(var key in obj){if(has.call(obj,key)){vals.push(obj[key])}}return vals};exports.merge=function(a,b){for(var key in b){if(has.call(b,key)){a[key]=b[key]}}return a};exports.length=function(obj){return exports.keys(obj).length};exports.isEmpty=function(obj){return 0==exports.length(obj)}},{}],17:[function(require,module,exports){var querystring=require("querystring");var Request=function(url,data){this.url=url;this.data=data||{}};Request.prototype.send=function(callback){var isIE=window.XDomainRequest?true:false;if(isIE){var xdr=new window.XDomainRequest;xdr.open("POST",this.url,true);xdr.onload=function(){callback(200,xdr.responseText)};xdr.onerror=function(){if(xdr.responseText==="Request Entity Too Large"){callback(413,xdr.responseText)}else{callback(500,xdr.responseText)}};xdr.ontimeout=function(){};xdr.onprogress=function(){};xdr.send(querystring.stringify(this.data))}else{var xhr=new XMLHttpRequest;xhr.open("POST",this.url,true);xhr.onreadystatechange=function(){if(xhr.readyState===4){callback(xhr.status,xhr.responseText)}};xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded; charset=UTF-8");xhr.send(querystring.stringify(this.data))}};module.exports=Request},{querystring:26}],26:[function(require,module,exports){var encode=encodeURIComponent;var decode=decodeURIComponent;var trim=require("trim");var type=require("type");exports.parse=function(str){if("string"!=typeof str)return{};str=trim(str);if(""==str)return{};if("?"==str.charAt(0))str=str.slice(1);var obj={};var pairs=str.split("&");for(var i=0;i0){if(q.length==2){if(typeof q[1]==FUNC_TYPE){result[q[0]]=q[1].call(this,match)}else{result[q[0]]=q[1]}}else if(q.length==3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){result[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{result[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length==4){result[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{result[q]=match?match:undefined}}}}i+=2}return result},str:function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;jthis.options.sessionTimeout){this._newSession=true;this._sessionId=now;if(this.options.includeUtm){this._initUtmData()}if(this.options.includeReferrer){this._saveReferrer(this._getReferrer())}}this._lastEventTime=now;_saveCookieData(this);if(this.options.saveEvents){this._unsentEvents=this._loadSavedUnsentEvents(this.options.unsentKey);this._unsentIdentifys=this._loadSavedUnsentEvents(this.options.unsentIdentifyKey);for(var i=0;i0){options[key]=inputValue}};for(var key in config){if(config.hasOwnProperty(key)){parseValidateAndLoad(key)}}};AmplitudeClient.prototype.runQueuedFunctions=function(){for(var i=0;i=this.options.eventUploadThreshold){this.sendEvents(callback);return true}if(!this._updateScheduled){this._updateScheduled=true;setTimeout(function(){this._updateScheduled=false;this.sendEvents()}.bind(this),this.options.eventUploadPeriodMillis)}return false};AmplitudeClient.prototype._getFromStorage=function _getFromStorage(storage,key){return storage.getItem(key+this._storageSuffix)};AmplitudeClient.prototype._setInStorage=function _setInStorage(storage,key,value){storage.setItem(key+this._storageSuffix,value)};var _upgradeCookeData=function _upgradeCookeData(scope){var cookieData=scope.cookieStorage.get(scope.options.cookieName);if(type(cookieData)==="object"&&cookieData.deviceId&&cookieData.sessionId&&cookieData.lastEventTime){return}var _getAndRemoveFromLocalStorage=function _getAndRemoveFromLocalStorage(key){var value=localStorage.getItem(key);localStorage.removeItem(key);return value};var apiKeySuffix=type(scope.options.apiKey)==="string"&&"_"+scope.options.apiKey.slice(0,6)||"";var localStorageDeviceId=_getAndRemoveFromLocalStorage(Constants.DEVICE_ID+apiKeySuffix);var localStorageUserId=_getAndRemoveFromLocalStorage(Constants.USER_ID+apiKeySuffix);var localStorageOptOut=_getAndRemoveFromLocalStorage(Constants.OPT_OUT+apiKeySuffix);if(localStorageOptOut!==null&&localStorageOptOut!==undefined){localStorageOptOut=String(localStorageOptOut)==="true"}var localStorageSessionId=parseInt(_getAndRemoveFromLocalStorage(Constants.SESSION_ID));var localStorageLastEventTime=parseInt(_getAndRemoveFromLocalStorage(Constants.LAST_EVENT_TIME));var localStorageEventId=parseInt(_getAndRemoveFromLocalStorage(Constants.LAST_EVENT_ID));var localStorageIdentifyId=parseInt(_getAndRemoveFromLocalStorage(Constants.LAST_IDENTIFY_ID));var localStorageSequenceNumber=parseInt(_getAndRemoveFromLocalStorage(Constants.LAST_SEQUENCE_NUMBER));var _getFromCookie=function _getFromCookie(key){return type(cookieData)==="object"&&cookieData[key]};scope.options.deviceId=_getFromCookie("deviceId")||localStorageDeviceId;scope.options.userId=_getFromCookie("userId")||localStorageUserId;scope._sessionId=_getFromCookie("sessionId")||localStorageSessionId||scope._sessionId;scope._lastEventTime=_getFromCookie("lastEventTime")||localStorageLastEventTime||scope._lastEventTime;scope._eventId=_getFromCookie("eventId")||localStorageEventId||scope._eventId;scope._identifyId=_getFromCookie("identifyId")||localStorageIdentifyId||scope._identifyId;scope._sequenceNumber=_getFromCookie("sequenceNumber")||localStorageSequenceNumber||scope._sequenceNumber;scope.options.optOut=localStorageOptOut||false;if(cookieData&&cookieData.optOut!==undefined&&cookieData.optOut!==null){scope.options.optOut=String(cookieData.optOut)==="true"}_saveCookieData(scope)};var _loadCookieData=function _loadCookieData(scope){var cookieData=scope.cookieStorage.get(scope.options.cookieName+scope._storageSuffix);if(type(cookieData)==="object"){if(cookieData.deviceId){scope.options.deviceId=cookieData.deviceId}if(cookieData.userId){scope.options.userId=cookieData.userId}if(cookieData.optOut!==null&&cookieData.optOut!==undefined){scope.options.optOut=cookieData.optOut}if(cookieData.sessionId){scope._sessionId=parseInt(cookieData.sessionId)}if(cookieData.lastEventTime){scope._lastEventTime=parseInt(cookieData.lastEventTime)}if(cookieData.eventId){scope._eventId=parseInt(cookieData.eventId)}if(cookieData.identifyId){scope._identifyId=parseInt(cookieData.identifyId)}if(cookieData.sequenceNumber){scope._sequenceNumber=parseInt(cookieData.sequenceNumber)}}};var _saveCookieData=function _saveCookieData(scope){scope.cookieStorage.set(scope.options.cookieName+scope._storageSuffix,{deviceId:scope.options.deviceId,userId:scope.options.userId,optOut:scope.options.optOut,sessionId:scope._sessionId,lastEventTime:scope._lastEventTime,eventId:scope._eventId,identifyId:scope._identifyId,sequenceNumber:scope._sequenceNumber})};AmplitudeClient.prototype._initUtmData=function _initUtmData(queryParams,cookieParams){queryParams=queryParams||location.search;cookieParams=cookieParams||this.cookieStorage.get("__utmz");var utmProperties=getUtmData(cookieParams,queryParams);_sendUserPropertiesOncePerSession(this,Constants.UTM_PROPERTIES,utmProperties)};var _sendUserPropertiesOncePerSession=function _sendUserPropertiesOncePerSession(scope,storageKey,userProperties){if(type(userProperties)!=="object"||Object.keys(userProperties).length===0){return}var identify=new Identify;for(var key in userProperties){if(userProperties.hasOwnProperty(key)){identify.setOnce("initial_"+key,userProperties[key])}}var hasSessionStorage=utils.sessionStorageEnabled();if(hasSessionStorage&&!scope._getFromStorage(sessionStorage,storageKey)||!hasSessionStorage){for(var property in userProperties){if(userProperties.hasOwnProperty(property)){identify.set(property,userProperties[property])}}if(hasSessionStorage){scope._setInStorage(sessionStorage,storageKey,JSON.stringify(userProperties))}}scope.identify(identify)};AmplitudeClient.prototype._getReferrer=function _getReferrer(){return document.referrer};AmplitudeClient.prototype._getReferringDomain=function _getReferringDomain(referrer){if(utils.isEmptyString(referrer)){return null}var parts=referrer.split("/");if(parts.length>=3){return parts[2]}return null};AmplitudeClient.prototype._saveReferrer=function _saveReferrer(referrer){if(utils.isEmptyString(referrer)){return}var referrerInfo={referrer:referrer,referring_domain:this._getReferringDomain(referrer)};_sendUserPropertiesOncePerSession(this,Constants.REFERRER,referrerInfo)};AmplitudeClient.prototype.saveEvents=function saveEvents(){try{this._setInStorage(localStorage,this.options.unsentKey,JSON.stringify(this._unsentEvents))}catch(e){}try{this._setInStorage(localStorage,this.options.unsentIdentifyKey,JSON.stringify(this._unsentIdentifys))}catch(e){}};AmplitudeClient.prototype.setDomain=function setDomain(domain){if(!utils.validateInput(domain,"domain","string")){return}try{this.cookieStorage.options({domain:domain});this.options.domain=this.cookieStorage.options().domain;_loadCookieData(this);_saveCookieData(this)}catch(e){utils.log(e)}};AmplitudeClient.prototype.setUserId=function setUserId(userId){try{this.options.userId=userId!==undefined&&userId!==null&&""+userId||null;_saveCookieData(this)}catch(e){utils.log(e)}};AmplitudeClient.prototype.setGroup=function(groupType,groupName){if(!this._apiKeySet("setGroup()")||!utils.validateInput(groupType,"groupType","string")||utils.isEmptyString(groupType)){return}var groups={};groups[groupType]=groupName;var identify=(new Identify).set(groupType,groupName);this._logEvent(Constants.IDENTIFY_EVENT,null,null,identify.userPropertiesOperations,groups,null)};AmplitudeClient.prototype.setOptOut=function setOptOut(enable){if(!utils.validateInput(enable,"enable","boolean")){return}try{this.options.optOut=enable;_saveCookieData(this)}catch(e){utils.log(e)}};AmplitudeClient.prototype.regenerateDeviceId=function regenerateDeviceId(){this.setDeviceId(UUID()+"R")};AmplitudeClient.prototype.setDeviceId=function setDeviceId(deviceId){if(!utils.validateInput(deviceId,"deviceId","string")){return}try{if(!utils.isEmptyString(deviceId)){this.options.deviceId=""+deviceId;_saveCookieData(this)}}catch(e){utils.log(e)}};AmplitudeClient.prototype.setUserProperties=function setUserProperties(userProperties){if(!this._apiKeySet("setUserProperties()")||!utils.validateInput(userProperties,"userProperties","object")){return}var identify=new Identify;for(var property in userProperties){if(userProperties.hasOwnProperty(property)){identify.set(property,userProperties[property])}}this.identify(identify)};AmplitudeClient.prototype.clearUserProperties=function clearUserProperties(){if(!this._apiKeySet("clearUserProperties()")){return}var identify=new Identify;identify.clearAll();this.identify(identify)};var _convertProxyObjectToRealObject=function _convertProxyObjectToRealObject(instance,proxy){for(var i=0;i0){return this._logEvent(Constants.IDENTIFY_EVENT,null,null,identify_obj.userPropertiesOperations,null,opt_callback)}}else{utils.log("Invalid identify input type. Expected Identify object but saw "+type(identify_obj))}if(type(opt_callback)==="function"){opt_callback(0,"No request sent")}};AmplitudeClient.prototype.setVersionName=function setVersionName(versionName){if(!utils.validateInput(versionName,"versionName","string")){return}this.options.versionName=versionName};AmplitudeClient.prototype._logEvent=function _logEvent(eventType,eventProperties,apiProperties,userProperties,groups,callback){_loadCookieData(this);if(!eventType||this.options.optOut){if(type(callback)==="function"){callback(0,"No request sent")}return}try{var eventId;if(eventType===Constants.IDENTIFY_EVENT){eventId=this.nextIdentifyId()}else{eventId=this.nextEventId()}var sequenceNumber=this.nextSequenceNumber();var eventTime=(new Date).getTime();if(!this._sessionId||!this._lastEventTime||eventTime-this._lastEventTime>this.options.sessionTimeout){this._sessionId=eventTime}this._lastEventTime=eventTime;_saveCookieData(this);userProperties=userProperties||{};apiProperties=apiProperties||{};eventProperties=eventProperties||{};groups=groups||{};var event={device_id:this.options.deviceId,user_id:this.options.userId,timestamp:eventTime,event_id:eventId,session_id:this._sessionId||-1,event_type:eventType,version_name:this.options.versionName||null,platform:this.options.platform,os_name:this._ua.browser.name||null,os_version:this._ua.browser.major||null,device_model:this._ua.os.name||null,language:this.options.language,api_properties:apiProperties,event_properties:utils.truncate(utils.validateProperties(eventProperties)),user_properties:utils.truncate(utils.validateProperties(userProperties)),uuid:UUID(),library:{name:"amplitude-js",version:version},sequence_number:sequenceNumber,groups:utils.truncate(utils.validateGroups(groups)),user_agent:this._userAgent};if(eventType===Constants.IDENTIFY_EVENT){this._unsentIdentifys.push(event);this._limitEventsQueued(this._unsentIdentifys)}else{this._unsentEvents.push(event);this._limitEventsQueued(this._unsentEvents)}if(this.options.saveEvents){this.saveEvents()}if(!this._sendEventsIfReady(callback)&&type(callback)==="function"){callback(0,"No request sent")}return eventId}catch(e){utils.log(e)}};AmplitudeClient.prototype._limitEventsQueued=function _limitEventsQueued(queue){if(queue.length>this.options.savedMaxCount){queue.splice(0,queue.length-this.options.savedMaxCount)}};AmplitudeClient.prototype.logEvent=function logEvent(eventType,eventProperties,opt_callback){if(!this._apiKeySet("logEvent()")||!utils.validateInput(eventType,"eventType","string")||utils.isEmptyString(eventType)){if(type(opt_callback)==="function"){opt_callback(0,"No request sent")}return-1}return this._logEvent(eventType,eventProperties,null,null,null,opt_callback)};AmplitudeClient.prototype.logEventWithGroups=function(eventType,eventProperties,groups,opt_callback){if(!this._apiKeySet("logEventWithGroup()")||!utils.validateInput(eventType,"eventType","string")){if(type(opt_callback)==="function"){opt_callback(0,"No request sent")}return-1}return this._logEvent(eventType,eventProperties,null,null,groups,opt_callback)};var _isNumber=function _isNumber(n){return!isNaN(parseFloat(n))&&isFinite(n)};AmplitudeClient.prototype.logRevenueV2=function logRevenueV2(revenue_obj){if(!this._apiKeySet("logRevenueV2()")){return}if(type(revenue_obj)==="object"&&revenue_obj.hasOwnProperty("_q")){revenue_obj=_convertProxyObjectToRealObject(new Revenue,revenue_obj)}if(revenue_obj instanceof Revenue){if(revenue_obj&&revenue_obj._isValidRevenue()){return this.logEvent(Constants.REVENUE_EVENT,revenue_obj._toJSONObject())}}else{utils.log("Invalid revenue input type. Expected Revenue object but saw "+type(revenue_obj))}};AmplitudeClient.prototype.logRevenue=function logRevenue(price,quantity,product){if(!this._apiKeySet("logRevenue()")||!_isNumber(price)||quantity!==undefined&&!_isNumber(quantity)){return-1}return this._logEvent(Constants.REVENUE_EVENT,{},{productId:product,special:"revenue_amount",quantity:quantity||1,price:price},null,null,null)};AmplitudeClient.prototype.removeEvents=function removeEvents(maxEventId,maxIdentifyId){_removeEvents(this,"_unsentEvents",maxEventId);_removeEvents(this,"_unsentIdentifys",maxIdentifyId)};var _removeEvents=function _removeEvents(scope,eventQueue,maxId){if(maxId<0){return}var filteredEvents=[];for(var i=0;imaxId){filteredEvents.push(scope[eventQueue][i])}}scope[eventQueue]=filteredEvents};AmplitudeClient.prototype.sendEvents=function sendEvents(callback){if(!this._apiKeySet("sendEvents()")||this._sending||this.options.optOut||this._unsentCount()===0){if(type(callback)==="function"){callback(0,"No request sent")}return}this._sending=true;var url=("https:"===window.location.protocol?"https":"http")+"://"+this.options.apiEndpoint+"/";var numEvents=Math.min(this._unsentCount(),this.options.uploadBatchSize);var mergedEvents=this._mergeEventsAndIdentifys(numEvents);var maxEventId=mergedEvents.maxEventId;var maxIdentifyId=mergedEvents.maxIdentifyId;var events=JSON.stringify(mergedEvents.eventsToSend);var uploadTime=(new Date).getTime();var data={client:this.options.apiKey,e:events,v:Constants.API_VERSION,upload_time:uploadTime,checksum:md5(Constants.API_VERSION+this.options.apiKey+events+uploadTime)};var scope=this;new Request(url,data).send(function(status,response){scope._sending=false;try{if(status===200&&response==="success"){scope.removeEvents(maxEventId,maxIdentifyId);if(scope.options.saveEvents){scope.saveEvents()}if(!scope._sendEventsIfReady(callback)&&type(callback)==="function"){callback(status,response)}}else if(status===413){if(scope.options.uploadBatchSize===1){scope.removeEvents(maxEventId,maxIdentifyId)}scope.options.uploadBatchSize=Math.ceil(numEvents/2);scope.sendEvents(callback)}else if(type(callback)==="function"){callback(status,response)}}catch(e){}})};AmplitudeClient.prototype._mergeEventsAndIdentifys=function _mergeEventsAndIdentifys(numEvents){var eventsToSend=[];var eventIndex=0;var maxEventId=-1;var identifyIndex=0;var maxIdentifyId=-1;while(eventsToSend.length=this._unsentIdentifys.length;var noEvents=eventIndex>=this._unsentEvents.length;if(noEvents&&noIdentifys){utils.log("Merging Events and Identifys, less events and identifys than expected");break}else if(noIdentifys){event=this._unsentEvents[eventIndex++];maxEventId=event.event_id}else if(noEvents){event=this._unsentIdentifys[identifyIndex++];maxIdentifyId=event.event_id}else{if(!("sequence_number"in this._unsentEvents[eventIndex])||this._unsentEvents[eventIndex].sequence_number>2;enc2=(chr1&3)<<4|chr2>>4;enc3=(chr2&15)<<2|chr3>>6;enc4=chr3&63;if(isNaN(chr2)){enc3=enc4=64}else if(isNaN(chr3)){enc4=64}output=output+Base64._keyStr.charAt(enc1)+Base64._keyStr.charAt(enc2)+Base64._keyStr.charAt(enc3)+Base64._keyStr.charAt(enc4)}return output},decode:function(input){try{if(window.btoa&&window.atob){return decodeURIComponent(escape(window.atob(input)))}}catch(e){}return Base64._decode(input)},_decode:function(input){var output="";var chr1,chr2,chr3;var enc1,enc2,enc3,enc4;var i=0;input=input.replace(/[^A-Za-z0-9\+\/\=]/g,"");while(i>4;chr2=(enc2&15)<<4|enc3>>2;chr3=(enc3&3)<<6|enc4;output=output+String.fromCharCode(chr1);if(enc3!==64){output=output+String.fromCharCode(chr2)}if(enc4!==64){output=output+String.fromCharCode(chr3)}}output=UTF8.decode(output);return output}};module.exports=Base64},{"./utf8":23}],23:[function(require,module,exports){var UTF8={encode:function(s){var utftext="";for(var n=0;n127&&c<2048){utftext+=String.fromCharCode(c>>6|192);utftext+=String.fromCharCode(c&63|128)}else{utftext+=String.fromCharCode(c>>12|224);utftext+=String.fromCharCode(c>>6&63|128);utftext+=String.fromCharCode(c&63|128)}}return utftext},decode:function(utftext){var s="";var i=0;var c=0,c1=0,c2=0;while(i191&&c<224){c1=utftext.charCodeAt(i+1);s+=String.fromCharCode((c&31)<<6|c1&63);i+=2}else{c1=utftext.charCodeAt(i+1);c2=utftext.charCodeAt(i+2);s+=String.fromCharCode((c&15)<<12|(c1&63)<<6|c2&63);i+=3}}return s}};module.exports=UTF8},{}],14:[function(require,module,exports){var json=window.JSON||{};var stringify=json.stringify;var parse=json.parse;module.exports=parse&&stringify?JSON:require("json-fallback")},{"json-fallback":24}],24:[function(require,module,exports){(function(){"use strict";var JSON=module.exports={};function f(n){return n<10?"0"+n:n}if(typeof Date.prototype.toJSON!=="function"){Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){return this.valueOf()}}var cx,escapable,gap,indent,meta,rep;function quote(string){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+'"'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==="object"&&typeof value.toJSON==="function"){value=value.toJSON(key)}if(typeof rep==="function"){value=rep.call(holder,key,value)}switch(typeof value){case"string":return quote(value);case"number":return isFinite(value)?String(value):"null";case"boolean":case"null":return String(value);case"object":if(!value){return"null"}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==="[object Array]"){length=value.length;for(i=0;iconstants.MAX_STRING_LENGTH?value.substring(0,constants.MAX_STRING_LENGTH):value}return value};var validateInput=function validateInput(input,name,expectedType){if(type(input)!==expectedType){log("Invalid "+name+" input type. Expected "+expectedType+" but received "+type(input));return false}return true};var validateProperties=function validateProperties(properties){var propsType=type(properties);if(propsType!=="object"){log("Error: invalid event properties format. Expecting Javascript object, received "+propsType+", ignoring");return{}}var copy={};for(var property in properties){if(!properties.hasOwnProperty(property)){continue}var key=property;var keyType=type(key);if(keyType!=="string"){key=String(key);log("WARNING: Non-string property key, received type "+keyType+', coercing to string "'+key+'"')}var value=validatePropertyValue(key,properties[property]);if(value===null){continue}copy[key]=value}return copy};var invalidValueTypes=["null","nan","undefined","function","arguments","regexp","element"];var validatePropertyValue=function validatePropertyValue(key,value){var valueType=type(value);if(invalidValueTypes.indexOf(valueType)!==-1){log('WARNING: Property key "'+key+'" with invalid value type '+valueType+", ignoring");value=null}else if(valueType==="error"){value=String(value);log('WARNING: Property key "'+key+'" with value type error, coercing to '+value)}else if(valueType==="array"){var arrayCopy=[];for(var i=0;i0){if(!this.userPropertiesOperations.hasOwnProperty(AMP_OP_CLEAR_ALL)){utils.log("Need to send $clearAll on its own Identify object without any other operations, skipping $clearAll")}return this}this.userPropertiesOperations[AMP_OP_CLEAR_ALL]="-";return this};Identify.prototype.prepend=function(property,value){this._addOperation(AMP_OP_PREPEND,property,value);return this};Identify.prototype.set=function(property,value){this._addOperation(AMP_OP_SET,property,value);return this};Identify.prototype.setOnce=function(property,value){this._addOperation(AMP_OP_SET_ONCE,property,value);return this};Identify.prototype.unset=function(property){this._addOperation(AMP_OP_UNSET,property,"-");return this};Identify.prototype._addOperation=function(operation,property,value){if(this.userPropertiesOperations.hasOwnProperty(AMP_OP_CLEAR_ALL)){utils.log("This identify already contains a $clearAll operation, skipping operation "+operation);return}if(this.properties.indexOf(property)!==-1){utils.log('User property "'+property+'" already used in this identify, skipping operation '+operation);return}if(!this.userPropertiesOperations.hasOwnProperty(operation)){this.userPropertiesOperations[operation]={}}this.userPropertiesOperations[operation][property]=value;this.properties.push(property)};module.exports=Identify},{"./type":8,"./utils":9}],16:[function(require,module,exports){(function($){"use strict";function safe_add(x,y){var lsw=(x&65535)+(y&65535),msw=(x>>16)+(y>>16)+(lsw>>16);return msw<<16|lsw&65535}function bit_rol(num,cnt){return num<>>32-cnt}function md5_cmn(q,a,b,x,s,t){return safe_add(bit_rol(safe_add(safe_add(a,q),safe_add(x,t)),s),b)}function md5_ff(a,b,c,d,x,s,t){return md5_cmn(b&c|~b&d,a,b,x,s,t)}function md5_gg(a,b,c,d,x,s,t){return md5_cmn(b&d|c&~d,a,b,x,s,t)}function md5_hh(a,b,c,d,x,s,t){return md5_cmn(b^c^d,a,b,x,s,t)}function md5_ii(a,b,c,d,x,s,t){return md5_cmn(c^(b|~d),a,b,x,s,t)}function binl_md5(x,len){x[len>>5]|=128<>>9<<4)+14]=len;var i,olda,oldb,oldc,oldd,a=1732584193,b=-271733879,c=-1732584194,d=271733878;for(i=0;i>5]>>>i%32&255)}return output}function rstr2binl(input){var i,output=[];output[(input.length>>2)-1]=undefined;for(i=0;i>5]|=(input.charCodeAt(i/8)&255)<16){bkey=binl_md5(bkey,key.length*8)}for(i=0;i<16;i+=1){ipad[i]=bkey[i]^909522486;opad[i]=bkey[i]^1549556828}hash=binl_md5(ipad.concat(rstr2binl(data)),512+data.length*8);return binl2rstr(binl_md5(opad.concat(hash),512+128))}function rstr2hex(input){var hex_tab="0123456789abcdef",output="",x,i;for(i=0;i>>4&15)+hex_tab.charAt(x&15)}return output}function str2rstr_utf8(input){return unescape(encodeURIComponent(input))}function raw_md5(s){return rstr_md5(str2rstr_utf8(s))}function hex_md5(s){return rstr2hex(raw_md5(s))}function raw_hmac_md5(k,d){return rstr_hmac_md5(str2rstr_utf8(k),str2rstr_utf8(d))}function hex_hmac_md5(k,d){return rstr2hex(raw_hmac_md5(k,d))}function md5(string,key,raw){if(!key){if(!raw){return hex_md5(string)}return raw_md5(string)}if(!raw){return hex_hmac_md5(key,string)}return raw_hmac_md5(key,string)}if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports){exports=module.exports=md5}exports.md5=md5}else{if(typeof define==="function"&&define.amd){define(function(){return md5})}else{$.md5=md5}}})(this)},{}],6:[function(require,module,exports){var has=Object.prototype.hasOwnProperty;exports.keys=Object.keys||function(obj){var keys=[];for(var key in obj){if(has.call(obj,key)){keys.push(key)}}return keys};exports.values=function(obj){var vals=[];for(var key in obj){if(has.call(obj,key)){vals.push(obj[key])}}return vals};exports.merge=function(a,b){for(var key in b){if(has.call(b,key)){a[key]=b[key]}}return a};exports.length=function(obj){return exports.keys(obj).length};exports.isEmpty=function(obj){return 0==exports.length(obj)}},{}],17:[function(require,module,exports){var querystring=require("querystring");var Request=function(url,data){this.url=url;this.data=data||{}};Request.prototype.send=function(callback){var isIE=window.XDomainRequest?true:false;if(isIE){var xdr=new window.XDomainRequest;xdr.open("POST",this.url,true);xdr.onload=function(){callback(200,xdr.responseText)};xdr.onerror=function(){if(xdr.responseText==="Request Entity Too Large"){callback(413,xdr.responseText)}else{callback(500,xdr.responseText)}};xdr.ontimeout=function(){};xdr.onprogress=function(){};xdr.send(querystring.stringify(this.data))}else{var xhr=new XMLHttpRequest;xhr.open("POST",this.url,true);xhr.onreadystatechange=function(){if(xhr.readyState===4){callback(xhr.status,xhr.responseText)}};xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded; charset=UTF-8");xhr.send(querystring.stringify(this.data))}};module.exports=Request},{querystring:26}],26:[function(require,module,exports){var encode=encodeURIComponent;var decode=decodeURIComponent;var trim=require("trim");var type=require("type");exports.parse=function(str){if("string"!=typeof str)return{};str=trim(str);if(""==str)return{};if("?"==str.charAt(0))str=str.slice(1);var obj={};var pairs=str.split("&");for(var i=0;i0){if(q.length==2){if(typeof q[1]==FUNC_TYPE){result[q[0]]=q[1].call(this,match)}else{result[q[0]]=q[1]}}else if(q.length==3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){result[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{result[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length==4){result[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{result[q]=match?match:undefined}}}}i+=2}return result},str:function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,uuid)};module.exports=uuid},{}],10:[function(require,module,exports){module.exports="3.0.1"},{}],11:[function(require,module,exports){var language=require("./language");module.exports={apiEndpoint:"api.amplitude.com",cookieExpiration:365*10,cookieName:"amplitude_id",domain:"",includeReferrer:false,includeUtm:false,language:language.language,optOut:false,platform:"Web",savedMaxCount:1e3,saveEvents:true,sessionTimeout:30*60*1e3,unsentKey:"amplitude_unsent",unsentIdentifyKey:"amplitude_unsent_identify",uploadBatchSize:100,batchEvents:false,eventUploadThreshold:30,eventUploadPeriodMillis:30*1e3}},{"./language":29}],29:[function(require,module,exports){var getLanguage=function(){return navigator&&(navigator.languages&&navigator.languages[0]||navigator.language||navigator.userLanguage)||undefined};module.exports={language:getLanguage()}},{}]},{},{1:""})); \ No newline at end of file diff --git a/documentation/Amplitude.html b/documentation/Amplitude.html index fa3ee486..f323b303 100644 --- a/documentation/Amplitude.html +++ b/documentation/Amplitude.html @@ -3091,7 +3091,7 @@
Parameters:
Source:
@@ -3137,7 +3137,7 @@

Home

Classes

  • diff --git a/documentation/AmplitudeClient.html b/documentation/AmplitudeClient.html index 1db5f46a..34ebb5d7 100644 --- a/documentation/AmplitudeClient.html +++ b/documentation/AmplitudeClient.html @@ -185,7 +185,7 @@

    __VERSION_
    Source:
    @@ -268,7 +268,7 @@

    cl
    Source:
    @@ -355,7 +355,7 @@

    getSessio
    Source:
    @@ -534,7 +534,7 @@

    Parameters:
    Source:
    @@ -742,7 +742,7 @@
    Parameters:
    Source:
    @@ -829,7 +829,7 @@

    isNewSess
    Source:
    @@ -1029,7 +1029,7 @@

    Parameters:
    Source:
    @@ -1240,7 +1240,7 @@
    Parameters:
    Source:
    @@ -1424,7 +1424,7 @@
    Parameters:
    Source:
    @@ -1563,7 +1563,7 @@
    Parameters:
    Source:
    @@ -1654,7 +1654,7 @@

    reg
    Source:
    @@ -1787,7 +1787,7 @@

    Parameters:
    Source:
    @@ -1923,7 +1923,7 @@
    Parameters:
    Source:
    @@ -2012,7 +2012,7 @@

    Source:
    @@ -2175,7 +2175,7 @@
    Parameters:
    Source:
    @@ -2311,7 +2311,7 @@
    Parameters:
    Source:
    @@ -2442,7 +2442,7 @@
    Parameters:
    Source:
    @@ -2602,7 +2602,7 @@
    Parameters:
    Source:
    @@ -2738,7 +2738,7 @@
    Parameters:
    Source:
    @@ -2791,7 +2791,7 @@

    Home

    Classes

    • diff --git a/documentation/Identify.html b/documentation/Identify.html index 2d148b94..2dc577fc 100644 --- a/documentation/Identify.html +++ b/documentation/Identify.html @@ -1295,7 +1295,7 @@

      Home

      Classes

      • diff --git a/documentation/Revenue.html b/documentation/Revenue.html index b7686da2..b06bc349 100644 --- a/documentation/Revenue.html +++ b/documentation/Revenue.html @@ -50,7 +50,7 @@

        new RevenueReadme for more information about logging Revenue. @@ -478,7 +478,7 @@

        setProduc
        - Set a value for the product identifer. This field is required for all revenue being logged. + Set a value for the product identifer.
        @@ -965,7 +965,7 @@

        Home

        Classes

        • diff --git a/documentation/amplitude-client.js.html b/documentation/amplitude-client.js.html index 72a4947e..f59f6516 100644 --- a/documentation/amplitude-client.js.html +++ b/documentation/amplitude-client.js.html @@ -69,6 +69,8 @@

          Source: amplitude-client.js

          this._newSession = false; this._sequenceNumber = 0; this._sessionId = null; + + this._userAgent = (navigator && navigator.userAgent) || null; }; AmplitudeClient.prototype.Identify = Identify; @@ -826,7 +828,8 @@

          Source: amplitude-client.js

          version: version }, sequence_number: sequenceNumber, // for ordering events and identifys - groups: utils.truncate(utils.validateGroups(groups)) + groups: utils.truncate(utils.validateGroups(groups)), + user_agent: this._userAgent // country: null }; @@ -1170,7 +1173,7 @@

          Home

          Classes

          • diff --git a/documentation/amplitude.js.html b/documentation/amplitude.js.html index 179a5ea9..d593fa7b 100644 --- a/documentation/amplitude.js.html +++ b/documentation/amplitude.js.html @@ -409,7 +409,7 @@

            Home

            Classes

            • diff --git a/documentation/identify.js.html b/documentation/identify.js.html index 24221343..c2175ffd 100644 --- a/documentation/identify.js.html +++ b/documentation/identify.js.html @@ -226,7 +226,7 @@

              Home

              Classes

              • diff --git a/documentation/index.html b/documentation/index.html index db9327a1..c270cacb 100644 --- a/documentation/index.html +++ b/documentation/index.html @@ -56,7 +56,7 @@

                Home

                Classes

                • diff --git a/documentation/revenue.js.html b/documentation/revenue.js.html index 8e4bea8f..322a58b3 100644 --- a/documentation/revenue.js.html +++ b/documentation/revenue.js.html @@ -32,14 +32,14 @@

                  Source: revenue.js

                  /* * Wrapper for logging Revenue data. Revenue objects get passed to amplitude.logRevenueV2 to send to Amplitude servers. - * Note: productId and price are required fields. If quantity is not specified, then defaults to 1. + * Note: price is the only required field. If quantity is not specified, then defaults to 1. */ /** * Revenue API - instance constructor. Revenue objects are a wrapper for revenue data. * Each method updates a revenue property in the Revenue object, and returns the same Revenue object, * allowing you to chain multiple method calls together. - * Note: productId and price are required fields to log revenue events. + * Note: price is a required field to log revenue events. * If quantity is not specified then defaults to 1. * See [Readme]{@link https://github.com/amplitude/Amplitude-Javascript#tracking-revenue} for more information * about logging Revenue. @@ -49,17 +49,17 @@

                  Source: revenue.js

                  */ var Revenue = function Revenue() { // required fields - this._productId = null; - this._quantity = 1; this._price = null; // optional fields + this._productId = null; + this._quantity = 1; this._revenueType = null; this._properties = null; }; /** - * Set a value for the product identifer. This field is required for all revenue being logged. + * Set a value for the product identifer. * @public * @param {string} productId - The value for the product identifier. Empty and invalid strings are ignored. * @return {Revenue} Returns the same Revenue object, allowing you to chain multiple method calls together. @@ -151,10 +151,6 @@

                  Source: revenue.js

                  * @private */ Revenue.prototype._isValidRevenue = function _isValidRevenue() { - if (type(this._productId) !== 'string' || utils.isEmptyString(this._productId)) { - utils.log('Invalid revenue, need to set productId field'); - return false; - } if (type(this._price) !== 'number') { utils.log('Invalid revenue, need to set price field'); return false; @@ -200,7 +196,7 @@

                  Home

                  Classes

                  • diff --git a/src/amplitude-client.js b/src/amplitude-client.js index d1476ac0..d0e58ade 100644 --- a/src/amplitude-client.js +++ b/src/amplitude-client.js @@ -41,6 +41,8 @@ var AmplitudeClient = function AmplitudeClient(instanceName) { this._newSession = false; this._sequenceNumber = 0; this._sessionId = null; + + this._userAgent = (navigator && navigator.userAgent) || null; }; AmplitudeClient.prototype.Identify = Identify; @@ -798,7 +800,8 @@ AmplitudeClient.prototype._logEvent = function _logEvent(eventType, eventPropert version: version }, sequence_number: sequenceNumber, // for ordering events and identifys - groups: utils.truncate(utils.validateGroups(groups)) + groups: utils.truncate(utils.validateGroups(groups)), + user_agent: this._userAgent // country: null }; diff --git a/src/revenue.js b/src/revenue.js index 78b04982..f3cd549b 100644 --- a/src/revenue.js +++ b/src/revenue.js @@ -4,14 +4,14 @@ var utils = require('./utils'); /* * Wrapper for logging Revenue data. Revenue objects get passed to amplitude.logRevenueV2 to send to Amplitude servers. - * Note: productId and price are required fields. If quantity is not specified, then defaults to 1. + * Note: price is the only required field. If quantity is not specified, then defaults to 1. */ /** * Revenue API - instance constructor. Revenue objects are a wrapper for revenue data. * Each method updates a revenue property in the Revenue object, and returns the same Revenue object, * allowing you to chain multiple method calls together. - * Note: productId and price are required fields to log revenue events. + * Note: price is a required field to log revenue events. * If quantity is not specified then defaults to 1. * See [Readme]{@link https://github.com/amplitude/Amplitude-Javascript#tracking-revenue} for more information * about logging Revenue. @@ -21,17 +21,17 @@ var utils = require('./utils'); */ var Revenue = function Revenue() { // required fields - this._productId = null; - this._quantity = 1; this._price = null; // optional fields + this._productId = null; + this._quantity = 1; this._revenueType = null; this._properties = null; }; /** - * Set a value for the product identifer. This field is required for all revenue being logged. + * Set a value for the product identifer. * @public * @param {string} productId - The value for the product identifier. Empty and invalid strings are ignored. * @return {Revenue} Returns the same Revenue object, allowing you to chain multiple method calls together. @@ -123,10 +123,6 @@ Revenue.prototype.setEventProperties = function setEventProperties(eventProperti * @private */ Revenue.prototype._isValidRevenue = function _isValidRevenue() { - if (type(this._productId) !== 'string' || utils.isEmptyString(this._productId)) { - utils.log('Invalid revenue, need to set productId field'); - return false; - } if (type(this._price) !== 'number') { utils.log('Invalid revenue, need to set price field'); return false; diff --git a/test/amplitude-client.js b/test/amplitude-client.js index cbdd900b..a624bf34 100644 --- a/test/amplitude-client.js +++ b/test/amplitude-client.js @@ -1983,6 +1983,21 @@ describe('setVersionName', function() { assert.equal(value, 200); assert.equal(message, 'success'); }); + + it('should track the raw user agent string', function() { + // Unit test UA is set by phantomJS test environment, should be constant for all tests + var phantomJSUA = 'AppleWebKit/534.34 (KHTML, like Gecko) PhantomJS/1.9.7 Safari/534.34'; + assert.isTrue(navigator.userAgent.indexOf(phantomJSUA) > -1); + assert.isTrue(amplitude._userAgent.indexOf(phantomJSUA) > -1); + + // log an event and verify UA field is filled out + amplitude.logEvent('testEvent'); + assert.lengthOf(server.requests, 1); + var events = JSON.parse(querystring.parse(server.requests[0].requestBody).e); + assert.lengthOf(events, 1); + assert.equal(events[0].event_type, 'testEvent'); + assert.isTrue(events[0].user_agent.indexOf(phantomJSUA) > -1); + }); }); describe('optOut', function() { diff --git a/test/revenue.js b/test/revenue.js index bb5ed21e..7a541630 100644 --- a/test/revenue.js +++ b/test/revenue.js @@ -98,7 +98,7 @@ describe('Revenue', function() { assert.isFalse(revenue2._isValidRevenue()); revenue2.setPrice(10.99); revenue2.setQuantity(15); - assert.isFalse(revenue2._isValidRevenue()); + assert.isTrue(revenue2._isValidRevenue()); revenue2.setProductId('testProductId'); assert.isTrue(revenue2._isValidRevenue()); });