diff --git a/README.md b/README.md index 806081d..ca12702 100644 --- a/README.md +++ b/README.md @@ -119,12 +119,13 @@ If you would like to customize how fields get added to your form, following opti affiliate: "IAFFILIATE" }; // Maps the initial affiliate parameter to IAFFILIATE form field - _uf.secure = true; // Enable secure cookies - _uf.sessionLength = 2; // In hours. Default is 1 hour - _uf.add_to_form = "first"; // 'none', 'all', 'first'. Default is 'all' - _uf.form_query_selector = 'form[action="/sign_up"]'; // custom form selector. Default is 'form', to select any form. - _uf.decode_uris = true; // Decode URLs before populating the form. e.g. use "https://" instead of "https%3A//". - // NOTE: This will decode the entire URL and, in rare cases, may have side effects. + _uf.reset_params_at_session_end = true; // Default: false. Use empty string values if UTM not present in the same session as the form submit + _uf.secure = true; // Enable secure cookies + _uf.sessionLength = 2; // In hours. Default is 1 hour + _uf.add_to_form = "first"; // 'none', 'all', 'first'. Default is 'all' + _uf.form_query_selector = 'form[action="/sign_up"]'; // custom form selector. Default is 'form', to select any form. + _uf.decode_uris = true; // Decode URLs before populating the form. e.g. use "https://" instead of "https%3A//". + // NOTE: This will decode the entire URL and, in rare cases, may have side effects. diff --git a/dest/utm_form-1.3.0.js b/dest/utm_form-1.3.0.js new file mode 100644 index 0000000..d0fd9ec --- /dev/null +++ b/dest/utm_form-1.3.0.js @@ -0,0 +1,379 @@ +var UtmCookie; + +UtmCookie = class UtmCookie { + constructor(options = {}) { + this._cookieNamePrefix = '_uc_'; + this._domain = options.domain; + this._secure = options.secure || false; + this._initialUtmParams = options.initialUtmParams || false; + this._resetParams = options.resetParams || false; + this._sessionLength = options.sessionLength || 1; + this._cookieExpiryDays = options.cookieExpiryDays || 365; + this._additionalParams = options.additionalParams || []; + this._additionalInitialParams = options.additionalInitialParams || []; + this._utmParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content']; + this.writeInitialReferrer(); + this.writeLastReferrer(); + this.writeInitialLandingPageUrl(); + this.writeAdditionalInitialParams(); + if (this._initialUtmParams) { + this.writeInitialUtmCookieFromParams(); + } + if (this._resetParams) { + if (!this.getCurrentSession()) { + this.writeAdditionalParams(); + this.writeUtmCookieFromParams(); + } + } else { + if (this.additionalParamsPresentInUrl()) { + this.writeAdditionalParams(); + } + if (this.utmPresentInUrl()) { + this.writeUtmCookieFromParams(); + } + } + this.setCurrentSession(); + return; + } + + createCookie(name, value, days, path, domain, secure) { + var cookieDomain, cookieExpire, cookiePath, cookieSecure, date, expireDate; + expireDate = null; + if (days) { + date = new Date; + date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000); + expireDate = date; + } + cookieExpire = expireDate != null ? '; expires=' + expireDate.toGMTString() : ''; + cookiePath = path != null ? '; path=' + path : '; path=/'; + cookieDomain = domain != null ? '; domain=' + domain : ''; + cookieSecure = secure ? '; secure' : ''; + document.cookie = this._cookieNamePrefix + name + '=' + escape(value) + cookieExpire + cookiePath + cookieDomain + cookieSecure; + } + + readCookie(name) { + var c, ca, i, nameEQ, value; + nameEQ = this._cookieNamePrefix + name + '='; + ca = document.cookie.split(';'); + i = 0; + while (i < ca.length) { + c = ca[i]; + while (c.charAt(0) === ' ') { + c = c.substring(1, c.length); + } + if (c.indexOf(nameEQ) === 0) { + value = c.substring(nameEQ.length, c.length); + if (value !== 'null') { + return value; + } + } + i++; + } + return null; + } + + eraseCookie(name) { + this.createCookie(name, '', -1, null, this._domain, this._secure); + } + + getParameterByName(name) { + var regex, regexS, results, value; + name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]'); + regexS = '[\\?&]' + name + '=([^&#]*)'; + regex = new RegExp(regexS); + results = regex.exec(window.location.search); + if (results) { + value = decodeURIComponent(results[1].replace(/\+/g, ' ')); + if (value.length > 0) { + return value; + } else { + return null; + } + } else { + return null; + } + } + + additionalParamsPresentInUrl() { + var j, len, param, ref; + ref = this._additionalParams; + for (j = 0, len = ref.length; j < len; j++) { + param = ref[j]; + if (this.getParameterByName(param)) { + return true; + } + } + return false; + } + + utmPresentInUrl() { + var j, len, param, ref; + ref = this._utmParams; + for (j = 0, len = ref.length; j < len; j++) { + param = ref[j]; + if (this.getParameterByName(param)) { + return true; + } + } + return false; + } + + writeCookie(name, value) { + this.createCookie(name, value, this._cookieExpiryDays, null, this._domain, this._secure); + } + + writeCookieOnce(name, value) { + var existingValue; + existingValue = this.readCookie(name); + if (!existingValue) { + this.writeCookie(name, value); + } + } + + writeAdditionalParams() { + var j, len, param, ref, value; + ref = this._additionalParams; + for (j = 0, len = ref.length; j < len; j++) { + param = ref[j]; + value = this.getParameterByName(param); + this.writeCookie(param, value); + } + } + + writeAdditionalInitialParams() { + var j, len, name, param, ref, value; + ref = this._additionalInitialParams; + for (j = 0, len = ref.length; j < len; j++) { + param = ref[j]; + name = 'initial_' + param; + value = this.getParameterByName(param); + this.writeCookieOnce(name, value); + } + } + + writeUtmCookieFromParams() { + var j, len, param, ref, value; + ref = this._utmParams; + for (j = 0, len = ref.length; j < len; j++) { + param = ref[j]; + value = this.getParameterByName(param); + this.writeCookie(param, value); + } + } + + writeInitialUtmCookieFromParams() { + var j, len, name, param, ref, value; + ref = this._utmParams; + for (j = 0, len = ref.length; j < len; j++) { + param = ref[j]; + name = 'initial_' + param; + value = this.getParameterByName(param); + this.writeCookieOnce(name, value); + } + } + + _sameDomainReferrer(referrer) { + var hostname; + hostname = document.location.hostname; + return referrer.indexOf(this._domain) > -1 || referrer.indexOf(hostname) > -1; + } + + _isInvalidReferrer(referrer) { + return referrer === '' || referrer === void 0; + } + + writeInitialReferrer() { + var value; + value = document.referrer; + if (this._isInvalidReferrer(value)) { + value = 'direct'; + } + this.writeCookieOnce('referrer', value); + } + + writeLastReferrer() { + var value; + value = document.referrer; + if (!this._sameDomainReferrer(value)) { + if (this._isInvalidReferrer(value)) { + value = 'direct'; + } + this.writeCookie('last_referrer', value); + } + } + + writeInitialLandingPageUrl() { + var value; + value = this.cleanUrl(); + if (value) { + this.writeCookieOnce('initial_landing_page', value); + } + } + + initialReferrer() { + return this.readCookie('referrer'); + } + + lastReferrer() { + return this.readCookie('last_referrer'); + } + + initialLandingPageUrl() { + return this.readCookie('initial_landing_page'); + } + + incrementVisitCount() { + var cookieName, existingValue, newValue; + cookieName = 'visits'; + existingValue = parseInt(this.readCookie(cookieName), 10); + if (isNaN(existingValue)) { + newValue = 1; + } else { + newValue = existingValue + 1; + } + this.writeCookie(cookieName, newValue); + } + + visits() { + return this.readCookie('visits'); + } + + getCurrentSession() { + var cookieName; + cookieName = 'current_session'; + return this.readCookie(cookieName); + } + + setCurrentSession() { + var cookieName, existingValue; + cookieName = 'current_session'; + existingValue = this.readCookie(cookieName); + if (!existingValue) { + this.createCookie(cookieName, 'true', this._sessionLength / 24, null, this._domain, this._secure); + this.incrementVisitCount(); + } + } + + cleanUrl() { + var cleanSearch; + cleanSearch = window.location.search.replace(/utm_[^&]+&?/g, '').replace(/&$/, '').replace(/^\?$/, ''); + return window.location.origin + window.location.pathname + cleanSearch + window.location.hash; + } + +}; + +var UtmForm, _uf; + +UtmForm = class UtmForm { + constructor(options = {}) { + this._utmParamsMap = {}; + this._utmParamsMap.utm_source = options.utm_source_field || 'USOURCE'; + this._utmParamsMap.utm_medium = options.utm_medium_field || 'UMEDIUM'; + this._utmParamsMap.utm_campaign = options.utm_campaign_field || 'UCAMPAIGN'; + this._utmParamsMap.utm_content = options.utm_content_field || 'UCONTENT'; + this._utmParamsMap.utm_term = options.utm_term_field || 'UTERM'; + this._initialUtmParamsMap = {}; + this._initialUtmParamsMap.initial_utm_source = options.initial_utm_source_field || 'IUSOURCE'; + this._initialUtmParamsMap.initial_utm_medium = options.initial_utm_medium_field || 'IUMEDIUM'; + this._initialUtmParamsMap.initial_utm_campaign = options.initial_utm_campaign_field || 'IUCAMPAIGN'; + this._initialUtmParamsMap.initial_utm_content = options.initial_utm_content_field || 'IUCONTENT'; + this._initialUtmParamsMap.initial_utm_term = options.initial_utm_term_field || 'IUTERM'; + this._additionalParamsMap = options.additional_params_map || {}; + this._additionalInitialParamsMap = options.additional_initial_params_map || {}; + this._initialReferrerField = options.initial_referrer_field || 'IREFERRER'; + this._lastReferrerField = options.last_referrer_field || 'LREFERRER'; + this._initialLandingPageField = options.initial_landing_page_field || 'ILANDPAGE'; + this._visitsField = options.visits_field || 'VISITS'; + // Options: + // "none": Don't add any fields to any form + // "first": Add UTM and other fields to only first form on the page + // "all": (Default) Add UTM and other fields to all forms on the page + this._addToForm = options.add_to_form || 'all'; + this._formQuerySelector = options.form_query_selector || 'form'; + this._decodeURIs = options.decode_uris || false; + this.utmCookie = new UtmCookie({ + domain: options.domain, + secure: options.secure, + sessionLength: options.sessionLength, + cookieExpiryDays: options.cookieExpiryDays, + initialUtmParams: options.initial_utm_params, + resetParams: options.reset_params_at_session_end, + additionalParams: Object.getOwnPropertyNames(this._additionalParamsMap), + additionalInitialParams: Object.getOwnPropertyNames(this._additionalInitialParamsMap) + }); + this.addAllFields(); + } + + addAllFields() { + var allForms, i, len; + allForms = document.querySelectorAll(this._formQuerySelector); + if (this._addToForm === 'none') { + len = 0; + } else if (this._addToForm === 'first') { + len = Math.min(1, allForms.length); + } else { + len = allForms.length; + } + i = 0; + while (i < len) { + this.addAllFieldsToForm(allForms[i]); + i++; + } + } + + addAllFieldsToForm(form) { + var cookieName, fieldName, param, ref, ref1, ref2, ref3; + if (form && !form._utm_tagged) { + form._utm_tagged = true; + ref = this._utmParamsMap; + for (param in ref) { + fieldName = ref[param]; + this.addFormElem(form, fieldName, this.utmCookie.readCookie(param)); + } + ref1 = this._initialUtmParamsMap; + for (param in ref1) { + fieldName = ref1[param]; + this.addFormElem(form, fieldName, this.utmCookie.readCookie(param)); + } + ref2 = this._additionalParamsMap; + for (param in ref2) { + fieldName = ref2[param]; + this.addFormElem(form, fieldName, this.utmCookie.readCookie(param)); + } + ref3 = this._additionalInitialParamsMap; + for (param in ref3) { + fieldName = ref3[param]; + cookieName = 'initial_' + param; + this.addFormElem(form, fieldName, this.utmCookie.readCookie(cookieName)); + } + this.addFormElem(form, this._initialReferrerField, this.utmCookie.initialReferrer()); + this.addFormElem(form, this._lastReferrerField, this.utmCookie.lastReferrer()); + this.addFormElem(form, this._initialLandingPageField, this.utmCookie.initialLandingPageUrl()); + this.addFormElem(form, this._visitsField, this.utmCookie.visits()); + } + } + + addFormElem(form, fieldName, fieldValue) { + this.insertAfter(this.getFieldEl(fieldName, fieldValue), form.lastChild); + } + + // NOTE: This should be called for each form element or since it + // attaches itself to the first form + getFieldEl(fieldName, fieldValue) { + var fieldEl; + fieldEl = document.createElement('input'); + fieldEl.type = "hidden"; + fieldEl.name = fieldName; + fieldEl.value = fieldValue ? this._decodeURIs ? decodeURIComponent(fieldValue) : fieldValue : fieldValue; + return fieldEl; + } + + insertAfter(newNode, referenceNode) { + return referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); + } + +}; + +_uf = window._uf || {}; + +window.UtmForm = new UtmForm(_uf); diff --git a/dest/utm_form-1.3.0.min.js b/dest/utm_form-1.3.0.min.js new file mode 100644 index 0000000..3055ede --- /dev/null +++ b/dest/utm_form-1.3.0.min.js @@ -0,0 +1 @@ +var UtmCookie,UtmForm,_uf;UtmCookie=class{constructor(options={}){this._cookieNamePrefix="_uc_",this._domain=options.domain,this._secure=options.secure||!1,this._initialUtmParams=options.initialUtmParams||!1,this._resetParams=options.resetParams||!1,this._sessionLength=options.sessionLength||1,this._cookieExpiryDays=options.cookieExpiryDays||365,this._additionalParams=options.additionalParams||[],this._additionalInitialParams=options.additionalInitialParams||[],this._utmParams=["utm_source","utm_medium","utm_campaign","utm_term","utm_content"],this.writeInitialReferrer(),this.writeLastReferrer(),this.writeInitialLandingPageUrl(),this.writeAdditionalInitialParams(),this._initialUtmParams&&this.writeInitialUtmCookieFromParams(),this._resetParams?this.getCurrentSession()||(this.writeAdditionalParams(),this.writeUtmCookieFromParams()):(this.additionalParamsPresentInUrl()&&this.writeAdditionalParams(),this.utmPresentInUrl()&&this.writeUtmCookieFromParams()),this.setCurrentSession()}createCookie(name,value,days,path,domain,secure){var cookieDomain,cookieExpire,cookiePath,cookieSecure,date,expireDate;expireDate=null,days&&((date=new Date).setTime(date.getTime()+24*days*60*60*1e3),expireDate=date),cookieExpire=null!=expireDate?"; expires="+expireDate.toGMTString():"",cookiePath=null!=path?"; path="+path:"; path=/",cookieDomain=null!=domain?"; domain="+domain:"",cookieSecure=secure?"; secure":"",document.cookie=this._cookieNamePrefix+name+"="+escape(value)+cookieExpire+cookiePath+cookieDomain+cookieSecure}readCookie(name){var c,ca,i,nameEQ,value;for(nameEQ=this._cookieNamePrefix+name+"=",ca=document.cookie.split(";"),i=0;i0?value:null}additionalParamsPresentInUrl(){var j,len,param,ref;for(j=0,len=(ref=this._additionalParams).length;j-1||referrer.indexOf(hostname)>-1}_isInvalidReferrer(referrer){return""===referrer||void 0===referrer}writeInitialReferrer(){var value;value=document.referrer,this._isInvalidReferrer(value)&&(value="direct"),this.writeCookieOnce("referrer",value)}writeLastReferrer(){var value;value=document.referrer,this._sameDomainReferrer(value)||(this._isInvalidReferrer(value)&&(value="direct"),this.writeCookie("last_referrer",value))}writeInitialLandingPageUrl(){var value;(value=this.cleanUrl())&&this.writeCookieOnce("initial_landing_page",value)}initialReferrer(){return this.readCookie("referrer")}lastReferrer(){return this.readCookie("last_referrer")}initialLandingPageUrl(){return this.readCookie("initial_landing_page")}incrementVisitCount(){var existingValue,newValue;existingValue=parseInt(this.readCookie("visits"),10),newValue=isNaN(existingValue)?1:existingValue+1,this.writeCookie("visits",newValue)}visits(){return this.readCookie("visits")}getCurrentSession(){return"current_session",this.readCookie("current_session")}setCurrentSession(){this.readCookie("current_session")||(this.createCookie("current_session","true",this._sessionLength/24,null,this._domain,this._secure),this.incrementVisitCount())}cleanUrl(){var cleanSearch;return cleanSearch=window.location.search.replace(/utm_[^&]+&?/g,"").replace(/&$/,"").replace(/^\?$/,""),window.location.origin+window.location.pathname+cleanSearch+window.location.hash}},UtmForm=class{constructor(options={}){this._utmParamsMap={},this._utmParamsMap.utm_source=options.utm_source_field||"USOURCE",this._utmParamsMap.utm_medium=options.utm_medium_field||"UMEDIUM",this._utmParamsMap.utm_campaign=options.utm_campaign_field||"UCAMPAIGN",this._utmParamsMap.utm_content=options.utm_content_field||"UCONTENT",this._utmParamsMap.utm_term=options.utm_term_field||"UTERM",this._initialUtmParamsMap={},this._initialUtmParamsMap.initial_utm_source=options.initial_utm_source_field||"IUSOURCE",this._initialUtmParamsMap.initial_utm_medium=options.initial_utm_medium_field||"IUMEDIUM",this._initialUtmParamsMap.initial_utm_campaign=options.initial_utm_campaign_field||"IUCAMPAIGN",this._initialUtmParamsMap.initial_utm_content=options.initial_utm_content_field||"IUCONTENT",this._initialUtmParamsMap.initial_utm_term=options.initial_utm_term_field||"IUTERM",this._additionalParamsMap=options.additional_params_map||{},this._additionalInitialParamsMap=options.additional_initial_params_map||{},this._initialReferrerField=options.initial_referrer_field||"IREFERRER",this._lastReferrerField=options.last_referrer_field||"LREFERRER",this._initialLandingPageField=options.initial_landing_page_field||"ILANDPAGE",this._visitsField=options.visits_field||"VISITS",this._addToForm=options.add_to_form||"all",this._formQuerySelector=options.form_query_selector||"form",this._decodeURIs=options.decode_uris||!1,this.utmCookie=new UtmCookie({domain:options.domain,secure:options.secure,sessionLength:options.sessionLength,cookieExpiryDays:options.cookieExpiryDays,initialUtmParams:options.initial_utm_params,resetParams:options.reset_params_at_session_end,additionalParams:Object.getOwnPropertyNames(this._additionalParamsMap),additionalInitialParams:Object.getOwnPropertyNames(this._additionalInitialParamsMap)}),this.addAllFields()}addAllFields(){var allForms,i,len;for(allForms=document.querySelectorAll(this._formQuerySelector),len="none"===this._addToForm?0:"first"===this._addToForm?Math.min(1,allForms.length):allForms.length,i=0;i @@ -56,7 +61,9 @@ class UtmCookie while c.charAt(0) == ' ' c = c.substring(1, c.length) if c.indexOf(nameEQ) == 0 - return c.substring(nameEQ.length, c.length) + value = c.substring(nameEQ.length, c.length) + if value != 'null' + return value i++ null @@ -70,9 +77,10 @@ class UtmCookie regex = new RegExp(regexS) results = regex.exec(window.location.search) if results - decodeURIComponent results[1].replace(/\+/g, ' ') + value = decodeURIComponent results[1].replace(/\+/g, ' ') + if value.length > 0 then value else null else - '' + null additionalParamsPresentInUrl: -> for param in @_additionalParams @@ -105,7 +113,7 @@ class UtmCookie writeAdditionalInitialParams: -> for param in @_additionalInitialParams name = 'initial_' + param - value = @getParameterByName(param) || null + value = @getParameterByName(param) @writeCookieOnce name, value return @@ -118,7 +126,7 @@ class UtmCookie writeInitialUtmCookieFromParams: -> for param in @_utmParams name = 'initial_' + param - value = @getParameterByName(param) || null + value = @getParameterByName(param) @writeCookieOnce name, value return @@ -172,6 +180,10 @@ class UtmCookie visits: -> @readCookie 'visits' + getCurrentSession: -> + cookieName = 'current_session' + return @readCookie(cookieName) + setCurrentSession: -> cookieName = 'current_session' existingValue = @readCookie(cookieName) diff --git a/lib/utm_form.coffee b/lib/utm_form.coffee index 8cd8716..5cdfab7 100644 --- a/lib/utm_form.coffee +++ b/lib/utm_form.coffee @@ -43,6 +43,7 @@ class UtmForm sessionLength: options.sessionLength, cookieExpiryDays: options.cookieExpiryDays, initialUtmParams: options.initial_utm_params, + resetParams: options.reset_params_at_session_end, additionalParams: Object.getOwnPropertyNames(@_additionalParamsMap), additionalInitialParams: Object.getOwnPropertyNames(@_additionalInitialParamsMap) }) @@ -99,7 +100,7 @@ class UtmForm fieldEl = document.createElement('input') fieldEl.type = "hidden" fieldEl.name = fieldName - fieldEl.value = if @_decodeURIs then decodeURIComponent(fieldValue) else fieldValue + fieldEl.value = if fieldValue then if @_decodeURIs then decodeURIComponent(fieldValue) else fieldValue else fieldValue fieldEl insertAfter: (newNode, referenceNode) -> diff --git a/package-lock.json b/package-lock.json index 5d65b22..e2c35d1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "utm-form", - "version": "1.2.0", + "version": "1.3.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index e7d5d97..0e6c170 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "utm-form", - "version": "1.2.0", + "version": "1.3.0", "description": "Add UTM parameters and other information to forms", "main": "", "scripts": {