From 3be88105e56eb9ee82f05b50ce689f9313590fb9 Mon Sep 17 00:00:00 2001 From: Raivo Laanemets Date: Fri, 14 Dec 2018 08:43:10 +0200 Subject: [PATCH] Rename analytics script to readers.min.js. (#173) --- analytics/{visitor.js => readers.js} | 16 ++++++++-------- package.json | 2 +- prolog/bc/bc_api_analytics.pl | 19 +++++++++++-------- prolog/bc/public/js/readers.min.js | 2 ++ prolog/bc/public/js/readers.min.js.map | 1 + webpack.js => webpack.readers.js | 4 ++-- 6 files changed, 25 insertions(+), 19 deletions(-) rename analytics/{visitor.js => readers.js} (89%) create mode 100644 prolog/bc/public/js/readers.min.js create mode 100644 prolog/bc/public/js/readers.min.js.map rename webpack.js => webpack.readers.js (85%) diff --git a/analytics/visitor.js b/analytics/readers.js similarity index 89% rename from analytics/visitor.js rename to analytics/readers.js index 5694d31..91b2812 100644 --- a/analytics/visitor.js +++ b/analytics/readers.js @@ -4,7 +4,7 @@ // Reads the previously stored user id. const readUserId = () => { - const match = document.cookie.match(/visitor_user\s*=\s*([a-z0-9\-]+)/); + const match = document.cookie.match(/readers_user\s*=\s*([a-z0-9\-]+)/); return match ? match[1] : null; }; @@ -12,16 +12,16 @@ const writeUserId = (userId) => { const expires = new Date(); expires.setFullYear(expires.getFullYear() + 1); - document.cookie = 'visitor_user=' + userId + '; path=/; expires=' + expires.toUTCString(); + document.cookie = 'readers_user=' + userId + '; path=/; expires=' + expires.toUTCString(); }; const readSessionId = () => { - const match = document.cookie.match(/visitor_session\s*=\s*([a-z0-9\-]+)/); + const match = document.cookie.match(/readers_session\s*=\s*([a-z0-9\-]+)/); return match ? match[1] : null; }; const writeSessionId = (sessionId) => { - document.cookie = 'visitor_session=' + sessionId + '; path=/'; + document.cookie = 'readers_session=' + sessionId + '; path=/'; }; // Helper to run HTTP POST request to the backend. @@ -50,7 +50,7 @@ cb(userId); } else { // Record the new user. - postJSON('/api/visitor/user', {}, (response) => { + postJSON('/api/readers/user', {}, (response) => { if (response.status === 'success') { const userId = response.data; // Remember for later. @@ -76,7 +76,7 @@ agent: navigator.userAgent || null, platform: navigator.platform || null }; - postJSON('/api/visitor/session', data, (response) => { + postJSON('/api/readers/session', data, (response) => { if (response.status === 'success') { const sessionId = response.data; // Remember for later. @@ -97,7 +97,7 @@ pageview_id: pageviewId, elapsed: elapsed } - postJSON('/api/visitor/pageview_extend', data, (response) => { + postJSON('/api/readers/pageview_extend', data, (response) => { if (response.status === 'success') { cb(userId, sessionId); } @@ -110,7 +110,7 @@ elapsed: elapsed }; // First pageview record. - postJSON('/api/visitor/pageview', data, (response) => { + postJSON('/api/readers/pageview', data, (response) => { if (response.status === 'success') { // Update the current pageview id. pageviewId = response.data; diff --git a/package.json b/package.json index ac8925f..22b5637 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "webpack-cli": "^3.1.2" }, "scripts": { - "build-analytics": "webpack --mode production --config webpack.js", + "build-readers": "webpack --mode production --config webpack.readers.js", "build-admin": "webpack --mode production --config webpack.admin.js", "build-admin-watch": "webpack --watch --mode production --config webpack.admin.js" } diff --git a/prolog/bc/bc_api_analytics.pl b/prolog/bc/bc_api_analytics.pl index 36a3872..26b763a 100644 --- a/prolog/bc/bc_api_analytics.pl +++ b/prolog/bc/bc_api_analytics.pl @@ -16,50 +16,52 @@ :- use_module(bc_analytics). :- use_module(bc_analytics_db). -:- route_post(api/visitor/user, record_user). +% Handlers for data from the readers script. + +:- route_post(api/readers/user, record_user). record_user:- bc_read_by_schema(bc_analytics_user, User), bc_record_user(User, UserId), bc_reply_success(UserId). -:- route_post(api/visitor/session, record_session). +:- route_post(api/readers/session, record_session). record_session:- bc_read_by_schema(bc_analytics_session, Session), bc_record_session(Session, SessionId), bc_reply_success(SessionId). -:- route_post(api/visitor/pageview, record_pageview). +:- route_post(api/readers/pageview, record_pageview). record_pageview:- bc_read_by_schema(bc_analytics_pageview, Pageview), bc_record_pageview(Pageview, PageviewId), bc_reply_success(PageviewId). -:- route_post(api/visitor/pageview_extend, record_pageview_extend). +:- route_post(api/readers/pageview_extend, record_pageview_extend). record_pageview_extend:- bc_read_by_schema(bc_analytics_pageview_extend, Pageview), bc_record_pageview_extend(Pageview), bc_reply_success(true). -:- route_get(bc/'visitor.min.js', visitor_script). +:- route_get(bc/'readers.min.js', visitor_script). visitor_script:- http_current_request(Request), module_property(bc_api_analytics, file(File)), file_directory_name(File, Dir), - atom_concat(Dir, '/public/js/visitor.min.js', Path), + atom_concat(Dir, '/public/js/readers.min.js', Path), http_reply_file(Path, [unsafe(true)], Request). -:- route_get(bc/'visitor.min.js.map', visitor_script_map). +:- route_get(bc/'readers.min.js.map', visitor_script_map). visitor_script_map:- http_current_request(Request), module_property(bc_api_analytics, file(File)), file_directory_name(File, Dir), - atom_concat(Dir, '/public/js/visitor.min.js.map', Path), + atom_concat(Dir, '/public/js/readers.min.js.map', Path), http_reply_file(Path, [unsafe(true)], Request). :- register_schema(bc_analytics_user, _{ @@ -100,6 +102,7 @@ % Analytic timeseries results for the administration API. +% TODO: add auth :- route_get(api/analytics/timeseries/From/To/Duration, analytics_timeseries(From, To, Duration)). diff --git a/prolog/bc/public/js/readers.min.js b/prolog/bc/public/js/readers.min.js new file mode 100644 index 0000000..20b9163 --- /dev/null +++ b/prolog/bc/public/js/readers.min.js @@ -0,0 +1,2 @@ +!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t){var n,r,o,a,u,s,i,c;a=Date.now(),u=null,s=function(e,t,n){if("undefined"!=typeof XMLHttpRequest&&"undefined"!=typeof JSON){var r=new XMLHttpRequest;r.open("POST",e),r.setRequestHeader("Content-Type","application/json"),r.send(JSON.stringify(t)),r.onreadystatechange=function(){4===r.readyState&&200===r.status&&n(JSON.parse(r.responseText))}}},i=function(e,t){var n,r=(n=document.cookie.match(/readers_session\s*=\s*([a-z0-9\-]+)/))?n[1]:null;if(r)t(e,r);else{var o={user_id:e,agent:navigator.userAgent||null,platform:navigator.platform||null};s("/api/readers/session",o,function(n){if("success"===n.status){var r=n.data;o=r,document.cookie="readers_session="+o+"; path=/",t(e,r)}var o})}},c=function e(t,n,r){!function(e,t,n){var r=Math.floor((Date.now()-a)/1e3);if(u)s("/api/readers/pageview_extend",{pageview_id:u,elapsed:r},function(r){"success"===r.status&&n(e,t)});else{var o={session_id:t,location:window.location.toString(),referrer:document.referrer||null,elapsed:r};s("/api/readers/pageview",o,function(r){"success"===r.status&&(u=r.data,n(e,t))})}}(n,n,function(){return setTimeout(function(){return e(t,n,2*r)},r)})},n=function(e){i(e,function(e,t){c(e,t,1e3)})},(o=(r=document.cookie.match(/readers_user\s*=\s*([a-z0-9\-]+)/))?r[1]:null)?n(o):s("/api/readers/user",{},function(e){if("success"===e.status){var t=e.data;r=t,(o=new Date).setFullYear(o.getFullYear()+1),document.cookie="readers_user="+r+"; path=/; expires="+o.toUTCString(),n(t)}var r,o})}]); +//# sourceMappingURL=readers.min.js.map \ No newline at end of file diff --git a/prolog/bc/public/js/readers.min.js.map b/prolog/bc/public/js/readers.min.js.map new file mode 100644 index 0000000..903a790 --- /dev/null +++ b/prolog/bc/public/js/readers.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./analytics/readers.js"],"names":["installedModules","__webpack_require__","moduleId","exports","module","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s","cb","match","userId","startTimestamp","pageviewId","postJSON","doWithSession","reportPageviewLoop","Date","now","url","data","XMLHttpRequest","JSON","xmlhttp","open","setRequestHeader","send","stringify","onreadystatechange","readyState","status","parse","responseText","sessionId","document","cookie","user_id","agent","navigator","userAgent","platform","response","timeout","elapsed","Math","floor","pageview_id","session_id","location","window","toString","referrer","doWithPageview","setTimeout","expires","setFullYear","getFullYear","toUTCString"],"mappings":"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","file":"readers.min.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","(() => {\n const startTimestamp = Date.now();\n let pageviewId = null;\n\n // Reads the previously stored user id.\n const readUserId = () => {\n const match = document.cookie.match(/readers_user\\s*=\\s*([a-z0-9\\-]+)/);\n return match ? match[1] : null;\n };\n\n // User ids use permanent cookies.\n const writeUserId = (userId) => {\n const expires = new Date();\n expires.setFullYear(expires.getFullYear() + 1);\n document.cookie = 'readers_user=' + userId + '; path=/; expires=' + expires.toUTCString();\n };\n\n const readSessionId = () => {\n const match = document.cookie.match(/readers_session\\s*=\\s*([a-z0-9\\-]+)/);\n return match ? match[1] : null;\n };\n\n const writeSessionId = (sessionId) => {\n document.cookie = 'readers_session=' + sessionId + '; path=/';\n };\n\n // Helper to run HTTP POST request to the backend.\n const postJSON = (url, data, cb) => {\n const supported = typeof XMLHttpRequest !== 'undefined' &&\n typeof JSON !== 'undefined';\n if (supported) {\n const xmlhttp = new XMLHttpRequest();\n xmlhttp.open('POST', url);\n xmlhttp.setRequestHeader('Content-Type', 'application/json');\n xmlhttp.send(JSON.stringify(data));\n xmlhttp.onreadystatechange = () => {\n if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {\n cb(JSON.parse(xmlhttp.responseText));\n }\n };\n }\n };\n\n // Tries to use either the stored user id\n // or generates a new one on the server.\n const doWithUser = (cb) => {\n const userId = readUserId();\n if (userId) {\n // Use the stored user id.\n cb(userId);\n } else {\n // Record the new user.\n postJSON('/api/readers/user', {}, (response) => {\n if (response.status === 'success') {\n const userId = response.data;\n // Remember for later.\n writeUserId(userId);\n // Use the generated user id.\n cb(userId);\n }\n });\n }\n };\n\n // Tries to use either the stored session id\n // or generates a new one on the server.\n const doWithSession = (userId, cb) => {\n const sessionId = readSessionId();\n if (sessionId) {\n // Use the stored session id.\n cb(userId, sessionId);\n } else {\n // Record the new session.\n const data = {\n user_id: userId,\n agent: navigator.userAgent || null,\n platform: navigator.platform || null\n };\n postJSON('/api/readers/session', data, (response) => {\n if (response.status === 'success') {\n const sessionId = response.data;\n // Remember for later.\n writeSessionId(sessionId);\n // Use the generated id.\n cb(userId, sessionId);\n }\n });\n }\n };\n\n // Starts reporting the current page.\n const doWithPageview = (userId, sessionId, cb) => {\n const elapsed = Math.floor((Date.now() - startTimestamp) / 1000);\n if (pageviewId) {\n // Repeated pageview. Just send elapsed time.\n const data = {\n pageview_id: pageviewId,\n elapsed: elapsed\n }\n postJSON('/api/readers/pageview_extend', data, (response) => {\n if (response.status === 'success') {\n cb(userId, sessionId);\n }\n });\n } else {\n const data = {\n session_id: sessionId,\n location: window.location.toString(),\n referrer: document.referrer || null,\n elapsed: elapsed\n };\n // First pageview record.\n postJSON('/api/readers/pageview', data, (response) => {\n if (response.status === 'success') {\n // Update the current pageview id.\n pageviewId = response.data;\n cb(userId, sessionId);\n }\n });\n }\n };\n\n // Reports the current page periodically with\n // exponential interval.\n const reportPageviewLoop = (userId, sessionId, timeout) => {\n doWithPageview(sessionId, sessionId, () =>\n setTimeout(() =>\n reportPageviewLoop(userId, sessionId, 2 * timeout), timeout));\n };\n\n doWithUser((userId) => {\n doWithSession(userId, (userId, sessionId) => {\n reportPageviewLoop(userId, sessionId, 1000);\n });\n }); \n})();\n"],"sourceRoot":""} \ No newline at end of file diff --git a/webpack.js b/webpack.readers.js similarity index 85% rename from webpack.js rename to webpack.readers.js index 848eb5f..166b2a1 100644 --- a/webpack.js +++ b/webpack.readers.js @@ -4,10 +4,10 @@ const assert = require('assert'); // Returns configuration for the given bundle. module.exports = { - entry: path.join(__dirname, 'analytics', 'visitor.js'), + entry: path.join(__dirname, 'analytics', 'readers.js'), output: { path: path.resolve(__dirname, 'prolog', 'bc', 'public', 'js'), - filename: `visitor.min.js` + filename: `readers.min.js` }, devtool: 'source-map', module: {