From e814e097a241a9e8961bf21ce68f182200b7064c Mon Sep 17 00:00:00 2001 From: Daniel Jih Date: Thu, 3 Sep 2015 19:51:47 -0700 Subject: [PATCH 1/4] Added support for callback functions --- CHANGELOG.md | 4 + README.md | 17 +++++ amplitude.js | 47 +++++++++--- amplitude.min.js | 4 +- src/amplitude.js | 47 +++++++++--- test/amplitude.js | 191 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 284 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4eba2fc3..f4317452 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## Unreleased +* Add support for passing callback functions to logEvent + +## 2.3.0 (September 2, 2015) + * Add option to batch events into a single request. ## 2.2.1 (Aug 13, 2015) diff --git a/README.md b/README.md index 1e15ab87..9fcde349 100644 --- a/README.md +++ b/README.md @@ -118,3 +118,20 @@ User IDs are automatically generated and stored in cookies if not specified. Device IDs are generated randomly, although you can define a custom device ID setting it as a configuration option or by calling: amplitude.setDeviceId("CUSTOM_DEVICE_ID"); + +You can pass a callback function to logEvent, which will get called after receiving a response from the server: + + amplitude.logEvent("EVENT_IDENTIFIER_HERE", null, callback_function); + +The status and response from the server are passed to the callback function, which you might find useful. An example of a callback function which redirects the browser to another site after a response: + +'''javascript + var callback_function = function(status, response) { + if (status === 200 && response === 'success') { + // do something here + } + window.location.replace('URL_OF_OTHER_SITE'); + }; +''' + +In the case that `optOut` is true, then no event will be logged, but the callback will be run. In the case that `batchEvents` is true, if the batch requirements `eventUploadThreshold` and `eventUploadPeriodMillis` are not met when `logEvent` is called, then no request is sent, but the callback is still called. diff --git a/amplitude.js b/amplitude.js index 20f30ca4..ad1346be 100644 --- a/amplitude.js +++ b/amplitude.js @@ -262,21 +262,24 @@ Amplitude.prototype.nextEventId = function() { return this._eventId; }; -Amplitude.prototype._sendEventsIfReady = function() { +// returns true if sendEvents called +Amplitude.prototype._sendEventsIfReady = function(callback) { if (this._unsentEvents.length === 0) { - return; + return false; } if (!this.options.batchEvents) { - this.sendEvents(); - return; + this.sendEvents(callback); + return true; } if (this._unsentEvents.length >= this.options.eventUploadThreshold) { - this.sendEvents(); - } else { - setTimeout(this.sendEvents.bind(this), this.options.eventUploadPeriodMillis); + this.sendEvents(callback); + return true; } + + setTimeout(this.sendEvents.bind(this), this.options.eventUploadPeriodMillis); + return false; }; var _loadCookieData = function(scope) { @@ -431,8 +434,15 @@ Amplitude.prototype.setVersionName = function(versionName) { /** * Private logEvent method. Keeps apiProperties from being publicly exposed. */ -Amplitude.prototype._logEvent = function(eventType, eventProperties, apiProperties) { +Amplitude.prototype._logEvent = function(eventType, eventProperties, apiProperties, callback) { + if (typeof callback !== 'function') { + callback = null; + } + if (!eventType || this.options.optOut) { + if (callback) { + callback(0, 'No request sent'); + } return; } try { @@ -501,7 +511,9 @@ Amplitude.prototype._logEvent = function(eventType, eventProperties, apiProperti this.saveEvents(); } - this._sendEventsIfReady(); + if (!this._sendEventsIfReady(callback) && callback) { + callback(0, 'No request sent'); + } return eventId; } catch (e) { @@ -513,6 +525,10 @@ Amplitude.prototype.logEvent = function(eventType, eventProperties) { return this._logEvent(eventType, eventProperties); }; +Amplitude.prototype.logEvent = function(eventType, eventProperties, callback) { + return this._logEvent(eventType, eventProperties, null, callback); +}; + // Test that n is a number or a numeric value. var _isNumber = function(n) { return !isNaN(parseFloat(n)) && isFinite(n); @@ -547,7 +563,7 @@ Amplitude.prototype.removeEvents = function (maxEventId) { this._unsentEvents = filteredEvents; }; -Amplitude.prototype.sendEvents = function() { +Amplitude.prototype.sendEvents = function(callback) { if (!this._sending && !this.options.optOut && this._unsentEvents.length > 0) { this._sending = true; var url = ('https:' === window.location.protocol ? 'https' : 'http') + '://' + @@ -581,7 +597,9 @@ Amplitude.prototype.sendEvents = function() { } // Send more events if any queued during previous send. - scope._sendEventsIfReady(); + if (!scope._sendEventsIfReady(callback) && callback) { + callback(status, response); + } } else if (status === 413) { //log('request too large'); @@ -593,12 +611,17 @@ Amplitude.prototype.sendEvents = function() { // The server complained about the length of the request. // Backoff and try again. scope.options.uploadBatchSize = Math.ceil(numEvents / 2); - scope.sendEvents(); + scope.sendEvents(callback); + + } else if (callback) { // If server turns something like a 400 + callback(status, response); } } catch (e) { //log('failed upload'); } }); + } else if (callback) { + callback(0, 'No request sent'); } }; diff --git a/amplitude.min.js b/amplitude.min.js index c61d3e86..56a350b2 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 q=old._q||[];var instance=new Amplitude;for(var i=0;ithis.options.sessionTimeout){this._newSession=true;this._sessionId=now;localStorage.setItem(LocalStorageKeys.SESSION_ID,this._sessionId)}this._lastEventTime=now;localStorage.setItem(LocalStorageKeys.LAST_EVENT_TIME,this._lastEventTime)}catch(e){log(e)}};Amplitude.prototype.isNewSession=function(){return this._newSession};Amplitude.prototype.nextEventId=function(){this._eventId++;return this._eventId};Amplitude.prototype._sendEventsIfReady=function(){if(this._unsentEvents.length===0){return}if(!this.options.batchEvents){this.sendEvents();return}if(this._unsentEvents.length>=this.options.eventUploadThreshold){this.sendEvents()}else{setTimeout(this.sendEvents.bind(this),this.options.eventUploadPeriodMillis)}};var _loadCookieData=function(scope){var cookieData=Cookie.get(scope.options.cookieName);if(cookieData){if(cookieData.deviceId){scope.options.deviceId=cookieData.deviceId}if(cookieData.userId){scope.options.userId=cookieData.userId}if(cookieData.globalUserProperties){scope.options.userProperties=cookieData.globalUserProperties}if(cookieData.optOut!==undefined){scope.options.optOut=cookieData.optOut}}};var _saveCookieData=function(scope){Cookie.set(scope.options.cookieName,{deviceId:scope.options.deviceId,userId:scope.options.userId,globalUserProperties:scope.options.userProperties,optOut:scope.options.optOut})};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||Cookie.get("__utmz");this._utmProperties=Amplitude._getUtmData(cookieParams,queryParams)};Amplitude.prototype._getReferrer=function(){return document.referrer};Amplitude.prototype._getReferringDomain=function(){var parts=this._getReferrer().split("/");if(parts.length>=3){return parts[2]}return""};Amplitude.prototype.saveEvents=function(){try{localStorage.setItem(this.options.unsentKey,JSON.stringify(this._unsentEvents))}catch(e){}};Amplitude.prototype.setDomain=function(domain){try{Cookie.options({domain:domain});this.options.domain=Cookie.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,opt_replace){try{if(opt_replace){this.options.userProperties=userProperties}else{this.options.userProperties=object.merge(this.options.userProperties||{},userProperties)}_saveCookieData(this)}catch(e){log(e)}};Amplitude.prototype.setVersionName=function(versionName){try{this.options.versionName=versionName}catch(e){log(e)}};Amplitude.prototype._logEvent=function(eventType,eventProperties,apiProperties){if(!eventType||this.options.optOut){return}try{var eventTime=(new Date).getTime();var eventId=this.nextEventId();var ua=this._ua;if(!this._sessionId||!this._lastEventTime||eventTime-this._lastEventTime>this.options.sessionTimeout){this._sessionId=eventTime;localStorage.setItem(LocalStorageKeys.SESSION_ID,this._sessionId)}this._lastEventTime=eventTime;localStorage.setItem(LocalStorageKeys.LAST_EVENT_TIME,this._lastEventTime);localStorage.setItem(LocalStorageKeys.LAST_EVENT_ID,eventId);var userProperties={};object.merge(userProperties,this.options.userProperties||{});object.merge(userProperties,this._utmProperties);if(this.options.includeReferrer){object.merge(userProperties,{referrer:this._getReferrer(),referring_domain:this._getReferringDomain()})}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:eventProperties,user_properties:userProperties,uuid:UUID(),library:{name:"amplitude-js",version:this.__VERSION__}};this._unsentEvents.push(event);if(this._unsentEvents.length>this.options.savedMaxCount){this._unsentEvents.splice(0,this._unsentEvents.length-this.options.savedMaxCount)}if(this.options.saveEvents){this.saveEvents()}this._sendEventsIfReady();return eventId}catch(e){log(e)}};Amplitude.prototype.logEvent=function(eventType,eventProperties){return this._logEvent(eventType,eventProperties)};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){var filteredEvents=[];for(var i=0;imaxEventId){filteredEvents.push(this._unsentEvents[i])}}this._unsentEvents=filteredEvents};Amplitude.prototype.sendEvents=function(){if(!this._sending&&!this.options.optOut&&this._unsentEvents.length>0){this._sending=true;var url=("https:"===window.location.protocol?"https":"http")+"://"+this.options.apiEndpoint+"/";var numEvents=Math.min(this._unsentEvents.length,this.options.uploadBatchSize);var maxEventId=this._unsentEvents[numEvents-1].event_id;var events=JSON.stringify(this._unsentEvents.slice(0,numEvents));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);if(scope.options.saveEvents){scope.saveEvents()}scope._sendEventsIfReady()}else if(status===413){if(scope.options.uploadBatchSize===1){scope.removeEvents(maxEventId)}scope.options.uploadBatchSize=Math.ceil(numEvents/2);scope.sendEvents()}}catch(e){}})}};Amplitude.prototype.setGlobalUserProperties=Amplitude.prototype.setUserProperties;Amplitude.prototype.__VERSION__=version;module.exports=Amplitude},{"./cookie":3,json:4,"./language":5,"./localstorage":6,"JavaScript-MD5":7,object:8,"./xhr":9,"ua-parser-js":10,"./uuid":11,"./version":12}],3:[function(require,module,exports){var Base64=require("./base64");var JSON=require("json");var topDomain=require("top-domain");var _options={expirationDays:undefined,domain:undefined};var reset=function(){_options={}};var options=function(opts){if(arguments.length===0){return _options}opts=opts||{};_options.expirationDays=opts.expirationDays;var domain=opts.domain!==undefined?opts.domain:"."+topDomain(window.location.href);var token=Math.random();_options.domain=domain;set("amplitude_test",token);var stored=get("amplitude_test");if(!stored||stored!==token){domain=null}remove("amplitude_test");_options.domain=domain};var _domainSpecific=function(name){var suffix="";if(_options.domain){suffix=_options.domain.charAt(0)==="."?_options.domain.substring(1):_options.domain}return name+suffix};var get=function(name){try{var nameEq=_domainSpecific(name)+"=";var ca=document.cookie.split(";");var value=null;for(var i=0;i>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":15}],15:[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":16}],16:[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(xdr.responseText)};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:18}],18:[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.3.0"},{}]},{},{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 q=old._q||[];var instance=new Amplitude;for(var i=0;ithis.options.sessionTimeout){this._newSession=true;this._sessionId=now;localStorage.setItem(LocalStorageKeys.SESSION_ID,this._sessionId)}this._lastEventTime=now;localStorage.setItem(LocalStorageKeys.LAST_EVENT_TIME,this._lastEventTime)}catch(e){log(e)}};Amplitude.prototype.isNewSession=function(){return this._newSession};Amplitude.prototype.nextEventId=function(){this._eventId++;return this._eventId};Amplitude.prototype._sendEventsIfReady=function(callback){if(this._unsentEvents.length===0){return false}if(!this.options.batchEvents){this.sendEvents(callback);return true}if(this._unsentEvents.length>=this.options.eventUploadThreshold){this.sendEvents(callback);return true}setTimeout(this.sendEvents.bind(this),this.options.eventUploadPeriodMillis);return false};var _loadCookieData=function(scope){var cookieData=Cookie.get(scope.options.cookieName);if(cookieData){if(cookieData.deviceId){scope.options.deviceId=cookieData.deviceId}if(cookieData.userId){scope.options.userId=cookieData.userId}if(cookieData.globalUserProperties){scope.options.userProperties=cookieData.globalUserProperties}if(cookieData.optOut!==undefined){scope.options.optOut=cookieData.optOut}}};var _saveCookieData=function(scope){Cookie.set(scope.options.cookieName,{deviceId:scope.options.deviceId,userId:scope.options.userId,globalUserProperties:scope.options.userProperties,optOut:scope.options.optOut})};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||Cookie.get("__utmz");this._utmProperties=Amplitude._getUtmData(cookieParams,queryParams)};Amplitude.prototype._getReferrer=function(){return document.referrer};Amplitude.prototype._getReferringDomain=function(){var parts=this._getReferrer().split("/");if(parts.length>=3){return parts[2]}return""};Amplitude.prototype.saveEvents=function(){try{localStorage.setItem(this.options.unsentKey,JSON.stringify(this._unsentEvents))}catch(e){}};Amplitude.prototype.setDomain=function(domain){try{Cookie.options({domain:domain});this.options.domain=Cookie.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,opt_replace){try{if(opt_replace){this.options.userProperties=userProperties}else{this.options.userProperties=object.merge(this.options.userProperties||{},userProperties)}_saveCookieData(this)}catch(e){log(e)}};Amplitude.prototype.setVersionName=function(versionName){try{this.options.versionName=versionName}catch(e){log(e)}};Amplitude.prototype._logEvent=function(eventType,eventProperties,apiProperties,callback){if(typeof callback!=="function"){callback=null}if(!eventType||this.options.optOut){if(callback){callback(0,"No request sent")}return}try{var eventTime=(new Date).getTime();var eventId=this.nextEventId();var ua=this._ua;if(!this._sessionId||!this._lastEventTime||eventTime-this._lastEventTime>this.options.sessionTimeout){this._sessionId=eventTime;localStorage.setItem(LocalStorageKeys.SESSION_ID,this._sessionId)}this._lastEventTime=eventTime;localStorage.setItem(LocalStorageKeys.LAST_EVENT_TIME,this._lastEventTime);localStorage.setItem(LocalStorageKeys.LAST_EVENT_ID,eventId);var userProperties={};object.merge(userProperties,this.options.userProperties||{});object.merge(userProperties,this._utmProperties);if(this.options.includeReferrer){object.merge(userProperties,{referrer:this._getReferrer(),referring_domain:this._getReferringDomain()})}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:eventProperties,user_properties:userProperties,uuid:UUID(),library:{name:"amplitude-js",version:this.__VERSION__}};this._unsentEvents.push(event);if(this._unsentEvents.length>this.options.savedMaxCount){this._unsentEvents.splice(0,this._unsentEvents.length-this.options.savedMaxCount)}if(this.options.saveEvents){this.saveEvents()}if(!this._sendEventsIfReady(callback)&&callback){callback(0,"No request sent")}return eventId}catch(e){log(e)}};Amplitude.prototype.logEvent=function(eventType,eventProperties){return this._logEvent(eventType,eventProperties)};Amplitude.prototype.logEvent=function(eventType,eventProperties,callback){return this._logEvent(eventType,eventProperties,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){var filteredEvents=[];for(var i=0;imaxEventId){filteredEvents.push(this._unsentEvents[i])}}this._unsentEvents=filteredEvents};Amplitude.prototype.sendEvents=function(callback){if(!this._sending&&!this.options.optOut&&this._unsentEvents.length>0){this._sending=true;var url=("https:"===window.location.protocol?"https":"http")+"://"+this.options.apiEndpoint+"/";var numEvents=Math.min(this._unsentEvents.length,this.options.uploadBatchSize);var maxEventId=this._unsentEvents[numEvents-1].event_id;var events=JSON.stringify(this._unsentEvents.slice(0,numEvents));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);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)}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.setGlobalUserProperties=Amplitude.prototype.setUserProperties;Amplitude.prototype.__VERSION__=version;module.exports=Amplitude},{"./cookie":3,json:4,"./language":5,"./localstorage":6,"JavaScript-MD5":7,object:8,"./xhr":9,"ua-parser-js":10,"./uuid":11,"./version":12}],3:[function(require,module,exports){var Base64=require("./base64");var JSON=require("json");var topDomain=require("top-domain");var _options={expirationDays:undefined,domain:undefined};var reset=function(){_options={}};var options=function(opts){if(arguments.length===0){return _options}opts=opts||{};_options.expirationDays=opts.expirationDays;var domain=opts.domain!==undefined?opts.domain:"."+topDomain(window.location.href);var token=Math.random();_options.domain=domain;set("amplitude_test",token);var stored=get("amplitude_test");if(!stored||stored!==token){domain=null}remove("amplitude_test");_options.domain=domain};var _domainSpecific=function(name){var suffix="";if(_options.domain){suffix=_options.domain.charAt(0)==="."?_options.domain.substring(1):_options.domain}return name+suffix};var get=function(name){try{var nameEq=_domainSpecific(name)+"=";var ca=document.cookie.split(";");var value=null;for(var i=0;i>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":15}],15:[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":16}],16:[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(xdr.responseText)};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:18}],18:[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.3.0"},{}]},{},{1:""})); \ No newline at end of file diff --git a/src/amplitude.js b/src/amplitude.js index a56ec7d7..8f9a73ae 100644 --- a/src/amplitude.js +++ b/src/amplitude.js @@ -150,21 +150,24 @@ Amplitude.prototype.nextEventId = function() { return this._eventId; }; -Amplitude.prototype._sendEventsIfReady = function() { +// returns true if sendEvents called +Amplitude.prototype._sendEventsIfReady = function(callback) { if (this._unsentEvents.length === 0) { - return; + return false; } if (!this.options.batchEvents) { - this.sendEvents(); - return; + this.sendEvents(callback); + return true; } if (this._unsentEvents.length >= this.options.eventUploadThreshold) { - this.sendEvents(); - } else { - setTimeout(this.sendEvents.bind(this), this.options.eventUploadPeriodMillis); + this.sendEvents(callback); + return true; } + + setTimeout(this.sendEvents.bind(this), this.options.eventUploadPeriodMillis); + return false; }; var _loadCookieData = function(scope) { @@ -319,8 +322,15 @@ Amplitude.prototype.setVersionName = function(versionName) { /** * Private logEvent method. Keeps apiProperties from being publicly exposed. */ -Amplitude.prototype._logEvent = function(eventType, eventProperties, apiProperties) { +Amplitude.prototype._logEvent = function(eventType, eventProperties, apiProperties, callback) { + if (typeof callback !== 'function') { + callback = null; + } + if (!eventType || this.options.optOut) { + if (callback) { + callback(0, 'No request sent'); + } return; } try { @@ -389,7 +399,9 @@ Amplitude.prototype._logEvent = function(eventType, eventProperties, apiProperti this.saveEvents(); } - this._sendEventsIfReady(); + if (!this._sendEventsIfReady(callback) && callback) { + callback(0, 'No request sent'); + } return eventId; } catch (e) { @@ -401,6 +413,10 @@ Amplitude.prototype.logEvent = function(eventType, eventProperties) { return this._logEvent(eventType, eventProperties); }; +Amplitude.prototype.logEvent = function(eventType, eventProperties, callback) { + return this._logEvent(eventType, eventProperties, null, callback); +}; + // Test that n is a number or a numeric value. var _isNumber = function(n) { return !isNaN(parseFloat(n)) && isFinite(n); @@ -435,7 +451,7 @@ Amplitude.prototype.removeEvents = function (maxEventId) { this._unsentEvents = filteredEvents; }; -Amplitude.prototype.sendEvents = function() { +Amplitude.prototype.sendEvents = function(callback) { if (!this._sending && !this.options.optOut && this._unsentEvents.length > 0) { this._sending = true; var url = ('https:' === window.location.protocol ? 'https' : 'http') + '://' + @@ -469,7 +485,9 @@ Amplitude.prototype.sendEvents = function() { } // Send more events if any queued during previous send. - scope._sendEventsIfReady(); + if (!scope._sendEventsIfReady(callback) && callback) { + callback(status, response); + } } else if (status === 413) { //log('request too large'); @@ -481,12 +499,17 @@ Amplitude.prototype.sendEvents = function() { // The server complained about the length of the request. // Backoff and try again. scope.options.uploadBatchSize = Math.ceil(numEvents / 2); - scope.sendEvents(); + scope.sendEvents(callback); + + } else if (callback) { // If server turns something like a 400 + callback(status, response); } } catch (e) { //log('failed upload'); } }); + } else if (callback) { + callback(0, 'No request sent'); } }; diff --git a/test/amplitude.js b/test/amplitude.js index 158c6e03..08e9c418 100644 --- a/test/amplitude.js +++ b/test/amplitude.js @@ -423,6 +423,197 @@ describe('Amplitude', function() { assert.lengthOf(events, 1); assert.deepEqual(events[0].event_properties, {index: 2}); }); + + it ('should run callback if no eventType', function () { + var counter = 0; + var callback = function (status, response) { counter++; } + amplitude.logEvent(null, null, callback); + assert.equal(counter, 1); + }); + + it ('should run callback if optout', function () { + amplitude.setOptOut(true); + var counter = 0; + var value = -1; + var message = ''; + var callback = function (status, response) { + counter++; + value = status; + message = response; + }; + amplitude.logEvent('test', null, callback); + assert.equal(counter, 1); + assert.equal(value, 0); + assert.equal(message, 'No request sent'); + }); + + it ('should not run callback if invalid callback and no eventType', function () { + amplitude.logEvent(null, null, 'invalid callback'); + }); + + it ('should run callback after logging event', function () { + var counter = 0; + var value = -1; + var message = ''; + var callback = function (status, response) { + counter++; + value = status; + message = response; + }; + amplitude.logEvent('test', null, callback); + + // before server responds, callback should not fire + assert.lengthOf(server.requests, 1); + assert.equal(counter, 0); + assert.equal(value, -1); + assert.equal(message, ''); + + // after server response, fire callback + server.respondWith('success'); + server.respond(); + assert.equal(counter, 1); + assert.equal(value, 200); + assert.equal(message, 'success'); + }); + + it ('should run callback if batchEvents but under threshold', function () { + var eventUploadPeriodMillis = 5*1000; + amplitude.init(apiKey, null, { + batchEvents: true, + eventUploadThreshold: 2, + eventUploadPeriodMillis: eventUploadPeriodMillis + }); + var counter = 0; + var value = -1; + var message = ''; + var callback = function (status, response) { + counter++; + value = status; + message = response; + }; + amplitude.logEvent('test', null, callback); + assert.lengthOf(server.requests, 0); + assert.equal(counter, 1); + assert.equal(value, 0); + assert.equal(message, 'No request sent'); + + // check that request is made after delay, but callback is not run a second time + clock.tick(eventUploadPeriodMillis); + assert.lengthOf(server.requests, 1); + server.respondWith('success'); + server.respond(); + assert.equal(counter, 1); + }); + + it ('should run callback once and only after all events are uploaded', function () { + amplitude.init(apiKey, null, {uploadBatchSize: 10}); + var counter = 0; + var value = -1; + var message = ''; + var callback = function (status, response) { + counter++; + value = status; + message = response; + }; + + // queue up 15 events, since batchsize 10, need to send in 2 batches + amplitude._sending = true; + for (var i = 0; i < 15; i++) { + amplitude.logEvent('Event', {index: i}); + } + amplitude._sending = false; + + amplitude.logEvent('Event', {index: 100}, callback); + + assert.lengthOf(server.requests, 1); + server.respondWith('success'); + server.respond(); + + // after first response received, callback should not have fired + assert.equal(counter, 0); + assert.equal(value, -1); + assert.equal(message, ''); + + assert.lengthOf(server.requests, 2); + server.respondWith('success'); + server.respond(); + + // after last response received, callback should fire + assert.equal(counter, 1); + assert.equal(value, 200); + assert.equal(message, 'success'); + }); + + it ('should run callback once and only after 413 resolved', function () { + var counter = 0; + var value = -1; + var message = ''; + var callback = function (status, response) { + counter++; + value = status; + message = response; + }; + + // queue up 15 events + amplitude._sending = true; + for (var i = 0; i < 15; i++) { + amplitude.logEvent('Event', {index: i}); + } + amplitude._sending = false; + + // 16th event with 413 will backoff to batches of 8 + amplitude.logEvent('Event', {index: 100}, callback); + + assert.lengthOf(server.requests, 1); + var events = JSON.parse(querystring.parse(server.requests[0].requestBody).e); + assert.lengthOf(events, 16); + + // after 413 response received, callback should not have fired + server.respondWith([413, {}, ""]); + server.respond(); + assert.equal(counter, 0); + assert.equal(value, -1); + assert.equal(message, ''); + + // after sending first backoff batch, callback still should not have fired + assert.lengthOf(server.requests, 2); + var events = JSON.parse(querystring.parse(server.requests[1].requestBody).e); + assert.lengthOf(events, 8); + server.respondWith('success'); + server.respond(); + assert.equal(counter, 0); + assert.equal(value, -1); + assert.equal(message, ''); + + // after sending second backoff batch, callback should fire + assert.lengthOf(server.requests, 3); + var events = JSON.parse(querystring.parse(server.requests[1].requestBody).e); + assert.lengthOf(events, 8); + server.respondWith('success'); + server.respond(); + assert.equal(counter, 1); + assert.equal(value, 200); + assert.equal(message, 'success'); + }); + + it ('should fire callback if server returns something other than 200 and 413', function () { + var counter = 0; + var value = -1; + var message = ''; + var callback = function (status, response) { + counter++; + value = status; + message = response; + }; + + amplitude.logEvent('test', null, callback); + server.respondWith([404, {}, 'Not found']); + server.respond(); + assert.equal(counter, 1); + assert.equal(value, 404); + assert.equal(message, 'Not found'); + }); + }); describe('optOut', function() { From 4718ebab6405c14285b64ef5c61c198a3917dbca Mon Sep 17 00:00:00 2001 From: Daniel Jih Date: Thu, 3 Sep 2015 19:56:03 -0700 Subject: [PATCH 2/4] update readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9fcde349..1daddea1 100644 --- a/README.md +++ b/README.md @@ -125,13 +125,13 @@ You can pass a callback function to logEvent, which will get called after receiv The status and response from the server are passed to the callback function, which you might find useful. An example of a callback function which redirects the browser to another site after a response: -'''javascript +```javascript var callback_function = function(status, response) { if (status === 200 && response === 'success') { // do something here } window.location.replace('URL_OF_OTHER_SITE'); }; -''' +``` -In the case that `optOut` is true, then no event will be logged, but the callback will be run. In the case that `batchEvents` is true, if the batch requirements `eventUploadThreshold` and `eventUploadPeriodMillis` are not met when `logEvent` is called, then no request is sent, but the callback is still called. +In the case that `optOut` is true, then no event will be logged, but the callback will be called. In the case that `batchEvents` is true, if the batch requirements `eventUploadThreshold` and `eventUploadPeriodMillis` are not met when `logEvent` is called, then no request is sent, but the callback is still called. In these cases, the callback will be called with an input status of 0 and response 'No request sent'. From 16a6ff14f726d9fc5677e4041847959b610496e3 Mon Sep 17 00:00:00 2001 From: Daniel Jih Date: Thu, 3 Sep 2015 20:02:25 -0700 Subject: [PATCH 3/4] updated test --- amplitude.js | 2 +- src/amplitude.js | 2 +- test/amplitude.js | 10 +++++++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/amplitude.js b/amplitude.js index ad1346be..e5ade629 100644 --- a/amplitude.js +++ b/amplitude.js @@ -262,7 +262,7 @@ Amplitude.prototype.nextEventId = function() { return this._eventId; }; -// returns true if sendEvents called +// returns true if sendEvents called immediately Amplitude.prototype._sendEventsIfReady = function(callback) { if (this._unsentEvents.length === 0) { return false; diff --git a/src/amplitude.js b/src/amplitude.js index 8f9a73ae..63c1319c 100644 --- a/src/amplitude.js +++ b/src/amplitude.js @@ -150,7 +150,7 @@ Amplitude.prototype.nextEventId = function() { return this._eventId; }; -// returns true if sendEvents called +// returns true if sendEvents called immediately Amplitude.prototype._sendEventsIfReady = function(callback) { if (this._unsentEvents.length === 0) { return false; diff --git a/test/amplitude.js b/test/amplitude.js index 08e9c418..7b042d49 100644 --- a/test/amplitude.js +++ b/test/amplitude.js @@ -426,9 +426,17 @@ describe('Amplitude', function() { it ('should run callback if no eventType', function () { var counter = 0; - var callback = function (status, response) { counter++; } + var value = -1; + var message = ''; + var callback = function (status, response) { + counter++; + value = status; + message = response; + } amplitude.logEvent(null, null, callback); assert.equal(counter, 1); + assert.equal(value, 0); + assert.equal(message, 'No request sent'); }); it ('should run callback if optout', function () { From 61d53da7270cead4995e5cc064180c8510682734 Mon Sep 17 00:00:00 2001 From: Daniel Jih Date: Fri, 4 Sep 2015 01:42:19 -0700 Subject: [PATCH 4/4] removed extra function --- amplitude.js | 4 ---- amplitude.min.js | 4 ++-- src/amplitude.js | 4 ---- test/amplitude.js | 2 +- 4 files changed, 3 insertions(+), 11 deletions(-) diff --git a/amplitude.js b/amplitude.js index e5ade629..ba280589 100644 --- a/amplitude.js +++ b/amplitude.js @@ -521,10 +521,6 @@ Amplitude.prototype._logEvent = function(eventType, eventProperties, apiProperti } }; -Amplitude.prototype.logEvent = function(eventType, eventProperties) { - return this._logEvent(eventType, eventProperties); -}; - Amplitude.prototype.logEvent = function(eventType, eventProperties, callback) { return this._logEvent(eventType, eventProperties, null, callback); }; diff --git a/amplitude.min.js b/amplitude.min.js index 56a350b2..681583f9 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 q=old._q||[];var instance=new Amplitude;for(var i=0;ithis.options.sessionTimeout){this._newSession=true;this._sessionId=now;localStorage.setItem(LocalStorageKeys.SESSION_ID,this._sessionId)}this._lastEventTime=now;localStorage.setItem(LocalStorageKeys.LAST_EVENT_TIME,this._lastEventTime)}catch(e){log(e)}};Amplitude.prototype.isNewSession=function(){return this._newSession};Amplitude.prototype.nextEventId=function(){this._eventId++;return this._eventId};Amplitude.prototype._sendEventsIfReady=function(callback){if(this._unsentEvents.length===0){return false}if(!this.options.batchEvents){this.sendEvents(callback);return true}if(this._unsentEvents.length>=this.options.eventUploadThreshold){this.sendEvents(callback);return true}setTimeout(this.sendEvents.bind(this),this.options.eventUploadPeriodMillis);return false};var _loadCookieData=function(scope){var cookieData=Cookie.get(scope.options.cookieName);if(cookieData){if(cookieData.deviceId){scope.options.deviceId=cookieData.deviceId}if(cookieData.userId){scope.options.userId=cookieData.userId}if(cookieData.globalUserProperties){scope.options.userProperties=cookieData.globalUserProperties}if(cookieData.optOut!==undefined){scope.options.optOut=cookieData.optOut}}};var _saveCookieData=function(scope){Cookie.set(scope.options.cookieName,{deviceId:scope.options.deviceId,userId:scope.options.userId,globalUserProperties:scope.options.userProperties,optOut:scope.options.optOut})};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||Cookie.get("__utmz");this._utmProperties=Amplitude._getUtmData(cookieParams,queryParams)};Amplitude.prototype._getReferrer=function(){return document.referrer};Amplitude.prototype._getReferringDomain=function(){var parts=this._getReferrer().split("/");if(parts.length>=3){return parts[2]}return""};Amplitude.prototype.saveEvents=function(){try{localStorage.setItem(this.options.unsentKey,JSON.stringify(this._unsentEvents))}catch(e){}};Amplitude.prototype.setDomain=function(domain){try{Cookie.options({domain:domain});this.options.domain=Cookie.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,opt_replace){try{if(opt_replace){this.options.userProperties=userProperties}else{this.options.userProperties=object.merge(this.options.userProperties||{},userProperties)}_saveCookieData(this)}catch(e){log(e)}};Amplitude.prototype.setVersionName=function(versionName){try{this.options.versionName=versionName}catch(e){log(e)}};Amplitude.prototype._logEvent=function(eventType,eventProperties,apiProperties,callback){if(typeof callback!=="function"){callback=null}if(!eventType||this.options.optOut){if(callback){callback(0,"No request sent")}return}try{var eventTime=(new Date).getTime();var eventId=this.nextEventId();var ua=this._ua;if(!this._sessionId||!this._lastEventTime||eventTime-this._lastEventTime>this.options.sessionTimeout){this._sessionId=eventTime;localStorage.setItem(LocalStorageKeys.SESSION_ID,this._sessionId)}this._lastEventTime=eventTime;localStorage.setItem(LocalStorageKeys.LAST_EVENT_TIME,this._lastEventTime);localStorage.setItem(LocalStorageKeys.LAST_EVENT_ID,eventId);var userProperties={};object.merge(userProperties,this.options.userProperties||{});object.merge(userProperties,this._utmProperties);if(this.options.includeReferrer){object.merge(userProperties,{referrer:this._getReferrer(),referring_domain:this._getReferringDomain()})}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:eventProperties,user_properties:userProperties,uuid:UUID(),library:{name:"amplitude-js",version:this.__VERSION__}};this._unsentEvents.push(event);if(this._unsentEvents.length>this.options.savedMaxCount){this._unsentEvents.splice(0,this._unsentEvents.length-this.options.savedMaxCount)}if(this.options.saveEvents){this.saveEvents()}if(!this._sendEventsIfReady(callback)&&callback){callback(0,"No request sent")}return eventId}catch(e){log(e)}};Amplitude.prototype.logEvent=function(eventType,eventProperties){return this._logEvent(eventType,eventProperties)};Amplitude.prototype.logEvent=function(eventType,eventProperties,callback){return this._logEvent(eventType,eventProperties,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){var filteredEvents=[];for(var i=0;imaxEventId){filteredEvents.push(this._unsentEvents[i])}}this._unsentEvents=filteredEvents};Amplitude.prototype.sendEvents=function(callback){if(!this._sending&&!this.options.optOut&&this._unsentEvents.length>0){this._sending=true;var url=("https:"===window.location.protocol?"https":"http")+"://"+this.options.apiEndpoint+"/";var numEvents=Math.min(this._unsentEvents.length,this.options.uploadBatchSize);var maxEventId=this._unsentEvents[numEvents-1].event_id;var events=JSON.stringify(this._unsentEvents.slice(0,numEvents));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);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)}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.setGlobalUserProperties=Amplitude.prototype.setUserProperties;Amplitude.prototype.__VERSION__=version;module.exports=Amplitude},{"./cookie":3,json:4,"./language":5,"./localstorage":6,"JavaScript-MD5":7,object:8,"./xhr":9,"ua-parser-js":10,"./uuid":11,"./version":12}],3:[function(require,module,exports){var Base64=require("./base64");var JSON=require("json");var topDomain=require("top-domain");var _options={expirationDays:undefined,domain:undefined};var reset=function(){_options={}};var options=function(opts){if(arguments.length===0){return _options}opts=opts||{};_options.expirationDays=opts.expirationDays;var domain=opts.domain!==undefined?opts.domain:"."+topDomain(window.location.href);var token=Math.random();_options.domain=domain;set("amplitude_test",token);var stored=get("amplitude_test");if(!stored||stored!==token){domain=null}remove("amplitude_test");_options.domain=domain};var _domainSpecific=function(name){var suffix="";if(_options.domain){suffix=_options.domain.charAt(0)==="."?_options.domain.substring(1):_options.domain}return name+suffix};var get=function(name){try{var nameEq=_domainSpecific(name)+"=";var ca=document.cookie.split(";");var value=null;for(var i=0;i>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":15}],15:[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":16}],16:[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(xdr.responseText)};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:18}],18:[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.3.0"},{}]},{},{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 q=old._q||[];var instance=new Amplitude;for(var i=0;ithis.options.sessionTimeout){this._newSession=true;this._sessionId=now;localStorage.setItem(LocalStorageKeys.SESSION_ID,this._sessionId)}this._lastEventTime=now;localStorage.setItem(LocalStorageKeys.LAST_EVENT_TIME,this._lastEventTime)}catch(e){log(e)}};Amplitude.prototype.isNewSession=function(){return this._newSession};Amplitude.prototype.nextEventId=function(){this._eventId++;return this._eventId};Amplitude.prototype._sendEventsIfReady=function(callback){if(this._unsentEvents.length===0){return false}if(!this.options.batchEvents){this.sendEvents(callback);return true}if(this._unsentEvents.length>=this.options.eventUploadThreshold){this.sendEvents(callback);return true}setTimeout(this.sendEvents.bind(this),this.options.eventUploadPeriodMillis);return false};var _loadCookieData=function(scope){var cookieData=Cookie.get(scope.options.cookieName);if(cookieData){if(cookieData.deviceId){scope.options.deviceId=cookieData.deviceId}if(cookieData.userId){scope.options.userId=cookieData.userId}if(cookieData.globalUserProperties){scope.options.userProperties=cookieData.globalUserProperties}if(cookieData.optOut!==undefined){scope.options.optOut=cookieData.optOut}}};var _saveCookieData=function(scope){Cookie.set(scope.options.cookieName,{deviceId:scope.options.deviceId,userId:scope.options.userId,globalUserProperties:scope.options.userProperties,optOut:scope.options.optOut})};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||Cookie.get("__utmz");this._utmProperties=Amplitude._getUtmData(cookieParams,queryParams)};Amplitude.prototype._getReferrer=function(){return document.referrer};Amplitude.prototype._getReferringDomain=function(){var parts=this._getReferrer().split("/");if(parts.length>=3){return parts[2]}return""};Amplitude.prototype.saveEvents=function(){try{localStorage.setItem(this.options.unsentKey,JSON.stringify(this._unsentEvents))}catch(e){}};Amplitude.prototype.setDomain=function(domain){try{Cookie.options({domain:domain});this.options.domain=Cookie.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,opt_replace){try{if(opt_replace){this.options.userProperties=userProperties}else{this.options.userProperties=object.merge(this.options.userProperties||{},userProperties)}_saveCookieData(this)}catch(e){log(e)}};Amplitude.prototype.setVersionName=function(versionName){try{this.options.versionName=versionName}catch(e){log(e)}};Amplitude.prototype._logEvent=function(eventType,eventProperties,apiProperties,callback){if(typeof callback!=="function"){callback=null}if(!eventType||this.options.optOut){if(callback){callback(0,"No request sent")}return}try{var eventTime=(new Date).getTime();var eventId=this.nextEventId();var ua=this._ua;if(!this._sessionId||!this._lastEventTime||eventTime-this._lastEventTime>this.options.sessionTimeout){this._sessionId=eventTime;localStorage.setItem(LocalStorageKeys.SESSION_ID,this._sessionId)}this._lastEventTime=eventTime;localStorage.setItem(LocalStorageKeys.LAST_EVENT_TIME,this._lastEventTime);localStorage.setItem(LocalStorageKeys.LAST_EVENT_ID,eventId);var userProperties={};object.merge(userProperties,this.options.userProperties||{});object.merge(userProperties,this._utmProperties);if(this.options.includeReferrer){object.merge(userProperties,{referrer:this._getReferrer(),referring_domain:this._getReferringDomain()})}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:eventProperties,user_properties:userProperties,uuid:UUID(),library:{name:"amplitude-js",version:this.__VERSION__}};this._unsentEvents.push(event);if(this._unsentEvents.length>this.options.savedMaxCount){this._unsentEvents.splice(0,this._unsentEvents.length-this.options.savedMaxCount)}if(this.options.saveEvents){this.saveEvents()}if(!this._sendEventsIfReady(callback)&&callback){callback(0,"No request sent")}return eventId}catch(e){log(e)}};Amplitude.prototype.logEvent=function(eventType,eventProperties,callback){return this._logEvent(eventType,eventProperties,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){var filteredEvents=[];for(var i=0;imaxEventId){filteredEvents.push(this._unsentEvents[i])}}this._unsentEvents=filteredEvents};Amplitude.prototype.sendEvents=function(callback){if(!this._sending&&!this.options.optOut&&this._unsentEvents.length>0){this._sending=true;var url=("https:"===window.location.protocol?"https":"http")+"://"+this.options.apiEndpoint+"/";var numEvents=Math.min(this._unsentEvents.length,this.options.uploadBatchSize);var maxEventId=this._unsentEvents[numEvents-1].event_id;var events=JSON.stringify(this._unsentEvents.slice(0,numEvents));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);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)}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.setGlobalUserProperties=Amplitude.prototype.setUserProperties;Amplitude.prototype.__VERSION__=version;module.exports=Amplitude},{"./cookie":3,json:4,"./language":5,"./localstorage":6,"JavaScript-MD5":7,object:8,"./xhr":9,"ua-parser-js":10,"./uuid":11,"./version":12}],3:[function(require,module,exports){var Base64=require("./base64");var JSON=require("json");var topDomain=require("top-domain");var _options={expirationDays:undefined,domain:undefined};var reset=function(){_options={}};var options=function(opts){if(arguments.length===0){return _options}opts=opts||{};_options.expirationDays=opts.expirationDays;var domain=opts.domain!==undefined?opts.domain:"."+topDomain(window.location.href);var token=Math.random();_options.domain=domain;set("amplitude_test",token);var stored=get("amplitude_test");if(!stored||stored!==token){domain=null}remove("amplitude_test");_options.domain=domain};var _domainSpecific=function(name){var suffix="";if(_options.domain){suffix=_options.domain.charAt(0)==="."?_options.domain.substring(1):_options.domain}return name+suffix};var get=function(name){try{var nameEq=_domainSpecific(name)+"=";var ca=document.cookie.split(";");var value=null;for(var i=0;i>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":15}],15:[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":16}],16:[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(xdr.responseText)};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:18}],18:[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.3.0"},{}]},{},{1:""})); \ No newline at end of file diff --git a/src/amplitude.js b/src/amplitude.js index 63c1319c..17062bb9 100644 --- a/src/amplitude.js +++ b/src/amplitude.js @@ -409,10 +409,6 @@ Amplitude.prototype._logEvent = function(eventType, eventProperties, apiProperti } }; -Amplitude.prototype.logEvent = function(eventType, eventProperties) { - return this._logEvent(eventType, eventProperties); -}; - Amplitude.prototype.logEvent = function(eventType, eventProperties, callback) { return this._logEvent(eventType, eventProperties, null, callback); }; diff --git a/test/amplitude.js b/test/amplitude.js index 7b042d49..df3a2be2 100644 --- a/test/amplitude.js +++ b/test/amplitude.js @@ -604,7 +604,7 @@ describe('Amplitude', function() { assert.equal(message, 'success'); }); - it ('should fire callback if server returns something other than 200 and 413', function () { + it ('should run callback if server returns something other than 200 and 413', function () { var counter = 0; var value = -1; var message = '';