last-campaign
Version:
Saves campaign query string parameters into a session cookie so they can be retrieved and passed to your marketing automation system when needed.
605 lines (509 loc) • 16.6 kB
JavaScript
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.lastCampaign = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
;
var querystring = require('querystring');
var cookie = require('cookie');
var merge = require('deepmerge');
/**
* Module exports.
* @public
*/
module.exports = saveLastCampaign;
/**
* saveLastCampaign
*
* Saves campaign query string parameters into a session cookie so they can be
* retrieved and passed to your marketing automation system when needed.
*
* @param {object} [opts] Options object.
* @param {string} [opts.prefix] Cookie name prefix.
* @param {array} [opts.params] Default parameters.
* @param {array} [opts.extra] Extra parameters.
* @public
*/
function saveLastCampaign(opts) {
var options = {
defaults: true,
prefix: '',
params: [
'utm_campaign',
'utm_source',
'utm_medium',
'utm_term',
'utm_content'
],
data: {},
path: '/',
domain: null,
timeout: 30
};
var pageQueryString = getQueryString();
var data = {};
var cookieOptions = {};
var now = new Date();
var expires;
// Remove default parameters if necessary
if (typeof opts === 'object') {
if (typeof opts.defaults !== 'undefined' && opts.defaults === false) {
options.params = [];
}
}
// Merge opts onto options
if (arguments.length && typeof opts === 'object') {
options = merge(options, opts);
}
var dataKeys = Object.keys(options.data);
// Set default cookie options
cookieOptions = {
domain: options.domain,
path: options.path
};
// Set cookie expiration and advance expiration for existing cookies
if (options.timeout) {
expires = new Date(now.setMinutes(now.getMinutes() + options.timeout));
// querystring param cookies
options.params.forEach(function (key) {
updateExpiration(options.prefix + key, expires, cookieOptions);
});
// data object cookies
if (dataKeys.length !== 0) {
dataKeys.forEach(function (key) {
updateExpiration(options.prefix + key, expires, cookieOptions);
});
}
}
// Parse the query string
if (pageQueryString.length !== 0) {
data = querystring.parse(pageQueryString);
var removed = false;
// Create the cookies
options.params.forEach(function (key) {
if (data[key]) {
// param found in querystring so remove all necessary existing cookies first
if (!removed) {
removeCookies(options, cookieOptions);
removed = true;
}
// Merge expires in to prevent the following error:
// Uncaught TypeError: opt.expires.toUTCString is not a function
setCookie(options.prefix + key, data[key], merge(cookieOptions, {
expires: expires
}));
}
});
}
// Save the data object
if (dataKeys.length !== 0) {
dataKeys.forEach(function (key) {
// Skip undefined, null, or empty values
if (typeof options.data[key] === 'undefined' || options.data[key] === null || options.data[key] === '') {
return;
}
// Create the cookie if it doesn't exist
if (!getCookie(key)) {
setCookie(options.prefix + key, options.data[key], merge(cookieOptions, {
expires: expires
}));
}
});
}
}
/**
* Remove all cookies matching options.params
* @param {Object} options
* @private
*/
function removeCookies(options, cookieOptions) {
options.params.forEach(function (key) {
document.cookie = cookie.serialize(options.prefix + key, '', merge(cookieOptions, {
expires: new Date('Thu, 01 Jan 1970 00:00:00 GMT')
}));
});
}
/**
* Returns the query string without its initial question mark.
*
* E.g. foo=bar&baz=qux
*
* @return {string}
* @private
*/
function getQueryString() {
return window.location.search.substring(1);
}
/**
* Sets a browser cookie given a valid cookie string.
*
* E.g. "foo=bar; httpOnly"
*
* @param {string} name - name of cookie
* @param {string} value - value of cookie
* @param {object} options - object containing cookie options
* @private
*/
function setCookie(name, value, options) {
document.cookie = cookie.serialize(name, value, options);
}
/**
* Returns cookie value
*
* @param {string} name - name of cookie
* @return {string} token
* @private
*/
function getCookie(name) {
if (arguments.length === 0 && typeof opts !== 'string') {
return;
}
var match = document.cookie.match('(?:^|; )' + name + '=([^;]+)');
if (match) {
return match[1];
} else {
return '';
}
}
/**
* Update cookie expiration
*
* @param {string} name - name of cookie
* @param {date} expires - expiration date/time of cookie
* @param {object} options - object containing cookie options
* @private
*/
function updateExpiration(name, expires, options) {
var existingValue = getCookie(name);
if (existingValue) {
setCookie(name, existingValue, merge(options, {
expires: expires
}));
}
}
},{"cookie":2,"deepmerge":3,"querystring":6}],2:[function(require,module,exports){
/*!
* cookie
* Copyright(c) 2012-2014 Roman Shtylman
* Copyright(c) 2015 Douglas Christopher Wilson
* MIT Licensed
*/
/**
* Module exports.
* @public
*/
exports.parse = parse;
exports.serialize = serialize;
/**
* Module variables.
* @private
*/
var decode = decodeURIComponent;
var encode = encodeURIComponent;
var pairSplitRegExp = /; */;
/**
* RegExp to match field-content in RFC 7230 sec 3.2
*
* field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
* field-vchar = VCHAR / obs-text
* obs-text = %x80-FF
*/
var fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/;
/**
* Parse a cookie header.
*
* Parse the given cookie header string into an object
* The object has the various cookies as keys(names) => values
*
* @param {string} str
* @param {object} [options]
* @return {object}
* @public
*/
function parse(str, options) {
if (typeof str !== 'string') {
throw new TypeError('argument str must be a string');
}
var obj = {}
var opt = options || {};
var pairs = str.split(pairSplitRegExp);
var dec = opt.decode || decode;
pairs.forEach(function(pair) {
var eq_idx = pair.indexOf('=')
// skip things that don't look like key=value
if (eq_idx < 0) {
return;
}
var key = pair.substr(0, eq_idx).trim()
var val = pair.substr(++eq_idx, pair.length).trim();
// quoted values
if ('"' == val[0]) {
val = val.slice(1, -1);
}
// only assign once
if (undefined == obj[key]) {
obj[key] = tryDecode(val, dec);
}
});
return obj;
}
/**
* Serialize data into a cookie header.
*
* Serialize the a name value pair into a cookie string suitable for
* http headers. An optional options object specified cookie parameters.
*
* serialize('foo', 'bar', { httpOnly: true })
* => "foo=bar; httpOnly"
*
* @param {string} name
* @param {string} val
* @param {object} [options]
* @return {string}
* @public
*/
function serialize(name, val, options) {
var opt = options || {};
var enc = opt.encode || encode;
if (!fieldContentRegExp.test(name)) {
throw new TypeError('argument name is invalid');
}
var value = enc(val);
if (value && !fieldContentRegExp.test(value)) {
throw new TypeError('argument val is invalid');
}
var pairs = [name + '=' + value];
if (null != opt.maxAge) {
var maxAge = opt.maxAge - 0;
if (isNaN(maxAge)) throw new Error('maxAge should be a Number');
pairs.push('Max-Age=' + Math.floor(maxAge));
}
if (opt.domain) {
if (!fieldContentRegExp.test(opt.domain)) {
throw new TypeError('option domain is invalid');
}
pairs.push('Domain=' + opt.domain);
}
if (opt.path) {
if (!fieldContentRegExp.test(opt.path)) {
throw new TypeError('option path is invalid');
}
pairs.push('Path=' + opt.path);
}
if (opt.expires) pairs.push('Expires=' + opt.expires.toUTCString());
if (opt.httpOnly) pairs.push('HttpOnly');
if (opt.secure) pairs.push('Secure');
if (opt.firstPartyOnly) pairs.push('First-Party-Only');
return pairs.join('; ');
}
/**
* Try decoding a string using a decoding function.
*
* @param {string} str
* @param {function} decode
* @private
*/
function tryDecode(str, decode) {
try {
return decode(str);
} catch (e) {
return str;
}
}
},{}],3:[function(require,module,exports){
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(factory);
} else if (typeof exports === 'object') {
module.exports = factory();
} else {
root.deepmerge = factory();
}
}(this, function () {
return function deepmerge(target, src) {
var array = Array.isArray(src);
var dst = array && [] || {};
if (array) {
target = target || [];
dst = dst.concat(target);
src.forEach(function(e, i) {
if (typeof dst[i] === 'undefined') {
dst[i] = e;
} else if (typeof e === 'object') {
dst[i] = deepmerge(target[i], e);
} else {
if (target.indexOf(e) === -1) {
dst.push(e);
}
}
});
} else {
if (target && typeof target === 'object') {
Object.keys(target).forEach(function (key) {
dst[key] = target[key];
})
}
Object.keys(src).forEach(function (key) {
if (typeof src[key] !== 'object' || !src[key]) {
dst[key] = src[key];
}
else {
if (!target[key]) {
dst[key] = src[key];
} else {
dst[key] = deepmerge(target[key], src[key]);
}
}
});
}
return dst;
}
}));
},{}],4:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
;
// If obj.hasOwnProperty has been overridden, then calling
// obj.hasOwnProperty(prop) will break.
// See: https://github.com/joyent/node/issues/1707
function hasOwnProperty(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
module.exports = function(qs, sep, eq, options) {
sep = sep || '&';
eq = eq || '=';
var obj = {};
if (typeof qs !== 'string' || qs.length === 0) {
return obj;
}
var regexp = /\+/g;
qs = qs.split(sep);
var maxKeys = 1000;
if (options && typeof options.maxKeys === 'number') {
maxKeys = options.maxKeys;
}
var len = qs.length;
// maxKeys <= 0 means that we should not limit keys count
if (maxKeys > 0 && len > maxKeys) {
len = maxKeys;
}
for (var i = 0; i < len; ++i) {
var x = qs[i].replace(regexp, '%20'),
idx = x.indexOf(eq),
kstr, vstr, k, v;
if (idx >= 0) {
kstr = x.substr(0, idx);
vstr = x.substr(idx + 1);
} else {
kstr = x;
vstr = '';
}
k = decodeURIComponent(kstr);
v = decodeURIComponent(vstr);
if (!hasOwnProperty(obj, k)) {
obj[k] = v;
} else if (isArray(obj[k])) {
obj[k].push(v);
} else {
obj[k] = [obj[k], v];
}
}
return obj;
};
var isArray = Array.isArray || function (xs) {
return Object.prototype.toString.call(xs) === '[object Array]';
};
},{}],5:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
;
var stringifyPrimitive = function(v) {
switch (typeof v) {
case 'string':
return v;
case 'boolean':
return v ? 'true' : 'false';
case 'number':
return isFinite(v) ? v : '';
default:
return '';
}
};
module.exports = function(obj, sep, eq, name) {
sep = sep || '&';
eq = eq || '=';
if (obj === null) {
obj = undefined;
}
if (typeof obj === 'object') {
return map(objectKeys(obj), function(k) {
var ks = encodeURIComponent(stringifyPrimitive(k)) + eq;
if (isArray(obj[k])) {
return map(obj[k], function(v) {
return ks + encodeURIComponent(stringifyPrimitive(v));
}).join(sep);
} else {
return ks + encodeURIComponent(stringifyPrimitive(obj[k]));
}
}).join(sep);
}
if (!name) return '';
return encodeURIComponent(stringifyPrimitive(name)) + eq +
encodeURIComponent(stringifyPrimitive(obj));
};
var isArray = Array.isArray || function (xs) {
return Object.prototype.toString.call(xs) === '[object Array]';
};
function map (xs, f) {
if (xs.map) return xs.map(f);
var res = [];
for (var i = 0; i < xs.length; i++) {
res.push(f(xs[i], i));
}
return res;
}
var objectKeys = Object.keys || function (obj) {
var res = [];
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) res.push(key);
}
return res;
};
},{}],6:[function(require,module,exports){
;
exports.decode = exports.parse = require('./decode');
exports.encode = exports.stringify = require('./encode');
},{"./decode":4,"./encode":5}]},{},[1])(1)
});