From: Calin Crisan Date: Mon, 30 Nov 2015 13:12:43 +0000 (+0200) Subject: minor javascript optimizations X-Git-Url: http://www.vanbest.org/gitweb/?a=commitdiff_plain;h=83fde5b48fed72da88b49d198b42f5be5317494b;p=motioneye-debian minor javascript optimizations --- diff --git a/motioneye/static/css/main.css b/motioneye/static/css/main.css index c0dbc0d..140c889 100644 --- a/motioneye/static/css/main.css +++ b/motioneye/static/css/main.css @@ -911,7 +911,7 @@ img.camera { } img.camera.error, -img.camera.loading { +img.camera.initializing { opacity: 0; } diff --git a/motioneye/static/js/main.js b/motioneye/static/js/main.js index 9a8a9cb..ced0289 100644 --- a/motioneye/static/js/main.js +++ b/motioneye/static/js/main.js @@ -10,14 +10,203 @@ var password = ''; var basePath = null; var signatureRegExp = new RegExp('[^a-zA-Z0-9/?_.=&{}\\[\\]":, _-]', 'g'); var initialConfigFetched = false; /* used to workaround browser extensions that trigger stupid change events */ +var pageContainer = null; - /* utils */ + /* Object utilities */ + +Object.keys = Object.keys || (function () { + var hasOwnProperty = Object.prototype.hasOwnProperty; + var hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'); + var dontEnums = [ + 'toString', + 'toLocaleString', + 'valueOf', + 'hasOwnProperty', + 'isPrototypeOf', + 'propertyIsEnumerable', + 'constructor' + ]; + var dontEnumsLength = dontEnums.length; + + return function (obj) { + if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) { + return []; + } + + var result = []; + for (var prop in obj) { + if (hasOwnProperty.call(obj, prop)) { + result.push(prop); + } + } + + if (hasDontEnumBug) { + for (var i = 0; i < dontEnumsLength; i++) { + if (hasOwnProperty.call(obj, dontEnums[i])) { + result.push(dontEnums[i]); + } + } + } + + return result; + }; +})(); + +Object.update = function (dest, source) { + for (var key in source) { + if (!source.hasOwnProperty(key)) { + continue; + } + + dest[key] = source[key]; + } +}; + + + /* Array utilities */ + +Array.prototype.indexOf = Array.prototype.indexOf || function (obj) { + for (var i = 0; i < this.length; i++) { + if (this[i] === obj) { + return i; + } + } + + return -1; +}; + +Array.prototype.forEach = Array.prototype.forEach || function (callback, thisArg) { + for (var i = 0; i < this.length; i++) { + callback.call(thisArg, this[i], i, this); + } +}; + +Array.prototype.every = Array.prototype.every || function (callback, thisArg) { + for (var i = 0; i < this.length; i++) { + if (!callback.call(thisArg, this[i], i, this)) { + return false; + } + } + + return true; +}; + +Array.prototype.some = Array.prototype.some || function (callback, thisArg) { + for (var i = 0; i < this.length; i++) { + if (callback.call(thisArg, this[i], i, this)) { + return true; + } + } + + return false; +}; + +Array.prototype.unique = function (callback, thisArg) { + var uniqueElements = []; + this.forEach(function (element) { + if (uniqueElements.indexOf(element, Utils.equals) === -1) { + uniqueElements.push(element); + } + }); + + return uniqueElements; +}; + +Array.prototype.filter = function (func, thisArg) { + var filtered = []; + for (var i = 0; i < this.length; i++) { + if (func.call(thisArg, this[i], i, this)) { + filtered.push(this[i]); + } + } + + return filtered; +}; + +Array.prototype.map = function (func, thisArg) { + var mapped = []; + for (var i = 0; i < this.length; i++) { + mapped.push(func.call(thisArg, this[i], i, this)); + } + + return mapped; +}; + +Array.prototype.sortKey = function (keyFunc, reverse) { + this.sort(function (e1, e2) { + var k1 = keyFunc(e1); + var k2 = keyFunc(e2); + + if ((k1 < k2 && !reverse) || (k1 > k2 && reverse)) { + return -1; + } + else if ((k1 > k2 && !reverse) || (k1 < k2 && reverse)) { + return 1; + } + else { + return 0; + } + }); +}; + + + /* String utilities */ + +String.prototype.startsWith = String.prototype.startsWith || function (str) { + return (this.substr(0, str.length) === str); +}; + +String.prototype.endsWith = String.prototype.endsWith || function (str) { + return (this.substr(this.length - str.length) === str); +}; + +String.prototype.trim = String.prototype.trim || function () { + return this.replace(new RegExp('^\\s*'), '').replace(new RegExp('\\s*$'), ''); +}; + +String.prototype.replaceAll = String.prototype.replaceAll || function (oldStr, newStr) { + var p, s = this; + while ((p = s.indexOf(oldStr)) >= 0) { + s = s.substring(0, p) + newStr + s.substring(p + oldStr.length, s.length); + } + + return s.toString(); +}; + +String.prototype.format = function () { + var text = this; + + var rex = new RegExp('%[sdf]'); + var match, i = 0; + while (match = text.match(rex)) { + text = text.substring(0, match.index) + arguments[i] + text.substring(match.index + 2); + i++; + } + + if (i) { /* %s format used */ + return text; + } + + var keywords = arguments[0]; + + for (var key in keywords) { + text = text.replace('%(' + key + ')s', "" + keywords[key]); + text = text.replace('%(' + key + ')d', "" + keywords[key]); + text = text.replace('%(' + key + ')f', "" + keywords[key]); + } + + return text; +}; + + + /* misc utilities */ var sha1 = (function () { - function hash(msg) { - var K = [0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6]; + var K = [0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6]; + var P = Math.pow(2, 32); + function hash(msg) { msg += String.fromCharCode(0x80); var l = msg.length / 4 + 2; @@ -31,9 +220,8 @@ var sha1 = (function () { (msg.charCodeAt(i * 64 + j * 4 + 2) << 8) | (msg.charCodeAt(i * 64 + j * 4 + 3)); } } - M[N-1][14] = ((msg.length-1) * 8) / Math.pow(2, 32); - M[N-1][14] = Math.floor(M[N-1][14]); - M[N-1][15] = ((msg.length-1) * 8) & 0xffffffff; + M[N - 1][14] = Math.floor(((msg.length - 1) * 8) / P); + M[N - 1][15] = ((msg.length - 1) * 8) & 0xffffffff; var H0 = 0x67452301; var H1 = 0xefcdab89; @@ -43,9 +231,9 @@ var sha1 = (function () { var W = new Array(80); var a, b, c, d, e; - for (var i = 0; i < N; i++) { + for (i = 0; i < N; i++) { for (var t = 0; t < 16; t++) W[t] = M[i][t]; - for (var t = 16; t < 80; t++) W[t] = ROTL(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1); + for (t = 16; t < 80; t++) W[t] = ROTL(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1); a = H0; b = H1; c = H2; d = H3; e = H4; @@ -176,7 +364,7 @@ function addAuthParams(method, url, body) { url += '_username=' + window.username; if (window._loginDialogSubmitted) { url += '&_login=true'; - delete _loginDialogSubmitted; + _loginDialogSubmitted = false; } var signature = computeSignature(method, url, body); url += '&_signature=' + signature; @@ -259,22 +447,24 @@ function ajax(method, url, data, callback, error, timeout) { } function getCookie(name) { - if (document.cookie.length <= 0) { + var cookie = document.cookie.substring(); + + if (cookie.length <= 0) { return null; } - var start = document.cookie.indexOf(name + '='); + var start = cookie.indexOf(name + '='); if (start == -1) { return null; } var start = start + name.length + 1; - var end = document.cookie.indexOf(';', start); + var end = cookie.indexOf(';', start); if (end == -1) { - end = document.cookie.length; + end = cookie.length; } - return unescape(document.cookie.substring(start, end)); + return cookie.substring(start, end); } function setCookie(name, value, days) { @@ -308,196 +498,6 @@ function doLogout() { window.location.reload(true); } - - /* Object utilities */ - -Object.keys = Object.keys || (function () { - var hasOwnProperty = Object.prototype.hasOwnProperty; - var hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'); - var dontEnums = [ - 'toString', - 'toLocaleString', - 'valueOf', - 'hasOwnProperty', - 'isPrototypeOf', - 'propertyIsEnumerable', - 'constructor' - ]; - var dontEnumsLength = dontEnums.length; - - return function (obj) { - if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) { - return []; - } - - var result = []; - for (var prop in obj) { - if (hasOwnProperty.call(obj, prop)) { - result.push(prop); - } - } - - if (hasDontEnumBug) { - for (var i = 0; i < dontEnumsLength; i++) { - if (hasOwnProperty.call(obj, dontEnums[i])) { - result.push(dontEnums[i]); - } - } - } - - return result; - }; -})(); - -Object.update = function (dest, source) { - for (var key in source) { - if (!source.hasOwnProperty(key)) { - continue; - } - - dest[key] = source[key]; - } -}; - - - /* Array utilities */ - -Array.prototype.indexOf = Array.prototype.indexOf || function (obj) { - for (var i = 0; i < this.length; i++) { - if (this[i] === obj) { - return i; - } - } - - return -1; -}; - -Array.prototype.forEach = Array.prototype.forEach || function (callback, thisArg) { - for (var i = 0; i < this.length; i++) { - callback.call(thisArg, this[i], i, this); - } -}; - -Array.prototype.every = Array.prototype.every || function (callback, thisArg) { - for (var i = 0; i < this.length; i++) { - if (!callback.call(thisArg, this[i], i, this)) { - return false; - } - } - - return true; -}; - -Array.prototype.some = Array.prototype.some || function (callback, thisArg) { - for (var i = 0; i < this.length; i++) { - if (callback.call(thisArg, this[i], i, this)) { - return true; - } - } - - return false; -}; - -Array.prototype.unique = function (callback, thisArg) { - var uniqueElements = []; - this.forEach(function (element) { - if (uniqueElements.indexOf(element, Utils.equals) === -1) { - uniqueElements.push(element); - } - }); - - return uniqueElements; -}; - -Array.prototype.filter = function (func, thisArg) { - var filtered = []; - for (var i = 0; i < this.length; i++) { - if (func.call(thisArg, this[i], i, this)) { - filtered.push(this[i]); - } - } - - return filtered; -}; - -Array.prototype.map = function (func, thisArg) { - var mapped = []; - for (var i = 0; i < this.length; i++) { - mapped.push(func.call(thisArg, this[i], i, this)); - } - - return mapped; -}; - -Array.prototype.sortKey = function (keyFunc, reverse) { - this.sort(function (e1, e2) { - var k1 = keyFunc(e1); - var k2 = keyFunc(e2); - - if ((k1 < k2 && !reverse) || (k1 > k2 && reverse)) { - return -1; - } - else if ((k1 > k2 && !reverse) || (k1 < k2 && reverse)) { - return 1; - } - else { - return 0; - } - }); -}; - - - /* String utilities */ - -String.prototype.startsWith = String.prototype.startsWith || function (str) { - return (this.substr(0, str.length) === str); -}; - -String.prototype.endsWith = String.prototype.endsWith || function (str) { - return (this.substr(this.length - str.length) === str); -}; - -String.prototype.trim = String.prototype.trim || function () { - return this.replace(new RegExp('^\\s*'), '').replace(new RegExp('\\s*$'), ''); -}; - -String.prototype.replaceAll = String.prototype.replaceAll || function (oldStr, newStr) { - var p, s = this; - while ((p = s.indexOf(oldStr)) >= 0) { - s = s.substring(0, p) + newStr + s.substring(p + oldStr.length, s.length); - } - - return s.toString(); -}; - -String.prototype.format = function () { - var text = this; - - var rex = new RegExp('%[sdf]'); - var match, i = 0; - while (match = text.match(rex)) { - text = text.substring(0, match.index) + arguments[i] + text.substring(match.index + 2); - i++; - } - - if (i) { /* %s format used */ - return text; - } - - var keywords = arguments[0]; - - for (var key in keywords) { - text = text.replace('%(' + key + ')s', "" + keywords[key]); - text = text.replace('%(' + key + ')d', "" + keywords[key]); - text = text.replace('%(' + key + ')f', "" + keywords[key]); - } - - return text; -}; - - - /* various */ - function authorizeUpload() { var service = $('#uploadServiceSelect').val(); var cameraId = $('#cameraSelect').val(); @@ -507,8 +507,8 @@ function authorizeUpload() { window.open(url, '_blank'); } - - /* UI initialization */ + + /* UI */ function initUI() { /* checkboxes */ @@ -818,6 +818,36 @@ function initUI() { }); } +function getPageContainer() { + if (!pageContainer) { + pageContainer = $('div.page-container'); + } + + return pageContainer; +} + +function getCameraFrames() { + return getPageContainer().children('div.camera-frame'); +} + +function getCameraFrame(cameraId) { + var frame = getPageContainer().children('div.camera-frame#camera' + cameraId); + if (!frame.length) { + /* look for camera frames detached from page container */ + frame = $('div.camera-frame#camera' + cameraId); + } + + return frame; +} + +function getCameraProgresses() { + return getCameraFrames().find('div.camera-progress'); +} + +function getCameraProgress(cameraId) { + return getCameraFrame(cameraId).find('div.camera-progress'); +} + /* settings */ @@ -827,7 +857,7 @@ function openSettings(cameraId) { } $('div.settings').addClass('open').removeClass('closed'); - $('div.page-container').addClass('stretched'); + getPageContainer().addClass('stretched'); $('div.settings-top-bar').addClass('open').removeClass('closed'); updateConfigUI(); @@ -839,7 +869,7 @@ function closeSettings() { pushConfigReboot = false; $('div.settings').removeClass('open').addClass('closed'); - $('div.page-container').removeClass('stretched'); + getPageContainer().removeClass('stretched'); $('div.settings-top-bar').removeClass('open').addClass('closed'); } @@ -1791,11 +1821,11 @@ function beginProgress(cameraIds) { /* show the camera progress indicators */ if (cameraIds) { cameraIds.forEach(function (cameraId) { - $('div.camera-frame#camera' + cameraId + ' div.camera-progress').addClass('visible'); + getCameraProgress(cameraId).addClass('visible'); }); } else { - $('div.camera-progress').addClass('visible'); + getCameraProgresses().addClass('visible'); } /* remove the settings progress lock */ @@ -1821,7 +1851,7 @@ function endProgress() { $('div.settings-progress').css('opacity', '0'); /* hide the camera progress indicator */ - $('div.camera-progress').removeClass('visible'); + getCameraProgresses().removeClass('visible'); setTimeout(function () { $('div.settings-progress').css('width', '0px'); @@ -2432,7 +2462,7 @@ function fetchCurrentConfig(onFetch) { } /* add a progress indicator */ - $('div.page-container').append(''); + getPageContainer().append(''); if (isAdmin()) { /* fetch the main configuration */ @@ -3607,11 +3637,9 @@ function runMediaDialog(cameraId, mediaType) { /* camera frames */ function addCameraFrameUi(cameraConfig) { - var pageContainer = $('div.page-container'); - if (cameraConfig == null) { var cameraFrameDivPlaceHolder = $('
'); - pageContainer.append(cameraFrameDivPlaceHolder); + getPageContainer().append(cameraFrameDivPlaceHolder); return; } @@ -3672,7 +3700,7 @@ function addCameraFrameUi(cameraConfig) { /* insert the new camera frame at the right position, * with respect to the camera id */ - var cameraFrames = pageContainer.find('div.camera-frame'); + var cameraFrames = getPageContainer().find('div.camera-frame'); var cameraIds = cameraFrames.map(function () {return parseInt(this.id.substring(6));}); cameraIds.sort(); @@ -3682,11 +3710,11 @@ function addCameraFrameUi(cameraConfig) { } if (index < cameraIds.length) { - var beforeCameraFrame = pageContainer.find('div.camera-frame#camera' + cameraIds[index]); + var beforeCameraFrame = getPageContainer().find('div.camera-frame#camera' + cameraIds[index]); cameraFrameDiv.insertAfter(beforeCameraFrame); } else { - pageContainer.append(cameraFrameDiv); + getPageContainer().append(cameraFrameDiv); } /* fade in */ @@ -3717,29 +3745,37 @@ function addCameraFrameUi(cameraConfig) { }(cameraId)); /* error and load handlers */ - cameraImg.error(function () { + cameraImg[0].onerror = function () { this.error = true; this.loading = 0; - cameraImg.addClass('error').removeClass('loading'); + cameraImg.addClass('error').removeClass('initializing'); cameraImg.height(Math.round(cameraImg.width() * 0.75)); cameraPlaceholder.css('opacity', 1); cameraProgress.removeClass('visible'); cameraFrameDiv.removeClass('motion-detected'); - }); - cameraImg.load(function () { + }; + cameraImg[0].onload = function () { if (refreshDisabled[cameraId]) { return; /* refresh temporarily disabled for updating */ } - this.error = false; + if (this.error) { + cameraImg.removeClass('error'); + cameraPlaceholder.css('opacity', 0); + cameraImg.css('height', ''); + this.error = false; + } + this.loading = 0; - cameraImg.removeClass('error').removeClass('loading'); - cameraImg.css('height', ''); - cameraPlaceholder.css('opacity', 0); - cameraProgress.removeClass('visible'); - + if (this.initializing) { + cameraProgress.removeClass('visible'); + cameraImg.removeClass('initializing'); + cameraImg.css('height', ''); + this.initializing = false; + } + /* there's no point in looking for a cookie update more often than once every second */ var now = new Date().getTime(); if ((!this.lastCookieTime || now - this.lastCookieTime > 1000) && (cameraFrameDiv[0].config['proto'] != 'mjpeg')) { @@ -3757,29 +3793,27 @@ function addCameraFrameUi(cameraConfig) { /* update the modal dialog position when image is loaded */ updateModalDialogPosition(); } - }); + }; - cameraImg.addClass('loading'); + cameraImg.addClass('initializing'); + cameraImg[0].initializing = true; cameraImg.height(Math.round(cameraImg.width() * 0.75)); } function remCameraFrameUi(cameraId) { - var pageContainer = $('div.page-container'); - var cameraFrameDiv = pageContainer.find('div.camera-frame#camera' + cameraId); + var cameraFrameDiv = getPageContainer().find('div.camera-frame#camera' + cameraId); cameraFrameDiv.animate({'opacity': 0}, 100, function () { cameraFrameDiv.remove(); }); } function recreateCameraFrames(cameras) { - var pageContainer = $('div.page-container'); - function updateCameras(cameras) { cameras = cameras.filter(function (camera) {return camera.enabled;}); var i, camera; /* remove everything on the page */ - pageContainer.children().remove(); + getPageContainer().children().remove(); /* add camera frames */ for (i = 0; i < cameras.length; i++) { @@ -3791,7 +3825,7 @@ function recreateCameraFrames(cameras) { /* invite the user to add a camera */ var addCameraLink = $('
' + 'You have not configured any camera yet. Click here to add one...
'); - pageContainer.append(addCameraLink); + getPageContainer().append(addCameraLink); } } @@ -3840,17 +3874,19 @@ function doFullScreenCamera(cameraId) { fullScreenCameraId = -1; /* avoids successive fast toggles of fullscreen */ - var cameraFrameDiv = $('#camera' + cameraId); + var cameraFrameDiv = getCameraFrame(cameraId); var cameraName = cameraFrameDiv.find('span.camera-name').text(); - var frameImg = cameraFrameDiv.find('img.camera'); - var aspectRatio = frameImg.width() / frameImg.height(); + var cameraImg = cameraFrameDiv.find('img.camera'); + var aspectRatio = cameraImg.width() / cameraImg.height(); var windowWidth = $(window).width(); var windowHeight = $(window).height(); var windowAspectRatio = windowWidth / windowHeight; var frameIndex = cameraFrameDiv.index(); - var pageContainer = $('div.page-container'); - if (frameImg.hasClass('error')) { + cameraImg.addClass('initializing'); + cameraImg[0].initializing = true; + + if (cameraImg.hasClass('error')) { return; /* no full screen for erroneous cameras */ } @@ -3879,12 +3915,12 @@ function doFullScreenCamera(cameraId) { onClose: function () { fullScreenCameraId = null; cameraFrameDiv.css('width', ''); - var nextFrame = pageContainer.children('div:eq(' + frameIndex + ')'); + var nextFrame = getPageContainer().children('div:eq(' + frameIndex + ')'); if (nextFrame.length) { nextFrame.before(cameraFrameDiv); } else { - pageContainer.append(cameraFrameDiv); + getPageContainer().append(cameraFrameDiv); } } }); @@ -3897,6 +3933,8 @@ function doFullScreenCamera(cameraId) { } function refreshCameraFrames() { + var timestamp = new Date().getTime(); + function refreshCameraFrame(cameraId, img, serverSideResize) { if (refreshDisabled[cameraId]) { /* camera refreshing disabled, retry later */ @@ -3915,7 +3953,6 @@ function refreshCameraFrames() { } } - var timestamp = new Date().getTime(); var path = basePath + 'picture/' + cameraId + '/current/?_=' + timestamp; if (serverSideResize) { path += '&width=' + img.width; @@ -3929,10 +3966,10 @@ function refreshCameraFrames() { var cameraFrames; if (fullScreenCameraId != null && fullScreenCameraId >= 0) { - cameraFrames = $('#camera' + fullScreenCameraId); + cameraFrames = getCameraFrame(fullScreenCameraId); } else { - cameraFrames = $('div.page-container').find('div.camera-frame'); + cameraFrames = getCameraFrames(); } cameraFrames.each(function () { @@ -3981,7 +4018,7 @@ function refreshCameraFrames() { function checkCameraErrors() { /* properly triggers the onerror event on the cameras whose imgs were not successfully loaded, * but the onerror event hasn't been triggered, for some reason (seems to happen in Chrome) */ - var cameraFrames = $('div.page-container').find('img.camera'); + var cameraFrames = getPageContainer().find('img.camera'); cameraFrames.each(function () { if (this.complete === true && this.naturalWidth === 0 && !this.error && this.src) {