diff --git a/debug.html b/debug.html index b295e9a..928e9d4 100644 --- a/debug.html +++ b/debug.html @@ -1,177 +1,240 @@ - +
-Script Output:
-+Session.js Debug / Testing Page
+Script Output:
+loading...- - - + + - - - + + + + - - + + + diff --git a/demo.html b/demo.html index 26c7146..fabe432 100644 --- a/demo.html +++ b/demo.html @@ -1,154 +1,213 @@ - -Demo for Session.js - + +Demo for Session.js + + -Session.js Demo Page
-Demo output:
-+Session.js Demo Page
+Demo output:
+loading...- - - + + - + + diff --git a/session.js b/session.js index 0fecd79..2253ca9 100644 --- a/session.js +++ b/session.js @@ -6,467 +6,590 @@ * This version uses google's jsapi library for location services. * For details, see: https://github.com/codejoust/session.js */ -var session_fetch = (function(win, doc, nav){ - 'use strict'; - // Changing the API Version invalidates olde cookies with previous api version tags. - var API_VERSION = 0.4; - // Settings: defaults - var options = { - // Use the HTML5 Geolocation API - // this ONLY returns lat & long, no city/address - use_html5_location: false, - // Attempts to use IPInfoDB if provided a valid key - // Get a key at http://ipinfodb.com/register.php - ipinfodb_key: false, - // Leaving true allows for fallback for both - // the HTML5 location and the IPInfoDB - gapi_location: true, - // Name of the location cookie (set blank to disable cookie) - // - WARNING: different providers use the same cookie - // - if switching providers, remember to use another cookie or provide checks for old cookies - location_cookie: "location", - // Location cookie expiration in hours - location_cookie_timeout: 5, - // Session expiration in days - session_timeout: 32, - // Session cookie name (set blank to disable cookie) - session_cookie: "first_session", - get_object: null, set_object: null // used for cookie session adaptors - // if null, will be reset to use cookies by default. - }; - - // Session object - var SessionRunner = function(){ - win.session = win.session || {}; - // Helper for querying. - // Usage: session.current_session.referrer_info.hostname.contains(['github.com','news.ycombinator.com']) - win.session.contains = function(other_str){ - if (typeof(other_str) === 'string'){ - return (this.indexOf(other_str) !== -1); } - for (var i = 0; i < other_str.length; i++){ - if (this.indexOf(other_str[i]) !== -1){ return true; } } - return false; }; - // Merge options - if (win.session && win.session.options) { - for (var option in win.session.options){ - options[option] = win.session.options[option]; } - } - // Modules to run - // If the module has arguments, - // it _needs_ to return a callback function. - var unloaded_modules = { - api_version: API_VERSION, - locale: modules.locale(), - current_session: modules.session(), - original_session: modules.session( - options.session_cookie, - options.session_timeout * 24 * 60 * 60 * 1000), - browser: modules.browser(), - plugins: modules.plugins(), - time: modules.time(), - device: modules.device() +let session_fetch = (function (win, doc, nav) { + 'use strict'; + // Changing the API Version invalidates old cookies with previous api version tags. + let API_VERSION = 0.4; + // Settings: defaults + let options = { + // Use the HTML5 Geolocation API + // this ONLY returns lat & long, no city/address + use_html5_location: false, + // Attempts to use IPInfoDB if provided a valid key + // Get a key at https://ipinfodb.com/register.php + ipinfodb_key: false, + // Leaving true allows for fallback for both + // the HTML5 location and the IPInfoDB + gapi_location: true, + // Name of the location cookie (set blank to disable cookie) + // - WARNING: different providers use the same cookie + // - if switching providers, remember to use another cookie or provide checks for old cookies + location_cookie: 'location', + // Location cookie expiration in hours + location_cookie_timeout: 5, + // Session expiration in days + session_timeout: 32, + // Session cookie name (set blank to disable cookie) + session_cookie: 'session-js', + get_object: null, set_object: null, // used for cookie session adaptors + // if null, will be reset to use cookies by default. + // Tracker ID: initialize with a random string so that repeated visits are + // easily tracked + tracker_id: null, + // Set to true to record extra client data + extra: false, }; - // Location switch - if (options.use_html5_location){ - unloaded_modules.location = modules.html5_location(); - } else if (options.ipinfodb_key){ - unloaded_modules.location = modules.ipinfodb_location(options.ipinfodb_key); - } else if (options.gapi_location){ - unloaded_modules.location = modules.gapi_location(); - } - // Cache win.session.start - if (win.session && win.session.start){ - var start = win.session.start; - } - // Set up checking, if all modules are ready - var asynchs = 0, module, result, - check_asynch = function(deinc){ - if (deinc){ asynchs--; } - if (asynchs === 0){ - // Run start calback - if (start){ start(win.session); } - } - }; - win.session = {}; - // Run asynchronous methods - for (var name in unloaded_modules){ - module = unloaded_modules[name]; - if (typeof module === "function"){ - try { - module(function(data){ - win.session[name] = data; - check_asynch(true); - }); - asynchs++; - } catch(err){ - if (win.console && typeof(console.log) === "function"){ - console.log(err); check_asynch(true); } - } - } else { - win.session[name] = module; - } } - check_asynch(); - }; - - // Browser (and OS) detection - var browser = { - detect: function(){ - var ret = { - browser: this.search(this.data.browser), - version: this.search(nav.userAgent) || this.search(nav.appVersion), - os: this.search(this.data.os) - }; - if (ret.os=='Linux'){ - var distros = ['CentOS','Debian','Fedora','Gentoo','Mandriva','Mageia','Red Hat','Slackware','SUSE','Turbolinux','Ubuntu']; - for (var i = 0; i < distros.length;i++){ - if (nav.userAgent.toLowerCase().match(distros[i].toLowerCase())){ - ret.distro = distros[i]; - break; - } - } - } - return ret; - }, - search: function(data) { - if (typeof data === "object"){ - // search for string match - for(var i = 0; i < data.length; i++) { - var dataString = data[i].string, - dataProp = data[i].prop; - this.version_string = data[i].versionSearch || data[i].identity; - if (dataString){ - if (dataString.indexOf(data[i].subString) != -1){ - return data[i].identity; + // Session object + let SessionRunner = function () { + win.session = win.session || {}; + // Helper for querying. + // Usage: session.current_session.referrer_info.hostname.contains(['github.com','news.ycombinator.com']) + win.session.contains = function (other_str) { + if (typeof (other_str) === 'string') { + return (this.indexOf(other_str) !== -1); + } + for (let i = 0; i < other_str.length; i++) { + if (this.indexOf(other_str[i]) !== -1) { + return true; + } + } + return false; + }; + // Merge options + if (win.session && win.session.options) { + for (let option in win.session.options) { + options[option] = win.session.options[option]; } - } else if (dataProp){ - return data[i].identity; - } } - } else { - // search for version number - var index = data.indexOf(this.version_string); - if (index == -1) return; - return parseFloat(data.substr(index + this.version_string.length + 1)); - } - }, - data: { - browser: [ - { string: nav.userAgent, subString: "Edge", identity: "Edge"}, - { string: nav.userAgent, subString: "Chrome", identity: "Chrome" }, - { string: nav.userAgent, subString: "OmniWeb", versionSearch: "OmniWeb/", identity: "OmniWeb" }, - { string: nav.vendor, subString: "Apple", identity: "Safari", versionSearch: "Version" }, - { prop: win.opera, identity: "Opera", versionSearch: "Version" }, - { string: nav.vendor, subString: "iCab",identity: "iCab" }, - { string: nav.vendor, subString: "KDE", identity: "Konqueror" }, - { string: nav.userAgent, subString: "Firefox", identity: "Firefox" }, - { string: nav.vendor, subString: "Camino", identity: "Camino" }, - { string: nav.userAgent, subString: "Netscape", identity: "Netscape" }, - { string: nav.userAgent, subString: "MSIE", identity: "Explorer", versionSearch: "MSIE" }, - { string: nav.userAgent, subString: "Trident", identity: "Explorer", versionSearch: "rv" }, - { string: nav.userAgent, subString: "Gecko", identity: "Mozilla", versionSearch: "rv" }, - { string: nav.userAgent, subString: "Mozilla", identity: "Netscape", versionSearch: "Mozilla" } - ], - os: [ - { string: nav.platform, subString: "Win", identity: "Windows" }, - { string: nav.platform, subString: "Mac", identity: "Mac" }, - { string: nav.userAgent, subString: "iPhone", identity: "iPhone/iPod" }, - { string: nav.userAgent, subString: "iPad", identity: "iPad" }, - { string: nav.userAgent, subString: "Android", identity: "Android" }, - { string: nav.platform, subString: "Linux", identity: "Linux" } - ]} - }; - - var modules = { - browser: function(){ - return browser.detect(); - }, - time: function(){ - // split date and grab timezone estimation. - // timezone estimation: http://www.onlineaspect.com/2007/06/08/auto-detect-a-time-zone-with-javascript/ - var d1 = new Date(), d2 = new Date(); - d1.setMonth(0); d1.setDate(1); d2.setMonth(6); d2.setDate(1); - return({tz_offset: -(new Date().getTimezoneOffset()) / 60, observes_dst: (d1.getTimezoneOffset() !== d2.getTimezoneOffset()) }); - // Gives a browser estimation, not guaranteed to be correct. - }, - locale: function() { - var lang = (( - nav.language || - nav.browserLanguage || - nav.systemLanguage || - nav.userLanguage - ) || '').split("-"); - if (lang.length == 2){ - return { country: lang[1].toLowerCase(), lang: lang[0].toLowerCase() }; - } else if (lang) { - return {lang: lang[0].toLowerCase(), country: null }; - } else { return{lang: null, country: null }; } - }, - device: function() { - var device = { - screen: { - width: win.screen.width, - height: win.screen.height + // Modules to run + // If the module has arguments, + // it _needs_ to return a callback function. + let unloaded_modules = { + api_version: API_VERSION, + locale: modules.locale(), + current_session: modules.session( + null, + null, + options.tracker_id, + ), + original_session: modules.session( + options.session_cookie, + options.session_timeout * 24 * 60 * 60 * 1000, + options.tracker_id, + ), + browser: modules.browser(), + plugins: modules.plugins(), + time: modules.time(), + device: modules.device(), + architecture: modules.architecture(), + }; + // Location switch + if (options.use_html5_location) { + unloaded_modules.location = modules.html5_location(); + } else if (options.ipinfodb_key) { + unloaded_modules.location = modules.ipinfodb_location(options.ipinfodb_key); + } else if (options.gapi_location) { + unloaded_modules.location = modules.gapi_location(); } - }; - var width, height; - try{ width = win.innerWidth || doc.documentElement.clientWidth || doc.body.clientWidth; } catch(e) { width = 0; } - try{ height = win.innerHeight || doc.documentElement.clientHeight || doc.body.clientHeight; } catch(e) { height = 0; } - device.viewport = { - width: width, - height: height - }; - device.is_tablet = !!nav.userAgent.match(/(iPad|SCH-I800|xoom|kindle)/i); - device.is_phone = !device.is_tablet && !!nav.userAgent.match(/(iPhone|iPod|blackberry|android|htc|lg|midp|mmp|mobile|nokia|opera mini|palm|pocket|psp|sgh|smartphone|symbian|treo mini|Playstation Portable|SonyEricsson|Samsung|MobileExplorer|PalmSource|Benq|Windows Phone|Windows Mobile|IEMobile|Windows CE|Nintendo Wii)/i); - device.is_mobile = device.is_tablet || device.is_phone; - return device; - }, - plugins: function(){ - var check_plugin = function(name){ - if (nav.plugins){ - var plugin, i = 0, length = nav.plugins.length; - for (; i < length; i++ ){ - plugin = nav.plugins[i]; - if (plugin && plugin.name && plugin.name.toLowerCase().indexOf(name) !== -1){ - return true; - } } - return false; - } return false; - }; - var check_activex_flash = function(versions){ - var found = true; - for (var i = 0; i < versions.length; i++){ - try { - var obj = new ActiveXObject("ShockwaveFlash.ShockwaveFlash" + versions[i]) - , found = !0; - } catch (e){ /* nil */ } - if (found) return true; + // Extra switch + if (options.extra) { + unloaded_modules.extra = modules.extra_data() } - return false; - } - return { - flash: check_plugin("flash") || check_activex_flash(['.7','.6','']), - silverlight: check_plugin("silverlight"), - java: check_plugin("java"), - quicktime: check_plugin("quicktime") - }; - }, - session: function (cookie, expires){ - var session = util.get_obj(cookie); - if (session == null){ - session = { - visits: 1, - start: new Date().getTime(), last_visit: new Date().getTime(), - url: win.location.href, path: win.location.pathname, - referrer: doc.referrer, referrer_info: util.parse_url(doc.referrer), - search: { engine: null, query: null } + // Cache win.session.start + let start; + if (win.session && win.session.start) { + start = win.session.start; + } + // Set up checking, if all modules are ready + let asynchs = 0; + let module; + let check_asynch = function (deinc) { + if (deinc) { + asynchs--; + } + if (asynchs === 0) { + // Run start calback + if (start) { + start(win.session); + } + } }; - var search_engines = [ - { name: "Google", host: "google", query: "q" }, - { name: "Bing", host: "bing.com", query: "q" }, - { name: "Yahoo", host: "search.yahoo", query: "p" }, - { name: "AOL", host: "search.aol", query: "q" }, - { name: "Ask", host: "ask.com", query: "q" }, - { name: "Baidu", host: "baidu.com", query: "wd" } - ], length = search_engines.length, - engine, match, i = 0, - fallbacks = 'q query term p wd query text'.split(' '); - for (i = 0; i < length; i++){ - engine = search_engines[i]; - if (session.referrer_info.host.indexOf(engine.host) !== -1){ - session.search.engine = engine.name; - session.search.query = session.referrer_info.query[engine.query]; - session.search.terms = session.search.query ? session.search.query.split(" ") : null; - break; - } + win.session = {}; + // Run asynchronous methods + for (let name in unloaded_modules) { + module = unloaded_modules[name]; + if (typeof module === 'function') { + try { + module(function (data) { + win.session[name] = data; + check_asynch(true); + }); + asynchs++; + } catch (err) { + if (win.console && typeof (console.log) === 'function') { + console.log(err); + check_asynch(true); + } + } + } else { + win.session[name] = module; + } } - if (session.search.engine === null && session.referrer_info.search.length > 1){ - for (i = 0; i < fallbacks.length; i++){ - var terms = session.referrer_info.query[fallbacks[i]]; - if (terms){ - session.search.engine = "Unknown"; - session.search.query = terms; session.search.terms = terms.split(" "); - break; + check_asynch(); + }; + + + // Browser (and OS) detection + // noinspection JSUnresolvedVariable + let browser = { + detect: function () { + let ret = { + browser: this.search(this.data.browser), + version: this.search(nav.userAgent) || this.search(nav.appVersion), + os: this.search(this.data.os) + }; + if (ret.os === 'Linux') { + let distros = ['CentOS', 'Debian', 'Fedora', 'Gentoo', 'Mandriva', 'Mageia', 'Red Hat', 'Slackware', 'SUSE', 'Turbolinux', 'Ubuntu']; + for (let i = 0; i < distros.length; i++) { + if (nav.userAgent.toLowerCase().match(distros[i].toLowerCase())) { + ret.distro = distros[i]; + break; + } + } + } + return ret; + }, + search: function (data) { + if (typeof data === 'object') { + // search for string match + for (let i = 0; i < data.length; i++) { + let dataString = data[i].string, + dataProp = data[i].prop; + this.version_string = data[i].versionSearch || data[i].identity; + if (dataString) { + if (dataString.indexOf(data[i].subString) !== -1) { + return data[i].identity; + } + } else if (dataProp) { + return data[i].identity; + } + } + } else { + // search for version number + let index = data.indexOf(this.version_string); + if (index === -1) return; + return parseFloat(data.substr(index + this.version_string.length + 1)); } - } + }, + data: { + browser: [ + {string: nav.userAgent, subString: 'Edge', identity: 'Edge'}, + {string: nav.userAgent, subString: 'Chrome', identity: 'Chrome'}, + {string: nav.userAgent, subString: 'OmniWeb', versionSearch: 'OmniWeb/', identity: 'OmniWeb'}, + {string: nav.vendor, subString: 'Apple', identity: 'Safari', versionSearch: 'Version'}, + {prop: win.opera, identity: 'Opera', versionSearch: 'Version'}, + {string: nav.vendor, subString: 'iCab', identity: 'iCab'}, + {string: nav.vendor, subString: 'KDE', identity: 'Konqueror'}, + {string: nav.userAgent, subString: 'Firefox', identity: 'Firefox'}, + {string: nav.vendor, subString: 'Camino', identity: 'Camino'}, + {string: nav.userAgent, subString: 'Netscape', identity: 'Netscape'}, + {string: nav.userAgent, subString: 'MSIE', identity: 'Explorer', versionSearch: 'MSIE'}, + {string: nav.userAgent, subString: 'Trident', identity: 'Explorer', versionSearch: 'rv'}, + {string: nav.userAgent, subString: 'Gecko', identity: 'Mozilla', versionSearch: 'rv'}, + {string: nav.userAgent, subString: 'Mozilla', identity: 'Netscape', versionSearch: 'Mozilla'}, + ], + os: [ + {string: nav.platform, subString: 'Win', identity: 'Windows'}, + {string: nav.platform, subString: 'Mac', identity: 'Mac'}, + {string: nav.userAgent, subString: 'iPhone', identity: 'iPhone/iPod'}, + {string: nav.userAgent, subString: 'iPad', identity: 'iPad'}, + {string: nav.userAgent, subString: 'Android', identity: 'Android'}, + {string: nav.platform, subString: 'Linux', identity: 'Linux'}, + ] } - } else { - session.prev_visit = session.last_visit; - session.last_visit = new Date().getTime(); - session.visits++; - session.time_since_last_visit = session.last_visit - session.prev_visit; - } - util.set_obj(cookie, session, expires); - return session; - }, - html5_location: function(){ - return function(callback){ - nav.geolocation.getCurrentPosition(function(pos){ - pos.source = 'html5'; - callback(pos); - }, function(err) { - if (options.gapi_location){ - modules.gapi_location()(callback); - } else { - callback({error: true, source: 'html5'}); } - }); - }; - }, - gapi_location: function(){ - return function(callback){ - var location = util.get_obj(options.location_cookie); - if (!location || location.source !== 'google'){ - win.gloader_ready = function() { - if ("google" in win){ - if (win.google.loader.ClientLocation){ - win.google.loader.ClientLocation.source = "google"; - callback(win.google.loader.ClientLocation); - } else { - callback({error: true, source: "google"}); - } - util.set_obj( - options.location_cookie, - win.google.loader.ClientLocation, - options.location_cookie_timeout * 60 * 60 * 1000); - }}; - util.embed_script("https://www.google.com/jsapi?callback=gloader_ready"); - } else { - callback(location); - }}; - }, - architecture: function(){ - var arch = n.userAgent.match(/x86_64|Win64|WOW64|x86-64|x64\;|AMD64|amd64/) || - (n.cpuClass === 'x64') ? 'x64' : 'x86'; - return { - arch: arch, - is_x64: arch == 'x64', - is_x86: arch == 'x68' - } - }, - ipinfodb_location: function(api_key){ - return function (callback){ - var location_cookie = util.get_obj(options.location_cookie); - if (!location_cookie && location_cookie.source === 'ipinfodb'){ - win.ipinfocb = function(data){ - if (data.statusCode === "OK"){ - data.source = "ipinfodb"; - util.set_obj( - options.location_cookie, - data, - options.location_cookie * 60 * 60 * 1000); - callback(data); - } else { - if (options.gapi_location){ return modules.gapi_location()(callback); } - else { callback({error: true, source: "ipinfodb", message: data.statusMessage}); } - }}; - util.embed_script("http://api.ipinfodb.com/v3/ip-city/?key=" + api_key + "&format=json&callback=ipinfocb"); - } else { callback(location_cookie); } - }} - }; + }; - // Utilities - var util = { - parse_url: function(url_str){ - var a = doc.createElement("a"), query = {}; - a.href = url_str; var query_str = a.search.substr(1); - // Disassemble query string - if (query_str != ''){ - var pairs = query_str.split("&"), i = 0, - length = pairs.length, parts; - for (; i < length; i++){ - parts = pairs[i].split("="); - if (parts.length === 2){ - query[parts[0]] = decodeURI(parts[1]); } + let modules = { + browser: function () { + return browser.detect(); + }, + time: function () { + // split date and grab timezone estimation. + // timezone estimation: https://www.onlineaspect.com/2007/06/08/auto-detect-a-time-zone-with-javascript/ + let d1 = new Date(), d2 = new Date(); + d1.setMonth(0); + d1.setDate(1); + d2.setMonth(6); + d2.setDate(1); + return ({ + tz_offset: -(new Date().getTimezoneOffset()) / 60, + observes_dst: (d1.getTimezoneOffset() !== d2.getTimezoneOffset()) + }); + // Gives a browser estimation, not guaranteed to be correct. + }, + locale: function () { + let lang = (( + nav.language || + (nav.hasOwnProperty('browserLanguage') + ? nav.browserLanguage + : nav.systemLanguage) + ) || '').split('-'); + if (lang.length === 2) { + return {country: lang[1].toLowerCase(), lang: lang[0].toLowerCase()}; + } else if (lang) { + return {lang: lang[0].toLowerCase(), country: null}; + } else { + return {lang: null, country: null}; + } + }, + device: function () { + let device = { + screen: { + width: win.screen.width, + height: win.screen.height + } + }; + let width, height; + try { + width = win.innerWidth || doc.documentElement.clientWidth || doc.body.clientWidth; + } catch (e) { + width = 0; + } + try { + height = win.innerHeight || doc.documentElement.clientHeight || doc.body.clientHeight; + } catch (e) { + height = 0; + } + device.viewport = { + width: width, + height: height + }; + device.is_tablet = !!nav.userAgent.match(/(iPad|SCH-I800|xoom|kindle)/i); + device.is_phone = !device.is_tablet && !!nav.userAgent.match(/(iPhone|iPod|blackberry|android|htc|lg|midp|mmp|mobile|nokia|opera mini|palm|pocket|psp|sgh|smartphone|symbian|treo mini|Playstation Portable|SonyEricsson|Samsung|MobileExplorer|PalmSource|Benq|Windows Phone|Windows Mobile|IEMobile|Windows CE|Nintendo Wii)/i); + device.is_mobile = device.is_tablet || device.is_phone; + return device; + }, + plugins: function () { + let check_plugin = function (name) { + if (nav.plugins) { + let plugin, i = 0, length = nav.plugins.length; + for (; i < length; i++) { + plugin = nav.plugins[i]; + if (plugin && plugin.name && plugin.name.toLowerCase().indexOf(name) !== -1) { + return true; + } + } + return false; + } + return false; + }; + let check_activex_flash = function (versions) { + let found = false; + for (let i = 0; i < versions.length; i++) { + try { + new ActiveXObject('ShockwaveFlash.ShockwaveFlash' + versions[i]); + found = true; + break; + } catch (e) { /* nil */ + } + } + return found; + }; + return { + flash: check_plugin('flash') || check_activex_flash(['.7', '.6', '']), + silverlight: check_plugin('silverlight'), + java: check_plugin('java'), + quicktime: check_plugin('quicktime') + }; + }, + session: function (cookie, expires, tracker = null) { + let session = util.get_obj(cookie); + if (session == null) { + session = { + tracker: tracker, + visits: 1, + start: new Date().getTime(), last_visit: new Date().getTime(), + url: win.location.href, path: win.location.pathname, + referrer: doc.referrer, referrer_info: util.parse_url(doc.referrer), + search: {engine: null, query: null}, + }; + let search_engines = [ + {name: 'Google', host: 'google', query: 'q'}, + {name: 'Bing', host: 'bing.com', query: 'q'}, + {name: 'Yahoo', host: 'search.yahoo', query: 'p'}, + {name: 'AOL', host: 'search.aol', query: 'q'}, + {name: 'Ask', host: 'ask.com', query: 'q'}, + {name: 'Baidu', host: 'baidu.com', query: 'wd'}, + ]; + let length = search_engines.length; + let engine; + let fallbacks = 'q query term p wd query text'.split(' '); + for (let i = 0; i < length; i++) { + engine = search_engines[i]; + if (session.referrer_info.host.indexOf(engine.host) !== -1) { + session.search.engine = engine.name; + session.search.query = session.referrer_info.query[engine.query]; + session.search.terms = session.search.query ? session.search.query.split(' ') : null; + break; + } + } + if (session.search.engine === null && session.referrer_info.search.length > 1) { + for (let i = 0; i < fallbacks.length; i++) { + let terms = session.referrer_info.query[fallbacks[i]]; + if (terms) { + session.search.engine = 'Unknown'; + session.search.query = terms; + session.search.terms = terms.split(' '); + break; + } + } + } + } else { + // Could've been initialized w/o id, so check and set + if (tracker && !session.tracker) { + session.tracker = tracker; + } + session.prev_visit = session.last_visit; + session.last_visit = new Date().getTime(); + session.visits++; + session.time_since_last_visit = session.last_visit - session.prev_visit; + } + util.set_obj(cookie, session, expires); + return session; + }, + html5_location: function () { + return function (callback) { + nav.geolocation.getCurrentPosition(function (pos) { + pos.source = 'html5'; + callback(pos); + }, function (err) { + if (options.gapi_location) { + modules.gapi_location()(callback); + } else { + callback({ + error: true, + source: 'html5', + message: err.message, + }); + } + }); + }; + }, + gapi_location: function () { + return function (callback) { + let location = util.get_obj(options.location_cookie); + if (!location || location.source !== 'google') { + win.gloader_ready = function () { + if ('google' in win + && win.google.hasOwnProperty('loader') + && win.google.loader.hasOwnProperty('ClientLocation') + ) { + if (win.google.loader.ClientLocation) { + win.google.loader.ClientLocation.source = 'google'; + callback(win.google.loader.ClientLocation); + } else { + callback({error: true, source: 'google'}); + } + // noinspection JSUnresolvedVariable + util.set_obj( + options.location_cookie, + win.google.loader.ClientLocation, + options.location_cookie_timeout * 60 * 60 * 1000, + ); + } + }; + util.embed_script('https://www.google.com/jsapi?callback=gloader_ready'); + } else { + callback(location); + } + }; + }, + ipinfodb_location: function (api_key) { + return function (callback) { + let location_cookie = util.get_obj(options.location_cookie); + if (!location_cookie && location_cookie.source === 'ipinfodb') { + win.ipinfocb = function (data) { + if (data.statusCode === 'OK') { + data.source = 'ipinfodb'; + util.set_obj( + options.location_cookie, + data, + options.location_cookie * 60 * 60 * 1000); + callback(data); + } else { + if (options.gapi_location) { + return modules.gapi_location()(callback); + } else { + callback({error: true, source: 'ipinfodb', message: data.statusMessage}); + } + } + }; + util.embed_script('https://api.ipinfodb.com/v3/ip-city/?key=' + api_key + '&format=json&callback=ipinfocb'); + } else { + callback(location_cookie); + } + } + }, + architecture: function () { + let arch = nav.userAgent.match(/x86_64|Win64|WOW64|x86-64|x64;|AMD64|amd64/) || + (nav.hasOwnProperty('cpuClass') && nav.cpuClass === 'x64') ? 'x64' : 'x86'; + return { + arch: arch, + is_x64: arch === 'x64', + is_x86: arch === 'x68', + } + }, + extra_data: function () { + let nav = {}; + for (let obj in navigator) { + if (typeof (navigator[obj]) === 'string') { + nav[obj] = navigator[obj]; + } + } + return { + navigator: nav, + document: { + referrer: document.referrer, + clientWidth: document.documentElement.clientWidth, + clientHeight: document.documentElement.clientHeight, + }, + window: { + innerWidth: window.innerWidth, + innerHeight: window.innerHeight, + location: window.location.href, + }, + } } - } - return { - host: a.host, - path: a.pathname, - protocol: a.protocol, - port: a.port === '' ? 80 : a.port, - search: a.search, - query: query } - }, - set_cookie: function(cname, value, expires, options){ // from jquery.cookie.js - if (!cname){ return null; } - if (!options){ options = {}; } - if (value === null || value === undefined){ expires = -1; } - if (expires){ options.expires = (new Date().getTime()) + expires; } - return (doc.cookie = [ - encodeURIComponent(cname), '=', - encodeURIComponent(String(value)), - options.expires ? '; expires=' + new Date(options.expires).toUTCString() : '', // use expires attribute, max-age is not supported by IE - '; path=' + (options.path ? options.path : '/'), - options.domain ? '; domain=' + options.domain : '', - (win.location && win.location.protocol === 'https:') ? '; secure' : '' - ].join('')); - }, - get_cookie: function(cookie_name, result){ // from jquery.cookie.js - return (result = new RegExp('(?:^|; )' + encodeURIComponent(cookie_name) + '=([^;]*)').exec(doc.cookie)) ? decodeURIComponent(result[1]) : null; - }, - embed_script: function(url){ - var element = doc.createElement("script"); - element.type = "text/javascript"; - element.src = url; - (doc.body || doc.getElementsByTagName("body")[0] || doc.head).appendChild(element); - }, - package_obj: function (obj){ - if(obj) { - obj.version = API_VERSION; - var ret = JSON.stringify(obj); - delete obj.version; - return ret; - } - }, - set_obj: function(cname, value, expires, options){ - util.set_cookie(cname, util.package_obj(value), expires, options); - }, - get_obj: function(cookie_name){ - var obj; - try { obj = JSON.parse(util.get_cookie(cookie_name)); } catch(e){} - if (obj && obj.version == API_VERSION){ - delete obj.version; return obj; - } - } - }; + }; + + // Utilities + let util = { + parse_url: function (url_str) { + let a = doc.createElement('a'), query = {}; + a.href = url_str; + let query_str = a.search.substr(1); + // Disassemble query string + if (query_str !== '') { + let pairs = query_str.split('&'), i = 0, + length = pairs.length, parts; + for (; i < length; i++) { + parts = pairs[i].split('='); + if (parts.length === 2) { + query[parts[0]] = decodeURI(parts[1]); + } + } + } + return { + host: a.host, + path: a.pathname, + protocol: a.protocol, + port: a.port === '' ? 80 : a.port, + search: a.search, + query: query + } + }, + set_cookie: function (cname, value, expires, options) { // from jquery.cookie.js + if (!cname) { + return null; + } + if (!options) { + options = {}; + } + if (value === null || value === undefined) { + expires = -1; + } + if (expires) { + options.expires = (new Date().getTime()) + expires; + } + return (doc.cookie = [ + encodeURIComponent(cname), '=', + encodeURIComponent(String(value)), + options.expires ? '; expires=' + new Date(options.expires).toUTCString() : '', // use expires attribute, max-age is not supported by IE + '; path=' + (options.path ? options.path : '/'), + options.domain ? '; domain=' + options.domain : '', + (win.location && win.location.protocol === 'https:') ? '; secure' : '' + ].join('')); + }, + get_cookie: function (cookie_name, result) { // from jquery.cookie.js + return (result = new RegExp('(?:^|; )' + encodeURIComponent(cookie_name) + '=([^;]*)').exec(doc.cookie)) ? decodeURIComponent(result[1]) : null; + }, + embed_script: function (url) { + let element = doc.createElement('script'); + element.type = 'text/javascript'; + element.src = url; + (doc.body || doc.getElementsByTagName('body')[0] || doc.head).appendChild(element); + }, + package_obj: function (obj) { + if (obj) { + obj.version = API_VERSION; + let ret = JSON.stringify(obj); + delete obj.version; + return ret; + } + }, + set_obj: function (cname, value, expires, options) { + util.set_cookie(cname, util.package_obj(value), expires, options); + }, + get_obj: function (cookie_name) { + let obj; + try { + obj = JSON.parse(util.get_cookie(cookie_name)); + } catch (e) { + console.log(e); + } + if (obj && obj.version === API_VERSION) { + delete obj.version; + return obj; + } + } + }; - // cookie options override - if (options.get_object != null){ - util.get_obj = options['get_object']; } - if (options.set_object != null){ - util.set_obj = options['set_object']; } + // cookie options override + if (options.get_object != null) { + util.get_obj = options['get_object']; + } + if (options.set_object != null) { + util.set_obj = options['set_object']; + } - // JSON - var JSON = { - parse: (win.JSON && win.JSON.parse) || function(data){ - if (typeof data !== "string" || !data){ return null; } - return (new Function("return " + data))(); - }, - stringify: (win.JSON && win.JSON.stringify) || function(object){ - var type = typeof object; - if (type !== "object" || object === null) { - if (type === "string"){ return '"' + object + '"'; } - } else { - var k, v, json = [], - isArray = (object && object.constructor === Array); - for (k in object ) { - v = object[k]; type = typeof v; - if (type === "string") - v = '"' + v + '"'; - else if (type === "object" && v !== null) - v = this.stringify(v); - json.push((isArray ? "" : '"' + k + '":') + v); + // JSON + let JSON = { + parse: (win.hasOwnProperty('JSON') && win.JSON && win.JSON.parse) || function (data) { + if (typeof data !== 'string' || !data) { + return null; + } + return (new Function('return ' + data))(); + }, + stringify: (win.hasOwnProperty('JSON') && win.JSON && win.JSON.stringify) || function (object) { + let type = typeof object; + if (type !== 'object' || object === null) { + if (type === 'string') { + return '"' + object + '"'; + } + } else { + let k, v, json = [], isArray = (object && object.constructor === Array); + for (k in object) { + v = object[k]; + type = typeof v; + if (type === 'string') + v = '"' + v + '"'; + else if (type === 'object' && v !== null) + v = this.stringify(v); + json.push((isArray ? "" : '"' + k + '":') + v); + } + return (isArray ? '[' : '{') + json.join(',') + (isArray ? ']' : '}'); + } } - return (isArray ? "[" : "{") + json.join(",") + (isArray ? "]" : "}"); - } } }; + }; - // Initialize SessionRunner - SessionRunner(); + // Initialize SessionRunner + SessionRunner(); }); // Switch for testing purposes. -if (typeof(window.exports) === 'undefined'){ - session_fetch(window, document, navigator); +if (typeof (window.exports) === 'undefined') { + session_fetch(window, document, navigator); } else { - window.exports.session = session_fetch; + window.exports.session = session_fetch; } diff --git a/session.min.js b/session.min.js index 85b992c..8899030 100644 --- a/session.min.js +++ b/session.min.js @@ -1 +1 @@ -var session_fetch=function(win,doc,nav){"use strict";var API_VERSION=.4;var options={use_html5_location:false,ipinfodb_key:false,gapi_location:true,location_cookie:"location",location_cookie_timeout:5,session_timeout:32,session_cookie:"first_session",get_object:null,set_object:null};var SessionRunner=function(){win.session=win.session||{};win.session.contains=function(other_str){if(typeof other_str==="string"){return this.indexOf(other_str)!==-1}for(var i=0;i1){for(i=0;i 1){for(let i=0;i