GAS w/ dc.js
/**
* @preserve Copyright 2011, Cardinal Path and DigitalInc.
*
* GAS - Google Analytics on Steroids
* https://github.com/CardinalPath/gas
*
* @author Eduardo Cereto <eduardocereto@gmail.com>
* Licensed under the GPLv3 license.
*/
(function(window, undefined) {
/**
* GAS - Google Analytics on Steroids
*
* Helper Functions
*
* Copyright 2011, Cardinal Path and Direct Performance
* Licensed under the MIT license.
*
* @author Eduardo Cereto <eduardocereto@gmail.com>
*/
/**
* GasHelper singleton class
*
* Should be called when ga.js is loaded to get the pageTracker.
*
* @constructor
*/
/*jshint -W079*/
var GasHelper = function () {
this._setDummyTracker();
};
/*jshint +W079*/
GasHelper.prototype._setDummyTracker = function () {
if (!this['tracker']) {
var trackers = window['_gat']['_getTrackers']();
if (trackers.length > 0) {
this['tracker'] = trackers[0];
}
}
};
/**
* Returns true if the element is found in the Array, false otherwise.
*
* @param {Array} obj Array to search at.
* @param {object} item Item to search form.
* @return {boolean} true if contains.
*/
GasHelper.prototype.inArray = function (obj, item) {
if (obj && obj.length) {
for (var i = 0; i < obj.length; i++) {
if (obj[i] === item) {
return true;
}
}
}
return false;
};
/**
* Removes special characters and Lowercase String
*
* @param {string} str to be sanitized.
* @param {boolean} strict_opt If we should remove any non ascii char.
* @return {string} Sanitized string.
*/
GasHelper.prototype._sanitizeString = function (str, strict_opt) {
str = str.toLowerCase()
.replace(/^\ +/, '')
.replace(/\ +$/, '')
.replace(/\s+/g, '_')
.replace(/[áàâãåäæª]/g, 'a')
.replace(/[éèêëЄ€]/g, 'e')
.replace(/[íìîï]/g, 'i')
.replace(/[óòôõöøº]/g, 'o')
.replace(/[úùûü]/g, 'u')
.replace(/[碩]/g, 'c');
if (strict_opt) {
str = str.replace(/[^a-z0-9_\-]/g, '_');
}
return str.replace(/_+/g, '_');
};
/**
* Cross Browser helper to addEventListener.
*
* ga_next.js currently have a _addEventListener directive. So _gas will
* allways prefer that if available, and will use this one only as a fallback
*
* @param {HTMLElement} obj The Element to attach event to.
* @param {string} evt The event that will trigger the binded function.
* @param {function(event)} ofnc The function to bind to the element.
* @param {boolean} bubble true if event should be fired at bubble phase.
* Defaults to false. Works only on W3C compliant browser. MSFT don't support
* it.
* @return {boolean} true if it was successfuly binded.
*/
GasHelper.prototype._addEventListener = function (obj, evt, ofnc, bubble) {
var fnc = function (event) {
if (!event || !event.target) {
event = window.event;
event.target = event.srcElement;
}
return ofnc.call(obj, event);
};
// W3C model
if (obj.addEventListener) {
obj.addEventListener(evt, fnc, !!bubble);
return true;
}
// M$ft model
else if (obj.attachEvent) {
return obj.attachEvent('on' + evt, fnc);
}
// Browser doesn't support W3C or M$ft model. Time to go old school
else {
evt = 'on' + evt;
if (typeof obj[evt] === 'function') {
// Object already has a function on traditional
// Let's wrap it with our own function inside another function
fnc = (function (f1, f2) {
return function () {
f1.apply(this, arguments);
f2.apply(this, arguments);
};
}(obj[evt], fnc));
}
obj[evt] = fnc;
return true;
}
};
/**
* Cross Browser Helper to emulate jQuery.live
*
* Binds to the document root. Listens to all events of the specific type.
* If event don't bubble it won't catch
*/
GasHelper.prototype._liveEvent = function (tag, evt, ofunc) {
var gh = this;
tag = tag.toUpperCase();
tag = tag.split(',');
gh._addEventListener(document, evt, function (me) {
for (var el = me.target; el.nodeName !== 'HTML';
el = el.parentNode)
{
if (gh.inArray(tag, el.nodeName) || el.parentNode === null) {
break;
}
}
if (el && gh.inArray(tag, el.nodeName)) {
ofunc.call(el, me);
}
}, true);
};
/**
* Cross Browser DomReady function.
*
* Inspired by: http://dean.edwards.name/weblog/2006/06/again/#comment367184
*
* @param {function(Event)} callback DOMReady callback.
* @return {boolean} Ignore return value.
*/
GasHelper.prototype._DOMReady = function (callback) {
var scp = this;
function cb() {
if (cb.done) return;
cb.done = true;
callback.apply(scp, arguments);
}
if (/^(interactive|complete)/.test(document.readyState)) return cb();
this._addEventListener(document, 'DOMContentLoaded', cb, false);
this._addEventListener(window, 'load', cb, false);
};
/**
* GAS - Google Analytics on Steroids
*
* Copyright 2011, Cardinal Path and Direct Performance
* Licensed under the MIT license.
*
* @author Eduardo Cereto <eduardocereto@gmail.com>
*/
/*global document:true*/
/**
* Google Analytics original _gaq.
*
* This never tries to do something that is not supposed to. So it won't break
* in the future.
*/
window['_gaq'] = window['_gaq'] || [];
var _prev_gas = window['_gas'] || [];
// Avoid duplicate definition
if (_prev_gas._accounts_length >= 0) {
return;
}
//Shortcuts, these speed up and compress the code
/*jshint -W079*/
var document = window.document,
toString = Object.prototype.toString,
hasOwn = Object.prototype.hasOwnProperty,
push = Array.prototype.push,
slice = Array.prototype.slice,
trim = String.prototype.trim,
sindexOf = String.prototype.indexOf,
url = document.location.href,
documentElement = document.documentElement;
/*jshint +W079*/
/**
* GAS Sigleton
* @constructor
*/
function GAS() {
var self = this;
self['version'] = '1.11.0';
self._accounts = {};
self._accounts_length = 0;
self._queue = _prev_gas;
self._default_tracker = '_gas1';
self.gh = {};
self._hooks = {
'_addHook': [self._addHook]
};
// Need to be pushed to make sure tracker is done
// Sets up helpers, very first thing pushed into gas
self.push(function () {
self.gh = new GasHelper();
});
}
/**
* First standard Hook that is responsible to add next Hooks
*
* _addHook calls always reurn false so they don't get pushed to _gaq
* @param {string} fn The function you wish to add a Hook to.
* @param {function()} cb The callback function to be appended to hooks.
* @return {boolean} Always false.
*/
GAS.prototype._addHook = function (fn, cb) {
if (typeof fn === 'string' && typeof cb === 'function') {
if (typeof _gas._hooks[fn] === 'undefined') {
_gas._hooks[fn] = [];
}
_gas._hooks[fn].push(cb);
}
return false;
};
/**
* Construct the correct account name to be used on _gaq calls.
*
* The account name for the first unamed account pushed to _gas is the standard
* account name. It's pushed without the account name to _gaq, so if someone
* calls directly _gaq it works as expected.
* @param {string} acct Account name.
* @return {string} Correct account name to be used already with trailling dot.
*/
function _build_acct_name(acct) {
return acct === _gas._default_tracker ? '' : acct + '.';
}
function _gaq_push(arr) {
if (_gas.debug_mode) {
try {
console.log(arr);
}catch (e) {}
}
return window['_gaq'].push(arr);
}
/**
* Everything pushed to _gas is executed by this call.
*
* This function should not be called directly. Instead use _gas.push
* @return {number} This is the same return as _gaq.push calls.
*/
GAS.prototype._execute = function () {
var args = slice.call(arguments),
self = this,
sub = args.shift(),
gaq_execute = true,
i, foo, hooks, acct_name, repl_sub, return_val = 0;
if (typeof sub === 'function') {
// Pushed functions are executed right away
return _gaq_push(
(function (s, gh) {
return function () {
// pushed functions receive helpers through this object
s.call(gh);
};
}(sub, self.gh))
);
} else if (typeof sub === 'object' && sub.length > 0) {
foo = sub.shift();
if (sindexOf.call(foo, '.') >= 0) {
acct_name = foo.split('.')[0];
foo = foo.split('.')[1];
} else {
acct_name = undefined;
}
// Execute hooks
hooks = self._hooks[foo];
if (hooks && hooks.length > 0) {
for (i = 0; i < hooks.length; i++) {
try {
repl_sub = hooks[i].apply(self.gh, sub);
if (repl_sub === false) {
// Returning false from a hook cancel the call
gaq_execute = false;
} else {
if (repl_sub && repl_sub.length > 0) {
// Returning an array changes the call parameters
sub = repl_sub;
}
}
} catch (e) {
if (foo !== '_trackException') {
self.push(['_trackException', e]);
}
}
}
}
// Cancel execution on _gaq if any hook returned false
if (gaq_execute === false) {
return 1;
}
// Intercept _setAccount calls
if (foo === '_setAccount') {
for (i in self._accounts) {
if (self._accounts[i] === sub[0]) {
// Repeated account
if (acct_name === undefined) {
return 1;
}
}
}
acct_name = acct_name || '_gas' +
String(self._accounts_length + 1);
// Force that the first unamed account is _gas1
if (typeof self._accounts['_gas1'] === 'undefined' &&
sindexOf.call(acct_name, '_gas') !== -1) {
acct_name = '_gas1';
}
self._accounts[acct_name] = sub[0];
self._accounts_length += 1;
acct_name = _build_acct_name(acct_name);
return_val = _gaq_push([acct_name + foo, sub[0]]);
// Must try t get the tracker if it's a _setAccount
self.gh._setDummyTracker();
return return_val;
}
// Intercept functions that can only be called once.
if (foo === '_link' || foo === '_linkByPost' || foo === '_require' ||
foo === '_anonymizeIp')
{
args = slice.call(sub);
args.unshift(foo);
return _gaq_push(args);
}
// If user provides account than trigger event for just that account.
var acc_foo;
if (acct_name && self._accounts[acct_name]) {
acc_foo = _build_acct_name(acct_name) + foo;
args = slice.call(sub);
args.unshift(acc_foo);
return _gaq_push(args);
}
// Call Original _gaq, for all accounts
if (self._accounts_length > 0) {
for (i in self._accounts) {
if (hasOwn.call(self._accounts, i)) {
acc_foo = _build_acct_name(i) + foo;
args = slice.call(sub);
args.unshift(acc_foo);
return_val += _gaq_push(args);
}
}
} else {
// If there are no accounts we just push it to _gaq
args = slice.call(sub);
args.unshift(foo);
return _gaq_push(args);
}
return return_val ? 1 : 0;
}
};
/**
* Standard method to execute GA commands.
*
* Everything pushed to _gas is in fact pushed back to _gaq. So Helpers are
* ready for hooks. This creates _gaq as a series of functions that call
* _gas._execute() with the same arguments.
*/
GAS.prototype.push = function () {
var self = this;
var args = slice.call(arguments);
for (var i = 0; i < args.length; i++) {
(function (arr, self) {
window['_gaq'].push(function () {
self._execute.call(self, arr);
});
}(args[i], self));
}
};
/**
* _gas main object.
*
* It's supposed to be used just like _gaq but here we extend it. In it's core
* everything pushed to _gas is run through possible hooks and then pushed to
* _gaq
*/
/*global _gas:true*/
window['_gas'] = _gas = new GAS();
/*global _gas:false*/
/**
* Hook for _trackException
*
* Watchout for circular calls
*/
_gas.push(['_addHook', '_trackException', function (exception, message) {
_gas.push(['_trackEvent',
'Exception ' + (exception.name || 'Error'),
message || exception.message || exception,
url
]);
return false;
}]);
/**
* Hook to enable Debug Mode
*/
_gas.push(['_addHook', '_setDebug', function (set_debug) {
_gas.debug_mode = !!set_debug;
}]);
/**
* Hook to Remove other Hooks
*
* It will remove the last inserted hook from a _gas function.
*
* @param {string} func _gas Function Name to remove Hooks from.
* @return {boolean} Always returns false.
*/
_gas.push(['_addHook', '_popHook', function (func) {
var arr = _gas._hooks[func];
if (arr && arr.pop) {
arr.pop();
}
return false;
}]);
/**
* Hook to set the default tracker.
*
* The default tracker is the nameless tracker that is pushed into _gaq_push
*/
_gas.push(['_addHook', '_gasSetDefaultTracker', function (tname) {
_gas._default_tracker = tname;
return false;
}]);
/**
* Wrap-up
*/
// Execute previous functions
while (_gas._queue.length > 0) {
_gas.push(_gas._queue.shift());
}
// Import ga.js
if (typeof window._gat === 'undefined') {
(function () {
var gasScript = document.getElementByID('gas-script');
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.async = true;
if (gasScript !== null && gasScript.getAttribute('data-use-dcjs') === 'true') {
ga.src = (
'https:' === document.location.protocol ?
'https://' : 'http://') +
'stats.g.doubleclick.net/dc.js';
} else {
ga.src = (
'https:' === document.location.protocol ?
'https://ssl' : 'http://www') +
'.google-analytics.com/ga.js';
}
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
}());
}
})(window);
/**
* @preserve Copyright 2011, Cardinal Path and DigitalInc.
*
* GAS - Google Analytics on Steroids
* https://github.com/CardinalPath/gas
*
* @author Eduardo Cereto <eduardocereto@gmail.com>
* Licensed under the GPLv3 license.
*/
!function(a,b){function c(){var a=this;a.version="1.11.0",a._accounts={},a._accounts_length=0,a._queue=g,a._default_tracker="_gas1",a.gh={},a._hooks={_addHook:[a._addHook]},a.push(function(){a.gh=new f})}function d(a){return a===_gas._default_tracker?"":a+"."}function e(b){if(_gas.debug_mode)try{console.log(b)}catch(c){}return a._gaq.push(b)}var f=function(){this._setDummyTracker()};f.prototype._setDummyTracker=function(){if(!this.tracker){var b=a._gat._getTrackers();b.length>0&&(this.tracker=b[0])}},f.prototype.inArray=function(a,b){if(a&&a.length)for(var c=0;c<a.length;c++)if(a[c]===b)return!0;return!1},f.prototype._sanitizeString=function(a,b){return a=a.toLowerCase().replace(/^\ +/,"").replace(/\ +$/,"").replace(/\s+/g,"_").replace(/[áàâãåäæª]/g,"a").replace(/[éèêëЄ€]/g,"e").replace(/[íìîï]/g,"i").replace(/[óòôõöøº]/g,"o").replace(/[úùûü]/g,"u").replace(/[碩]/g,"c"),b&&(a=a.replace(/[^a-z0-9_\-]/g,"_")),a.replace(/_+/g,"_")},f.prototype._addEventListener=function(b,c,d,e){var f=function(c){return c&&c.target||(c=a.event,c.target=c.srcElement),d.call(b,c)};return b.addEventListener?(b.addEventListener(c,f,!!e),!0):b.attachEvent?b.attachEvent("on"+c,f):(c="on"+c,"function"==typeof b[c]&&(f=function(a,b){return function(){a.apply(this,arguments),b.apply(this,arguments)}}(b[c],f)),b[c]=f,!0)},f.prototype._liveEvent=function(a,b,c){var d=this;a=a.toUpperCase(),a=a.split(","),d._addEventListener(h,b,function(b){for(var e=b.target;"HTML"!==e.nodeName&&!d.inArray(a,e.nodeName)&&null!==e.parentNode;e=e.parentNode);e&&d.inArray(a,e.nodeName)&&c.call(e,b)},!0)},f.prototype._DOMReady=function(b){function c(){c.done||(c.done=!0,b.apply(d,arguments))}var d=this;return/^(interactive|complete)/.test(h.readyState)?c():(this._addEventListener(h,"DOMContentLoaded",c,!1),this._addEventListener(a,"load",c,!1),void 0)},a._gaq=a._gaq||[];var g=a._gas||[];if(!(g._accounts_length>=0)){var h=a.document,i=(Object.prototype.toString,Object.prototype.hasOwnProperty),j=(Array.prototype.push,Array.prototype.slice),k=(String.prototype.trim,String.prototype.indexOf),l=h.location.href;for(h.documentElement,c.prototype._addHook=function(a,b){return"string"==typeof a&&"function"==typeof b&&("undefined"==typeof _gas._hooks[a]&&(_gas._hooks[a]=[]),_gas._hooks[a].push(b)),!1},c.prototype._execute=function(){var a,c,f,g,h,l=j.call(arguments),m=this,n=l.shift(),o=!0,p=0;if("function"==typeof n)return e(function(a,b){return function(){a.call(b)}}(n,m.gh));if("object"==typeof n&&n.length>0){if(c=n.shift(),k.call(c,".")>=0?(g=c.split(".")[0],c=c.split(".")[1]):g=b,f=m._hooks[c],f&&f.length>0)for(a=0;a<f.length;a++)try{h=f[a].apply(m.gh,n),h===!1?o=!1:h&&h.length>0&&(n=h)}catch(q){"_trackException"!==c&&m.push(["_trackException",q])}if(o===!1)return 1;if("_setAccount"===c){for(a in m._accounts)if(m._accounts[a]===n[0]&&g===b)return 1;return g=g||"_gas"+String(m._accounts_length+1),"undefined"==typeof m._accounts._gas1&&-1!==k.call(g,"_gas")&&(g="_gas1"),m._accounts[g]=n[0],m._accounts_length+=1,g=d(g),p=e([g+c,n[0]]),m.gh._setDummyTracker(),p}if("_link"===c||"_linkByPost"===c||"_require"===c||"_anonymizeIp"===c)return l=j.call(n),l.unshift(c),e(l);var r;if(g&&m._accounts[g])return r=d(g)+c,l=j.call(n),l.unshift(r),e(l);if(!(m._accounts_length>0))return l=j.call(n),l.unshift(c),e(l);for(a in m._accounts)i.call(m._accounts,a)&&(r=d(a)+c,l=j.call(n),l.unshift(r),p+=e(l));return p?1:0}},c.prototype.push=function(){for(var b=this,c=j.call(arguments),d=0;d<c.length;d++)!function(b,c){a._gaq.push(function(){c._execute.call(c,b)})}(c[d],b)},a._gas=_gas=new c,_gas.push(["_addHook","_trackException",function(a,b){return _gas.push(["_trackEvent","Exception "+(a.name||"Error"),b||a.message||a,l]),!1}]),_gas.push(["_addHook","_setDebug",function(a){_gas.debug_mode=!!a}]),_gas.push(["_addHook","_popHook",function(a){var b=_gas._hooks[a];return b&&b.pop&&b.pop(),!1}]),_gas.push(["_addHook","_gasSetDefaultTracker",function(a){return _gas._default_tracker=a,!1}]);_gas._queue.length>0;)_gas.push(_gas._queue.shift());"undefined"==typeof a._gat&&!function(){var a=h.getElementByID("gas-script"),b=h.createElement("script");b.type="text/javascript",b.async=!0,b.src=null!==a&&"true"===a.getAttribute("data-use-dcjs")?("https:"===h.location.protocol?"https://":"http://")+"stats.g.doubleclick.net/dc.js":("https:"===h.location.protocol?"https://ssl":"http://www")+".google-analytics.com/ga.js";var c=h.getElementsByTagName("script")[0];c.parentNode.insertBefore(b,c)}()}}(window);
/**
* @preserve Copyright 2011, Cardinal Path and DigitalInc.
*
* GAS - Google Analytics on Steroids
* https://github.com/CardinalPath/gas
*
* @author Eduardo Cereto <eduardocereto@gmail.com>
* Licensed under the GPLv3 license.
*/
(function(window, undefined) {
/**
* GAS - Google Analytics on Steroids
*
* Helper Functions
*
* Copyright 2011, Cardinal Path and Direct Performance
* Licensed under the MIT license.
*
* @author Eduardo Cereto <eduardocereto@gmail.com>
*/
/**
* GasHelper singleton class
*
* Should be called when ga.js is loaded to get the pageTracker.
*
* @constructor
*/
/*jshint -W079*/
var GasHelper = function () {
this._setDummyTracker();
};
/*jshint +W079*/
GasHelper.prototype._setDummyTracker = function () {
if (!this['tracker']) {
var trackers = window['_gat']['_getTrackers']();
if (trackers.length > 0) {
this['tracker'] = trackers[0];
}
}
};
/**
* Returns true if the element is found in the Array, false otherwise.
*
* @param {Array} obj Array to search at.
* @param {object} item Item to search form.
* @return {boolean} true if contains.
*/
GasHelper.prototype.inArray = function (obj, item) {
if (obj && obj.length) {
for (var i = 0; i < obj.length; i++) {
if (obj[i] === item) {
return true;
}
}
}
return false;
};
/**
* Removes special characters and Lowercase String
*
* @param {string} str to be sanitized.
* @param {boolean} strict_opt If we should remove any non ascii char.
* @return {string} Sanitized string.
*/
GasHelper.prototype._sanitizeString = function (str, strict_opt) {
str = str.toLowerCase()
.replace(/^\ +/, '')
.replace(/\ +$/, '')
.replace(/\s+/g, '_')
.replace(/[áàâãåäæª]/g, 'a')
.replace(/[éèêëЄ€]/g, 'e')
.replace(/[íìîï]/g, 'i')
.replace(/[óòôõöøº]/g, 'o')
.replace(/[úùûü]/g, 'u')
.replace(/[碩]/g, 'c');
if (strict_opt) {
str = str.replace(/[^a-z0-9_\-]/g, '_');
}
return str.replace(/_+/g, '_');
};
/**
* Cross Browser helper to addEventListener.
*
* ga_next.js currently have a _addEventListener directive. So _gas will
* allways prefer that if available, and will use this one only as a fallback
*
* @param {HTMLElement} obj The Element to attach event to.
* @param {string} evt The event that will trigger the binded function.
* @param {function(event)} ofnc The function to bind to the element.
* @param {boolean} bubble true if event should be fired at bubble phase.
* Defaults to false. Works only on W3C compliant browser. MSFT don't support
* it.
* @return {boolean} true if it was successfuly binded.
*/
GasHelper.prototype._addEventListener = function (obj, evt, ofnc, bubble) {
var fnc = function (event) {
if (!event || !event.target) {
event = window.event;
event.target = event.srcElement;
}
return ofnc.call(obj, event);
};
// W3C model
if (obj.addEventListener) {
obj.addEventListener(evt, fnc, !!bubble);
return true;
}
// M$ft model
else if (obj.attachEvent) {
return obj.attachEvent('on' + evt, fnc);
}
// Browser doesn't support W3C or M$ft model. Time to go old school
else {
evt = 'on' + evt;
if (typeof obj[evt] === 'function') {
// Object already has a function on traditional
// Let's wrap it with our own function inside another function
fnc = (function (f1, f2) {
return function () {
f1.apply(this, arguments);
f2.apply(this, arguments);
};
}(obj[evt], fnc));
}
obj[evt] = fnc;
return true;
}
};
/**
* Cross Browser Helper to emulate jQuery.live
*
* Binds to the document root. Listens to all events of the specific type.
* If event don't bubble it won't catch
*/
GasHelper.prototype._liveEvent = function (tag, evt, ofunc) {
var gh = this;
tag = tag.toUpperCase();
tag = tag.split(',');
gh._addEventListener(document, evt, function (me) {
for (var el = me.target; el.nodeName !== 'HTML';
el = el.parentNode)
{
if (gh.inArray(tag, el.nodeName) || el.parentNode === null) {
break;
}
}
if (el && gh.inArray(tag, el.nodeName)) {
ofunc.call(el, me);
}
}, true);
};
/**
* Cross Browser DomReady function.
*
* Inspired by: http://dean.edwards.name/weblog/2006/06/again/#comment367184
*
* @param {function(Event)} callback DOMReady callback.
* @return {boolean} Ignore return value.
*/
GasHelper.prototype._DOMReady = function (callback) {
var scp = this;
function cb() {
if (cb.done) return;
cb.done = true;
callback.apply(scp, arguments);
}
if (/^(interactive|complete)/.test(document.readyState)) return cb();
this._addEventListener(document, 'DOMContentLoaded', cb, false);
this._addEventListener(window, 'load', cb, false);
};
/**
* GAS - Google Analytics on Steroids
*
* Copyright 2011, Cardinal Path and Direct Performance
* Licensed under the MIT license.
*
* @author Eduardo Cereto <eduardocereto@gmail.com>
*/
/*global document:true*/
/**
* Google Analytics original _gaq.
*
* This never tries to do something that is not supposed to. So it won't break
* in the future.
*/
window['_gaq'] = window['_gaq'] || [];
var _prev_gas = window['_gas'] || [];
// Avoid duplicate definition
if (_prev_gas._accounts_length >= 0) {
return;
}
//Shortcuts, these speed up and compress the code
/*jshint -W079*/
var document = window.document,
toString = Object.prototype.toString,
hasOwn = Object.prototype.hasOwnProperty,
push = Array.prototype.push,
slice = Array.prototype.slice,
trim = String.prototype.trim,
sindexOf = String.prototype.indexOf,
url = document.location.href,
documentElement = document.documentElement;
/*jshint +W079*/
/**
* GAS Sigleton
* @constructor
*/
function GAS() {
var self = this;
self['version'] = '1.11.0';
self._accounts = {};
self._accounts_length = 0;
self._queue = _prev_gas;
self._default_tracker = '_gas1';
self.gh = {};
self._hooks = {
'_addHook': [self._addHook]
};
// Need to be pushed to make sure tracker is done
// Sets up helpers, very first thing pushed into gas
self.push(function () {
self.gh = new GasHelper();
});
}
/**
* First standard Hook that is responsible to add next Hooks
*
* _addHook calls always reurn false so they don't get pushed to _gaq
* @param {string} fn The function you wish to add a Hook to.
* @param {function()} cb The callback function to be appended to hooks.
* @return {boolean} Always false.
*/
GAS.prototype._addHook = function (fn, cb) {
if (typeof fn === 'string' && typeof cb === 'function') {
if (typeof _gas._hooks[fn] === 'undefined') {
_gas._hooks[fn] = [];
}
_gas._hooks[fn].push(cb);
}
return false;
};
/**
* Construct the correct account name to be used on _gaq calls.
*
* The account name for the first unamed account pushed to _gas is the standard
* account name. It's pushed without the account name to _gaq, so if someone
* calls directly _gaq it works as expected.
* @param {string} acct Account name.
* @return {string} Correct account name to be used already with trailling dot.
*/
function _build_acct_name(acct) {
return acct === _gas._default_tracker ? '' : acct + '.';
}
function _gaq_push(arr) {
if (_gas.debug_mode) {
try {
console.log(arr);
}catch (e) {}
}
return window['_gaq'].push(arr);
}
/**
* Everything pushed to _gas is executed by this call.
*
* This function should not be called directly. Instead use _gas.push
* @return {number} This is the same return as _gaq.push calls.
*/
GAS.prototype._execute = function () {
var args = slice.call(arguments),
self = this,
sub = args.shift(),
gaq_execute = true,
i, foo, hooks, acct_name, repl_sub, return_val = 0;
if (typeof sub === 'function') {
// Pushed functions are executed right away
return _gaq_push(
(function (s, gh) {
return function () {
// pushed functions receive helpers through this object
s.call(gh);
};
}(sub, self.gh))
);
} else if (typeof sub === 'object' && sub.length > 0) {
foo = sub.shift();
if (sindexOf.call(foo, '.') >= 0) {
acct_name = foo.split('.')[0];
foo = foo.split('.')[1];
} else {
acct_name = undefined;
}
// Execute hooks
hooks = self._hooks[foo];
if (hooks && hooks.length > 0) {
for (i = 0; i < hooks.length; i++) {
try {
repl_sub = hooks[i].apply(self.gh, sub);
if (repl_sub === false) {
// Returning false from a hook cancel the call
gaq_execute = false;
} else {
if (repl_sub && repl_sub.length > 0) {
// Returning an array changes the call parameters
sub = repl_sub;
}
}
} catch (e) {
if (foo !== '_trackException') {
self.push(['_trackException', e]);
}
}
}
}
// Cancel execution on _gaq if any hook returned false
if (gaq_execute === false) {
return 1;
}
// Intercept _setAccount calls
if (foo === '_setAccount') {
for (i in self._accounts) {
if (self._accounts[i] === sub[0]) {
// Repeated account
if (acct_name === undefined) {
return 1;
}
}
}
acct_name = acct_name || '_gas' +
String(self._accounts_length + 1);
// Force that the first unamed account is _gas1
if (typeof self._accounts['_gas1'] === 'undefined' &&
sindexOf.call(acct_name, '_gas') !== -1) {
acct_name = '_gas1';
}
self._accounts[acct_name] = sub[0];
self._accounts_length += 1;
acct_name = _build_acct_name(acct_name);
return_val = _gaq_push([acct_name + foo, sub[0]]);
// Must try t get the tracker if it's a _setAccount
self.gh._setDummyTracker();
return return_val;
}
// Intercept functions that can only be called once.
if (foo === '_link' || foo === '_linkByPost' || foo === '_require' ||
foo === '_anonymizeIp')
{
args = slice.call(sub);
args.unshift(foo);
return _gaq_push(args);
}
// If user provides account than trigger event for just that account.
var acc_foo;
if (acct_name && self._accounts[acct_name]) {
acc_foo = _build_acct_name(acct_name) + foo;
args = slice.call(sub);
args.unshift(acc_foo);
return _gaq_push(args);
}
// Call Original _gaq, for all accounts
if (self._accounts_length > 0) {
for (i in self._accounts) {
if (hasOwn.call(self._accounts, i)) {
acc_foo = _build_acct_name(i) + foo;
args = slice.call(sub);
args.unshift(acc_foo);
return_val += _gaq_push(args);
}
}
} else {
// If there are no accounts we just push it to _gaq
args = slice.call(sub);
args.unshift(foo);
return _gaq_push(args);
}
return return_val ? 1 : 0;
}
};
/**
* Standard method to execute GA commands.
*
* Everything pushed to _gas is in fact pushed back to _gaq. So Helpers are
* ready for hooks. This creates _gaq as a series of functions that call
* _gas._execute() with the same arguments.
*/
GAS.prototype.push = function () {
var self = this;
var args = slice.call(arguments);
for (var i = 0; i < args.length; i++) {
(function (arr, self) {
window['_gaq'].push(function () {
self._execute.call(self, arr);
});
}(args[i], self));
}
};
/**
* _gas main object.
*
* It's supposed to be used just like _gaq but here we extend it. In it's core
* everything pushed to _gas is run through possible hooks and then pushed to
* _gaq
*/
/*global _gas:true*/
window['_gas'] = _gas = new GAS();
/*global _gas:false*/
/**
* Hook for _trackException
*
* Watchout for circular calls
*/
_gas.push(['_addHook', '_trackException', function (exception, message) {
_gas.push(['_trackEvent',
'Exception ' + (exception.name || 'Error'),
message || exception.message || exception,
url
]);
return false;
}]);
/**
* Hook to enable Debug Mode
*/
_gas.push(['_addHook', '_setDebug', function (set_debug) {
_gas.debug_mode = !!set_debug;
}]);
/**
* Hook to Remove other Hooks
*
* It will remove the last inserted hook from a _gas function.
*
* @param {string} func _gas Function Name to remove Hooks from.
* @return {boolean} Always returns false.
*/
_gas.push(['_addHook', '_popHook', function (func) {
var arr = _gas._hooks[func];
if (arr && arr.pop) {
arr.pop();
}
return false;
}]);
/**
* Hook to set the default tracker.
*
* The default tracker is the nameless tracker that is pushed into _gaq_push
*/
_gas.push(['_addHook', '_gasSetDefaultTracker', function (tname) {
_gas._default_tracker = tname;
return false;
}]);
/**
* This is kept just for backward compatibility since it's now supported
* natively in _gaq.
*/
_gas.push(['_addHook', '_trackPageview', function () {
var args = slice.call(arguments);
if (args.length >= 2 &&
typeof args[0] === 'string' && typeof args[1] === 'string')
{
return [{
'page': args[0],
'title': args[1]
}];
}
return args;
}]);
/**
* GAS - Google Analytics on Steroids
*
* Download Tracking Plugin
*
* Copyright 2011, Cardinal Path and Direct Performance
* Licensed under the GPLv3 license.
*
* @author Eduardo Cereto <eduardocereto@gmail.com>
*/
/**
* Extracts the file extension and check it against a list
*
* Will extract the extensions from a url and check if it matches one of
* possible options. Used to verify if a url corresponds to a download link.
*
* @this {GasHelper} GA Helper object.
* @param {string} src The url to check.
* @param {Array} extensions an Array with strings containing the possible
* extensions.
* @return {boolean|string} the file extension or false.
*/
function _checkFile(src, extensions) {
if (typeof src !== 'string') {
return false;
}
var ext = src.split('?')[0];
ext = ext.split('.');
ext = ext[ext.length - 1];
if (ext && this.inArray(extensions, ext)) {
return ext;
}
return false;
}
/**
* Register the event to listen to downloads
*
* @this {GasHelper} GA Helper object.
* @param {Array|object} opts List of possible extensions for download
* links.
*/
var _trackDownloads = function (opts) {
var gh = this;
if (!gh._downloadTracked) {
gh._downloadTracked = true;
} else {
//Oops double tracking detected.
return;
}
if (!opts) {
opts = {'extensions': []};
} else if (typeof opts === 'string') {
// support legacy opts as String of extensions
opts = {'extensions': opts.split(',')};
} else if (opts.length >= 1) {
// support legacy opts Array of extensions
opts = {'extensions': opts};
}
opts['category'] = opts['category'] || 'Download';
var ext = 'xls,xlsx,doc,docx,ppt,pptx,pdf,txt,zip';
ext += ',rar,7z,exe,wma,mov,avi,wmv,mp3,csv,tsv';
ext = ext.split(',');
opts['extensions'] = opts['extensions'].concat(ext);
gh._liveEvent('a', 'mousedown', function (e) {
var el = this;
if (el.href) {
var ext = _checkFile.call(gh,
el.href, opts['extensions']
);
if (ext) {
_gas.push(['_trackEvent',
opts['category'], ext, el.href
]);
}
}
});
return false;
};
/**
* GAA Hook, receive the extensions to extend default extensions. And trigger
* the binding of the events.
*
* @param {string|Array|object} opts GAs Options. Also backward compatible
* with array or string of extensions.
*/
_gas.push(['_addHook', '_gasTrackDownloads', _trackDownloads]);
// Old API to be deprecated on v2.0
_gas.push(['_addHook', '_trackDownloads', _trackDownloads]);
/**
* GAS - Google Analytics on Steroids
*
* Ecommerce Meta
*
* Copyright 2012, Cardinal Path and Direct Performance
* Licensed under the GPLv3 license.
*
* @author Eduardo Cereto <eduardocereto@gmail.com>
*/
function _gasMetaEcommerce() {
var i, meta,
f_trans = 0,
f_item = 0,
metas = document.getElementsByTagName('meta');
for (i = 0; i < metas.length; i++) {
if (metas[i].name === 'ga_trans') {
// Fire transaction
meta = metas[i].content.split('^');
if (meta.length < 3) {
// 3 is the minimum for transaction
break;
}
// Add default values for remaining params
while (meta.length < 8) {
meta.push('');
}
_gas.push(['_addTrans',
meta[0],
meta[1],
meta[2],
meta[3],
meta[4],
meta[5],
meta[6],
meta[7]
]);
f_trans++;
}
else if (metas[i].name === 'ga_item') {
// Fire item
meta = metas[i].content.split('^');
if (meta.length === 6) {
_gas.push(['_addItem',
meta[0],
meta[1],
meta[2],
meta[3],
meta[4],
meta[5]
]);
f_item++;
}
}
}
if (f_trans > 0 && f_item > 0) {
_gas.push(['_trackTrans']);
//_gas.push(['_clearTrans']);
}
}
_gas.push(['_addHook', '_gasMetaEcommerce', _gasMetaEcommerce]);
/**
* Hook to sanity check trackEvents
*
* The value is rounded and parsed to integer.
* Negative values are sent as zero.
* If val is NaN than it is sent as zero.
*/
_gas.push(['_addHook', '_trackEvent', function () {
var args = slice.call(arguments);
if (args[3]) {
args[3] = (args[3] < 0 ? 0 : Math.round(args[3])) || 0;
}
return args;
}]);
/**
* GAS - Google Analytics on Steroids
*
* Form Tracking Plugin
*
* Copyright 2011, Cardinal Path and Direct Performance
* Licensed under the GPLv3 license.
*
* @author Eduardo Cereto <eduardocereto@gmail.com>
*/
/**
* get the form name for a specific elemet
*
* @param {DOMElemet} el Dom Element.
* @return {String} Form Name or Id.
*/
function getFormName(el) {
while (el && el.nodeName !== 'HTML') {
if (el.nodeName === 'FORM') {break; }
el = el.parentNode;
}
if (el.nodeName === 'FORM') {
return el.name || el.id || 'none';
}
return 'none';
}
var _gasTrackForms = function (opts) {
if (!this._formTracked) {
this._formTracked = true;
} else {
//Oops double tracking detected.
return;
}
var scp = this;
if (typeof opts !== 'object') {
opts = {};
}
// Make sure required attrs are defined or fallback to default
opts['category'] = opts['category'] || 'Form Tracking';
//opts['live'] = opts['live'] || true; //Ignored
var trackField = function (e) {
var el = e.target,
el_name = el.name || el.id || el.type || el.nodeName,
form_name = getFormName(el),
action = 'form (' + form_name + ')',
label = el_name + ' (' + e.type + ')';
_gas.push(['_trackEvent', opts['category'], action, label]);
};
scp._DOMReady(function () {
var changeTags = ['input', 'select', 'textarea', 'hidden'];
var submitTags = ['form'];
var elements = [];
var i, j;
for (i = 0; i < changeTags.length; i++) {
elements = document.getElementsByTagName(changeTags[i]);
for (j = 0; j < elements.length; j++) {
scp._addEventListener(elements[j], 'change', trackField);
}
}
for (i = 0; i < submitTags.length; i++) {
elements = document.getElementsByTagName(submitTags[i]);
for (j = 0; j < elements.length; j++) {
scp._addEventListener(elements[j], 'submit', trackField);
}
}
});
};
_gas.push(['_addHook', '_gasTrackForms', _gasTrackForms]);
// Old API to be deprecated on v2.0
_gas.push(['_addHook', '_trackForms', _gasTrackForms]);
/**
* GAS - Google Analytics on Steroids
*
* HTML5 Video Tracking Plugin
*
* Copyright 2011, Cardinal Path
* Licensed under the GPLv3 license.
*
* @author Eduardo Cereto <eduardocereto@gmail.com>
*/
/**
* Triggers the actual video/audio GA events
*
* To be used as a callback for the HTML5 media events
*
* @param {Event} e A reference to the HTML event fired.
* @this {HTMLMediaElement} The HTML element firing the event
*/
function _trackMediaElement(e) {
_gas.push(['_trackEvent', this.tagName, e.type, this.currentSrc]);
}
/**
* Triggers the HTML5 Video Tracking on the page
* @param {String} tag Either 'audio' or 'video'.
* @this {GasHelper} GA Helper object.
*/
var _trackMedia = function (tag) {
var self = this;
self._liveEvent(tag, 'play', _trackMediaElement);
self._liveEvent(tag, 'pause', _trackMediaElement);
self._liveEvent(tag, 'ended', _trackMediaElement);
};
var _trackVideo = function () {
if (!this._videoTracked) {
this._videoTracked = true;
} else {
//Oops double tracking detected.
return;
}
_trackMedia.call(this, 'video');
};
var _trackAudio = function () {
if (!this._audioTracked) {
this._audioTracked = true;
} else {
//Oops double tracking detected.
return;
}
_trackMedia.call(this, 'audio');
};
_gas.push(['_addHook', '_gasTrackVideo', _trackVideo]);
_gas.push(['_addHook', '_gasTrackAudio', _trackAudio]);
// Old API to be deprecated on v2.0
_gas.push(['_addHook', '_trackVideo', _trackVideo]);
_gas.push(['_addHook', '_trackAudio', _trackAudio]);
/**
* GAS - Google Analytics on Steroids
*
* HTML Markup Plugin
*
* Copyright 2012, Cardinal Path and Direct Performance
* Licensed under the GPLv3 license.
*
* @author Eduardo Cereto <eduardocereto@gmail.com>
*/
/**
* Sets Default pagename and Custom Vars based on Meta
*
* If a meta name='ga_vpv' is availalbe on the page use that as a page
* replacement if the pageview is not passed as parameter.
*
* If meta name="ga_custom_var" the 4 values for a custom var must be on
* content separated by a caret (^).
*/
function _gasMeta() {
var i, meta,
metas = document.getElementsByTagName('meta');
for (i = 0; i < metas.length; i++) {
if (metas[i].name === 'ga_vpv') {
meta = metas[i].content;
(function (vpv) {
window._gas.push(['_addHook', '_trackPageview', function (p) {
if (p === undefined) {
return [vpv];
}
}]);
}(meta));
} else if (metas[i].name === 'ga_custom_var') {
meta = metas[i].content.split('^');
if (meta.length === 4) {
window._gas.push(['_setCustomVar',
parseInt(meta[0], 10),
meta[1],
meta[2],
parseInt(meta[3], 10)
]);
}
}
}
}
/**
* Listens to all clicks and looks for a tagged element on it.
*
* Events have the following params:
* x-ga-event-category (required) – The category of the event specified in
* the solution design document
* x-ga-event-action (required) – The action of the event specified in the
* solution design document
* x-ga-event-label (optional) – The label of the event specified in the
* solution design document. If no label is specified in the solution design
* document, this attribute can be omitted
* x-ga-event-value (optional) – The value (integer) of the event specified
* in the solution design document. If no value is specified in the solution
* design document, this attribute can be omitted
* x-ga-event-noninteractive (optional) – Boolean (true/false) value
* specified in the solution design document. If the non-interactive value is
* not specified, this attribute can be omitted
*
* Social Actions have the following params:
* x-ga-social-network (required) – The network of the social interaction
* specified in the solution design document
* x-ga-social-action (required) – The action of the social interaction
* specified in the solution design document
* x-ga-social-target (optional) – The target of the social interaction
* specified in the solution design document. If no target is specified, this
* attribute can be omitted
* x-ga-social-pagepath (optional) – The page path of the social interaction
* specified in the solution design document. If no page path is specified,
* this attribute can be omitted
*/
function _gasHTMLMarkup() {
var gh = this;
gh._addEventListener(document, 'mousedown', function (me) {
var el;
for (el = me.target; el.nodeName !== 'HTML';
el = el.parentNode) {
if (el.getAttribute('x-ga-event-category')) {
// Event element clicked, fire the _trackEvent
window._gas.push(['_trackEvent',
el.getAttribute('x-ga-event-category'),
el.getAttribute('x-ga-event-action'),
el.getAttribute('x-ga-event-label') || undefined,
parseInt(el.getAttribute('x-ga-event-value'), 10) || 0,
el.getAttribute('x-ga-event-noninteractive') === 'true' ? true : false
]);
}
if (el.getAttribute('x-ga-social-network')) {
// Social Action Clicked fire _trackSocial
window._gas.push(['_trackSocial',
el.getAttribute('x-ga-social-network'),
el.getAttribute('x-ga-social-action'),
el.getAttribute('x-ga-social-target') || undefined,
el.getAttribute('x-ga-social-pagepath') || undefined
]);
}
if (el.parentNode === null) {
break;
}
}
}, true);
}
_gas.push(['_addHook', '_gasMeta', _gasMeta]);
_gas.push(['_addHook', '_gasHTMLMarkup', _gasHTMLMarkup]);
/**
* GAS - Google Analytics on Steroids
*
* MailTo tracking plugin
*
* Copyright 2011, Cardinal Path and Direct Performance
* Licensed under the GPLv3 license.
*/
/**
* GAS plugin to track mailto: links
*
* @param {object} opts GAS Options.
*/
var _gasTrackMailto = function (opts) {
if (!this._mailtoTracked) {
this._mailtoTracked = true;
} else {
//Oops double tracking detected.
return;
}
if (!opts) {
opts = {};
}
opts['category'] = opts['category'] || 'Mailto';
this._liveEvent('a', 'mousedown', function (e) {
var el = e.target;
if (el && el.href && el.href.toLowerCase &&
sindexOf.call(el.href.toLowerCase(), 'mailto:') === 0) {
_gas.push(['_trackEvent', opts['category'], el.href.substr(7)]);
}
});
return false;
};
_gas.push(['_addHook', '_gasTrackMailto', _gasTrackMailto]);
// Old API to be deprecated on v2.0
_gas.push(['_addHook', '_trackMailto', _gasTrackMailto]);
/**
* GAS - Google Analytics on Steroids
*
* Max-Scroll Tracking Plugin
*
* Copyright 2011, Cardinal Path and Direct Performance
* Licensed under the GPLv3 license.
*
* @author Eduardo Cereto <eduardocereto@gmail.com>
*/
var _maxScrollOpts;
/**
* Get current browser viewpane heigtht
*
* @return {number} height.
*/
function _get_window_height() {
return window.innerHeight || documentElement.clientHeight ||
document.body.clientHeight || 0;
}
/**
* Get current absolute window scroll position
*
* @return {number} YScroll.
*/
function _get_window_Yscroll() {
return window.pageYOffset || document.body.scrollTop ||
documentElement.scrollTop || 0;
}
/**
* Get current absolute document height
*
* @return {number} Current document height.
*/
function _get_doc_height() {
return Math.max(
document.body.scrollHeight || 0, documentElement.scrollHeight || 0,
document.body.offsetHeight || 0, documentElement.offsetHeight || 0,
document.body.clientHeight || 0, documentElement.clientHeight || 0
);
}
/**
* Get current vertical scroll percentage
*
* @return {number} Current vertical scroll percentage.
*/
function _get_scroll_percentage() {
return (
(_get_window_Yscroll() + _get_window_height()) / _get_doc_height()
) * 100;
}
var _t = null;
var _max_scroll = 0;
function _update_scroll_percentage(now) {
if (_t) {
clearTimeout(_t);
}
if (now === true) {
_max_scroll = Math.max(_get_scroll_percentage(), _max_scroll);
return;
}
_t = setTimeout(function () {
_max_scroll = Math.max(_get_scroll_percentage(), _max_scroll);
}, 400);
}
function _sendMaxScroll() {
_update_scroll_percentage(true);
_max_scroll = Math.floor(_max_scroll);
if (_max_scroll <= 0 || _max_scroll > 100) return;
var bucket = (_max_scroll > 10 ? 1 : 0) * (
Math.floor((_max_scroll - 1) / 10) * 10 + 1
);
bucket = String(bucket) + '-' +
String(Math.ceil(_max_scroll / 10) * 10);
_gas.push(['_trackEvent',
_maxScrollOpts['category'],
url,
bucket,
Math.floor(_max_scroll),
true // non-interactive
]);
}
/**
* Tracks the max Scroll on the page.
*
* @param {object} opts GAS Options to be used.
* @this {GasHelper} The Ga Helper object
*/
function _trackMaxScroll(opts) {
if (!this._maxScrollTracked) {
this._maxScrollTracked = true;
} else {
//Oops double tracking detected.
return;
}
_maxScrollOpts = opts || {};
_maxScrollOpts['category'] = _maxScrollOpts['category'] || 'Max Scroll';
this._addEventListener(window, 'scroll', _update_scroll_percentage);
this._addEventListener(window, 'beforeunload', _sendMaxScroll);
}
_gas.push(['_addHook', '_gasTrackMaxScroll', _trackMaxScroll]);
// Old API to be deprecated on v2.0
_gas.push(['_addHook', '_trackMaxScroll', _trackMaxScroll]);
/**
* GAS - Google Analytics on Steroids
*
* Multi-Domain Tracking Plugin
*
* Copyright 2011, Cardinal Path and Direct Performance
* Licensed under the GPLv3 license.
*
* @author Eduardo Cereto <eduardocereto@gmail.com>
*/
/**
* Private variable to store allowAnchor choice
*/
_gas._allowAnchor = false;
/**
* _setAllowAnchor Hook to store choice for easier use of Anchor
*
* This stored value is used on _getLinkerUrl, _link and _linkByPost so it's
* used the same by default
*/
_gas.push(['_addHook', '_setAllowAnchor', function (val) {
_gas._allowAnchor = !!val;
}]);
/**
* _link Hook to use stored allowAnchor value.
*/
_gas.push(['_addHook', '_link', function (url, use_anchor) {
if (use_anchor === undefined) {
use_anchor = _gas._allowAnchor;
}
return [url, use_anchor];
}]);
/**
* _linkByPost Hook to use stored allowAnchor value.
*/
_gas.push(['_addHook', '_linkByPost', function (url, use_anchor) {
if (use_anchor === undefined) {
use_anchor = _gas._allowAnchor;
}
return [url, use_anchor];
}]);
/**
* Store all domains pushed by _setDomainName that don't match current domain.
*
* @type {Array.<string>}
*/
var _external_domains = [];
/**
* Store the internal domain name
*
* @type string
*/
var _internal_domain;
/**
* _setDomainName Hook to add pushed domains to _external_domains if it doesn't
* match current domain.
*
* This Hook let you call _setDomainName multiple times. So _gas will only
* apply the one that matches the current domain and the other ones will be
* used to track external domains with cookie data.
*/
_gas.push(['_addHook', '_setDomainName', function (domainName) {
if (sindexOf.call('.' + document.location.hostname, domainName) < 0) {
_external_domains.push(domainName);
return false;
}
_internal_domain = domainName;
}]);
/**
* _addExternalDomainName Hook.
*
* This hook let you add external domains so that urls on current page to this
* domain are marked to send cookies.
* You should use _setDomainName for this in most of the cases.
*/
_gas.push(['_addHook', '_addExternalDomainName', function (domainName) {
_external_domains.push(domainName);
return false;
}]);
/**
* Function to mark links on the current pages to send links
*
* This function is used to make it easy to implement multi-domain-tracking.
* @param {string} event_used Should be 'now', 'click' or 'mousedown'. Default
* 'click'.
* @this {GasHelper} GAS Helper functions
* @return {boolean} Returns false to avoid this is puhed to _gaq.
*/
function track_links(event_used) {
if (!this._multidomainTracked) {
this._multidomainTracked = true;
} else {
//Oops double tracking detected.
return;
}
var internal = document.location.hostname,
gh = this,
i, j, el,
links = document.getElementsByTagName('a');
if (event_used !== 'now' && event_used !== 'mousedown') {
event_used = 'click';
}
for (i = 0; i < links.length; i++) {
el = links[i];
if (sindexOf.call(el.href, 'http') === 0) {
// Check to see if it's a internal link
if (el.hostname === internal ||
sindexOf.call(el.hostname, _internal_domain) >= 0) {
continue;
}
// Tag external Links either now or on mouse event.
for (j = 0; j < _external_domains.length; j++) {
if (sindexOf.call(el.hostname, _external_domains[j]) >= 0) {
if (event_used === 'now') {
el.href = gh['tracker']['_getLinkerUrl'](
el.href,
_gas._allowAnchor
);
} else {
if (event_used === 'click') {
this._addEventListener(el, event_used, function (e) {
if (this.target && this.target === '_blank') {
window.open(
gh['tracker']['_getLinkerUrl'](
this.href, _gas._allowAnchor
)
);
} else {
_gas.push(
['_link', this.href, _gas._allowAnchor]
);
}
if (e.preventDefault)
e.preventDefault();
else
e.returnValue = false;
return false; //needed for ie7
});
} else {
this._addEventListener(el, event_used, function () {
this.href = gh['tracker']['_getLinkerUrl'](
this.href,
_gas._allowAnchor
);
});
}
}
}
}
}
}
return false;
}
var _gasMultiDomain = function () {
var gh = this;
var args = slice.call(arguments);
if (gh && gh._DOMReady) {
gh._DOMReady(function () {
track_links.apply(gh, args);
});
}
};
/**
* Registers Hook to _setMultiDomain
*/
_gas.push(['_addHook', '_gasMultiDomain', _gasMultiDomain]);
// Old API to be deprecated on v2.0
_gas.push(['_addHook', '_setMultiDomain', _gasMultiDomain]);
/**
* GAS - Google Analytics on Steroids
*
* Outbound Link Tracking Plugin
*
* Copyright 2011, Cardinal Path and Direct Performance
* Licensed under the GPLv3 license.
*
* @author Eduardo Cereto <eduardocereto@gmail.com>
*/
/**
* Triggers the Outbound Link Tracking on the page
*
* @this {object} GA Helper object.
* @param {object} opts Custom options for Outbound Links.
*/
var _gasTrackOutboundLinks = function (opts) {
if (!this._outboundTracked) {
this._outboundTracked = true;
} else {
//Oops double tracking detected.
return;
}
var gh = this;
if (!opts) {
opts = {};
}
opts['category'] = opts['category'] || 'Outbound';
gh._liveEvent('a', 'mousedown', function (e) {
var l = this;
if (
(l.protocol === 'http:' || l.protocol === 'https:') &&
sindexOf.call(l.hostname, document.location.hostname) === -1)
{
var path = (l.pathname + l.search + ''),
utm = sindexOf.call(path, '__utm');
if (utm !== -1) {
path = path.substring(0, utm);
}
_gas.push(['_trackEvent',
opts['category'],
l.hostname,
path
]);
}
});
};
_gas.push(['_addHook', '_gasTrackOutboundLinks', _gasTrackOutboundLinks]);
// Old API to be deprecated on v2.0
_gas.push(['_addHook', '_trackOutboundLinks', _gasTrackOutboundLinks]);
/**
* GAS - Google Analytics on Steroids
*
* Vimeo Video Tracking Plugin
*
* Copyright 2011, Cardinal Path and Direct Performance
* Licensed under the GPLv3 license.
*
* @author Eduardo Cereto <eduardocereto@gmail.com>
*/
var _vimeoTimeTriggers = [];
var _vimeoPoolMaps = {};
/**
* Cached urls for vimeo players on the page.
*
* @type {object}
*/
var _vimeo_urls = {};
function _vimeoPool(data) {
if (!_vimeoPoolMaps[data.player_id]) {
_vimeoPoolMaps[data.player_id] = {};
_vimeoPoolMaps[data.player_id].timeTriggers = slice.call(
_vimeoTimeTriggers
);
}
if (_vimeoPoolMaps[data.player_id].timeTriggers.length > 0) {
if (data.data.percent * 100 >=
_vimeoPoolMaps[data.player_id].timeTriggers[0])
{
var action = _vimeoPoolMaps[data.player_id].timeTriggers.shift();
_gas.push([
'_trackEvent',
'Vimeo Video',
action + '%',
_vimeo_urls[data.player_id]
]);
}
}
}
/**
* Helper function to post messages to a vimeo player
*
* @param {string} method The method from the vimeo API.
* @param {string} params to be passed as the value of the method.
* @param {object} target Iframe DOM Element for the Vimeo player.
* @return {boolean} true if it worked or false otherwise.
*/
function _vimeoPostMessage(method, params, target) {
if (!target.contentWindow || !target.contentWindow.postMessage || !JSON) {
return false;
}
var url = target.getAttribute('src').split('?')[0],
data = JSON.stringify({
method: method,
value: params
});
target.contentWindow.postMessage(data, url);
return true;
}
/**
* Flag that indicates if the global listener has been bind to the window
* @type {boolean}
*/
var _has_vimeo_window_event = false;
var _vimeoOpts;
/**
* postMessage Listener
* @param {Object} event The Vimeo API return event.
*/
/*jshint latedef:false*/
function _vimeoPostMessageListener(event) {
if (sindexOf.call(event.origin, '//player.vimeo.com') > -1) {
var data = JSON.parse(event.data);
if (data.event === 'ready') {
_trackVimeo.call(_gas.gh); // Force rerun since a player is ready
} else if (data.method) {
if (data.method === 'getVideoUrl') {
_vimeo_urls[data.player_id] = data.value;
}
} else if (data.event === 'playProgress') {
_vimeoPool(data);
} else {
_gas.push(['_trackEvent', _vimeoOpts['category'],
data.event, _vimeo_urls[data.player_id]]);
}
}
}
/*jshint latedef:true*/
/**
* Triggers the Vimeo Tracking on the page
*
* Only works for the Universal Tag from Vimeo (iframe). The video must have
* the parameter api=1 on the url in order to make the tracking work.
*
* @this {GasHelper} GA Helper object.
*/
function _trackVimeo() {
var iframes = document.getElementsByTagName('iframe');
var vimeo_videos = 0;
var player_id;
var player_src;
var separator;
var force = _vimeoOpts['force'];
var partials = _vimeoOpts['percentages'];
for (var i = 0; i < iframes.length; i++) {
if (sindexOf.call(iframes[i].src, '//player.vimeo.com') > -1) {
player_id = 'gas_vimeo_' + i;
player_src = iframes[i].src;
separator = '?';
if (sindexOf.call(player_src, '?') > -1) {
separator = '&';
}
if (sindexOf.call(player_src, 'api=1') < 0) {
if (force) {
// Reload the video enabling the api
player_src += separator + 'api=1&player_id=' + player_id;
} else {
// We won't track players that don't have api enabled.
continue;
}
} else {
if (sindexOf.call(player_src, 'player_id=') < -1) {
player_src += separator + 'player_id=' + player_id;
}
}
vimeo_videos++;
iframes[i].id = player_id;
if (iframes[i].src !== player_src) {
iframes[i].src = player_src;
break; // break to wait until it is ready since we reloaded it.
}
// We need to cache the video url since vimeo won't provide it
// in the event
_vimeoPostMessage('getVideoUrl', '', iframes[i]);
_vimeoPostMessage('addEventListener', 'play', iframes[i]);
_vimeoPostMessage('addEventListener', 'pause', iframes[i]);
_vimeoPostMessage('addEventListener', 'finish', iframes[i]);
if (partials) {
_vimeoTimeTriggers = partials;
_vimeoPostMessage('addEventListener', 'playProgress',
iframes[i]);
}
}
}
if (vimeo_videos > 0 && _has_vimeo_window_event === false) {
this._addEventListener(window, 'message',
_vimeoPostMessageListener, false
);
_has_vimeo_window_event = true;
}
}
var _gasTrackVimeo = function (opts) {
var gh = this;
// Support
if (typeof opts === 'boolean' || opts === 'force') {
opts = {'force': !!opts};
}
opts = opts || {};
opts['category'] = opts['category'] || 'Vimeo Video';
opts['percentages'] = opts['percentages'] || [];
opts['force'] = opts['force'] || false;
_vimeoOpts = opts;
gh._DOMReady(function () {
_trackVimeo.call(gh);
});
return false;
};
_gas.push(['_addHook', '_gasTrackVimeo', _gasTrackVimeo]);
// Old API to be deprecated on v2.0
_gas.push(['_addHook', '_trackVimeo', _gasTrackVimeo]);
/**
* GAS - Google Analytics on Steroids
*
* YouTube Video Tracking Plugin
*
* Copyright 2011, Cardinal Path and Direct Performance
* Licensed under the GPLv3 license.
*
* @author Eduardo Cereto <eduardocereto@gmail.com>
*/
/**
* Array of percentage to fire events.
*/
var _ytTimeTriggers = [];
var _ytOpts;
/**
* Used to map each vid to a set of timeTriggers and it's pool timer
*/
var _ytPoolMaps = {};
function _ytPool(target, hash) {
if (_ytPoolMaps[hash] === undefined ||
_ytPoolMaps[hash].timeTriggers.length <= 0) {
return false;
}
var p = target['getCurrentTime']() / target['getDuration']() * 100;
if (p >= _ytPoolMaps[hash].timeTriggers[0]) {
var action = _ytPoolMaps[hash].timeTriggers.shift();
_gas.push([
'_trackEvent',
_ytOpts['category'],
action + '%',
target['getVideoUrl']()
]);
}
_ytPoolMaps[hash].timer = setTimeout(_ytPool, 1000, target, hash);
}
function _ytStopPool(target) {
var h = target['getVideoUrl']();
if (_ytPoolMaps[h] && _ytPoolMaps[h].timer) {
_ytPool(target, h); // Pool one last time before clearing it.
clearTimeout(_ytPoolMaps[h].timer);
}
}
function _ytStartPool(target) {
if (_ytTimeTriggers && _ytTimeTriggers.length) {
var h = target['getVideoUrl']();
if (_ytPoolMaps[h]) {
_ytStopPool(target);
} else {
_ytPoolMaps[h] = {};
_ytPoolMaps[h].timeTriggers = slice.call(_ytTimeTriggers);
}
_ytPoolMaps[h].timer = setTimeout(_ytPool, 1000, target, h);
}
}
/**
* Called when the Video State changes
*
* We are currently tracking only finish, play and pause events
*
* @param {Object} event the event passed by the YT api.
*/
function _ytStateChange(event) {
var action = '';
switch (event['data']) {
case 0:
action = 'finish';
_ytStopPool(event['target']);
break;
case 1:
action = 'play';
_ytStartPool(event['target']);
break;
case 2:
action = 'pause';
_ytStopPool(event['target']);
break;
}
if (action) {
_gas.push(['_trackEvent',
_ytOpts['category'], action, event['target']['getVideoUrl']()
]);
}
}
/**
* Called when the player fires an Error Event
*
* @param {Object} event the event passed by the YT api.
*/
function _ytError(event) {
_gas.push(['_trackEvent',
_ytOpts['category'],
'error (' + event['data'] + ')',
event['target']['getVideoUrl']()
]);
}
/**
* Looks for object/embed youtube videos and migrate them to the iframe method
* so it tries to track them
*/
function _ytMigrateObjectEmbed() {
var objs = document.getElementsByTagName('object');
var pars, ifr, ytid;
var r = /(https?:\/\/www\.youtube(-nocookie)?\.com[^\/]*).*\/v\/([^&?]+)/;
for (var i = 0; i < objs.length; i++) {
pars = objs[i].getElementsByTagName('param');
for (var j = 0; j < pars.length; j++) {
if (pars[j].name === 'movie' && pars[j].value) {
// Replace the object with an iframe
ytid = pars[j].value.match(r);
if (ytid && ytid[1] && ytid[3]) {
ifr = document.createElement('iframe');
ifr.src = ytid[1] + '/embed/' + ytid[3] + '?enablejsapi=1';
ifr.width = objs[i].width;
ifr.height = objs[i].height;
ifr.setAttribute('frameBorder', '0');
ifr.setAttribute('allowfullscreen', '');
objs[i].parentNode.insertBefore(ifr, objs[i]);
objs[i].parentNode.removeChild(objs[i]);
// Since we removed the object the Array changed
i--;
}
break;
}
}
}
}
/**
* Triggers the YouTube Tracking on the page
*
* Only works for the iframe tag. The video must have the parameter
* enablejsapi=1 on the url in order to make the tracking work.
*
* @param {(object)} opts GAS Options object.
*/
function _trackYoutube(opts) {
var force = opts['force'];
var opt_timeTriggers = opts['percentages'];
if (force) {
try {
_ytMigrateObjectEmbed();
}catch (e) {
_gas.push(['_trackException', e,
'GAS Error on youtube.js:_ytMigrateObjectEmbed'
]);
}
}
var youtube_videos = [];
var iframes = document.getElementsByTagName('iframe');
for (var i = 0; i < iframes.length; i++) {
if (sindexOf.call(iframes[i].src, '//www.youtube.com/embed') > -1) {
if (sindexOf.call(iframes[i].src, 'enablejsapi=1') < 0) {
if (force) {
// Reload the video enabling the api
if (sindexOf.call(iframes[i].src, '?') < 0) {
iframes[i].src += '?enablejsapi=1';
} else {
iframes[i].src += '&enablejsapi=1';
}
} else {
// We can't track players that don't have api enabled.
continue;
}
}
youtube_videos.push(iframes[i]);
}
}
if (youtube_videos.length > 0) {
if (opt_timeTriggers && opt_timeTriggers.length) {
_ytTimeTriggers = opt_timeTriggers;
}
// this function will be called when the youtube api loads
window['onYouTubePlayerAPIReady'] = function () {
var p;
for (var i = 0; i < youtube_videos.length; i++) {
p = new window['YT']['Player'](youtube_videos[i]);
p.addEventListener('onStateChange', _ytStateChange);
p.addEventListener('onError', _ytError);
}
};
// load the youtube player api
var tag = document.createElement('script');
//XXX use document.location.protocol
var protocol = 'http:';
if (document.location.protocol === 'https:') {
protocol = 'https:';
}
tag.src = protocol + '//www.youtube.com/player_api';
tag.type = 'text/javascript';
tag.async = true;
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
}
}
var _gasTrackYoutube = function (opts) {
// Support for legacy parameters
var args = slice.call(arguments);
if (args[0] && (typeof args[0] === 'boolean' || args[0] === 'force')) {
opts = {'force': !!args[0]};
if (args[1] && args[1].length) {
opts['percentages'] = args[1];
}
}
opts = opts || {};
opts['force'] = opts['force'] || false;
opts['category'] = opts['category'] || 'YouTube Video';
opts['percentages'] = opts['percentages'] || [];
_ytOpts = opts;
var gh = this;
gh._DOMReady(function () {
_trackYoutube.call(gh, opts);
});
return false;
};
_gas.push(['_addHook', '_gasTrackYoutube', _gasTrackYoutube]);
// Old API to be deprecated on v2.0
_gas.push(['_addHook', '_trackYoutube', _gasTrackYoutube]);
/**
* Wrap-up
*/
// Execute previous functions
while (_gas._queue.length > 0) {
_gas.push(_gas._queue.shift());
}
// Import ga.js
if (typeof window._gat === 'undefined') {
(function () {
var gasScript = document.getElementByID('gas-script');
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.async = true;
if (gasScript !== null && gasScript.getAttribute('data-use-dcjs') === 'true') {
ga.src = (
'https:' === document.location.protocol ?
'https://' : 'http://') +
'stats.g.doubleclick.net/dc.js';
} else {
ga.src = (
'https:' === document.location.protocol ?
'https://ssl' : 'http://www') +
'.google-analytics.com/ga.js';
}
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
}());
}
})(window);
/**
* @preserve Copyright 2011, Cardinal Path and DigitalInc.
*
* GAS - Google Analytics on Steroids
* https://github.com/CardinalPath/gas
*
* @author Eduardo Cereto <eduardocereto@gmail.com>
* Licensed under the GPLv3 license.
*/
!function(a,b){function c(){var a=this;a.version="1.11.0",a._accounts={},a._accounts_length=0,a._queue=F,a._default_tracker="_gas1",a.gh={},a._hooks={_addHook:[a._addHook]},a.push(function(){a.gh=new E})}function d(a){return a===_gas._default_tracker?"":a+"."}function e(b){if(_gas.debug_mode)try{console.log(b)}catch(c){}return a._gaq.push(b)}function f(a,b){if("string"!=typeof a)return!1;var c=a.split("?")[0];return c=c.split("."),c=c[c.length-1],c&&this.inArray(b,c)?c:!1}function g(){var a,b,c=0,d=0,e=G.getElementsByTagName("meta");for(a=0;a<e.length;a++)if("ga_trans"===e[a].name){if(b=e[a].content.split("^"),b.length<3)break;for(;b.length<8;)b.push("");_gas.push(["_addTrans",b[0],b[1],b[2],b[3],b[4],b[5],b[6],b[7]]),c++}else"ga_item"===e[a].name&&(b=e[a].content.split("^"),6===b.length&&(_gas.push(["_addItem",b[0],b[1],b[2],b[3],b[4],b[5]]),d++));c>0&&d>0&&_gas.push(["_trackTrans"])}function h(a){for(;a&&"HTML"!==a.nodeName&&"FORM"!==a.nodeName;)a=a.parentNode;return"FORM"===a.nodeName?a.name||a.id||"none":"none"}function i(a){_gas.push(["_trackEvent",this.tagName,a.type,this.currentSrc])}function j(){var c,d,e=G.getElementsByTagName("meta");for(c=0;c<e.length;c++)"ga_vpv"===e[c].name?(d=e[c].content,function(c){a._gas.push(["_addHook","_trackPageview",function(a){return a===b?[c]:void 0}])}(d)):"ga_custom_var"===e[c].name&&(d=e[c].content.split("^"),4===d.length&&a._gas.push(["_setCustomVar",parseInt(d[0],10),d[1],d[2],parseInt(d[3],10)]))}function k(){var c=this;c._addEventListener(G,"mousedown",function(c){var d;for(d=c.target;"HTML"!==d.nodeName&&(d.getAttribute("x-ga-event-category")&&a._gas.push(["_trackEvent",d.getAttribute("x-ga-event-category"),d.getAttribute("x-ga-event-action"),d.getAttribute("x-ga-event-label")||b,parseInt(d.getAttribute("x-ga-event-value"),10)||0,"true"===d.getAttribute("x-ga-event-noninteractive")?!0:!1]),d.getAttribute("x-ga-social-network")&&a._gas.push(["_trackSocial",d.getAttribute("x-ga-social-network"),d.getAttribute("x-ga-social-action"),d.getAttribute("x-ga-social-target")||b,d.getAttribute("x-ga-social-pagepath")||b]),null!==d.parentNode);d=d.parentNode);},!0)}function l(){return a.innerHeight||L.clientHeight||G.body.clientHeight||0}function m(){return a.pageYOffset||G.body.scrollTop||L.scrollTop||0}function n(){return Math.max(G.body.scrollHeight||0,L.scrollHeight||0,G.body.offsetHeight||0,L.offsetHeight||0,G.body.clientHeight||0,L.clientHeight||0)}function o(){return 100*((m()+l())/n())}function p(a){return T&&clearTimeout(T),a===!0?(U=Math.max(o(),U),void 0):(T=setTimeout(function(){U=Math.max(o(),U)},400),void 0)}function q(){if(p(!0),U=Math.floor(U),!(0>=U||U>100)){var a=(U>10?1:0)*(10*Math.floor((U-1)/10)+1);a=String(a)+"-"+String(10*Math.ceil(U/10)),_gas.push(["_trackEvent",S.category,K,a,Math.floor(U),!0])}}function r(b){this._maxScrollTracked||(this._maxScrollTracked=!0,S=b||{},S.category=S.category||"Max Scroll",this._addEventListener(a,"scroll",p),this._addEventListener(a,"beforeunload",q))}function s(b){if(!this._multidomainTracked){this._multidomainTracked=!0;var c,d,e,f=G.location.hostname,g=this,h=G.getElementsByTagName("a");for("now"!==b&&"mousedown"!==b&&(b="click"),c=0;c<h.length;c++)if(e=h[c],0===J.call(e.href,"http")){if(e.hostname===f||J.call(e.hostname,V)>=0)continue;for(d=0;d<W.length;d++)J.call(e.hostname,W[d])>=0&&("now"===b?e.href=g.tracker._getLinkerUrl(e.href,_gas._allowAnchor):"click"===b?this._addEventListener(e,b,function(b){return this.target&&"_blank"===this.target?a.open(g.tracker._getLinkerUrl(this.href,_gas._allowAnchor)):_gas.push(["_link",this.href,_gas._allowAnchor]),b.preventDefault?b.preventDefault():b.returnValue=!1,!1}):this._addEventListener(e,b,function(){this.href=g.tracker._getLinkerUrl(this.href,_gas._allowAnchor)}))}return!1}}function t(a){if(_[a.player_id]||(_[a.player_id]={},_[a.player_id].timeTriggers=I.call($)),_[a.player_id].timeTriggers.length>0&&100*a.data.percent>=_[a.player_id].timeTriggers[0]){var b=_[a.player_id].timeTriggers.shift();_gas.push(["_trackEvent","Vimeo Video",b+"%",ab[a.player_id]])}}function u(a,b,c){if(!c.contentWindow||!c.contentWindow.postMessage||!JSON)return!1;var d=c.getAttribute("src").split("?")[0],e=JSON.stringify({method:a,value:b});return c.contentWindow.postMessage(e,d),!0}function v(a){if(J.call(a.origin,"//player.vimeo.com")>-1){var b=JSON.parse(a.data);"ready"===b.event?w.call(_gas.gh):b.method?"getVideoUrl"===b.method&&(ab[b.player_id]=b.value):"playProgress"===b.event?t(b):_gas.push(["_trackEvent",Z.category,b.event,ab[b.player_id]])}}function w(){for(var b,c,d,e=G.getElementsByTagName("iframe"),f=0,g=Z.force,h=Z.percentages,i=0;i<e.length;i++)if(J.call(e[i].src,"//player.vimeo.com")>-1){if(b="gas_vimeo_"+i,c=e[i].src,d="?",J.call(c,"?")>-1&&(d="&"),J.call(c,"api=1")<0){if(!g)continue;c+=d+"api=1&player_id="+b}else J.call(c,"player_id=")<-1&&(c+=d+"player_id="+b);if(f++,e[i].id=b,e[i].src!==c){e[i].src=c;break}u("getVideoUrl","",e[i]),u("addEventListener","play",e[i]),u("addEventListener","pause",e[i]),u("addEventListener","finish",e[i]),h&&($=h,u("addEventListener","playProgress",e[i]))}f>0&&bb===!1&&(this._addEventListener(a,"message",v,!1),bb=!0)}function x(a,c){if(fb[c]===b||fb[c].timeTriggers.length<=0)return!1;var d=100*(a.getCurrentTime()/a.getDuration());if(d>=fb[c].timeTriggers[0]){var e=fb[c].timeTriggers.shift();_gas.push(["_trackEvent",db.category,e+"%",a.getVideoUrl()])}fb[c].timer=setTimeout(x,1e3,a,c)}function y(a){var b=a.getVideoUrl();fb[b]&&fb[b].timer&&(x(a,b),clearTimeout(fb[b].timer))}function z(a){if(eb&&eb.length){var b=a.getVideoUrl();fb[b]?y(a):(fb[b]={},fb[b].timeTriggers=I.call(eb)),fb[b].timer=setTimeout(x,1e3,a,b)}}function A(a){var b="";switch(a.data){case 0:b="finish",y(a.target);break;case 1:b="play",z(a.target);break;case 2:b="pause",y(a.target)}b&&_gas.push(["_trackEvent",db.category,b,a.target.getVideoUrl()])}function B(a){_gas.push(["_trackEvent",db.category,"error ("+a.data+")",a.target.getVideoUrl()])}function C(){for(var a,b,c,d=G.getElementsByTagName("object"),e=/(https?:\/\/www\.youtube(-nocookie)?\.com[^\/]*).*\/v\/([^&?]+)/,f=0;f<d.length;f++){a=d[f].getElementsByTagName("param");for(var g=0;g<a.length;g++)if("movie"===a[g].name&&a[g].value){c=a[g].value.match(e),c&&c[1]&&c[3]&&(b=G.createElement("iframe"),b.src=c[1]+"/embed/"+c[3]+"?enablejsapi=1",b.width=d[f].width,b.height=d[f].height,b.setAttribute("frameBorder","0"),b.setAttribute("allowfullscreen",""),d[f].parentNode.insertBefore(b,d[f]),d[f].parentNode.removeChild(d[f]),f--);break}}}function D(b){var c=b.force,d=b.percentages;if(c)try{C()}catch(e){_gas.push(["_trackException",e,"GAS Error on youtube.js:_ytMigrateObjectEmbed"])}for(var f=[],g=G.getElementsByTagName("iframe"),h=0;h<g.length;h++)if(J.call(g[h].src,"//www.youtube.com/embed")>-1){if(J.call(g[h].src,"enablejsapi=1")<0){if(!c)continue;g[h].src+=J.call(g[h].src,"?")<0?"?enablejsapi=1":"&enablejsapi=1"}f.push(g[h])}if(f.length>0){d&&d.length&&(eb=d),a.onYouTubePlayerAPIReady=function(){for(var b,c=0;c<f.length;c++)b=new a.YT.Player(f[c]),b.addEventListener("onStateChange",A),b.addEventListener("onError",B)};var i=G.createElement("script"),j="http:";"https:"===G.location.protocol&&(j="https:"),i.src=j+"//www.youtube.com/player_api",i.type="text/javascript",i.async=!0;var k=G.getElementsByTagName("script")[0];k.parentNode.insertBefore(i,k)}}var E=function(){this._setDummyTracker()};E.prototype._setDummyTracker=function(){if(!this.tracker){var b=a._gat._getTrackers();b.length>0&&(this.tracker=b[0])}},E.prototype.inArray=function(a,b){if(a&&a.length)for(var c=0;c<a.length;c++)if(a[c]===b)return!0;return!1},E.prototype._sanitizeString=function(a,b){return a=a.toLowerCase().replace(/^\ +/,"").replace(/\ +$/,"").replace(/\s+/g,"_").replace(/[áàâãåäæª]/g,"a").replace(/[éèêëЄ€]/g,"e").replace(/[íìîï]/g,"i").replace(/[óòôõöøº]/g,"o").replace(/[úùûü]/g,"u").replace(/[碩]/g,"c"),b&&(a=a.replace(/[^a-z0-9_\-]/g,"_")),a.replace(/_+/g,"_")},E.prototype._addEventListener=function(b,c,d,e){var f=function(c){return c&&c.target||(c=a.event,c.target=c.srcElement),d.call(b,c)};return b.addEventListener?(b.addEventListener(c,f,!!e),!0):b.attachEvent?b.attachEvent("on"+c,f):(c="on"+c,"function"==typeof b[c]&&(f=function(a,b){return function(){a.apply(this,arguments),b.apply(this,arguments)}}(b[c],f)),b[c]=f,!0)},E.prototype._liveEvent=function(a,b,c){var d=this;a=a.toUpperCase(),a=a.split(","),d._addEventListener(G,b,function(b){for(var e=b.target;"HTML"!==e.nodeName&&!d.inArray(a,e.nodeName)&&null!==e.parentNode;e=e.parentNode);e&&d.inArray(a,e.nodeName)&&c.call(e,b)},!0)},E.prototype._DOMReady=function(b){function c(){c.done||(c.done=!0,b.apply(d,arguments))}var d=this;return/^(interactive|complete)/.test(G.readyState)?c():(this._addEventListener(G,"DOMContentLoaded",c,!1),this._addEventListener(a,"load",c,!1),void 0)},a._gaq=a._gaq||[];var F=a._gas||[];if(!(F._accounts_length>=0)){var G=a.document,H=(Object.prototype.toString,Object.prototype.hasOwnProperty),I=(Array.prototype.push,Array.prototype.slice),J=(String.prototype.trim,String.prototype.indexOf),K=G.location.href,L=G.documentElement;c.prototype._addHook=function(a,b){return"string"==typeof a&&"function"==typeof b&&("undefined"==typeof _gas._hooks[a]&&(_gas._hooks[a]=[]),_gas._hooks[a].push(b)),!1},c.prototype._execute=function(){var a,c,f,g,h,i=I.call(arguments),j=this,k=i.shift(),l=!0,m=0;if("function"==typeof k)return e(function(a,b){return function(){a.call(b)}}(k,j.gh));if("object"==typeof k&&k.length>0){if(c=k.shift(),J.call(c,".")>=0?(g=c.split(".")[0],c=c.split(".")[1]):g=b,f=j._hooks[c],f&&f.length>0)for(a=0;a<f.length;a++)try{h=f[a].apply(j.gh,k),h===!1?l=!1:h&&h.length>0&&(k=h)}catch(n){"_trackException"!==c&&j.push(["_trackException",n])}if(l===!1)return 1;if("_setAccount"===c){for(a in j._accounts)if(j._accounts[a]===k[0]&&g===b)return 1;return g=g||"_gas"+String(j._accounts_length+1),"undefined"==typeof j._accounts._gas1&&-1!==J.call(g,"_gas")&&(g="_gas1"),j._accounts[g]=k[0],j._accounts_length+=1,g=d(g),m=e([g+c,k[0]]),j.gh._setDummyTracker(),m}if("_link"===c||"_linkByPost"===c||"_require"===c||"_anonymizeIp"===c)return i=I.call(k),i.unshift(c),e(i);var o;if(g&&j._accounts[g])return o=d(g)+c,i=I.call(k),i.unshift(o),e(i);if(!(j._accounts_length>0))return i=I.call(k),i.unshift(c),e(i);for(a in j._accounts)H.call(j._accounts,a)&&(o=d(a)+c,i=I.call(k),i.unshift(o),m+=e(i));return m?1:0}},c.prototype.push=function(){for(var b=this,c=I.call(arguments),d=0;d<c.length;d++)!function(b,c){a._gaq.push(function(){c._execute.call(c,b)})}(c[d],b)},a._gas=_gas=new c,_gas.push(["_addHook","_trackException",function(a,b){return _gas.push(["_trackEvent","Exception "+(a.name||"Error"),b||a.message||a,K]),!1}]),_gas.push(["_addHook","_setDebug",function(a){_gas.debug_mode=!!a}]),_gas.push(["_addHook","_popHook",function(a){var b=_gas._hooks[a];return b&&b.pop&&b.pop(),!1}]),_gas.push(["_addHook","_gasSetDefaultTracker",function(a){return _gas._default_tracker=a,!1}]),_gas.push(["_addHook","_trackPageview",function(){var a=I.call(arguments);return a.length>=2&&"string"==typeof a[0]&&"string"==typeof a[1]?[{page:a[0],title:a[1]}]:a}]);var M=function(a){var b=this;if(!b._downloadTracked){b._downloadTracked=!0,a?"string"==typeof a?a={extensions:a.split(",")}:a.length>=1&&(a={extensions:a}):a={extensions:[]},a.category=a.category||"Download";var c="xls,xlsx,doc,docx,ppt,pptx,pdf,txt,zip";return c+=",rar,7z,exe,wma,mov,avi,wmv,mp3,csv,tsv",c=c.split(","),a.extensions=a.extensions.concat(c),b._liveEvent("a","mousedown",function(){var c=this;if(c.href){var d=f.call(b,c.href,a.extensions);d&&_gas.push(["_trackEvent",a.category,d,c.href])}}),!1}};_gas.push(["_addHook","_gasTrackDownloads",M]),_gas.push(["_addHook","_trackDownloads",M]),_gas.push(["_addHook","_gasMetaEcommerce",g]),_gas.push(["_addHook","_trackEvent",function(){var a=I.call(arguments);return a[3]&&(a[3]=(a[3]<0?0:Math.round(a[3]))||0),a}]);var N=function(a){if(!this._formTracked){this._formTracked=!0;var b=this;"object"!=typeof a&&(a={}),a.category=a.category||"Form Tracking";var c=function(b){var c=b.target,d=c.name||c.id||c.type||c.nodeName,e=h(c),f="form ("+e+")",g=d+" ("+b.type+")";_gas.push(["_trackEvent",a.category,f,g])};b._DOMReady(function(){var a,d,e=["input","select","textarea","hidden"],f=["form"],g=[];for(a=0;a<e.length;a++)for(g=G.getElementsByTagName(e[a]),d=0;d<g.length;d++)b._addEventListener(g[d],"change",c);for(a=0;a<f.length;a++)for(g=G.getElementsByTagName(f[a]),d=0;d<g.length;d++)b._addEventListener(g[d],"submit",c)})}};_gas.push(["_addHook","_gasTrackForms",N]),_gas.push(["_addHook","_trackForms",N]);var O=function(a){var b=this;b._liveEvent(a,"play",i),b._liveEvent(a,"pause",i),b._liveEvent(a,"ended",i)},P=function(){this._videoTracked||(this._videoTracked=!0,O.call(this,"video"))},Q=function(){this._audioTracked||(this._audioTracked=!0,O.call(this,"audio"))};_gas.push(["_addHook","_gasTrackVideo",P]),_gas.push(["_addHook","_gasTrackAudio",Q]),_gas.push(["_addHook","_trackVideo",P]),_gas.push(["_addHook","_trackAudio",Q]),_gas.push(["_addHook","_gasMeta",j]),_gas.push(["_addHook","_gasHTMLMarkup",k]);var R=function(a){return this._mailtoTracked?void 0:(this._mailtoTracked=!0,a||(a={}),a.category=a.category||"Mailto",this._liveEvent("a","mousedown",function(b){var c=b.target;c&&c.href&&c.href.toLowerCase&&0===J.call(c.href.toLowerCase(),"mailto:")&&_gas.push(["_trackEvent",a.category,c.href.substr(7)])}),!1)};_gas.push(["_addHook","_gasTrackMailto",R]),_gas.push(["_addHook","_trackMailto",R]);var S,T=null,U=0;_gas.push(["_addHook","_gasTrackMaxScroll",r]),_gas.push(["_addHook","_trackMaxScroll",r]),_gas._allowAnchor=!1,_gas.push(["_addHook","_setAllowAnchor",function(a){_gas._allowAnchor=!!a}]),_gas.push(["_addHook","_link",function(a,c){return c===b&&(c=_gas._allowAnchor),[a,c]}]),_gas.push(["_addHook","_linkByPost",function(a,c){return c===b&&(c=_gas._allowAnchor),[a,c]}]);var V,W=[];_gas.push(["_addHook","_setDomainName",function(a){return J.call("."+G.location.hostname,a)<0?(W.push(a),!1):(V=a,void 0)}]),_gas.push(["_addHook","_addExternalDomainName",function(a){return W.push(a),!1}]);var X=function(){var a=this,b=I.call(arguments);a&&a._DOMReady&&a._DOMReady(function(){s.apply(a,b)})};_gas.push(["_addHook","_gasMultiDomain",X]),_gas.push(["_addHook","_setMultiDomain",X]);var Y=function(a){if(!this._outboundTracked){this._outboundTracked=!0;var b=this;a||(a={}),a.category=a.category||"Outbound",b._liveEvent("a","mousedown",function(){var b=this;if(("http:"===b.protocol||"https:"===b.protocol)&&-1===J.call(b.hostname,G.location.hostname)){var c=b.pathname+b.search+"",d=J.call(c,"__utm");-1!==d&&(c=c.substring(0,d)),_gas.push(["_trackEvent",a.category,b.hostname,c])}})}};_gas.push(["_addHook","_gasTrackOutboundLinks",Y]),_gas.push(["_addHook","_trackOutboundLinks",Y]);var Z,$=[],_={},ab={},bb=!1,cb=function(a){var b=this;return("boolean"==typeof a||"force"===a)&&(a={force:!!a}),a=a||{},a.category=a.category||"Vimeo Video",a.percentages=a.percentages||[],a.force=a.force||!1,Z=a,b._DOMReady(function(){w.call(b)}),!1};_gas.push(["_addHook","_gasTrackVimeo",cb]),_gas.push(["_addHook","_trackVimeo",cb]);var db,eb=[],fb={},gb=function(a){var b=I.call(arguments);!b[0]||"boolean"!=typeof b[0]&&"force"!==b[0]||(a={force:!!b[0]},b[1]&&b[1].length&&(a.percentages=b[1])),a=a||{},a.force=a.force||!1,a.category=a.category||"YouTube Video",a.percentages=a.percentages||[],db=a;var c=this;return c._DOMReady(function(){D.call(c,a)}),!1};for(_gas.push(["_addHook","_gasTrackYoutube",gb]),_gas.push(["_addHook","_trackYoutube",gb]);_gas._queue.length>0;)_gas.push(_gas._queue.shift());"undefined"==typeof a._gat&&!function(){var a=G.getElementByID("gas-script"),b=G.createElement("script");b.type="text/javascript",b.async=!0,b.src=null!==a&&"true"===a.getAttribute("data-use-dcjs")?("https:"===G.location.protocol?"https://":"http://")+"stats.g.doubleclick.net/dc.js":("https:"===G.location.protocol?"https://ssl":"http://www")+".google-analytics.com/ga.js";var c=G.getElementsByTagName("script")[0];c.parentNode.insertBefore(b,c)}()}}(window);