From fcec3f9c051390b7377cc80964da455536bef2fc Mon Sep 17 00:00:00 2001 From: Daniel Jih Date: Tue, 5 Jan 2016 16:32:29 -0800 Subject: [PATCH 1/2] add clear all operation --- CHANGELOG.md | 2 ++ README.md | 24 ++++++++++++------- amplitude-segment-snippet.min.js | 12 +++++----- amplitude-snippet.min.js | 16 ++++++------- amplitude.js | 28 ++++++++++++++++++++++ amplitude.min.js | 4 ++-- src/amplitude-snippet.js | 4 ++-- src/amplitude.js | 7 ++++++ src/identify.js | 21 ++++++++++++++++ test/amplitude.js | 29 ++++++++++++++++++++++ test/browser/amplitudejs.html | 5 ++-- test/browser/amplitudejs2.html | 5 ++-- test/identify.js | 41 ++++++++++++++++++++++++++++++++ 13 files changed, 166 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70fe45e6..1b9a6df4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## Unreleased +* Add ability to clear all user properties. + ## 2.8.0 (December 15, 2015) * Add getSessionId helper method to fetch the current sessionId. diff --git a/README.md b/README.md index 1db63a05..56aeddf2 100644 --- a/README.md +++ b/README.md @@ -9,15 +9,15 @@ Amplitude-Javascript ``` @@ -142,6 +142,14 @@ var userProperties = { amplitude.setUserProperties(userProperties); ``` +### Clearing User Properties ### + +You may use `clearUserProperties` to clear all user properties at once. Note: the result is irreversible! + +```javascript +amplitude.clearUserProperties(); +``` + # Tracking Revenue # To track revenue from a user, call diff --git a/amplitude-segment-snippet.min.js b/amplitude-segment-snippet.min.js index 0267092f..ce8f8ded 100644 --- a/amplitude-segment-snippet.min.js +++ b/amplitude-segment-snippet.min.js @@ -1,6 +1,6 @@ -(function(t,e){var n=t.amplitude||{};var r=function(){this._q=[];return this};function s(t){ -r.prototype[t]=function(){this._q.push([t].concat(Array.prototype.slice.call(arguments,0))); -return this}}var i=["add","append","set","setOnce","unset"];for(var o=0;o 0) { + if (!(AMP_OP_CLEAR_ALL in this.userPropertiesOperations)) { + 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.set = function(property, value) { this._addOperation(AMP_OP_SET, property, value); return this; @@ -3565,6 +3587,12 @@ Identify.prototype.unset = function(property) { }; Identify.prototype._addOperation = function(operation, property, value) { + // check that the identify doesn't already contain a clearAll + if (AMP_OP_CLEAR_ALL in this.userPropertiesOperations) { + log('This identify already contains a $clearAll operation, skipping operation ' + operation); + return; + } + // check that property wasn't already used in this Identify if (this.properties.indexOf(property) !== -1) { log('User property "' + property + '" already used in this identify, skipping operation ' + operation); diff --git a/amplitude.min.js b/amplitude.min.js index 5273cbe1..5c5ba3f9 100644 --- a/amplitude.min.js +++ b/amplitude.min.js @@ -1,2 +1,2 @@ -(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 instance=new Amplitude;instance._q=old._q||[];module.exports=instance},{"./amplitude":2}],2:[function(require,module,exports){var cookieStorage=require("./cookiestorage");var JSON=require("json");var language=require("./language");var localStorage=require("./localstorage");var md5=require("JavaScript-MD5");var object=require("object");var Request=require("./xhr");var UAParser=require("ua-parser-js");var UUID=require("./uuid");var version=require("./version");var Identify=require("./identify");var type=require("./type");var log=function(s){console.log("[Amplitude] "+s)};var IDENTIFY_EVENT="$identify";var API_VERSION=2;var MAX_STRING_LENGTH=1024;var DEFAULT_OPTIONS={apiEndpoint:"api.amplitude.com",cookieExpiration:365*10,cookieName:"amplitude_id",domain:undefined,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};var LocalStorageKeys={LAST_EVENT_ID:"amplitude_lastEventId",LAST_IDENTIFY_ID:"amplitude_lastIdentifyId",LAST_SEQUENCE_NUMBER:"amplitude_lastSequenceNumber",LAST_EVENT_TIME:"amplitude_lastEventTime",SESSION_ID:"amplitude_sessionId",REFERRER:"amplitude_referrer",DEVICE_ID:"amplitude_deviceId",USER_ID:"amplitude_userId",OPT_OUT:"amplitude_optOut"};var Amplitude=function(){this._unsentEvents=[];this._unsentIdentifys=[];this._ua=new UAParser(navigator.userAgent).getResult();this.options=object.merge({},DEFAULT_OPTIONS);this.cookieStorage=(new cookieStorage).getStorage();this._q=[]};Amplitude.prototype._eventId=0;Amplitude.prototype._identifyId=0;Amplitude.prototype._sequenceNumber=0;Amplitude.prototype._sending=false;Amplitude.prototype._lastEventTime=null;Amplitude.prototype._sessionId=null;Amplitude.prototype._newSession=false;Amplitude.prototype._updateScheduled=false;Amplitude.prototype.Identify=Identify;Amplitude.prototype.init=function(apiKey,opt_userId,opt_config,callback){try{this.options.apiKey=apiKey;if(opt_config){if(opt_config.saveEvents!==undefined){this.options.saveEvents=!!opt_config.saveEvents}if(opt_config.domain!==undefined){this.options.domain=opt_config.domain}if(opt_config.includeUtm!==undefined){this.options.includeUtm=!!opt_config.includeUtm}if(opt_config.includeReferrer!==undefined){this.options.includeReferrer=!!opt_config.includeReferrer}if(opt_config.batchEvents!==undefined){this.options.batchEvents=!!opt_config.batchEvents}this.options.platform=opt_config.platform||this.options.platform;this.options.language=opt_config.language||this.options.language;this.options.sessionTimeout=opt_config.sessionTimeout||this.options.sessionTimeout;this.options.uploadBatchSize=opt_config.uploadBatchSize||this.options.uploadBatchSize;this.options.eventUploadThreshold=opt_config.eventUploadThreshold||this.options.eventUploadThreshold;this.options.savedMaxCount=opt_config.savedMaxCount||this.options.savedMaxCount;this.options.eventUploadPeriodMillis=opt_config.eventUploadPeriodMillis||this.options.eventUploadPeriodMillis}this.cookieStorage.options({expirationDays:this.options.cookieExpiration,domain:this.options.domain});this.options.domain=this.cookieStorage.options().domain;_migrateLocalStorageDataToCookie(this);_loadCookieData(this);this.options.deviceId=opt_config&&opt_config.deviceId!==undefined&&opt_config.deviceId!==null&&opt_config.deviceId||this.options.deviceId||UUID();this.options.userId=opt_userId!==undefined&&opt_userId!==null&&opt_userId||this.options.userId||null;this._lastEventTime=this._lastEventTime||parseInt(localStorage.getItem(LocalStorageKeys.LAST_EVENT_TIME))||null;this._sessionId=this._sessionId||parseInt(localStorage.getItem(LocalStorageKeys.SESSION_ID))||null;this._eventId=this._eventId||parseInt(localStorage.getItem(LocalStorageKeys.LAST_EVENT_ID))||0;this._identifyId=this._identifyId||parseInt(localStorage.getItem(LocalStorageKeys.LAST_IDENTIFY_ID))||0;this._sequenceNumber=this._sequenceNumber||parseInt(localStorage.getItem(LocalStorageKeys.LAST_SEQUENCE_NUMBER))||0;var now=(new Date).getTime();if(!this._sessionId||!this._lastEventTime||now-this._lastEventTime>this.options.sessionTimeout){this._newSession=true;this._sessionId=now}this._lastEventTime=now;_saveCookieData(this);_clearSessionAndEventTrackingFromLocalStorage();if(this.options.saveEvents){this._loadSavedUnsentEvents(this.options.unsentKey,"_unsentEvents");this._loadSavedUnsentEvents(this.options.unsentIdentifyKey,"_unsentIdentifys")}this._sendEventsIfReady();if(this.options.includeUtm){this._initUtmData()}if(this.options.includeReferrer){this._saveReferrer(this._getReferrer())}}catch(e){log(e)}if(callback&&type(callback)==="function"){callback()}};Amplitude.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};var _migrateLocalStorageDataToCookie=function(scope){var cookieData=scope.cookieStorage.get(scope.options.cookieName);if(cookieData&&cookieData.deviceId){return}var cookieDeviceId=cookieData&&cookieData.deviceId||null;var cookieUserId=cookieData&&cookieData.userId||null;var cookieOptOut=cookieData&&cookieData.optOut!==null&&cookieData.optOut!==undefined?cookieData.optOut:null;var keySuffix="_"+scope.options.apiKey.slice(0,6);var localStorageDeviceId=localStorage.getItem(LocalStorageKeys.DEVICE_ID+keySuffix);if(localStorageDeviceId){localStorage.removeItem(LocalStorageKeys.DEVICE_ID+keySuffix)}var localStorageUserId=localStorage.getItem(LocalStorageKeys.USER_ID+keySuffix);if(localStorageUserId){localStorage.removeItem(LocalStorageKeys.USER_ID+keySuffix)}var localStorageOptOut=localStorage.getItem(LocalStorageKeys.OPT_OUT+keySuffix);if(localStorageOptOut!==null&&localStorageOptOut!==undefined){localStorage.removeItem(LocalStorageKeys.OPT_OUT+keySuffix);localStorageOptOut=String(localStorageOptOut)==="true"}scope.cookieStorage.set(scope.options.cookieName,{deviceId:cookieDeviceId||localStorageDeviceId,userId:cookieUserId||localStorageUserId,optOut:cookieOptOut!==undefined&&cookieOptOut!==null?cookieOptOut:localStorageOptOut})};var _loadCookieData=function(scope){var cookieData=scope.cookieStorage.get(scope.options.cookieName);if(cookieData){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(scope){scope.cookieStorage.set(scope.options.cookieName,{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})};var _clearSessionAndEventTrackingFromLocalStorage=function(){localStorage.removeItem(LocalStorageKeys.SESSION_ID);localStorage.removeItem(LocalStorageKeys.LAST_EVENT_TIME);localStorage.removeItem(LocalStorageKeys.LAST_EVENT_ID);localStorage.removeItem(LocalStorageKeys.LAST_IDENTIFY_ID);localStorage.removeItem(LocalStorageKeys.LAST_SEQUENCE_NUMBER)};Amplitude._getUtmParam=function(name,query){name=name.replace(/[\[]/,"\\[").replace(/[\]]/,"\\]");var regex=new RegExp("[\\?&]"+name+"=([^&#]*)");var results=regex.exec(query);return results===null?undefined:decodeURIComponent(results[1].replace(/\+/g," "))};Amplitude._getUtmData=function(rawCookie,query){var cookie=rawCookie?"?"+rawCookie.split(".").slice(-1)[0].replace(/\|/g,"&"):"";var fetchParam=function(queryName,query,cookieName,cookie){return Amplitude._getUtmParam(queryName,query)||Amplitude._getUtmParam(cookieName,cookie)};return{utm_source:fetchParam("utm_source",query,"utmcsr",cookie),utm_medium:fetchParam("utm_medium",query,"utmcmd",cookie),utm_campaign:fetchParam("utm_campaign",query,"utmccn",cookie),utm_term:fetchParam("utm_term",query,"utmctr",cookie),utm_content:fetchParam("utm_content",query,"utmcct",cookie)}};Amplitude.prototype._initUtmData=function(queryParams,cookieParams){queryParams=queryParams||location.search;cookieParams=cookieParams||this.cookieStorage.get("__utmz");this._utmProperties=Amplitude._getUtmData(cookieParams,queryParams)};Amplitude.prototype._getReferrer=function(){return document.referrer};Amplitude.prototype._getReferringDomain=function(referrer){if(referrer===null||referrer===undefined||referrer===""){return null}var parts=referrer.split("/");if(parts.length>=3){return parts[2]}return null};Amplitude.prototype._saveReferrer=function(referrer){if(referrer===null||referrer===undefined||referrer===""){return}var referring_domain=this._getReferringDomain(referrer);var identify=(new Identify).setOnce("initial_referrer",referrer);identify.setOnce("initial_referring_domain",referring_domain);var hasSessionStorage=window.sessionStorage?true:false;if(hasSessionStorage&&!window.sessionStorage.getItem(LocalStorageKeys.REFERRER)||!hasSessionStorage){identify.set("referrer",referrer).set("referring_domain",referring_domain);if(hasSessionStorage){window.sessionStorage.setItem(LocalStorageKeys.REFERRER,referrer)}}this.identify(identify)};Amplitude.prototype.saveEvents=function(){try{localStorage.setItem(this.options.unsentKey,JSON.stringify(this._unsentEvents));localStorage.setItem(this.options.unsentIdentifyKey,JSON.stringify(this._unsentIdentifys))}catch(e){}};Amplitude.prototype.setDomain=function(domain){try{this.cookieStorage.options({domain:domain});this.options.domain=this.cookieStorage.options().domain;_loadCookieData(this);_saveCookieData(this)}catch(e){log(e)}};Amplitude.prototype.setUserId=function(userId){try{this.options.userId=userId!==undefined&&userId!==null&&""+userId||null;_saveCookieData(this)}catch(e){log(e)}};Amplitude.prototype.setOptOut=function(enable){try{this.options.optOut=enable;_saveCookieData(this)}catch(e){log(e)}};Amplitude.prototype.setDeviceId=function(deviceId){try{if(deviceId){this.options.deviceId=""+deviceId;_saveCookieData(this)}}catch(e){log(e)}};Amplitude.prototype.setUserProperties=function(userProperties){var identify=new Identify;for(var property in userProperties){if(userProperties.hasOwnProperty(property)){identify.set(property,userProperties[property])}}this.identify(identify)};Amplitude.prototype.identify=function(identify){if(type(identify)==="object"&&"_q"in identify){var instance=new Identify;for(var i=0;i0){this._logEvent(IDENTIFY_EVENT,null,null,identify.userPropertiesOperations)}};Amplitude.prototype.setVersionName=function(versionName){try{this.options.versionName=versionName}catch(e){log(e)}};Amplitude.prototype._truncate=function(value){if(type(value)==="array"){for(var i=0;iMAX_STRING_LENGTH?value.substring(0,MAX_STRING_LENGTH):value}return value};Amplitude.prototype._logEvent=function(eventType,eventProperties,apiProperties,userProperties,callback){if(type(callback)!=="function"){callback=null}if(!eventType||this.options.optOut){if(callback){callback(0,"No request sent")}return}try{var eventId;if(eventType===IDENTIFY_EVENT){eventId=this.nextIdentifyId()}else{eventId=this.nextEventId()}var sequenceNumber=this.nextSequenceNumber();var eventTime=(new Date).getTime();var ua=this._ua;if(!this._sessionId||!this._lastEventTime||eventTime-this._lastEventTime>this.options.sessionTimeout){this._sessionId=eventTime}this._lastEventTime=eventTime;_saveCookieData(this);userProperties=userProperties||{};if(eventType!==IDENTIFY_EVENT){object.merge(userProperties,this._utmProperties)}apiProperties=apiProperties||{};eventProperties=eventProperties||{};var event={device_id:this.options.deviceId,user_id:this.options.userId||this.options.deviceId,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:ua.browser.name||null,os_version:ua.browser.major||null,device_model:ua.os.name||null,language:this.options.language,api_properties:apiProperties,event_properties:this._truncate(eventProperties),user_properties:this._truncate(userProperties),uuid:UUID(),library:{name:"amplitude-js",version:this.__VERSION__},sequence_number:sequenceNumber};if(eventType===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)&&callback){callback(0,"No request sent")}return eventId}catch(e){log(e)}};Amplitude.prototype._limitEventsQueued=function(queue){if(queue.length>this.options.savedMaxCount){queue.splice(0,queue.length-this.options.savedMaxCount)}};Amplitude.prototype.logEvent=function(eventType,eventProperties,callback){return this._logEvent(eventType,eventProperties,null,null,callback)};var _isNumber=function(n){return!isNaN(parseFloat(n))&&isFinite(n)};Amplitude.prototype.logRevenue=function(price,quantity,product){if(!_isNumber(price)||quantity!==undefined&&!_isNumber(quantity)){return}return this._logEvent("revenue_amount",{},{productId:product,special:"revenue_amount",quantity:quantity||1,price:price})};Amplitude.prototype.removeEvents=function(maxEventId,maxIdentifyId){if(maxEventId>=0){var filteredEvents=[];for(var i=0;imaxEventId){filteredEvents.push(this._unsentEvents[i])}}this._unsentEvents=filteredEvents}if(maxIdentifyId>=0){var filteredIdentifys=[];for(var j=0;jmaxIdentifyId){filteredIdentifys.push(this._unsentIdentifys[j])}}this._unsentIdentifys=filteredIdentifys}};Amplitude.prototype.sendEvents=function(callback){if(!this._sending&&!this.options.optOut&&this._unsentCount()>0){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:API_VERSION,upload_time:uploadTime,checksum:md5(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)&&callback){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(callback){callback(status,response)}}catch(e){}})}else if(callback){callback(0,"No request sent")}};Amplitude.prototype._mergeEventsAndIdentifys=function(numEvents){var eventsToSend=[];var eventIndex=0;var maxEventId=-1;var identifyIndex=0;var maxIdentifyId=-1;while(eventsToSend.length=this._unsentIdentifys.length){event=this._unsentEvents[eventIndex++];maxEventId=event.event_id}else if(eventIndex>=this._unsentEvents.length){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":18}],18:[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},{}],4:[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":19}],19:[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;i>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)},{}],8:[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)}},{}],9:[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:21}],21:[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},{}],12:[function(require,module,exports){module.exports="2.8.0"},{}],13:[function(require,module,exports){var type=require("./type");var AMP_OP_ADD="$add";var AMP_OP_APPEND="$append";var AMP_OP_SET="$set";var AMP_OP_SET_ONCE="$setOnce";var AMP_OP_UNSET="$unset";var log=function(s){console.log("[Amplitude] "+s)};var Identify=function(){this.userPropertiesOperations={};this.properties=[]};Identify.prototype.add=function(property,value){if(type(value)==="number"||type(value)==="string"){this._addOperation(AMP_OP_ADD,property,value)}else{log("Unsupported type for value: "+type(value)+", expecting number or string")}return this};Identify.prototype.append=function(property,value){this._addOperation(AMP_OP_APPEND,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.properties.indexOf(property)!==-1){log('User property "'+property+'" already used in this identify, skipping operation '+operation);return}if(!(operation in this.userPropertiesOperations)){this.userPropertiesOperations[operation]={}}this.userPropertiesOperations[operation][property]=value;this.properties.push(property)};module.exports=Identify},{"./type":14}],14:[function(require,module,exports){var toString=Object.prototype.toString;module.exports=function(val){switch(toString.call(val)){case"[object Date]":return"date";case"[object RegExp]":return"regexp";case"[object Arguments]":return"arguments";case"[object Array]":return"array";case"[object Error]":return"error"}if(val===null){return"null"}if(val===undefined){return"undefined"}if(val!==val){return"nan"}if(val&&val.nodeType===1){return"element"}if(typeof Buffer!=="undefined"&&Buffer.isBuffer(val)){return"buffer"}val=val.valueOf?val.valueOf():Object.prototype.valueOf.apply(val);return typeof val}},{}]},{},{1:""})); \ No newline at end of file +(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 instance=new Amplitude;instance._q=old._q||[];module.exports=instance},{"./amplitude":2}],2:[function(require,module,exports){var cookieStorage=require("./cookiestorage");var JSON=require("json");var language=require("./language");var localStorage=require("./localstorage");var md5=require("JavaScript-MD5");var object=require("object");var Request=require("./xhr");var UAParser=require("ua-parser-js");var UUID=require("./uuid");var version=require("./version");var Identify=require("./identify");var type=require("./type");var log=function(s){console.log("[Amplitude] "+s)};var IDENTIFY_EVENT="$identify";var API_VERSION=2;var MAX_STRING_LENGTH=1024;var DEFAULT_OPTIONS={apiEndpoint:"api.amplitude.com",cookieExpiration:365*10,cookieName:"amplitude_id",domain:undefined,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};var LocalStorageKeys={LAST_EVENT_ID:"amplitude_lastEventId",LAST_IDENTIFY_ID:"amplitude_lastIdentifyId",LAST_SEQUENCE_NUMBER:"amplitude_lastSequenceNumber",LAST_EVENT_TIME:"amplitude_lastEventTime",SESSION_ID:"amplitude_sessionId",REFERRER:"amplitude_referrer",DEVICE_ID:"amplitude_deviceId",USER_ID:"amplitude_userId",OPT_OUT:"amplitude_optOut"};var Amplitude=function(){this._unsentEvents=[];this._unsentIdentifys=[];this._ua=new UAParser(navigator.userAgent).getResult();this.options=object.merge({},DEFAULT_OPTIONS);this.cookieStorage=(new cookieStorage).getStorage();this._q=[]};Amplitude.prototype._eventId=0;Amplitude.prototype._identifyId=0;Amplitude.prototype._sequenceNumber=0;Amplitude.prototype._sending=false;Amplitude.prototype._lastEventTime=null;Amplitude.prototype._sessionId=null;Amplitude.prototype._newSession=false;Amplitude.prototype._updateScheduled=false;Amplitude.prototype.Identify=Identify;Amplitude.prototype.init=function(apiKey,opt_userId,opt_config,callback){try{this.options.apiKey=apiKey;if(opt_config){if(opt_config.saveEvents!==undefined){this.options.saveEvents=!!opt_config.saveEvents}if(opt_config.domain!==undefined){this.options.domain=opt_config.domain}if(opt_config.includeUtm!==undefined){this.options.includeUtm=!!opt_config.includeUtm}if(opt_config.includeReferrer!==undefined){this.options.includeReferrer=!!opt_config.includeReferrer}if(opt_config.batchEvents!==undefined){this.options.batchEvents=!!opt_config.batchEvents}this.options.platform=opt_config.platform||this.options.platform;this.options.language=opt_config.language||this.options.language;this.options.sessionTimeout=opt_config.sessionTimeout||this.options.sessionTimeout;this.options.uploadBatchSize=opt_config.uploadBatchSize||this.options.uploadBatchSize;this.options.eventUploadThreshold=opt_config.eventUploadThreshold||this.options.eventUploadThreshold;this.options.savedMaxCount=opt_config.savedMaxCount||this.options.savedMaxCount;this.options.eventUploadPeriodMillis=opt_config.eventUploadPeriodMillis||this.options.eventUploadPeriodMillis}this.cookieStorage.options({expirationDays:this.options.cookieExpiration,domain:this.options.domain});this.options.domain=this.cookieStorage.options().domain;_migrateLocalStorageDataToCookie(this);_loadCookieData(this);this.options.deviceId=opt_config&&opt_config.deviceId!==undefined&&opt_config.deviceId!==null&&opt_config.deviceId||this.options.deviceId||UUID();this.options.userId=opt_userId!==undefined&&opt_userId!==null&&opt_userId||this.options.userId||null;this._lastEventTime=this._lastEventTime||parseInt(localStorage.getItem(LocalStorageKeys.LAST_EVENT_TIME))||null;this._sessionId=this._sessionId||parseInt(localStorage.getItem(LocalStorageKeys.SESSION_ID))||null;this._eventId=this._eventId||parseInt(localStorage.getItem(LocalStorageKeys.LAST_EVENT_ID))||0;this._identifyId=this._identifyId||parseInt(localStorage.getItem(LocalStorageKeys.LAST_IDENTIFY_ID))||0;this._sequenceNumber=this._sequenceNumber||parseInt(localStorage.getItem(LocalStorageKeys.LAST_SEQUENCE_NUMBER))||0;var now=(new Date).getTime();if(!this._sessionId||!this._lastEventTime||now-this._lastEventTime>this.options.sessionTimeout){this._newSession=true;this._sessionId=now}this._lastEventTime=now;_saveCookieData(this);_clearSessionAndEventTrackingFromLocalStorage();if(this.options.saveEvents){this._loadSavedUnsentEvents(this.options.unsentKey,"_unsentEvents");this._loadSavedUnsentEvents(this.options.unsentIdentifyKey,"_unsentIdentifys")}this._sendEventsIfReady();if(this.options.includeUtm){this._initUtmData()}if(this.options.includeReferrer){this._saveReferrer(this._getReferrer())}}catch(e){log(e)}if(callback&&type(callback)==="function"){callback()}};Amplitude.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};var _migrateLocalStorageDataToCookie=function(scope){var cookieData=scope.cookieStorage.get(scope.options.cookieName);if(cookieData&&cookieData.deviceId){return}var cookieDeviceId=cookieData&&cookieData.deviceId||null;var cookieUserId=cookieData&&cookieData.userId||null;var cookieOptOut=cookieData&&cookieData.optOut!==null&&cookieData.optOut!==undefined?cookieData.optOut:null;var keySuffix="_"+scope.options.apiKey.slice(0,6);var localStorageDeviceId=localStorage.getItem(LocalStorageKeys.DEVICE_ID+keySuffix);if(localStorageDeviceId){localStorage.removeItem(LocalStorageKeys.DEVICE_ID+keySuffix)}var localStorageUserId=localStorage.getItem(LocalStorageKeys.USER_ID+keySuffix);if(localStorageUserId){localStorage.removeItem(LocalStorageKeys.USER_ID+keySuffix)}var localStorageOptOut=localStorage.getItem(LocalStorageKeys.OPT_OUT+keySuffix);if(localStorageOptOut!==null&&localStorageOptOut!==undefined){localStorage.removeItem(LocalStorageKeys.OPT_OUT+keySuffix);localStorageOptOut=String(localStorageOptOut)==="true"}scope.cookieStorage.set(scope.options.cookieName,{deviceId:cookieDeviceId||localStorageDeviceId,userId:cookieUserId||localStorageUserId,optOut:cookieOptOut!==undefined&&cookieOptOut!==null?cookieOptOut:localStorageOptOut})};var _loadCookieData=function(scope){var cookieData=scope.cookieStorage.get(scope.options.cookieName);if(cookieData){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(scope){scope.cookieStorage.set(scope.options.cookieName,{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})};var _clearSessionAndEventTrackingFromLocalStorage=function(){localStorage.removeItem(LocalStorageKeys.SESSION_ID);localStorage.removeItem(LocalStorageKeys.LAST_EVENT_TIME);localStorage.removeItem(LocalStorageKeys.LAST_EVENT_ID);localStorage.removeItem(LocalStorageKeys.LAST_IDENTIFY_ID);localStorage.removeItem(LocalStorageKeys.LAST_SEQUENCE_NUMBER)};Amplitude._getUtmParam=function(name,query){name=name.replace(/[\[]/,"\\[").replace(/[\]]/,"\\]");var regex=new RegExp("[\\?&]"+name+"=([^&#]*)");var results=regex.exec(query);return results===null?undefined:decodeURIComponent(results[1].replace(/\+/g," "))};Amplitude._getUtmData=function(rawCookie,query){var cookie=rawCookie?"?"+rawCookie.split(".").slice(-1)[0].replace(/\|/g,"&"):"";var fetchParam=function(queryName,query,cookieName,cookie){return Amplitude._getUtmParam(queryName,query)||Amplitude._getUtmParam(cookieName,cookie)};return{utm_source:fetchParam("utm_source",query,"utmcsr",cookie),utm_medium:fetchParam("utm_medium",query,"utmcmd",cookie),utm_campaign:fetchParam("utm_campaign",query,"utmccn",cookie),utm_term:fetchParam("utm_term",query,"utmctr",cookie),utm_content:fetchParam("utm_content",query,"utmcct",cookie)}};Amplitude.prototype._initUtmData=function(queryParams,cookieParams){queryParams=queryParams||location.search;cookieParams=cookieParams||this.cookieStorage.get("__utmz");this._utmProperties=Amplitude._getUtmData(cookieParams,queryParams)};Amplitude.prototype._getReferrer=function(){return document.referrer};Amplitude.prototype._getReferringDomain=function(referrer){if(referrer===null||referrer===undefined||referrer===""){return null}var parts=referrer.split("/");if(parts.length>=3){return parts[2]}return null};Amplitude.prototype._saveReferrer=function(referrer){if(referrer===null||referrer===undefined||referrer===""){return}var referring_domain=this._getReferringDomain(referrer);var identify=(new Identify).setOnce("initial_referrer",referrer);identify.setOnce("initial_referring_domain",referring_domain);var hasSessionStorage=window.sessionStorage?true:false;if(hasSessionStorage&&!window.sessionStorage.getItem(LocalStorageKeys.REFERRER)||!hasSessionStorage){identify.set("referrer",referrer).set("referring_domain",referring_domain);if(hasSessionStorage){window.sessionStorage.setItem(LocalStorageKeys.REFERRER,referrer)}}this.identify(identify)};Amplitude.prototype.saveEvents=function(){try{localStorage.setItem(this.options.unsentKey,JSON.stringify(this._unsentEvents));localStorage.setItem(this.options.unsentIdentifyKey,JSON.stringify(this._unsentIdentifys))}catch(e){}};Amplitude.prototype.setDomain=function(domain){try{this.cookieStorage.options({domain:domain});this.options.domain=this.cookieStorage.options().domain;_loadCookieData(this);_saveCookieData(this)}catch(e){log(e)}};Amplitude.prototype.setUserId=function(userId){try{this.options.userId=userId!==undefined&&userId!==null&&""+userId||null;_saveCookieData(this)}catch(e){log(e)}};Amplitude.prototype.setOptOut=function(enable){try{this.options.optOut=enable;_saveCookieData(this)}catch(e){log(e)}};Amplitude.prototype.setDeviceId=function(deviceId){try{if(deviceId){this.options.deviceId=""+deviceId;_saveCookieData(this)}}catch(e){log(e)}};Amplitude.prototype.setUserProperties=function(userProperties){var identify=new Identify;for(var property in userProperties){if(userProperties.hasOwnProperty(property)){identify.set(property,userProperties[property])}}this.identify(identify)};Amplitude.prototype.clearUserProperties=function(){var identify=new Identify;identify.clearAll();this.identify(identify)};Amplitude.prototype.identify=function(identify){if(type(identify)==="object"&&"_q"in identify){var instance=new Identify;for(var i=0;i0){this._logEvent(IDENTIFY_EVENT,null,null,identify.userPropertiesOperations)}};Amplitude.prototype.setVersionName=function(versionName){try{this.options.versionName=versionName}catch(e){log(e)}};Amplitude.prototype._truncate=function(value){if(type(value)==="array"){for(var i=0;iMAX_STRING_LENGTH?value.substring(0,MAX_STRING_LENGTH):value}return value};Amplitude.prototype._logEvent=function(eventType,eventProperties,apiProperties,userProperties,callback){if(type(callback)!=="function"){callback=null}if(!eventType||this.options.optOut){if(callback){callback(0,"No request sent")}return}try{var eventId;if(eventType===IDENTIFY_EVENT){eventId=this.nextIdentifyId()}else{eventId=this.nextEventId()}var sequenceNumber=this.nextSequenceNumber();var eventTime=(new Date).getTime();var ua=this._ua;if(!this._sessionId||!this._lastEventTime||eventTime-this._lastEventTime>this.options.sessionTimeout){this._sessionId=eventTime}this._lastEventTime=eventTime;_saveCookieData(this);userProperties=userProperties||{};if(eventType!==IDENTIFY_EVENT){object.merge(userProperties,this._utmProperties)}apiProperties=apiProperties||{};eventProperties=eventProperties||{};var event={device_id:this.options.deviceId,user_id:this.options.userId||this.options.deviceId,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:ua.browser.name||null,os_version:ua.browser.major||null,device_model:ua.os.name||null,language:this.options.language,api_properties:apiProperties,event_properties:this._truncate(eventProperties),user_properties:this._truncate(userProperties),uuid:UUID(),library:{name:"amplitude-js",version:this.__VERSION__},sequence_number:sequenceNumber};if(eventType===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)&&callback){callback(0,"No request sent")}return eventId}catch(e){log(e)}};Amplitude.prototype._limitEventsQueued=function(queue){if(queue.length>this.options.savedMaxCount){queue.splice(0,queue.length-this.options.savedMaxCount)}};Amplitude.prototype.logEvent=function(eventType,eventProperties,callback){return this._logEvent(eventType,eventProperties,null,null,callback)};var _isNumber=function(n){return!isNaN(parseFloat(n))&&isFinite(n)};Amplitude.prototype.logRevenue=function(price,quantity,product){if(!_isNumber(price)||quantity!==undefined&&!_isNumber(quantity)){return}return this._logEvent("revenue_amount",{},{productId:product,special:"revenue_amount",quantity:quantity||1,price:price})};Amplitude.prototype.removeEvents=function(maxEventId,maxIdentifyId){if(maxEventId>=0){var filteredEvents=[];for(var i=0;imaxEventId){filteredEvents.push(this._unsentEvents[i])}}this._unsentEvents=filteredEvents}if(maxIdentifyId>=0){var filteredIdentifys=[];for(var j=0;jmaxIdentifyId){filteredIdentifys.push(this._unsentIdentifys[j])}}this._unsentIdentifys=filteredIdentifys}};Amplitude.prototype.sendEvents=function(callback){if(!this._sending&&!this.options.optOut&&this._unsentCount()>0){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:API_VERSION,upload_time:uploadTime,checksum:md5(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)&&callback){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(callback){callback(status,response)}}catch(e){}})}else if(callback){callback(0,"No request sent")}};Amplitude.prototype._mergeEventsAndIdentifys=function(numEvents){var eventsToSend=[];var eventIndex=0;var maxEventId=-1;var identifyIndex=0;var maxIdentifyId=-1;while(eventsToSend.length=this._unsentIdentifys.length){event=this._unsentEvents[eventIndex++];maxEventId=event.event_id}else if(eventIndex>=this._unsentEvents.length){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":18}],18:[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},{}],4:[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":19}],19:[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;i>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)},{}],8:[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)}},{}],9:[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:21}],21:[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},{}],12:[function(require,module,exports){module.exports="2.8.0"},{}],13:[function(require,module,exports){var type=require("./type");var AMP_OP_ADD="$add";var AMP_OP_APPEND="$append";var AMP_OP_CLEAR_ALL="$clearAll";var AMP_OP_SET="$set";var AMP_OP_SET_ONCE="$setOnce";var AMP_OP_UNSET="$unset";var log=function(s){console.log("[Amplitude] "+s)};var Identify=function(){this.userPropertiesOperations={};this.properties=[]};Identify.prototype.add=function(property,value){if(type(value)==="number"||type(value)==="string"){this._addOperation(AMP_OP_ADD,property,value)}else{log("Unsupported type for value: "+type(value)+", expecting number or string")}return this};Identify.prototype.append=function(property,value){this._addOperation(AMP_OP_APPEND,property,value);return this};Identify.prototype.clearAll=function(){if(Object.keys(this.userPropertiesOperations).length>0){if(!(AMP_OP_CLEAR_ALL in this.userPropertiesOperations)){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.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(AMP_OP_CLEAR_ALL in this.userPropertiesOperations){log("This identify already contains a $clearAll operation, skipping operation "+operation);return}if(this.properties.indexOf(property)!==-1){log('User property "'+property+'" already used in this identify, skipping operation '+operation);return}if(!(operation in this.userPropertiesOperations)){this.userPropertiesOperations[operation]={}}this.userPropertiesOperations[operation][property]=value;this.properties.push(property)};module.exports=Identify},{"./type":14}],14:[function(require,module,exports){var toString=Object.prototype.toString;module.exports=function(val){switch(toString.call(val)){case"[object Date]":return"date";case"[object RegExp]":return"regexp";case"[object Arguments]":return"arguments";case"[object Array]":return"array";case"[object Error]":return"error"}if(val===null){return"null"}if(val===undefined){return"undefined"}if(val!==val){return"nan"}if(val&&val.nodeType===1){return"element"}if(typeof Buffer!=="undefined"&&Buffer.isBuffer(val)){return"buffer"}val=val.valueOf?val.valueOf():Object.prototype.valueOf.apply(val);return typeof val}},{}]},{},{1:""})); \ No newline at end of file diff --git a/src/amplitude-snippet.js b/src/amplitude-snippet.js index 9f1d1141..13a06569 100644 --- a/src/amplitude-snippet.js +++ b/src/amplitude-snippet.js @@ -13,7 +13,7 @@ this._q.push([fn].concat(Array.prototype.slice.call(arguments, 0))); return this; }; } - var identifyFuncs = ['add', 'append', 'set', 'setOnce', 'unset']; + var identifyFuncs = ['add', 'append', 'clearAll', 'set', 'setOnce', 'unset']; for (var i = 0; i < identifyFuncs.length; i++) { proxyIdentify(identifyFuncs[i]); } @@ -26,7 +26,7 @@ } var funcs = ['init', 'logEvent', 'logRevenue', 'setUserId', 'setUserProperties', 'setOptOut', 'setVersionName', 'setDomain', 'setDeviceId', - 'setGlobalUserProperties', 'identify']; + 'setGlobalUserProperties', 'identify', 'clearUserProperties']; for (var j = 0; j < funcs.length; j++) { proxy(funcs[j]); } diff --git a/src/amplitude.js b/src/amplitude.js index 39ddb677..9f16ae76 100644 --- a/src/amplitude.js +++ b/src/amplitude.js @@ -467,6 +467,13 @@ Amplitude.prototype.setUserProperties = function(userProperties) { this.identify(identify); }; +// Clearing user properties is irreversible! +Amplitude.prototype.clearUserProperties = function(){ + var identify = new Identify(); + identify.clearAll(); + this.identify(identify); +}; + Amplitude.prototype.identify = function(identify) { if (type(identify) === 'object' && '_q' in identify) { diff --git a/src/identify.js b/src/identify.js index baff1f16..30aaaa2b 100644 --- a/src/identify.js +++ b/src/identify.js @@ -8,6 +8,7 @@ var type = require('./type'); var AMP_OP_ADD = '$add'; var AMP_OP_APPEND = '$append'; +var AMP_OP_CLEAR_ALL = '$clearAll'; var AMP_OP_SET = '$set'; var AMP_OP_SET_ONCE = '$setOnce'; var AMP_OP_UNSET = '$unset'; @@ -35,6 +36,20 @@ Identify.prototype.append = function(property, value) { return this; }; +// clearAll should be sent on its own Identify object +// If there are already other operations, then don't add clearAll +// If clearAll already in Identify, don't add other operations +Identify.prototype.clearAll = function() { + if (Object.keys(this.userPropertiesOperations).length > 0) { + if (!(AMP_OP_CLEAR_ALL in this.userPropertiesOperations)) { + 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.set = function(property, value) { this._addOperation(AMP_OP_SET, property, value); return this; @@ -51,6 +66,12 @@ Identify.prototype.unset = function(property) { }; Identify.prototype._addOperation = function(operation, property, value) { + // check that the identify doesn't already contain a clearAll + if (AMP_OP_CLEAR_ALL in this.userPropertiesOperations) { + log('This identify already contains a $clearAll operation, skipping operation ' + operation); + return; + } + // check that property wasn't already used in this Identify if (this.properties.indexOf(property) !== -1) { log('User property "' + property + '" already used in this identify, skipping operation ' + operation); diff --git a/test/amplitude.js b/test/amplitude.js index 181ac4dd..c60a175d 100644 --- a/test/amplitude.js +++ b/test/amplitude.js @@ -332,6 +332,35 @@ describe('Amplitude', function() { }); }); + describe('clearUserProperties', function() { + beforeEach(function() { + amplitude.init(apiKey); + }); + + afterEach(function() { + reset(); + }); + + it('should log identify call from clear user properties', function() { + assert.equal(amplitude._unsentCount(), 0); + amplitude.clearUserProperties(); + + assert.lengthOf(amplitude._unsentEvents, 0); + assert.lengthOf(amplitude._unsentIdentifys, 1); + assert.equal(amplitude._unsentCount(), 1); + 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, '$identify'); + assert.deepEqual(events[0].event_properties, {}); + + var expected = { + '$clearAll': '-' + }; + assert.deepEqual(events[0].user_properties, expected); + }); + }); + describe('setDeviceId', function() { afterEach(function() { diff --git a/test/browser/amplitudejs.html b/test/browser/amplitudejs.html index f848c2d4..4f55dad4 100644 --- a/test/browser/amplitudejs.html +++ b/test/browser/amplitudejs.html @@ -16,7 +16,7 @@ this._q.push([fn].concat(Array.prototype.slice.call(arguments, 0))); return this; }; } - var identifyFuncs = ['add', 'append', 'set', 'setOnce', 'unset']; + var identifyFuncs = ['add', 'append', 'clearAll', 'set', 'setOnce', 'unset']; for (var i = 0; i < identifyFuncs.length; i++) { proxyIdentify(identifyFuncs[i]); } @@ -29,14 +29,13 @@ } var funcs = ['init', 'logEvent', 'logRevenue', 'setUserId', 'setUserProperties', 'setOptOut', 'setVersionName', 'setDomain', 'setDeviceId', - 'setGlobalUserProperties', 'identify']; + 'setGlobalUserProperties', 'identify', 'clearUserProperties']; for (var j = 0; j < funcs.length; j++) { proxy(funcs[j]); } window.amplitude = amplitude; })(window, document); - var setUserId = function() { var userId = prompt('Input userId', 'user01'); amplitude.setUserId(userId); diff --git a/test/browser/amplitudejs2.html b/test/browser/amplitudejs2.html index 7062006a..2394535d 100644 --- a/test/browser/amplitudejs2.html +++ b/test/browser/amplitudejs2.html @@ -16,7 +16,7 @@ this._q.push([fn].concat(Array.prototype.slice.call(arguments, 0))); return this; }; } - var identifyFuncs = ['add', 'append', 'set', 'setOnce', 'unset']; + var identifyFuncs = ['add', 'append', 'clearAll', 'set', 'setOnce', 'unset']; for (var i = 0; i < identifyFuncs.length; i++) { proxyIdentify(identifyFuncs[i]); } @@ -29,14 +29,13 @@ } var funcs = ['init', 'logEvent', 'logRevenue', 'setUserId', 'setUserProperties', 'setOptOut', 'setVersionName', 'setDomain', 'setDeviceId', - 'setGlobalUserProperties', 'identify']; + 'setGlobalUserProperties', 'identify', 'clearUserProperties']; for (var j = 0; j < funcs.length; j++) { proxy(funcs[j]); } window.amplitude = amplitude; })(window, document); - var setUserId = function() { var userId = prompt('Input userId', 'user01'); amplitude.setUserId(userId); diff --git a/test/identify.js b/test/identify.js index 35455c16..ba6d83c9 100644 --- a/test/identify.js +++ b/test/identify.js @@ -197,4 +197,45 @@ describe('Identify', function() { assert.deepEqual(expected, identify.userPropertiesOperations); assert.deepEqual([property], identify.properties); }); + + it ('should disallow other operations on a clearAll identify', function() { + var property = "testProperty"; + var value1 = "testValue"; + var value2 = 0.123; + var value3 = true; + var value4 = {}; + + var identify = new Identify().clearAll(); + identify.setOnce(property, value1).add(property, value2).set(property, value3).unset(property); + + var expected = { + '$clearAll': '-' + }; + + assert.deepEqual(expected, identify.userPropertiesOperations); + assert.deepEqual([], identify.properties); + }); + + it ('should disallow clearAll on an identify with other operations', function() { + var property = "testProperty"; + var value1 = "testValue"; + var value2 = 0.123; + var value3 = true; + var value4 = {}; + + var identify = new Identify().setOnce(property, value1).add(property, value2); + identify.set(property, value3).unset(property).clearAll(); + + var expected = { + '$setOnce': {} + }; + expected['$setOnce'][property] = value1 + + assert.deepEqual(expected, identify.userPropertiesOperations); + assert.deepEqual([property], identify.properties); + }); + + it ('should not log any warnings for calling clearAll multiple times on a single identify', function() { + var identify = new Identify().clearAll().clearAll().clearAll(); + }); }); From a23cdcff671e015095f3b209b7027a4c085d2519 Mon Sep 17 00:00:00 2001 From: Daniel Jih Date: Mon, 11 Jan 2016 13:20:50 -0800 Subject: [PATCH 2/2] add links for testing to browser page --- test/browser/amplitudejs.html | 7 ++++--- test/browser/amplitudejs2.html | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/test/browser/amplitudejs.html b/test/browser/amplitudejs.html index 4f55dad4..8d7e1391 100644 --- a/test/browser/amplitudejs.html +++ b/test/browser/amplitudejs.html @@ -55,18 +55,18 @@ var addToPhotoCount = function() { var photoCount = parseInt(prompt('Input amount to increment photo count by', '2'), 10); amplitude.identify(new amplitude.Identify().add('photoCount', photoCount)); - } + }; var clickOnLinkA = function() { amplitude.logEvent('Clicked on link A', null, function() { window.location='https://www.google.com'; }); }; var setPhotoCount = function() { var photoCount = parseInt(prompt('Input photo count to set', '2'), 10); amplitude.identify(new amplitude.Identify().set('photoCount', photoCount)); - } + }; var setOncePhotoCount = function() { var photoCount = parseInt(prompt('Input photo count to setOnce', '2'), 10); amplitude.identify(new amplitude.Identify().setOnce('photoCount', photoCount)); - } + };