Browse Source

Merge branch 'master' of https://github.com/keen/keen-tracking.js into FIX-deepExtend-undefined-bug

# Conflicts:
#	dist/keen-tracking.min.js
master
aroc 9 years ago
parent
commit
d5fafb967f
  1. 2
      .gitignore
  2. 19
      CHANGELOG.md
  3. 27
      README.md
  4. 1156
      dist/keen-tracking.js
  5. 6
      dist/keen-tracking.min.js
  6. 89
      lib/browser.js
  7. 171
      lib/index.js
  8. 7
      lib/record-events-browser.js
  9. 2
      lib/server.js
  10. 47
      lib/utils/base64.js
  11. 7
      lib/utils/cookie.js
  12. 4
      lib/utils/deepExtend.js
  13. 28
      lib/utils/each.js
  14. 9
      lib/utils/extend.js
  15. 1
      lib/utils/json.js
  16. 17
      lib/utils/parseParams.js
  17. 5
      package.json
  18. 4
      test/unit/modules/client-spec.js
  19. 3
      test/unit/modules/record-events-browser-spec.js

2
.gitignore

@ -4,4 +4,4 @@ bower_components
node_modules
test/unit/build/*
.awspublish-keen-js
dist/keen-tracking.js.map
dist/*.map

19
CHANGELOG.md

@ -6,28 +6,39 @@
**BREAKING:**
-->
<a name="0.1.1></a>
<a name="1.0.0"></a>
# 1.0.0
**NEW:**
* Move core client functionality to keen-core.js (PR #40).
**BREAKING:**
* Replaced [cookies-js](https://github.com/ScottHamper/Cookies) with [js-cookie](https://github.com/js-cookie/js-cookie). Cookies are now encoded properly, but will require a workaround (covered [here](./README.md#cookie-migration)) to fix previous cookie data.
* Removed `client.writePath()` method and `writePath` config option in favor of internal resource mapping and the `client.resources()` method.
<a name="0.1.1"></a>
# 0.1.1
**CHANGE:**
* Publish to bower
<a name="0.1.0></a>
<a name="0.1.0"></a>
# 0.1.0
**CHANGE:**
* Reworked `.url()` method to use resource templates, with an internal collection that makes specific API URLS easy to request and customize.
<a name="0.0.5></a>
<a name="0.0.5"></a>
# 0.0.5
**FIXED:**
* Define noop func when no callback is provided (fixes #34)
<a name="0.0.4></a>
<a name="0.0.4"></a>
# 0.0.4
**FIXED:**

27
README.md

@ -130,7 +130,7 @@ Copy/paste this snippet of JavaScript above the `</head>` tag of your page to lo
// Loads the library asynchronously from any URI
!function(name,path,ctx){
var latest,prev=name!=='Keen'&&window.Keen?window.Keen:false;ctx[name]=ctx[name]||{ready:function(fn){var h=document.getElementsByTagName('head')[0],s=document.createElement('script'),w=window,loaded;s.onload=s.onerror=s.onreadystatechange=function(){if((s.readyState&&!(/^c|loade/.test(s.readyState)))||loaded){return}s.onload=s.onreadystatechange=null;loaded=1;latest=w.Keen;if(prev){w.Keen=prev}else{try{delete w.Keen}catch(e){w.Keen=void 0}}ctx[name]=latest;ctx[name].ready(fn)};s.async=1;s.src=path;h.parentNode.insertBefore(s,h)}}
}('Keen','https://d26b395fwzu5fz.cloudfront.net/keen-tracking-0.1.1.min.js',this);
}('Keen','https://d26b395fwzu5fz.cloudfront.net/keen-tracking-1.0.0.min.js',this);
// Executes when the library is loaded and ready
Keen.ready(function(){
@ -502,7 +502,30 @@ sessionCookie.options({
**Important:** Some browsers do not allow cookies to be created or accessed from a local file (`file://dev/index.html`), which can make local development and testing problematic. `.set()` and `.get()` methods will only function correctly when cookies are enabled.
This utility uses [ScottHamper's](https://github.com/ScottHamper) wonderfully simple [Cookies.js](https://github.com/ScottHamper/Cookies) library. Read all options for Cookies.js [here](https://github.com/ScottHamper/Cookies#cookiessetkey-value--options).
This utility uses [js-cookie](https://github.com/js-cookie/js-cookie).
<a name="cookie-migration"></a>
Prior to the 1.0 release, this library used [Cookies.js](https://github.com/ScottHamper/Cookies), but incorrectly encoded the cookie data twice. Data stored in cookies by v0.1.1 or earlier can be accessed and resolved like so:
```javascript
var cookies = document.cookie.split(';');
var myCookie = Keen.utils.cookie('your-cookie-name');
var badData, newData;
for (var i = 0; i < cookies.length; i++) {
if (cookies[i].indexOf('your-cookie-name=') < 0) continue;
badData = cookies[i].split('your-cookie-name=')[1];
newData = JSON.parse(
decodeURIComponent(
decodeURIComponent(badData)
)
);
myCookie.set(newData);
break;
}
```
### Listeners

1156
dist/keen-tracking.js

File diff suppressed because it is too large

6
dist/keen-tracking.min.js

File diff suppressed because one or more lines are too long

89
lib/browser.js

@ -1,41 +1,17 @@
;(function (f) {
// RequireJS
if ('undefined' !== typeof define && define.amd && typeof define === 'function') {
define('keen-tracking', [], function(){ return f(); });
}
// CommonJS
if (typeof exports === 'object' && typeof module !== 'undefined') {
module.exports = f();
}
// Global
var g = null;
if (typeof window !== 'undefined') {
g = window;
} else if (typeof global !== 'undefined') {
g = global;
} else if (typeof self !== 'undefined') {
g = self;
}
if (g) {
g.Keen = f();
}
})(function() {
(function(env) {
'use strict';
var Keen = require('./');
var K = require('./');
var each = require('./utils/each');
var extend = require('./utils/extend');
var listener = require('./utils/listener')(Keen);
var root = 'undefined' !== typeof window ? window : this;
var previousKeen = root.Keen;
var listener = require('./utils/listener')(K);
// ------------------------
// Methods
// ------------------------
extend(Keen.prototype, require('./record-events-browser'));
extend(Keen.prototype, require('./defer-events'));
extend(Keen.prototype, {
extend(K.prototype, require('./record-events-browser'));
extend(K.prototype, require('./defer-events'));
extend(K.prototype, {
'extendEvent': require('./extend-events').extendEvent,
'extendEvents': require('./extend-events').extendEvents
});
@ -43,12 +19,12 @@
// ------------------------
// Deprecated
// ------------------------
Keen.prototype.trackExternalLink = trackExternalLink;
K.prototype.trackExternalLink = trackExternalLink;
// ------------------------
// Helpers
// ------------------------
extend(Keen.helpers, {
extend(K.helpers, {
'getBrowserProfile' : require('./helpers/getBrowserProfile'),
'getDatetimeIndex' : require('./helpers/getDatetimeIndex'),
'getDomNodePath' : require('./helpers/getDomNodePath'),
@ -60,17 +36,14 @@
// ------------------------
// Utils
// ------------------------
extend(Keen.utils, {
extend(K.utils, {
'cookie' : require('./utils/cookie'),
'deepExtend' : require('./utils/deepExtend'),
'each' : each,
'extend' : extend,
'listener' : listener,
'parseParams': require('./utils/parseParams'),
'timer' : require('./utils/timer')
});
Keen.listenTo = function(listenerHash){
K.listenTo = function(listenerHash){
each(listenerHash, function(callback, key){
var split = key.split(' ');
var eventType = split[0],
@ -80,27 +53,22 @@
});
};
Keen.noConflict = function(){
root.Keen = previousKeen;
return Keen;
};
Keen.ready = function(fn){
if (Keen.loaded) {
K.ready = function(fn){
if (K.loaded) {
fn();
}
else {
Keen.once('ready', fn);
K.once('ready', fn);
}
};
domReady(function(){
Keen.loaded = true;
Keen.emit('ready');
K.loaded = true;
K.emit('ready');
});
function domReady(fn){
if (Keen.loaded || 'undefined' === typeof document) {
if (K.loaded || 'undefined' === typeof document) {
fn();
return;
}
@ -204,6 +172,25 @@
};
}
module.exports = Keen;
return Keen;
});
// Module Definitions
// --------------------
// Global
if (env) {
env.Keen = K;
}
// CommonJS
if (typeof module !== 'undefined' && module.exports) {
module.exports = K;
}
// RequireJS
if (typeof define !== 'undefined' && define.amd) {
define('keen-tracking', [], function(){
return K;
});
}
}).call(this, typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {});

171
lib/index.js

@ -1,154 +1,32 @@
var Emitter = require('component-emitter');
var json = require('./utils/json');
var K = require('keen-core');
var each = require('./utils/each');
var extend = require('./utils/extend');
var queue = require('./utils/queue');
var each = require('./utils/each'),
extend = require('./utils/extend'),
queue = require('./utils/queue');
var K = function(config){
var self = this;
K.helpers = K.helpers || {};
K.resources.events = '{protocol}://{host}/3.0/projects/{projectId}/events';
this.configure(config);
extend(this.config.resources, K.resources);
this.extensions = {
// Install internal queue
K.on('client', function(client){
client.extensions = {
events: [],
collections: {}
};
this.queue = queue();
this.queue.on('flush', function(){
self.recordDeferredEvents();
client.queue = queue();
client.queue.on('flush', function(){
client.recordDeferredEvents();
});
if (K.debug) {
this.on('error', K.log);
}
this.emit('ready');
K.emit('client', this);
};
Emitter(K);
Emitter(K.prototype);
extend(K, {
debug: false,
enabled: true,
loaded: false,
helpers: {},
resources: {
'base' : '{protocol}://{host}',
'version' : '{protocol}://{host}/3.0',
'projects' : '{protocol}://{host}/3.0/projects',
'projectId' : '{protocol}://{host}/3.0/projects/{projectId}',
'events' : '{protocol}://{host}/3.0/projects/{projectId}/events'
},
utils: {},
version: '__VERSION__'
});
K.log = function(message) {
if (K.debug && typeof console == 'object') {
console.log('[Keen IO]', message);
}
};
K.prototype.configure = function(cfg){
var self = this,
config = cfg || {},
defaultProtocol = 'https';
this.config = this.config || {
projectId: undefined,
writeKey: undefined,
host: 'api.keen.io',
protocol: defaultProtocol,
requestType: 'jsonp',
resources: {},
writePath: undefined
};
// IE<10 request shim
if ('undefined' !== typeof document && document.all) {
config['protocol'] = (document.location.protocol !== 'https:') ? 'http' : defaultProtocol;
}
if (config['host']) {
config['host'].replace(/.*?:\/\//g, '');
}
extend(this.config, config);
return self;
};
K.prototype.projectId = function(str){
if (!arguments.length) return this.config.projectId;
this.config.projectId = (str ? String(str) : null);
return this;
};
// Accessors
K.prototype.writeKey = function(str){
if (!arguments.length) return this.config.writeKey;
this.config.writeKey = (str ? String(str) : null);
return this;
};
K.prototype.resources = function(obj){
if (!arguments.length) return this.config.resources;
var self = this;
if (typeof obj === 'object') {
each(obj, function(value, key){
self.config.resources[key] = (value ? value : null);
});
}
return this;
};
K.prototype.url = function(name){
var args = Array.prototype.slice.call(arguments, 1),
baseUrl = K.resources.base || '{protocol}://{host}',
path;
if (name && typeof name === 'string') {
if (this.config.resources[name]) {
path = this.config.resources[name];
}
else {
path = baseUrl + name;
}
}
else {
path = baseUrl;
}
each(this.config, function(value, key){
if (typeof value !== 'object') {
path = path.replace('{' + key + '}', value);
}
});
each(args, function(arg, i){
if (typeof arg === 'string') {
path += '/' + arg;
}
else if (typeof arg === 'object') {
path += '?';
each(arg, function(value, key){
path += key + '=' + value + '&';
});
path = path.slice(0, -1);
}
});
return path;
};
// ----------------------
// DEPRECATED
// ----------------------
K.prototype.setGlobalProperties = function(props){
K.log('This method has been deprecated. Check out #extendEvents: https://github.com/keen/keen-tracking.js#extend-events');
if (!props || typeof props !== 'function') {
@ -159,27 +37,4 @@ K.prototype.setGlobalProperties = function(props){
return this;
};
K.prototype.writePath = function(str){
K.log('This method has been deprecated. Use client.url(\'events\') instead.');
if (!arguments.length) return this.config.writePath;
if (!this.projectId()) {
this.emit('error', 'Client instance is missing a projectId property');
return this.config.writePath || ('/3.0/projects/' + this.projectId() + '/events');
}
this.config.writePath = str ? String(str) : ('/3.0/projects/' + this.projectId() + '/events');
return this;
};
function serialize(data){
var query = [];
each(data, function(value, key){
if ('string' !== typeof value) {
value = json.stringify(value);
}
query.push(key + '=' + encodeURIComponent(value));
});
return query.join('&');
}
module.exports = K;

7
lib/record-events-browser.js

@ -3,7 +3,6 @@ var base64 = require('./utils/base64');
var each = require('./utils/each');
var extend = require('./utils/extend');
var extendEvents = require('./extend-events');
var json = require('./utils/json');
module.exports = {
'recordEvent': recordEvent,
@ -67,7 +66,7 @@ function recordEvent(eventCollection, eventBody, callback, async){
getRequestUrl = this.url('events', encodeURIComponent(eventCollection), {
api_key : this.writeKey(),
data : base64.encode( json.stringify(extendedEventBody) ),
data : base64.encode( JSON.stringify(extendedEventBody) ),
modified : new Date().getTime()
});
getRequestUrlOkLength = getRequestUrl.length < getUrlMaxLength();
@ -271,7 +270,7 @@ function sendXhr(method, url, data, callback){
if (xhr.readyState == 4) {
if (xhr.status >= 200 && xhr.status < 300) {
try {
response = json.parse( xhr.responseText );
response = JSON.parse( xhr.responseText );
} catch (e) {
Keen.emit('error', 'Could not parse HTTP response: ' + xhr.responseText);
if (cb) {
@ -296,7 +295,7 @@ function sendXhr(method, url, data, callback){
xhr.setRequestHeader('Content-Type', 'application/json');
if (data) {
payload = json.stringify(data);
payload = JSON.stringify(data);
}
if (method.toUpperCase() === 'GET') {

2
lib/server.js

@ -24,8 +24,6 @@ extend(Keen.helpers, {
// ------------------------
extend(Keen.utils, {
'deepExtend' : require('./utils/deepExtend'),
'each' : require('./utils/each'),
'extend' : extend,
'timer' : require('./utils/timer')
});

47
lib/utils/base64.js

@ -1,46 +1 @@
module.exports = {
map: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
encode: function (n) {
"use strict";
var o = "", i = 0, m = this.map, i1, i2, i3, e1, e2, e3, e4;
n = this.utf8.encode(n);
while (i < n.length) {
i1 = n.charCodeAt(i++); i2 = n.charCodeAt(i++); i3 = n.charCodeAt(i++);
e1 = (i1 >> 2); e2 = (((i1 & 3) << 4) | (i2 >> 4)); e3 = (isNaN(i2) ? 64 : ((i2 & 15) << 2) | (i3 >> 6));
e4 = (isNaN(i2) || isNaN(i3)) ? 64 : i3 & 63;
o = o + m.charAt(e1) + m.charAt(e2) + m.charAt(e3) + m.charAt(e4);
} return o;
},
decode: function (n) {
"use strict";
var o = "", i = 0, m = this.map, cc = String.fromCharCode, e1, e2, e3, e4, c1, c2, c3;
n = n.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < n.length) {
e1 = m.indexOf(n.charAt(i++)); e2 = m.indexOf(n.charAt(i++));
e3 = m.indexOf(n.charAt(i++)); e4 = m.indexOf(n.charAt(i++));
c1 = (e1 << 2) | (e2 >> 4); c2 = ((e2 & 15) << 4) | (e3 >> 2);
c3 = ((e3 & 3) << 6) | e4;
o = o + (cc(c1) + ((e3 != 64) ? cc(c2) : "")) + (((e4 != 64) ? cc(c3) : ""));
} return this.utf8.decode(o);
},
utf8: {
encode: function (n) {
"use strict";
var o = "", i = 0, cc = String.fromCharCode, c;
while (i < n.length) {
c = n.charCodeAt(i++); o = o + ((c < 128) ? cc(c) : ((c > 127) && (c < 2048)) ?
(cc((c >> 6) | 192) + cc((c & 63) | 128)) : (cc((c >> 12) | 224) + cc(((c >> 6) & 63) | 128) + cc((c & 63) | 128)));
} return o;
},
decode: function (n) {
"use strict";
var o = "", i = 0, cc = String.fromCharCode, c2, c;
while (i < n.length) {
c = n.charCodeAt(i);
o = o + ((c < 128) ? [cc(c), i++][0] : ((c > 191) && (c < 224)) ?
[cc(((c & 31) << 6) | ((c2 = n.charCodeAt(i + 1)) & 63)), (i += 2)][0] :
[cc(((c & 15) << 12) | (((c2 = n.charCodeAt(i + 1)) & 63) << 6) | ((c3 = n.charCodeAt(i + 2)) & 63)), (i += 3)][0]);
} return o;
}
}
};
module.exports = require('keen-core/lib/utils/base64');

7
lib/utils/cookie.js

@ -1,5 +1,4 @@
var Cookies = require('js-cookie');
var json = require('./json');
var extend = require('./extend');
module.exports = cookie;
@ -24,7 +23,7 @@ cookie.prototype.get = function(str){
var data = {};
if (Cookies.get(this.config.key)) {
data = json.parse(Cookies.get(this.config.key));
data = JSON.parse(Cookies.get(this.config.key));
}
if (str) {
return (typeof data[str] !== 'undefined') ? data[str] : null;
@ -51,7 +50,7 @@ cookie.prototype.expire = function(daysUntilExpire){
Cookies.set(this.config.key, this.data, extend(this.config.options, { expires: daysUntilExpire }));
} else {
Cookies.remove(this.config.key);
this.data = {};
this.data = {};
}
return this;
};
@ -64,4 +63,4 @@ cookie.prototype.options = function(obj){
cookie.prototype.enabled = function(){
return navigator.cookieEnabled;
};
};

4
lib/utils/deepExtend.js

@ -1,5 +1,3 @@
var json = require('./json');
module.exports = deepExtend;
function deepExtend(target){
@ -30,5 +28,5 @@ function deepExtend(target){
}
function clone(input){
return json.parse( json.stringify(input) );
return JSON.parse( JSON.stringify(input) );
}

28
lib/utils/each.js

@ -1,27 +1 @@
module.exports = each;
function each(o, cb, s){
var n;
if (!o){
return 0;
}
s = !s ? o : s;
if (o instanceof Array){
// Indexed arrays, needed for Safari
for (n=0; n<o.length; n++) {
if (cb.call(s, o[n], n, o) === false){
return 0;
}
}
} else {
// Hashtables
for (n in o){
if (o.hasOwnProperty(n)) {
if (cb.call(s, o[n], n, o) === false){
return 0;
}
}
}
}
return 1;
}
module.exports = require('keen-core/lib/utils/each');

9
lib/utils/extend.js

@ -1,8 +1 @@
module.exports = function(target){
for (var i = 1; i < arguments.length; i++) {
for (var prop in arguments[i]){
target[prop] = arguments[i][prop];
}
}
return target;
};
module.exports = require('keen-core/lib/utils/extend');

1
lib/utils/json.js

@ -1 +0,0 @@
module.exports = ('undefined' !== typeof window && window.JSON) ? window.JSON : require("json3");

17
lib/utils/parseParams.js

@ -1,16 +1 @@
function parseParams(str){
// via: http://stackoverflow.com/a/2880929/2511985
var urlParams = {},
match,
pl = /\+/g, // Regex for replacing addition symbol with a space
search = /([^&=]+)=?([^&]*)/g,
decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); },
query = str.split("?")[1];
while (!!(match=search.exec(query))) {
urlParams[decode(match[1])] = decode(match[2]);
}
return urlParams;
};
module.exports = parseParams;
module.exports = require('keen-core/lib/utils/parse-params');

5
package.json

@ -1,6 +1,6 @@
{
"name": "keen-tracking",
"version": "0.1.1",
"version": "1.0.0",
"description": "Data Collection SDK for Keen IO",
"main": "lib/server.js",
"browser": "lib/browser.js",
@ -20,13 +20,14 @@
"Eric Anderson <eric@keen.io> (https://github.com/aroc)",
"Alex Kleissner <alex@keen.io> (https://github.com/hex337)"
],
"license": "MIT",
"engines": {
"node": "0.10.x"
},
"dependencies": {
"component-emitter": "^1.2.0",
"js-cookie": "2.1.0",
"json3": "^3.3.2"
"keen-core": "0.0.2"
},
"devDependencies": {
"browserify": "^9.0.8",

4
test/unit/modules/client-spec.js

@ -30,14 +30,12 @@ describe('Keen (browser)', function() {
projectId: '123',
writeKey: '456',
protocol: 'http',
host: 'none',
writePath: '/customWritePath'
host: 'none'
});
assert.equal(this.client.projectId(), '123');
assert.equal(this.client.writeKey(), '456');
assert.equal(this.client.config.host, 'none');
assert.equal(this.client.config.protocol, 'http');
assert.equal(this.client.writePath(), '/customWritePath');
});
});

3
test/unit/modules/record-events-browser-spec.js

@ -1,5 +1,4 @@
var assert = require('proclaim');
var json = require('../../../lib/utils/json');
var Keen = require('../../../lib/browser');
var config = require('../helpers/client-config');
@ -90,7 +89,7 @@ describe('.recordEvent(s) methods (browser)', function() {
{ page: 'same again' }
]
};
this.batchResponse = json.stringify({
this.batchResponse = JSON.stringify({
click: [
{ 'success': true }
],

Loading…
Cancel
Save