summaryrefslogtreecommitdiff
path: root/web/kbn_assets/app.js
diff options
context:
space:
mode:
authorEvgeny Zinoviev <me@ch1p.io>2024-02-17 02:48:57 +0300
committerEvgeny Zinoviev <me@ch1p.io>2024-02-17 02:48:57 +0300
commitb7f1d55c9b4de4d21b11e5615a5dc8be0d4e883c (patch)
treedf3cba57518e21590d579b014867611002d92de5 /web/kbn_assets/app.js
parentc4ace358182d1f58724336714490e3caac6b60df (diff)
parent05c85757b8e2340441057d9ddfde2e9649ae8676 (diff)
Merge branch 'website-python-rewrite'
Diffstat (limited to 'web/kbn_assets/app.js')
-rw-r--r--web/kbn_assets/app.js368
1 files changed, 368 insertions, 0 deletions
diff --git a/web/kbn_assets/app.js b/web/kbn_assets/app.js
new file mode 100644
index 0000000..d575a5a
--- /dev/null
+++ b/web/kbn_assets/app.js
@@ -0,0 +1,368 @@
+(function() {
+var RE_WHITESPACE = /[\t\r\n\f]/g
+
+window.ajax = {
+ get: function(url, data) {
+ if (typeof data == 'object') {
+ var index = 0;
+ for (var key in data) {
+ var val = data[key];
+ url += index === 0 && url.indexOf('?') === -1 ? '?' : '&';
+ url += encodeURIComponent(key) + '=' + encodeURIComponent(val);
+ }
+ }
+ return this.raw(url);
+ },
+
+ post: function(url, body) {
+ var opts = {
+ method: 'POST'
+ };
+ if (body)
+ opts.body = body;
+ return this.raw(url, opts);
+ },
+
+ raw: function(url, options) {
+ if (!options)
+ options = {}
+
+ return fetch(url, Object.assign({
+ headers: {
+ 'X-Requested-With': 'XMLHttpRequest',
+ }
+ }, options))
+ .then(resp => {
+ return resp.json()
+ })
+ }
+};
+
+window.extend = function(a, b) {
+ return Object.assign(a, b);
+}
+
+window.ge = function(id) {
+ return document.getElementById(id);
+}
+
+var ua = navigator.userAgent.toLowerCase();
+window.browserInfo = {
+ version: (ua.match(/.+(?:me|ox|on|rv|it|ra|ie)[\/: ]([\d.]+)/) || [0,'0'])[1],
+ //opera: /opera/i.test(ua),
+ msie: (/msie/i.test(ua) && !/opera/i.test(ua)) || /trident/i.test(ua),
+ mozilla: /firefox/i.test(ua),
+ android: /android/i.test(ua),
+ mac: /mac/i.test(ua),
+ samsungBrowser: /samsungbrowser/i.test(ua),
+ chrome: /chrome/i.test(ua),
+ safari: /safari/i.test(ua),
+ mobile: /iphone|ipod|ipad|opera mini|opera mobi|iemobile|android/i.test(ua),
+ operaMini: /opera mini/i.test(ua),
+ ios: /iphone|ipod|ipad|watchos/i.test(ua) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1),
+};
+
+window.isTouchDevice = function() {
+ return 'ontouchstart' in window || navigator.msMaxTouchPoints;
+}
+
+window.hasClass = function(el, name) {
+ if (!el)
+ throw new Error('hasClass: invalid element')
+
+ if (el.nodeType !== 1)
+ throw new Error('hasClass: expected nodeType is 1, got' + el.nodeType)
+
+ if (window.DOMTokenList && el.classList instanceof DOMTokenList) {
+ return el.classList.contains(name)
+ } else {
+ return (" " + el.className + " ").replace(RE_WHITESPACE, " ").indexOf(" " + name + " ") >= 0
+ }
+}
+
+window.addClass = function(el, name) {
+ if (!hasClass(el, name)) {
+ el.className = (el.className ? el.className + ' ' : '') + name;
+ return true
+ }
+ return false
+}
+
+window.Cameras = {
+ hlsOptions: null,
+ h265webjsOptions: null,
+ host: null,
+ proto: null,
+ hlsDebugVideoEvents: false,
+
+ getUrl: function(name) {
+ return this.proto + '://' + this.host + '/ipcam/' + name + '/live.m3u8';
+ },
+
+ setupHls: function(video, name, useHls) {
+ var src = this.getUrl(name);
+
+ // hls.js is not supported on platforms that do not have Media Source Extensions (MSE) enabled.
+
+ // When the browser has built-in HLS support (check using `canPlayType`), we can provide an HLS manifest (i.e. .m3u8 URL) directly to the video element through the `src` property.
+ // This is using the built-in support of the plain video element, without using hls.js.
+
+ if (useHls) {
+ var config = this.hlsOptions;
+ config.xhrSetup = function (xhr,url) {
+ xhr.withCredentials = true;
+ };
+
+ var hls = new Hls(config);
+ hls.loadSource(src);
+ hls.attachMedia(video);
+ hls.on(Hls.Events.MEDIA_ATTACHED, function () {
+ video.muted = true;
+ video.play();
+ });
+ } else {
+ console.warn('hls.js is not supported, trying the native way...')
+
+ video.autoplay = true;
+ video.muted = true;
+ video.playsInline = true;
+ video.autoplay = true;
+ if (window.browserInfo.ios)
+ video.setAttribute('controls', 'controls');
+
+ video.src = src;
+
+ var events = ['canplay'];
+ if (this.hlsDebugVideoEvents)
+ events.push('canplay', 'canplaythrough', 'durationchange', 'ended', 'loadeddata', 'loadedmetadata', 'pause', 'play', 'playing', 'progress', 'seeked', 'seeking', 'stalled', 'suspend', 'timeupdate', 'waiting');
+
+ for (var i = 0; i < events.length; i++) {
+ var evt = events[i];
+ (function(evt, video, name) {
+ video.addEventListener(evt, function(e) {
+ if (this.debugVideoEvents)
+ console.log(name + ': ' + evt, e);
+
+ if (!window.browserInfo.ios && ['canplay', 'loadedmetadata'].includes(evt))
+ video.play();
+ })
+ })(evt, video, name);
+ }
+ }
+ },
+
+ setupH265WebJS: function(videoContainer, name) {
+ var containerHeightFixed = false;
+ var config = {
+ player: 'video-'+name,
+ width: videoContainer.offsetWidth,
+ height: parseInt(videoContainer.offsetWidth * 9 / 16, 10),
+ accurateSeek: true,
+ token: this.h265webjsOptions.token,
+ extInfo: {
+ moovStartFlag: true,
+ readyShow: true,
+ autoPlay: true,
+ rawFps: 15,
+ }
+ };
+
+ var mediaInfo;
+ var player = window.new265webjs(this.getUrl(name), config);
+
+ player.onSeekStart = (pts) => {
+ console.log(name + ": onSeekStart:" + pts);
+ };
+
+ player.onSeekFinish = () => {
+ console.log(name + ": onSeekFinish");
+ };
+
+ player.onPlayFinish = () => {
+ console.log(name + ": onPlayFinish");
+ };
+
+ player.onRender = (width, height, imageBufferY, imageBufferB, imageBufferR) => {
+ // console.log(name + ": onRender");
+ if (!containerHeightFixed) {
+ var ratio = height / width;
+ videoContainer.style.width = parseInt(videoContainer.offsetWidth * ratio, 10)+'px';
+ containerHeightFixed = true;
+ }
+ };
+
+ player.onOpenFullScreen = () => {
+ console.log(name + ": onOpenFullScreen");
+ };
+
+ player.onCloseFullScreen = () => {
+ console.log(name + ": onCloseFullScreen");
+ };
+
+ player.onSeekFinish = () => {
+ console.log(name + ": onSeekFinish");
+ };
+
+ player.onLoadCache = () => {
+ console.log(name + ": onLoadCache");
+ };
+
+ player.onLoadCacheFinshed = () => {
+ console.log(name + ": onLoadCacheFinshed");
+ };
+
+ player.onReadyShowDone = () => {
+ // console.log(name + ": onReadyShowDone:【You can play now】");
+ player.play()
+ };
+
+ player.onLoadFinish = () => {
+ console.log(name + ": onLoadFinish");
+
+ player.setVoice(1.0);
+
+ mediaInfo = player.mediaInfo();
+ console.log("onLoadFinish mediaInfo===========>", mediaInfo);
+
+ var codecName = "h265";
+ if (mediaInfo.meta.isHEVC === false) {
+ console.log(name + ": onLoadFinish is Not HEVC/H.265");
+ codecName = "h264";
+ } else {
+ console.log(name + ": onLoadFinish is HEVC/H.265");
+ }
+
+ console.log(name + ": onLoadFinish media Codec:" + codecName);
+ console.log(name + ": onLoadFinish media FPS:" + mediaInfo.meta.fps);
+ console.log(name + ": onLoadFinish media size:" + mediaInfo.meta.size.width + "x" + mediaInfo.meta.size.height);
+
+ if (mediaInfo.meta.audioNone) {
+ console.log(name + ": onLoadFinish media no Audio");
+ } else {
+ console.log(name + ": onLoadFinish media sampleRate:" + mediaInfo.meta.sampleRate);
+ }
+
+ if (mediaInfo.videoType == "vod") {
+ console.log(name + ": onLoadFinish media is VOD");
+ console.log(name + ": onLoadFinish media dur:" + Math.ceil(mediaInfo.meta.durationMs) / 1000.0);
+ } else {
+ console.log(name + ": onLoadFinish media is LIVE");
+ }
+ };
+
+ player.onCacheProcess = (cPts) => {
+ console.log(name + ": onCacheProcess:" + cPts);
+ };
+
+ player.onPlayTime = (videoPTS) => {
+ if (mediaInfo.videoType == "vod") {
+ console.log(name + ": onPlayTime:" + videoPTS);
+ } else {
+ // LIVE
+ }
+ };
+
+ player.do();
+ // console.log('setupH265WebJS: video: ', video.offsetWidth, video.offsetHeight)
+ },
+
+ init: function(opts) {
+ this.proto = opts.proto;
+ this.host = opts.host;
+ this.hlsOptions = opts.hlsConfig;
+ this.h265webjsOptions = opts.h265webjsConfig;
+
+ var useHls;
+ if (opts.hlsConfig !== undefined) {
+ useHls = Hls.isSupported();
+ if (!useHls && !this.hasFallbackSupport()) {
+ alert('Neither HLS nor vnd.apple.mpegurl is not supported by your browser.');
+ return;
+ }
+ }
+
+ for (var camId in opts.camsByType) {
+ var name = camId + '';
+ if (opts.isLow)
+ name += '-low';
+ var type = opts.camsByType[camId];
+
+ switch (type) {
+ case 'h265':
+ var videoContainer = document.createElement('div');
+ videoContainer.setAttribute('id', 'video-'+name);
+ videoContainer.setAttribute('style', 'position: relative'); // a hack to fix an error in h265webjs lib
+ videoContainer.className = 'video-container';
+ document.getElementById('videos').appendChild(videoContainer);
+ try {
+ this.setupH265WebJS(videoContainer, name);
+ } catch (e) {
+ console.error('cam'+camId+': error', e)
+ }
+ break;
+
+ case 'h264':
+ var video = document.createElement('video');
+ video.setAttribute('id', 'video-'+name);
+ document.getElementById('videos').appendChild(video);
+ this.setupHls(video, name, useHls);
+ break;
+ }
+ }
+ },
+
+ hasFallbackSupport: function() {
+ var video = document.createElement('video');
+ return video.canPlayType('application/vnd.apple.mpegurl');
+ },
+};
+})();
+
+
+class ModemStatusUpdater {
+ constructor(id) {
+ this.id = id;
+ this.elem = ge('modem_data_'+id);
+ this.fetch()
+ }
+
+ fetch() {
+ ajax.get('/modems/info.ajx', {
+ id: this.id
+ }).then(({response}) => {
+ const {html} = response;
+ this.elem.innerHTML = html;
+
+ // TODO enqueue rerender
+ });
+ }
+}
+
+
+var ModemStatus = {
+ _modems: [],
+
+ init: function(modems) {
+ for (var i = 0; i < modems.length; i++) {
+ var modem = modems[i];
+ this._modems.push(new ModemStatusUpdater(modem));
+ }
+ }
+};
+
+
+var Inverter = {
+ poll: function () {
+ setInterval(this._tick, 1000);
+ },
+
+ _tick: function() {
+ ajax.get('/inverter.ajx')
+ .then(({response}) => {
+ if (response) {
+ var el = document.getElementById('inverter_status');
+ el.innerHTML = response.html;
+ }
+ });
+ }
+}; \ No newline at end of file