public override void handleGETRequest(HttpProcessor p) { try { string requestedPage = Uri.UnescapeDataString(p.request_url.AbsolutePath.TrimStart('/')); if (requestedPage == "admin") { p.writeRedirect("admin/main"); return; } if (requestedPage == "login") { LogOutUser(p, null); return; } Session s = sm.GetSession(p.requestCookies.GetValue("cps"), p.requestCookies.GetValue("auth"), p.GetParam("rawauth")); if (s.sid != null && s.sid.Length == 16) { p.responseCookies.Add("cps", s.sid, TimeSpan.FromMinutes(s.sessionLengthMinutes)); } if (requestedPage == "logout") { LogOutUser(p, s); return; } if (requestedPage.StartsWith("admin/")) { string adminPage = requestedPage == "admin" ? "" : requestedPage.Substring("admin/".Length); if (string.IsNullOrWhiteSpace(adminPage)) { adminPage = "main"; } int idxQueryStringStart = adminPage.IndexOf('?'); if (idxQueryStringStart == -1) { idxQueryStringStart = adminPage.Length; } adminPage = adminPage.Substring(0, idxQueryStringStart); Pages.Admin.AdminPage.HandleRequest(adminPage, p, s); return; } else if (requestedPage.StartsWith("image/")) { requestedPage = requestedPage.Substring("image/".Length); #region image/ if (requestedPage.EndsWith(".jpg") || requestedPage.EndsWith(".jpeg") || requestedPage.EndsWith(".png") || requestedPage.EndsWith(".webp")) { int extensionLength = requestedPage[requestedPage.Length - 4] == '.' ? 4 : 5; string format = requestedPage.Substring(requestedPage.Length - (extensionLength - 1)); string cameraId = requestedPage.Substring(0, requestedPage.Length - extensionLength); cameraId = cameraId.ToLower(); int minPermission = cm.GetCameraMinPermission(cameraId); if (minPermission == 101) { p.writeFailure(); return; } if ((s == null && minPermission > 0) || (s != null && s.permission < minPermission)) { LogOutUser(p, s); return; } int wait = p.GetIntParam("wait", 5000); IPCameraBase cam = cm.GetCamera(cameraId); byte[] latestImage = cm.GetLatestImage(cameraId, wait); int patience = p.GetIntParam("patience"); if (patience > 0) { if (patience > 5000) { patience = 5000; } int timeLeft = patience; System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch(); timer.Start(); while (s.DuplicateImageSendCheck(cameraId, latestImage) && cam != null && timeLeft > 0) { // The latest image was already sent to the user in a previous image request. // Wait for up to 5 seconds as desired by the user to get a "new" image. cam.newFrameWaitHandle.WaitOne(Math.Max(50, timeLeft)); // This EventWaitHandle nonsense isn't perfect, so this should prevent excessively long delays in the event of a timing error. latestImage = cm.GetLatestImage(cameraId); timeLeft = patience - (int)timer.ElapsedMilliseconds; } } if (latestImage.Length == 0) { p.writeFailure("502 Bad Gateway"); return; } ImageFormat imgFormat = ImageFormat.Jpeg; latestImage = ImageConverter.HandleRequestedConversionIfAny(latestImage, p, ref imgFormat, format); p.tcpClient.SendBufferSize = latestImage.Length + 256; p.writeSuccess(Util.GetMime(imgFormat), latestImage.Length); p.outputStream.Flush(); p.rawOutputStream.Write(latestImage, 0, latestImage.Length); } else if (requestedPage.EndsWith(".mjpg")) { string cameraId = requestedPage.Substring(0, requestedPage.Length - 5); cameraId = cameraId.ToLower(); int minPermission = cm.GetCameraMinPermission(cameraId); if (minPermission == 101) { p.writeFailure(); return; } if ((s == null && minPermission > 0) || (s != null && s.permission < minPermission)) { LogOutUser(p, s); return; } if (cm.GetLatestImage(cameraId).Length == 0) { return; } // Increasing the send buffer size here does not help streaming fluidity. p.writeSuccess("multipart/x-mixed-replace;boundary=ipcamera"); byte[] newImage; byte[] lastImage = null; while (!this.stopRequested) { try { newImage = cm.GetLatestImage(cameraId); while (newImage == lastImage) { Thread.Sleep(1); newImage = cm.GetLatestImage(cameraId); if (this.stopRequested) { return; } } lastImage = newImage; ImageFormat imgFormat = ImageFormat.Jpeg; byte[] sendImage = ImageConverter.HandleRequestedConversionIfAny(newImage, p, ref imgFormat); p.outputStream.WriteLine("--ipcamera"); p.outputStream.WriteLine("Content-Type: " + Util.GetMime(imgFormat)); p.outputStream.WriteLine("Content-Length: " + sendImage.Length); p.outputStream.WriteLine(); p.outputStream.Flush(); p.rawOutputStream.Write(sendImage, 0, sendImage.Length); p.rawOutputStream.Flush(); p.outputStream.WriteLine(); } catch (Exception ex) { if (!p.isOrdinaryDisconnectException(ex)) { Logger.Debug(ex); } break; } } } else if (requestedPage.EndsWith(".ogg")) { string cameraId = requestedPage.Substring(0, requestedPage.Length - 4); cameraId = cameraId.ToLower(); int minPermission = cm.GetCameraMinPermission(cameraId); if (minPermission == 101) { p.writeFailure(); return; } if ((s == null && minPermission > 0) || (s != null && s.permission < minPermission)) { LogOutUser(p, s); return; } IPCameraBase _cam = cm.GetCamera(cameraId); if (_cam is Html5VideoCamera) { Html5VideoCamera cam = (Html5VideoCamera)_cam; ConcurrentQueue <byte[]> myDataListener = new ConcurrentQueue <byte[]>(); try { cam.RegisterStreamListener(myDataListener); p.writeSuccess("application/octet-stream"); p.outputStream.Flush(); byte[] outputBuffer; int chunkCount = 0; while (!this.stopRequested) { try { chunkCount = myDataListener.Count; if (chunkCount > 100) { return; // This connection is falling too far behind. End it. } else if (chunkCount > 0) { Console.Write(chunkCount + " "); if (myDataListener.TryDequeue(out outputBuffer)) { p.rawOutputStream.Write(outputBuffer, 0, outputBuffer.Length); p.rawOutputStream.Flush(); } } else { Thread.Sleep(1); } } catch (Exception ex) { if (!p.isOrdinaryDisconnectException(ex)) { Logger.Debug(ex); } break; } } } finally { cam.UnregisterStreamListener(myDataListener); } } else { p.writeFailure("501 Not Implemented"); } } else if (requestedPage.EndsWith(".cam")) { string cameraId = requestedPage.Substring(0, requestedPage.Length - 4); cameraId = cameraId.ToLower(); int minPermission = cm.GetCameraMinPermission(cameraId); if (minPermission == 101) { p.writeFailure(); return; } if ((s == null && minPermission > 0) || (s != null && s.permission < minPermission)) { LogOutUser(p, s); return; } IPCameraBase cam = cm.GetCamera(cameraId); if (cam != null && cam.cameraSpec.ptzType == MJpegCameraProxy.Configuration.PtzType.Dahua || cam.cameraSpec.ptzType == MJpegCameraProxy.Configuration.PtzType.Hikvision) { p.writeRedirect("../Camera.html?cam=" + cameraId); return; } string userAgent = p.GetHeaderValue("User-Agent", ""); bool isMobile = userAgent.Contains("iPad") || userAgent.Contains("iPhone") || userAgent.Contains("Android") || userAgent.Contains("BlackBerry"); bool isLanConnection = p == null ? false : p.IsLanConnection; int defaultRefresh = isLanConnection && !isMobile ? -1 : 250; string html = CamPage.GetHtml(cameraId, !isMobile, p.GetIntParam("refresh", defaultRefresh), p.GetBoolParam("override") ? -1 : 600000, p); if (string.IsNullOrEmpty(html) || html == "NO") { p.writeFailure(); return; } p.writeSuccess("text/html"); p.outputStream.Write(html); } else if (requestedPage == "PTZPRESETIMG") { string cameraId = p.GetParam("id"); cameraId = cameraId.ToLower(); IPCameraBase cam = cm.GetCamera(cameraId); if (cam != null) { int index = p.GetIntParam("index", -1); if (index > -1) { if (cam.cameraSpec.ptz_proxy) { string auth = (!string.IsNullOrEmpty(cam.cameraSpec.ptz_username) && !string.IsNullOrEmpty(cam.cameraSpec.ptz_password)) ? "rawauth=" + HttpUtility.UrlEncode(cam.cameraSpec.ptz_username) + ":" + HttpUtility.UrlEncode(cam.cameraSpec.ptz_password) + "&" : ""; byte[] data = SimpleProxy.GetData("http://" + cam.cameraSpec.ptz_hostName + "/PTZPRESETIMG?" + auth + "id=" + HttpUtility.UrlEncode(cam.cameraSpec.ptz_proxy_cameraId) + "&index=" + index); if (data.Length > 0) { p.writeSuccess("image/jpg", data.Length); p.outputStream.Flush(); p.rawOutputStream.Write(data, 0, data.Length); return; } } else { string fileName = Globals.ThumbsDirectoryBase + cameraId + index + ".jpg"; int minPermission = cm.GetCameraMinPermission(cameraId); if ((s == null && minPermission > 0) || (s != null && s.permission < minPermission) || minPermission == 101) { } else { if (File.Exists(fileName)) { byte[] bytes = File.ReadAllBytes(fileName); p.writeSuccess("image/jpg", bytes.Length); p.outputStream.Flush(); p.rawOutputStream.Write(bytes, 0, bytes.Length); return; } } } } } { // Failed to get image thumbnail byte[] bytes = File.ReadAllBytes(Globals.WWWPublicDirectoryBase + "Images/qmark.png"); p.writeSuccess("image/png", bytes.Length); p.outputStream.Flush(); p.rawOutputStream.Write(bytes, 0, bytes.Length); return; } } else if (requestedPage.EndsWith(".wanscamstream")) { string cameraId = requestedPage.Substring(0, requestedPage.Length - ".wanscamstream".Length); IPCameraBase cam = cm.GetCamera(cameraId); if (cam == null) { return; } if (!cam.cameraSpec.wanscamCompatibilityMode) { return; } if (p.RemoteIPAddress != "127.0.0.1") { return; } Uri url = new Uri(cam.cameraSpec.imageryUrl); string host = url.Host; int port = url.Port; string path = url.PathAndQuery; //string path = "/livestream.cgi?user=admin&pwd=nooilwell&streamid=0&audio=0&filename="; //string path = "/videostream.cgi?user=admin&pwd=nooilwell&resolution=8"; int total = 0; try { //Console.WriteLine("opening"); Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.Connect(host, port); byte[] buffer = new byte[4096]; socket.Send(UTF8Encoding.UTF8.GetBytes("GET " + path + " HTTP/1.1\r\nHost: " + host + ":" + port + "\r\nConnection: close\r\n\r\n")); //Console.WriteLine("open"); int read = socket.Receive(buffer); p.writeSuccess("video/raw"); p.outputStream.Flush(); while (read > 0 && socket.Connected && p.tcpClient.Connected) { p.rawOutputStream.Write(buffer, 0, read); total += read; //Console.WriteLine(read); read = socket.Receive(buffer); } //Console.WriteLine("close"); } catch (Exception ex) { if (!p.isOrdinaryDisconnectException(ex)) { Logger.Debug(ex); } } } #endregion } else if (requestedPage.StartsWith("control/")) { requestedPage = requestedPage.Substring("control/".Length); #region control/ if (requestedPage == "keepalive") { string cameraId = p.GetParam("id"); cameraId = cameraId.ToLower(); int minPermission = cm.GetCameraMinPermission(cameraId); if (minPermission == 101) { p.writeFailure(); return; } if ((s == null && minPermission > 0) || (s != null && s.permission < minPermission)) { p.writeFailure("403 Forbidden"); return; } cm.GetRTSPUrl(cameraId, p); p.writeSuccess("text/plain"); p.outputStream.Write("1"); } else if (requestedPage == "PTZ") { string cameraId = p.GetParam("id"); cameraId = cameraId.ToLower(); int minPermission = cm.GetCameraMinPermission(cameraId); if (minPermission == 101) { p.writeFailure(); return; } if ((s == null && minPermission > 0) || (s != null && s.permission < minPermission)) { LogOutUser(p, s); return; } PTZ.RunCommand(cameraId, p.GetParam("cmd")); p.writeSuccess("text/plain"); } #endregion } else { #region www int permissionRequired; if (!Util.TryGetValue(requestedPage.ToLower(), MJpegWrapper.cfg.GetWwwFilesList(), out permissionRequired)) { permissionRequired = -1; } string wwwDirectory = permissionRequired == -1 ? Globals.WWWPublicDirectoryBase : Globals.WWWDirectoryBase; if (permissionRequired < 0) { permissionRequired = 0; } else if (permissionRequired > 100) { permissionRequired = 100; } if (permissionRequired > s.permission) { LogOutUser(p, s); return; } DirectoryInfo WWWDirectory = new DirectoryInfo(wwwDirectory); string wwwDirectoryBase = WWWDirectory.FullName.Replace('\\', '/').TrimEnd('/') + '/'; FileInfo fi = new FileInfo(wwwDirectoryBase + requestedPage); string targetFilePath = fi.FullName.Replace('\\', '/'); if (!targetFilePath.StartsWith(wwwDirectoryBase) || targetFilePath.Contains("../")) { p.writeFailure("400 Bad Request"); return; } if (!fi.Exists) { p.writeFailure(); return; } // && (fi.Extension == ".html" || fi.Extension == ".htm") if (fi.Name.ToLower() == "camera.html" && fi.Length < 256000) { p.writeSuccess(Mime.GetMimeType(fi.Extension)); string html = File.ReadAllText(fi.FullName); CamPage2 cp = new CamPage2(html, p); html = cp.Html; html = html.Replace("%ALLCAMS%", string.Join(",", MJpegServer.cm.GenerateAllCameraIdList())); html = html.Replace("%ALLCAMS_IDS_NAMES_JS_ARRAY%", MJpegServer.cm.GenerateAllCameraIdNameList(s == null ? 0 : s.permission)); try { html = html.Replace("%REMOTEIP%", p.RemoteIPAddress); } catch (Exception ex) { Logger.Debug(ex); } p.outputStream.Write(html); p.outputStream.Flush(); } else if ((fi.Extension == ".html" || fi.Extension == ".htm") && fi.Length < 256000) { p.writeSuccess(Mime.GetMimeType(fi.Extension)); string html = File.ReadAllText(fi.FullName); html = html.Replace("%ALLCAMS%", string.Join(",", MJpegServer.cm.GenerateAllCameraIdList())); html = html.Replace("%ALLCAMS_IDS_NAMES_JS_ARRAY%", MJpegServer.cm.GenerateAllCameraIdNameList(s == null ? 0 : s.permission)); try { html = html.Replace("%REMOTEIP%", p.RemoteIPAddress); } catch (Exception ex) { Logger.Debug(ex); } p.outputStream.Write(html); p.outputStream.Flush(); } else { List <KeyValuePair <string, string> > additionalHeaders = new List <KeyValuePair <string, string> >(); additionalHeaders.Add(new KeyValuePair <string, string>("Cache-Control", "max-age=3600, public")); p.writeSuccess(Mime.GetMimeType(fi.Extension), additionalHeaders: additionalHeaders); p.outputStream.Flush(); using (FileStream fs = fi.OpenRead()) { fs.CopyTo(p.rawOutputStream); } p.rawOutputStream.Flush(); } #endregion } } catch (Exception ex) { if (!p.isOrdinaryDisconnectException(ex)) { Logger.Debug(ex); } } }
public static string GetHtml(string camId, bool enableAutoResize, int refreshTime = 250, int disableRefreshAfter = 600000, HttpProcessor httpProcessor = null) { IPCameraBase cam = MJpegServer.cm.GetCameraAndGetItRunning(camId); if (cam == null) { return("NO"); } int width = 0; int height = 0; int patience = (cam.cameraSpec.delayBetweenImageGrabs > 0 ? cam.cameraSpec.delayBetweenImageGrabs : 0) + 5000; string cameraImgLink = @"<img id=""imgFrame"" class=""CamImg"" />"; string keepalive = ""; if (cam.cameraSpec.type == CameraType.h264_rtsp_proxy) { bool sizeOverridden = cam.cameraSpec.h264_video_width > 0 && cam.cameraSpec.h264_video_height > 0; width = sizeOverridden ? cam.cameraSpec.h264_video_width : 640; height = sizeOverridden ? cam.cameraSpec.h264_video_height : 360; keepalive = @" var keepaliveInterval; $(function() { sizeOverridden = " + (sizeOverridden ? "true" : "false") + @"; keepaliveInterval = setInterval(keepalive, 4000); var vlc = document.getElementById('vlc'); registerVLCEvent('MediaPlayerPlaying', handlePlayerPlaying); var url = '" + HttpUtility.HtmlEncode(HttpUtility.JavaScriptStringEncode(MJpegServer.cm.GetRTSPUrl(cam.cameraSpec.id, httpProcessor))) + @"'; url = url.replace('$$$HOST$$$', location.hostname); vlc.playlist.add(url); vlc.playlist.play(); if(sizeOverridden) vlc.video.aspectRatio = '" + width + ":" + height + @"'; }); function keepalive() { if(refreshDisabled) clearInterval(keepaliveInterval); else $.ajax('keepalive?id=" + camId + @"'); } function handlePlayerPlaying() { if(!sizeOverridden) { setTimeout(resize, 500); setTimeout(resize, 1000); setTimeout(resize, 1500); } } function registerVLCEvent(event, handler) { var vlc = document.getElementById('vlc'); if (vlc) { if (vlc.attachEvent) { // Microsoft vlc.attachEvent(event, handler); } else if (vlc.addEventListener) { // Mozilla: DOM level 2 vlc.addEventListener(event, handler, false); } else { // DOM level 0 vlc['on' + event] = handler; } } }" ; cameraImgLink = @"<div id=""vlcFrame"" style=""width:" + width + @"px;height:" + height + @"px;""><embed type=""application/x-vlc-plugin"" id=""vlc"" pluginspage=""http://www.videolan.org"" width=""" + width + @""" height=""" + height + @""" toolbar=""false"" src="""" mute=""false"" /></div>"; } else { for (int i = 0; i < 50; i++) { if (cam.ImageSize.Width != 0 && cam.ImageSize.Height != 0) { break; } Thread.Sleep(100); } if (cam.ImageSize.Width == 0 || cam.ImageSize.Height == 0) { return(@"<!DOCTYPE HTML> <html> <head> <title>" + HttpUtility.HtmlEncode(cam.cameraSpec.name) + @"</title> </head> <body> This camera is starting up.<br/> Please <a href=""javascript:top.location.reload();"">try again in a few moments</a>. </body> </html>" ); } width = cam.ImageSize.Width; height = cam.ImageSize.Height; } string minutesLabel = disableRefreshAfter < 0 ? "a very long time" : (TimeSpan.FromMilliseconds(disableRefreshAfter).TotalMinutes + " minutes"); return(@"<!DOCTYPE HTML> <html> <head> <title>" + HttpUtility.HtmlEncode(cam.cameraSpec.name) + @"</title> <script src=""" + Globals.jQueryPath + @""" type=""text/javascript""></script> <script type=""text/javascript""> var disableRefreshAfter = " + disableRefreshAfter + @"; var refreshDisabled = false; var originwidth = parseInt(" + width + @"); var originheight = parseInt(" + height + @"); var lastUpdate = 0; var clickCausesResize = true; var showPTH = " + (cam.cameraSpec.ptzType == PtzType.None ? "false" : "true") + @"; var sizeOverridden = false; $(function() { if(disableRefreshAfter > -1) setTimeout(""disableRefresh()"", disableRefreshAfter); }); " + keepalive + @" function myOnLoad() { $(""#imgFrame"").load(function () { " + (refreshTime < 0 ? "" : @" if (!this.complete || typeof this.naturalWidth == ""undefined"" || this.naturalWidth == 0) { alert('Bad image data was received. A data transmission error may have occurred, or this camera may be offline. Please reload this page to try again.'); } else if(!refreshDisabled) { lastUpdate = new Date().getTime(); setTimeout(""GetNewImage();"", " + refreshTime + @"); } ") + @" }); $(""#imgFrame"").error(function () { setTimeout(""GetNewImage();"", " + (refreshTime < 0 ? 1000 : refreshTime) + @"); }); GetNewImage(); } function PopupMessage(msg) { var pm = $(""#popupMessage""); if(pm.length < 1) $(""#camFrame"").after('<div id=""popupFrame""><div id=""popupMessage"">' + msg + '</div><center><input type=""button"" value=""Close Message"" onclick=""CloseMessage()""/></center></div>'); else pm.append(msg); } function CloseMessage() { $(""#popupFrame"").remove(); } function GetNewImage() { $(""#imgFrame"").attr('src', '" + camId + "." + (refreshTime < 0 ? "m" : "") + @"jpg?patience=" + patience + "&" + httpProcessor.GetParam("imgargs") + @"&nocache=' + new Date().getTime()); } function disableRefresh() { refreshDisabled = true; PopupMessage('This page has been open for " + minutesLabel + @". To save resources, the image will no longer refresh automatically. <a href=""javascript:top.location.reload();"">Click here</a> to reload this page.');" + (refreshTime < 0 ? @"$(""#imgFrame"").attr('src', '" + camId + @".jpg?nocache=' + new Date().getTime());" : "") + @" } " + (enableAutoResize ? "$(window).load(resize); $(window).resize(resize);" : "") + @" function resize(width, height) { var newHeight; var newWidth; var currentImage = document.getElementById(""imgFrame""); if (currentImage == null) { currentImage = document.getElementById(""vlcFrame""); var vlc = document.getElementById('vlc') if(vlc && !sizeOverridden) { originwidth = parseInt(vlc.video.width); originheight = parseInt(vlc.video.height); if(originwidth == 0) originwidth = 640; if(originheight == 0) originheight = 360; } } if (currentImage == null) return; if (typeof (width) == 'undefined' || typeof (height) == 'undefined') { var imgOff = FindOffsets(currentImage); var screenOff = GetViewportDims(); // Calculate available dimensions var availableHeight = (screenOff.height - imgOff.top) - 7 - (showPTH ? 130 : 0); var availableWidth = (screenOff.width - imgOff.left) - 21; // Take into consideration the original width and height for the image newHeight = originheight < availableHeight ? originheight : availableHeight; newWidth = originwidth < availableWidth ? originwidth : availableWidth; // Calculate ratios var originRatio = originwidth / originheight; var newRatio = newWidth / newHeight; if (newRatio < originRatio) newHeight = newWidth / originRatio; else newWidth = newHeight * originRatio; currentImage.onclick = function () { if(clickCausesResize) resize(originwidth, originheight); } } else { var newHeight = height; var newWidth = width; currentImage.onclick = function () { if(clickCausesResize) resize(); } } $(currentImage).height(newHeight); $(currentImage).width(newWidth); $(""#vlcFrame"").width(newWidth); $(""#vlcFrame"").height(newHeight); $(""#vlc"").attr('width', newWidth + 'px'); $(""#vlc"").attr('height', newHeight + 'px'); $(""#camFrame"").width(newWidth); } function GetViewportDims() { var w; var h; if (typeof (window.innerWidth) != 'undefined') { w = window.innerWidth; h = window.innerHeight; } else if (typeof (document.documentElement) != 'undefined' && typeof (document.documentElement.clientWidth) != 'undefined' && document.documentElement.clientWidth != 0) { w = document.documentElement.clientWidth; h = document.documentElement.clientHeight; } else { w = document.getElementsByTagName('body')[0].clientWidth; h = document.getElementsByTagName('body')[0].clientHeight; } return { width: w, height: h }; } function FindOffsets(node) { var oLeft = 0; var oTop = 0; var oWidth = node.offsetWidth; var oHeight = node.offsetHeight; if (oWidth == 0) oWidth += node.scrollWidth; oLeft = node.offsetLeft; oTop = node.offsetTop; if (oHeight == 0) { if (node.childNodes.length > 0) { oHeight += node.childNodes[0].offsetHeight; oTop = node.childNodes[0].offsetTop; } if (oHeight == 0) oHeight += node.scrollHeight; } node = node.offsetParent; while (node) { oLeft += node.offsetLeft; oTop += node.offsetTop; node = node.offsetParent; } return { left: oLeft, top: oTop, width: oWidth, height: oHeight }; } </script> <style type=""text/css""> .CamImg { width: " + width + @"px; height: " + height + @"px; } #vlcFrame { width: " + width + @"px; height: " + height + @"px; } body { margin: 3px; } #camFrame { background-color: #666666; border: 1px Solid Black; border-radius: 5px; padding: 3px; width: " + width + @"px; } #popupFrame { position: fixed; background-color: #BB0000; border: 1px Solid White; border-radius: 5px; padding: 5px; top: 15px; width: 300px; left: 20px; } #popupMessage { color: White; margin-bottom: 5px; } #popupMessage a { color: #DDDDFF; } #pthDiv, #camControls { margin-top: 3px; padding: 0px 3px 0px 3px; background-color: #AAAAAA; } #pthCell { vertical-align: top; } #pthTable td { text-align: center; } .nounderline, .nounderline a { text-decoration: none; } .flip-horizontal { -moz-transform: scaleX(-1); -webkit-transform: scaleX(-1); -o-transform: scaleX(-1); transform: scaleX(-1); -ms-filter: fliph; /*IE*/ filter: fliph; /*IE*/ } .flip-vertical { -moz-transform: scaleY(-1); -webkit-transform: scaleY(-1); -o-transform: scaleY(-1); transform: scaleY(-1); -ms-filter: flipv; /*IE*/ filter: flipv; /*IE*/ } .flip-both { -moz-transform: scale(-1,-1); -webkit-transform: scale(-1,-1); -o-transform: scale(-1,-1); transform: scale(-1,-1); -ms-filter: flipv fliph; /*IE*/ filter: flipv fliph; /*IE*/ } .arrow-up { width: 0px; height: 0px; border-bottom: 20px solid #00AA00; border-left: 20px solid transparent; border-right: 20px solid transparent; } .arrow-down { width: 0px; height: 0px; border-top: 20px solid #00AA00; border-left: 20px solid transparent; border-right: 20px solid transparent; } .arrow-left { width: 0px; height: 0px; border-bottom: 10px solid transparent; border-top: 10px solid transparent; border-right: 10px solid #00AA00; } .arrow-right { width: 0px; height: 0px; border-bottom: 10px solid transparent; border-left: 10px solid #00AA00; border-top: 10px solid transparent; } </style> </head> <body onload=""myOnLoad();""> <div id=""camFrame""> " + cameraImgLink + @" <table style=""width: 100%""> <tbody> <tr> " + PTZ.GetHtml(camId, cam) + @" </tr> </tbody> </table> </div> </body> </html>"); }