(function (){
'use strict';
if('undefined'===typeof lightvcData){
return;
}
const config={
postId: lightvcData.postId,
ajaxUrl: lightvcData.ajaxUrl,
scrollThreshold:
lightvcData.scrollThreshold!==undefined
? parseInt(lightvcData.scrollThreshold, 10)
: 50,
timeWindow: lightvcData.timeWindow!==undefined ? parseInt(lightvcData.timeWindow, 10):1800,
fastMode: lightvcData.fastMode||false,
excludeBots: lightvcData.excludeBots!==false,
};
const STORAGE_KEY='lightvc_viewed_posts';
let hasScrolled=false;
let hasCounted=false;
let localStorageAvailable=false;
function isLocalStorageAvailable(){
try {
const test='__lightvc_test__';
localStorage.setItem(test, test);
localStorage.removeItem(test);
return true;
} catch (e){
return false;
}}
function isBot(){
if(!config.excludeBots){
return false;
}
const userAgent=navigator.userAgent.toLowerCase();
const botPatterns=[
'googlebot',
'bingbot',
'slurp',
'duckduckbot',
'baiduspider',
'yandexbot',
'sogou',
'exabot',
'facebot',
'ia_archiver',
'crawler',
'spider',
'bot',
'facebookexternalhit',
'twitterbot',
'rogerbot',
'linkedinbot',
'embedly',
'quora link preview',
'showyoubot',
'outbrain',
'pinterest',
'developers.google.com/+/web/snippet',
'slackbot',
'vkshare',
'w3c_validator',
'redditbot',
'applebot',
'whatsapp',
'flipboard',
'tumblr',
'bitlybot',
'skypeuripreview',
'nuzzel',
'discordbot',
'qwantify',
'pinterestbot',
'bitrix',
'headlesschrome',
'phantomjs',
'slimerjs',
'ahrefsbot',
'semrushbot',
'dotbot',
'mj12bot',
'petalbot',
'screaming frog',
'sitebulb',
'lighthouse',
];
for (let i=0; i < botPatterns.length; i++){
if(userAgent.indexOf(botPatterns[i])!==-1){
return true;
}}
if(navigator.webdriver){
return true;
}
if(window.navigator.plugins&&window.navigator.plugins.length===0){
if(!window.chrome ||
typeof window.chrome.runtime==='undefined'
){
if(userAgent.indexOf('chrome')!==-1 ||
userAgent.indexOf('chromium')!==-1
){
return true;
}}
}
if(typeof navigator.languages==='undefined' ||
!navigator.languages.length
){
return true;
}
if(window.document.documentElement.getAttribute('webdriver')){
return true;
}
if(window._phantom||window.callPhantom){
return true;
}
if(window.__nightmare){
return true;
}
if(window.document.$cdc_asdjflasutopfhvcZLmcfl_ ||
window.document.documentElement.getAttribute('selenium')
){
return true;
}
if(window.document.documentElement.getAttribute('driver')){
return true;
}
return false;
}
function getViewedPosts(){
if(!localStorageAvailable){
return {};}
try {
const data=localStorage.getItem(STORAGE_KEY);
return data ? JSON.parse(data):{};} catch (e){
console.error('LVC: localStorage error', e);
return {};}}
function saveViewedPost(postId){
if(!localStorageAvailable){
return;
}
try {
const viewedPosts=getViewedPosts();
viewedPosts[postId]=Date.now();
const sevenDaysAgo=Date.now() - 7 * 24 * 60 * 60 * 1000;
Object.keys(viewedPosts).forEach(function (id){
if(viewedPosts[id] < sevenDaysAgo){
delete viewedPosts[id];
}});
localStorage.setItem(STORAGE_KEY, JSON.stringify(viewedPosts));
} catch (e){
console.error('LVC: localStorage save error', e);
}}
function wasRecentlyViewed(postId){
if(!localStorageAvailable){
return false;
}
const viewedPosts=getViewedPosts();
const lastViewed=viewedPosts[postId];
if(!lastViewed){
return false;
}
const timeWindow=config.timeWindow * 1000;
const timeSinceView=Date.now() - lastViewed;
return timeSinceView < timeWindow;
}
function getScrollPercentage(){
const windowHeight=window.innerHeight;
const documentHeight=document.documentElement.scrollHeight;
const scrollTop =
window.pageYOffset||document.documentElement.scrollTop;
const trackLength=documentHeight - windowHeight;
if(trackLength <=0){
return 100;
}
const percentScrolled=Math.floor((scrollTop / trackLength) * 100);
return Math.min(percentScrolled, 100);
}
function countView(){
if(hasCounted){
return;
}
hasCounted=true;
const postData=JSON.stringify({
post_id: config.postId,
});
if(config.fastMode&&typeof navigator.sendBeacon!=='undefined'){
const blob=new Blob([postData], { type: 'application/json' });
const sent=navigator.sendBeacon(config.ajaxUrl, blob);
if(sent){
saveViewedPost(config.postId);
if(typeof CustomEvent!=='undefined'){
const event=new CustomEvent('lvcViewCounted', {
detail: {
postId: config.postId,
fastMode: true,
},
});
document.dispatchEvent(event);
}}
return;
}
if(typeof fetch!=='undefined'){
fetch(config.ajaxUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: postData,
})
.then(function (response){
return response.json();
})
.then(function (data){
if(data.success){
saveViewedPost(config.postId);
if(typeof CustomEvent!=='undefined'){
const event=new CustomEvent('lvcViewCounted', {
detail: {
postId: config.postId,
viewCount: data.view_count,
},
});
document.dispatchEvent(event);
}}
})
.catch(function (error){
console.error('LVC: Count failed', error);
hasCounted=false;
});
}else{
const xhr=new XMLHttpRequest();
xhr.open('POST', config.ajaxUrl, true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload=function (){
if(xhr.status >=200&&xhr.status < 400){
try {
const data=JSON.parse(xhr.responseText);
if(data.success){
saveViewedPost(config.postId);
}} catch (e){
console.error('LVC: Parse error', e);
}}
};
xhr.onerror=function (){
console.error('LVC: Request failed');
hasCounted=false;
};
xhr.send(postData);
}}
function handleScroll(){
if(hasScrolled){
return;
}
const scrollPercentage=getScrollPercentage();
if(scrollPercentage >=config.scrollThreshold){
hasScrolled=true;
window.removeEventListener('scroll', handleScroll);
countView();
}}
function isContentShort(){
const windowHeight=window.innerHeight;
const documentHeight=document.documentElement.scrollHeight;
return documentHeight <=windowHeight;
}
function init(){
if(isBot()){
return;
}
localStorageAvailable=isLocalStorageAvailable();
if(wasRecentlyViewed(config.postId)){
return;
}
if(config.scrollThreshold===0){
countView();
return;
}
if(document.readyState==='loading'){
document.addEventListener('DOMContentLoaded', function (){
initializeScrollTracking();
});
}else{
initializeScrollTracking();
}}
function initializeScrollTracking(){
if(isContentShort()){
setTimeout(function (){
countView();
}, 1000);
return;
}
window.addEventListener('scroll', handleScroll, { passive: true });
setTimeout(handleScroll, 500);
}
init();
})();