From 7a9a3bc67805bbfb434cd9cb7bb9f4461a4af04f Mon Sep 17 00:00:00 2001 From: Josh Lind Date: Sun, 7 May 2017 17:33:57 -0700 Subject: [PATCH] break apart main file --- dist/groucho.js | 546 +++++++++++++++++++++++--------------------- dist/groucho.map | 2 +- dist/groucho.min.js | 6 +- src/favorites.js | 164 +++++++++++++ src/groucho.js | 397 +------------------------------- src/storage.js | 14 +- src/tracking.js | 77 +++++++ src/utility.js | 180 +++++++++++++++ test/groucho.html | 12 + 9 files changed, 724 insertions(+), 674 deletions(-) create mode 100644 src/favorites.js create mode 100644 src/tracking.js create mode 100644 src/utility.js diff --git a/dist/groucho.js b/dist/groucho.js index 2ead9d1..3fa2d2a 100644 --- a/dist/groucho.js +++ b/dist/groucho.js @@ -1,197 +1,11 @@ -/*! Groucho - v0.2.3 - 2016-09-23 +/*! Groucho - v0.2.3 - 2017-05-07 * https://github.com/tableau-mkt/groucho -* Copyright (c) 2016 Josh Lind; Licensed MIT */ +* Copyright (c) 2017 Josh Lind; Licensed MIT */ var groucho = window.groucho || {}; -// Functions in need of a little jQuery or similar selector library. (function($, groucho) { - // Defaults. - var defaults = { - 'taxonomyProperty': 'tags', - 'trackExtent': 50, - 'favThreshold': 1, - 'trackProperties': [ - 'title', - 'type', - 'tags' - ], - 'lastClicked': 'a', - 'ttl': 0, - 'addons': {} - }; - - // Set empty configs to defaults. - for (var config in defaults) { - if (!groucho.config.hasOwnProperty(config)) { - groucho.config[config] = defaults[config]; - } - } - - // Data availability. - groucho.userDeferred = groucho.userDeferred || $.Deferred(); - // Make favorites "static". - groucho.favoriteTerms = false; - - - /** - * Stash user origins. - */ - groucho.trackOrigins = function () { - var n = new Date().getTime(), - hit = { - 'timestamp': n, - 'url': window.location.href - }; - - // Stash the session entry point. - if (!groucho.storage.get('user.sessionOrigin') || !document.referrer) { - groucho.storage.set('user.sessionOrigin', hit); - } - - // Stash the deep origin. - if (!groucho.storage.get('user.origin')) { - hit.referrer = document.referrer; - groucho.storage.set('user.origin', hit); - } - - // Reliable availability. - groucho.userDeferred.resolve(); - }; - - - /** - * Track page hit. - */ - groucho.trackHit = function () { - var dlHelper = new DataLayerHelper(dataLayer), - trackIds = groucho.config.trackProperties, - trackVals = { - 'url': window.location.href - }; - - // Log all configured items. - // @todo Allow toggling tracking on and off as a localStorage help config override. - if (typeof dataLayer !== 'undefined') { - for (var i in trackIds) { - // Safety measure. - if (!trackIds.hasOwnProperty(i)) continue; - - // Add each item. - if (typeof dlHelper.get(trackIds[i]) !== 'undefined') { - trackVals[trackIds[i]] = dlHelper.get(trackIds[i]); - } - } - } - - // Stash tracking in localStorage. - groucho.createActivity('browsing', trackVals); - }; - - - /** - * Stash last clicked text. - */ - groucho.trackClicks = function () { - if (!groucho.config.lastClicked) return; - // Bind click event to configured selector. - if (typeof $.fn.click === 'function') { - $(groucho.config.lastClicked).click(function () { - groucho.storage.set('user.lastClicked', $(this).text()); - }); - } - }; - - - /** - * Put a tracking record into storage. - * - * @param {string} group - * Name of the tracking group to store the data as. - * @param {string} data - * Data to store-- string, int, object. - * @param {number} ttl (optional) - * Time-to-live in milliseconds. - */ - groucho.createActivity = function (group, data, ttl) { - var results = groucho.getActivities(group), - n = new Date().getTime(), - diff = 0; - - // Log event, first. - groucho.storage.set('track.' + group + '.' + n, data, ttl); - - // Ensure space limit is maintained. - if (results.length >= groucho.config.trackExtent) { - diff = results.length - groucho.config.trackExtent; - // Kill off oldest extra tracking activities. - for (var i=0; i<=diff; i++) { - groucho.storage.remove(results[i]._key); - } - } - }; - - - /** - * Access records of a specific tracking group. - * - * @param {string} group - * Name of the tracking group to return values for. - * - * @return {array} - * List of tracking localStorage entries. - */ - groucho.getActivities = function (group) { - var results = groucho.storage.index(), - returnVals = [], - matchable = (group) ? new RegExp("^track." + group + ".", "g") : false, - record; - - for (var i in results) { - // Safety measure. - if (!results.hasOwnProperty(i)) continue; - - // Remove unwanted types and return records. - if (group) { - if (results[i].match(matchable) !== null) { - // Collect relevant. - record = groucho.storage.get(results[i]); - // Move key to property. - record._key = results[i]; - returnVals.push(record); - } - } - else { - // Collect and return all. - record = groucho.storage.get(results[i]); - // Move key to property. - record._key = results[i]; - returnVals.push(record); - } - } - - // Ensure proper key sorting regardless of index result order. - returnVals.sort(function (a, b) { - // Created non-standard or outside Groucho. - // Should always contain an original key which contains a dot. - if (!a.hasOwnProperty('_key') || !a._key.match(/\./) || - !b.hasOwnProperty('_key') || !b._key.match(/\./)) { - return 0; - } - // Sort by post-prefix key. - if (parseInt(b._key.split('.')[2], 10) > parseInt(a._key.split('.')[2], 10)) { - return -1; - } - else { - return 1; - } - }); - - return returnVals; - }; - - /** * Use browsing history and find user's top terms. * @@ -347,86 +161,39 @@ var groucho = window.groucho || {}; return returnTerms; }; +})(window.jQuery || window.Zepto || window.$, groucho); - /** - * Set user properties in localStorage. - * - * @param {object} data - * @param {boolean} keepExisting - * Default is to overwrite value. - * @param {number} ttl - */ - groucho.userSet = function (data, keepExisting, ttl) { - var userProperty; - - // Walk through data and attempt to set. - for (var property in data) { - if (!data.hasOwnProperty(property) || typeof property !== 'string') { - // Irregular, skip. - continue; - } - if (keepExisting) { - userProperty = groucho.storage.get('user.' + property); - if (!(userProperty === null || userProperty === undefined)) { - // Continue to next property. - continue; - } - } - // Set user property, may be skipped above. - groucho.storage.set('user.' + property, data[property], ttl); - } - }; +var groucho = window.groucho || {}; +// Functions in need of a little jQuery or similar selector library. +(function($, groucho) { - /** - * Retrieve user data, one or all properties. - * - * @param {string} property - * Optionally specify a property. - * - * @return {mixed} - */ - groucho.userGet = function (property) { - var index = groucho.storage.index(), - userProperties = {}, - propertyName; + // Defaults. + var defaults = { + 'taxonomyProperty': 'tags', + 'trackExtent': 50, + 'favThreshold': 1, + 'trackProperties': [ + 'title', + 'type', + 'tags' + ], + 'lastClicked': 'a', + 'ttl': 0, + 'addons': {} + }; - // Handle single property lookup. - if (typeof property !== 'undefined') { - return groucho.storage.get('user.' + property); + // Set empty configs to defaults. + for (var config in defaults) { + if (!groucho.config.hasOwnProperty(config)) { + groucho.config[config] = defaults[config]; } - // Look for all user properties. - for (var i in index) { - if ((typeof index[i] === 'string') && (index[i].indexOf('user.') === 0)) { - propertyName = index[i].replace('user.', ''); - userProperties[propertyName] = groucho.storage.get(index[i]); - } - } - - return userProperties; - }; - - - /** - * Data transforms due to version updates. - */ - groucho.schema = function () { - // Update keys. - var keys = { - 'user.sessionOrigin': { - 'oldKey': 'user.session_origin', - 'version': '0.2.0' - } - }; - - for (var newKey in keys) { - if ((groucho.storage.get(newKey) === null) && (groucho.storage.get(keys[newKey].oldKey) !== null)) { - groucho.storage.set(newKey, groucho.storage.set(keys[newKey].oldKey)); - groucho.storage.remove(keys[newKey].oldKey); - } - } - }; + } + // Data availability. + groucho.userDeferred = groucho.userDeferred || $.Deferred(); + // Make favorites "static". + groucho.favoriteTerms = false; // React to page load. $(document).ready(function () { @@ -442,7 +209,7 @@ var groucho = window.groucho || {}; var groucho = window.groucho || {}; -(function($, g) { +(function($, groucho) { var defaultStorage, error; @@ -456,11 +223,11 @@ var groucho = window.groucho || {}; // jStorage default, or error handler. // @todo Allow only overriding some functions. - if (!g.storage) { + if (!groucho.storage) { defaultStorage = ($.hasOwnProperty('jStorage')) && (typeof $.jStorage === 'object'); // Assign storage function defaults. - g.storage = { + groucho.storage = { /** * Set localStorage item. * @@ -469,7 +236,7 @@ var groucho = window.groucho || {}; * @param {number} ttl */ set: (defaultStorage) ? function set(id, value, ttl) { - ttl = ttl || g.config.ttl || 0; + ttl = ttl || groucho.config.ttl || 0; return $.jStorage.set(id, value, {TTL: ttl}); } : error, @@ -519,3 +286,250 @@ var groucho = window.groucho || {}; } })(window.jQuery || window.Zepto || window.$, groucho); + +var groucho = window.groucho || {}; + +(function($, groucho) { + + /** + * Stash user origins. + */ + groucho.trackOrigins = function () { + var n = new Date().getTime(), + hit = { + 'timestamp': n, + 'url': window.location.href + }; + + // Stash the session entry point. + if (!groucho.storage.get('user.sessionOrigin') || !document.referrer) { + groucho.storage.set('user.sessionOrigin', hit); + } + + // Stash the deep origin. + if (!groucho.storage.get('user.origin')) { + hit.referrer = document.referrer; + groucho.storage.set('user.origin', hit); + } + + // Reliable availability. + groucho.userDeferred.resolve(); + }; + + + /** + * Track page hit. + */ + groucho.trackHit = function () { + var dlHelper = new DataLayerHelper(dataLayer), + trackIds = groucho.config.trackProperties, + trackVals = { + 'url': window.location.href + }; + + // Log all configured items. + // @todo Allow toggling tracking on and off as a localStorage help config override. + if (typeof dataLayer !== 'undefined') { + for (var i in trackIds) { + // Safety measure. + if (!trackIds.hasOwnProperty(i)) continue; + + // Add each item. + if (typeof dlHelper.get(trackIds[i]) !== 'undefined') { + trackVals[trackIds[i]] = dlHelper.get(trackIds[i]); + } + } + } + + // Stash tracking in localStorage. + groucho.createActivity('browsing', trackVals); + }; + + + /** + * Stash last clicked text. + */ + groucho.trackClicks = function () { + if (!groucho.config.lastClicked) return; + // Bind click event to configured selector. + if (typeof $.fn.click === 'function') { + $(groucho.config.lastClicked).click(function () { + groucho.storage.set('user.lastClicked', $(this).text()); + }); + } + }; + +})(window.jQuery || window.Zepto || window.$, groucho); + +var groucho = window.groucho || {}; + +(function($, groucho) { + + /** + * Set user properties in localStorage. + * + * @param {object} data + * @param {boolean} keepExisting + * Default is to overwrite value. + * @param {number} ttl + */ + groucho.userSet = function (data, keepExisting, ttl) { + var userProperty; + + // Walk through data and attempt to set. + for (var property in data) { + if (!data.hasOwnProperty(property) || typeof property !== 'string') { + // Irregular, skip. + continue; + } + if (keepExisting) { + userProperty = groucho.storage.get('user.' + property); + if (!(userProperty === null || userProperty === undefined)) { + // Continue to next property. + continue; + } + } + // Set user property, may be skipped above. + groucho.storage.set('user.' + property, data[property], ttl); + } + }; + + + /** + * Retrieve user data, one or all properties. + * + * @param {string} property + * Optionally specify a property. + * + * @return {mixed} + */ + groucho.userGet = function (property) { + var index = groucho.storage.index(), + userProperties = {}, + propertyName; + + // Handle single property lookup. + if (typeof property !== 'undefined') { + return groucho.storage.get('user.' + property); + } + // Look for all user properties. + for (var i in index) { + if ((typeof index[i] === 'string') && (index[i].indexOf('user.') === 0)) { + propertyName = index[i].replace('user.', ''); + userProperties[propertyName] = groucho.storage.get(index[i]); + } + } + + return userProperties; + }; + + + /** + * Put a tracking record into storage. + * + * @param {string} group + * Name of the tracking group to store the data as. + * @param {string} data + * Data to store-- string, int, object. + * @param {number} ttl (optional) + * Time-to-live in milliseconds. + */ + groucho.createActivity = function (group, data, ttl) { + var results = groucho.getActivities(group), + n = new Date().getTime(), + diff = 0; + + // Log event, first. + groucho.storage.set('track.' + group + '.' + n, data, ttl); + + // Ensure space limit is maintained. + if (results.length >= groucho.config.trackExtent) { + diff = results.length - groucho.config.trackExtent; + // Kill off oldest extra tracking activities. + for (var i=0; i<=diff; i++) { + groucho.storage.remove(results[i]._key); + } + } + }; + + + /** + * Access records of a specific tracking group. + * + * @param {string} group + * Name of the tracking group to return values for. + * + * @return {array} + * List of tracking localStorage entries. + */ + groucho.getActivities = function (group) { + var results = groucho.storage.index(), + returnVals = [], + matchable = (group) ? new RegExp("^track." + group + ".", "g") : false, + record; + + for (var i in results) { + // Safety measure. + if (!results.hasOwnProperty(i)) continue; + + // Remove unwanted types and return records. + if (group) { + if (results[i].match(matchable) !== null) { + // Collect relevant. + record = groucho.storage.get(results[i]); + // Move key to property. + record._key = results[i]; + returnVals.push(record); + } + } + else { + // Collect and return all. + record = groucho.storage.get(results[i]); + // Move key to property. + record._key = results[i]; + returnVals.push(record); + } + } + + // Ensure proper key sorting regardless of index result order. + returnVals.sort(function (a, b) { + // Created non-standard or outside Groucho. + // Should always contain an original key which contains a dot. + if (!a.hasOwnProperty('_key') || !a._key.match(/\./) || + !b.hasOwnProperty('_key') || !b._key.match(/\./)) { + return 0; + } + // Sort by post-prefix key. + if (parseInt(b._key.split('.')[2], 10) > parseInt(a._key.split('.')[2], 10)) { + return -1; + } + else { + return 1; + } + }); + + return returnVals; + }; + + + /** + * Data transforms due to version updates. Prevents past use data corruption. + */ + groucho.schema = function () { + // Update keys. + var keys = { + 'user.sessionOrigin': { + 'oldKey': 'user.session_origin', + 'version': '0.2.0' + } + }; + + for (var newKey in keys) { + if ((groucho.storage.get(newKey) === null) && (groucho.storage.get(keys[newKey].oldKey) !== null)) { + groucho.storage.set(newKey, groucho.storage.set(keys[newKey].oldKey)); + groucho.storage.remove(keys[newKey].oldKey); + } + } + }; + +})(window.jQuery || window.Zepto || window.$, groucho); diff --git a/dist/groucho.map b/dist/groucho.map index ae6a151..0b1bf75 100644 --- a/dist/groucho.map +++ b/dist/groucho.map @@ -1 +1 @@ -{"version":3,"file":"dist/groucho.min.js","sources":["dist/groucho.js"],"names":["groucho","window","$","defaults","taxonomyProperty","trackExtent","favThreshold","trackProperties","lastClicked","ttl","addons","config","hasOwnProperty","userDeferred","Deferred","favoriteTerms","trackOrigins","n","Date","getTime","hit","timestamp","url","location","href","storage","get","document","referrer","set","resolve","trackHit","dlHelper","DataLayerHelper","dataLayer","trackIds","trackVals","i","createActivity","trackClicks","fn","click","this","text","group","data","results","getActivities","diff","length","remove","_key","record","index","returnVals","matchable","RegExp","match","push","sort","a","b","parseInt","split","getFavoriteTerms","vocab","returnAll","threshold","collectTerms","vocName","tid","termProp","returnTerms","count","name","filterByCount","topCount","isEmpty","makeArray","obj","arr","id","prop","pages","userSet","keepExisting","userProperty","property","undefined","userGet","propertyName","userProperties","indexOf","replace","schema","keys","user.sessionOrigin","oldKey","version","newKey","ready","jQuery","Zepto","g","defaultStorage","error","console","log","jStorage","value","TTL","deleteKey","available","storageAvailable","clear","flush"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"dist/groucho.min.js","sources":["dist/groucho.js"],"names":["groucho","window","$","getFavoriteTerms","vocab","returnAll","threshold","collectTerms","vocName","i","tid","results","termProp","returnTerms","hasOwnProperty","count","name","filterByCount","topCount","isEmpty","makeArray","obj","arr","id","push","prop","getActivities","config","taxonomyProperty","pages","favThreshold","url","favoriteTerms","jQuery","Zepto","defaults","trackExtent","trackProperties","lastClicked","ttl","addons","userDeferred","Deferred","document","ready","schema","trackOrigins","trackHit","trackClicks","defaultStorage","error","console","log","storage","jStorage","set","value","TTL","get","remove","deleteKey","index","available","storageAvailable","clear","flush","n","Date","getTime","hit","timestamp","location","href","referrer","resolve","dlHelper","DataLayerHelper","dataLayer","trackIds","trackVals","createActivity","fn","click","this","text","userSet","data","keepExisting","userProperty","property","undefined","userGet","propertyName","userProperties","indexOf","replace","group","diff","length","_key","record","returnVals","matchable","RegExp","match","sort","a","b","parseInt","split","keys","user.sessionOrigin","oldKey","version","newKey"],"mappings":"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"} \ No newline at end of file diff --git a/dist/groucho.min.js b/dist/groucho.min.js index 3df6ee9..602c162 100644 --- a/dist/groucho.min.js +++ b/dist/groucho.min.js @@ -1,7 +1,7 @@ -/*! Groucho - v0.2.3 - 2016-09-23 +/*! Groucho - v0.2.3 - 2017-05-07 * https://github.com/tableau-mkt/groucho -* Copyright (c) 2016 Josh Lind; Licensed MIT */ +* Copyright (c) 2017 Josh Lind; Licensed MIT */ -var groucho=window.groucho||{};!function(a,b){var c={taxonomyProperty:"tags",trackExtent:50,favThreshold:1,trackProperties:["title","type","tags"],lastClicked:"a",ttl:0,addons:{}};for(var d in c)b.config.hasOwnProperty(d)||(b.config[d]=c[d]);b.userDeferred=b.userDeferred||a.Deferred(),b.favoriteTerms=!1,b.trackOrigins=function(){var a=(new Date).getTime(),c={timestamp:a,url:window.location.href};b.storage.get("user.sessionOrigin")&&document.referrer||b.storage.set("user.sessionOrigin",c),b.storage.get("user.origin")||(c.referrer=document.referrer,b.storage.set("user.origin",c)),b.userDeferred.resolve()},b.trackHit=function(){var a=new DataLayerHelper(dataLayer),c=b.config.trackProperties,d={url:window.location.href};if("undefined"!=typeof dataLayer)for(var e in c)c.hasOwnProperty(e)&&"undefined"!=typeof a.get(c[e])&&(d[c[e]]=a.get(c[e]));b.createActivity("browsing",d)},b.trackClicks=function(){b.config.lastClicked&&"function"==typeof a.fn.click&&a(b.config.lastClicked).click(function(){b.storage.set("user.lastClicked",a(this).text())})},b.createActivity=function(a,c,d){var e=b.getActivities(a),f=(new Date).getTime(),g=0;if(b.storage.set("track."+a+"."+f,c,d),e.length>=b.config.trackExtent){g=e.length-b.config.trackExtent;for(var h=0;g>=h;h++)b.storage.remove(e[h]._key)}},b.getActivities=function(a){var c,d=b.storage.index(),e=[],f=a?new RegExp("^track."+a+".","g"):!1;for(var g in d)d.hasOwnProperty(g)&&(a?null!==d[g].match(f)&&(c=b.storage.get(d[g]),c._key=d[g],e.push(c)):(c=b.storage.get(d[g]),c._key=d[g],e.push(c)));return e.sort(function(a,b){return a.hasOwnProperty("_key")&&a._key.match(/\./)&&b.hasOwnProperty("_key")&&b._key.match(/\./)?parseInt(b._key.split(".")[2],10)>parseInt(a._key.split(".")[2],10)?-1:1:0}),e},b.getFavoriteTerms=function(a,c,d){function e(a,b){for(var c in j[b][k][a])m.hasOwnProperty(a)||(m[a]={}),m[a].hasOwnProperty(c)?m[a][c].count++:m[a][c]={name:j[b][k][a][c],count:1}}function f(a){var b=d;for(var c in m[a])m[a][c].count>=b&&(b=m[a][c].count);for(c in m[a])m[a][c].count=b&&(b=m[a][c].count);for(c in m[a])m[a][c].count=b.config.trackExtent){g=e.length-b.config.trackExtent;for(var h=0;g>=h;h++)b.storage.remove(e[h]._key)}},b.getActivities=function(a){var c,d=b.storage.index(),e=[],f=a?new RegExp("^track."+a+".","g"):!1;for(var g in d)d.hasOwnProperty(g)&&(a?null!==d[g].match(f)&&(c=b.storage.get(d[g]),c._key=d[g],e.push(c)):(c=b.storage.get(d[g]),c._key=d[g],e.push(c)));return e.sort(function(a,b){return a.hasOwnProperty("_key")&&a._key.match(/\./)&&b.hasOwnProperty("_key")&&b._key.match(/\./)?parseInt(b._key.split(".")[2],10)>parseInt(a._key.split(".")[2],10)?-1:1:0}),e},b.schema=function(){var a={"user.sessionOrigin":{oldKey:"user.session_origin",version:"0.2.0"}};for(var c in a)null===b.storage.get(c)&&null!==b.storage.get(a[c].oldKey)&&(b.storage.set(c,b.storage.set(a[c].oldKey)),b.storage.remove(a[c].oldKey))}}(window.jQuery||window.Zepto||window.$,groucho); //# sourceMappingURL=dist/groucho.map \ No newline at end of file diff --git a/src/favorites.js b/src/favorites.js new file mode 100644 index 0000000..0a74793 --- /dev/null +++ b/src/favorites.js @@ -0,0 +1,164 @@ +/** + * @file Compute the preferences of a user. + */ + +var groucho = window.groucho || {}; + +(function($, groucho) { + + /** + * Use browsing history and find user's top terms. + * + * @param {string} vocab + * The taxonomy vocabulary to collect favorites from. + * @param {boolean} returnAll + * Whether or not to return all term hit counts. + * + * @return {array} + * List of vocabs with top taxonomy terms and counts. + */ + groucho.getFavoriteTerms = function (vocab, returnAll, threshold) { + var results = groucho.getActivities('browsing'), + termProp = groucho.config.taxonomyProperty, + pages = [], + returnTerms = {}, + vocName; + + // Params optional. + vocab = vocab || '*'; + returnAll = returnAll || false; + threshold = threshold || groucho.config.favThreshold; + + /** + * Assemble term counts. + * + * @param {string} vocName + */ + function collectTerms(vocName, i) { + for (var tid in results[i][termProp][vocName]) { + // Non-existant vocab. + if (!returnTerms.hasOwnProperty(vocName)) { + returnTerms[vocName] = {}; + } + + // Existing term, add to count. + if (returnTerms[vocName].hasOwnProperty(tid)) { + returnTerms[vocName][tid].count++; + } + else { + // New, add it on and create count. + returnTerms[vocName][tid] = { 'name': results[i][termProp][vocName][tid], 'count': 1 }; + } + } + } + + /** + * Remove lesser count terms. + * + * @param {string} vocName + */ + function filterByCount(vocName) { + var topCount = threshold; + + // Find top count. + for (var tid in returnTerms[vocName]) { + // Find top term hit count. + if (returnTerms[vocName][tid].count >= topCount) { + topCount = returnTerms[vocName][tid].count; + } + } + // Get those with top count. + for (tid in returnTerms[vocName]) { + if (returnTerms[vocName][tid].count < topCount) { + delete returnTerms[vocName][tid]; + } + } + // Destroy empty vocabs. + if (isEmpty(returnTerms[vocName])) { + delete returnTerms[vocName]; + } + } + + /** + * Utility: Term returns should be an array. + */ + function makeArray(obj) { + var arr = []; + for (var i in obj) { + obj[i].id = i; + arr.push(obj[i]); + } + return arr; + } + + /** + * Utility: check for empty vocab object. + * + * @param {object} obj + */ + function isEmpty(obj) { + for (var prop in obj) { + if (obj.hasOwnProperty(prop)) { + return false; + } + } + return true; + } + + // No data will be available. + if (typeof termProp !== 'undefined') { + // Walk through all tracking records. + for (var i in results) { + // Safety measure. + if (!results.hasOwnProperty(i)) continue; + + // Only count each URL once. + if (typeof pages[results[i].url] === 'undefined' && results[i].hasOwnProperty(termProp)) { + // For de-duping URL hits. + pages[results[i].url] = true; + + if (vocab === '*') { + // Walk through all vocabs on record. + for (vocName in results[i][termProp]) { + collectTerms(vocName, i); + } + } + else { + // Just use requested vocabulary. + if (results[i][termProp].hasOwnProperty(vocab)) { + collectTerms(vocab, i); + } + } + + } + } + + // Filter to top terms if desired. + if (!returnAll) { + for (vocName in returnTerms) { + filterByCount(vocName); + } + } + + // Format output. + if (vocab === '*') { + for (vocName in returnTerms) { + // Return arrays of terms. + returnTerms[vocName] = makeArray(returnTerms[vocName]); + } + + // Set favorites on page if no arguments were passed. + if (returnAll === false) { + groucho.favoriteTerms = returnTerms; + } + } + else { + // // Return array of terms in requested vocabulary. + returnTerms = makeArray(returnTerms[vocab]); + } + } + + return returnTerms; + }; + +})(window.jQuery || window.Zepto || window.$, groucho); diff --git a/src/groucho.js b/src/groucho.js index 69e37a4..8db66ca 100644 --- a/src/groucho.js +++ b/src/groucho.js @@ -1,6 +1,5 @@ /** - * @file - * Track browsing history or other logging stats. + * @file Track browsing history or other logging stats. */ var groucho = window.groucho || {}; @@ -35,400 +34,6 @@ var groucho = window.groucho || {}; // Make favorites "static". groucho.favoriteTerms = false; - - /** - * Stash user origins. - */ - groucho.trackOrigins = function () { - var n = new Date().getTime(), - hit = { - 'timestamp': n, - 'url': window.location.href - }; - - // Stash the session entry point. - if (!groucho.storage.get('user.sessionOrigin') || !document.referrer) { - groucho.storage.set('user.sessionOrigin', hit); - } - - // Stash the deep origin. - if (!groucho.storage.get('user.origin')) { - hit.referrer = document.referrer; - groucho.storage.set('user.origin', hit); - } - - // Reliable availability. - groucho.userDeferred.resolve(); - }; - - - /** - * Track page hit. - */ - groucho.trackHit = function () { - var dlHelper = new DataLayerHelper(dataLayer), - trackIds = groucho.config.trackProperties, - trackVals = { - 'url': window.location.href - }; - - // Log all configured items. - // @todo Allow toggling tracking on and off as a localStorage help config override. - if (typeof dataLayer !== 'undefined') { - for (var i in trackIds) { - // Safety measure. - if (!trackIds.hasOwnProperty(i)) continue; - - // Add each item. - if (typeof dlHelper.get(trackIds[i]) !== 'undefined') { - trackVals[trackIds[i]] = dlHelper.get(trackIds[i]); - } - } - } - - // Stash tracking in localStorage. - groucho.createActivity('browsing', trackVals); - }; - - - /** - * Stash last clicked text. - */ - groucho.trackClicks = function () { - if (!groucho.config.lastClicked) return; - // Bind click event to configured selector. - if (typeof $.fn.click === 'function') { - $(groucho.config.lastClicked).click(function () { - groucho.storage.set('user.lastClicked', $(this).text()); - }); - } - }; - - - /** - * Put a tracking record into storage. - * - * @param {string} group - * Name of the tracking group to store the data as. - * @param {string} data - * Data to store-- string, int, object. - * @param {number} ttl (optional) - * Time-to-live in milliseconds. - */ - groucho.createActivity = function (group, data, ttl) { - var results = groucho.getActivities(group), - n = new Date().getTime(), - diff = 0; - - // Log event, first. - groucho.storage.set('track.' + group + '.' + n, data, ttl); - - // Ensure space limit is maintained. - if (results.length >= groucho.config.trackExtent) { - diff = results.length - groucho.config.trackExtent; - // Kill off oldest extra tracking activities. - for (var i=0; i<=diff; i++) { - groucho.storage.remove(results[i]._key); - } - } - }; - - - /** - * Access records of a specific tracking group. - * - * @param {string} group - * Name of the tracking group to return values for. - * - * @return {array} - * List of tracking localStorage entries. - */ - groucho.getActivities = function (group) { - var results = groucho.storage.index(), - returnVals = [], - matchable = (group) ? new RegExp("^track." + group + ".", "g") : false, - record; - - for (var i in results) { - // Safety measure. - if (!results.hasOwnProperty(i)) continue; - - // Remove unwanted types and return records. - if (group) { - if (results[i].match(matchable) !== null) { - // Collect relevant. - record = groucho.storage.get(results[i]); - // Move key to property. - record._key = results[i]; - returnVals.push(record); - } - } - else { - // Collect and return all. - record = groucho.storage.get(results[i]); - // Move key to property. - record._key = results[i]; - returnVals.push(record); - } - } - - // Ensure proper key sorting regardless of index result order. - returnVals.sort(function (a, b) { - // Created non-standard or outside Groucho. - // Should always contain an original key which contains a dot. - if (!a.hasOwnProperty('_key') || !a._key.match(/\./) || - !b.hasOwnProperty('_key') || !b._key.match(/\./)) { - return 0; - } - // Sort by post-prefix key. - if (parseInt(b._key.split('.')[2], 10) > parseInt(a._key.split('.')[2], 10)) { - return -1; - } - else { - return 1; - } - }); - - return returnVals; - }; - - - /** - * Use browsing history and find user's top terms. - * - * @param {string} vocab - * The taxonomy vocabulary to collect favorites from. - * @param {boolean} returnAll - * Whether or not to return all term hit counts. - * - * @return {array} - * List of vocabs with top taxonomy terms and counts. - */ - groucho.getFavoriteTerms = function (vocab, returnAll, threshold) { - var results = groucho.getActivities('browsing'), - termProp = groucho.config.taxonomyProperty, - pages = [], - returnTerms = {}, - vocName; - - // Params optional. - vocab = vocab || '*'; - returnAll = returnAll || false; - threshold = threshold || groucho.config.favThreshold; - - /** - * Assemble term counts. - * - * @param {string} vocName - */ - function collectTerms(vocName, i) { - for (var tid in results[i][termProp][vocName]) { - // Non-existant vocab. - if (!returnTerms.hasOwnProperty(vocName)) { - returnTerms[vocName] = {}; - } - - // Existing term, add to count. - if (returnTerms[vocName].hasOwnProperty(tid)) { - returnTerms[vocName][tid].count++; - } - else { - // New, add it on and create count. - returnTerms[vocName][tid] = { 'name': results[i][termProp][vocName][tid], 'count': 1 }; - } - } - } - - /** - * Remove lesser count terms. - * - * @param {string} vocName - */ - function filterByCount(vocName) { - var topCount = threshold; - - // Find top count. - for (var tid in returnTerms[vocName]) { - // Find top term hit count. - if (returnTerms[vocName][tid].count >= topCount) { - topCount = returnTerms[vocName][tid].count; - } - } - // Get those with top count. - for (tid in returnTerms[vocName]) { - if (returnTerms[vocName][tid].count < topCount) { - delete returnTerms[vocName][tid]; - } - } - // Destroy empty vocabs. - if (isEmpty(returnTerms[vocName])) { - delete returnTerms[vocName]; - } - } - - /** - * Utility: Term returns should be an array. - */ - function makeArray(obj) { - var arr = []; - for (var i in obj) { - obj[i].id = i; - arr.push(obj[i]); - } - return arr; - } - - /** - * Utility: check for empty vocab object. - * - * @param {object} obj - */ - function isEmpty(obj) { - for (var prop in obj) { - if (obj.hasOwnProperty(prop)) { - return false; - } - } - return true; - } - - // No data will be available. - if (typeof termProp !== 'undefined') { - // Walk through all tracking records. - for (var i in results) { - // Safety measure. - if (!results.hasOwnProperty(i)) continue; - - // Only count each URL once. - if (typeof pages[results[i].url] === 'undefined' && results[i].hasOwnProperty(termProp)) { - // For de-duping URL hits. - pages[results[i].url] = true; - - if (vocab === '*') { - // Walk through all vocabs on record. - for (vocName in results[i][termProp]) { - collectTerms(vocName, i); - } - } - else { - // Just use requested vocabulary. - if (results[i][termProp].hasOwnProperty(vocab)) { - collectTerms(vocab, i); - } - } - - } - } - - // Filter to top terms if desired. - if (!returnAll) { - for (vocName in returnTerms) { - filterByCount(vocName); - } - } - - // Format output. - if (vocab === '*') { - for (vocName in returnTerms) { - // Return arrays of terms. - returnTerms[vocName] = makeArray(returnTerms[vocName]); - } - - // Set favorites on page if no arguments were passed. - if (returnAll === false) { - groucho.favoriteTerms = returnTerms; - } - } - else { - // // Return array of terms in requested vocabulary. - returnTerms = makeArray(returnTerms[vocab]); - } - } - - return returnTerms; - }; - - - /** - * Set user properties in localStorage. - * - * @param {object} data - * @param {boolean} keepExisting - * Default is to overwrite value. - * @param {number} ttl - */ - groucho.userSet = function (data, keepExisting, ttl) { - var userProperty; - - // Walk through data and attempt to set. - for (var property in data) { - if (!data.hasOwnProperty(property) || typeof property !== 'string') { - // Irregular, skip. - continue; - } - if (keepExisting) { - userProperty = groucho.storage.get('user.' + property); - if (!(userProperty === null || userProperty === undefined)) { - // Continue to next property. - continue; - } - } - // Set user property, may be skipped above. - groucho.storage.set('user.' + property, data[property], ttl); - } - }; - - - /** - * Retrieve user data, one or all properties. - * - * @param {string} property - * Optionally specify a property. - * - * @return {mixed} - */ - groucho.userGet = function (property) { - var index = groucho.storage.index(), - userProperties = {}, - propertyName; - - // Handle single property lookup. - if (typeof property !== 'undefined') { - return groucho.storage.get('user.' + property); - } - // Look for all user properties. - for (var i in index) { - if ((typeof index[i] === 'string') && (index[i].indexOf('user.') === 0)) { - propertyName = index[i].replace('user.', ''); - userProperties[propertyName] = groucho.storage.get(index[i]); - } - } - - return userProperties; - }; - - - /** - * Data transforms due to version updates. - */ - groucho.schema = function () { - // Update keys. - var keys = { - 'user.sessionOrigin': { - 'oldKey': 'user.session_origin', - 'version': '0.2.0' - } - }; - - for (var newKey in keys) { - if ((groucho.storage.get(newKey) === null) && (groucho.storage.get(keys[newKey].oldKey) !== null)) { - groucho.storage.set(newKey, groucho.storage.set(keys[newKey].oldKey)); - groucho.storage.remove(keys[newKey].oldKey); - } - } - }; - - // React to page load. $(document).ready(function () { // Data transforms due to version updates. diff --git a/src/storage.js b/src/storage.js index b4f3684..01d1aca 100644 --- a/src/storage.js +++ b/src/storage.js @@ -4,21 +4,19 @@ * Use any local storage backend. * Provide simple wrappers for library functions where you do other groucho configs... * - * g.storage.set = function(id, val) { + * groucho.storage.set = function(id, val) { * mySetter(id, val); * } * - * g.storage.get = function(id) { + * groucho.storage.get = function(id) { * myGetter(id); * } - * - * Include: get(), set(), remove(), index(), just for testing: available(), and clear(). */ var groucho = window.groucho || {}; -(function($, g) { +(function($, groucho) { var defaultStorage, error; @@ -32,11 +30,11 @@ var groucho = window.groucho || {}; // jStorage default, or error handler. // @todo Allow only overriding some functions. - if (!g.storage) { + if (!groucho.storage) { defaultStorage = ($.hasOwnProperty('jStorage')) && (typeof $.jStorage === 'object'); // Assign storage function defaults. - g.storage = { + groucho.storage = { /** * Set localStorage item. * @@ -45,7 +43,7 @@ var groucho = window.groucho || {}; * @param {number} ttl */ set: (defaultStorage) ? function set(id, value, ttl) { - ttl = ttl || g.config.ttl || 0; + ttl = ttl || groucho.config.ttl || 0; return $.jStorage.set(id, value, {TTL: ttl}); } : error, diff --git a/src/tracking.js b/src/tracking.js new file mode 100644 index 0000000..6fa7040 --- /dev/null +++ b/src/tracking.js @@ -0,0 +1,77 @@ +/** + * @file Tracking user behavior and events. + */ + +var groucho = window.groucho || {}; + +(function($, groucho) { + + /** + * Stash user origins. + */ + groucho.trackOrigins = function () { + var n = new Date().getTime(), + hit = { + 'timestamp': n, + 'url': window.location.href + }; + + // Stash the session entry point. + if (!groucho.storage.get('user.sessionOrigin') || !document.referrer) { + groucho.storage.set('user.sessionOrigin', hit); + } + + // Stash the deep origin. + if (!groucho.storage.get('user.origin')) { + hit.referrer = document.referrer; + groucho.storage.set('user.origin', hit); + } + + // Reliable availability. + groucho.userDeferred.resolve(); + }; + + + /** + * Track page hit. + */ + groucho.trackHit = function () { + var dlHelper = new DataLayerHelper(dataLayer), + trackIds = groucho.config.trackProperties, + trackVals = { + 'url': window.location.href + }; + + // Log all configured items. + // @todo Allow toggling tracking on and off as a localStorage help config override. + if (typeof dataLayer !== 'undefined') { + for (var i in trackIds) { + // Safety measure. + if (!trackIds.hasOwnProperty(i)) continue; + + // Add each item. + if (typeof dlHelper.get(trackIds[i]) !== 'undefined') { + trackVals[trackIds[i]] = dlHelper.get(trackIds[i]); + } + } + } + + // Stash tracking in localStorage. + groucho.createActivity('browsing', trackVals); + }; + + + /** + * Stash last clicked text. + */ + groucho.trackClicks = function () { + if (!groucho.config.lastClicked) return; + // Bind click event to configured selector. + if (typeof $.fn.click === 'function') { + $(groucho.config.lastClicked).click(function () { + groucho.storage.set('user.lastClicked', $(this).text()); + }); + } + }; + +})(window.jQuery || window.Zepto || window.$, groucho); diff --git a/src/utility.js b/src/utility.js new file mode 100644 index 0000000..ee5a4f2 --- /dev/null +++ b/src/utility.js @@ -0,0 +1,180 @@ +/** + * @file Library utilities for accessing data strcuture. + * + * Helper functions used throughout the library for common operations. + * + * groucho.userSet = function({country: "US"}); + */ + +var groucho = window.groucho || {}; + +(function($, groucho) { + + /** + * Set user properties in localStorage. + * + * @param {object} data + * @param {boolean} keepExisting + * Default is to overwrite value. + * @param {number} ttl + */ + groucho.userSet = function (data, keepExisting, ttl) { + var userProperty; + + // Walk through data and attempt to set. + for (var property in data) { + if (!data.hasOwnProperty(property) || typeof property !== 'string') { + // Irregular, skip. + continue; + } + if (keepExisting) { + userProperty = groucho.storage.get('user.' + property); + if (!(userProperty === null || userProperty === undefined)) { + // Continue to next property. + continue; + } + } + // Set user property, may be skipped above. + groucho.storage.set('user.' + property, data[property], ttl); + } + }; + + + /** + * Retrieve user data, one or all properties. + * + * @param {string} property + * Optionally specify a property. + * + * @return {mixed} + */ + groucho.userGet = function (property) { + var index = groucho.storage.index(), + userProperties = {}, + propertyName; + + // Handle single property lookup. + if (typeof property !== 'undefined') { + return groucho.storage.get('user.' + property); + } + // Look for all user properties. + for (var i in index) { + if ((typeof index[i] === 'string') && (index[i].indexOf('user.') === 0)) { + propertyName = index[i].replace('user.', ''); + userProperties[propertyName] = groucho.storage.get(index[i]); + } + } + + return userProperties; + }; + + + /** + * Put a tracking record into storage. + * + * @param {string} group + * Name of the tracking group to store the data as. + * @param {string} data + * Data to store-- string, int, object. + * @param {number} ttl (optional) + * Time-to-live in milliseconds. + */ + groucho.createActivity = function (group, data, ttl) { + var results = groucho.getActivities(group), + n = new Date().getTime(), + diff = 0; + + // Log event, first. + groucho.storage.set('track.' + group + '.' + n, data, ttl); + + // Ensure space limit is maintained. + if (results.length >= groucho.config.trackExtent) { + diff = results.length - groucho.config.trackExtent; + // Kill off oldest extra tracking activities. + for (var i=0; i<=diff; i++) { + groucho.storage.remove(results[i]._key); + } + } + }; + + + /** + * Access records of a specific tracking group. + * + * @param {string} group + * Name of the tracking group to return values for. + * + * @return {array} + * List of tracking localStorage entries. + */ + groucho.getActivities = function (group) { + var results = groucho.storage.index(), + returnVals = [], + matchable = (group) ? new RegExp("^track." + group + ".", "g") : false, + record; + + for (var i in results) { + // Safety measure. + if (!results.hasOwnProperty(i)) continue; + + // Remove unwanted types and return records. + if (group) { + if (results[i].match(matchable) !== null) { + // Collect relevant. + record = groucho.storage.get(results[i]); + // Move key to property. + record._key = results[i]; + returnVals.push(record); + } + } + else { + // Collect and return all. + record = groucho.storage.get(results[i]); + // Move key to property. + record._key = results[i]; + returnVals.push(record); + } + } + + // Ensure proper key sorting regardless of index result order. + returnVals.sort(function (a, b) { + // Created non-standard or outside Groucho. + // Should always contain an original key which contains a dot. + if (!a.hasOwnProperty('_key') || !a._key.match(/\./) || + !b.hasOwnProperty('_key') || !b._key.match(/\./)) { + return 0; + } + // Sort by post-prefix key. + if (parseInt(b._key.split('.')[2], 10) > parseInt(a._key.split('.')[2], 10)) { + return -1; + } + else { + return 1; + } + }); + + return returnVals; + }; + + + /** + * Data transforms due to version updates. Prevents past use data corruption. + */ + groucho.schema = function () { + // Update keys. + var keys = { + 'user.sessionOrigin': { + 'oldKey': 'user.session_origin', + 'version': '0.2.0' + } + }; + + for (var newKey in keys) { + if ((groucho.storage.get(newKey) === null) && (groucho.storage.get(keys[newKey].oldKey) !== null)) { + groucho.storage.set(newKey, groucho.storage.set(keys[newKey].oldKey)); + groucho.storage.remove(keys[newKey].oldKey); + } + } + }; + +})(window.jQuery || window.Zepto || window.$, groucho); diff --git a/test/groucho.html b/test/groucho.html index ca3c800..7391906 100644 --- a/test/groucho.html +++ b/test/groucho.html @@ -49,6 +49,18 @@ // Utilities. return simpleInclude('../src/storage.js'); }) + .then(function() { + // Utilities. + return simpleInclude('../src/utility.js'); + }) + .then(function() { + // Utilities. + return simpleInclude('../src/tracking.js'); + }) + .then(function() { + // Utilities. + return simpleInclude('../src/favorites.js'); + }) .then(function() { // Clear out past tests, unless explicitly not. if (!location.href.match(/\?noflush\=|&noflush\=/)) {