diff options
author | vipsoft <vipsoft@59fd770c-687e-43c8-a1e3-f5a4ff64c105> | 2009-05-28 03:10:11 +0400 |
---|---|---|
committer | vipsoft <vipsoft@59fd770c-687e-43c8-a1e3-f5a4ff64c105> | 2009-05-28 03:10:11 +0400 |
commit | c5ee66efac7bc3cea460f4905a27b6f33212a90f (patch) | |
tree | f02932d8b4491834e41a8ee7285bc3da1eef9de0 /js | |
parent | cb769d35031a6fc516790777ddb544e503d9b7c8 (diff) |
fixes #355 - OO version of piwik.js which no longer modifies DOM;
remove misc/testJavascripTracker (interactive tests);
add tests/javascript (QUnit unit tests);
fixes #661 - use click event instead of mousedown;
fixes #549 - define your own download/outlink tracking classes;
fixes #82 - add hook interface for module
Diffstat (limited to 'js')
-rwxr-xr-x | js/index.php | 41 | ||||
-rw-r--r-- | js/piwik.js | 1008 |
2 files changed, 1049 insertions, 0 deletions
diff --git a/js/index.php b/js/index.php new file mode 100755 index 0000000000..60786f2849 --- /dev/null +++ b/js/index.php @@ -0,0 +1,41 @@ +<?php +/** + * Piwik - Open source web analytics + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html Gpl v3 or later + * @version $Id$ + */ + +$file = "../piwik.js"; + +/* + * Conditional GET + */ +if (file_exists($file)) { + $modifiedSince = ''; + if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { + $modifiedSince = $_SERVER['HTTP_IF_MODIFIED_SINCE']; + } + $lastModified = gmdate('D, d M Y H:i:s', filemtime($file)) . ' GMT'; + + // strip any trailing data appended to header + if (false !== ($semicolon = strpos($modifiedSince, ';'))) { + $modifiedSince = substr($modifiedSince, 0, $semicolon); + } + + if ($modifiedSince == $lastModified) { + header('HTTP/1.1 304 Not Modified'); + } else { + header('Last-Modified: ' . $lastModified); + header('Content-Length: ' . filesize($file)); + header('Content-Type: application/x-javascript'); + + if (!readfile($file)) { + header ("HTTP/1.0 505 Internal server error"); + } + } +} else { + header ("HTTP/1.0 404 Not Found"); +} +exit; diff --git a/js/piwik.js b/js/piwik.js new file mode 100644 index 0000000000..63b26796ac --- /dev/null +++ b/js/piwik.js @@ -0,0 +1,1008 @@ +/*! + * Piwik - Web Analytics + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html Gpl v3 or later + * @version $Id$ + */ + +/*jslint browser:true, forin:true, plusplus:false, onevar:false, eqeqeq:false */ +/*global window escape unescape ActiveXObject */ + +// Note: YUICompressor 2.4.2 won't compress piwik_log() because of the the "evil" eval(). +// Override this behaviour using http://yuilibrary.com/projects/yuicompressor/ticket/2343811 +/*jslint evil:true */ + +/* + * Browser [In]Compatibility + * + * This version of piwik.js is known to not work with: + * - IE4 (and below) - try..catch and for..in not introduced until IE5 + */ + +// Guard against loading the script twice +var Piwik; +try { + if (Piwik.getTracker) { } +} catch (e) { + // Piwik singleton and namespace + Piwik = (function () { + /************************************************************ + * Private data + ************************************************************/ + + var expireDateTime, + + /* plugins */ + plugins = {}, + + /* alias frequently used globals for added minification */ + documentAlias = document, + navigatorAlias = navigator, + screenAlias = screen, + windowAlias = window, + + /* DOM Ready */ + hasLoaded = false, + registeredOnLoadHandlers = []; + + /************************************************************ + * Private methods + ************************************************************/ + + /* + * Is property (or variable) defined? + */ + function isDefined(property) { + return typeof property !== 'undefined'; + } + + /* + * Cross-browser helper function to add event handler + */ + function addEventListener(element, eventType, eventHandler, useCapture) { + if (element.addEventListener) { + element.addEventListener(eventType, eventHandler, useCapture); + return true; + } else if (element.attachEvent) { + return element.attachEvent('on' + eventType, eventHandler); + } + element['on' + eventType] = eventHandler; + } + + /* + * Call plugin hook methods + */ + function executePluginMethod(methodName, callback) { + var result = '', i, pluginMethod; + + for (i in plugins) { + pluginMethod = plugins[i][methodName]; + if (typeof pluginMethod === 'function') { + result += pluginMethod(callback); + } + } + + return result; + } + + /* + * Handle beforeunload event + */ + function beforeUnloadHandler(unloadEvent /* not used */) { + /* + * Delay/pause (blocks UI) + */ + if (isDefined(expireDateTime)) { + var now = new Date(); + + while (now.getTime() < expireDateTime) { + now = new Date(); + } + } + + executePluginMethod('unload'); + } + + /* + * Handler for onload event + */ + function loadHandler(loadEvent /* unused */) { + if (!hasLoaded) { + hasLoaded = true; + executePluginMethod('load'); + for (var i = 0; i < registeredOnLoadHandlers.length; i++) { + registeredOnLoadHandlers[i](); + } + } + return true; + } + + /* + * Add onload or DOM ready handler + */ + function addReadyListener() { + if (documentAlias.addEventListener) { + addEventListener(documentAlias, "DOMContentLoaded", function () { + documentAlias.removeEventListener("DOMContentLoaded", arguments.callee, false); + loadHandler(); + }); + } else if (documentAlias.attachEvent) { + documentAlias.attachEvent("onreadystatechange", function () { + if (documentAlias.readyState === "complete") { + documentAlias.detachEvent("onreadystatechange", arguments.callee); + loadHandler(); + } + }); + + if (documentAlias.documentElement.doScroll && windowAlias == windowAlias.top) { + (function () { + if (hasLoaded) { + return; + } + try { + documentAlias.documentElement.doScroll("left"); + } catch (error) { + setTimeout(arguments.callee, 0); + return; + } + loadHandler(); + }()); + } + } + // fallback + addEventListener(windowAlias, 'load', loadHandler, false); + } + + /* + * Piwik Tracker class + * + * trackerUrl and trackerSiteId are optional arguments to the constructor + * + * See: Tracker.setTrackerUrl() and Tracker.setSiteId() + */ + function Tracker(trackerUrl, siteId) { + /************************************************************ + * Private members + ************************************************************/ + + var // Tracker URL + configTrackerUrl = trackerUrl || '', + + // Site ID + configTrackerSiteId = siteId || '', + + // Document title + configTitle = documentAlias.title || '', + + // Extensions to be treated as download links + configDownloadExtensions = '7z|aac|arc|arj|asf|asx|avi|bin|csv|doc|exe|flv|gif|gz|gzip|hqx|jar|jpe?g|js|mp(2|3|4|e?g)|mov(ie)?|msi|msp|pdf|phps|png|ppt|qtm?|ra(m|r)?|sea|sit|tar|tgz|torrent|txt|wav|wma|wmv|wpd||xls|xml|z|zip', + + // Hosts or alias(es) to not treat as outlinks + configHostsAlias = [windowAlias.location.hostname], + + // Anchor classes to not track + configIgnoreClasses = [], + + // Download class name + configDownloadClass = 'piwik_download', + + // (Out) Link class name + configLinkClass = 'piwik_link', + + // Maximum delay to wait for web bug image to be fetched (in milliseconds) + configTrackerPause = 500, + + // Custom data + configCustomData, + + // Client-side data collection + browserHasCookies = '0', + pageReferrer, + + // Plugin, Parameter name, MIME type, ActiveX progid, detected, version + pluginMap = { + director: ['dir', 'application/x-director', + ['SWCtl.SWctl.1'], + '0', ''], + flash: ['fla', 'application/x-shockwave-flash', + ['ShockwaveFlash.ShockwaveFlash.1'], + '0', ''], + pdf: ['pdf', 'application/pdf', + ['AcroPDF.PDF.1', 'PDF.PdfCtrl.6', 'PDF.PdfCtrl.5', 'PDF.PdfCtrl.1'], + '0', ''], + realplayer: ['realp', 'audio/x-pn-realaudio-plugin', + ['rmocx.RealPlayer G2 Control.1', 'RealPlayer.RealPlayer(tm) ActiveX Control (32-bit)'], + '0', ''], + wma: ['wma', 'application/x-mplayer2', + ['WMPlayer.OCX', 'MediaPlayer.MediaPlayer.1'], + '0', ''] + }, + + // Guard against installing the link tracker more than once per Tracker instance + linkTrackingInstalled = false, + + /* + * encode or escape + * - encodeURIComponent added in IE5.5 + */ + escapeWrapper = windowAlias.encodeURIComponent || escape, + + /* + * decode or unescape + * - decodeURIComponent added in IE5.5 + */ + unescapeWrapper = windowAlias.decodeURIComponent || unescape, + + /* + * stringify + * - based on public domain JSON implementation at http://www.json.org/json2.js (2009-04-16) + */ + stringify = function (value) { + + var escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + // table of character substitutions + meta = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'}; + + // If the string contains no control characters, no quote characters, and no + // backslash characters, then we can safely slap some quotes around it. + // Otherwise we must also replace the offending characters with safe escape + // sequences. + function quote(string) { + escapable.lastIndex = 0; + return escapable.test(string) ? + '"' + string.replace(escapable, function (a) { + var c = meta[a]; + return typeof c === 'string' ? c : + '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }) + '"' : + '"' + string + '"'; + } + + function f(n) { + return n < 10 ? '0' + n : n; + } + + // Produce a string from holder[key]. + function str(key, holder) { + var i, // The loop counter. + k, // The member key. + v, // The member value. + partial, + value = holder[key]; + + if (value === null) { + return 'null'; + } + + // If the value has a toJSON method, call it to obtain a replacement value. + if (value && typeof value === 'object' && typeof value.toJSON === 'function') { + value = value.toJSON(key); + } + + // What happens next depends on the value's type. + switch (typeof value) { + case 'string': + return quote(value); + + case 'number': + // JSON numbers must be finite. Encode non-finite numbers as null. + return isFinite(value) ? String(value) : 'null'; + + case 'boolean': + case 'null': + // If the value is a boolean or null, convert it to a string. Note: + // typeof null does not produce 'null'. The case is included here in + // the remote chance that this gets fixed someday. + return String(value); + + case 'object': + // Make an array to hold the partial results of stringifying this object value. + partial = []; + + // Is the value an array? +// if (Object.prototype.toString.call(value)=="[object Array]") { // call added in IE5.5 + if (value instanceof Array) { + // The value is an array. Stringify every element. Use null as a placeholder + // for non-JSON values. + for (i = 0; i < value.length; i++) { + partial[i] = str(i, value) || 'null'; + } + + // Join all of the elements together, separated with commas, and wrap them in + // brackets. + v = partial.length === 0 ? '[]' : '[' + partial.join(',') + ']'; + return v; + } + +// if (Object.prototype.toString.call(value)=="[object Date]") { // call added in IE5.5 + if (value instanceof Date) { + return quote(value.getUTCFullYear() + '-' + + f(value.getUTCMonth() + 1) + '-' + + f(value.getUTCDate()) + 'T' + + f(value.getUTCHours()) + ':' + + f(value.getUTCMinutes()) + ':' + + f(value.getUTCSeconds()) + 'Z'); + } + + // Otherwise, iterate through all of the keys in the object. + for (k in value) { + v = str(k, value); + if (v) { + // partial.push(quote(k) + ':' + v); // array.push added in IE5.5 + partial[partial.length] = quote(k) + ':' + v; + } + } + + // Join all of the member texts together, separated with commas, + // and wrap them in braces. + v = partial.length === 0 ? '{}' : '{' + partial.join(',') + '}'; + return v; + } + } + + return str('', {'': value}); + }, + + /* + * registered (user-defined) hooks + */ + registeredHooks = {}; + + /* + * Platform test for Internet Explorer on Windows + */ + function isWindowsIE() { + var agent = navigatorAlias.userAgent.toLowerCase(); + + return (agent.indexOf('msie') != -1) && (agent.indexOf('opera') == -1) && + ((agent.indexOf('win') != -1) || (agent.indexOf('32bit') != -1)); + } + + /* + * Set cookie value + */ + function setCookie(cookieName, value, daysToExpire, path, domain, secure) { + var expiryDate; + + if (daysToExpire) { + // time is in milliseconds + expiryDate = new Date(); + // there are 1000 * 60 * 60 * 24 milliseconds in a day (i.e., 86400000 or 8.64e7) + expiryDate.setTime(expiryDate.getTime() + daysToExpire * 8.64e7); + } + + documentAlias.cookie = cookieName + '=' + escapeWrapper(value) + + (daysToExpire ? ';expires=' + expiryDate.toGMTString() : '') + + ';path=' + (path ? path : '/') + + (domain ? ';domain=' + domain : '') + + (secure ? ';secure' : ''); + } + + /* + * Get cookie value + */ + function getCookie(cookieName, path, domain) { + var cookiePattern = new RegExp('(^|;)[ ]*' + cookieName + '=([^;]*)' + + (path ? '(;[ ]*expires=[^;]*)?;[ ]*path=' + path.replace('/', '\\/') + '' : '') + + (domain ? ';[ ]*domain=' + domain + '(;|$)' : '')), + + cookieMatch = cookiePattern.exec(documentAlias.cookie); + + return cookieMatch ? unescapeWrapper(cookieMatch[2]) : 0; + } + + /* + * Discard cookie + */ +/* // NOT CURRENTLY USED + function dropCookie(cookieName, path, domain) { + // browser may not delete cookie until browser closed (session ends) + if (getCookie(cookieName)) { + // clear value, set expires in the past + setCookie(cookieName, '', -1, path, domain); + } + } +*/ + + /* + * Send image request to Piwik server using GET. + */ + function getImage(url, delay) { + var now = new Date(), + image = new Image(1, 1); + + expireDateTime = now.getTime() + delay; + + image.onLoad = function () { }; + image.src = url; + } + + /* + * Browser plugin tests + */ + function detectBrowserPlugins() { + var i, mimeTypes = ''; + + function setPluginInfo(pluginName, mimeType) { + if (mimeTypes.indexOf(mimeType) != -1 && navigatorAlias.mimeTypes[mimeType].enabledPlugin !== null) { + pluginMap[pluginName][3] = '1'; + } + } + + /* + * Note: an ActiveXObject object has no intrinsic properties or methods; + * thus, no standard means of getting a plugin's version number + */ + function setIEPluginInfo(pluginName, progids) { + if (progids !== null && isDefined(windowAlias.ActiveXObject)) { + for (var j = 0; j < progids.length; j++) { + try { + if (new ActiveXObject(progids[j])) { + pluginMap[pluginName][3] = '1'; + break; + } + } catch (e) { } + } + } + } + + // Begin detection of browser plugins + if (isWindowsIE()) { + for (i in pluginMap) { + setIEPluginInfo(i, pluginMap[i][2]); + } + } else { + for (i = 0; i < navigatorAlias.mimeTypes.length; i++) { + mimeTypes += navigatorAlias.mimeTypes[i].type.toLowerCase(); + } + + for (i in pluginMap) { + setPluginInfo(i, pluginMap[i][1]); + } + } + } + + /* + * Get page referrer + */ + function getReferrer() { + var referrer = ''; + try { + referrer = top.document.referrer; + } catch (e) { + if (parent) { + try { + referrer = parent.document.referrer; + } catch (e2) { + referrer = ''; + } + } + } + if (referrer === '') { + referrer = documentAlias.referrer; + } + + return referrer; + } + + /* + * Does browser have cookies enabled (for this site)? + */ + function hasCookies() { + var testCookieName = '_pk_testcookie'; + if (!isDefined(navigatorAlias.cookieEnabled)) { + setCookie(testCookieName, '1'); + return getCookie(testCookieName) == '1' ? '1' : '0'; + } + + return navigatorAlias.cookieEnabled ? '1' : '0'; + } + + + /* + * Get the web bug image (transparent single pixel, 1x1, image) to log visit in Piwik + */ + function getWebBug() { + var i, customDataString, pluginString, extraString, now, request; + + /* + * encode custom vars + */ + customDataString = ''; + if (isDefined(configCustomData)) { + customDataString = '&data=' + escapeWrapper(stringify(configCustomData)); + } + + /* + * encode plugin data + */ + pluginString = ''; + for (i in pluginMap) { + pluginString += '&' + pluginMap[i][0] + '=' + pluginMap[i][3]; + } + + extraString = executePluginMethod('log'); + + now = new Date(); + request = configTrackerUrl + '?idsite=' + configTrackerSiteId + + '&url=' + escapeWrapper(documentAlias.location.href) + + '&action_name=' + escapeWrapper(configTitle) + // refs #530 + '&res=' + screenAlias.width + 'x' + screenAlias.height + + '&h=' + now.getHours() + '&m=' + now.getMinutes() + '&s=' + now.getSeconds() + + '&cookie=' + browserHasCookies + + '&urlref=' + escapeWrapper(pageReferrer) + + pluginString + customDataString + extraString; + + getImage(request, configTrackerPause); + } + + /* + * Log the click with the server + */ + function logClick(url, linkType, customData) { + var customDataString, extraString, request; + + /* + * encode custom data + */ + customDataString = ''; + if (isDefined(customData)) { + customDataString = '&data=' + escapeWrapper(stringify(configCustomData)); + } + + extraString = executePluginMethod('click'); + + request = configTrackerUrl + '?idsite=' + configTrackerSiteId + + '&' + linkType + '=' + escapeWrapper(url) + + '&rand=' + Math.random() + + '&redirect=0' + + customDataString + extraString; + + getImage(request, configTrackerPause); + } + + /* + * Is the host local? (i.e., not an outlink) + */ + function isSiteHostName(hostName) { + var i, alias, offset; + + for (i = 0; i < configHostsAlias.length; i++) { + alias = configHostsAlias[i]; + + if (hostName == alias) { + return true; + } + + if (alias.substr(0, 2) == '*.') { + if ((hostName) == alias.substr(2)) { + return true; + } + + offset = hostName.length - alias.length + 1; + if ((offset > 0) && (hostName.substr(offset) == alias.substr(1))) { + return true; + } + } + } + + return false; + } + + /* + * Link or Download? + */ + function getLinkType(className, href, isInLink) { + // outlinks + if (!isInLink) { + return 'link'; + } + + // does class indicate whether it is an (explicit/forced) outlink or a download? + var downloadOrLinkPattern = new RegExp('(^| )(' + configDownloadClass + '|' + configLinkClass + ')( |$)'), + match = downloadOrLinkPattern.exec(className), + + // does file extension indicate that it is a download? + downloadExtensionsPattern = new RegExp('\\.(' + configDownloadExtensions + ')$', 'i'); + + // optimization of the nested if..elseif..else construct below + return match ? (match[2] == configDownloadClass ? 'download' : 'link') : + (downloadExtensionsPattern.test(href) ? 'download' : 0); + +/* + var linkType; + + if (match) { + if (match[2] == configDownloadClass) { + // class attribute contains 'piwik_download' (or user's override) + linkType = 'download'; + } else { + // class attribute contains 'piwik_link' (or user's override) + linkType = 'link'; + } + } else if (downloadExtensionsPattern.test(sourceHref)) { + // file extension matches a defined download extension + linkType = 'download'; + } else { + // otherwise none of the above + linkType = 0; + } + + return linkType; + */ + } + + /* + * Handle click event + */ + function clickHandler(clickEvent) { + var sourceElement, tag, linkType; + + if (!isDefined(clickEvent)) { + clickEvent = windowAlias.event; + } + + if (isDefined(clickEvent.target)) { + sourceElement = clickEvent.target; + } else if (isDefined(clickEvent.srcElement)) { + sourceElement = clickEvent.srcElement; + } else { + return true; + } + + while ((tag = sourceElement.tagName) != 'A' && tag != 'AREA') { + sourceElement = sourceElement.parentNode; + } + + if (isDefined(sourceElement.href)) { + // browsers, such as Safari, don't downcase hostname and href + var originalSourceHostName = sourceElement.hostname, + sourceHostName = originalSourceHostName.toLowerCase(), + sourceHref = sourceElement.href.replace(originalSourceHostName, sourceHostName), + scriptProtocol = /^(javascript|vbscript|jscript|mocha|livescript|ecmascript):/i; + + // ignore script pseudo-protocol links + if (!scriptProtocol.test(sourceHref)) { + // track outlinks and all downloads + linkType = getLinkType(sourceElement.className, sourceHref, isSiteHostName(sourceHostName)); + if (linkType) { + logClick(sourceHref, linkType); + } + } + } + + // Returns true so href isn't cancelled + return true; + } + + /* + * Construct regular expression of classes to be ignored + */ + function getIgnoreRegExp() { + var i, ignoreRegExp = '(^| )(piwik_ignore'; + + if (isDefined(configIgnoreClasses)) { + for (i = 0; i < configIgnoreClasses.length; i++) { + ignoreRegExp += '|' + configIgnoreClasses[i]; + } + } + ignoreRegExp += ')( |$)'; + + return new RegExp(ignoreRegExp); + } + + /* + * Add click listener to a DOM element + */ + function addClickListener(element) { + addEventListener(element, 'click', clickHandler, false); + } + + /* + * Add click handlers to anchor and AREA elements, except those to be ignored + */ + function addClickListeners() { + if (!linkTrackingInstalled) { + linkTrackingInstalled = true; + + // iterate through anchor elements with href and AREA elements + + var i, ignorePattern = getIgnoreRegExp(), linkElements = documentAlias.links; + + if (linkElements) { + for (i = 0; i < linkElements.length; i++) { + if (!ignorePattern.test(linkElements[i].className)) { + addClickListener(linkElements[i]); + } + } + } + } + } + + /* + * Register a user-defined hook + * - if userHook is a string, object construction is deferred, + * permitting the addition of privileged members + */ + function registerHook(hookName, userHook) { + var hookObj = null; + + if (typeof hookName == 'string' && !isDefined(registeredHooks[hookName])) { + if (typeof userHook == 'object') { + hookObj = userHook; + } else if (typeof userHook == 'string') { + try { + eval('hookObj =' + userHook); + } catch (e) { } + } + + registeredHooks[hookName] = hookObj; + } + return hookObj; + } + + /************************************************************ + * Constructor + ************************************************************/ + + /* + * initialize tracker + */ + pageReferrer = getReferrer(); + browserHasCookies = hasCookies(); + detectBrowserPlugins(); + + /* + * initialize plugins + */ + executePluginMethod('run', registerHook); + + /************************************************************ + * Public data and methods + ************************************************************/ + + return { + /* + * Hook accessors + */ + hook: registeredHooks, + getHook: function (hookName) { + return registeredHooks[hookName]; + }, + + /* + * Specify the Piwik server URL + */ + setTrackerUrl: function (trackerUrl) { + if (isDefined(trackerUrl)) { + configTrackerUrl = trackerUrl; + } + }, + + /* + * Specify the site ID + */ + setSiteId: function (siteId) { + if (isDefined(siteId)) { + configTrackerSiteId = siteId; + } + }, + + /* + * Pass custom data to the server + */ + setCustomData: function (customData) { + if (isDefined(customData)) { + configCustomData = customData; + } + }, + + /* + * Set delay for link tracking (in milliseconds) + */ + setLinkTrackingTimer: function (delay) { + if (isDefined(delay)) { + configTrackerPause = delay; + } + }, + + /* + * Set list of file extensions to be recognized as downloads + */ + setDownloadExtensions: function (extensions) { + if (isDefined(extensions)) { + configDownloadExtensions = extensions; + } + }, + + /* + * Specify additional file extensions to be recognized as downloads + */ + addDownloadExtensions: function (extensions) { + if (isDefined(extensions)) { + configDownloadExtensions += '|' + extensions; + } + }, + + /* + * Set array of domains to be treated as local + */ + setDomains: function (hostsAlias) { + if (typeof hostsAlias == 'object' && hostsAlias instanceof Array) { + configHostsAlias = hostsAlias; + // configHostAlias.push(windowAlias.location.hostname); // array.push added in IE5.5 + configHostsAlias[configHostsAlias.length] = windowAlias.location.hostname; + } else if (typeof hostsAlias == 'string') { + configHostsAlias = [hostsAlias, windowAlias.location.hostname]; + } + }, + + /* + * Set array of classes to be ignored if present in link + */ + setIgnoreClasses: function (ignoreClasses) { + if (typeof ignoreClasses == 'object' && ignoreClasses instanceof Array) { + configIgnoreClasses = ignoreClasses; + } else if (typeof ignoreClasses == 'string') { + configIgnoreClasses = [ignoreClasses]; + } + }, + + /* + * Override document.title + */ + setDocumentTitle: function (title) { + if (isDefined(title)) { + configTitle = title; + } + }, + + /* + * Set download class name (i.e., override default: piwik_download) + */ + setDownloadClass: function (className) { + if (typeof className == 'string' && className.length > 0) { + configDownloadClass = className; + } + }, + + /* + * Set outlink class name (i.e., override default: piwik_link) + */ + setLinkClass: function (className) { + if (typeof className == 'string' && className.length > 0) { + configLinkClass = className; + } + }, + + /* + * Add click listener to a specific link element. + * When clicked, Piwik will log the click automatically. + */ + addListener: function (element) { + if (isDefined(element)) { + addClickListener(element); + } + }, + + /* + * Install link tracker + */ + enableLinkTracking: function () { + if (hasLoaded) { + // the load event has already fired, add the click listeners now + addClickListeners(); + } else { + // defer until page has loaded + registeredOnLoadHandlers[registeredOnLoadHandlers.length] = function () { + addClickListeners(); + }; + } + }, + + /* + * Manually log a click from your own code. + */ + trackLink: function (sourceUrl, linkType, customData) { + logClick(sourceUrl, linkType, customData); + }, + + /* + * Log visit to this page + */ + trackPageView: function () { + getWebBug(); + } + }; + } + + /************************************************************ + * Constructor + ************************************************************/ + + // initialize the Piwik singleton + addEventListener(windowAlias, 'beforeunload', beforeUnloadHandler, false); + addReadyListener(); + + /************************************************************ + * Public data and methods + ************************************************************/ + + return { + /* + * Add plugin + */ + addPlugin: function (pluginName, pluginObj) { + plugins[pluginName] = pluginObj; + }, + + /* + * Get Tracker + */ + getTracker: function (piwikUrl, siteId) { + return new Tracker(piwikUrl, siteId); + } + }; + }()); + + /************************************************************ + * Deprecated functionality below + * - for legacy piwik.js compatibility + ************************************************************/ + + /* + * Piwik globals + * + * var piwik_install_tracker, piwik_tracker_pause, piwik_download_extensions, piwik_hosts_alias, piwik_ignore_classes; + */ + + /* + * Track click manually (function is defined below) + */ + var piwik_track; + + /* + * Track page visit + */ + function piwik_log(documentTitle, siteId, piwikUrl, customData) { + + function getOption(optionName) { + try { + return eval('piwik_' + optionName); + } catch (e) { } + + return; /* undefined */ + } + + // instantiate the tracker + var piwikTracker = Piwik.getTracker(piwikUrl, siteId); + + // initializer tracker + piwikTracker.setDocumentTitle(documentTitle); + piwikTracker.setCustomData(customData); + + // handle Piwik globals + piwikTracker.setLinkTrackingTimer(getOption('tracker_pause')); + piwikTracker.setDownloadExtensions(getOption('download_extensions')); + piwikTracker.setDomains(getOption('hosts_alias')); + piwikTracker.setIgnoreClasses(getOption('ignore_classes')); + + // track this page view + piwikTracker.trackPageView(); + + // default is to install the link tracker + if (getOption('install_tracker') !== false) { + + // set-up click handler + piwik_track = function (sourceUrl, siteId, piwikUrl, linkType) { + piwikTracker.setSiteId(siteId); + piwikTracker.setTrackerUrl(piwikUrl); + piwikTracker.trackLink(sourceUrl, linkType); + }; + + // set-up link tracking + piwikTracker.enableLinkTracking(); + } + } +} |