PageSourceSearch

https://licyk.github.io/js/openkounter.js

js licyk.github.io collected 2026-10-03 09:30:54 UTC 5,198 bytes, 173 lines download raw bytes

1/* global CONFIG, Fluid */
2
3(function(window, document) {
4  'use strict';
5
6  // Get server URL from config
7  const API_SERVER = (CONFIG.web_analytics.openkounter && CONFIG.web_analytics.openkounter.server_url) || '';
8
9  if (!API_SERVER) {
10    console.warn('OpenKounter: server_url is not configured');
11    return;
12  }
13
14  function getRecord(target) {
15    return fetch(`${API_SERVER}/api/counter?target=${encodeURIComponent(target)}`)
16      .then(resp => {
17        if (!resp.ok) {
18          throw new Error(`HTTP ${resp.status}: ${resp.statusText}`);
19        }
20        return resp.json();
21      })
22      .then(({ data, code, message }) => {
23        if (code !== 0) {
24          throw new Error(message || 'Unknown error');
25        }
26        return { time: data.time || 0, objectId: data.target };
27      })
28      .catch(error => {
29        console.error('OpenKounter fetch error:', error);
30        return { time: 0, objectId: target };
31      });
32  }
33
34  function increment(incrArr) {
35    if (!incrArr || incrArr.length === 0) {
36      return Promise.resolve([]);
37    }
38
39    return fetch(`${API_SERVER}/api/counter`, {
40      method: 'POST',
41      headers: { 'Content-Type': 'application/json' },
42      body: JSON.stringify({
43        action: 'batch_inc',
44        requests: incrArr
45      })
46    })
47      .then(res => {
48        if (!res.ok) {
49          throw new Error(`HTTP ${res.status}: ${res.statusText}`);
50        }
51        return res.json();
52      })
53      .then(res => {
54        if (res.code !== 0) {
55          throw new Error(res.message || 'Failed to increment counter');
56        }
57        return res.data;
58      })
59      .catch(error => {
60        console.error('OpenKounter increment error:', error);
61      });
62  }
63
64  function buildIncrement(objectId) {
65    return { target: objectId };
66  }
67
68  // 校验是否为有效的主机(排除本地开发环境)
69  function validHost() {
70    const ignoreLocal = CONFIG.web_analytics.openkounter && CONFIG.web_analytics.openkounter.ignore_local;
71    if (ignoreLocal !== false) {
72      const hostname = window.location.hostname;
73      if (hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '[::1]') {
74        return false;
75      }
76    }
77    return true;
78  }
79
80  // 校验是否为有效的独立访客(24小时内只计一次)
81  function validUV() {
82    const key = 'OpenKounter_UV_Flag';
83    const now = Date.now();
84
85    try {
86      const flag = localStorage.getItem(key);
87      if (flag) {
88        const lastVisit = parseInt(flag, 10);
89        // 距离上次访问小于 24 小时则不计为新 UV
90        if (now - lastVisit <= 86400000) {
91          return false;
92        }
93      }
94      localStorage.setItem(key, now.toString());
95    } catch (e) {
96      // localStorage 不可用时默认计为 UV
97      console.warn('OpenKounter: localStorage is not available');
98    }
99    return true;
100  }
101
102  function addCount() {
103    const enableIncr = CONFIG.web_analytics.enable && (!window.Fluid || !Fluid.ctx.dnt) && validHost();
104    const getterArr = [];
105    const incrArr = [];
106
107    // 请求站点 PV 并自增
108    const pvCtn = document.querySelector('#openkounter-site-pv-container');
109    if (pvCtn) {
110      const pvGetter = getRecord('site-pv').then((record) => {
111        if (enableIncr) {
112          incrArr.push(buildIncrement(record.objectId));
113        }
114        const ele = document.querySelector('#openkounter-site-pv');
115        if (ele) {
116          ele.innerText = (record.time || 0) + (enableIncr ? 1 : 0);
117          pvCtn.style.display = 'inline';
118        }
119      });
120      getterArr.push(pvGetter);
121    }
122
123    // 请求站点 UV 并自增
124    const uvCtn = document.querySelector('#openkounter-site-uv-container');
125    if (uvCtn) {
126      const uvGetter = getRecord('site-uv').then((record) => {
127        const incrUV = validUV() && enableIncr;
128        if (incrUV) {
129          incrArr.push(buildIncrement(record.objectId));
130        }
131        const ele = document.querySelector('#openkounter-site-uv');
132        if (ele) {
133          ele.innerText = (record.time || 0) + (incrUV ? 1 : 0);
134          uvCtn.style.display = 'inline';
135        }
136      });
137      getterArr.push(uvGetter);
138    }
139
140    // 请求页面浏览数并自增
141    const viewCtn = document.querySelector('#openkounter-page-views-container');
142    if (viewCtn) {
143      const pathConfig = CONFIG.web_analytics.openkounter.path || 'window.location.pathname';
144      const path = eval(pathConfig);
145      const target = decodeURI(path.replace(/\/*(index.html)?$/, '/'));
146
147      const viewGetter = getRecord(target).then((record) => {
148        if (enableIncr) {
149          incrArr.push(buildIncrement(record.objectId));
150        }
151        const ele = document.querySelector('#openkounter-page-views');
152        if (ele) {
153          ele.innerText = (record.time || 0) + (enableIncr ? 1 : 0);
154          viewCtn.style.display = 'inline';
155        }
156      });
157      getterArr.push(viewGetter);
158    }
159
160    // 批量发起统计请求
161    Promise.all(getterArr).then(() => {
162      if (enableIncr && incrArr.length > 0) {
163        increment(incrArr);
164      }
165    }).catch(error => {
166      console.error('OpenKounter error:', error);
167    });
168  }
169
170  addCount();
171
172})(window, document);
173

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.