1/** 2 * Sphere Post Views - log AJAX count. 3 * 4 * @copyright 2022 ThemeSphere 5 */ 6'use strict'; 7 8(() => { 9 const STORAGE_KEY = 'sphere-post-views'; 10 let configs; 11 12 function init(postID) { 13 configs = Sphere_PostViews; 14 postID = postID || configs.postID || null; 15 16 if (!window.fetch || !configs || !postID) { 17 return; 18 } 19 20 if (configs.sampling) { 21 const rand = Math.floor(Math.random() * configs.samplingRate) + 1; 22 if (rand !== 1) { 23 return; 24 } 25 } 26 27 if (isCrawler()) { 28 return; 29 } 30 31 // Already counted. 32 if (recentlyCounted(postID)) { 33 return; 34 } 35 36 const params = { 37 method: 'POST', 38 headers: { 39 'Content-type': 'application/x-www-form-urlencoded' 40 }, 41 body: [ 42 'post_id=' + postID, 43 'action=update_views_ajax', 44 'token=' + configs.token 45 ].join('&') 46 }; 47 48 fetch(configs.ajaxUrl, params) 49 .then(resp => resp.text()) 50 .then(data => logViewCount(postID)); 51 } 52 53 /** 54 * Check if post count was recently counted. 55 */ 56 function recentlyCounted(id) { 57 if (!configs.repeatCountDelay) { 58 return false; 59 } 60
61 // Seconds in Hours converted to ms. 62 const repeatCountDelay = 3600 * parseFloat(configs.repeatCountDelay) * 1000; 63 const viewed = getStorage() || {}; 64 65 if (!viewed || !viewed.posts || (!id in viewed.posts)) { 66 return false; 67 } 68 69 const lastViewed = parseInt(viewed.posts[id]); 70 if ((Date.now() - lastViewed) < repeatCountDelay) { 71 return true; 72 } 73 74 return false; 75 } 76 77 /** 78 * @returns {Boolean|Object} 79 */ 80 function getStorage() { 81 let viewed = localStorage.getItem(STORAGE_KEY); 82 if (!viewed) { 83 return false; 84 } 85 86 try { 87 viewed = JSON.parse(viewed); 88 89 // Grown too large. 90 if (viewed.posts && Object.keys(viewed.posts).length > 10000) { 91 viewed = {}; 92 } 93 94 } catch(e) { 95 return false; 96 } 97 98 return viewed; 99 } 100 101 /** 102 * Add a view count to storage, if needed. 103 * 104 * @param {Number} id 105 */ 106 function logViewCount(id) { 107 if (!configs.repeatCountDelay) { 108 return; 109 } 110 111 const viewed = getStorage() || {}; 112 viewed.posts = viewed.posts || {}; 113 viewed.posts[id] = Date.now(); 114 115 localStorage.setItem(STORAGE_KEY, JSON.stringify(viewed)); 116 } 117 118 /** 119 * Minimal crawler detection of popular bots. 120 */ 121 function isCrawler() { 122 if (navigator.webdriver) { 123 return true; 124 } 125 126 const isBot = /headless|bot|spider|crawl|google|baidu|bing|msn|teoma|slurp|yandex/i.test(navigator.userAgent); 127 return isBot; 128 } 129 130 document.readyState !== 'loading' 131 ? init() 132 : document.addEventListener('DOMContentLoaded', () => init()); 133 134 document.addEventListener('spc-alp-pageview', e => { 135 if (!e.detail.id) { 136 return; 137 } 138 139 init(e.detail.id); 140 }); 141})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.