From 74328568567c0b9e47eb355dbf2624da9b68706d Mon Sep 17 00:00:00 2001 From: 2mik Date: Mon, 25 Apr 2016 17:37:59 +0300 Subject: [PATCH 001/382] Initial commit of ScadaData.Svc --- ScadaData/ScadaData.Svc/BaseSvcInstaller.cs | 111 ++++++++++++++++++ .../ScadaData.Svc/Properties/AssemblyInfo.cs | 36 ++++++ ScadaData/ScadaData.Svc/ScadaData.Svc.csproj | 60 ++++++++++ ScadaData/ScadaData.sln | 10 +- .../ScadaSchemeWeb/clientBin/ScadaScheme.xap | Bin 32970 -> 31232 bytes 5 files changed, 215 insertions(+), 2 deletions(-) create mode 100644 ScadaData/ScadaData.Svc/BaseSvcInstaller.cs create mode 100644 ScadaData/ScadaData.Svc/Properties/AssemblyInfo.cs create mode 100644 ScadaData/ScadaData.Svc/ScadaData.Svc.csproj diff --git a/ScadaData/ScadaData.Svc/BaseSvcInstaller.cs b/ScadaData/ScadaData.Svc/BaseSvcInstaller.cs new file mode 100644 index 000000000..0291bdfcf --- /dev/null +++ b/ScadaData/ScadaData.Svc/BaseSvcInstaller.cs @@ -0,0 +1,111 @@ +/* + * Copyright 2016 Mikhail Shiryaev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Product : Rapid SCADA + * Module : ScadaData.Svc + * Summary : The base class for Windows service installer + * + * Author : Mikhail Shiryaev + * Created : 2007 + * Modified : 2016 + */ + +using System; +using System.Configuration.Install; +using System.IO; +using System.Reflection; +using System.ServiceProcess; +using System.Xml; + +namespace Scada.Svc +{ + /// + /// The base class for Windows service installer + /// Базовый класс установщика службы Windows + /// + public abstract class BaseSvcInstaller : Installer + { + /// + /// Имя файла, содержащего свойства службы + /// + private const string SvcPropsFileName = "svc_config.xml"; + + + /// + /// Загрузить свойства службы + /// + private bool LoadSeriveProps(out string svcName, out string svcDescr) + { + try + { + string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + string fileName = path + Path.DirectorySeparatorChar + SvcPropsFileName; + + if (File.Exists(fileName)) + { + XmlDocument xmlDoc = new XmlDocument(); + xmlDoc.Load(fileName); + + svcName = xmlDoc.DocumentElement.GetChildAsString("ServiceName"); + svcDescr = xmlDoc.DocumentElement.GetChildAsString("Description"); + return true; + } + else + { + svcName = ""; + svcDescr = ""; + return false; + } + } + catch (Exception ex) + { + throw new Exception("Error loading service properties", ex); + } + } + + /// + /// Инициализировать установщик службы + /// + protected void Init(string defSvcName, string defDescr) + { + string svcName; + string svcDescr; + + if (!LoadSeriveProps(out svcName, out svcDescr)) + { + svcName = defSvcName; + svcDescr = defDescr; + } + + if (string.IsNullOrEmpty(svcName)) + throw new ScadaException("Service name must not be null or empty."); + + ServiceInstaller serviceInstaller = new ServiceInstaller(); + ServiceProcessInstaller serviceProcessInstaller = new ServiceProcessInstaller(); + + serviceInstaller.ServiceName = svcName; + serviceInstaller.DisplayName = svcName; + serviceInstaller.Description = svcDescr ?? ""; + serviceInstaller.StartType = ServiceStartMode.Automatic; + + serviceProcessInstaller.Account = ServiceAccount.LocalSystem; + serviceProcessInstaller.Password = null; + serviceProcessInstaller.Username = null; + + Installers.AddRange(new Installer[] {serviceInstaller, serviceProcessInstaller}); + } + } +} diff --git a/ScadaData/ScadaData.Svc/Properties/AssemblyInfo.cs b/ScadaData/ScadaData.Svc/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..c44e170f3 --- /dev/null +++ b/ScadaData/ScadaData.Svc/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ScadaData.Svc")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Rapid SCADA")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("827ccedc-f418-4261-9448-ea28f9033a4d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/ScadaData/ScadaData.Svc/ScadaData.Svc.csproj b/ScadaData/ScadaData.Svc/ScadaData.Svc.csproj new file mode 100644 index 000000000..b9bec8988 --- /dev/null +++ b/ScadaData/ScadaData.Svc/ScadaData.Svc.csproj @@ -0,0 +1,60 @@ + + + + + Debug + AnyCPU + {827CCEDC-F418-4261-9448-EA28F9033A4D} + Library + Properties + Scada.Svc + ScadaData.Svc + v4.0 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + bin\Release\ScadaData.Svc.XML + + + + + + + + + + + Component + + + + + + {c5dc293f-13ca-435f-a7db-4ba91639c292} + ScadaData + + + + + \ No newline at end of file diff --git a/ScadaData/ScadaData.sln b/ScadaData/ScadaData.sln index af164b750..0c618f178 100644 --- a/ScadaData/ScadaData.sln +++ b/ScadaData/ScadaData.sln @@ -1,11 +1,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 +# Visual Studio 14 +VisualStudioVersion = 14.0.24720.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScadaData", "ScadaData\ScadaData.csproj", "{C5DC293F-13CA-435F-A7DB-4BA91639C292}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScadaDataTest", "ScadaDataTest\ScadaDataTest.csproj", "{31415B61-53C3-4746-B586-CFD7FC16CED2}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScadaData.Svc", "ScadaData.Svc\ScadaData.Svc.csproj", "{827CCEDC-F418-4261-9448-EA28F9033A4D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -20,6 +22,10 @@ Global {31415B61-53C3-4746-B586-CFD7FC16CED2}.Debug|Any CPU.Build.0 = Debug|Any CPU {31415B61-53C3-4746-B586-CFD7FC16CED2}.Release|Any CPU.ActiveCfg = Release|Any CPU {31415B61-53C3-4746-B586-CFD7FC16CED2}.Release|Any CPU.Build.0 = Release|Any CPU + {827CCEDC-F418-4261-9448-EA28F9033A4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {827CCEDC-F418-4261-9448-EA28F9033A4D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {827CCEDC-F418-4261-9448-EA28F9033A4D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {827CCEDC-F418-4261-9448-EA28F9033A4D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ScadaScheme/ScadaSchemeWeb/clientBin/ScadaScheme.xap b/ScadaScheme/ScadaSchemeWeb/clientBin/ScadaScheme.xap index 72511712297d29e7fe0113fe5f1153d46d99d158..490d403b631c673741f85c60dab6ab1307c09ddf 100644 GIT binary patch delta 30759 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 basmbdLi8Qu_y1qx9!b9gSfI$g{v-V#Fn(nH delta 32490 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 Lbq;)y*Ms~IWI*Rm From 99e3e6c04b4a1702aa206ddd0471021395c51de6 Mon Sep 17 00:00:00 2001 From: 2mik Date: Mon, 25 Apr 2016 19:16:37 +0300 Subject: [PATCH 002/382] ScadaServer: multiple services support --- ScadaData/ScadaData.Svc/BaseSvcInstaller.cs | 64 ++------- ScadaData/ScadaData.Svc/ScadaData.Svc.csproj | 10 +- ScadaData/ScadaData.Svc/SvcProps.cs | 133 ++++++++++++++++++ ScadaServer/Res/{AboutEn.jpg => about_en.jpg} | Bin 16621 -> 16619 bytes ScadaServer/Res/{AboutRu.jpg => about_ru.jpg} | Bin 16908 -> 16901 bytes ScadaServer/Res/svc/svc_config.xml | 5 + ScadaServer/Res/svc/svc_install.bat | 2 + ScadaServer/Res/svc/svc_uninstall.bat | 2 + ScadaServer/ScadaServerCtrl/AppPhrases.cs | 2 +- ScadaServer/ScadaServerCtrl/FrmAbout.cs | 2 +- ScadaServer/ScadaServerCtrl/FrmMain.cs | 129 ++++++++++++----- .../Lang/ScadaServer.en-GB.xml | 2 +- .../ScadaServerCtrl/ScadaServerCtrl.csproj | 3 + .../ScadaServerSvc/InstMain.Designer.cs | 61 -------- ScadaServer/ScadaServerSvc/InstMain.resx | 129 ----------------- ScadaServer/ScadaServerSvc/MainLogic.cs | 2 +- .../ScadaServerSvc/ScadaServerSvc.csproj | 15 +- .../{InstMain.cs => SvcInstaller.cs} | 19 ++- 18 files changed, 274 insertions(+), 306 deletions(-) create mode 100644 ScadaData/ScadaData.Svc/SvcProps.cs rename ScadaServer/Res/{AboutEn.jpg => about_en.jpg} (74%) rename ScadaServer/Res/{AboutRu.jpg => about_ru.jpg} (81%) create mode 100644 ScadaServer/Res/svc/svc_config.xml create mode 100644 ScadaServer/Res/svc/svc_install.bat create mode 100644 ScadaServer/Res/svc/svc_uninstall.bat delete mode 100644 ScadaServer/ScadaServerSvc/InstMain.Designer.cs delete mode 100644 ScadaServer/ScadaServerSvc/InstMain.resx rename ScadaServer/ScadaServerSvc/{InstMain.cs => SvcInstaller.cs} (68%) diff --git a/ScadaData/ScadaData.Svc/BaseSvcInstaller.cs b/ScadaData/ScadaData.Svc/BaseSvcInstaller.cs index 0291bdfcf..61a2ead78 100644 --- a/ScadaData/ScadaData.Svc/BaseSvcInstaller.cs +++ b/ScadaData/ScadaData.Svc/BaseSvcInstaller.cs @@ -19,16 +19,13 @@ * Summary : The base class for Windows service installer * * Author : Mikhail Shiryaev - * Created : 2007 + * Created : 2016 * Modified : 2016 */ using System; using System.Configuration.Install; -using System.IO; -using System.Reflection; using System.ServiceProcess; -using System.Xml; namespace Scada.Svc { @@ -38,67 +35,30 @@ namespace Scada.Svc /// public abstract class BaseSvcInstaller : Installer { - /// - /// Имя файла, содержащего свойства службы - /// - private const string SvcPropsFileName = "svc_config.xml"; - - - /// - /// Загрузить свойства службы - /// - private bool LoadSeriveProps(out string svcName, out string svcDescr) - { - try - { - string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - string fileName = path + Path.DirectorySeparatorChar + SvcPropsFileName; - - if (File.Exists(fileName)) - { - XmlDocument xmlDoc = new XmlDocument(); - xmlDoc.Load(fileName); - - svcName = xmlDoc.DocumentElement.GetChildAsString("ServiceName"); - svcDescr = xmlDoc.DocumentElement.GetChildAsString("Description"); - return true; - } - else - { - svcName = ""; - svcDescr = ""; - return false; - } - } - catch (Exception ex) - { - throw new Exception("Error loading service properties", ex); - } - } - /// /// Инициализировать установщик службы /// protected void Init(string defSvcName, string defDescr) { - string svcName; - string svcDescr; + // загрузка и проверка свойств службы + SvcProps svcProps = new SvcProps(); - if (!LoadSeriveProps(out svcName, out svcDescr)) + if (!svcProps.LoadFromFile()) { - svcName = defSvcName; - svcDescr = defDescr; + svcProps.ServiceName = defSvcName; + svcProps.Description = defDescr; } - if (string.IsNullOrEmpty(svcName)) - throw new ScadaException("Service name must not be null or empty."); + if (string.IsNullOrEmpty(svcProps.ServiceName)) + throw new Exception(SvcProps.ServiceNameEmptyError); + // настройка установщика ServiceInstaller serviceInstaller = new ServiceInstaller(); ServiceProcessInstaller serviceProcessInstaller = new ServiceProcessInstaller(); - serviceInstaller.ServiceName = svcName; - serviceInstaller.DisplayName = svcName; - serviceInstaller.Description = svcDescr ?? ""; + serviceInstaller.ServiceName = svcProps.ServiceName; + serviceInstaller.DisplayName = svcProps.ServiceName; + serviceInstaller.Description = svcProps.Description ?? ""; serviceInstaller.StartType = ServiceStartMode.Automatic; serviceProcessInstaller.Account = ServiceAccount.LocalSystem; diff --git a/ScadaData/ScadaData.Svc/ScadaData.Svc.csproj b/ScadaData/ScadaData.Svc/ScadaData.Svc.csproj index b9bec8988..3c771fdc6 100644 --- a/ScadaData/ScadaData.Svc/ScadaData.Svc.csproj +++ b/ScadaData/ScadaData.Svc/ScadaData.Svc.csproj @@ -31,6 +31,9 @@ bin\Release\ScadaData.Svc.XML + + ..\ScadaData\bin\Release\ScadaData.dll + @@ -42,12 +45,7 @@ Component - - - - {c5dc293f-13ca-435f-a7db-4ba91639c292} - ScadaData - + GL~7N%_NWPPU_Wl1tz)K+FD^(0S;Whw%CsO63-P7OforCTuNx_S38 z$zSpWr%`ey{{ZXmV0F}nCtZ7CXUJZve|3+KCY&j_jq}$Ct=XM7cKxT%J;e3`8;r6>-|=o%8iHXxlXPmG$|PBvlU7I` z+v6=_*hh`=ndS)AKgk$=K@5vWoLpCHOjh)3j5QWJfEauI4hnuJgZVB?0ne!)e;%_1 z%@4SLQ4!+^I-P}TS(b>yQ9eTVcO++RX=cdw zTMGXG8+gna7Y<~`#o3J zO@xVS&gD7idp6E&d&D{konzDJ8izd|a6zQ0asD-u%yG6*!c>*qL+QDzAFC=r=^ARj zH7sEU!t1Ws?(B;aGB@jvzv^BElw=Ieu$wUm+dNN=w*$N+?;hgJA+LJpe*@es7lL++ z8{;XTqzI(4nPWu~%yLpTZ&KLp#Z#k0owi3C!ym;kt^L=@27%8rHUP{UsW#UQVB|wh zGGyxw%{9@JAx|5Y?Eao1)m@68B-$;;ik7-TvD*2n)t5>;G`02Bx=ik2D^n!A%8;Bi z{O5~Z;miqQxFr0~ulPF&e_@?}%I_Fa$z~Cj?0Y^C*zLC1?AttQFH?+5vijENh%yHd z_(~xFgX`8vvKq*1Qxuif=9boyO*Ms}J3oUbdp`3O)x??a2S?(wX~wX6ceIiioZdf3 zt`*t#w(J;EtQ{JMSpNVvcbCWI?(=E6=${b2xwo^XgxErbOD}dtf1n2-Hm{G3xXP0b zMK8kUNFru$eqha*ZbG(Mou2D}owZhb!CM9chfk!qJgID|AsCP~@*$5R?Tcj$kjmwH zGPYh#rXLeMK`H|u-pn_Y@rjGJPBjYK_89(F*ZvlD4XPuL_X>YzA<~e_Kq7GabnV&SYJj)~T(F=B!B`tXC-ue;F6E_y!k^a?WOf?EX<` z!ur9;+_}#j!ZOrHX+Wtp`G>i zL6_pTL2_olk%4xpa~dn2+LroG`xTW&+~W#MD70?jkL?R5A$SCiRc5VTxG{dG!uX)v z>v4ucFJh4=H7B%gUMAr!ZNKnWnEY*9KfqhRY&kdER<@$&Lw` zW~TsA_y zxN#Aae{c?H#WKxrgyx8>LQk{qoQPyOOX+h6wlk8ZE)gq6fh5GHgqKI!f`HTi0I7C5 z%UhWJneb$R>mAMW2C+Ed>~iIt+Mgn<4mmer@{D71@(v}*Stn@rb0ojv{4auCVp3)n zwTu`os!W>;^jYhhQR$CwY1)pCAfjvn*J< zll@=G8uugAjA1;JP2pI3x+fWN%!d!mERN79TXs~HE-KZ{SyQj8t)SB?Q`U)2_9^aJMILJVX#Rt*`CE}WS5MHx>6BZ!9)Q0wCinYxB%=jlMo(%=~3;{Pm%WNHsy3u5}Zi zak!2vG;V9a&=e?Tk))HZL7{VGvH2QeEm(-RhIW@*6CGu$(WOyC2(T^wlfXaalfOUa zlfOUalfOSJNFokRcE3Ex_}k@4%WLbc1WzohMSs_Zy2Mu(sz+4L(A;StUsY gYGy)if1ks75HEQ`bc*%~}W(*OVf delta 3278 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 diff --git a/ScadaServer/Res/svc/svc_config.xml b/ScadaServer/Res/svc/svc_config.xml new file mode 100644 index 000000000..2e9adb8d6 --- /dev/null +++ b/ScadaServer/Res/svc/svc_config.xml @@ -0,0 +1,5 @@ + + + ScadaServerService + + \ No newline at end of file diff --git a/ScadaServer/Res/svc/svc_install.bat b/ScadaServer/Res/svc/svc_install.bat new file mode 100644 index 000000000..3e4beeda2 --- /dev/null +++ b/ScadaServer/Res/svc/svc_install.bat @@ -0,0 +1,2 @@ +cd /d "%~dp0" +C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe ScadaServerSvc.exe \ No newline at end of file diff --git a/ScadaServer/Res/svc/svc_uninstall.bat b/ScadaServer/Res/svc/svc_uninstall.bat new file mode 100644 index 000000000..e5353700f --- /dev/null +++ b/ScadaServer/Res/svc/svc_uninstall.bat @@ -0,0 +1,2 @@ +cd /d "%~dp0" +C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe /u ScadaServerSvc.exe \ No newline at end of file diff --git a/ScadaServer/ScadaServerCtrl/AppPhrases.cs b/ScadaServer/ScadaServerCtrl/AppPhrases.cs index a949718d0..95723ddb6 100644 --- a/ScadaServer/ScadaServerCtrl/AppPhrases.cs +++ b/ScadaServer/ScadaServerCtrl/AppPhrases.cs @@ -116,7 +116,7 @@ private static void SetToDefault() ModulesNode = "Модули"; GeneratorNode = "Генератор"; StatsNode = "Статистика"; - ServiceState = "Состояние службы SCADA-Сервера: {0}"; + ServiceState = "Состояние службы {0}: {1}"; RestartNeeded = "Изменения настроек вступят в силу после перезапуска службы SCADA-Сервера."; GetFileListError = "Ошибка при получении списка файлов данных"; LoadModuleError = "Ошибка при загрузке модуля"; diff --git a/ScadaServer/ScadaServerCtrl/FrmAbout.cs b/ScadaServer/ScadaServerCtrl/FrmAbout.cs index ce42554b1..8ab3aa603 100644 --- a/ScadaServer/ScadaServerCtrl/FrmAbout.cs +++ b/ScadaServer/ScadaServerCtrl/FrmAbout.cs @@ -37,7 +37,7 @@ namespace Scada.Server.Ctrl /// public partial class FrmAbout : Form { - private const string Version = "4.5.0.5"; // версия приложения + private const string Version = "4.5.0.6"; // версия приложения private static FrmAbout frmAbout = null; // форма о программе private string exeDir; // директория исполняемого файла приложения diff --git a/ScadaServer/ScadaServerCtrl/FrmMain.cs b/ScadaServer/ScadaServerCtrl/FrmMain.cs index f065ab5f1..274100b88 100644 --- a/ScadaServer/ScadaServerCtrl/FrmMain.cs +++ b/ScadaServer/ScadaServerCtrl/FrmMain.cs @@ -26,6 +26,7 @@ using Scada.Client; using Scada.Data; using Scada.Server.Modules; +using Scada.Svc; using Scada.UI; using System; using System.Collections.Generic; @@ -80,6 +81,10 @@ public NodeTag(params TabPage[] tabPages) /// private const string ErrFileName = "ScadaServerCtrl.err"; /// + /// Имя службы по умолчанию + /// + private const string DefServiceName = "ScadaServerService"; + /// /// Интервал ожидания перезапуска службы /// private static readonly TimeSpan WaitForRestartSpan = TimeSpan.FromSeconds(30); @@ -99,6 +104,7 @@ public NodeTag(params TabPage[] tabPages) private AppDirs appDirs; // директории приложения private Log errLog; // журнал ошибок приложения private Mutex mutex; // объект для проверки запуска второй копии программы + private string serviceName; // имя службы private ServiceController svcContr; // контроллер службы private ServiceControllerStatus prevSvcStatus; // предыдущее состояние службы private Icon[] notifyIcons; // значки для представления состояния службы @@ -148,6 +154,7 @@ public FrmMain() errLog = new Log(Log.Formats.Simple); errLog.Encoding = Encoding.UTF8; mutex = null; + serviceName = DefServiceName; svcContr = null; prevSvcStatus = ServiceControllerStatus.Stopped; @@ -234,7 +241,7 @@ private void SetServiceStateText(ServiceControllerStatus? status) break; } - lblServiceState.Text = string.Format(AppPhrases.ServiceState, state); + lblServiceState.Text = string.Format(AppPhrases.ServiceState, serviceName, state); } /// @@ -289,6 +296,37 @@ private void ShowForm() nodeFiles.Expand(); } + /// + /// Загрузить локализацию приложения + /// + private void Localize(StringBuilder sbError) + { + string errMsg; + + if (Localization.LoadingRequired(appDirs.LangDir, "ScadaData")) + { + if (Localization.LoadDictionaries(appDirs.LangDir, "ScadaData", out errMsg)) + CommonPhrases.Init(); + else + sbError.AppendLine(errMsg); + } + + if (Localization.LoadingRequired(appDirs.LangDir, "ScadaServer")) + { + if (Localization.LoadDictionaries(appDirs.LangDir, "ScadaServer", out errMsg)) + { + ModPhrases.InitFromDictionaries(); + Translator.TranslateForm(this, "Scada.Server.Ctrl.FrmMain", toolTip, cmsNotify); + AppPhrases.Init(); + TranslateTree(); + } + else + { + sbError.AppendLine(errMsg); + } + } + } + /// /// Перевести текст узлов дерева /// @@ -326,6 +364,45 @@ private void PrepareTree() treeView.SelectedNode = nodeCommonParams; } + /// + /// Загрузить имя службы + /// + private void LoadServiceName(StringBuilder sbError) + { + SvcProps svcProps = new SvcProps(); + string svcPropsFileName = appDirs.ExeDir + SvcProps.SvcPropsFileName; + + if (File.Exists(svcPropsFileName)) + { + string errMsg; + if (svcProps.LoadFromFile(svcPropsFileName, out errMsg)) + serviceName = svcProps.ServiceName; + else + sbError.AppendLine(errMsg); + } + } + + /// + /// Проверить, что вторая копия приложения не запущена + /// + private void CheckSecondInstance(StringBuilder sbError, out bool closeApp) + { + closeApp = false; + + try + { + bool createdNew; + mutex = new Mutex(true, "ScadaServerCtrlMutex - " + serviceName, out createdNew); + + if (!createdNew) + closeApp = true; + } + catch (Exception ex) + { + sbError.AppendLine(AppPhrases.CheckSecondInstanceError + ":\r\n" + ex.Message); + } + } + /// /// Настроить элементы управления генератора команды /// @@ -625,50 +702,25 @@ private void FrmMain_Load(object sender, EventArgs e) stateFileName = appDirs.LogDir + StateFileName; logFileName = appDirs.LogDir + LogFileName; - // локализация приложения + // переменные для записи информации об ошибках StringBuilder sbError = new StringBuilder(); string errMsg; - if (Localization.LoadingRequired(appDirs.LangDir, "ScadaData")) - { - if (Localization.LoadDictionaries(appDirs.LangDir, "ScadaData", out errMsg)) - CommonPhrases.Init(); - else - sbError.AppendLine(errMsg); - } + // локализация приложения + Localize(sbError); - if (Localization.LoadingRequired(appDirs.LangDir, "ScadaServer")) - { - if (Localization.LoadDictionaries(appDirs.LangDir, "ScadaServer", out errMsg)) - { - ModPhrases.InitFromDictionaries(); - Translator.TranslateForm(this, "Scada.Server.Ctrl.FrmMain", toolTip, cmsNotify); - AppPhrases.Init(); - TranslateTree(); - notifyIcon.Text = AppPhrases.MainFormTitle; - } - else - { - sbError.AppendLine(errMsg); - } - } + // загрузка имени службы + LoadServiceName(sbError); // проверка запуска второй копии программы - try - { - bool createdNew; - mutex = new Mutex(true, "ScadaServerCtrlMutex", out createdNew); + bool closeApp; + CheckSecondInstance(sbError, out closeApp); - if (!createdNew) - { - ScadaUiUtils.ShowInfo(AppPhrases.SecondInstanceClosed); - Close(); - return; - } - } - catch (Exception ex) + if (closeApp) { - sbError.AppendLine(AppPhrases.CheckSecondInstanceError + ":\r\n" + ex.Message); + ScadaUiUtils.ShowInfo(AppPhrases.SecondInstanceClosed); + Close(); + return; } // подготовка интерфейса @@ -681,11 +733,12 @@ private void FrmMain_Load(object sender, EventArgs e) dtpEvTime.Value = dtpEvDate2.Value = DateTime.Today; pnlCmdData.Top = pnlCmdKP.Top = pnlCmdVal.Top; TuneGenCmd(); + notifyIcon.Text = serviceName == DefServiceName ? AppPhrases.MainFormTitle : serviceName; // определение состояния службы try { - svcContr = new ServiceController("ScadaServerService"); + svcContr = new ServiceController(serviceName); prevSvcStatus = svcContr.Status; SetServiceStateText(prevSvcStatus); SetServiceButtonsEnabled(prevSvcStatus); diff --git a/ScadaServer/ScadaServerCtrl/Lang/ScadaServer.en-GB.xml b/ScadaServer/ScadaServerCtrl/Lang/ScadaServer.en-GB.xml index 23fb57f50..10427094c 100644 --- a/ScadaServer/ScadaServerCtrl/Lang/ScadaServer.en-GB.xml +++ b/ScadaServer/ScadaServerCtrl/Lang/ScadaServer.en-GB.xml @@ -198,7 +198,7 @@ Modules Generator Stats - SCADA-Server service state: {0} + State of {0}: {1} Settings changes will take effect after restarting SCADA-Server service. Error getting list of data files Error loading module diff --git a/ScadaServer/ScadaServerCtrl/ScadaServerCtrl.csproj b/ScadaServer/ScadaServerCtrl/ScadaServerCtrl.csproj index dafe98a7a..f9be40a27 100644 --- a/ScadaServer/ScadaServerCtrl/ScadaServerCtrl.csproj +++ b/ScadaServer/ScadaServerCtrl/ScadaServerCtrl.csproj @@ -84,6 +84,9 @@ False ..\..\ScadaData\ScadaData\bin\Release\ScadaData.dll + + ..\..\ScadaData\ScadaData.Svc\bin\Release\ScadaData.Svc.dll + diff --git a/ScadaServer/ScadaServerSvc/InstMain.Designer.cs b/ScadaServer/ScadaServerSvc/InstMain.Designer.cs deleted file mode 100644 index 9e91eed90..000000000 --- a/ScadaServer/ScadaServerSvc/InstMain.Designer.cs +++ /dev/null @@ -1,61 +0,0 @@ -namespace Scada.Server.Svc -{ - partial class InstMain - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Component Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.serviceInstaller = new System.ServiceProcess.ServiceInstaller(); - this.serviceProcessInstaller = new System.ServiceProcess.ServiceProcessInstaller(); - // - // serviceInstaller - // - this.serviceInstaller.Description = "SCADA-Server manages the archive database, performs mathematical calculations and" + - " provides information"; - this.serviceInstaller.DisplayName = "ScadaServerService"; - this.serviceInstaller.ServiceName = "ScadaServerService"; - this.serviceInstaller.StartType = System.ServiceProcess.ServiceStartMode.Automatic; - // - // serviceProcessInstaller - // - this.serviceProcessInstaller.Account = System.ServiceProcess.ServiceAccount.LocalSystem; - this.serviceProcessInstaller.Password = null; - this.serviceProcessInstaller.Username = null; - // - // InstMain - // - this.Installers.AddRange(new System.Configuration.Install.Installer[] { - this.serviceInstaller, - this.serviceProcessInstaller}); - - } - - #endregion - - private System.ServiceProcess.ServiceInstaller serviceInstaller; - private System.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller; - } -} \ No newline at end of file diff --git a/ScadaServer/ScadaServerSvc/InstMain.resx b/ScadaServer/ScadaServerSvc/InstMain.resx deleted file mode 100644 index 293b60a90..000000000 --- a/ScadaServer/ScadaServerSvc/InstMain.resx +++ /dev/null @@ -1,129 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - 17, 56 - - - 17, 17 - - - False - - \ No newline at end of file diff --git a/ScadaServer/ScadaServerSvc/MainLogic.cs b/ScadaServer/ScadaServerSvc/MainLogic.cs index 7d12c9de7..f5758e2b9 100644 --- a/ScadaServer/ScadaServerSvc/MainLogic.cs +++ b/ScadaServer/ScadaServerSvc/MainLogic.cs @@ -92,7 +92,7 @@ static WorkStateNames() /// /// Строковая запись версии приложения /// - public const string AppVersion = "4.5.0.5"; + public const string AppVersion = "4.5.0.6"; /// /// Имя файла журнала приложения /// diff --git a/ScadaServer/ScadaServerSvc/ScadaServerSvc.csproj b/ScadaServer/ScadaServerSvc/ScadaServerSvc.csproj index 91ed41fa7..39c8530d1 100644 --- a/ScadaServer/ScadaServerSvc/ScadaServerSvc.csproj +++ b/ScadaServer/ScadaServerSvc/ScadaServerSvc.csproj @@ -57,6 +57,9 @@ False ..\..\ScadaData\ScadaData\bin\Release\ScadaData.dll + + ..\..\ScadaData\ScadaData.Svc\bin\Release\ScadaData.Svc.dll + @@ -68,15 +71,12 @@ - - Component - - - InstMain.cs - + + Component + Component @@ -87,9 +87,6 @@ - - InstMain.cs - SvcMain.cs diff --git a/ScadaServer/ScadaServerSvc/InstMain.cs b/ScadaServer/ScadaServerSvc/SvcInstaller.cs similarity index 68% rename from ScadaServer/ScadaServerSvc/InstMain.cs rename to ScadaServer/ScadaServerSvc/SvcInstaller.cs index f4fffe57d..6561cc8a4 100644 --- a/ScadaServer/ScadaServerSvc/InstMain.cs +++ b/ScadaServer/ScadaServerSvc/SvcInstaller.cs @@ -1,5 +1,5 @@ /* - * Copyright 2014 Mikhail Shiryaev + * Copyright 2016 Mikhail Shiryaev * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,11 +19,11 @@ * Summary : Service installer * * Author : Mikhail Shiryaev - * Created : 2013 - * Modified : 2013 + * Created : 2016 + * Modified : 2016 */ -using System; +using Scada.Svc; using System.ComponentModel; namespace Scada.Server.Svc @@ -33,11 +33,16 @@ namespace Scada.Server.Svc /// Инсталлятор службы /// [RunInstaller(true)] - public partial class InstMain : System.Configuration.Install.Installer + public class SvcInstaller : BaseSvcInstaller { - public InstMain() + /// + /// Конструктор + /// + public SvcInstaller() { - InitializeComponent(); + Init( + "ScadaServerService", + "SCADA-Server manages the archive database, performs mathematical calculations and provides information"); } } } From cb4d0620c661ec157e057a91852194054d04072f Mon Sep 17 00:00:00 2001 From: 2mik Date: Mon, 25 Apr 2016 19:36:10 +0300 Subject: [PATCH 003/382] ScadaData: service installer fix --- ScadaData/ScadaData.Svc/BaseSvcInstaller.cs | 2 +- ScadaData/ScadaData.Svc/SvcProps.cs | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/ScadaData/ScadaData.Svc/BaseSvcInstaller.cs b/ScadaData/ScadaData.Svc/BaseSvcInstaller.cs index 61a2ead78..497a4512b 100644 --- a/ScadaData/ScadaData.Svc/BaseSvcInstaller.cs +++ b/ScadaData/ScadaData.Svc/BaseSvcInstaller.cs @@ -50,7 +50,7 @@ protected void Init(string defSvcName, string defDescr) } if (string.IsNullOrEmpty(svcProps.ServiceName)) - throw new Exception(SvcProps.ServiceNameEmptyError); + throw new ScadaException(SvcProps.ServiceNameEmptyError); // настройка установщика ServiceInstaller serviceInstaller = new ServiceInstaller(); diff --git a/ScadaData/ScadaData.Svc/SvcProps.cs b/ScadaData/ScadaData.Svc/SvcProps.cs index 90699db8e..11e74d3ea 100644 --- a/ScadaData/ScadaData.Svc/SvcProps.cs +++ b/ScadaData/ScadaData.Svc/SvcProps.cs @@ -104,9 +104,9 @@ public bool LoadFromFile(string fileName, out string errMsg) } catch (Exception ex) { - errMsg = (Localization.UseRussian ? - "Error loading service properties: " : - "Ошибка при загрузке свойств службы: ") + ex.Message; + errMsg = (Localization.UseRussian ? + "Ошибка при загрузке свойств службы: " : + "Error loading service properties: ") + ex.Message; return false; } } @@ -122,7 +122,10 @@ public bool LoadFromFile() if (File.Exists(fileName)) { string errMsg; - return LoadFromFile(fileName, out errMsg); + if (LoadFromFile(fileName, out errMsg)) + return true; + else + throw new ScadaException(errMsg); } else { From 5ef2565e52020f5a510242814fb401540f27568c Mon Sep 17 00:00:00 2001 From: 2mik Date: Tue, 26 Apr 2016 09:30:58 +0300 Subject: [PATCH 004/382] ScadaComm: multiple instances support --- ScadaComm/Res/svc/svc_config.xml | 5 + ScadaComm/Res/svc/svc_install.bat | 2 + ScadaComm/Res/svc/svc_uninstall.bat | 2 + ScadaComm/ScadaCommCtrl/AppPhrases.cs | 6 +- ScadaComm/ScadaCommCtrl/FrmMain.cs | 163 ++++++++++++------ .../ScadaCommCtrl/Lang/ScadaComm.en-GB.xml | 2 +- ScadaComm/ScadaCommCtrl/ScadaCommCtrl.csproj | 3 + ScadaComm/ScadaCommSvc/InstMain.Designer.cs | 60 ------- ScadaComm/ScadaCommSvc/InstMain.resx | 132 -------------- ScadaComm/ScadaCommSvc/ScadaCommSvc.csproj | 16 +- .../{InstMain.cs => SvcInstaller.cs} | 23 +-- 11 files changed, 143 insertions(+), 271 deletions(-) create mode 100644 ScadaComm/Res/svc/svc_config.xml create mode 100644 ScadaComm/Res/svc/svc_install.bat create mode 100644 ScadaComm/Res/svc/svc_uninstall.bat delete mode 100644 ScadaComm/ScadaCommSvc/InstMain.Designer.cs delete mode 100644 ScadaComm/ScadaCommSvc/InstMain.resx rename ScadaComm/ScadaCommSvc/{InstMain.cs => SvcInstaller.cs} (70%) diff --git a/ScadaComm/Res/svc/svc_config.xml b/ScadaComm/Res/svc/svc_config.xml new file mode 100644 index 000000000..ddc2536cf --- /dev/null +++ b/ScadaComm/Res/svc/svc_config.xml @@ -0,0 +1,5 @@ + + + ScadaCommService + + \ No newline at end of file diff --git a/ScadaComm/Res/svc/svc_install.bat b/ScadaComm/Res/svc/svc_install.bat new file mode 100644 index 000000000..d2441ffec --- /dev/null +++ b/ScadaComm/Res/svc/svc_install.bat @@ -0,0 +1,2 @@ +cd /d "%~dp0" +C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe ScadaCommSvc.exe \ No newline at end of file diff --git a/ScadaComm/Res/svc/svc_uninstall.bat b/ScadaComm/Res/svc/svc_uninstall.bat new file mode 100644 index 000000000..0f1c4cfe5 --- /dev/null +++ b/ScadaComm/Res/svc/svc_uninstall.bat @@ -0,0 +1,2 @@ +cd /d "%~dp0" +C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe /u ScadaCommSvc.exe \ No newline at end of file diff --git a/ScadaComm/ScadaCommCtrl/AppPhrases.cs b/ScadaComm/ScadaCommCtrl/AppPhrases.cs index 67d1ce853..95070637d 100644 --- a/ScadaComm/ScadaCommCtrl/AppPhrases.cs +++ b/ScadaComm/ScadaCommCtrl/AppPhrases.cs @@ -1,5 +1,5 @@ /* - * Copyright 2015 Mikhail Shiryaev + * Copyright 2016 Mikhail Shiryaev * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ * * Author : Mikhail Shiryaev * Created : 2014 - * Modified : 2015 + * Modified : 2016 */ namespace Scada.Comm.Ctrl @@ -88,7 +88,7 @@ private static void SetToDefault() LinesNode = "Линии связи"; LineStatsNode = "Статистика линии связи"; StatsNode = "Статистика"; - ServiceState = "Состояние службы SCADA-Коммуникатора: {0}"; + ServiceState = "Состояние службы {0}: {1}"; RestartNeeded = "Изменения настроек вступят в силу после перезапуска службы SCADA-Коммуникатора."; Yes = "Да"; No = "Нет"; diff --git a/ScadaComm/ScadaCommCtrl/FrmMain.cs b/ScadaComm/ScadaCommCtrl/FrmMain.cs index d19856895..23ab78433 100644 --- a/ScadaComm/ScadaCommCtrl/FrmMain.cs +++ b/ScadaComm/ScadaCommCtrl/FrmMain.cs @@ -1,5 +1,5 @@ /* - * Copyright 2015 Mikhail Shiryaev + * Copyright 2016 Mikhail Shiryaev * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,13 +20,14 @@ * * Author : Mikhail Shiryaev * Created : 2008 - * Modified : 2015 + * Modified : 2016 */ using Scada.Client; using Scada.Comm.Channels; using Scada.Comm.Devices; using Scada.Data; +using Scada.Svc; using Scada.UI; using System; using System.Collections.Generic; @@ -138,6 +139,10 @@ public CustomParam(string name, string value) /// private const string ErrFileName = "ScadaCommCtrl.err"; /// + /// Имя службы по умолчанию + /// + private const string DefServiceName = "ScadaCommService"; + /// /// Нулевое время, отформатированное в соответствии с культурой приложения /// private static readonly string ZeroTime = new DateTime(0).ToString("T", Localization.Culture); @@ -147,6 +152,7 @@ public CustomParam(string name, string value) private Mutex mutex; // объект для проверки запуска второй копии программы private Icon icoStart; // пиктограмма работающей службы private Icon icoStop; // пиктограмма остановленной службы + private string serviceName; // имя службы private ServiceController svcContr; // контроллер службы private ServiceControllerStatus prevSvcStatus; // предыдущее состояние службы @@ -220,6 +226,7 @@ public FrmMain() mutex = null; icoStart = Icon.FromHandle((ilMain.Images["star_on.ico"] as Bitmap).GetHicon()); icoStop = Icon.FromHandle((ilMain.Images["star_off.ico"] as Bitmap).GetHicon()); + serviceName = DefServiceName; svcContr = null; prevSvcStatus = ServiceControllerStatus.Stopped; @@ -260,6 +267,90 @@ public FrmMain() } + /// + /// Отобразить форму - развернуть и сделать активной + /// + private void ShowForm() + { + Show(); + ShowInTaskbar = true; + WindowState = FormWindowState.Normal; + Activate(); + + // развёртывание узла линий связи, т.к. состояние узлов не сохраняется при скрытии формы + nodeLines.Expand(); + } + + /// + /// Загрузить локализацию приложения + /// + private void Localize(StringBuilder sbError) + { + string errMsg; + + if (Localization.LoadingRequired(appDirs.LangDir, "ScadaData")) + { + if (Localization.LoadDictionaries(appDirs.LangDir, "ScadaData", out errMsg)) + CommonPhrases.Init(); + else + sbError.AppendLine(errMsg); + } + + if (Localization.LoadingRequired(appDirs.LangDir, "ScadaComm")) + { + if (Localization.LoadDictionaries(appDirs.LangDir, "ScadaComm", out errMsg)) + { + Translator.TranslateForm(this, "Scada.Comm.Ctrl.FrmMain", toolTip, cmsNotify, cmsLine, cmsKP); + AppPhrases.Init(); + CommPhrases.InitFromDictionaries(); + TranslateTree(); + } + else + { + sbError.AppendLine(errMsg); + } + } + } + + /// + /// Загрузить имя службы + /// + private void LoadServiceName(StringBuilder sbError) + { + SvcProps svcProps = new SvcProps(); + string svcPropsFileName = appDirs.ExeDir + SvcProps.SvcPropsFileName; + + if (File.Exists(svcPropsFileName)) + { + string errMsg; + if (svcProps.LoadFromFile(svcPropsFileName, out errMsg)) + serviceName = svcProps.ServiceName; + else + sbError.AppendLine(errMsg); + } + } + + /// + /// Проверить, что вторая копия приложения не запущена + /// + private void CheckSecondInstance(StringBuilder sbError, out bool closeApp) + { + closeApp = false; + + try + { + bool createdNew; + mutex = new Mutex(true, "ScadaCommCtrlMutex - " + serviceName, out createdNew); + + if (!createdNew) + closeApp = true; + } + catch (Exception ex) + { + sbError.AppendLine(AppPhrases.CheckSecondInstanceError + ":\r\n" + ex.Message); + } + } + /// /// Установить текст строки состояния в зависимости от статуса службы /// @@ -295,7 +386,7 @@ private void SetServiceStateText(ServiceControllerStatus? status) break; } - lblServiceState.Text = string.Format(AppPhrases.ServiceState, state); + lblServiceState.Text = string.Format(AppPhrases.ServiceState, serviceName, state); } /// @@ -326,20 +417,6 @@ private void SetServiceButtonsEnabled(ServiceControllerStatus? status) } } - /// - /// Отобразить форму - развернуть и сделать активной - /// - private void ShowForm() - { - Show(); - ShowInTaskbar = true; - WindowState = FormWindowState.Normal; - Activate(); - - // развёртывание узла линий связи, т.к. состояние узлов не сохраняется при скрытии формы - nodeLines.Expand(); - } - /// /// Перевести текст узлов дерева /// @@ -950,56 +1027,31 @@ private void FrmMain_Load(object sender, EventArgs e) // установка имени файла журнала ошибок errLog.FileName = appDirs.LogDir + ErrFileName; - // локализация приложения + // переменные для записи информации об ошибках StringBuilder sbError = new StringBuilder(); string errMsg; - - if (Localization.LoadingRequired(appDirs.LangDir, "ScadaData")) - { - if (Localization.LoadDictionaries(appDirs.LangDir, "ScadaData", out errMsg)) - CommonPhrases.Init(); - else - sbError.AppendLine(errMsg); - } - if (Localization.LoadingRequired(appDirs.LangDir, "ScadaComm")) - { - if (Localization.LoadDictionaries(appDirs.LangDir, "ScadaComm", out errMsg)) - { - Translator.TranslateForm(this, "Scada.Comm.Ctrl.FrmMain", toolTip, cmsNotify, cmsLine, cmsKP); - AppPhrases.Init(); - CommPhrases.InitFromDictionaries(); - TranslateTree(); - notifyIcon.Text = AppPhrases.MainFormTitle; - } - else - { - sbError.AppendLine(errMsg); - } - } + // локализация приложения + Localize(sbError); + + // загрузка имени службы + LoadServiceName(sbError); // проверка запуска второй копии программы - try - { - bool createdNew; - mutex = new Mutex(true, "ScadaCommCtrlMutex", out createdNew); + bool closeApp; + CheckSecondInstance(sbError, out closeApp); - if (!createdNew) - { - ScadaUiUtils.ShowInfo(AppPhrases.SecondInstanceClosed); - Close(); - return; - } - } - catch (Exception ex) + if (closeApp) { - sbError.AppendLine(AppPhrases.CheckSecondInstanceError + ":\r\n" + ex.Message); + ScadaUiUtils.ShowInfo(AppPhrases.SecondInstanceClosed); + Close(); + return; } // определение состояния службы try { - svcContr = new ServiceController("ScadaCommService"); + svcContr = new ServiceController(serviceName); prevSvcStatus = svcContr.Status; SetServiceStateText(prevSvcStatus); SetServiceButtonsEnabled(prevSvcStatus); @@ -1025,6 +1077,7 @@ private void FrmMain_Load(object sender, EventArgs e) TuneKpCmd(); MakeTree(); FillCommCnlTypes(); + notifyIcon.Text = serviceName == DefServiceName ? AppPhrases.MainFormTitle : serviceName; // получение информации о типах КП GetKpDllInfo(); diff --git a/ScadaComm/ScadaCommCtrl/Lang/ScadaComm.en-GB.xml b/ScadaComm/ScadaCommCtrl/Lang/ScadaComm.en-GB.xml index 4da4b2e41..92f52e63e 100644 --- a/ScadaComm/ScadaCommCtrl/Lang/ScadaComm.en-GB.xml +++ b/ScadaComm/ScadaCommCtrl/Lang/ScadaComm.en-GB.xml @@ -137,7 +137,7 @@ Communication Lines Communication Line Stats Stats - SCADA-Communicator service state: {0} + {0} state: {1} Settings changes will take effect after restarting SCADA-Communicator service. Yes No diff --git a/ScadaComm/ScadaCommCtrl/ScadaCommCtrl.csproj b/ScadaComm/ScadaCommCtrl/ScadaCommCtrl.csproj index 67680b71b..aa99810db 100644 --- a/ScadaComm/ScadaCommCtrl/ScadaCommCtrl.csproj +++ b/ScadaComm/ScadaCommCtrl/ScadaCommCtrl.csproj @@ -67,6 +67,9 @@ False ..\..\ScadaData\ScadaData\bin\Release\ScadaData.dll + + ..\..\ScadaData\ScadaData.Svc\bin\Release\ScadaData.Svc.dll + diff --git a/ScadaComm/ScadaCommSvc/InstMain.Designer.cs b/ScadaComm/ScadaCommSvc/InstMain.Designer.cs deleted file mode 100644 index 6cfbf4006..000000000 --- a/ScadaComm/ScadaCommSvc/InstMain.Designer.cs +++ /dev/null @@ -1,60 +0,0 @@ -namespace Scada.Comm.Svc -{ - partial class InstMain - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Component Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.serviceInstaller = new System.ServiceProcess.ServiceInstaller(); - this.serviceProcessInstaller = new System.ServiceProcess.ServiceProcessInstaller(); - // - // serviceInstaller - // - this.serviceInstaller.Description = "SCADA-Communicator interacts with controllers, transmits data to SCADA-Server"; - this.serviceInstaller.DisplayName = "ScadaCommService"; - this.serviceInstaller.ServiceName = "ScadaCommService"; - this.serviceInstaller.StartType = System.ServiceProcess.ServiceStartMode.Automatic; - // - // serviceProcessInstaller - // - this.serviceProcessInstaller.Account = System.ServiceProcess.ServiceAccount.LocalSystem; - this.serviceProcessInstaller.Password = null; - this.serviceProcessInstaller.Username = null; - // - // InstMain - // - this.Installers.AddRange(new System.Configuration.Install.Installer[] { - this.serviceInstaller, - this.serviceProcessInstaller}); - - } - - #endregion - - private System.ServiceProcess.ServiceInstaller serviceInstaller; - private System.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller; - } -} \ No newline at end of file diff --git a/ScadaComm/ScadaCommSvc/InstMain.resx b/ScadaComm/ScadaCommSvc/InstMain.resx deleted file mode 100644 index 71ea9ae6d..000000000 --- a/ScadaComm/ScadaCommSvc/InstMain.resx +++ /dev/null @@ -1,132 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - 17, 56 - - - 17, 17 - - - True - - - False - - \ No newline at end of file diff --git a/ScadaComm/ScadaCommSvc/ScadaCommSvc.csproj b/ScadaComm/ScadaCommSvc/ScadaCommSvc.csproj index b1eeb9de6..d98272bf5 100644 --- a/ScadaComm/ScadaCommSvc/ScadaCommSvc.csproj +++ b/ScadaComm/ScadaCommSvc/ScadaCommSvc.csproj @@ -59,6 +59,9 @@ False ..\..\ScadaData\ScadaData\bin\Release\ScadaData.dll + + ..\..\ScadaData\ScadaData.Svc\bin\Release\ScadaData.Svc.dll + @@ -69,14 +72,11 @@ - - Component - - - InstMain.cs - + + Component + Component @@ -87,10 +87,6 @@ - - Designer - InstMain.cs - Designer SvcMain.cs diff --git a/ScadaComm/ScadaCommSvc/InstMain.cs b/ScadaComm/ScadaCommSvc/SvcInstaller.cs similarity index 70% rename from ScadaComm/ScadaCommSvc/InstMain.cs rename to ScadaComm/ScadaCommSvc/SvcInstaller.cs index c1abb3e24..a1e417d5d 100644 --- a/ScadaComm/ScadaCommSvc/InstMain.cs +++ b/ScadaComm/ScadaCommSvc/SvcInstaller.cs @@ -1,5 +1,5 @@ /* - * Copyright 2014 Mikhail Shiryaev + * Copyright 2016 Mikhail Shiryaev * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,14 +19,12 @@ * Summary : Service installer * * Author : Mikhail Shiryaev - * Created : 2006 - * Modified : 2006 + * Created : 2016 + * Modified : 2016 */ -using System; -using System.Collections.Generic; +using Scada.Svc; using System.ComponentModel; -using System.Configuration.Install; namespace Scada.Comm.Svc { @@ -35,11 +33,16 @@ namespace Scada.Comm.Svc /// Инсталлятор службы /// [RunInstaller(true)] - public partial class InstMain : Installer + public class SvcInstaller : BaseSvcInstaller { - public InstMain() + /// + /// Конструктор + /// + public SvcInstaller() { - InitializeComponent(); + Init( + "ScadaCommService", + "SCADA-Communicator interacts with controllers, transmits data to SCADA-Server"); } } -} \ No newline at end of file +} From d9dbbff4a52464570fbb91543710bd97e3832315 Mon Sep 17 00:00:00 2001 From: 2mik Date: Tue, 26 Apr 2016 09:31:18 +0300 Subject: [PATCH 005/382] ScadaServer: minor updates --- ScadaServer/ScadaServerCtrl/AppPhrases.cs | 4 +- ScadaServer/ScadaServerCtrl/FrmMain.cs | 295 +++++++++--------- .../Lang/ScadaServer.en-GB.xml | 2 +- .../ScadaServerCtrl/ScadaServerCtrl.csproj | 4 +- 4 files changed, 154 insertions(+), 151 deletions(-) diff --git a/ScadaServer/ScadaServerCtrl/AppPhrases.cs b/ScadaServer/ScadaServerCtrl/AppPhrases.cs index 95723ddb6..230185a3d 100644 --- a/ScadaServer/ScadaServerCtrl/AppPhrases.cs +++ b/ScadaServer/ScadaServerCtrl/AppPhrases.cs @@ -1,5 +1,5 @@ /* - * Copyright 2015 Mikhail Shiryaev + * Copyright 2016 Mikhail Shiryaev * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ * * Author : Mikhail Shiryaev * Created : 2014 - * Modified : 2015 + * Modified : 2016 */ namespace Scada.Server.Ctrl diff --git a/ScadaServer/ScadaServerCtrl/FrmMain.cs b/ScadaServer/ScadaServerCtrl/FrmMain.cs index 274100b88..ea8cb6e2d 100644 --- a/ScadaServer/ScadaServerCtrl/FrmMain.cs +++ b/ScadaServer/ScadaServerCtrl/FrmMain.cs @@ -206,6 +206,91 @@ private ServerComm ServerComm } } + + /// + /// Отобразить форму - развернуть и сделать активной + /// + private void ShowForm() + { + Show(); + ShowInTaskbar = true; + WindowState = FormWindowState.Normal; + Activate(); + + // развёртывание узла файлов данных, т.к. состояние узлов не сохраняется при скрытии формы + nodeFiles.Expand(); + } + + /// + /// Загрузить локализацию приложения + /// + private void Localize(StringBuilder sbError) + { + string errMsg; + + if (Localization.LoadingRequired(appDirs.LangDir, "ScadaData")) + { + if (Localization.LoadDictionaries(appDirs.LangDir, "ScadaData", out errMsg)) + CommonPhrases.Init(); + else + sbError.AppendLine(errMsg); + } + + if (Localization.LoadingRequired(appDirs.LangDir, "ScadaServer")) + { + if (Localization.LoadDictionaries(appDirs.LangDir, "ScadaServer", out errMsg)) + { + ModPhrases.InitFromDictionaries(); + Translator.TranslateForm(this, "Scada.Server.Ctrl.FrmMain", toolTip, cmsNotify); + AppPhrases.Init(); + TranslateTree(); + } + else + { + sbError.AppendLine(errMsg); + } + } + } + + /// + /// Загрузить имя службы + /// + private void LoadServiceName(StringBuilder sbError) + { + SvcProps svcProps = new SvcProps(); + string svcPropsFileName = appDirs.ExeDir + SvcProps.SvcPropsFileName; + + if (File.Exists(svcPropsFileName)) + { + string errMsg; + if (svcProps.LoadFromFile(svcPropsFileName, out errMsg)) + serviceName = svcProps.ServiceName; + else + sbError.AppendLine(errMsg); + } + } + + /// + /// Проверить, что вторая копия приложения не запущена + /// + private void CheckSecondInstance(StringBuilder sbError, out bool closeApp) + { + closeApp = false; + + try + { + bool createdNew; + mutex = new Mutex(true, "ScadaServerCtrlMutex - " + serviceName, out createdNew); + + if (!createdNew) + closeApp = true; + } + catch (Exception ex) + { + sbError.AppendLine(AppPhrases.CheckSecondInstanceError + ":\r\n" + ex.Message); + } + } + /// /// Установить текст строки состояния в зависимости от статуса службы /// @@ -269,64 +354,6 @@ private void SetServiceButtonsEnabled(ServiceControllerStatus? status) } } - /// - /// Установить доступность кнопок действий с модулями - /// - private void SetModulesButtonsEnabled() - { - int selInd = lbModDll.SelectedIndex; - btnMoveUpMod.Enabled = selInd > 0; - btnMoveDownMod.Enabled = selInd < lbModDll.Items.Count - 1; - btnDelMod.Enabled = selInd >= 0; - if (selInd < 0) - btnModProps.Enabled = false; - } - - /// - /// Отобразить форму - развернуть и сделать активной - /// - private void ShowForm() - { - Show(); - ShowInTaskbar = true; - WindowState = FormWindowState.Normal; - Activate(); - - // развёртывание узла файлов данных, т.к. состояние узлов не сохраняется при скрытии формы - nodeFiles.Expand(); - } - - /// - /// Загрузить локализацию приложения - /// - private void Localize(StringBuilder sbError) - { - string errMsg; - - if (Localization.LoadingRequired(appDirs.LangDir, "ScadaData")) - { - if (Localization.LoadDictionaries(appDirs.LangDir, "ScadaData", out errMsg)) - CommonPhrases.Init(); - else - sbError.AppendLine(errMsg); - } - - if (Localization.LoadingRequired(appDirs.LangDir, "ScadaServer")) - { - if (Localization.LoadDictionaries(appDirs.LangDir, "ScadaServer", out errMsg)) - { - ModPhrases.InitFromDictionaries(); - Translator.TranslateForm(this, "Scada.Server.Ctrl.FrmMain", toolTip, cmsNotify); - AppPhrases.Init(); - TranslateTree(); - } - else - { - sbError.AppendLine(errMsg); - } - } - } - /// /// Перевести текст узлов дерева /// @@ -365,44 +392,83 @@ private void PrepareTree() } /// - /// Загрузить имя службы + /// Подготовить страницу файлов к работе /// - private void LoadServiceName(StringBuilder sbError) + private void PrepareFilesPage() { - SvcProps svcProps = new SvcProps(); - string svcPropsFileName = appDirs.ExeDir + SvcProps.SvcPropsFileName; - - if (File.Exists(svcPropsFileName)) + // настройка элементов управления и определение директории просматриваемых данных + if (lastNode == nodeBase) { - string errMsg; - if (svcProps.LoadFromFile(svcPropsFileName, out errMsg)) - serviceName = svcProps.ServiceName; + rbFilesMain.CheckedChanged -= rbFiles_CheckedChanged; + rbFilesMain.Checked = true; + rbFilesMain.CheckedChanged += rbFiles_CheckedChanged; + rbFilesMain.Enabled = rbFilesCopy.Enabled = false; + txtDataDir.Text = settings.BaseDATDir; + } + else + { + rbFilesMain.Enabled = rbFilesCopy.Enabled = true; + string arcDir = rbFilesMain.Checked ? settings.ArcDir : settings.ArcCopyDir; + + if (lastNode == nodeCurSrez) + txtDataDir.Text = arcDir + @"Cur\"; + else if (lastNode == nodeMinSrez) + txtDataDir.Text = arcDir + @"Min\"; + else if (lastNode == nodeHrSrez) + txtDataDir.Text = arcDir + @"Hour\"; + else if (lastNode == nodeEvents) + txtDataDir.Text = arcDir + @"Events\"; else - sbError.AppendLine(errMsg); + txtDataDir.Text = ""; } - } - /// - /// Проверить, что вторая копия приложения не запущена - /// - private void CheckSecondInstance(StringBuilder sbError, out bool closeApp) - { - closeApp = false; + // получение списка файлов данных + lbFiles.Items.Clear(); - try + if (txtDataDir.Text != "") { - bool createdNew; - mutex = new Mutex(true, "ScadaServerCtrlMutex - " + serviceName, out createdNew); + try + { + DirectoryInfo dirInfo = new DirectoryInfo(txtDataDir.Text); + FileInfo[] fileInfoArr = dirInfo.GetFiles("*.dat", SearchOption.TopDirectoryOnly); - if (!createdNew) - closeApp = true; + foreach (FileInfo fileInfo in fileInfoArr) + lbFiles.Items.Add(Path.GetFileName(fileInfo.Name)); + } + catch (Exception ex) + { + string errMsg = AppPhrases.GetFileListError + ":\r\n" + ex.Message; + errLog.WriteAction(errMsg); + ScadaUiUtils.ShowError(errMsg); + } } - catch (Exception ex) + + // выбор первого файла в списке и установка доступности кнопок просмотра и редактирования файлов + if (lbFiles.Items.Count > 0) { - sbError.AppendLine(AppPhrases.CheckSecondInstanceError + ":\r\n" + ex.Message); + lbFiles.SelectedIndex = 0; + btnViewFile.Enabled = true; + btnEditFile.Enabled = lastNode != nodeBase; + } + else + { + btnViewFile.Enabled = btnEditFile.Enabled = false; } } + /// + /// Установить доступность кнопок действий с модулями + /// + private void SetModulesButtonsEnabled() + { + int selInd = lbModDll.SelectedIndex; + btnMoveUpMod.Enabled = selInd > 0; + btnMoveDownMod.Enabled = selInd < lbModDll.Items.Count - 1; + btnDelMod.Enabled = selInd >= 0; + if (selInd < 0) + btnModProps.Enabled = false; + } + /// /// Настроить элементы управления генератора команды /// @@ -584,71 +650,6 @@ private bool ApplySettings() } } - /// - /// Подготовить страницу файлов к работе - /// - private void PrepareFilesPage() - { - // настройка элементов управления и определение директории просматриваемых данных - if (lastNode == nodeBase) - { - rbFilesMain.CheckedChanged -= rbFiles_CheckedChanged; - rbFilesMain.Checked = true; - rbFilesMain.CheckedChanged += rbFiles_CheckedChanged; - rbFilesMain.Enabled = rbFilesCopy.Enabled = false; - txtDataDir.Text = settings.BaseDATDir; - } - else - { - rbFilesMain.Enabled = rbFilesCopy.Enabled = true; - string arcDir = rbFilesMain.Checked ? settings.ArcDir : settings.ArcCopyDir; - - if (lastNode == nodeCurSrez) - txtDataDir.Text = arcDir + @"Cur\"; - else if (lastNode == nodeMinSrez) - txtDataDir.Text = arcDir + @"Min\"; - else if (lastNode == nodeHrSrez) - txtDataDir.Text = arcDir + @"Hour\"; - else if (lastNode == nodeEvents) - txtDataDir.Text = arcDir + @"Events\"; - else - txtDataDir.Text = ""; - } - - // получение списка файлов данных - lbFiles.Items.Clear(); - - if (txtDataDir.Text != "") - { - try - { - DirectoryInfo dirInfo = new DirectoryInfo(txtDataDir.Text); - FileInfo[] fileInfoArr = dirInfo.GetFiles("*.dat", SearchOption.TopDirectoryOnly); - - foreach (FileInfo fileInfo in fileInfoArr) - lbFiles.Items.Add(Path.GetFileName(fileInfo.Name)); - } - catch (Exception ex) - { - string errMsg = AppPhrases.GetFileListError + ":\r\n" + ex.Message; - errLog.WriteAction(errMsg); - ScadaUiUtils.ShowError(errMsg); - } - } - - // выбор первого файла в списке и установка доступности кнопок просмотра и редактирования файлов - if (lbFiles.Items.Count > 0) - { - lbFiles.SelectedIndex = 0; - btnViewFile.Enabled = true; - btnEditFile.Enabled = lastNode != nodeBase; - } - else - { - btnViewFile.Enabled = btnEditFile.Enabled = false; - } - } - /// /// Получить пользовательский интерфейс модуля из словаря, создав его при необходимости /// diff --git a/ScadaServer/ScadaServerCtrl/Lang/ScadaServer.en-GB.xml b/ScadaServer/ScadaServerCtrl/Lang/ScadaServer.en-GB.xml index 10427094c..96d3900c0 100644 --- a/ScadaServer/ScadaServerCtrl/Lang/ScadaServer.en-GB.xml +++ b/ScadaServer/ScadaServerCtrl/Lang/ScadaServer.en-GB.xml @@ -198,7 +198,7 @@ Modules Generator Stats - State of {0}: {1} + {0} state: {1} Settings changes will take effect after restarting SCADA-Server service. Error getting list of data files Error loading module diff --git a/ScadaServer/ScadaServerCtrl/ScadaServerCtrl.csproj b/ScadaServer/ScadaServerCtrl/ScadaServerCtrl.csproj index f9be40a27..729244c8f 100644 --- a/ScadaServer/ScadaServerCtrl/ScadaServerCtrl.csproj +++ b/ScadaServer/ScadaServerCtrl/ScadaServerCtrl.csproj @@ -14,6 +14,7 @@ 512 + false publish\ true Disk @@ -26,7 +27,6 @@ true 0 1.0.0.%2a - false false true @@ -58,6 +58,7 @@ prompt true true + ..\..\..\..\Program Files (x86)\Microsoft Visual Studio 14.0\Team Tools\Static Analysis Tools\Rule Sets\MinimumRecommendedRules.ruleset bin\Release\ @@ -68,6 +69,7 @@ prompt false false + ..\..\..\..\Program Files (x86)\Microsoft Visual Studio 14.0\Team Tools\Static Analysis Tools\Rule Sets\MinimumRecommendedRules.ruleset app.manifest From 015bfaf4a1791301bddec36d52a68d1c60fa0130 Mon Sep 17 00:00:00 2001 From: 2mik Date: Tue, 26 Apr 2016 09:49:47 +0300 Subject: [PATCH 006/382] Update phrases --- ScadaComm/ScadaCommCtrl/Lang/ScadaComm.en-GB.xml | 2 +- ScadaServer/ScadaServerCtrl/Lang/ScadaServer.en-GB.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ScadaComm/ScadaCommCtrl/Lang/ScadaComm.en-GB.xml b/ScadaComm/ScadaCommCtrl/Lang/ScadaComm.en-GB.xml index 92f52e63e..cf5dca92b 100644 --- a/ScadaComm/ScadaCommCtrl/Lang/ScadaComm.en-GB.xml +++ b/ScadaComm/ScadaCommCtrl/Lang/ScadaComm.en-GB.xml @@ -137,7 +137,7 @@ Communication Lines Communication Line Stats Stats - {0} state: {1} + State of {0}: {1} Settings changes will take effect after restarting SCADA-Communicator service. Yes No diff --git a/ScadaServer/ScadaServerCtrl/Lang/ScadaServer.en-GB.xml b/ScadaServer/ScadaServerCtrl/Lang/ScadaServer.en-GB.xml index 96d3900c0..10427094c 100644 --- a/ScadaServer/ScadaServerCtrl/Lang/ScadaServer.en-GB.xml +++ b/ScadaServer/ScadaServerCtrl/Lang/ScadaServer.en-GB.xml @@ -198,7 +198,7 @@ Modules Generator Stats - {0} state: {1} + State of {0}: {1} Settings changes will take effect after restarting SCADA-Server service. Error getting list of data files Error loading module From 729ede24a2b2746fc83be56a1cb4def87bfb4d17 Mon Sep 17 00:00:00 2001 From: 2mik Date: Wed, 27 Apr 2016 12:37:14 +0300 Subject: [PATCH 007/382] PlgScheme: debug mode --- .../OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.cs b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.cs index 3a096694b..59b7c3988 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.cs @@ -23,11 +23,10 @@ * Modified : 2016 */ -//#define DEBUG_MODE +#define DEBUG_MODE using Scada.Scheme; using System; -using System.Web; namespace Scada.Web.Plugins.Scheme { @@ -57,8 +56,11 @@ protected void Page_Load(object sender, EventArgs e) UserData userData = UserData.GetUserData(); #if DEBUG_MODE - appData.Init(Server.MapPath("~")); debugMode = true; + appData.Init(Server.MapPath("~")); + appData.UserMonitor.AddUser(userData); + string errMsg; + userData.Login("admin", out errMsg); #else debugMode = false; #endif From f5af7a17591a278895bf19a2653b5fe5e756251a Mon Sep 17 00:00:00 2001 From: 2mik Date: Wed, 27 Apr 2016 13:59:53 +0300 Subject: [PATCH 008/382] ScadaComm: minor updates --- ScadaComm/ScadaCommCommon/Channels/CommTcpServerLogic.cs | 6 ++++-- ScadaComm/ScadaCommCommon/Channels/TcpConnection.cs | 2 +- ScadaComm/ScadaCommSvc/CommLine.cs | 7 ++++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/ScadaComm/ScadaCommCommon/Channels/CommTcpServerLogic.cs b/ScadaComm/ScadaCommCommon/Channels/CommTcpServerLogic.cs index 26e355a3b..a6a7d3a31 100644 --- a/ScadaComm/ScadaCommCommon/Channels/CommTcpServerLogic.cs +++ b/ScadaComm/ScadaCommCommon/Channels/CommTcpServerLogic.cs @@ -225,7 +225,8 @@ protected void Execute() tcpConn = new TcpConnection(tcpClient); tcpConn.WriteToLog = WriteToLog; WriteToLog(string.Format(Localization.UseRussian ? - "{0} Соединение с клиентом {1}" : "{0} Connect to the client {1}", + "{0} Соединение с клиентом {1}" : + "{0} Connect to the client {1}", CommUtils.GetNowDT(), tcpConn.RemoteAddress)); tcpConnList.Add(tcpConn); @@ -253,7 +254,8 @@ protected void Execute() if ((nowDT - tcpConn.ActivityDT).TotalSeconds > inactiveTime || tcpConn.Broken) { WriteToLog(string.Format(Localization.UseRussian ? - "{0} Отключение клиента {1}" : "{0} Disconnect the client {1}", + "{0} Отключение клиента {1}" : + "{0} Disconnect the client {1}", nowDT.ToString(CommUtils.CommLineDTFormat), tcpConn.RemoteAddress)); tcpConn.Close(); tcpConnList.RemoveAt(connInd); diff --git a/ScadaComm/ScadaCommCommon/Channels/TcpConnection.cs b/ScadaComm/ScadaCommCommon/Channels/TcpConnection.cs index f00a71f8f..406a57285 100644 --- a/ScadaComm/ScadaCommCommon/Channels/TcpConnection.cs +++ b/ScadaComm/ScadaCommCommon/Channels/TcpConnection.cs @@ -145,7 +145,7 @@ public int MaxLineSize public bool JustConnected { get; set; } /// - /// Получить или установить признак, что соединение обрвано и его необходимо закрыть + /// Получить или установить признак, что соединение обровано и его необходимо закрыть /// public bool Broken { get; set; } diff --git a/ScadaComm/ScadaCommSvc/CommLine.cs b/ScadaComm/ScadaCommSvc/CommLine.cs index 66fc1c0bc..e08f776d6 100644 --- a/ScadaComm/ScadaCommSvc/CommLine.cs +++ b/ScadaComm/ScadaCommSvc/CommLine.cs @@ -881,9 +881,10 @@ private void WorkCycle() { KPInvalidateCurData(kpLogic); log.WriteLine(); - log.WriteAction(Localization.UseRussian ? - "Невозможно выполнить сеанс опроса КП, т.к. соединение не установлено" : - "Unable to communicate with the device because connection is not established"); + log.WriteAction(string.Format(Localization.UseRussian ? + "Невозможно выполнить сеанс связи с {0}, т.к. соединение не установлено" : + "Unable to communicate with {0} because connection is not established", + kpLogic.Caption)); } else { From e3739b038e6b476498dd80e97548f677b62ea077 Mon Sep 17 00:00:00 2001 From: 2mik Date: Wed, 27 Apr 2016 15:59:11 +0300 Subject: [PATCH 009/382] ScadaComm: TCP server fix --- ScadaComm/ScadaCommCommon/Channels/CommTcpChannelLogic.cs | 2 +- ScadaComm/ScadaCommCommon/Channels/TcpConnection.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ScadaComm/ScadaCommCommon/Channels/CommTcpChannelLogic.cs b/ScadaComm/ScadaCommCommon/Channels/CommTcpChannelLogic.cs index c32a439bb..103212f6e 100644 --- a/ScadaComm/ScadaCommCommon/Channels/CommTcpChannelLogic.cs +++ b/ScadaComm/ScadaCommCommon/Channels/CommTcpChannelLogic.cs @@ -88,7 +88,7 @@ public override void Init(SortedList commCnlParams, List kpByCallNumList; if (!kpCallNumDict.TryGetValue(callNum, out kpByCallNumList)) diff --git a/ScadaComm/ScadaCommCommon/Channels/TcpConnection.cs b/ScadaComm/ScadaCommCommon/Channels/TcpConnection.cs index 406a57285..cdc24d84a 100644 --- a/ScadaComm/ScadaCommCommon/Channels/TcpConnection.cs +++ b/ScadaComm/ScadaCommCommon/Channels/TcpConnection.cs @@ -432,7 +432,7 @@ public override string ToString() } else { - sb.Append(Localization.UseRussian ? "КП " : "Device "); + sb.Append(Localization.UseRussian ? "КП " : "Devices "); for (int i = 0, lastInd = kpCnt - 1; i < kpCnt; i++) { sb.Append(relatedKPList[i].Number); From 662b8f04c3b1d4b83c17b0191a8559b0614708b5 Mon Sep 17 00:00:00 2001 From: 2mik Date: Wed, 27 Apr 2016 18:17:33 +0300 Subject: [PATCH 010/382] ScadaComm: CPU load fix --- ScadaComm/ScadaCommSvc/CommLine.cs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/ScadaComm/ScadaCommSvc/CommLine.cs b/ScadaComm/ScadaCommSvc/CommLine.cs index e08f776d6..46072a89c 100644 --- a/ScadaComm/ScadaCommSvc/CommLine.cs +++ b/ScadaComm/ScadaCommSvc/CommLine.cs @@ -886,9 +886,8 @@ private void WorkCycle() "Unable to communicate with {0} because connection is not established", kpLogic.Caption)); } - else + else if (KPSession(kpLogic)) { - KPSession(kpLogic); commCnt++; } WriteKPInfo(kpLogic); @@ -1008,11 +1007,12 @@ private void CommCnlAfterSession(KPLogic kpLogic) /// /// Выполнить сеанс опроса КП /// - private void KPSession(KPLogic kpLogic) + private bool KPSession(KPLogic kpLogic) { try { kpLogic.Session(); + return true; } catch (Exception ex) { @@ -1020,6 +1020,7 @@ private void KPSession(KPLogic kpLogic) log.WriteAction((Localization.UseRussian ? "Ошибка при выполнении сеанса опроса КП: " : "Error communicating with the device: ") + ex.Message); + return false; } } @@ -1044,11 +1045,12 @@ private void KPInvalidateCurData(KPLogic kpLogic) /// /// Отправить команду ТУ /// - private void KPSendCmd(KPLogic kpLogic, Command cmd) + private bool KPSendCmd(KPLogic kpLogic, Command cmd) { try { kpLogic.SendCmd(cmd); + return true; } catch (Exception ex) { @@ -1056,6 +1058,7 @@ private void KPSendCmd(KPLogic kpLogic, Command cmd) log.WriteAction((Localization.UseRussian ? "Ошибка при отправке команды ТУ: " : "Error sending command: ") + ex.Message); + return false; } } @@ -1127,15 +1130,16 @@ private void ProcCommands(ref int commCnt, out KPLogic extraKP) CommCnlBeforeSession(kpLogic); if (kpLogic.ConnRequired && (kpLogic.Connection == null || !kpLogic.Connection.Connected)) { - log.WriteAction(Localization.UseRussian ? - "Невозможно отправить команду ТУ, т.к. соединение не установлено: " : - "Unable to send command because connection is not established"); + log.WriteAction(string.Format(Localization.UseRussian ? + "Невозможно отправить команду ТУ для {0}, т.к. соединение не установлено: " : + "Unable to send command to {0} because connection is not established", + kpLogic.Caption)); } else { - KPSendCmd(kpLogic, cmd); + if (KPSendCmd(kpLogic, cmd)) + commCnt++; WriteKPInfo(kpLogic); - commCnt++; } CommCnlAfterSession(kpLogic); } From 88129027338e8a1b2cd295925786d84543ef101c Mon Sep 17 00:00:00 2001 From: 2mik Date: Wed, 27 Apr 2016 19:14:10 +0300 Subject: [PATCH 011/382] ScadaWeb5: main menu --- ScadaWeb/ScadaWebShell5Beta/MasterMain.Master | 2 +- .../ScadaWebShell5Beta/MasterMain.Master.cs | 5 ++-- ScadaWeb/ScadaWebShell5Beta/View.aspx | 2 +- .../ScadaWebShell5Beta/css/mastermain.css | 20 ++++++++++++++ .../ScadaWebShell5Beta/css/mastermain.less | 24 +++++++++++++++++ .../ScadaWebShell5Beta/css/mastermain.min.css | 2 +- ScadaWeb/ScadaWebShell5Beta/js/mastermain.js | 27 +++++++++++++++++++ .../storage/admin/App/RememberMe.xml | 4 +-- 8 files changed, 79 insertions(+), 7 deletions(-) diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master index cc232568c..b0ae91a68 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master @@ -46,7 +46,7 @@ - + +
Collapse menu
diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs index 2363bfb77..607ed0b6c 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs @@ -38,6 +38,17 @@ public partial class MasterMain : System.Web.UI.MasterPage private AppData appData; // общие данные веб-приложения private UserData userData; // данные пользователя приложения + protected bool mainMenuVisible; // отобразить главное меню при загрузке страницы + + + /// + /// Установить видимость главного меню + /// + private void SetMainMenuVisible() + { + mainMenuVisible = !string.Equals(Request.Url.GetLeftPart(UriPartial.Path), + ResolveUrl("~/View.aspx"), StringComparison.OrdinalIgnoreCase); + } /// /// Генерировать HTML-код меню @@ -76,9 +87,9 @@ protected void Page_Load(object sender, EventArgs e) { appData = AppData.GetAppData(); userData = UserData.GetUserData(); - - // проверка входа в систему userData.CheckLoggedOn(true); + + SetMainMenuVisible(); } protected void lbtnMainLogout_Click(object sender, EventArgs e) diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css index d672c4ca1..ffcfd4128 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css @@ -30,7 +30,7 @@ a:active { line-height: 30px; white-space: nowrap; } -.hdr-btn { +#divMainHeader .hdr-btn { display: inline-block; height: 30px; margin: 0 0 0 5px; @@ -39,26 +39,26 @@ a:active { font-size: 13px; text-decoration: none; } -.hdr-btn:hover { +#divMainHeader .hdr-btn:hover { background-color: #32373c; color: #00b9eb; text-decoration: none; } -.txt-btn { +#divMainHeader .txt-btn { color: #eee; } -.img-btn { +#divMainHeader .img-btn { color: #9ca1a6; } -.img-btn .glyphicon { +#divMainHeader .img-btn .glyphicon { position: relative; top: 2px; } #spanMainShowMenu { - margin: 0 5px 0 -10px; - font-size: 15px; - display: none; - /*display: none;*/ + margin: 0 5px 0 -10px !important; + font-size: 15px !important; + display: none !important; + /*inline-block*/ } #spanMainAppName { display: inline-block; @@ -81,6 +81,14 @@ a:active { font-size: 14px; overflow: hidden; } +#divMainLeftPane .tool-window { + height: calc(100% - 40px); + padding: 5px 0 0 0; + display: none; + /*block*/ + overflow-x: hidden; + overflow-y: auto; +} #divMainTabs { position: absolute; left: 30px; @@ -108,12 +116,6 @@ a:active { cursor: pointer; } /* Main Menu */ -#divMainMenu { - height: calc(100% - 40px); - padding: 5px 0 0 0; - overflow-x: hidden; - overflow-y: auto; -} #divMainMenu .menu-item, #divMainMenu .menu-subitem { margin-left: 30px; @@ -124,6 +126,7 @@ a:active { padding-left: 20px; color: #9ca1a6; display: none; + /*block*/ font-size: 13px; } #divMainMenu .menu-item:hover, @@ -159,6 +162,7 @@ a:active { #divMainMenu .expander.expanded::before { content: "\e259"; } +/* Explorer */ /* Collapse Pane */ #divMainCollapsePane { position: absolute; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less index 26dd07bd0..2a7784a05 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less @@ -41,7 +41,7 @@ a:active { white-space: nowrap; } -.hdr-btn { +#divMainHeader .hdr-btn { display: inline-block; height: @header-heigth; margin: 0 0 0 5px; @@ -51,30 +51,29 @@ a:active { text-decoration: none; } -.hdr-btn:hover { +#divMainHeader .hdr-btn:hover { background-color: @menu-item-hover-back-color; color: @menu-item-hover-fore-color; text-decoration: none; } -.txt-btn { +#divMainHeader .txt-btn { color: @menu-fore-color; } -.img-btn { +#divMainHeader .img-btn { color: @menu-fore-color-dark; } -.img-btn .glyphicon { +#divMainHeader .img-btn .glyphicon { position: relative; top: 2px; } #spanMainShowMenu { - margin: 0 5px 0 -10px; - font-size: 15px; - display: none; - /*display: none;*/ + margin: 0 5px 0 -10px !important; + font-size: 15px !important; + display: none !important; /*inline-block*/ } #spanMainAppName { @@ -102,6 +101,14 @@ a:active { overflow: hidden; } +#divMainLeftPane .tool-window { + height: calc(~"100% - "@collapse-pane-heigth); + padding: 5px 0 0 0; + display: none; /*block*/ + overflow-x: hidden; + overflow-y: auto; +} + #divMainTabs { position: absolute; left: @left-tabs-heigth; @@ -134,13 +141,6 @@ a:active { /* Main Menu */ -#divMainMenu { - height: calc(~"100% - "@collapse-pane-heigth); - padding: 5px 0 0 0; - overflow-x: hidden; - overflow-y: auto; -} - #divMainMenu .menu-item, #divMainMenu .menu-subitem { margin-left: @left-tabs-heigth; @@ -151,7 +151,7 @@ a:active { #divMainMenu .menu-subitem { padding-left: 20px; color: @menu-fore-color-dark; - display: none; + display: none; /*block*/ font-size: 13px; } @@ -195,6 +195,8 @@ a:active { content: "\e259"; } +/* Explorer */ + /* Collapse Pane */ #divMainCollapsePane { diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css index d29193aba..7d5862358 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 230px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;white-space:nowrap;}.hdr-btn{display:inline-block;height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;font-size:13px;text-decoration:none;}.hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}.txt-btn{color:#eee;}.img-btn{color:#9ca1a6;}.img-btn .glyphicon{position:relative;top:2px;}#spanMainShowMenu{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;}#divMainUserMenu{float:right;height:30px;}#divMainLeftPane{position:fixed;left:0;top:30px;width:230px;min-height:300px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainMenu{height:calc(100% - 40px);padding:5px 0 0 0;overflow-x:hidden;overflow-y:auto;}#divMainMenu .menu-item,#divMainMenu .menu-subitem{margin-left:30px;padding:5px 10px;text-decoration:none;}#divMainMenu .menu-subitem{padding-left:20px;color:#9ca1a6;display:none;font-size:13px;}#divMainMenu .menu-item:hover,#divMainMenu .menu-subitem:hover{background-color:#191e23;color:#00b9eb;text-decoration:none;}#divMainMenu .menu-item.selected,#divMainMenu .menu-subitem.selected{background-color:#0073aa;color:#fff;}#divMainMenu .menu-item a,#divMainMenu .menu-subitem a{display:inline-block;width:100%;}#divMainMenu .menu-item.with-expander a{width:calc(100% - 20px);}#divMainMenu .expander{width:20px;cursor:pointer;float:right;text-align:right;}#divMainMenu .expander::before{content:"";font-family:'Glyphicons Halflings';font-size:10px;}#divMainMenu .expander.expanded::before{content:"";}#divMainCollapsePane{position:absolute;bottom:0;width:200px;height:40px;margin:0 0 0 30px;padding:5px 10px;color:#9ca1a6;font-size:13px;}#divMainCollapsePane i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapsePane:hover{color:#00b9eb;cursor:pointer;} \ No newline at end of file +body{margin:0;padding:30px 0 0 230px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;white-space:nowrap;}#divMainHeader .hdr-btn{display:inline-block;height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;font-size:13px;text-decoration:none;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#spanMainShowMenu{margin:0 5px 0 -10px !important;font-size:15px !important;display:none !important;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;}#divMainUserMenu{float:right;height:30px;}#divMainLeftPane{position:fixed;left:0;top:30px;width:230px;min-height:300px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 40px);padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainMenu .menu-item,#divMainMenu .menu-subitem{margin-left:30px;padding:5px 10px;text-decoration:none;}#divMainMenu .menu-subitem{padding-left:20px;color:#9ca1a6;display:none;font-size:13px;}#divMainMenu .menu-item:hover,#divMainMenu .menu-subitem:hover{background-color:#191e23;color:#00b9eb;text-decoration:none;}#divMainMenu .menu-item.selected,#divMainMenu .menu-subitem.selected{background-color:#0073aa;color:#fff;}#divMainMenu .menu-item a,#divMainMenu .menu-subitem a{display:inline-block;width:100%;}#divMainMenu .menu-item.with-expander a{width:calc(100% - 20px);}#divMainMenu .expander{width:20px;cursor:pointer;float:right;text-align:right;}#divMainMenu .expander::before{content:"";font-family:'Glyphicons Halflings';font-size:10px;}#divMainMenu .expander.expanded::before{content:"";}#divMainCollapsePane{position:absolute;bottom:0;width:200px;height:40px;margin:0 0 0 30px;padding:5px 10px;color:#9ca1a6;font-size:13px;}#divMainCollapsePane i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapsePane:hover{color:#00b9eb;cursor:pointer;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js index 638faab3b..814c131c1 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js @@ -9,22 +9,48 @@ function updateMainLayout() { divMainTabs.outerWidth(paneH); } +// Make visible main menu or views explorer +function showMenuOrExplorer() { + if (mainMenuVisible) { + $("#divMainMenu").css("display", "block"); + } else { + $("#divMainExplorer").css("display", "block"); + } +} + // Expand or collapse the menu item function toggleMenuItem(divExpander) { - var menuItem = divExpander.parent(); - var menuSubitems = menuItem.nextUntil(".menu-item", ".menu-subitem"); + var divMenuItem = divExpander.parent(); + var divMenuSubitems = divMenuItem.nextUntil(".menu-item", ".menu-subitem"); if (divExpander.hasClass("expanded")) { divExpander.removeClass("expanded"); - menuSubitems.css("display", "none"); + divMenuSubitems.css("display", "none"); } else { divExpander.addClass("expanded"); - menuSubitems.css("display", "block"); + divMenuSubitems.css("display", "block"); + } +} + +// Expand selected menu item +function expandSelectedMenuItem() { + var divSelMenuItem = $("#divMainMenu .menu-item.selected"); // top level item + + if (divSelMenuItem.length == 0) { + var divSelMenuSubitem = $("#divMainMenu .menu-subitem.selected"); + divSelMenuItem = divSelMenuSubitem.prevAll(".menu-item:first"); + } + + var divExpander = divSelMenuItem.find(".expander"); + if (!divExpander.hasClass("expanded")) { + toggleMenuItem(divExpander); } } $(document).ready(function () { updateMainLayout(); + showMenuOrExplorer(); + expandSelectedMenuItem(); // update layout on window resize $(window).resize(function () { From b771b1324195d4d35992831b7487a7f769077090 Mon Sep 17 00:00:00 2001 From: 2mik Date: Thu, 28 Apr 2016 19:23:00 +0300 Subject: [PATCH 013/382] ScadaWeb5: tree view --- ScadaWeb/ScadaWebCommon5Beta/ViewSettings.cs | 50 ++++++++++++++++++ ScadaWeb/ScadaWebShell5Beta/MasterMain.Master | 8 +-- .../ScadaWebShell5Beta/MasterMain.Master.cs | 23 ++++++++- .../MasterMain.Master.designer.cs | 9 ++++ .../ScadaWebShell5Beta.csproj | 1 + .../config/ViewSettings.xml | 8 +++ .../ScadaWebShell5Beta/css/mastermain.css | 19 +++++-- .../ScadaWebShell5Beta/css/mastermain.less | 22 ++++++-- .../ScadaWebShell5Beta/css/mastermain.min.css | 2 +- ScadaWeb/ScadaWebShell5Beta/js/mastermain.js | 51 +++++++++++++++---- 10 files changed, 169 insertions(+), 24 deletions(-) create mode 100644 ScadaWeb/ScadaWebShell5Beta/config/ViewSettings.xml diff --git a/ScadaWeb/ScadaWebCommon5Beta/ViewSettings.cs b/ScadaWeb/ScadaWebCommon5Beta/ViewSettings.cs index 70f346977..627447b99 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/ViewSettings.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/ViewSettings.cs @@ -23,6 +23,8 @@ * Modified : 2016 */ +using System.Collections.Generic; + namespace Scada.Web { /// @@ -31,6 +33,47 @@ namespace Scada.Web /// public class ViewSettings : ISettings { + /// + /// Элемент настроек, соответствующий представлению + /// + public class ViewItem + { + /// + /// Конструктор, ограничивающий создание объекта без параметров + /// + protected ViewItem() + { + } + /// + /// Конструктор + /// + public ViewItem(int viewID, string text, int alarmCnlNum) + { + ViewID = viewID; + Text = text; + AlarmCnlNum = alarmCnlNum; + Subitems = new List(); + } + + /// + /// Получить идентификатор представления + /// + public int ViewID { get; protected set; } + /// + /// Получить текст + /// + public string Text { get; protected set; } + /// + /// Получить номер входного канала, информирующего о тревожном состоянии представления + /// + public int AlarmCnlNum { get; protected set; } + /// + /// Получить дочерние элементы + /// + public List Subitems { get; protected set; } + } + + /// /// Имя файла настроек по умолчанию /// @@ -42,9 +85,16 @@ public class ViewSettings : ISettings /// public ViewSettings() { + ViewItems = new List(); } + /// + /// Получить элементы настроек представлений + /// + public List ViewItems { get; protected set; } + + /// /// Создать новый объект настроек /// diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master index 8f87c623a..34ec1b80f 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master @@ -40,8 +40,8 @@
-
Main Menu
-
Views
+
Main Menu
+
Views
@@ -62,7 +62,9 @@ -->
-
+
+ +
Collapse menu
diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs index 607ed0b6c..f7647e58d 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs @@ -26,6 +26,7 @@ using Scada.Web.Shell; using System; using System.Text; +using System.Web.UI.WebControls; namespace Scada.Web { @@ -62,7 +63,7 @@ protected string GenerateMenuHtml() StringBuilder sbHtml = new StringBuilder(); string curUrl = Request.Url.AbsolutePath; - foreach (MenuItem menuItem in userData.UserMenu.LinearMenuItems) + foreach (Shell.MenuItem menuItem in userData.UserMenu.LinearMenuItems) { string text = Server.HtmlEncode(menuItem.Text); string url = ResolveUrl(menuItem.Url); @@ -90,6 +91,26 @@ protected void Page_Load(object sender, EventArgs e) userData.CheckLoggedOn(true); SetMainMenuVisible(); + + // тест + TreeNode node1 = new TreeNode("Test group"); + node1.NavigateUrl = "javascript:void(0);"; + tvMainExplorer.Nodes.Add(node1); + TreeNode node2 = new TreeNode("Test view 1", "1"); + node2.NavigateUrl = "javascript:alert(1);"; + node1.ChildNodes.Add(node2); + + node2 = new TreeNode("Test view 2", "2"); + node2.NavigateUrl = "javascript:alert(2);"; + node1.ChildNodes.Add(node2); + + for (int i = 3; i < 100; i++) + { + node2 = new TreeNode("Test view 012345 6789 - " + i, i.ToString()); + node2.ToolTip = node2.Text; + node2.NavigateUrl = string.Format("javascript:alert({0});", i); + tvMainExplorer.Nodes.Add(node2); + } } protected void lbtnMainLogout_Click(object sender, EventArgs e) diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.designer.cs b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.designer.cs index d83338eb6..75ec62be9 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.designer.cs +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.designer.cs @@ -39,6 +39,15 @@ public partial class MasterMain { /// protected global::System.Web.UI.WebControls.LinkButton lbtnMainLogout; + /// + /// tvMainExplorer control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TreeView tvMainExplorer; + /// /// cphMainContent control. /// diff --git a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj index 3f73f9e19..3958f9f1e 100644 --- a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj +++ b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj @@ -67,6 +67,7 @@ lang\ScadaData.en-GB.xml + globalvar.less diff --git a/ScadaWeb/ScadaWebShell5Beta/config/ViewSettings.xml b/ScadaWeb/ScadaWebShell5Beta/config/ViewSettings.xml new file mode 100644 index 000000000..c6c9184b4 --- /dev/null +++ b/ScadaWeb/ScadaWebShell5Beta/config/ViewSettings.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css index ffcfd4128..03896279d 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css @@ -54,10 +54,10 @@ a:active { position: relative; top: 2px; } -#spanMainShowMenu { - margin: 0 5px 0 -10px !important; - font-size: 15px !important; - display: none !important; +#divMainHeader #spanMainShowMenu { + margin: 0 5px 0 -10px; + font-size: 15px; + display: none; /*inline-block*/ } #spanMainAppName { @@ -83,6 +83,7 @@ a:active { } #divMainLeftPane .tool-window { height: calc(100% - 40px); + margin: 0 0 0 30px; padding: 5px 0 0 0; display: none; /*block*/ @@ -118,7 +119,6 @@ a:active { /* Main Menu */ #divMainMenu .menu-item, #divMainMenu .menu-subitem { - margin-left: 30px; padding: 5px 10px; text-decoration: none; } @@ -163,6 +163,15 @@ a:active { content: "\e259"; } /* Explorer */ +#tvMainExplorer { + font-size: 13px; +} +#tvMainExplorer td { + padding: 3px 0; +} +#tvMainExplorer td:first-child { + padding-left: 10px; +} /* Collapse Pane */ #divMainCollapsePane { position: absolute; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less index 2a7784a05..2d3a6fc67 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less @@ -70,10 +70,10 @@ a:active { top: 2px; } -#spanMainShowMenu { - margin: 0 5px 0 -10px !important; - font-size: 15px !important; - display: none !important; /*inline-block*/ +#divMainHeader #spanMainShowMenu { + margin: 0 5px 0 -10px; + font-size: 15px; + display: none; /*inline-block*/ } #spanMainAppName { @@ -103,6 +103,7 @@ a:active { #divMainLeftPane .tool-window { height: calc(~"100% - "@collapse-pane-heigth); + margin: 0 0 0 @left-tabs-heigth; padding: 5px 0 0 0; display: none; /*block*/ overflow-x: hidden; @@ -143,7 +144,6 @@ a:active { #divMainMenu .menu-item, #divMainMenu .menu-subitem { - margin-left: @left-tabs-heigth; padding: 5px 10px; text-decoration: none; } @@ -197,6 +197,18 @@ a:active { /* Explorer */ +#tvMainExplorer { + font-size: 13px; +} + +#tvMainExplorer td { + padding: 3px 0; +} + +#tvMainExplorer td:first-child { + padding-left: 10px; +} + /* Collapse Pane */ #divMainCollapsePane { diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css index 7d5862358..ee99487fe 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 230px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;white-space:nowrap;}#divMainHeader .hdr-btn{display:inline-block;height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;font-size:13px;text-decoration:none;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#spanMainShowMenu{margin:0 5px 0 -10px !important;font-size:15px !important;display:none !important;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;}#divMainUserMenu{float:right;height:30px;}#divMainLeftPane{position:fixed;left:0;top:30px;width:230px;min-height:300px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 40px);padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainMenu .menu-item,#divMainMenu .menu-subitem{margin-left:30px;padding:5px 10px;text-decoration:none;}#divMainMenu .menu-subitem{padding-left:20px;color:#9ca1a6;display:none;font-size:13px;}#divMainMenu .menu-item:hover,#divMainMenu .menu-subitem:hover{background-color:#191e23;color:#00b9eb;text-decoration:none;}#divMainMenu .menu-item.selected,#divMainMenu .menu-subitem.selected{background-color:#0073aa;color:#fff;}#divMainMenu .menu-item a,#divMainMenu .menu-subitem a{display:inline-block;width:100%;}#divMainMenu .menu-item.with-expander a{width:calc(100% - 20px);}#divMainMenu .expander{width:20px;cursor:pointer;float:right;text-align:right;}#divMainMenu .expander::before{content:"";font-family:'Glyphicons Halflings';font-size:10px;}#divMainMenu .expander.expanded::before{content:"";}#divMainCollapsePane{position:absolute;bottom:0;width:200px;height:40px;margin:0 0 0 30px;padding:5px 10px;color:#9ca1a6;font-size:13px;}#divMainCollapsePane i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapsePane:hover{color:#00b9eb;cursor:pointer;} \ No newline at end of file +body{margin:0;padding:30px 0 0 230px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;white-space:nowrap;}#divMainHeader .hdr-btn{display:inline-block;height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;font-size:13px;text-decoration:none;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenu{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;}#divMainUserMenu{float:right;height:30px;}#divMainLeftPane{position:fixed;left:0;top:30px;width:230px;min-height:300px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 40px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainMenu .menu-item,#divMainMenu .menu-subitem{padding:5px 10px;text-decoration:none;}#divMainMenu .menu-subitem{padding-left:20px;color:#9ca1a6;display:none;font-size:13px;}#divMainMenu .menu-item:hover,#divMainMenu .menu-subitem:hover{background-color:#191e23;color:#00b9eb;text-decoration:none;}#divMainMenu .menu-item.selected,#divMainMenu .menu-subitem.selected{background-color:#0073aa;color:#fff;}#divMainMenu .menu-item a,#divMainMenu .menu-subitem a{display:inline-block;width:100%;}#divMainMenu .menu-item.with-expander a{width:calc(100% - 20px);}#divMainMenu .expander{width:20px;cursor:pointer;float:right;text-align:right;}#divMainMenu .expander::before{content:"";font-family:'Glyphicons Halflings';font-size:10px;}#divMainMenu .expander.expanded::before{content:"";}#tvMainExplorer{font-size:13px;}#tvMainExplorer td{padding:3px 0;}#tvMainExplorer td:first-child{padding-left:10px;}#divMainCollapsePane{position:absolute;bottom:0;width:200px;height:40px;margin:0 0 0 30px;padding:5px 10px;color:#9ca1a6;font-size:13px;}#divMainCollapsePane i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapsePane:hover{color:#00b9eb;cursor:pointer;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js index 814c131c1..bdd1600cc 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js @@ -9,26 +9,54 @@ function updateMainLayout() { divMainTabs.outerWidth(paneH); } -// Make visible main menu or views explorer -function showMenuOrExplorer() { +// Choose a tool window and activate it +function chooseToolWindow() { if (mainMenuVisible) { - $("#divMainMenu").css("display", "block"); + activateToolWindow($("#divMainMenuTab")); } else { - $("#divMainExplorer").css("display", "block"); + activateToolWindow($("#divMainExplorerTab")); + } +} + +// Make visible main menu or views explorer +function activateToolWindow(divClickedTab) { + // highlight clicked tab + var tabs = $("#divMainTabs .tab"); + tabs.removeClass("selected"); + divClickedTab.addClass("selected"); + + // deactivate all the tool windows + var toolWindows = $("#divMainLeftPane .tool-window"); + toolWindows.css("display", "none"); + + // activate the appropriate tool window + var clickedTabId = divClickedTab.attr('id'); + var toolWindow; + + if (clickedTabId == "divMainMenuTab") { + toolWindow = $("#divMainMenu") + } else if (clickedTabId == "divMainExplorerTab") { + toolWindow = $("#divMainExplorer") + } else { + toolWindow = null; + } + + if (toolWindow) { + toolWindow.css("display", "block"); } } // Expand or collapse the menu item function toggleMenuItem(divExpander) { - var divMenuItem = divExpander.parent(); - var divMenuSubitems = divMenuItem.nextUntil(".menu-item", ".menu-subitem"); + var menuItem = divExpander.parent(); + var menuSubitems = menuItem.nextUntil(".menu-item", ".menu-subitem"); if (divExpander.hasClass("expanded")) { divExpander.removeClass("expanded"); - divMenuSubitems.css("display", "none"); + menuSubitems.css("display", "none"); } else { divExpander.addClass("expanded"); - divMenuSubitems.css("display", "block"); + menuSubitems.css("display", "block"); } } @@ -49,7 +77,7 @@ function expandSelectedMenuItem() { $(document).ready(function () { updateMainLayout(); - showMenuOrExplorer(); + chooseToolWindow(); expandSelectedMenuItem(); // update layout on window resize @@ -57,6 +85,11 @@ $(document).ready(function () { updateMainLayout(); }); + // activate tool window on tab click + $("#divMainTabs .tab").click(function (event) { + activateToolWindow($(this)); + }); + // toggle main menu items on click $("#divMainMenu .expander").click(function() { toggleMenuItem($(this)); From 21e1a83772a9ad6e583da9ef7a0e38db8609a0c6 Mon Sep 17 00:00:00 2001 From: 2mik Date: Fri, 29 Apr 2016 17:30:22 +0300 Subject: [PATCH 014/382] ScadaWeb5: development --- ScadaData/ScadaData/Client/DataAccess.cs | 36 +++++ ScadaData/ScadaData/Data/BaseValues.cs | 2 +- ScadaData/ScadaData/Data/EntityRights.cs | 61 ++++++++ ScadaData/ScadaData/ScadaData.csproj | 1 + .../PlgScheme/AppCode/SchemeSpec.cs | 3 +- .../ScadaWebCommon5Beta.csproj | 4 + .../ScadaWebCommon5Beta/Shell/IWebTreeNode.cs | 68 ++++++++ .../Shell/TreeViewRenderer.cs | 44 ++++++ .../ScadaWebCommon5Beta/Shell/UserMenu.cs | 13 +- .../ScadaWebCommon5Beta/Shell/UserViews.cs | 146 ++++++++++++++++++ .../ScadaWebCommon5Beta/Shell/ViewNode.cs | 122 +++++++++++++++ ScadaWeb/ScadaWebCommon5Beta/UserData.cs | 12 +- ScadaWeb/ScadaWebCommon5Beta/UserRights.cs | 67 +++++++- ScadaWeb/ScadaWebCommon5Beta/ViewSettings.cs | 74 +++++++-- ScadaWeb/ScadaWebCommon5Beta/WebPhrases.cs | 13 ++ ScadaWeb/ScadaWebCommon5Beta/WebSettings.cs | 2 +- .../lang/ScadaWeb.en-GB.xml | 4 + .../lang/ScadaWeb.ru-RU.xml | 4 + .../storage/admin/App/RememberMe.xml | 4 +- 19 files changed, 652 insertions(+), 28 deletions(-) create mode 100644 ScadaData/ScadaData/Data/EntityRights.cs create mode 100644 ScadaWeb/ScadaWebCommon5Beta/Shell/IWebTreeNode.cs create mode 100644 ScadaWeb/ScadaWebCommon5Beta/Shell/TreeViewRenderer.cs create mode 100644 ScadaWeb/ScadaWebCommon5Beta/Shell/UserViews.cs create mode 100644 ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs diff --git a/ScadaData/ScadaData/Client/DataAccess.cs b/ScadaData/ScadaData/Client/DataAccess.cs index 3d6c769e8..6324ee664 100644 --- a/ScadaData/ScadaData/Client/DataAccess.cs +++ b/ScadaData/ScadaData/Client/DataAccess.cs @@ -25,6 +25,7 @@ using Scada.Data; using System; +using System.Collections.Generic; using System.Data; using System.IO; using Utils; @@ -237,6 +238,41 @@ public ViewProps GetViewProps(int viewID) } } + /// + /// Получить права на представления по идентификатору роли + /// + public Dictionary GetViewRights(int roleID) + { + lock (baseLock) + { + Dictionary viewRightsDict = new Dictionary(); + + try + { + dataCache.RefreshBaseTables(); + + DataTable tblRight = dataCache.BaseTables.RightTable; + BaseTables.CheckIsNotEmpty(tblRight, true); + tblRight.DefaultView.RowFilter = "RoleID = " + roleID; + + foreach (DataRowView rowView in tblRight.DefaultView) + { + int viewID = (int)rowView["ItfID"]; + EntityRights rights = new EntityRights((bool)rowView["ViewRight"], (bool)rowView["CtrlRight"]); + viewRightsDict[viewID] = rights; + } + } + catch (Exception ex) + { + log.WriteException(ex, Localization.UseRussian ? + "Ошибка при получении прав на представления для роли с ид.={0}" : + "Error getting view access rights for the role with ID={0}", roleID); + } + + return viewRightsDict; + } + } + /// /// Получить идентификатор пользователя по имени /// diff --git a/ScadaData/ScadaData/Data/BaseValues.cs b/ScadaData/ScadaData/Data/BaseValues.cs index 54c72ccc8..9b99da41e 100644 --- a/ScadaData/ScadaData/Data/BaseValues.cs +++ b/ScadaData/ScadaData/Data/BaseValues.cs @@ -83,7 +83,7 @@ public static string GetRoleName(int roleID) return Localization.UseRussian ? "Гость" : "Guest"; else if (roleID == App) return Localization.UseRussian ? "Приложение" : "Application"; - if (Custom <= roleID && roleID < Err) + else if (Custom <= roleID && roleID < Err) return Localization.UseRussian ? "Настраиваемая роль" : "Custom role"; else if (roleID == Err) return Localization.UseRussian ? "Ошибка" : "Error"; diff --git a/ScadaData/ScadaData/Data/EntityRights.cs b/ScadaData/ScadaData/Data/EntityRights.cs new file mode 100644 index 000000000..c4e71f5b1 --- /dev/null +++ b/ScadaData/ScadaData/Data/EntityRights.cs @@ -0,0 +1,61 @@ +/* + * Copyright 2016 Mikhail Shiryaev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Product : Rapid SCADA + * Module : ScadaData + * Summary : Rights to access some entity + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + */ + +namespace Scada.Data +{ + /// + /// Rights to access some entity + /// Права на доступ к некоторой сущности + /// + public struct EntityRights + { + /// + /// Отсутствие прав + /// + public static readonly EntityRights NoRights = new EntityRights(false, false); + + + /// + /// Конструктор + /// + public EntityRights(bool viewRight, bool ctrlRight) + : this() + { + ViewRight = viewRight; + ControlRight = ctrlRight; + } + + + /// + /// Получить или установить право на просмотр + /// + public bool ViewRight { get; set; } + + /// + /// Получить или установить право на управление + /// + public bool ControlRight { get; set; } + } +} diff --git a/ScadaData/ScadaData/ScadaData.csproj b/ScadaData/ScadaData/ScadaData.csproj index 816b626cf..42e4fb7f2 100644 --- a/ScadaData/ScadaData/ScadaData.csproj +++ b/ScadaData/ScadaData/ScadaData.csproj @@ -61,6 +61,7 @@ + diff --git a/ScadaWeb/OpenPlugins/PlgScheme/AppCode/SchemeSpec.cs b/ScadaWeb/OpenPlugins/PlgScheme/AppCode/SchemeSpec.cs index 3c285a5bd..d75cf7e5b 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/AppCode/SchemeSpec.cs +++ b/ScadaWeb/OpenPlugins/PlgScheme/AppCode/SchemeSpec.cs @@ -49,7 +49,8 @@ public override string ViewTypeCode { get { - return "SchemeView"; + // TODO: заменить на SchemeView после добавления поля ViewTypeCode в базу конфигурации + return "sch"; } } diff --git a/ScadaWeb/ScadaWebCommon5Beta/ScadaWebCommon5Beta.csproj b/ScadaWeb/ScadaWebCommon5Beta/ScadaWebCommon5Beta.csproj index 9fd347fc7..c46efb928 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/ScadaWebCommon5Beta.csproj +++ b/ScadaWeb/ScadaWebCommon5Beta/ScadaWebCommon5Beta.csproj @@ -56,9 +56,13 @@ + + + + diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/IWebTreeNode.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/IWebTreeNode.cs new file mode 100644 index 000000000..bcd8acaa9 --- /dev/null +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/IWebTreeNode.cs @@ -0,0 +1,68 @@ +/* + * Copyright 2016 Mikhail Shiryaev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Product : Rapid SCADA + * Module : ScadaWebCommon + * Summary : Interface that represents tree node to display on a web page + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + */ + +using System.Collections; +using System.Collections.Generic; + +namespace Scada.Web.Shell +{ + /// + /// Interface that represents tree node to display on a web page + /// Интерфейс, представляющий узел дерева для отображения на веб-странице + /// + public interface IWebTreeNode + { + /// + /// Получить текст + /// + string Text { get; } + + /// + /// Получить ссылку + /// + string Url { get; } + + /// + /// Получить или установить уровень вложенности + /// + int Level { get; set; } + + /// + /// Получить атрибуты данных в виде пар "имя-значение" + /// + SortedList DataAttrs { get; } + + /// + /// Получить дочерние узлы + /// + IList Children { get; } + + + /// + /// Определить, что узел соответствует выбранному объекту + /// + bool IsSelected(object selObj); + } +} diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/TreeViewRenderer.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/TreeViewRenderer.cs new file mode 100644 index 000000000..eb43d2ccc --- /dev/null +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/TreeViewRenderer.cs @@ -0,0 +1,44 @@ +/* + * Copyright 2016 Mikhail Shiryaev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Product : Rapid SCADA + * Module : ScadaWebCommon + * Summary : Renders tree view HTML + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + */ + +using System.Collections; + +namespace Scada.Web.Shell +{ + /// + /// Renders tree view HTML + /// Формирует HTML код дерева + /// + public class TreeViewRenderer + { + /// + /// Генерировать HTML-код дерева для узлов, поддерживающих IWebTreeNode + /// + public string GenerateHtml(IList treeNodes, object selObj) + { + return ""; + } + } +} diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/UserMenu.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/UserMenu.cs index eb166d544..db95600e8 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/UserMenu.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/UserMenu.cs @@ -133,13 +133,20 @@ protected static void FillLinearMenuItems(List linearItems, List /// Инициализировать меню пользователя /// - public void Init(UserData userData, List pluginSpecs) + public void Init(UserData userData) { + if (userData == null) + throw new ArgumentNullException("userData"); + try { MenuItems.Clear(); - foreach (PluginSpec pluginSpec in pluginSpecs) - MergeMenuItems(MenuItems, pluginSpec.GetMenuItems(userData)); + + if (userData.PluginSpecs != null) + { + foreach (PluginSpec pluginSpec in userData.PluginSpecs) + MergeMenuItems(MenuItems, pluginSpec.GetMenuItems(userData)); + } LinearMenuItems.Clear(); FillLinearMenuItems(LinearMenuItems, MenuItems, 0); diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/UserViews.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/UserViews.cs new file mode 100644 index 000000000..1c077b6d7 --- /dev/null +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/UserViews.cs @@ -0,0 +1,146 @@ +/* + * Copyright 2016 Mikhail Shiryaev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Product : Rapid SCADA + * Module : ScadaWebCommon + * Summary : Views accessible to the web application user + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + */ + +using Scada.Client; +using Scada.Data; +using Scada.Web.Plugins; +using System; +using System.Collections.Generic; +using Utils; + +namespace Scada.Web.Shell +{ + /// + /// Views accessible to the web application user + /// Представления, доступные пользователю веб-приложения + /// + public class UserViews + { + /// + /// Журнал + /// + protected readonly Log log; + + /// + /// Права пользователя + /// + protected UserRights userRights; + /// + /// Спецификации представлений + /// + protected Dictionary viewSpecs; + /// + /// Объект для доступа к данным кеша клиентов + /// + protected DataAccess dataAccess; + + + /// + /// Конструктор, ограничивающий создание объекта без параметров + /// + protected UserViews() + { + } + + /// + /// Конструктор + /// + public UserViews(Log log) + { + if (log == null) + throw new ArgumentNullException("log"); + + this.log = log; + userRights = null; + viewSpecs = null; + dataAccess = null; + + ViewNodes = new List(); + } + + + /// + /// Получить узлы дерева представлений + /// + public List ViewNodes { get; protected set; } + + + /// + /// Рекурсивно создать узлы дерева представлений на основе элементов настроек + /// + protected void CreateViewNodes(List destViewNodes, List srcViewItems) + { + foreach (ViewSettings.ViewItem viewItem in srcViewItems) + { + int viewID = viewItem.ViewID; + + // пропуск представления, на которое нет прав + if (viewID > 0 && !userRights.GetViewRights(viewItem.ViewID).ViewRight) + continue; + + // получение спецификации представления + ViewSpec viewSpec = null; + if (viewID > 0) + { + ViewProps viewProps = dataAccess.GetViewProps(viewID); + viewSpecs.TryGetValue(viewProps.ViewTypeCode, out viewSpec); + } + + // создание узла дерева и дочерних узлов + ViewNode viewNode = new ViewNode(viewItem, viewSpec); + CreateViewNodes(viewNode.ChildNodes, viewItem.Subitems); + + // добавление узла, если он соответствует представлению или имеет дочерние узлы + if (viewID > 0 || viewNode.ChildNodes.Count > 0) + destViewNodes.Add(viewNode); + } + } + + /// + /// Инициализировать представления пользователя + /// + public void Init(UserData userData, DataAccess dataAccess) + { + if (userData == null) + throw new ArgumentNullException("userData"); + + try + { + userRights = userData.UserRights; + viewSpecs = userData.ViewSpecs; + this.dataAccess = dataAccess; + + if (userRights != null && viewSpecs != null) + CreateViewNodes(ViewNodes, userData.ViewSettings.ViewItems); + } + catch (Exception ex) + { + log.WriteException(ex, Localization.UseRussian ? + "Ошибка при инициализации представлений пользователя" : + "Error initializing user views"); + } + } + } +} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs new file mode 100644 index 000000000..a61b3716d --- /dev/null +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs @@ -0,0 +1,122 @@ +/* + * Copyright 2016 Mikhail Shiryaev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Product : Rapid SCADA + * Module : ScadaWebCommon + * Summary : View tree node + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + */ + +using Scada.Web.Plugins; +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Scada.Web.Shell +{ + /// + /// View tree node + /// Узел дерева предсталений + /// + public class ViewNode : IWebTreeNode + { + /// + /// Конструктор, ограничивающий создание объекта без параметров + /// + protected ViewNode() + { + } + + /// + /// Конструктор + /// + public ViewNode(ViewSettings.ViewItem viewItem, ViewSpec viewSpec) + { + if (viewItem == null) + throw new ArgumentNullException("viewItem"); + + Text = viewItem.Text ?? ""; + Url = viewSpec == null ? "" : viewSpec.GetViewUrl(viewItem.ViewID); + AlarmCnlNum = viewItem.AlarmCnlNum; + Level = -1; + ChildNodes = new List(); + InitDataAttrs(); + } + + + /// + /// Получить текст + /// + public string Text { get; protected set; } + + /// + /// Получить ссылку + /// + public string Url { get; protected set; } + + /// + /// Получить номер входного канала, информирующего о тревожном состоянии представления + /// + public int AlarmCnlNum { get; protected set; } + + /// + /// Получить или установить уровень вложенности + /// + public int Level { get; set; } + + /// + /// Получить дочерние узлы + /// + public List ChildNodes { get; protected set; } + + /// + /// Получить атрибуты данных в виде пар "имя-значение" (IWebTreeNode) + /// + public SortedList DataAttrs { get; protected set; } + + /// + /// Получить дочерние узлы (IWebTreeNode) + /// + public IList Children + { + get + { + return ChildNodes; + } + } + + + /// + /// Инициализировать атрибуты данных + /// + protected void InitDataAttrs() + { + DataAttrs = new SortedList(); + DataAttrs.Add("cnl", AlarmCnlNum.ToString()); + } + + /// + /// Определить, что узел соответствует выбранному объекту + /// + public bool IsSelected(object selObj) + { + return false; + } + } +} diff --git a/ScadaWeb/ScadaWebCommon5Beta/UserData.cs b/ScadaWeb/ScadaWebCommon5Beta/UserData.cs index d275ec555..eeb63e3b0 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/UserData.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/UserData.cs @@ -111,6 +111,11 @@ private UserData() /// public UserMenu UserMenu { get; private set; } + /// + /// Получить представления пользователя + /// + public UserViews UserViews { get; private set; } + /// /// Получить ссылку на настройки веб-приложения @@ -145,6 +150,7 @@ private void ClearUser() LogonDT = DateTime.MinValue; UserRights = null; UserMenu = null; + UserViews = null; } /// @@ -252,10 +258,12 @@ public bool Login(string username, string password, out string errMsg) UpdateAppDataRefs(); UserRights = new UserRights(); - UserRights.Init(roleID); + UserRights.Init(roleID, AppData.DataAccess); RaiseOnUserLogin(); UserMenu = new UserMenu(AppData.Log); - UserMenu.Init(this, PluginSpecs); + UserMenu.Init(this); + UserViews = new UserViews(AppData.Log); + UserViews.Init(this, AppData.DataAccess); return true; } else diff --git a/ScadaWeb/ScadaWebCommon5Beta/UserRights.cs b/ScadaWeb/ScadaWebCommon5Beta/UserRights.cs index c8d5f99c1..e64202cec 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/UserRights.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/UserRights.cs @@ -25,7 +25,8 @@ using Scada.Client; using Scada.Data; -using Utils; +using System; +using System.Collections.Generic; namespace Scada.Web { @@ -35,30 +36,84 @@ namespace Scada.Web /// public class UserRights { + protected bool viewAllViewsRight; // право просмотра всех представлений + protected bool controlAllViewsRight; // право управления для всех представлений + protected Dictionary viewRightsDict; // права на предсталения + + /// /// Конструктор /// public UserRights() { - ConfigRight = false; + SetToDefault(); } + /// + /// Получить право конфигурирования системы + /// + public bool ConfigRight { get; protected set; } + + + /// + /// Установить значения прав по умолчанию + /// + protected void SetToDefault() + { + viewAllViewsRight = false; + controlAllViewsRight = false; + viewRightsDict = null; + + ConfigRight = false; + } + /// /// Инициализировать права пользователя /// - public void Init(int roleID) + public void Init(int roleID, DataAccess dataAccess) { + if (dataAccess == null) + throw new ArgumentNullException("dataAccess"); + + SetToDefault(); + if (roleID == BaseValues.Roles.Admin) { + viewAllViewsRight = true; + controlAllViewsRight = true; ConfigRight = true; } + else if (roleID == BaseValues.Roles.Dispatcher) + { + viewAllViewsRight = true; + controlAllViewsRight = true; + } + else if (roleID == BaseValues.Roles.Guest) + { + viewAllViewsRight = true; + } + else if (BaseValues.Roles.Custom <= roleID && roleID < BaseValues.Roles.Err) + { + viewRightsDict = dataAccess.GetViewRights(roleID); + } } - /// - /// Получить право конфигурирования системы + /// Получить права на предсталение /// - public bool ConfigRight { get; protected set; } + public EntityRights GetViewRights(int viewID) + { + if (viewAllViewsRight) + { + return new EntityRights(viewAllViewsRight, controlAllViewsRight); + } + else + { + EntityRights rights; + return viewRightsDict != null && viewRightsDict.TryGetValue(viewID, out rights) ? + rights : EntityRights.NoRights; + } + } } } \ No newline at end of file diff --git a/ScadaWeb/ScadaWebCommon5Beta/ViewSettings.cs b/ScadaWeb/ScadaWebCommon5Beta/ViewSettings.cs index 627447b99..fbf77044d 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/ViewSettings.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/ViewSettings.cs @@ -23,7 +23,10 @@ * Modified : 2016 */ +using System; using System.Collections.Generic; +using System.IO; +using System.Xml; namespace Scada.Web { @@ -39,9 +42,10 @@ public class ViewSettings : ISettings public class ViewItem { /// - /// Конструктор, ограничивающий создание объекта без параметров + /// Конструктор /// - protected ViewItem() + public ViewItem() + : this(0, "", 0) { } /// @@ -56,17 +60,17 @@ public ViewItem(int viewID, string text, int alarmCnlNum) } /// - /// Получить идентификатор представления + /// Получить или установить идентификатор представления /// - public int ViewID { get; protected set; } + public int ViewID { get; set; } /// - /// Получить текст + /// Получить или установить текст /// - public string Text { get; protected set; } + public string Text { get; set; } /// - /// Получить номер входного канала, информирующего о тревожном состоянии представления + /// Получить или установить номер входного канала, информирующего о тревожном состоянии представления /// - public int AlarmCnlNum { get; protected set; } + public int AlarmCnlNum { get; set; } /// /// Получить дочерние элементы /// @@ -95,6 +99,23 @@ public ViewSettings() public List ViewItems { get; protected set; } + /// + /// Рекурсивно загрузить элемент настроек представлений + /// + private void LoadViewItem(XmlElement viewItemElem, List viewItems) + { + ViewItem viewItem = new ViewItem(); + viewItem.ViewID = viewItemElem.GetAttrAsInt("viewID"); + viewItem.Text = viewItemElem.GetAttribute("text"); + viewItem.AlarmCnlNum = viewItemElem.GetAttrAsInt("alarmCnlNum"); + viewItems.Add(viewItem); + + XmlNodeList viewItemNodes = viewItemElem.SelectNodes("ViewItem"); + foreach (XmlElement elem in viewItemNodes) + LoadViewItem(elem, viewItem.Subitems); + } + + /// /// Создать новый объект настроек /// @@ -116,8 +137,30 @@ public bool Equals(ISettings settings) /// public bool LoadFromFile(string fileName, out string errMsg) { - errMsg = "ViewSettings not implemented"; - return false; + // установка значений по умолчанию + ViewItems.Clear(); + + // загрузка настроек + try + { + if (!File.Exists(fileName)) + throw new FileNotFoundException(string.Format(CommonPhrases.NamedFileNotFound, fileName)); + + XmlDocument xmlDoc = new XmlDocument(); + xmlDoc.Load(fileName); + + XmlNodeList viewItemNodes = xmlDoc.DocumentElement.SelectNodes("ViewItem"); + foreach (XmlElement viewItemElem in viewItemNodes) + LoadViewItem(viewItemElem, ViewItems); + + errMsg = ""; + return true; + } + catch (Exception ex) + { + errMsg = WebPhrases.LoadWebSettingsError + ": " + ex.Message; + return false; + } } /// @@ -125,8 +168,15 @@ public bool LoadFromFile(string fileName, out string errMsg) /// public bool SaveToFile(string fileName, out string errMsg) { - errMsg = "ViewSettings not implemented"; - return false; + try + { + throw new NotImplementedException("Method not implemented."); + } + catch (Exception ex) + { + errMsg = WebPhrases.SaveViewSettingsError + ": " + ex.Message; + return false; + } } } } diff --git a/ScadaWeb/ScadaWebCommon5Beta/WebPhrases.cs b/ScadaWeb/ScadaWebCommon5Beta/WebPhrases.cs index 99c150932..27e453274 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/WebPhrases.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/WebPhrases.cs @@ -47,6 +47,10 @@ static WebPhrases() public static string NoRights { get; private set; } public static string IllegalRole { get; private set; } + // Словарь Scada.Web.ViewSettings + public static string LoadViewSettingsError { get; private set; } + public static string SaveViewSettingsError { get; private set; } + // Словарь Scada.Web.WebSettings public static string LoadWebSettingsError { get; private set; } public static string SaveWebSettingsError { get; private set; } @@ -68,6 +72,9 @@ private static void SetToDefault() NoRights = Localization.Dict.GetEmptyPhrase("NoRights"); IllegalRole = Localization.Dict.GetEmptyPhrase("IllegalRole"); + LoadViewSettingsError = Localization.Dict.GetEmptyPhrase("LoadViewSettingsError"); + SaveViewSettingsError = Localization.Dict.GetEmptyPhrase("SaveViewSettingsError"); + LoadWebSettingsError = Localization.Dict.GetEmptyPhrase("LoadWebSettingsError"); SaveWebSettingsError = Localization.Dict.GetEmptyPhrase("SaveWebSettingsError"); @@ -94,6 +101,12 @@ public static void Init() IllegalRole = dict.GetPhrase("IllegalRole", IllegalRole); } + if (Localization.Dictionaries.TryGetValue("Scada.Web.ViewSettings", out dict)) + { + LoadWebSettingsError = dict.GetPhrase("LoadViewSettingsError", LoadViewSettingsError); + SaveWebSettingsError = dict.GetPhrase("SaveViewSettingsError", SaveViewSettingsError); + } + if (Localization.Dictionaries.TryGetValue("Scada.Web.WebSettings", out dict)) { LoadWebSettingsError = dict.GetPhrase("LoadWebSettingsError", LoadWebSettingsError); diff --git a/ScadaWeb/ScadaWebCommon5Beta/WebSettings.cs b/ScadaWeb/ScadaWebCommon5Beta/WebSettings.cs index 86b19dee3..51a9224fe 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/WebSettings.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/WebSettings.cs @@ -137,7 +137,7 @@ public bool LoadFromFile(string fileName, out string errMsg) if (!File.Exists(fileName)) throw new FileNotFoundException(string.Format(CommonPhrases.NamedFileNotFound, fileName)); - XmlDocument xmlDoc = new XmlDocument(); // обрабатываемый XML-документ + XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load(fileName); XmlElement rootElem = xmlDoc.DocumentElement; diff --git a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml index 8a07b8920..d96fc22fa 100644 --- a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml +++ b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml @@ -9,6 +9,10 @@ Insufficient rights. Illegal user role. + + Error loading view settings + Error saving view settings + Error loading web application settings Error saving web application settings diff --git a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml index 92c28e867..52b51b988 100644 --- a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml +++ b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml @@ -9,6 +9,10 @@ Недостаточно прав. Недопустимая роль пользователя. + + Ошибка при загрузке настроек представлений + Ошибка при сохранении настроек представлений + Ошибка при загрузке настроек веб-приложения Ошибка при сохранении настроек веб-приложения diff --git a/ScadaWeb/ScadaWebShell5Beta/storage/admin/App/RememberMe.xml b/ScadaWeb/ScadaWebShell5Beta/storage/admin/App/RememberMe.xml index 7000a85c8..5d1a7b873 100644 --- a/ScadaWeb/ScadaWebShell5Beta/storage/admin/App/RememberMe.xml +++ b/ScadaWeb/ScadaWebShell5Beta/storage/admin/App/RememberMe.xml @@ -18,7 +18,7 @@ 4811d600-639e-40a1-b361-ce411fb43be8 - bd9daa3c-ce15-450e-9c6a-ec16623e47a1 - 04/27/2016 18:59:12 + ecc7f4b1-7afd-4146-81c9-2b01f0b1fe77 + 04/29/2016 09:57:23 \ No newline at end of file From 84cb93e575ec82193d79f4844558e8c99eac2228 Mon Sep 17 00:00:00 2001 From: 2mik Date: Sat, 30 Apr 2016 00:05:27 +0300 Subject: [PATCH 015/382] ScadaWeb5: render tree view --- .../ScadaWebCommon5Beta/Shell/IWebTreeNode.cs | 5 + .../Shell/TreeViewRenderer.cs | 91 ++++++++++++++++++- .../ScadaWebCommon5Beta/Shell/UserViews.cs | 11 ++- .../ScadaWebCommon5Beta/Shell/ViewNode.cs | 20 +++- ScadaWeb/ScadaWebCommon5Beta/UserRights.cs | 15 ++- ScadaWeb/ScadaWebShell5Beta/MasterMain.Master | 5 +- .../ScadaWebShell5Beta/MasterMain.Master.cs | 26 ++++-- .../config/ViewSettings.xml | 6 +- .../storage/admin/App/RememberMe.xml | 15 --- 9 files changed, 155 insertions(+), 39 deletions(-) diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/IWebTreeNode.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/IWebTreeNode.cs index bcd8acaa9..c2f7bbfd1 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/IWebTreeNode.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/IWebTreeNode.cs @@ -43,6 +43,11 @@ public interface IWebTreeNode /// Получить ссылку /// string Url { get; } + + /// + /// Получить ссылку на иконку + /// + string IconUrl { get; } /// /// Получить или установить уровень вложенности diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/TreeViewRenderer.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/TreeViewRenderer.cs index eb43d2ccc..7d4f8c773 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/TreeViewRenderer.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/TreeViewRenderer.cs @@ -24,6 +24,9 @@ */ using System.Collections; +using System.Collections.Generic; +using System.Text; +using System.Web; namespace Scada.Web.Shell { @@ -33,12 +36,98 @@ namespace Scada.Web.Shell /// public class TreeViewRenderer { + /// + /// Генерировать HTML-код атрибутов данных + /// + protected string GenDataAttrsHtml(IWebTreeNode webTreeNode) + { + const string DataAttrTemplate = " data-{0}='{1}'"; + + StringBuilder sbHtml = new StringBuilder(); + sbHtml.Append(string.Format(DataAttrTemplate, "level", webTreeNode.Level)); + + if (webTreeNode.DataAttrs != null) + { + foreach (KeyValuePair pair in webTreeNode.DataAttrs) + { + if (string.IsNullOrWhiteSpace(pair.Key)) + sbHtml.Append(string.Format(DataAttrTemplate, pair.Key, pair.Value)); + } + } + + return sbHtml.ToString(); + } + + /// + /// Рекурсивно генерировать HTML-код дерева + /// + protected string GenTreeViewHtml(IList treeNodes, object selObj, bool topLevel) + { + const string NodeTemplate = + "
" + + "
" + + "
" + + "
" + + "
{4}
" + + "
{5}
"; + const string ImageTemplate = ""; + const string LinkTemplate = "{1}"; + + StringBuilder sbHtml = new StringBuilder(); + sbHtml.AppendLine(topLevel ? + "
" : + "
"); + + if (treeNodes != null) + { + foreach (object treeNode in treeNodes) + { + IWebTreeNode webTreeNode = treeNode as IWebTreeNode; + if (webTreeNode != null) + { + string selected = webTreeNode.IsSelected(selObj) ? " selected" : ""; + string dataAttrs = GenDataAttrsHtml(webTreeNode); + + bool containsSubitems = webTreeNode.Children.Count > 0; + string expanderEmpty = containsSubitems ? "" : " empty"; + + string iconEmpty; + string icon; + if (string.IsNullOrEmpty(webTreeNode.IconUrl)) + { + iconEmpty = " empty"; + icon = ""; + } + else + { + iconEmpty = ""; + icon = string.Format(ImageTemplate, webTreeNode.IconUrl); + } + + string text = HttpUtility.HtmlEncode(webTreeNode.Text); + string linkOrText = containsSubitems || !string.IsNullOrEmpty(webTreeNode.Url) ? + string.Format(LinkTemplate, webTreeNode.Url, text) : text; + + sbHtml.AppendLine(string.Format(NodeTemplate, + selected, dataAttrs, expanderEmpty, iconEmpty, icon, linkOrText)); + + if (containsSubitems) + sbHtml.Append(GenTreeViewHtml(webTreeNode.Children, selObj, false)); + } + } + } + + sbHtml.AppendLine("
"); + return sbHtml.ToString(); + } + + /// /// Генерировать HTML-код дерева для узлов, поддерживающих IWebTreeNode /// public string GenerateHtml(IList treeNodes, object selObj) { - return ""; + return GenTreeViewHtml(treeNodes, selObj, true); } } } diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/UserViews.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/UserViews.cs index 1c077b6d7..7cf6704a9 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/UserViews.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/UserViews.cs @@ -90,7 +90,7 @@ public UserViews(Log log) /// /// Рекурсивно создать узлы дерева представлений на основе элементов настроек /// - protected void CreateViewNodes(List destViewNodes, List srcViewItems) + protected void CreateViewNodes(List destViewNodes, List srcViewItems, int level) { foreach (ViewSettings.ViewItem viewItem in srcViewItems) { @@ -105,12 +105,13 @@ protected void CreateViewNodes(List destViewNodes, List 0) { ViewProps viewProps = dataAccess.GetViewProps(viewID); - viewSpecs.TryGetValue(viewProps.ViewTypeCode, out viewSpec); + if (viewProps != null) + viewSpecs.TryGetValue(viewProps.ViewTypeCode, out viewSpec); } // создание узла дерева и дочерних узлов - ViewNode viewNode = new ViewNode(viewItem, viewSpec); - CreateViewNodes(viewNode.ChildNodes, viewItem.Subitems); + ViewNode viewNode = new ViewNode(viewItem, viewSpec) { Level = level }; + CreateViewNodes(viewNode.ChildNodes, viewItem.Subitems, level + 1); // добавление узла, если он соответствует представлению или имеет дочерние узлы if (viewID > 0 || viewNode.ChildNodes.Count > 0) @@ -133,7 +134,7 @@ public void Init(UserData userData, DataAccess dataAccess) this.dataAccess = dataAccess; if (userRights != null && viewSpecs != null) - CreateViewNodes(ViewNodes, userData.ViewSettings.ViewItems); + CreateViewNodes(ViewNodes, userData.ViewSettings.ViewItems, 0); } catch (Exception ex) { diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs index a61b3716d..ba2f058ba 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs @@ -52,7 +52,18 @@ public ViewNode(ViewSettings.ViewItem viewItem, ViewSpec viewSpec) throw new ArgumentNullException("viewItem"); Text = viewItem.Text ?? ""; - Url = viewSpec == null ? "" : viewSpec.GetViewUrl(viewItem.ViewID); + + if (viewSpec == null) + { + Url = ""; + IconUrl = ""; + } + else + { + Url = viewSpec.GetViewUrl(viewItem.ViewID); + IconUrl = viewSpec.IconUrl; + } + AlarmCnlNum = viewItem.AlarmCnlNum; Level = -1; ChildNodes = new List(); @@ -69,7 +80,12 @@ public ViewNode(ViewSettings.ViewItem viewItem, ViewSpec viewSpec) /// Получить ссылку /// public string Url { get; protected set; } - + + /// + /// Получить ссылку на иконку + /// + public string IconUrl { get; protected set; } + /// /// Получить номер входного канала, информирующего о тревожном состоянии представления /// diff --git a/ScadaWeb/ScadaWebCommon5Beta/UserRights.cs b/ScadaWeb/ScadaWebCommon5Beta/UserRights.cs index e64202cec..0843c0297 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/UserRights.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/UserRights.cs @@ -36,9 +36,18 @@ namespace Scada.Web /// public class UserRights { - protected bool viewAllViewsRight; // право просмотра всех представлений - protected bool controlAllViewsRight; // право управления для всех представлений - protected Dictionary viewRightsDict; // права на предсталения + /// + /// Право просмотра всех представлений + /// + protected bool viewAllViewsRight; + /// + /// Право управления для всех представлений + /// + protected bool controlAllViewsRight; + /// + /// Права на предсталения + /// + protected Dictionary viewRightsDict; /// diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master index 34ec1b80f..de1bd23e3 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master @@ -45,7 +45,7 @@
- <%= GenerateMenuHtml() %> + <%= GenerateMainMenuHtml() %> @@ -63,7 +63,8 @@ -->
- + <%= GenerateExplorerHtml() %> +
Collapse menu diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs index f7647e58d..691493b30 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs @@ -26,7 +26,6 @@ using Scada.Web.Shell; using System; using System.Text; -using System.Web.UI.WebControls; namespace Scada.Web { @@ -36,8 +35,11 @@ namespace Scada.Web /// public partial class MasterMain : System.Web.UI.MasterPage { - private AppData appData; // общие данные веб-приложения - private UserData userData; // данные пользователя приложения + private static readonly TreeViewRenderer treeViewRenderer = + new TreeViewRenderer(); // формирует дерево представлений + + private AppData appData; // общие данные веб-приложения + private UserData userData; // данные пользователя приложения protected bool mainMenuVisible; // отобразить главное меню при загрузке страницы @@ -52,9 +54,9 @@ private void SetMainMenuVisible() } /// - /// Генерировать HTML-код меню + /// Генерировать HTML-код главного меню /// - protected string GenerateMenuHtml() + protected string GenerateMainMenuHtml() { const string ItemTemplate = "
{1}{2}
"; const string LinkTemplate = "{1}"; @@ -63,7 +65,7 @@ protected string GenerateMenuHtml() StringBuilder sbHtml = new StringBuilder(); string curUrl = Request.Url.AbsolutePath; - foreach (Shell.MenuItem menuItem in userData.UserMenu.LinearMenuItems) + foreach (MenuItem menuItem in userData.UserMenu.LinearMenuItems) { string text = Server.HtmlEncode(menuItem.Text); string url = ResolveUrl(menuItem.Url); @@ -83,6 +85,14 @@ protected string GenerateMenuHtml() return sbHtml.ToString(); } + /// + /// Генерировать HTML-код проводника представлений + /// + protected string GenerateExplorerHtml() + { + return treeViewRenderer.GenerateHtml(userData.UserViews.ViewNodes, null); + } + protected void Page_Load(object sender, EventArgs e) { @@ -93,7 +103,7 @@ protected void Page_Load(object sender, EventArgs e) SetMainMenuVisible(); // тест - TreeNode node1 = new TreeNode("Test group"); + /*TreeNode node1 = new TreeNode("Test group"); node1.NavigateUrl = "javascript:void(0);"; tvMainExplorer.Nodes.Add(node1); TreeNode node2 = new TreeNode("Test view 1", "1"); @@ -110,7 +120,7 @@ protected void Page_Load(object sender, EventArgs e) node2.ToolTip = node2.Text; node2.NavigateUrl = string.Format("javascript:alert({0});", i); tvMainExplorer.Nodes.Add(node2); - } + }*/ } protected void lbtnMainLogout_Click(object sender, EventArgs e) diff --git a/ScadaWeb/ScadaWebShell5Beta/config/ViewSettings.xml b/ScadaWeb/ScadaWebShell5Beta/config/ViewSettings.xml index c6c9184b4..ae0cf8fab 100644 --- a/ScadaWeb/ScadaWebShell5Beta/config/ViewSettings.xml +++ b/ScadaWeb/ScadaWebShell5Beta/config/ViewSettings.xml @@ -1,8 +1,8 @@  - - + + - + \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/storage/admin/App/RememberMe.xml b/ScadaWeb/ScadaWebShell5Beta/storage/admin/App/RememberMe.xml index 5d1a7b873..cb0871d79 100644 --- a/ScadaWeb/ScadaWebShell5Beta/storage/admin/App/RememberMe.xml +++ b/ScadaWeb/ScadaWebShell5Beta/storage/admin/App/RememberMe.xml @@ -1,21 +1,6 @@  admin - - 3657e72d-771d-4e8f-a25c-d4f1620c5a9c - 74af23f9-f056-482e-9d1f-b44028e8bf7a - 04/22/2016 12:54:02 - - - 0ccb203b-8d39-4b6f-a7c3-13f93beff892 - eae17565-8b6e-4dfd-b70d-07db61295f0b - 04/22/2016 12:54:17 - - - e77ca15c-2926-4f25-b13b-b03db526f0bd - 5713f3d8-2201-44c9-a817-e531c0713e9b - 04/22/2016 17:08:52 - 4811d600-639e-40a1-b361-ce411fb43be8 ecc7f4b1-7afd-4146-81c9-2b01f0b1fe77 From b6c6792ad6cc9ec247fd4461e7da26937ef51faa Mon Sep 17 00:00:00 2001 From: 2mik Date: Sun, 1 May 2016 00:36:57 +0300 Subject: [PATCH 016/382] ScadaWeb5: tree view --- ScadaData/ScadaData/Client/DataAccess.cs | 3 +- .../PlgScheme/AppCode/SchemeSpec.cs | 2 +- .../Shell/TreeViewRenderer.cs | 2 +- .../ScadaWebCommon5Beta/Shell/ViewNode.cs | 8 ++- ScadaWeb/ScadaWebShell5Beta/MasterMain.Master | 2 + .../ScadaWebShell5Beta.csproj | 8 +++ .../ScadaWebShell5Beta/compilerconfig.json | 4 ++ .../ScadaWebShell5Beta/css/mastermain.css | 11 ++- .../ScadaWebShell5Beta/css/mastermain.less | 10 ++- .../ScadaWebShell5Beta/css/mastermain.min.css | 2 +- ScadaWeb/ScadaWebShell5Beta/css/treeview.css | 33 +++++++++ ScadaWeb/ScadaWebShell5Beta/css/treeview.less | 40 +++++++++++ .../ScadaWebShell5Beta/css/treeview.min.css | 1 + ScadaWeb/ScadaWebShell5Beta/js/mastermain.js | 1 + ScadaWeb/ScadaWebShell5Beta/js/treeview.js | 69 +++++++++++++++++++ 15 files changed, 188 insertions(+), 8 deletions(-) create mode 100644 ScadaWeb/ScadaWebShell5Beta/css/treeview.css create mode 100644 ScadaWeb/ScadaWebShell5Beta/css/treeview.less create mode 100644 ScadaWeb/ScadaWebShell5Beta/css/treeview.min.css create mode 100644 ScadaWeb/ScadaWebShell5Beta/js/treeview.js diff --git a/ScadaData/ScadaData/Client/DataAccess.cs b/ScadaData/ScadaData/Client/DataAccess.cs index 6324ee664..117a0222a 100644 --- a/ScadaData/ScadaData/Client/DataAccess.cs +++ b/ScadaData/ScadaData/Client/DataAccess.cs @@ -220,7 +220,8 @@ public ViewProps GetViewProps(int viewID) { ViewProps viewProps = new ViewProps(viewID); viewProps.FileName = (string)tblInterface.DefaultView[0]["Name"]; - viewProps.ViewTypeCode = Path.GetExtension(viewProps.FileName); + string ext = Path.GetExtension(viewProps.FileName); + viewProps.ViewTypeCode = ext == null ? "" : ext.TrimStart('.'); return viewProps; } else diff --git a/ScadaWeb/OpenPlugins/PlgScheme/AppCode/SchemeSpec.cs b/ScadaWeb/OpenPlugins/PlgScheme/AppCode/SchemeSpec.cs index d75cf7e5b..c4af6bb1b 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/AppCode/SchemeSpec.cs +++ b/ScadaWeb/OpenPlugins/PlgScheme/AppCode/SchemeSpec.cs @@ -38,7 +38,7 @@ public override string IconUrl { get { - return ""; + return "~/plugins/Scheme/images/schemeicon.png"; } } diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/TreeViewRenderer.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/TreeViewRenderer.cs index 7d4f8c773..14e8b16e7 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/TreeViewRenderer.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/TreeViewRenderer.cs @@ -50,7 +50,7 @@ protected string GenDataAttrsHtml(IWebTreeNode webTreeNode) { foreach (KeyValuePair pair in webTreeNode.DataAttrs) { - if (string.IsNullOrWhiteSpace(pair.Key)) + if (!string.IsNullOrWhiteSpace(pair.Key)) sbHtml.Append(string.Format(DataAttrTemplate, pair.Key, pair.Value)); } } diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs index ba2f058ba..840b014e3 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs @@ -36,6 +36,12 @@ namespace Scada.Web.Shell /// public class ViewNode : IWebTreeNode { + /// + /// Шаблон ссылки на страницу отображения представления + /// + protected const string ViewUrlTemplate = "~/View.aspx?viewID={0}"; + + /// /// Конструктор, ограничивающий создание объекта без параметров /// @@ -60,7 +66,7 @@ public ViewNode(ViewSettings.ViewItem viewItem, ViewSpec viewSpec) } else { - Url = viewSpec.GetViewUrl(viewItem.ViewID); + Url = string.Format(ViewUrlTemplate, viewItem.ViewID); IconUrl = viewSpec.IconUrl; } diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master index de1bd23e3..6a1d068c7 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master @@ -13,9 +13,11 @@ + + diff --git a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj index 3958f9f1e..01800c3c4 100644 --- a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj +++ b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj @@ -87,12 +87,19 @@ mastermain.css + + treeview.less + + + treeview.css + + @@ -218,6 +225,7 @@ + Web.config diff --git a/ScadaWeb/ScadaWebShell5Beta/compilerconfig.json b/ScadaWeb/ScadaWebShell5Beta/compilerconfig.json index cc5d0940b..12e007bbd 100644 --- a/ScadaWeb/ScadaWebShell5Beta/compilerconfig.json +++ b/ScadaWeb/ScadaWebShell5Beta/compilerconfig.json @@ -10,5 +10,9 @@ { "outputFile": "css/login.css", "inputFile": "css/login.less" + }, + { + "outputFile": "css/treeview.css", + "inputFile": "css/treeview.less" } ] \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css index 03896279d..762357510 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css @@ -166,12 +166,19 @@ a:active { #tvMainExplorer { font-size: 13px; } -#tvMainExplorer td { - padding: 3px 0; +/*#tvMainExplorer td { + padding: 3px 0; } + #tvMainExplorer td:first-child { + padding-left: 10px; +}*/ +#divMainExplorer .tree-view .indent { padding-left: 10px; } +#divMainExplorer .tree-view .icon { + width: 21px; +} /* Collapse Pane */ #divMainCollapsePane { position: absolute; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less index 2d3a6fc67..c02dccdca 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less @@ -201,12 +201,20 @@ a:active { font-size: 13px; } -#tvMainExplorer td { +/*#tvMainExplorer td { padding: 3px 0; } #tvMainExplorer td:first-child { padding-left: 10px; +}*/ + +#divMainExplorer .tree-view .indent { + padding-left: 10px; +} + +#divMainExplorer .tree-view .icon { + width: 21px; } /* Collapse Pane */ diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css index ee99487fe..54ba7d593 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 230px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;white-space:nowrap;}#divMainHeader .hdr-btn{display:inline-block;height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;font-size:13px;text-decoration:none;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenu{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;}#divMainUserMenu{float:right;height:30px;}#divMainLeftPane{position:fixed;left:0;top:30px;width:230px;min-height:300px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 40px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainMenu .menu-item,#divMainMenu .menu-subitem{padding:5px 10px;text-decoration:none;}#divMainMenu .menu-subitem{padding-left:20px;color:#9ca1a6;display:none;font-size:13px;}#divMainMenu .menu-item:hover,#divMainMenu .menu-subitem:hover{background-color:#191e23;color:#00b9eb;text-decoration:none;}#divMainMenu .menu-item.selected,#divMainMenu .menu-subitem.selected{background-color:#0073aa;color:#fff;}#divMainMenu .menu-item a,#divMainMenu .menu-subitem a{display:inline-block;width:100%;}#divMainMenu .menu-item.with-expander a{width:calc(100% - 20px);}#divMainMenu .expander{width:20px;cursor:pointer;float:right;text-align:right;}#divMainMenu .expander::before{content:"";font-family:'Glyphicons Halflings';font-size:10px;}#divMainMenu .expander.expanded::before{content:"";}#tvMainExplorer{font-size:13px;}#tvMainExplorer td{padding:3px 0;}#tvMainExplorer td:first-child{padding-left:10px;}#divMainCollapsePane{position:absolute;bottom:0;width:200px;height:40px;margin:0 0 0 30px;padding:5px 10px;color:#9ca1a6;font-size:13px;}#divMainCollapsePane i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapsePane:hover{color:#00b9eb;cursor:pointer;} \ No newline at end of file +body{margin:0;padding:30px 0 0 230px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;white-space:nowrap;}#divMainHeader .hdr-btn{display:inline-block;height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;font-size:13px;text-decoration:none;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenu{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;}#divMainUserMenu{float:right;height:30px;}#divMainLeftPane{position:fixed;left:0;top:30px;width:230px;min-height:300px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 40px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainMenu .menu-item,#divMainMenu .menu-subitem{padding:5px 10px;text-decoration:none;}#divMainMenu .menu-subitem{padding-left:20px;color:#9ca1a6;display:none;font-size:13px;}#divMainMenu .menu-item:hover,#divMainMenu .menu-subitem:hover{background-color:#191e23;color:#00b9eb;text-decoration:none;}#divMainMenu .menu-item.selected,#divMainMenu .menu-subitem.selected{background-color:#0073aa;color:#fff;}#divMainMenu .menu-item a,#divMainMenu .menu-subitem a{display:inline-block;width:100%;}#divMainMenu .menu-item.with-expander a{width:calc(100% - 20px);}#divMainMenu .expander{width:20px;cursor:pointer;float:right;text-align:right;}#divMainMenu .expander::before{content:"";font-family:'Glyphicons Halflings';font-size:10px;}#divMainMenu .expander.expanded::before{content:"";}#tvMainExplorer{font-size:13px;}#divMainExplorer .tree-view .indent{padding-left:10px;}#divMainExplorer .tree-view .icon{width:21px;}#divMainCollapsePane{position:absolute;bottom:0;width:200px;height:40px;margin:0 0 0 30px;padding:5px 10px;color:#9ca1a6;font-size:13px;}#divMainCollapsePane i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapsePane:hover{color:#00b9eb;cursor:pointer;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/treeview.css b/ScadaWeb/ScadaWebShell5Beta/css/treeview.css new file mode 100644 index 000000000..c407573ef --- /dev/null +++ b/ScadaWeb/ScadaWebShell5Beta/css/treeview.css @@ -0,0 +1,33 @@ +.tree-view .child-nodes { + display: none; + /*block*/ +} +.tree-view .node div { + display: inline-block; +} +.tree-view .indent { + width: 20px; +} +.tree-view .expander { + width: 20px; + cursor: pointer; +} +.tree-view .expander::before { + content: "\e258"; + font-family: 'Glyphicons Halflings'; +} +.tree-view .expander.expanded::before { + content: "\e259"; +} +.tree-view .expander.empty { + visibility: hidden; +} +.tree-view .stateIcon, +.tree-view .icon { + width: 0; +} +.tree-view .icon img { + width: 16px; + height: 16px; + border: none; +} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/treeview.less b/ScadaWeb/ScadaWebShell5Beta/css/treeview.less new file mode 100644 index 000000000..75b8ad6f4 --- /dev/null +++ b/ScadaWeb/ScadaWebShell5Beta/css/treeview.less @@ -0,0 +1,40 @@ +.tree-view .child-nodes { + display: none; /*block*/ +} + +.tree-view .node div { + display: inline-block; +} + +.tree-view .indent { + width: 20px; +} + +.tree-view .expander { + width: 20px; + cursor: pointer; +} + +.tree-view .expander::before { + content: "\e258"; + font-family: 'Glyphicons Halflings'; +} + +.tree-view .expander.expanded::before { + content: "\e259"; +} + +.tree-view .expander.empty { + visibility: hidden; +} + +.tree-view .stateIcon, +.tree-view .icon { + width: 0; +} + +.tree-view .icon img { + width: 16px; + height: 16px; + border: none; +} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/treeview.min.css b/ScadaWeb/ScadaWebShell5Beta/css/treeview.min.css new file mode 100644 index 000000000..20314abcb --- /dev/null +++ b/ScadaWeb/ScadaWebShell5Beta/css/treeview.min.css @@ -0,0 +1 @@ +.tree-view .child-nodes{display:none;}.tree-view .node div{display:inline-block;}.tree-view .indent{width:20px;}.tree-view .expander{width:20px;cursor:pointer;}.tree-view .expander::before{content:"";font-family:'Glyphicons Halflings';}.tree-view .expander.expanded::before{content:"";}.tree-view .expander.empty{visibility:hidden;}.tree-view .stateIcon,.tree-view .icon{width:0;}.tree-view .icon img{width:16px;height:16px;border:none;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js index bdd1600cc..73b71dff1 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js @@ -79,6 +79,7 @@ $(document).ready(function () { updateMainLayout(); chooseToolWindow(); expandSelectedMenuItem(); + scada.treeView.prepare(); // update layout on window resize $(window).resize(function () { diff --git a/ScadaWeb/ScadaWebShell5Beta/js/treeview.js b/ScadaWeb/ScadaWebShell5Beta/js/treeview.js new file mode 100644 index 000000000..d2a822189 --- /dev/null +++ b/ScadaWeb/ScadaWebShell5Beta/js/treeview.js @@ -0,0 +1,69 @@ +/* + * Tree view control + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + */ + +/* + * Requires: + * - jquery + */ + +// Rapid SCADA namespace +var scada = scada || {}; + +scada.treeView = { + // Expand or collapse tree node + _toggleTreeNode: function (divExpander) { + var treeNode = divExpander.parent(); + var childNodes = treeNode.next(".child-nodes"); + + if (divExpander.hasClass("expanded")) { + divExpander.removeClass("expanded"); + childNodes.css("display", "none"); + } else { + divExpander.addClass("expanded"); + childNodes.css("display", "block"); + } + }, + + // Expand selected tree node + _expandSelectedTreeNode: function (allNodes) { + var selNodes = allNodes.filter(".selected"); + var parDivs = selNodes.parentsUntil(".tree-view", ".child-nodes"); + + parDivs.prev(".node").children(".expander").addClass("expanded"); + parDivs.css("display", "block"); + }, + + // Tune tree view elements and bind events + prepare: function () { + var treeViews = $(".tree-view"); + var indentWidth = treeViews.find(".indent:first").width(); + var allNodes = treeViews.find(".node"); + + // set width of indents according to their level + allNodes.each(function () { + var level = $(this).attr("data-level"); + $(this).children(".indent").width(indentWidth * level); + }); + + // expand selected tree node + this._expandSelectedTreeNode(allNodes); + + // toggle tree node on click + var thisTreeView = this; + allNodes.children(".expander").click(function () { + thisTreeView._toggleTreeNode($(this)); + }); + + allNodes.find("a").click(function (event) { + if (!$(this).attr("href")) { + event.preventDefault(); + thisTreeView._toggleTreeNode($(this).parent().siblings(".expander")); + } + }); + } +}; \ No newline at end of file From 9b0ba11b7c308d2d6e31b9b426c6090a229105dd Mon Sep 17 00:00:00 2001 From: 2mik Date: Wed, 4 May 2016 22:08:45 +0300 Subject: [PATCH 017/382] ScadaWeb5: tree --- .../ScadaWebCommon5Beta/Shell/TreeViewRenderer.cs | 8 ++++---- .../ScadaWebShell5Beta/ScadaWebShell5Beta.csproj | 1 + ScadaWeb/ScadaWebShell5Beta/css/mastermain.css | 12 +++++++++++- ScadaWeb/ScadaWebShell5Beta/css/mastermain.less | 14 +++++++++++++- .../ScadaWebShell5Beta/css/mastermain.min.css | 2 +- .../ScadaWebShell5Beta/images/treeview/doc1.png | Bin 0 -> 223 bytes .../images/treeview/folder1.png | Bin 0 -> 202 bytes .../images/treeview/scheme1.png | Bin 0 -> 170 bytes 8 files changed, 30 insertions(+), 7 deletions(-) create mode 100644 ScadaWeb/ScadaWebShell5Beta/images/treeview/doc1.png create mode 100644 ScadaWeb/ScadaWebShell5Beta/images/treeview/folder1.png create mode 100644 ScadaWeb/ScadaWebShell5Beta/images/treeview/scheme1.png diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/TreeViewRenderer.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/TreeViewRenderer.cs index 14e8b16e7..74b482f68 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/TreeViewRenderer.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/TreeViewRenderer.cs @@ -91,16 +91,16 @@ protected string GenTreeViewHtml(IList treeNodes, object selObj, bool topLevel) bool containsSubitems = webTreeNode.Children.Count > 0; string expanderEmpty = containsSubitems ? "" : " empty"; - string iconEmpty; + string iconCssClass; string icon; if (string.IsNullOrEmpty(webTreeNode.IconUrl)) { - iconEmpty = " empty"; + iconCssClass = containsSubitems ? " folder" : " empty"; icon = ""; } else { - iconEmpty = ""; + iconCssClass = ""; icon = string.Format(ImageTemplate, webTreeNode.IconUrl); } @@ -109,7 +109,7 @@ protected string GenTreeViewHtml(IList treeNodes, object selObj, bool topLevel) string.Format(LinkTemplate, webTreeNode.Url, text) : text; sbHtml.AppendLine(string.Format(NodeTemplate, - selected, dataAttrs, expanderEmpty, iconEmpty, icon, linkOrText)); + selected, dataAttrs, expanderEmpty, iconCssClass, icon, linkOrText)); if (containsSubitems) sbHtml.Append(GenTreeViewHtml(webTreeNode.Children, selObj, false)); diff --git a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj index 01800c3c4..90b160506 100644 --- a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj +++ b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj @@ -96,6 +96,7 @@ + diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css index 762357510..411ead5cd 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css @@ -173,11 +173,21 @@ a:active { #tvMainExplorer td:first-child { padding-left: 10px; }*/ -#divMainExplorer .tree-view .indent { +#divMainExplorer .tree-view .node { padding-left: 10px; } #divMainExplorer .tree-view .icon { width: 21px; + height: 20px; + background-size: 16px 16px; + background-repeat: no-repeat; + background-position: left bottom; +} +#divMainExplorer .tree-view .icon.folder { + background-image: url(../images/treeview/folder1.png); +} +#divMainExplorer .tree-view .icon.empty { + background-image: url(../images/treeview/scheme1.png); } /* Collapse Pane */ #divMainCollapsePane { diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less index c02dccdca..8d6c524e7 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less @@ -209,12 +209,24 @@ a:active { padding-left: 10px; }*/ -#divMainExplorer .tree-view .indent { +#divMainExplorer .tree-view .node { padding-left: 10px; } #divMainExplorer .tree-view .icon { width: 21px; + height: 20px; + background-size: 16px 16px; + background-repeat: no-repeat; + background-position: left bottom; +} + +#divMainExplorer .tree-view .icon.folder { + background-image: url(../images/treeview/folder1.png); +} + +#divMainExplorer .tree-view .icon.empty { + background-image: url(../images/treeview/scheme1.png); } /* Collapse Pane */ diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css index 54ba7d593..edb9045d2 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 230px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;white-space:nowrap;}#divMainHeader .hdr-btn{display:inline-block;height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;font-size:13px;text-decoration:none;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenu{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;}#divMainUserMenu{float:right;height:30px;}#divMainLeftPane{position:fixed;left:0;top:30px;width:230px;min-height:300px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 40px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainMenu .menu-item,#divMainMenu .menu-subitem{padding:5px 10px;text-decoration:none;}#divMainMenu .menu-subitem{padding-left:20px;color:#9ca1a6;display:none;font-size:13px;}#divMainMenu .menu-item:hover,#divMainMenu .menu-subitem:hover{background-color:#191e23;color:#00b9eb;text-decoration:none;}#divMainMenu .menu-item.selected,#divMainMenu .menu-subitem.selected{background-color:#0073aa;color:#fff;}#divMainMenu .menu-item a,#divMainMenu .menu-subitem a{display:inline-block;width:100%;}#divMainMenu .menu-item.with-expander a{width:calc(100% - 20px);}#divMainMenu .expander{width:20px;cursor:pointer;float:right;text-align:right;}#divMainMenu .expander::before{content:"";font-family:'Glyphicons Halflings';font-size:10px;}#divMainMenu .expander.expanded::before{content:"";}#tvMainExplorer{font-size:13px;}#divMainExplorer .tree-view .indent{padding-left:10px;}#divMainExplorer .tree-view .icon{width:21px;}#divMainCollapsePane{position:absolute;bottom:0;width:200px;height:40px;margin:0 0 0 30px;padding:5px 10px;color:#9ca1a6;font-size:13px;}#divMainCollapsePane i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapsePane:hover{color:#00b9eb;cursor:pointer;} \ No newline at end of file +body{margin:0;padding:30px 0 0 230px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;white-space:nowrap;}#divMainHeader .hdr-btn{display:inline-block;height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;font-size:13px;text-decoration:none;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenu{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;}#divMainUserMenu{float:right;height:30px;}#divMainLeftPane{position:fixed;left:0;top:30px;width:230px;min-height:300px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 40px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainMenu .menu-item,#divMainMenu .menu-subitem{padding:5px 10px;text-decoration:none;}#divMainMenu .menu-subitem{padding-left:20px;color:#9ca1a6;display:none;font-size:13px;}#divMainMenu .menu-item:hover,#divMainMenu .menu-subitem:hover{background-color:#191e23;color:#00b9eb;text-decoration:none;}#divMainMenu .menu-item.selected,#divMainMenu .menu-subitem.selected{background-color:#0073aa;color:#fff;}#divMainMenu .menu-item a,#divMainMenu .menu-subitem a{display:inline-block;width:100%;}#divMainMenu .menu-item.with-expander a{width:calc(100% - 20px);}#divMainMenu .expander{width:20px;cursor:pointer;float:right;text-align:right;}#divMainMenu .expander::before{content:"";font-family:'Glyphicons Halflings';font-size:10px;}#divMainMenu .expander.expanded::before{content:"";}#tvMainExplorer{font-size:13px;}#divMainExplorer .tree-view .node{padding-left:10px;}#divMainExplorer .tree-view .icon{width:21px;height:20px;background-size:16px 16px;background-repeat:no-repeat;background-position:left bottom;}#divMainExplorer .tree-view .icon.folder{background-image:url(../images/treeview/folder1.png);}#divMainExplorer .tree-view .icon.empty{background-image:url(../images/treeview/scheme1.png);}#divMainCollapsePane{position:absolute;bottom:0;width:200px;height:40px;margin:0 0 0 30px;padding:5px 10px;color:#9ca1a6;font-size:13px;}#divMainCollapsePane i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapsePane:hover{color:#00b9eb;cursor:pointer;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/images/treeview/doc1.png b/ScadaWeb/ScadaWebShell5Beta/images/treeview/doc1.png new file mode 100644 index 0000000000000000000000000000000000000000..cb45e88707de58750753e398e303f0828cf0fd26 GIT binary patch literal 223 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5X8a-VcLn>}1B`gpyI3#g@U+r&yhqvqsWltVG+SPEGL0(>d z&aT*)7zr*eE(4xK$M79_Z>xCDIv#A~ss8qc(U7&F?G1Yrn|Vv8$p0punFfhX3=?+B zs~vO6T5vF?=`!~XhE3ZY?m4pgOmTa>f-%IPfi-&7;?OiF#sf>voN(Y}5J+Iq&d@Ae TbAjV2&@l|2u6{1-oD!MngRLzjX)f8>VJgy6-!d?ry=j>Z4*{N7;h?KzA^By85}Sb4q9e0FORLGXMYp literal 0 HcmV?d00001 diff --git a/ScadaWeb/ScadaWebShell5Beta/images/treeview/scheme1.png b/ScadaWeb/ScadaWebShell5Beta/images/treeview/scheme1.png new file mode 100644 index 0000000000000000000000000000000000000000..1b111b4182866f5f3b9202997ee1d9c69f9da55d GIT binary patch literal 170 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9F5he4R}c>anMpkR!r zi(^Pd+|kL7Tn>sHF4nWZ|BT&xYscHmizGPME-NGo#O?`PW8xILJDgiVY56V2ttP8| z7)pwn73^m)+&$0m^R%~8lPPONjAp~z-de5$K6*m)AN=#~`OmsO$fQokbH|;OUEkb* P_Aq$5`njxgN@xNALrysu literal 0 HcmV?d00001 From 271e86122f13c08cae2baedde31d034683d7dc40 Mon Sep 17 00:00:00 2001 From: 2mik Date: Wed, 4 May 2016 23:06:21 +0300 Subject: [PATCH 018/382] ScadaWeb5: tree view images --- .../ScadaWebShell5Beta/ScadaWebShell5Beta.csproj | 3 +++ ScadaWeb/ScadaWebShell5Beta/css/mastermain.css | 13 +++---------- ScadaWeb/ScadaWebShell5Beta/css/mastermain.less | 14 +++----------- .../ScadaWebShell5Beta/css/mastermain.min.css | 2 +- .../ScadaWebShell5Beta/images/treeview/doc1.png | Bin 223 -> 0 bytes .../images/treeview/document.png | Bin 0 -> 2860 bytes .../images/treeview/folder.png | Bin 0 -> 168 bytes .../images/treeview/folder1.png | Bin 202 -> 0 bytes .../images/treeview/scheme.png | Bin 0 -> 233 bytes .../images/treeview/scheme1.png | Bin 170 -> 0 bytes .../ScadaWebShell5Beta/images/treeview/table.png | Bin 0 -> 168 bytes 11 files changed, 10 insertions(+), 22 deletions(-) delete mode 100644 ScadaWeb/ScadaWebShell5Beta/images/treeview/doc1.png create mode 100644 ScadaWeb/ScadaWebShell5Beta/images/treeview/document.png create mode 100644 ScadaWeb/ScadaWebShell5Beta/images/treeview/folder.png delete mode 100644 ScadaWeb/ScadaWebShell5Beta/images/treeview/folder1.png create mode 100644 ScadaWeb/ScadaWebShell5Beta/images/treeview/scheme.png delete mode 100644 ScadaWeb/ScadaWebShell5Beta/images/treeview/scheme1.png create mode 100644 ScadaWeb/ScadaWebShell5Beta/images/treeview/table.png diff --git a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj index 90b160506..600a4b1df 100644 --- a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj +++ b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj @@ -96,7 +96,10 @@ + + + diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css index 411ead5cd..5da947c6d 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css @@ -163,16 +163,9 @@ a:active { content: "\e259"; } /* Explorer */ -#tvMainExplorer { +#divMainExplorer { font-size: 13px; } -/*#tvMainExplorer td { - padding: 3px 0; -} - -#tvMainExplorer td:first-child { - padding-left: 10px; -}*/ #divMainExplorer .tree-view .node { padding-left: 10px; } @@ -184,10 +177,10 @@ a:active { background-position: left bottom; } #divMainExplorer .tree-view .icon.folder { - background-image: url(../images/treeview/folder1.png); + background-image: url(../images/treeview/folder.png); } #divMainExplorer .tree-view .icon.empty { - background-image: url(../images/treeview/scheme1.png); + background-image: url(../images/treeview/table.png); } /* Collapse Pane */ #divMainCollapsePane { diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less index 8d6c524e7..37c274426 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less @@ -197,18 +197,10 @@ a:active { /* Explorer */ -#tvMainExplorer { +#divMainExplorer { font-size: 13px; } -/*#tvMainExplorer td { - padding: 3px 0; -} - -#tvMainExplorer td:first-child { - padding-left: 10px; -}*/ - #divMainExplorer .tree-view .node { padding-left: 10px; } @@ -222,11 +214,11 @@ a:active { } #divMainExplorer .tree-view .icon.folder { - background-image: url(../images/treeview/folder1.png); + background-image: url(../images/treeview/folder.png); } #divMainExplorer .tree-view .icon.empty { - background-image: url(../images/treeview/scheme1.png); + background-image: url(../images/treeview/table.png); } /* Collapse Pane */ diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css index edb9045d2..4dcb7b18b 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 230px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;white-space:nowrap;}#divMainHeader .hdr-btn{display:inline-block;height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;font-size:13px;text-decoration:none;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenu{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;}#divMainUserMenu{float:right;height:30px;}#divMainLeftPane{position:fixed;left:0;top:30px;width:230px;min-height:300px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 40px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainMenu .menu-item,#divMainMenu .menu-subitem{padding:5px 10px;text-decoration:none;}#divMainMenu .menu-subitem{padding-left:20px;color:#9ca1a6;display:none;font-size:13px;}#divMainMenu .menu-item:hover,#divMainMenu .menu-subitem:hover{background-color:#191e23;color:#00b9eb;text-decoration:none;}#divMainMenu .menu-item.selected,#divMainMenu .menu-subitem.selected{background-color:#0073aa;color:#fff;}#divMainMenu .menu-item a,#divMainMenu .menu-subitem a{display:inline-block;width:100%;}#divMainMenu .menu-item.with-expander a{width:calc(100% - 20px);}#divMainMenu .expander{width:20px;cursor:pointer;float:right;text-align:right;}#divMainMenu .expander::before{content:"";font-family:'Glyphicons Halflings';font-size:10px;}#divMainMenu .expander.expanded::before{content:"";}#tvMainExplorer{font-size:13px;}#divMainExplorer .tree-view .node{padding-left:10px;}#divMainExplorer .tree-view .icon{width:21px;height:20px;background-size:16px 16px;background-repeat:no-repeat;background-position:left bottom;}#divMainExplorer .tree-view .icon.folder{background-image:url(../images/treeview/folder1.png);}#divMainExplorer .tree-view .icon.empty{background-image:url(../images/treeview/scheme1.png);}#divMainCollapsePane{position:absolute;bottom:0;width:200px;height:40px;margin:0 0 0 30px;padding:5px 10px;color:#9ca1a6;font-size:13px;}#divMainCollapsePane i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapsePane:hover{color:#00b9eb;cursor:pointer;} \ No newline at end of file +body{margin:0;padding:30px 0 0 230px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;white-space:nowrap;}#divMainHeader .hdr-btn{display:inline-block;height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;font-size:13px;text-decoration:none;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenu{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;}#divMainUserMenu{float:right;height:30px;}#divMainLeftPane{position:fixed;left:0;top:30px;width:230px;min-height:300px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 40px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainMenu .menu-item,#divMainMenu .menu-subitem{padding:5px 10px;text-decoration:none;}#divMainMenu .menu-subitem{padding-left:20px;color:#9ca1a6;display:none;font-size:13px;}#divMainMenu .menu-item:hover,#divMainMenu .menu-subitem:hover{background-color:#191e23;color:#00b9eb;text-decoration:none;}#divMainMenu .menu-item.selected,#divMainMenu .menu-subitem.selected{background-color:#0073aa;color:#fff;}#divMainMenu .menu-item a,#divMainMenu .menu-subitem a{display:inline-block;width:100%;}#divMainMenu .menu-item.with-expander a{width:calc(100% - 20px);}#divMainMenu .expander{width:20px;cursor:pointer;float:right;text-align:right;}#divMainMenu .expander::before{content:"";font-family:'Glyphicons Halflings';font-size:10px;}#divMainMenu .expander.expanded::before{content:"";}#divMainExplorer{font-size:13px;}#divMainExplorer .tree-view .node{padding-left:10px;}#divMainExplorer .tree-view .icon{width:21px;height:20px;background-size:16px 16px;background-repeat:no-repeat;background-position:left bottom;}#divMainExplorer .tree-view .icon.folder{background-image:url(../images/treeview/folder.png);}#divMainExplorer .tree-view .icon.empty{background-image:url(../images/treeview/table.png);}#divMainCollapsePane{position:absolute;bottom:0;width:200px;height:40px;margin:0 0 0 30px;padding:5px 10px;color:#9ca1a6;font-size:13px;}#divMainCollapsePane i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapsePane:hover{color:#00b9eb;cursor:pointer;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/images/treeview/doc1.png b/ScadaWeb/ScadaWebShell5Beta/images/treeview/doc1.png deleted file mode 100644 index cb45e88707de58750753e398e303f0828cf0fd26..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 223 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 OR0dC1KbLh*2~7YeLpmD( literal 0 HcmV?d00001 diff --git a/ScadaWeb/ScadaWebShell5Beta/images/treeview/folder1.png b/ScadaWeb/ScadaWebShell5Beta/images/treeview/folder1.png deleted file mode 100644 index ff87650fd98d77b21822803c45dcb8ef74f2fa9b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 202 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9F5M?jcysy3fAP_V|+ z#WAEJ?(Jnmt|JNptQYJLGFPYu-%j~1C0Hb!|By4M*WL3iYYwjym$<89G|RNw)~SDH zHcEPFK9tt6cWDXgnaFIx@S)J*_Om&Uqq4t5WFK7Dbyen=1c#yXS}xu7-#^`KoU-Yc wh*QIN>ngRLzjX)f8>VJgy6-!d?ry=j>Z4*{N7;h?KzA^By85}Sb4q9e0FORLGXMYp diff --git a/ScadaWeb/ScadaWebShell5Beta/images/treeview/scheme.png b/ScadaWeb/ScadaWebShell5Beta/images/treeview/scheme.png new file mode 100644 index 0000000000000000000000000000000000000000..f3eef656b750f75f674f16b3960242c163527372 GIT binary patch literal 233 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx;}931;MLP~iB|!~f&I`>!UYtfw7~FZY}^^S5{) zmGEo}(=NNM*D|LDam_lw?DJ%MQ>12<Xnweig-%d!J&37l*aGUD&)|_U{MI>qcweFokV5 fT<$uDiHBigku#T)^!6h_XEAuX`njxgN@xNAm@!v! literal 0 HcmV?d00001 diff --git a/ScadaWeb/ScadaWebShell5Beta/images/treeview/scheme1.png b/ScadaWeb/ScadaWebShell5Beta/images/treeview/scheme1.png deleted file mode 100644 index 1b111b4182866f5f3b9202997ee1d9c69f9da55d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 170 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9F5he4R}c>anMpkR!r zi(^Pd+|kL7Tn>sHF4nWZ|BT&xYscHmizGPME-NGo#O?`PW8xILJDgiVY56V2ttP8| z7)pwn73^m)+&$0m^R%~8lPPONjAp~z-de5$K6*m)AN=#~`OmsO$fQokbH|;OUEkb* P_Aq$5`njxgN@xNALrysu diff --git a/ScadaWeb/ScadaWebShell5Beta/images/treeview/table.png b/ScadaWeb/ScadaWebShell5Beta/images/treeview/table.png new file mode 100644 index 0000000000000000000000000000000000000000..1b61744c55b4dc28cf46307bbbfa2deaf9084b7a GIT binary patch literal 168 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XygXeTLn>}1{rUgjo>{e_v(f1QQ}Ukr|MOIqb8vAPEs|hh z?QwbazyCb<|A`_Gk}fOo6jU@%obcUkuYts#15Mhkg4`19Hs(w$5)1{q&) literal 0 HcmV?d00001 From cea3c186dbfee90c782a395ceec76790c644e38c Mon Sep 17 00:00:00 2001 From: 2mik Date: Thu, 5 May 2016 00:36:36 +0300 Subject: [PATCH 019/382] ScadaWeb5: tree view tricks --- .../ScadaWebShell5Beta/ScadaWebShell5Beta.csproj | 8 ++++---- ScadaWeb/ScadaWebShell5Beta/css/mastermain.css | 10 +++++++++- ScadaWeb/ScadaWebShell5Beta/css/mastermain.less | 15 ++++++++++++++- .../ScadaWebShell5Beta/css/mastermain.min.css | 2 +- 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj index 600a4b1df..51b63f8ac 100644 --- a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj +++ b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj @@ -96,10 +96,10 @@ - - - - + + + + diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css index 5da947c6d..3a5ba6680 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css @@ -165,13 +165,18 @@ a:active { /* Explorer */ #divMainExplorer { font-size: 13px; + line-height: 13px; } #divMainExplorer .tree-view .node { padding-left: 10px; } +#divMainExplorer .tree-view .expander::before { + font-size: 10px; +} #divMainExplorer .tree-view .icon { width: 21px; - height: 20px; + height: 16px; + line-height: 0; background-size: 16px 16px; background-repeat: no-repeat; background-position: left bottom; @@ -182,6 +187,9 @@ a:active { #divMainExplorer .tree-view .icon.empty { background-image: url(../images/treeview/table.png); } +/*#divMainExplorer .tree-view .node div { + border: 1px solid red; +}*/ /* Collapse Pane */ #divMainCollapsePane { position: absolute; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less index 37c274426..a98448e7a 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less @@ -199,15 +199,21 @@ a:active { #divMainExplorer { font-size: 13px; + line-height: 13px; } #divMainExplorer .tree-view .node { padding-left: 10px; } +#divMainExplorer .tree-view .expander::before { + font-size: 10px; +} + #divMainExplorer .tree-view .icon { width: 21px; - height: 20px; + height: 16px; + line-height: 0; background-size: 16px 16px; background-repeat: no-repeat; background-position: left bottom; @@ -221,6 +227,13 @@ a:active { background-image: url(../images/treeview/table.png); } +#divMainExplorer .tree-view .text { +} + +/*#divMainExplorer .tree-view .node div { + border: 1px solid red; +}*/ + /* Collapse Pane */ #divMainCollapsePane { diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css index 4dcb7b18b..781735f6a 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 230px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;white-space:nowrap;}#divMainHeader .hdr-btn{display:inline-block;height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;font-size:13px;text-decoration:none;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenu{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;}#divMainUserMenu{float:right;height:30px;}#divMainLeftPane{position:fixed;left:0;top:30px;width:230px;min-height:300px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 40px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainMenu .menu-item,#divMainMenu .menu-subitem{padding:5px 10px;text-decoration:none;}#divMainMenu .menu-subitem{padding-left:20px;color:#9ca1a6;display:none;font-size:13px;}#divMainMenu .menu-item:hover,#divMainMenu .menu-subitem:hover{background-color:#191e23;color:#00b9eb;text-decoration:none;}#divMainMenu .menu-item.selected,#divMainMenu .menu-subitem.selected{background-color:#0073aa;color:#fff;}#divMainMenu .menu-item a,#divMainMenu .menu-subitem a{display:inline-block;width:100%;}#divMainMenu .menu-item.with-expander a{width:calc(100% - 20px);}#divMainMenu .expander{width:20px;cursor:pointer;float:right;text-align:right;}#divMainMenu .expander::before{content:"";font-family:'Glyphicons Halflings';font-size:10px;}#divMainMenu .expander.expanded::before{content:"";}#divMainExplorer{font-size:13px;}#divMainExplorer .tree-view .node{padding-left:10px;}#divMainExplorer .tree-view .icon{width:21px;height:20px;background-size:16px 16px;background-repeat:no-repeat;background-position:left bottom;}#divMainExplorer .tree-view .icon.folder{background-image:url(../images/treeview/folder.png);}#divMainExplorer .tree-view .icon.empty{background-image:url(../images/treeview/table.png);}#divMainCollapsePane{position:absolute;bottom:0;width:200px;height:40px;margin:0 0 0 30px;padding:5px 10px;color:#9ca1a6;font-size:13px;}#divMainCollapsePane i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapsePane:hover{color:#00b9eb;cursor:pointer;} \ No newline at end of file +body{margin:0;padding:30px 0 0 230px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;white-space:nowrap;}#divMainHeader .hdr-btn{display:inline-block;height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;font-size:13px;text-decoration:none;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenu{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;}#divMainUserMenu{float:right;height:30px;}#divMainLeftPane{position:fixed;left:0;top:30px;width:230px;min-height:300px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 40px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainMenu .menu-item,#divMainMenu .menu-subitem{padding:5px 10px;text-decoration:none;}#divMainMenu .menu-subitem{padding-left:20px;color:#9ca1a6;display:none;font-size:13px;}#divMainMenu .menu-item:hover,#divMainMenu .menu-subitem:hover{background-color:#191e23;color:#00b9eb;text-decoration:none;}#divMainMenu .menu-item.selected,#divMainMenu .menu-subitem.selected{background-color:#0073aa;color:#fff;}#divMainMenu .menu-item a,#divMainMenu .menu-subitem a{display:inline-block;width:100%;}#divMainMenu .menu-item.with-expander a{width:calc(100% - 20px);}#divMainMenu .expander{width:20px;cursor:pointer;float:right;text-align:right;}#divMainMenu .expander::before{content:"";font-family:'Glyphicons Halflings';font-size:10px;}#divMainMenu .expander.expanded::before{content:"";}#divMainExplorer{font-size:13px;line-height:13px;}#divMainExplorer .tree-view .node{padding-left:10px;}#divMainExplorer .tree-view .expander::before{font-size:10px;}#divMainExplorer .tree-view .icon{width:21px;height:16px;line-height:0;background-size:16px 16px;background-repeat:no-repeat;background-position:left bottom;}#divMainExplorer .tree-view .icon.folder{background-image:url(../images/treeview/folder.png);}#divMainExplorer .tree-view .icon.empty{background-image:url(../images/treeview/table.png);}#divMainCollapsePane{position:absolute;bottom:0;width:200px;height:40px;margin:0 0 0 30px;padding:5px 10px;color:#9ca1a6;font-size:13px;}#divMainCollapsePane i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapsePane:hover{color:#00b9eb;cursor:pointer;} \ No newline at end of file From 3866267b931fd931e46202f722fbcd0bb067eee0 Mon Sep 17 00:00:00 2001 From: 2mik Date: Thu, 5 May 2016 12:46:04 +0300 Subject: [PATCH 020/382] ScadaWeb5: tree view ready! --- .../OpenPlugins/PlgScheme/PlgScheme.csproj | 1 + .../plugins/Scheme/images/schemeicon.png | Bin 0 -> 233 bytes .../ScadaWebCommon5Beta/Shell/IWebTreeNode.cs | 8 +- .../ScadaWebCommon5Beta/Shell/MenuItem.cs | 48 +++++++++- .../Shell/TreeViewRenderer.cs | 62 +++++++++---- .../ScadaWebCommon5Beta/Shell/UserMenu.cs | 33 +++---- .../ScadaWebCommon5Beta/Shell/ViewNode.cs | 19 ++-- ScadaWeb/ScadaWebShell5Beta/MasterMain.Master | 17 ---- .../ScadaWebShell5Beta/MasterMain.Master.cs | 60 +++--------- .../MasterMain.Master.designer.cs | 9 -- .../ScadaWebShell5Beta/css/mastermain.css | 83 ++++++----------- .../ScadaWebShell5Beta/css/mastermain.less | 87 ++++++------------ .../ScadaWebShell5Beta/css/mastermain.min.css | 2 +- ScadaWeb/ScadaWebShell5Beta/css/treeview.css | 29 +++++- ScadaWeb/ScadaWebShell5Beta/css/treeview.less | 34 ++++++- .../ScadaWebShell5Beta/css/treeview.min.css | 2 +- ScadaWeb/ScadaWebShell5Beta/js/mastermain.js | 42 --------- 17 files changed, 243 insertions(+), 293 deletions(-) create mode 100644 ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/images/schemeicon.png diff --git a/ScadaWeb/OpenPlugins/PlgScheme/PlgScheme.csproj b/ScadaWeb/OpenPlugins/PlgScheme/PlgScheme.csproj index b3b6c01ce..61e527bf5 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/PlgScheme.csproj +++ b/ScadaWeb/OpenPlugins/PlgScheme/PlgScheme.csproj @@ -85,6 +85,7 @@ scheme.css + diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/images/schemeicon.png b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/images/schemeicon.png new file mode 100644 index 0000000000000000000000000000000000000000..f3eef656b750f75f674f16b3960242c163527372 GIT binary patch literal 233 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx;}931;MLP~iB|!~f&I`>!UYtfw7~FZY}^^S5{) zmGEo}(=NNM*D|LDam_lw?DJ%MQ>12<Xnweig-%d!J&37l*aGUD&)|_U{MI>qcweFokV5 fT<$uDiHBigku#T)^!6h_XEAuX`njxgN@xNAm@!v! literal 0 HcmV?d00001 diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/IWebTreeNode.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/IWebTreeNode.cs index c2f7bbfd1..8e67393bc 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/IWebTreeNode.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/IWebTreeNode.cs @@ -55,14 +55,14 @@ public interface IWebTreeNode int Level { get; set; } /// - /// Получить атрибуты данных в виде пар "имя-значение" + /// Получить дочерние узлы /// - SortedList DataAttrs { get; } + IList Children { get; } /// - /// Получить дочерние узлы + /// Получить атрибуты данных в виде пар "имя-значение" /// - IList Children { get; } + SortedList DataAttrs { get; } /// diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/MenuItem.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/MenuItem.cs index 26e1f09f5..84dafc230 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/MenuItem.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/MenuItem.cs @@ -24,7 +24,9 @@ */ using System; +using System.Collections; using System.Collections.Generic; +using System.Web; namespace Scada.Web.Shell { @@ -32,7 +34,7 @@ namespace Scada.Web.Shell /// Menu item /// Элемент меню /// - public class MenuItem : IComparable + public class MenuItem : IWebTreeNode, IComparable { /// /// Конструктор, ограничивающий создание объекта без параметров @@ -48,7 +50,7 @@ protected MenuItem() public MenuItem(string text, string url, int sortOrder = SortOrders.First) { Text = text ?? ""; - Url = url ?? ""; + Url = string.IsNullOrEmpty(url) ? "" : VirtualPathUtility.ToAbsolute(url); SortOrder = sortOrder; Level = -1; Subitems = new List(); @@ -81,6 +83,40 @@ public MenuItem(string text, string url, int sortOrder = SortOrders.First) public List Subitems { get; protected set; } + /// + /// Получить ссылку на иконку + /// + string IWebTreeNode.IconUrl + { + get + { + return ""; + } + } + + /// + /// Получить дочерние узлы + /// + IList IWebTreeNode.Children + { + get + { + return Subitems; + } + } + + /// + /// Получить атрибуты данных в виде пар "имя-значение" + /// + SortedList IWebTreeNode.DataAttrs + { + get + { + return null; + } + } + + /// /// Сравнить текущий объект с другим объектом такого же типа /// @@ -122,5 +158,13 @@ public static MenuItem FromStandardMenuItem(StandardMenuItems standardMenuItem) return new MenuItem(WebPhrases.AboutMenuItem, "~/About.aspx", SortOrders.Last); } } + + /// + /// Определить, что узел соответствует выбранному объекту + /// + bool IWebTreeNode.IsSelected(object selObj) + { + return selObj == null ? false : string.Equals(Url, selObj.ToString(), StringComparison.OrdinalIgnoreCase); + } } } diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/TreeViewRenderer.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/TreeViewRenderer.cs index 74b482f68..14e8bb83c 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/TreeViewRenderer.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/TreeViewRenderer.cs @@ -36,6 +36,26 @@ namespace Scada.Web.Shell /// public class TreeViewRenderer { + /// + /// Параметры отображения дерева + /// + public class Options + { + /// + /// Получить или установить + /// + public bool ShowIcons { get; set; } + /// + /// Получить или установить ссылку на иконку папки + /// + public string FolderImageUrl { get; set; } + /// + /// Получить или установить ссылку на иконку документа, если иконка узла пустая + /// + public string DocumentImageUrl { get; set; } + } + + /// /// Генерировать HTML-код атрибутов данных /// @@ -61,16 +81,19 @@ protected string GenDataAttrsHtml(IWebTreeNode webTreeNode) /// /// Рекурсивно генерировать HTML-код дерева /// - protected string GenTreeViewHtml(IList treeNodes, object selObj, bool topLevel) + protected string GenTreeViewHtml(IList treeNodes, object selObj, Options options, bool topLevel) { const string NodeTemplate = - "
" + + "
" + + "
" + "
" + - "
" + + "
" + "
" + - "
{4}
" + - "
{5}
"; - const string ImageTemplate = ""; + "
{3}
" + + "
{4}
" + + "
" + + "
"; + const string IconTemplate = ""; const string LinkTemplate = "{1}"; StringBuilder sbHtml = new StringBuilder(); @@ -91,28 +114,28 @@ protected string GenTreeViewHtml(IList treeNodes, object selObj, bool topLevel) bool containsSubitems = webTreeNode.Children.Count > 0; string expanderEmpty = containsSubitems ? "" : " empty"; - string iconCssClass; string icon; - if (string.IsNullOrEmpty(webTreeNode.IconUrl)) + if (options.ShowIcons) { - iconCssClass = containsSubitems ? " folder" : " empty"; - icon = ""; + string iconUrl = string.IsNullOrEmpty(webTreeNode.IconUrl) ? + (containsSubitems ? options.FolderImageUrl : options.DocumentImageUrl) : + webTreeNode.IconUrl; + icon = string.Format(IconTemplate, iconUrl); } else { - iconCssClass = ""; - icon = string.Format(ImageTemplate, webTreeNode.IconUrl); + icon = ""; } string text = HttpUtility.HtmlEncode(webTreeNode.Text); - string linkOrText = containsSubitems || !string.IsNullOrEmpty(webTreeNode.Url) ? - string.Format(LinkTemplate, webTreeNode.Url, text) : text; + string textOrLink = string.IsNullOrEmpty(webTreeNode.Url) ? + text : string.Format(LinkTemplate, webTreeNode.Url, text); sbHtml.AppendLine(string.Format(NodeTemplate, - selected, dataAttrs, expanderEmpty, iconCssClass, icon, linkOrText)); + selected, dataAttrs, expanderEmpty, icon, textOrLink)); if (containsSubitems) - sbHtml.Append(GenTreeViewHtml(webTreeNode.Children, selObj, false)); + sbHtml.Append(GenTreeViewHtml(webTreeNode.Children, selObj, options, false)); } } } @@ -125,9 +148,12 @@ protected string GenTreeViewHtml(IList treeNodes, object selObj, bool topLevel) /// /// Генерировать HTML-код дерева для узлов, поддерживающих IWebTreeNode /// - public string GenerateHtml(IList treeNodes, object selObj) + public string GenerateHtml(IList treeNodes, object selObj, Options options) { - return GenTreeViewHtml(treeNodes, selObj, true); + if (options == null) + options = new Options(); + + return GenTreeViewHtml(treeNodes, selObj, options, true); } } } diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/UserMenu.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/UserMenu.cs index db95600e8..7537a83ae 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/UserMenu.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/UserMenu.cs @@ -59,7 +59,6 @@ public UserMenu(Log log) this.log = log; MenuItems = new List(); - LinearMenuItems = new List(); } @@ -68,16 +67,11 @@ public UserMenu(Log log) /// public List MenuItems { get; private set; } - /// - /// Получить элементы меню в виде линейного списка - /// - public List LinearMenuItems { get; private set; } - /// /// Рекурсивно слить элементы меню /// - protected static void MergeMenuItems(List existingItems, List addedItems) + protected static void MergeMenuItems(List existingItems, List addedItems, int level) { if (addedItems != null) { @@ -85,6 +79,7 @@ protected static void MergeMenuItems(List existingItems, List= 0) @@ -94,13 +89,14 @@ protected static void MergeMenuItems(List existingItems, List 0 && addedItem.Subitems.Count > 0) { // рекурсивное добавление подпунктов меню - MergeMenuItems(existingItem.Subitems, addedItem.Subitems); + MergeMenuItems(existingItem.Subitems, addedItem.Subitems, level + 1); } else { // упрощённое добавление подпунктов меню addedItem.Subitems.Sort(); existingItem.Subitems.AddRange(addedItem.Subitems); + SetMenuItemLevels(addedItem.Subitems, level + 1); } } else @@ -108,23 +104,23 @@ protected static void MergeMenuItems(List existingItems, List - /// Рекурсивно заполнить линейный список элементов меню + /// Рекурсивно установить уровень вложенности элементов меню /// - protected static void FillLinearMenuItems(List linearItems, List addedItems, int level) + protected static void SetMenuItemLevels(List items, int level) { - if (addedItems != null) + if (items != null) { - foreach (MenuItem addedItem in addedItems) + foreach (MenuItem item in items) { - addedItem.Level = level; - linearItems.Add(addedItem); - FillLinearMenuItems(linearItems, addedItem.Subitems, level + 1); + item.Level = level; + SetMenuItemLevels(item.Subitems, level + 1); } } } @@ -140,16 +136,17 @@ public void Init(UserData userData) try { + // слияние меню плагинов MenuItems.Clear(); if (userData.PluginSpecs != null) { foreach (PluginSpec pluginSpec in userData.PluginSpecs) - MergeMenuItems(MenuItems, pluginSpec.GetMenuItems(userData)); + MergeMenuItems(MenuItems, pluginSpec.GetMenuItems(userData), 0); } - LinearMenuItems.Clear(); - FillLinearMenuItems(LinearMenuItems, MenuItems, 0); + // добавление пункта меню о системе + MenuItems.Add(MenuItem.FromStandardMenuItem(StandardMenuItems.About)); } catch (Exception ex) { diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs index 840b014e3..676233508 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs @@ -27,12 +27,13 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Web; namespace Scada.Web.Shell { /// /// View tree node - /// Узел дерева предсталений + /// Узел дерева представлений /// public class ViewNode : IWebTreeNode { @@ -66,8 +67,8 @@ public ViewNode(ViewSettings.ViewItem viewItem, ViewSpec viewSpec) } else { - Url = string.Format(ViewUrlTemplate, viewItem.ViewID); - IconUrl = viewSpec.IconUrl; + Url = VirtualPathUtility.ToAbsolute(string.Format(ViewUrlTemplate, viewItem.ViewID)); + IconUrl = VirtualPathUtility.ToAbsolute(viewSpec.IconUrl); } AlarmCnlNum = viewItem.AlarmCnlNum; @@ -108,12 +109,7 @@ public ViewNode(ViewSettings.ViewItem viewItem, ViewSpec viewSpec) public List ChildNodes { get; protected set; } /// - /// Получить атрибуты данных в виде пар "имя-значение" (IWebTreeNode) - /// - public SortedList DataAttrs { get; protected set; } - - /// - /// Получить дочерние узлы (IWebTreeNode) + /// Получить дочерние узлы /// public IList Children { @@ -123,6 +119,11 @@ public IList Children } } + /// + /// Получить атрибуты данных в виде пар "имя-значение" + /// + public SortedList DataAttrs { get; protected set; } + /// /// Инициализировать атрибуты данных diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master index 6a1d068c7..fc2467350 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master @@ -46,27 +46,10 @@
Views
- <%= GenerateMainMenuHtml() %> - - - - -
<%= GenerateExplorerHtml() %> -
Collapse menu diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs index 691493b30..b9f6dce88 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs @@ -35,8 +35,11 @@ namespace Scada.Web /// public partial class MasterMain : System.Web.UI.MasterPage { + // Дерево представлений + private const string FolderImageUrl = "~/images/treeview/folder.png"; + private const string DocumentImageUrl = "~/images/treeview/document.png"; private static readonly TreeViewRenderer treeViewRenderer = - new TreeViewRenderer(); // формирует дерево представлений + new TreeViewRenderer(); private AppData appData; // общие данные веб-приложения private UserData userData; // данные пользователя приложения @@ -58,31 +61,8 @@ private void SetMainMenuVisible() /// protected string GenerateMainMenuHtml() { - const string ItemTemplate = "
{1}{2}
"; - const string LinkTemplate = "{1}"; - const string Expander = "
"; - - StringBuilder sbHtml = new StringBuilder(); - string curUrl = Request.Url.AbsolutePath; - - foreach (MenuItem menuItem in userData.UserMenu.LinearMenuItems) - { - string text = Server.HtmlEncode(menuItem.Text); - string url = ResolveUrl(menuItem.Url); - bool topLevel = menuItem.Level <= 0; - bool selected = string.Equals(url, curUrl, StringComparison.OrdinalIgnoreCase); - bool containsSubitems = topLevel && menuItem.Subitems.Count > 0; - - string cssClass = (topLevel ? "menu-item" : "menu-subitem") + - (containsSubitems ? " with-expander" : "") + (selected ? " selected" : ""); - string linkOrText = containsSubitems || !string.IsNullOrEmpty(url) ? - string.Format(LinkTemplate, url, text) : text; - string expander = containsSubitems ? Expander : ""; - - sbHtml.AppendLine(string.Format(ItemTemplate, cssClass, linkOrText, expander)); - } - - return sbHtml.ToString(); + TreeViewRenderer.Options options = new TreeViewRenderer.Options() { ShowIcons = false }; + return treeViewRenderer.GenerateHtml(userData.UserMenu.MenuItems, Request.Url.AbsolutePath, options); } /// @@ -90,7 +70,13 @@ protected string GenerateMainMenuHtml() /// protected string GenerateExplorerHtml() { - return treeViewRenderer.GenerateHtml(userData.UserViews.ViewNodes, null); + TreeViewRenderer.Options options = new TreeViewRenderer.Options() + { + ShowIcons = true, + FolderImageUrl = ResolveUrl(FolderImageUrl), + DocumentImageUrl = ResolveUrl(DocumentImageUrl) + }; + return treeViewRenderer.GenerateHtml(userData.UserViews.ViewNodes, null, options); } @@ -101,26 +87,6 @@ protected void Page_Load(object sender, EventArgs e) userData.CheckLoggedOn(true); SetMainMenuVisible(); - - // тест - /*TreeNode node1 = new TreeNode("Test group"); - node1.NavigateUrl = "javascript:void(0);"; - tvMainExplorer.Nodes.Add(node1); - TreeNode node2 = new TreeNode("Test view 1", "1"); - node2.NavigateUrl = "javascript:alert(1);"; - node1.ChildNodes.Add(node2); - - node2 = new TreeNode("Test view 2", "2"); - node2.NavigateUrl = "javascript:alert(2);"; - node1.ChildNodes.Add(node2); - - for (int i = 3; i < 100; i++) - { - node2 = new TreeNode("Test view 012345 6789 - " + i, i.ToString()); - node2.ToolTip = node2.Text; - node2.NavigateUrl = string.Format("javascript:alert({0});", i); - tvMainExplorer.Nodes.Add(node2); - }*/ } protected void lbtnMainLogout_Click(object sender, EventArgs e) diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.designer.cs b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.designer.cs index 75ec62be9..d83338eb6 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.designer.cs +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.designer.cs @@ -39,15 +39,6 @@ public partial class MasterMain { /// protected global::System.Web.UI.WebControls.LinkButton lbtnMainLogout; - /// - /// tvMainExplorer control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TreeView tvMainExplorer; - /// /// cphMainContent control. /// diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css index 3a5ba6680..8150d1d94 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css @@ -1,7 +1,7 @@ /*Verdana, Geneva, Tahoma, sans-serif;*/ body { margin: 0; - padding: 30px 0 0 230px; + padding: 30px 0 0 250px; background-color: #f1f1f1; font-family: 'Open Sans', sans-serif; font-size: 12px; @@ -74,7 +74,7 @@ a:active { position: fixed; left: 0; top: 30px; - width: 230px; + width: 250px; min-height: 300px; background-color: #23282d; color: #eee; @@ -116,51 +116,37 @@ a:active { color: #00b9eb; cursor: pointer; } -/* Main Menu */ -#divMainMenu .menu-item, -#divMainMenu .menu-subitem { - padding: 5px 10px; - text-decoration: none; -} -#divMainMenu .menu-subitem { - padding-left: 20px; - color: #9ca1a6; - display: none; - /*block*/ - font-size: 13px; -} -#divMainMenu .menu-item:hover, -#divMainMenu .menu-subitem:hover { +/* Trees */ +#divMainLeftPane .tree-view .node:hover { background-color: #191e23; color: #00b9eb; - text-decoration: none; } -#divMainMenu .menu-item.selected, -#divMainMenu .menu-subitem.selected { +#divMainLeftPane .tree-view .node.selected { background-color: #0073aa; color: white; } -#divMainMenu .menu-item a, -#divMainMenu .menu-subitem a { - display: inline-block; - width: 100%; +#divMainLeftPane .tree-view .expander::before { + font-size: 10px; } -#divMainMenu .menu-item.with-expander a { - width: calc(100% - 20px); +/* Main Menu */ +#divMainMenu .node[data-level="1"], +#divMainMenu .node[data-level="2"], +#divMainMenu .node[data-level="3"], +#divMainMenu .node[data-level="4"], +#divMainMenu .node[data-level="5"] { + color: #9ca1a6; + font-size: 13px; } -#divMainMenu .expander { - width: 20px; - cursor: pointer; - float: right; - text-align: right; +#divMainMenu .tree-view .node { + padding: 5px 10px 5px 10px; } -#divMainMenu .expander::before { - content: "\e257"; - font-family: 'Glyphicons Halflings'; - font-size: 10px; +#divMainMenu .tree-view .expander.left { + display: none; + width: 0; } -#divMainMenu .expander.expanded::before { - content: "\e259"; +#divMainMenu .tree-view .expander.right { + display: table-cell; + width: 20px; } /* Explorer */ #divMainExplorer { @@ -168,33 +154,20 @@ a:active { line-height: 13px; } #divMainExplorer .tree-view .node { - padding-left: 10px; -} -#divMainExplorer .tree-view .expander::before { - font-size: 10px; + padding: 7px 5px 3px 10px; } #divMainExplorer .tree-view .icon { width: 21px; - height: 16px; - line-height: 0; - background-size: 16px 16px; - background-repeat: no-repeat; - background-position: left bottom; -} -#divMainExplorer .tree-view .icon.folder { - background-image: url(../images/treeview/folder.png); } -#divMainExplorer .tree-view .icon.empty { - background-image: url(../images/treeview/table.png); +#divMainExplorer .tree-view img { + position: relative; + top: -2px; } -/*#divMainExplorer .tree-view .node div { - border: 1px solid red; -}*/ /* Collapse Pane */ #divMainCollapsePane { position: absolute; bottom: 0; - width: 200px; + width: 220px; height: 40px; margin: 0 0 0 30px; padding: 5px 10px; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less index a98448e7a..71a99151c 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less @@ -4,7 +4,7 @@ @header-heigth: 30px; @left-tabs-heigth: 30px; @collapse-pane-heigth: 40px; -@left-pane-width: 230px; +@left-pane-width: 250px; body { margin: 0; @@ -140,59 +140,45 @@ a:active { cursor: pointer; } -/* Main Menu */ - -#divMainMenu .menu-item, -#divMainMenu .menu-subitem { - padding: 5px 10px; - text-decoration: none; -} - -#divMainMenu .menu-subitem { - padding-left: 20px; - color: @menu-fore-color-dark; - display: none; /*block*/ - font-size: 13px; -} +/* Trees */ -#divMainMenu .menu-item:hover, -#divMainMenu .menu-subitem:hover { +#divMainLeftPane .tree-view .node:hover { background-color: @menu-item-hover-back-color-dark; color: @menu-item-hover-fore-color; - text-decoration: none; } -#divMainMenu .menu-item.selected, -#divMainMenu .menu-subitem.selected { +#divMainLeftPane .tree-view .node.selected { background-color: @menu-item-selected-back-color; color: @menu-item-selected-fore-color; } -#divMainMenu .menu-item a, -#divMainMenu .menu-subitem a { - display: inline-block; - width: 100%; +#divMainLeftPane .tree-view .expander::before { + font-size: 10px; } -#divMainMenu .menu-item.with-expander a { - width: calc(~"100% - 20px"); +/* Main Menu */ + +#divMainMenu .node[data-level="1"], +#divMainMenu .node[data-level="2"], +#divMainMenu .node[data-level="3"], +#divMainMenu .node[data-level="4"], +#divMainMenu .node[data-level="5"] { + color: @menu-fore-color-dark; + font-size: 13px; } -#divMainMenu .expander { - width: 20px; - cursor: pointer; - float: right; - text-align: right; +#divMainMenu .tree-view .node { + padding: 5px 10px 5px 10px; } -#divMainMenu .expander::before { - content: "\e257"; - font-family: 'Glyphicons Halflings'; - font-size: 10px; +#divMainMenu .tree-view .expander.left { + display: none; + width: 0; } -#divMainMenu .expander.expanded::before { - content: "\e259"; +#divMainMenu .tree-view .expander.right { + display: table-cell; + width: 20px; } /* Explorer */ @@ -203,37 +189,18 @@ a:active { } #divMainExplorer .tree-view .node { - padding-left: 10px; -} - -#divMainExplorer .tree-view .expander::before { - font-size: 10px; + padding: 7px 5px 3px 10px; } #divMainExplorer .tree-view .icon { width: 21px; - height: 16px; - line-height: 0; - background-size: 16px 16px; - background-repeat: no-repeat; - background-position: left bottom; -} - -#divMainExplorer .tree-view .icon.folder { - background-image: url(../images/treeview/folder.png); } -#divMainExplorer .tree-view .icon.empty { - background-image: url(../images/treeview/table.png); -} - -#divMainExplorer .tree-view .text { +#divMainExplorer .tree-view img { + position: relative; + top: -2px; } -/*#divMainExplorer .tree-view .node div { - border: 1px solid red; -}*/ - /* Collapse Pane */ #divMainCollapsePane { diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css index 781735f6a..5d69202dd 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 230px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;white-space:nowrap;}#divMainHeader .hdr-btn{display:inline-block;height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;font-size:13px;text-decoration:none;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenu{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;}#divMainUserMenu{float:right;height:30px;}#divMainLeftPane{position:fixed;left:0;top:30px;width:230px;min-height:300px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 40px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainMenu .menu-item,#divMainMenu .menu-subitem{padding:5px 10px;text-decoration:none;}#divMainMenu .menu-subitem{padding-left:20px;color:#9ca1a6;display:none;font-size:13px;}#divMainMenu .menu-item:hover,#divMainMenu .menu-subitem:hover{background-color:#191e23;color:#00b9eb;text-decoration:none;}#divMainMenu .menu-item.selected,#divMainMenu .menu-subitem.selected{background-color:#0073aa;color:#fff;}#divMainMenu .menu-item a,#divMainMenu .menu-subitem a{display:inline-block;width:100%;}#divMainMenu .menu-item.with-expander a{width:calc(100% - 20px);}#divMainMenu .expander{width:20px;cursor:pointer;float:right;text-align:right;}#divMainMenu .expander::before{content:"";font-family:'Glyphicons Halflings';font-size:10px;}#divMainMenu .expander.expanded::before{content:"";}#divMainExplorer{font-size:13px;line-height:13px;}#divMainExplorer .tree-view .node{padding-left:10px;}#divMainExplorer .tree-view .expander::before{font-size:10px;}#divMainExplorer .tree-view .icon{width:21px;height:16px;line-height:0;background-size:16px 16px;background-repeat:no-repeat;background-position:left bottom;}#divMainExplorer .tree-view .icon.folder{background-image:url(../images/treeview/folder.png);}#divMainExplorer .tree-view .icon.empty{background-image:url(../images/treeview/table.png);}#divMainCollapsePane{position:absolute;bottom:0;width:200px;height:40px;margin:0 0 0 30px;padding:5px 10px;color:#9ca1a6;font-size:13px;}#divMainCollapsePane i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapsePane:hover{color:#00b9eb;cursor:pointer;} \ No newline at end of file +body{margin:0;padding:30px 0 0 250px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;white-space:nowrap;}#divMainHeader .hdr-btn{display:inline-block;height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;font-size:13px;text-decoration:none;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenu{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;}#divMainUserMenu{float:right;height:30px;}#divMainLeftPane{position:fixed;left:0;top:30px;width:250px;min-height:300px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 40px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainLeftPane .tree-view .node:hover{background-color:#191e23;color:#00b9eb;}#divMainLeftPane .tree-view .node.selected{background-color:#0073aa;color:#fff;}#divMainLeftPane .tree-view .expander::before{font-size:10px;}#divMainMenu .node[data-level="1"],#divMainMenu .node[data-level="2"],#divMainMenu .node[data-level="3"],#divMainMenu .node[data-level="4"],#divMainMenu .node[data-level="5"]{color:#9ca1a6;font-size:13px;}#divMainMenu .tree-view .node{padding:5px 10px 5px 10px;}#divMainMenu .tree-view .expander.left{display:none;width:0;}#divMainMenu .tree-view .expander.right{display:table-cell;width:20px;}#divMainExplorer{font-size:13px;line-height:13px;}#divMainExplorer .tree-view .node{padding:7px 5px 3px 10px;}#divMainExplorer .tree-view .icon{width:21px;}#divMainExplorer .tree-view img{position:relative;top:-2px;}#divMainCollapsePane{position:absolute;bottom:0;width:220px;height:40px;margin:0 0 0 30px;padding:5px 10px;color:#9ca1a6;font-size:13px;}#divMainCollapsePane i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapsePane:hover{color:#00b9eb;cursor:pointer;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/treeview.css b/ScadaWeb/ScadaWebShell5Beta/css/treeview.css index c407573ef..1faa6a0de 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/treeview.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/treeview.css @@ -1,19 +1,38 @@ -.tree-view .child-nodes { +.tree-view .node { + cursor: pointer; + display: table; + width: 100%; +} +.tree-view .node-items { + display: table-row; + width: 200px; +} +.tree-view .node-items > div { + display: table-cell; +} +.tree-view .child-nodes { display: none; /*block*/ } -.tree-view .node div { - display: inline-block; -} .tree-view .indent { width: 20px; } .tree-view .expander { width: 20px; - cursor: pointer; +} +.tree-view .expander.right { + display: none; + text-align: right; + width: 0; } .tree-view .expander::before { + font-family: 'Glyphicons Halflings'; +} +.tree-view .expander.left::before { content: "\e258"; +} +.tree-view .expander.right::before { + content: "\e257"; font-family: 'Glyphicons Halflings'; } .tree-view .expander.expanded::before { diff --git a/ScadaWeb/ScadaWebShell5Beta/css/treeview.less b/ScadaWeb/ScadaWebShell5Beta/css/treeview.less index 75b8ad6f4..7c80d49b0 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/treeview.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/treeview.less @@ -1,9 +1,20 @@ -.tree-view .child-nodes { - display: none; /*block*/ +.tree-view .node { + cursor: pointer; + display: table; + width: 100%; +} + +.tree-view .node-items { + display: table-row; + width: 200px; } -.tree-view .node div { - display: inline-block; +.tree-view .node-items > div { + display: table-cell; +} + +.tree-view .child-nodes { + display: none; /*block*/ } .tree-view .indent { @@ -12,11 +23,24 @@ .tree-view .expander { width: 20px; - cursor: pointer; +} + +.tree-view .expander.right { + display: none; + text-align: right; + width: 0; } .tree-view .expander::before { + font-family: 'Glyphicons Halflings'; +} + +.tree-view .expander.left::before { content: "\e258"; +} + +.tree-view .expander.right::before { + content: "\e257"; font-family: 'Glyphicons Halflings'; } diff --git a/ScadaWeb/ScadaWebShell5Beta/css/treeview.min.css b/ScadaWeb/ScadaWebShell5Beta/css/treeview.min.css index 20314abcb..b9cdee224 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/treeview.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/treeview.min.css @@ -1 +1 @@ -.tree-view .child-nodes{display:none;}.tree-view .node div{display:inline-block;}.tree-view .indent{width:20px;}.tree-view .expander{width:20px;cursor:pointer;}.tree-view .expander::before{content:"";font-family:'Glyphicons Halflings';}.tree-view .expander.expanded::before{content:"";}.tree-view .expander.empty{visibility:hidden;}.tree-view .stateIcon,.tree-view .icon{width:0;}.tree-view .icon img{width:16px;height:16px;border:none;} \ No newline at end of file +.tree-view .node{cursor:pointer;display:table;width:100%;}.tree-view .node-items{display:table-row;width:200px;}.tree-view .node-items>div{display:table-cell;}.tree-view .child-nodes{display:none;}.tree-view .indent{width:20px;}.tree-view .expander{width:20px;}.tree-view .expander.right{display:none;text-align:right;width:0;}.tree-view .expander::before{font-family:'Glyphicons Halflings';}.tree-view .expander.left::before{content:"";}.tree-view .expander.right::before{content:"";font-family:'Glyphicons Halflings';}.tree-view .expander.expanded::before{content:"";}.tree-view .expander.empty{visibility:hidden;}.tree-view .stateIcon,.tree-view .icon{width:0;}.tree-view .icon img{width:16px;height:16px;border:none;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js index 73b71dff1..af5a6fea2 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js @@ -46,39 +46,9 @@ function activateToolWindow(divClickedTab) { } } -// Expand or collapse the menu item -function toggleMenuItem(divExpander) { - var menuItem = divExpander.parent(); - var menuSubitems = menuItem.nextUntil(".menu-item", ".menu-subitem"); - - if (divExpander.hasClass("expanded")) { - divExpander.removeClass("expanded"); - menuSubitems.css("display", "none"); - } else { - divExpander.addClass("expanded"); - menuSubitems.css("display", "block"); - } -} - -// Expand selected menu item -function expandSelectedMenuItem() { - var divSelMenuItem = $("#divMainMenu .menu-item.selected"); // top level item - - if (divSelMenuItem.length == 0) { - var divSelMenuSubitem = $("#divMainMenu .menu-subitem.selected"); - divSelMenuItem = divSelMenuSubitem.prevAll(".menu-item:first"); - } - - var divExpander = divSelMenuItem.find(".expander"); - if (!divExpander.hasClass("expanded")) { - toggleMenuItem(divExpander); - } -} - $(document).ready(function () { updateMainLayout(); chooseToolWindow(); - expandSelectedMenuItem(); scada.treeView.prepare(); // update layout on window resize @@ -90,16 +60,4 @@ $(document).ready(function () { $("#divMainTabs .tab").click(function (event) { activateToolWindow($(this)); }); - - // toggle main menu items on click - $("#divMainMenu .expander").click(function() { - toggleMenuItem($(this)); - }); - - $("#divMainMenu .menu-item.with-expander a").click(function (event) { - if (!$(this).attr("href")) { - event.preventDefault(); - toggleMenuItem($(this).siblings(".expander")); - } - }); }); \ No newline at end of file From 1244a0a1c06dcdeb1c4337cde8874e34477846e0 Mon Sep 17 00:00:00 2001 From: 2mik Date: Thu, 5 May 2016 12:57:12 +0300 Subject: [PATCH 021/382] ScadaWeb5: tree update --- ScadaWeb/ScadaWebShell5Beta/js/treeview.js | 28 ++++++++++++---------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/ScadaWeb/ScadaWebShell5Beta/js/treeview.js b/ScadaWeb/ScadaWebShell5Beta/js/treeview.js index d2a822189..28f98ff5c 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/treeview.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/treeview.js @@ -17,7 +17,7 @@ var scada = scada || {}; scada.treeView = { // Expand or collapse tree node _toggleTreeNode: function (divExpander) { - var treeNode = divExpander.parent(); + var treeNode = divExpander.parent().parent(); var childNodes = treeNode.next(".child-nodes"); if (divExpander.hasClass("expanded")) { @@ -34,36 +34,40 @@ scada.treeView = { var selNodes = allNodes.filter(".selected"); var parDivs = selNodes.parentsUntil(".tree-view", ".child-nodes"); - parDivs.prev(".node").children(".expander").addClass("expanded"); + parDivs.prev(".node").find(".expander").addClass("expanded"); parDivs.css("display", "block"); }, // Tune tree view elements and bind events prepare: function () { var treeViews = $(".tree-view"); - var indentWidth = treeViews.find(".indent:first").width(); + var oneIndent = treeViews.find(".indent:first").width(); var allNodes = treeViews.find(".node"); // set width of indents according to their level allNodes.each(function () { var level = $(this).attr("data-level"); - $(this).children(".indent").width(indentWidth * level); + $(this).find(".indent").width(oneIndent * level); }); // expand selected tree node this._expandSelectedTreeNode(allNodes); - // toggle tree node on click + // go to link or toggle tree node on click var thisTreeView = this; - allNodes.children(".expander").click(function () { - thisTreeView._toggleTreeNode($(this)); - }); + allNodes.click(function () { + var link = $(this).find("a"); - allNodes.find("a").click(function (event) { - if (!$(this).attr("href")) { - event.preventDefault(); - thisTreeView._toggleTreeNode($(this).parent().siblings(".expander")); + if (link.length > 0) { + location = link.attr("href"); + } else { + var expander = $(this).find(".expander"); + if (!expander.hasClass("empty")) { + thisTreeView._toggleTreeNode(expander); + } } + + $(this).find("a").click(); }); } }; \ No newline at end of file From b195ebe13172e6b0348cb68ee971f40dc17aea2f Mon Sep 17 00:00:00 2001 From: 2mik Date: Thu, 5 May 2016 12:57:57 +0300 Subject: [PATCH 022/382] ScadaWeb5: update --- ScadaWeb/ScadaWebShell5Beta/css/mastermain.css | 2 +- ScadaWeb/ScadaWebShell5Beta/css/mastermain.less | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css index 8150d1d94..a106932a1 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css @@ -116,7 +116,7 @@ a:active { color: #00b9eb; cursor: pointer; } -/* Trees */ +/* Tree views */ #divMainLeftPane .tree-view .node:hover { background-color: #191e23; color: #00b9eb; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less index 71a99151c..a61cb5bf9 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less @@ -140,7 +140,7 @@ a:active { cursor: pointer; } -/* Trees */ +/* Tree views */ #divMainLeftPane .tree-view .node:hover { background-color: @menu-item-hover-back-color-dark; From 948f8e12e5b3f64697b212a1b24769f5e563b0a7 Mon Sep 17 00:00:00 2001 From: 2mik Date: Thu, 5 May 2016 19:18:21 +0300 Subject: [PATCH 023/382] ScadaWeb5: full screen mode --- ScadaWeb/ScadaWebShell5Beta/MasterMain.Master | 10 +- .../ScadaWebShell5Beta/css/mastermain.css | 21 ++- .../ScadaWebShell5Beta/css/mastermain.less | 22 +++- .../ScadaWebShell5Beta/css/mastermain.min.css | 2 +- ScadaWeb/ScadaWebShell5Beta/js/mastermain.js | 124 +++++++++++++++--- ScadaWeb/ScadaWebShell5Beta/js/scadautils.js | 42 ++++++ 6 files changed, 201 insertions(+), 20 deletions(-) diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master index fc2467350..8f2e4ad46 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master @@ -17,9 +17,14 @@ + @@ -40,6 +45,9 @@ class="glyphicon glyphicon-fullscreen" aria-hidden="true">
+
+ +
Main Menu
diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css index a106932a1..8bbb00979 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css @@ -31,11 +31,11 @@ a:active { white-space: nowrap; } #divMainHeader .hdr-btn { - display: inline-block; height: 30px; margin: 0 0 0 5px; padding: 0 10px; cursor: pointer; + display: inline-block; font-size: 13px; text-decoration: none; } @@ -69,6 +69,25 @@ a:active { float: right; height: 30px; } +#divMainNormalView { + position: fixed; + top: 0; + right: 0; + height: 30px; + padding: 0 10px; + background-color: white; + display: none; + /*inline-block*/ + line-height: 30px; + opacity: 0.5; + cursor: pointer; + color: #9ca1a6; + font-size: 13px; +} +#divMainNormalView:hover { + color: #00b9eb; + opacity: 1; +} /* Left Pane */ #divMainLeftPane { position: fixed; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less index a61cb5bf9..eb2a767e1 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less @@ -42,11 +42,11 @@ a:active { } #divMainHeader .hdr-btn { - display: inline-block; height: @header-heigth; margin: 0 0 0 5px; padding: 0 10px; cursor: pointer; + display: inline-block; font-size: 13px; text-decoration: none; } @@ -87,6 +87,26 @@ a:active { height: @header-heigth; } +#divMainNormalView { + position: fixed; + top: 0; + right: 0; + height: @header-heigth; + padding: 0 10px; + background-color: white; + display: none; /*inline-block*/ + line-height: @header-heigth; + opacity: 0.5; + cursor: pointer; + color: @menu-fore-color-dark; + font-size: 13px; +} + +#divMainNormalView:hover { + color: @menu-item-hover-fore-color; + opacity: 1; +} + /* Left Pane */ #divMainLeftPane { diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css index 5d69202dd..7b7f4db58 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 250px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;white-space:nowrap;}#divMainHeader .hdr-btn{display:inline-block;height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;font-size:13px;text-decoration:none;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenu{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;}#divMainUserMenu{float:right;height:30px;}#divMainLeftPane{position:fixed;left:0;top:30px;width:250px;min-height:300px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 40px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainLeftPane .tree-view .node:hover{background-color:#191e23;color:#00b9eb;}#divMainLeftPane .tree-view .node.selected{background-color:#0073aa;color:#fff;}#divMainLeftPane .tree-view .expander::before{font-size:10px;}#divMainMenu .node[data-level="1"],#divMainMenu .node[data-level="2"],#divMainMenu .node[data-level="3"],#divMainMenu .node[data-level="4"],#divMainMenu .node[data-level="5"]{color:#9ca1a6;font-size:13px;}#divMainMenu .tree-view .node{padding:5px 10px 5px 10px;}#divMainMenu .tree-view .expander.left{display:none;width:0;}#divMainMenu .tree-view .expander.right{display:table-cell;width:20px;}#divMainExplorer{font-size:13px;line-height:13px;}#divMainExplorer .tree-view .node{padding:7px 5px 3px 10px;}#divMainExplorer .tree-view .icon{width:21px;}#divMainExplorer .tree-view img{position:relative;top:-2px;}#divMainCollapsePane{position:absolute;bottom:0;width:220px;height:40px;margin:0 0 0 30px;padding:5px 10px;color:#9ca1a6;font-size:13px;}#divMainCollapsePane i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapsePane:hover{color:#00b9eb;cursor:pointer;} \ No newline at end of file +body{margin:0;padding:30px 0 0 250px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;white-space:nowrap;}#divMainHeader .hdr-btn{height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;display:inline-block;font-size:13px;text-decoration:none;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenu{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;}#divMainUserMenu{float:right;height:30px;}#divMainNormalView{position:fixed;top:0;right:0;height:30px;padding:0 10px;background-color:#fff;display:none;line-height:30px;opacity:.5;cursor:pointer;color:#9ca1a6;font-size:13px;}#divMainNormalView:hover{color:#00b9eb;opacity:1;}#divMainLeftPane{position:fixed;left:0;top:30px;width:250px;min-height:300px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 40px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainLeftPane .tree-view .node:hover{background-color:#191e23;color:#00b9eb;}#divMainLeftPane .tree-view .node.selected{background-color:#0073aa;color:#fff;}#divMainLeftPane .tree-view .expander::before{font-size:10px;}#divMainMenu .node[data-level="1"],#divMainMenu .node[data-level="2"],#divMainMenu .node[data-level="3"],#divMainMenu .node[data-level="4"],#divMainMenu .node[data-level="5"]{color:#9ca1a6;font-size:13px;}#divMainMenu .tree-view .node{padding:5px 10px 5px 10px;}#divMainMenu .tree-view .expander.left{display:none;width:0;}#divMainMenu .tree-view .expander.right{display:table-cell;width:20px;}#divMainExplorer{font-size:13px;line-height:13px;}#divMainExplorer .tree-view .node{padding:7px 5px 3px 10px;}#divMainExplorer .tree-view .icon{width:21px;}#divMainExplorer .tree-view img{position:relative;top:-2px;}#divMainCollapsePane{position:absolute;bottom:0;width:220px;height:40px;margin:0 0 0 30px;padding:5px 10px;color:#9ca1a6;font-size:13px;}#divMainCollapsePane i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapsePane:hover{color:#00b9eb;cursor:pointer;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js index af5a6fea2..6ad95a046 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js @@ -1,5 +1,12 @@ -// Update layout of the master page -function updateMainLayout() { +var scada = scada || {}; +scada.masterMain = scada.masterMain || {}; +scada.masterMain.params = scada.masterMain.params || {}; + +// Left pane that displays main menu or views explorer is expanded +scada.masterMain.leftPaneExpanded = true; + +// Update layout of the master page +scada.masterMain.updateLayout = function () { var divMainHeader = $("#divMainHeader"); var divMainLeftPane = $("#divMainLeftPane"); var divMainTabs = $("#divMainTabs"); @@ -7,19 +14,19 @@ function updateMainLayout() { var paneH = $(window).height() - divMainHeader.outerHeight(); divMainLeftPane.outerHeight(paneH); divMainTabs.outerWidth(paneH); -} +}; // Choose a tool window and activate it -function chooseToolWindow() { - if (mainMenuVisible) { - activateToolWindow($("#divMainMenuTab")); +scada.masterMain.chooseToolWindow = function () { + if (this.params.mainMenuVisible) { + this.activateToolWindow($("#divMainMenuTab")); } else { - activateToolWindow($("#divMainExplorerTab")); + this.activateToolWindow($("#divMainExplorerTab")); } -} +}; // Make visible main menu or views explorer -function activateToolWindow(divClickedTab) { +scada.masterMain.activateToolWindow = function (divClickedTab) { // highlight clicked tab var tabs = $("#divMainTabs .tab"); tabs.removeClass("selected"); @@ -44,20 +51,105 @@ function activateToolWindow(divClickedTab) { if (toolWindow) { toolWindow.css("display", "block"); } -} +}; + +// Hide main menu and views explorer +scada.masterMain.hideLeftPane = function () { + $("#divMainLeftPane").css("display", "none"); + $("body").css("padding-left", "0"); +}; + +// Show main menu and views explorer +scada.masterMain.showLeftPane = function () { + $("body").css("padding-left", ""); + $("#divMainLeftPane").css("display", ""); +}; + +// Collapse main menu and views explorer +scada.masterMain.collapseLeftPane = function () { + $("#spanMainShowMenu").css("display", "inline-block"); + this.hideLeftPane(); + leftPaneExpanded = false; +}; + +// Expand main menu and views explorer +scada.masterMain.expandLeftPane = function () { + this.showLeftPane(); + $("#spanMainShowMenu").css("display", "none"); + leftPaneExpanded = true; +}; + +// Hide page header +scada.masterMain.hideHeader = function () { + $("#divMainHeader").css("display", "none"); + $("body").css("padding-top", "0"); +}; + +// Show page header +scada.masterMain.showHeader = function () { + $("#divMainHeader").css("display", ""); + $("body").css("padding-top", ""); +}; + +// Hide all the menus and switch browser to fullscreen mode +scada.masterMain.switchToFullscreen = function () { + if (leftPaneExpanded) { + this.hideLeftPane(); + } + this.hideHeader(); + $("#divMainNormalView").css("display", "inline-block"); + scada.utils.requestFullscreen(); +}; + +// Show all the menus and exit browser fullscreen mode +scada.masterMain.switchToNormalView = function () { + $("#divMainNormalView").css("display", "none"); + if (leftPaneExpanded) { + this.showLeftPane(); + } + this.showHeader(); + scada.utils.exitFullscreen(); +}; $(document).ready(function () { - updateMainLayout(); - chooseToolWindow(); + scada.masterMain.updateLayout(); + scada.masterMain.chooseToolWindow(); scada.treeView.prepare(); // update layout on window resize $(window).resize(function () { - updateMainLayout(); + scada.masterMain.updateLayout(); + }); + + // activate tool window if tab is clicked + $("#divMainTabs .tab").click(function () { + scada.masterMain.activateToolWindow($(this)); + }); + + // collapse left pane on button click + $("#divMainCollapsePane").click(function () { + scada.masterMain.collapseLeftPane(); + }); + + // expand left pane on button click + $("#spanMainShowMenu").click(function () { + scada.masterMain.expandLeftPane(); + }); + + // switch to full screen mode on button click + $("#spanMainFullScreen").click(function () { + scada.masterMain.switchToFullscreen(); + }); + + // switch to normal view mode on button click + $("#divMainNormalView").click(function () { + scada.masterMain.switchToNormalView(); }); - // activate tool window on tab click - $("#divMainTabs .tab").click(function (event) { - activateToolWindow($(this)); + // show menus if user exits fullscreen mode + $(document).on("webkitfullscreenchange mozfullscreenchange fullscreenchange MSFullscreenChange", function () { + if (!scada.utils.isFullScreen) { + scada.masterMain.switchToNormalView(); + } }); }); \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/js/scadautils.js b/ScadaWeb/ScadaWebShell5Beta/js/scadautils.js index 5ec8d4efe..db4201d3d 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/scadautils.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/scadautils.js @@ -32,5 +32,47 @@ scada.utils = { logFailedRequest: function (operation, jqXHR) { console.error(this.getCurTime() + " Request '" + operation + "' failed: " + jqXHR.status + " (" + jqXHR.statusText + ")"); + }, + + // Check if browser is in fullscreen mode + // See https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API + get isFullScreen() { + return document.fullscreenElement || document.mozFullScreenElement || + document.webkitFullscreenElement || document.msFullscreenElement; + }, + + // Switch browser to fullscreen mode + requestFullscreen: function () { + if (document.documentElement.requestFullscreen) { + document.documentElement.requestFullscreen(); + } else if (document.documentElement.msRequestFullscreen) { + document.documentElement.msRequestFullscreen(); + } else if (document.documentElement.mozRequestFullScreen) { + document.documentElement.mozRequestFullScreen(); + } else if (document.documentElement.webkitRequestFullscreen) { + document.documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); + } + }, + + // Exit browser fullscreen mode + exitFullscreen: function () { + if (document.exitFullscreen) { + document.exitFullscreen(); + } else if (document.msExitFullscreen) { + document.msExitFullscreen(); + } else if (document.mozCancelFullScreen) { + document.mozCancelFullScreen(); + } else if (document.webkitExitFullscreen) { + document.webkitExitFullscreen(); + } + }, + + // Switch browser to full screen mode and back to normal view + toggleFullscreen: function () { + if (this.isFullScreen) { + this.exitFullscreen(); + } else { + this.requestFullscreen(); + } } }; From f176f6c99b4c3e860493f88a9d417c50730b528d Mon Sep 17 00:00:00 2001 From: 2mik Date: Thu, 5 May 2016 23:38:23 +0300 Subject: [PATCH 024/382] ScadaWeb5: develop --- ScadaWeb/ScadaWebShell5Beta/About.aspx | 6 ++++++ ScadaWeb/ScadaWebShell5Beta/About.aspx.cs | 17 +++++++++++++++++ .../ScadaWebShell5Beta/About.aspx.designer.cs | 15 +++++++++++++++ .../ScadaWebShell5Beta/MasterMain.Master.cs | 4 ++-- .../ScadaWebShell5Beta.csproj | 8 ++++++++ ScadaWeb/ScadaWebShell5Beta/js/mastermain.js | 8 ++++---- 6 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 ScadaWeb/ScadaWebShell5Beta/About.aspx create mode 100644 ScadaWeb/ScadaWebShell5Beta/About.aspx.cs create mode 100644 ScadaWeb/ScadaWebShell5Beta/About.aspx.designer.cs diff --git a/ScadaWeb/ScadaWebShell5Beta/About.aspx b/ScadaWeb/ScadaWebShell5Beta/About.aspx new file mode 100644 index 000000000..c99c9d297 --- /dev/null +++ b/ScadaWeb/ScadaWebShell5Beta/About.aspx @@ -0,0 +1,6 @@ +<%@ Page Title="" Language="C#" MasterPageFile="~/MasterMain.Master" AutoEventWireup="true" CodeBehind="About.aspx.cs" Inherits="Scada.Web.WFrmAbout" %> + + + + About... + diff --git a/ScadaWeb/ScadaWebShell5Beta/About.aspx.cs b/ScadaWeb/ScadaWebShell5Beta/About.aspx.cs new file mode 100644 index 000000000..c62bcf28d --- /dev/null +++ b/ScadaWeb/ScadaWebShell5Beta/About.aspx.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.UI; +using System.Web.UI.WebControls; + +namespace Scada.Web +{ + public partial class WFrmAbout : System.Web.UI.Page + { + protected void Page_Load(object sender, EventArgs e) + { + + } + } +} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/About.aspx.designer.cs b/ScadaWeb/ScadaWebShell5Beta/About.aspx.designer.cs new file mode 100644 index 000000000..dbf41f848 --- /dev/null +++ b/ScadaWeb/ScadaWebShell5Beta/About.aspx.designer.cs @@ -0,0 +1,15 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Scada.Web { + + + public partial class WFrmAbout { + } +} diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs index b9f6dce88..9ded6021e 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs @@ -52,8 +52,8 @@ public partial class MasterMain : System.Web.UI.MasterPage /// private void SetMainMenuVisible() { - mainMenuVisible = !string.Equals(Request.Url.GetLeftPart(UriPartial.Path), - ResolveUrl("~/View.aspx"), StringComparison.OrdinalIgnoreCase); + mainMenuVisible = !string.Equals(Request.Url.AbsolutePath, ResolveUrl("~/View.aspx"), + StringComparison.OrdinalIgnoreCase); } /// diff --git a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj index 51b63f8ac..679f3c0ae 100644 --- a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj +++ b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj @@ -66,6 +66,7 @@ lang\ScadaData.en-GB.xml + @@ -138,6 +139,13 @@ + + About.aspx + ASPXCodeBehind + + + About.aspx + Global.asax diff --git a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js index 6ad95a046..91189006c 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js @@ -69,14 +69,14 @@ scada.masterMain.showLeftPane = function () { scada.masterMain.collapseLeftPane = function () { $("#spanMainShowMenu").css("display", "inline-block"); this.hideLeftPane(); - leftPaneExpanded = false; + this.leftPaneExpanded = false; }; // Expand main menu and views explorer scada.masterMain.expandLeftPane = function () { this.showLeftPane(); $("#spanMainShowMenu").css("display", "none"); - leftPaneExpanded = true; + this.leftPaneExpanded = true; }; // Hide page header @@ -93,7 +93,7 @@ scada.masterMain.showHeader = function () { // Hide all the menus and switch browser to fullscreen mode scada.masterMain.switchToFullscreen = function () { - if (leftPaneExpanded) { + if (this.leftPaneExpanded) { this.hideLeftPane(); } this.hideHeader(); @@ -104,7 +104,7 @@ scada.masterMain.switchToFullscreen = function () { // Show all the menus and exit browser fullscreen mode scada.masterMain.switchToNormalView = function () { $("#divMainNormalView").css("display", "none"); - if (leftPaneExpanded) { + if (this.leftPaneExpanded) { this.showLeftPane(); } this.showHeader(); From 00089d1f850c406a00fe57496ab34afbc6b6ebb7 Mon Sep 17 00:00:00 2001 From: 2mik Date: Fri, 6 May 2016 12:43:19 +0300 Subject: [PATCH 025/382] ScadaWeb5: master page is done --- ScadaData/ScadaData/UI/Translator.cs | 16 ++ .../ScadaWebCommon5Beta.csproj | 1 + .../ScadaWebCommon5Beta/Shell/UrlTemplates.cs | 39 +++ ScadaWeb/ScadaWebShell5Beta/MasterMain.Master | 23 +- .../ScadaWebShell5Beta/MasterMain.Master.cs | 27 +- .../MasterMain.Master.designer.cs | 45 ++++ .../ScadaWebShell5Beta/css/mastermain.css | 6 +- .../ScadaWebShell5Beta/css/mastermain.less | 6 +- .../ScadaWebShell5Beta/css/mastermain.min.css | 2 +- ScadaWeb/ScadaWebShell5Beta/js/mastermain.js | 232 +++++++++--------- ScadaWeb/ScadaWebShell5Beta/js/scadautils.js | 4 +- .../lang/ScadaWeb.en-GB.xml | 8 + .../lang/ScadaWeb.ru-RU.xml | 10 +- .../storage/admin/App/RememberMe.xml | 5 - 14 files changed, 266 insertions(+), 158 deletions(-) create mode 100644 ScadaWeb/ScadaWebCommon5Beta/Shell/UrlTemplates.cs diff --git a/ScadaData/ScadaData/UI/Translator.cs b/ScadaData/ScadaData/UI/Translator.cs index 795ce5833..4f339907c 100644 --- a/ScadaData/ScadaData/UI/Translator.cs +++ b/ScadaData/ScadaData/UI/Translator.cs @@ -272,24 +272,40 @@ private static void TranslateWebControls(ControlCollection controls, Label label = (Label)control; if (controlInfo.Text != null) label.Text = controlInfo.Text; + if (controlInfo.ToolTip != null) + label.ToolTip = controlInfo.ToolTip; } else if (control is TextBox) { TextBox textBox = (TextBox)control; if (controlInfo.Text != null) textBox.Text = controlInfo.Text; + if (controlInfo.ToolTip != null) + textBox.ToolTip = controlInfo.ToolTip; } else if (control is CheckBox) { CheckBox checkBox = (CheckBox)control; if (controlInfo.Text != null) checkBox.Text = controlInfo.Text; + if (controlInfo.ToolTip != null) + checkBox.ToolTip = controlInfo.ToolTip; } else if (control is Button) { Button button = (Button)control; if (controlInfo.Text != null) button.Text = controlInfo.Text; + if (controlInfo.ToolTip != null) + button.ToolTip = controlInfo.ToolTip; + } + else if (control is LinkButton) + { + LinkButton linkButton = (LinkButton)control; + if (controlInfo.Text != null) + linkButton.Text = controlInfo.Text; + if (controlInfo.ToolTip != null) + linkButton.ToolTip = controlInfo.ToolTip; } else if (control is Image) { diff --git a/ScadaWeb/ScadaWebCommon5Beta/ScadaWebCommon5Beta.csproj b/ScadaWeb/ScadaWebCommon5Beta/ScadaWebCommon5Beta.csproj index c46efb928..5b186a6c4 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/ScadaWebCommon5Beta.csproj +++ b/ScadaWeb/ScadaWebCommon5Beta/ScadaWebCommon5Beta.csproj @@ -60,6 +60,7 @@ + diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/UrlTemplates.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/UrlTemplates.cs new file mode 100644 index 000000000..e9547854f --- /dev/null +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/UrlTemplates.cs @@ -0,0 +1,39 @@ +/* + * Copyright 2016 Mikhail Shiryaev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Product : Rapid SCADA + * Module : ScadaWebCommon + * Summary : URL templates of the web application pages + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + */ + +namespace Scada.Web.Shell +{ + /// + /// URL templates of the web application pages + /// Шаблоны адресов страниц веб-приложения + /// + public static class UrlTemplates + { + /// + /// Информация о пользователе + /// + public const string User = "~/User.aspx?userID={0}"; + } +} diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master index 8f2e4ad46..465ba101b 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master @@ -21,9 +21,8 @@ @@ -34,24 +33,20 @@
Rapid SCADA
AdminLogout + ID="lbtnMainLogout" runat="server" CssClass="hdr-btn txt-btn" OnClick="lbtnMainLogout_Click">Logout
-
- -
+
-
Main Menu
-
Views
+
+
<%= GenerateMainMenuHtml() %> @@ -60,7 +55,7 @@ <%= GenerateExplorerHtml() %>
- Collapse menu +
diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs index 9ded6021e..45c45519e 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs @@ -23,9 +23,9 @@ * Modified : 2016 */ +using Scada.UI; using Scada.Web.Shell; using System; -using System.Text; namespace Scada.Web { @@ -41,20 +41,9 @@ public partial class MasterMain : System.Web.UI.MasterPage private static readonly TreeViewRenderer treeViewRenderer = new TreeViewRenderer(); - private AppData appData; // общие данные веб-приложения - private UserData userData; // данные пользователя приложения + private AppData appData; // общие данные веб-приложения + private UserData userData; // данные пользователя приложения - protected bool mainMenuVisible; // отобразить главное меню при загрузке страницы - - - /// - /// Установить видимость главного меню - /// - private void SetMainMenuVisible() - { - mainMenuVisible = !string.Equals(Request.Url.AbsolutePath, ResolveUrl("~/View.aspx"), - StringComparison.OrdinalIgnoreCase); - } /// /// Генерировать HTML-код главного меню @@ -86,7 +75,15 @@ protected void Page_Load(object sender, EventArgs e) userData = UserData.GetUserData(); userData.CheckLoggedOn(true); - SetMainMenuVisible(); + if (!IsPostBack) + { + // перевод веб-страницы + Translator.TranslatePage(Page, "Scada.Web.MasterMain"); + + // настройка элементов управления + hlMainUser.Text = userData.UserName; + hlMainUser.NavigateUrl = string.Format(UrlTemplates.User, userData.UserID); + } } protected void lbtnMainLogout_Click(object sender, EventArgs e) diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.designer.cs b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.designer.cs index d83338eb6..d7d32746c 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.designer.cs +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.designer.cs @@ -39,6 +39,51 @@ public partial class MasterMain { /// protected global::System.Web.UI.WebControls.LinkButton lbtnMainLogout; + /// + /// lblMainFullscreenBtn control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblMainFullscreenBtn; + + /// + /// lblMainNormalViewBtn control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblMainNormalViewBtn; + + /// + /// lblMainMenu control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblMainMenu; + + /// + /// lblMainExplorer control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblMainExplorer; + + /// + /// lblMainCollapseMenu control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblMainCollapseMenu; + /// /// cphMainContent control. /// diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css index 8bbb00979..042bce9a9 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css @@ -54,7 +54,7 @@ a:active { position: relative; top: 2px; } -#divMainHeader #spanMainShowMenu { +#divMainHeader #spanMainShowMenuBtn { margin: 0 5px 0 -10px; font-size: 15px; display: none; @@ -69,7 +69,7 @@ a:active { float: right; height: 30px; } -#divMainNormalView { +#lblMainNormalViewBtn { position: fixed; top: 0; right: 0; @@ -84,7 +84,7 @@ a:active { color: #9ca1a6; font-size: 13px; } -#divMainNormalView:hover { +#lblMainNormalViewBtn:hover { color: #00b9eb; opacity: 1; } diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less index eb2a767e1..34831cf7d 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less @@ -70,7 +70,7 @@ a:active { top: 2px; } -#divMainHeader #spanMainShowMenu { +#divMainHeader #spanMainShowMenuBtn { margin: 0 5px 0 -10px; font-size: 15px; display: none; /*inline-block*/ @@ -87,7 +87,7 @@ a:active { height: @header-heigth; } -#divMainNormalView { +#lblMainNormalViewBtn { position: fixed; top: 0; right: 0; @@ -102,7 +102,7 @@ a:active { font-size: 13px; } -#divMainNormalView:hover { +#lblMainNormalViewBtn:hover { color: @menu-item-hover-fore-color; opacity: 1; } diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css index 7b7f4db58..e6157581e 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 250px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;white-space:nowrap;}#divMainHeader .hdr-btn{height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;display:inline-block;font-size:13px;text-decoration:none;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenu{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;}#divMainUserMenu{float:right;height:30px;}#divMainNormalView{position:fixed;top:0;right:0;height:30px;padding:0 10px;background-color:#fff;display:none;line-height:30px;opacity:.5;cursor:pointer;color:#9ca1a6;font-size:13px;}#divMainNormalView:hover{color:#00b9eb;opacity:1;}#divMainLeftPane{position:fixed;left:0;top:30px;width:250px;min-height:300px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 40px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainLeftPane .tree-view .node:hover{background-color:#191e23;color:#00b9eb;}#divMainLeftPane .tree-view .node.selected{background-color:#0073aa;color:#fff;}#divMainLeftPane .tree-view .expander::before{font-size:10px;}#divMainMenu .node[data-level="1"],#divMainMenu .node[data-level="2"],#divMainMenu .node[data-level="3"],#divMainMenu .node[data-level="4"],#divMainMenu .node[data-level="5"]{color:#9ca1a6;font-size:13px;}#divMainMenu .tree-view .node{padding:5px 10px 5px 10px;}#divMainMenu .tree-view .expander.left{display:none;width:0;}#divMainMenu .tree-view .expander.right{display:table-cell;width:20px;}#divMainExplorer{font-size:13px;line-height:13px;}#divMainExplorer .tree-view .node{padding:7px 5px 3px 10px;}#divMainExplorer .tree-view .icon{width:21px;}#divMainExplorer .tree-view img{position:relative;top:-2px;}#divMainCollapsePane{position:absolute;bottom:0;width:220px;height:40px;margin:0 0 0 30px;padding:5px 10px;color:#9ca1a6;font-size:13px;}#divMainCollapsePane i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapsePane:hover{color:#00b9eb;cursor:pointer;} \ No newline at end of file +body{margin:0;padding:30px 0 0 250px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;white-space:nowrap;}#divMainHeader .hdr-btn{height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;display:inline-block;font-size:13px;text-decoration:none;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenuBtn{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;}#divMainUserMenu{float:right;height:30px;}#lblMainNormalViewBtn{position:fixed;top:0;right:0;height:30px;padding:0 10px;background-color:#fff;display:none;line-height:30px;opacity:.5;cursor:pointer;color:#9ca1a6;font-size:13px;}#lblMainNormalViewBtn:hover{color:#00b9eb;opacity:1;}#divMainLeftPane{position:fixed;left:0;top:30px;width:250px;min-height:300px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 40px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainLeftPane .tree-view .node:hover{background-color:#191e23;color:#00b9eb;}#divMainLeftPane .tree-view .node.selected{background-color:#0073aa;color:#fff;}#divMainLeftPane .tree-view .expander::before{font-size:10px;}#divMainMenu .node[data-level="1"],#divMainMenu .node[data-level="2"],#divMainMenu .node[data-level="3"],#divMainMenu .node[data-level="4"],#divMainMenu .node[data-level="5"]{color:#9ca1a6;font-size:13px;}#divMainMenu .tree-view .node{padding:5px 10px 5px 10px;}#divMainMenu .tree-view .expander.left{display:none;width:0;}#divMainMenu .tree-view .expander.right{display:table-cell;width:20px;}#divMainExplorer{font-size:13px;line-height:13px;}#divMainExplorer .tree-view .node{padding:7px 5px 3px 10px;}#divMainExplorer .tree-view .icon{width:21px;}#divMainExplorer .tree-view img{position:relative;top:-2px;}#divMainCollapsePane{position:absolute;bottom:0;width:220px;height:40px;margin:0 0 0 30px;padding:5px 10px;color:#9ca1a6;font-size:13px;}#divMainCollapsePane i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapsePane:hover{color:#00b9eb;cursor:pointer;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js index 91189006c..21a01f31f 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js @@ -1,114 +1,118 @@ var scada = scada || {}; -scada.masterMain = scada.masterMain || {}; -scada.masterMain.params = scada.masterMain.params || {}; - -// Left pane that displays main menu or views explorer is expanded -scada.masterMain.leftPaneExpanded = true; - -// Update layout of the master page -scada.masterMain.updateLayout = function () { - var divMainHeader = $("#divMainHeader"); - var divMainLeftPane = $("#divMainLeftPane"); - var divMainTabs = $("#divMainTabs"); - - var paneH = $(window).height() - divMainHeader.outerHeight(); - divMainLeftPane.outerHeight(paneH); - divMainTabs.outerWidth(paneH); -}; - -// Choose a tool window and activate it -scada.masterMain.chooseToolWindow = function () { - if (this.params.mainMenuVisible) { - this.activateToolWindow($("#divMainMenuTab")); - } else { - this.activateToolWindow($("#divMainExplorerTab")); - } -}; - -// Make visible main menu or views explorer -scada.masterMain.activateToolWindow = function (divClickedTab) { - // highlight clicked tab - var tabs = $("#divMainTabs .tab"); - tabs.removeClass("selected"); - divClickedTab.addClass("selected"); - - // deactivate all the tool windows - var toolWindows = $("#divMainLeftPane .tool-window"); - toolWindows.css("display", "none"); - - // activate the appropriate tool window - var clickedTabId = divClickedTab.attr('id'); - var toolWindow; - - if (clickedTabId == "divMainMenuTab") { - toolWindow = $("#divMainMenu") - } else if (clickedTabId == "divMainExplorerTab") { - toolWindow = $("#divMainExplorer") - } else { - toolWindow = null; - } - - if (toolWindow) { - toolWindow.css("display", "block"); - } -}; - -// Hide main menu and views explorer -scada.masterMain.hideLeftPane = function () { - $("#divMainLeftPane").css("display", "none"); - $("body").css("padding-left", "0"); -}; - -// Show main menu and views explorer -scada.masterMain.showLeftPane = function () { - $("body").css("padding-left", ""); - $("#divMainLeftPane").css("display", ""); -}; - -// Collapse main menu and views explorer -scada.masterMain.collapseLeftPane = function () { - $("#spanMainShowMenu").css("display", "inline-block"); - this.hideLeftPane(); - this.leftPaneExpanded = false; -}; - -// Expand main menu and views explorer -scada.masterMain.expandLeftPane = function () { - this.showLeftPane(); - $("#spanMainShowMenu").css("display", "none"); - this.leftPaneExpanded = true; -}; - -// Hide page header -scada.masterMain.hideHeader = function () { - $("#divMainHeader").css("display", "none"); - $("body").css("padding-top", "0"); -}; - -// Show page header -scada.masterMain.showHeader = function () { - $("#divMainHeader").css("display", ""); - $("body").css("padding-top", ""); -}; +scada.env = scada.env || {}; + +scada.masterMain = { + // The left pane, which displays tool windows, is expanded + leftPaneExpanded: true, + + // Update layout of the master page + updateLayout: function () { + var divMainHeader = $("#divMainHeader"); + var divMainLeftPane = $("#divMainLeftPane"); + var divMainTabs = $("#divMainTabs"); + + var paneH = $(window).height() - divMainHeader.outerHeight(); + divMainLeftPane.outerHeight(paneH); + divMainTabs.outerWidth(paneH); + }, + + // Choose a tool window according to the current URL and activate it + chooseToolWindow: function () { + var rootPath = scada.env.rootPath; + var explorerVisible = rootPath && rootPath + "View.aspx" == window.location.pathname; + + if (explorerVisible) { + this.activateToolWindow($("#divMainExplorerTab")); + } else { + this.activateToolWindow($("#divMainMenuTab")); + } + }, + + // Make the tool window, that corresponds a clicked tab, visible + activateToolWindow: function (divClickedTab) { + // highlight clicked tab + var tabs = $("#divMainTabs .tab"); + tabs.removeClass("selected"); + divClickedTab.addClass("selected"); + + // deactivate all the tool windows + var toolWindows = $("#divMainLeftPane .tool-window"); + toolWindows.css("display", "none"); + + // activate the appropriate tool window + var clickedTabId = divClickedTab.attr('id'); + var toolWindow; + + if (clickedTabId == "divMainMenuTab") { + toolWindow = $("#divMainMenu") + } else if (clickedTabId == "divMainExplorerTab") { + toolWindow = $("#divMainExplorer") + } else { + toolWindow = null; + } -// Hide all the menus and switch browser to fullscreen mode -scada.masterMain.switchToFullscreen = function () { - if (this.leftPaneExpanded) { + if (toolWindow) { + toolWindow.css("display", "block"); + } + }, + + // Hide the left pane + hideLeftPane: function () { + $("#divMainLeftPane").css("display", "none"); + $("body").css("padding-left", "0"); + }, + + // Show the left pane + showLeftPane: function () { + $("body").css("padding-left", ""); + $("#divMainLeftPane").css("display", ""); + }, + + // Collapse the left pane and show the menu button + collapseLeftPane: function () { + $("#spanMainShowMenuBtn").css("display", "inline-block"); this.hideLeftPane(); - } - this.hideHeader(); - $("#divMainNormalView").css("display", "inline-block"); - scada.utils.requestFullscreen(); -}; + this.leftPaneExpanded = false; + }, -// Show all the menus and exit browser fullscreen mode -scada.masterMain.switchToNormalView = function () { - $("#divMainNormalView").css("display", "none"); - if (this.leftPaneExpanded) { + // Expand the left pane and hide the menu button + expandLeftPane: function () { this.showLeftPane(); + $("#spanMainShowMenuBtn").css("display", "none"); + this.leftPaneExpanded = true; + }, + + // Hide the page header + hideHeader: function () { + $("#divMainHeader").css("display", "none"); + $("body").css("padding-top", "0"); + }, + + // Show the page header + showHeader: function () { + $("#divMainHeader").css("display", ""); + $("body").css("padding-top", ""); + }, + + // Hide all the menus and switch browser to fullscreen mode + switchToFullscreen: function () { + if (this.leftPaneExpanded) { + this.hideLeftPane(); + } + this.hideHeader(); + $("#lblMainNormalViewBtn").css("display", "inline-block"); + scada.utils.requestFullscreen(); + }, + + // Show all the menus and exit browser fullscreen mode + switchToNormalView: function () { + $("#lblMainNormalViewBtn").css("display", "none"); + if (this.leftPaneExpanded) { + this.showLeftPane(); + } + this.showHeader(); + scada.utils.exitFullscreen(); } - this.showHeader(); - scada.utils.exitFullscreen(); }; $(document).ready(function () { @@ -121,34 +125,34 @@ $(document).ready(function () { scada.masterMain.updateLayout(); }); - // activate tool window if tab is clicked + // activate a tool window if the tab is clicked $("#divMainTabs .tab").click(function () { scada.masterMain.activateToolWindow($(this)); }); - // collapse left pane on button click + // collapse the left pane on the button click $("#divMainCollapsePane").click(function () { scada.masterMain.collapseLeftPane(); }); - // expand left pane on button click - $("#spanMainShowMenu").click(function () { + // expand the left pane on the button click + $("#spanMainShowMenuBtn").click(function () { scada.masterMain.expandLeftPane(); }); - // switch to full screen mode on button click - $("#spanMainFullScreen").click(function () { + // switch to full screen mode on the button click + $("#lblMainFullscreenBtn").click(function () { scada.masterMain.switchToFullscreen(); }); - // switch to normal view mode on button click - $("#divMainNormalView").click(function () { + // switch to normal view mode on the button click + $("#lblMainNormalViewBtn").click(function () { scada.masterMain.switchToNormalView(); }); // show menus if user exits fullscreen mode $(document).on("webkitfullscreenchange mozfullscreenchange fullscreenchange MSFullscreenChange", function () { - if (!scada.utils.isFullScreen) { + if (!scada.utils.isFullscreen()) { scada.masterMain.switchToNormalView(); } }); diff --git a/ScadaWeb/ScadaWebShell5Beta/js/scadautils.js b/ScadaWeb/ScadaWebShell5Beta/js/scadautils.js index db4201d3d..880ae0449 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/scadautils.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/scadautils.js @@ -36,7 +36,7 @@ scada.utils = { // Check if browser is in fullscreen mode // See https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API - get isFullScreen() { + isFullscreen: function() { return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement; }, @@ -69,7 +69,7 @@ scada.utils = { // Switch browser to full screen mode and back to normal view toggleFullscreen: function () { - if (this.isFullScreen) { + if (this.isFullscreen()) { this.exitFullscreen(); } else { this.requestFullscreen(); diff --git a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml index d96fc22fa..5144c1e8a 100644 --- a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml +++ b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml @@ -9,6 +9,14 @@ Insufficient rights. Illegal user role. + + Logout + Full screen + Normal view + Main Menu + Views + Collapse menu + Error loading view settings Error saving view settings diff --git a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml index 52b51b988..77b40afac 100644 --- a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml +++ b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml @@ -9,6 +9,14 @@ Недостаточно прав. Недопустимая роль пользователя. + + Выйти + На весь экран + Нормальный вид + Главное меню + Представления + Свернуть меню + Ошибка при загрузке настроек представлений Ошибка при сохранении настроек представлений @@ -27,7 +35,7 @@ Отчёты Конфигурация - О программе + О приложении Обнаружено нарушение безопасности. Незамедлительно обратитесь к системному администратору. diff --git a/ScadaWeb/ScadaWebShell5Beta/storage/admin/App/RememberMe.xml b/ScadaWeb/ScadaWebShell5Beta/storage/admin/App/RememberMe.xml index cb0871d79..2218e540f 100644 --- a/ScadaWeb/ScadaWebShell5Beta/storage/admin/App/RememberMe.xml +++ b/ScadaWeb/ScadaWebShell5Beta/storage/admin/App/RememberMe.xml @@ -1,9 +1,4 @@  admin - - 4811d600-639e-40a1-b361-ce411fb43be8 - ecc7f4b1-7afd-4146-81c9-2b01f0b1fe77 - 04/29/2016 09:57:23 - \ No newline at end of file From c2e4ac4a2493676f457b7219a532fa184f625142 Mon Sep 17 00:00:00 2001 From: 2mik Date: Fri, 6 May 2016 15:07:44 +0300 Subject: [PATCH 026/382] ScadaWeb5: views --- ScadaWeb/ScadaWebShell5Beta/MasterMain.Master | 2 +- .../ScadaWebShell5Beta.csproj | 7 ++++ ScadaWeb/ScadaWebShell5Beta/View.aspx | 4 +-- ScadaWeb/ScadaWebShell5Beta/View.aspx.cs | 36 +++++++++++++++---- .../ScadaWebShell5Beta/compilerconfig.json | 4 +++ ScadaWeb/ScadaWebShell5Beta/css/view.css | 10 ++++++ ScadaWeb/ScadaWebShell5Beta/css/view.less | 11 ++++++ ScadaWeb/ScadaWebShell5Beta/css/view.min.css | 1 + ScadaWeb/ScadaWebShell5Beta/js/mastermain.js | 1 + ScadaWeb/ScadaWebShell5Beta/js/view.js | 24 +++++++++---- 10 files changed, 84 insertions(+), 16 deletions(-) create mode 100644 ScadaWeb/ScadaWebShell5Beta/css/view.css create mode 100644 ScadaWeb/ScadaWebShell5Beta/css/view.less create mode 100644 ScadaWeb/ScadaWebShell5Beta/css/view.min.css diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master index 465ba101b..69a95c6eb 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master @@ -58,7 +58,7 @@
-
+
diff --git a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj index 679f3c0ae..a5acc8ddf 100644 --- a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj +++ b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj @@ -94,6 +94,12 @@ treeview.css + + view.less + + + view.css + @@ -238,6 +244,7 @@ + Web.config diff --git a/ScadaWeb/ScadaWebShell5Beta/View.aspx b/ScadaWeb/ScadaWebShell5Beta/View.aspx index 4334a5fc9..51fa1f5b8 100644 --- a/ScadaWeb/ScadaWebShell5Beta/View.aspx +++ b/ScadaWeb/ScadaWebShell5Beta/View.aspx @@ -1,8 +1,8 @@ <%@ Page Title="Views - Rapid SCADA" Language="C#" MasterPageFile="~/MasterMain.Master" AutoEventWireup="true" CodeBehind="View.aspx.cs" Inherits="Scada.Web.WFrmView" %> + -
H
-
Test Content
+
diff --git a/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs b/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs index 803009fdc..fd1305cd8 100644 --- a/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs +++ b/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs @@ -1,12 +1,36 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using System.Web.UI; -using System.Web.UI.WebControls; +/* + * Copyright 2016 Mikhail Shiryaev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Product : Rapid SCADA + * Module : SCADA-Web + * Summary : View web form + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + */ + +using System; namespace Scada.Web { + /// + /// View web form + /// Веб-форма представления + /// public partial class WFrmView : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) diff --git a/ScadaWeb/ScadaWebShell5Beta/compilerconfig.json b/ScadaWeb/ScadaWebShell5Beta/compilerconfig.json index 12e007bbd..56fa3e74e 100644 --- a/ScadaWeb/ScadaWebShell5Beta/compilerconfig.json +++ b/ScadaWeb/ScadaWebShell5Beta/compilerconfig.json @@ -14,5 +14,9 @@ { "outputFile": "css/treeview.css", "inputFile": "css/treeview.less" + }, + { + "outputFile": "css/view.css", + "inputFile": "css/view.less" } ] \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/view.css b/ScadaWeb/ScadaWebShell5Beta/css/view.css new file mode 100644 index 000000000..6dedaf930 --- /dev/null +++ b/ScadaWeb/ScadaWebShell5Beta/css/view.css @@ -0,0 +1,10 @@ +/*Verdana, Geneva, Tahoma, sans-serif;*/ +iframe { + margin: 0; + padding: 0; + width: 100%; + height: 100%; + border: none; + display: block; + overflow: hidden; +} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/view.less b/ScadaWeb/ScadaWebShell5Beta/css/view.less new file mode 100644 index 000000000..4b0b20180 --- /dev/null +++ b/ScadaWeb/ScadaWebShell5Beta/css/view.less @@ -0,0 +1,11 @@ +@import "globalvar.less"; + +iframe { + margin: 0; + padding: 0; + width: 100%; + height: 100%; + border: none; + display: block; + overflow: hidden; +} diff --git a/ScadaWeb/ScadaWebShell5Beta/css/view.min.css b/ScadaWeb/ScadaWebShell5Beta/css/view.min.css new file mode 100644 index 000000000..1f3b68043 --- /dev/null +++ b/ScadaWeb/ScadaWebShell5Beta/css/view.min.css @@ -0,0 +1 @@ +iframe{margin:0;padding:0;width:100%;height:100%;border:none;display:block;overflow:hidden;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js index 21a01f31f..5b3499482 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js @@ -14,6 +14,7 @@ scada.masterMain = { var paneH = $(window).height() - divMainHeader.outerHeight(); divMainLeftPane.outerHeight(paneH); divMainTabs.outerWidth(paneH); + $("#divMainContent").outerHeight(paneH); }, // Choose a tool window according to the current URL and activate it diff --git a/ScadaWeb/ScadaWebShell5Beta/js/view.js b/ScadaWeb/ScadaWebShell5Beta/js/view.js index 0c4f294f3..d96c73193 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/view.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/view.js @@ -1,11 +1,21 @@ -$(document).ready(function () { - var divTest = $("#divTest"); +var scada = scada || {}; - for (var i = 0; i < 100; i++) { - divTest.html(divTest.html() + "
Test " + i); +scada.view = { + // Page title just after loading + initialPageTitle: "", + + // Load view + load: function (url) { + document.title = this.initialPageTitle; + $("#frameView").attr("src", url); + }, + + // Set page title + setPageTitle: function (viewTitle) { + document.title = viewTitle + " - Rapid SCADA"; } +}; - $(window).resize(function () { - $("#divH").text($(window).height()); - }); +$(document).ready(function () { + scada.view.initialPageTitle = document.title; }); \ No newline at end of file From 240c482add8d92810ec12e15fa954b06222448dd Mon Sep 17 00:00:00 2001 From: 2mik Date: Sat, 7 May 2016 00:05:37 +0300 Subject: [PATCH 027/382] ScadaComm: CPU load fixed --- .../Channels/CommSerialLogic.cs | 59 +++++++++++++++---- .../Channels/SerialConnection.cs | 56 ++++++++++++++++-- .../ScadaCommCommon/Channels/TcpConnection.cs | 4 +- ScadaComm/ScadaCommCtrl/FrmAbout.cs | 2 +- ScadaComm/ScadaCommSvc/CommLine.cs | 8 +-- ScadaComm/ScadaCommSvc/Manager.cs | 2 +- 6 files changed, 108 insertions(+), 23 deletions(-) diff --git a/ScadaComm/ScadaCommCommon/Channels/CommSerialLogic.cs b/ScadaComm/ScadaCommCommon/Channels/CommSerialLogic.cs index f76126757..6f1b003ea 100644 --- a/ScadaComm/ScadaCommCommon/Channels/CommSerialLogic.cs +++ b/ScadaComm/ScadaCommCommon/Channels/CommSerialLogic.cs @@ -1,5 +1,5 @@ /* - * Copyright 2015 Mikhail Shiryaev + * Copyright 2016 Mikhail Shiryaev * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ * * Author : Mikhail Shiryaev * Created : 2015 - * Modified : 2015 + * Modified : 2016 */ using Scada.Comm.Devices; @@ -170,6 +170,30 @@ public override OperatingBehaviors Behavior } + /// + /// Открытие последовательного порта + /// + protected void OpenSerialPort() + { + WriteToLog(""); + WriteToLog(string.Format(Localization.UseRussian ? + "{0} Открытие последовательного порта {1}" : + "{0} Open serial port {1}", CommUtils.GetNowDT(), serialConn.SerialPort.PortName)); + serialConn.Open(); + } + + /// + /// Закрытие последовательного порта + /// + protected void CloseSerialPort() + { + WriteToLog(""); + WriteToLog(string.Format(Localization.UseRussian ? + "{0} Закрытие последовательного порта {1}" : + "{0} Close serial port {1}", CommUtils.GetNowDT(), serialConn.SerialPort.PortName)); + serialConn.Close(); + } + /// /// Обработать событие приёма данных по последовательному порту /// @@ -214,10 +238,7 @@ public override void Init(SortedList commCnlParams, List 0) @@ -237,11 +258,27 @@ public override void Stop() kpLogic.Connection = null; // закрытие последовательного порта - serialConn.Close(); - WriteToLog(""); - WriteToLog(string.Format(Localization.UseRussian ? - "{0} Последовательный порт '{1}' закрыт" : - "{0} Serial port '{1}' is closed", CommUtils.GetNowDT(), serialConn.SerialPort.PortName)); + CloseSerialPort(); + } + + /// + /// Выполнить действия перед сеансом опроса КП или отправкой команды + /// + public override void BeforeSession(KPLogic kpLogic) + { + // открытие последовательного порта, если он закрыт + if (serialConn != null && !serialConn.Connected) + OpenSerialPort(); + } + + /// + /// Выполнить действия после сеанса опроса КП или отправки команды + /// + public override void AfterSession(KPLogic kpLogic) + { + // закрытие последовательного порта, если произошла ошибка записи в порт + if (serialConn != null && serialConn.Connected && serialConn.WriteError) + CloseSerialPort(); } /// diff --git a/ScadaComm/ScadaCommCommon/Channels/SerialConnection.cs b/ScadaComm/ScadaCommCommon/Channels/SerialConnection.cs index 058b4e872..55625d3e3 100644 --- a/ScadaComm/ScadaCommCommon/Channels/SerialConnection.cs +++ b/ScadaComm/ScadaCommCommon/Channels/SerialConnection.cs @@ -1,5 +1,5 @@ /* - * Copyright 2015 Mikhail Shiryaev + * Copyright 2016 Mikhail Shiryaev * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ * * Author : Mikhail Shiryaev * Created : 2015 - * Modified : 2015 + * Modified : 2016 */ using System; @@ -37,6 +37,17 @@ namespace Scada.Comm.Channels /// public class SerialConnection : Connection { + /// + /// Периодичность попыток открытия порта, с + /// + protected const int OpenPeriod = 5; + + /// + /// Дата и время неудачной попытки открытия порта + /// + protected DateTime openFailDT; + + /// /// Конструктор, ограничивающий создание объекта без параметров /// @@ -52,10 +63,13 @@ public SerialConnection(SerialPort serialPort) { if (serialPort == null) throw new ArgumentNullException("serialPort"); - + + openFailDT = DateTime.MinValue; + SerialPort = serialPort; SerialPort.WriteTimeout = DefaultWriteTimeout; SerialPort.NewLine = NewLine; + WriteError = false; } @@ -91,6 +105,12 @@ public override string NewLine /// public SerialPort SerialPort { get; protected set; } + /// + /// Ошибка записи в порт + /// + /// Сигнализирует о нарушении связи с сетевым конвертером + public bool WriteError { get; protected set; } + /// /// Считать данные @@ -236,6 +256,7 @@ public override void Write(byte[] buffer, int offset, int count, { SerialPort.Write(buffer, offset, count); logText = BuildWriteLogText(buffer, offset, count, logFormat); + WriteError = false; } catch (TimeoutException ex) { @@ -244,6 +265,7 @@ public override void Write(byte[] buffer, int offset, int count, } catch (Exception ex) { + WriteError = true; throw new InvalidOperationException(CommPhrases.WriteDataError + ": " + ex.Message, ex); } } @@ -262,6 +284,7 @@ public override void WriteLine(string text, out string logText) { SerialPort.WriteLine(text); logText = CommPhrases.SendNotation + ": " + text; + WriteError = false; } catch (TimeoutException ex) { @@ -270,6 +293,7 @@ public override void WriteLine(string text, out string logText) } catch (Exception ex) { + WriteError = true; throw new InvalidOperationException(CommPhrases.WriteLineError + ": " + ex.Message, ex); } } @@ -280,7 +304,31 @@ public override void WriteLine(string text, out string logText) /// public void Open() { - SerialPort.Open(); + WriteError = false; + DateTime nowDT = DateTime.Now; + + if ((nowDT - openFailDT).TotalSeconds >= OpenPeriod || nowDT < openFailDT /*время переведено назад*/) + { + try + { + SerialPort.Open(); + openFailDT = DateTime.MinValue; + } + catch (Exception ex) + { + openFailDT = nowDT; + throw new Exception((Localization.UseRussian ? + "Ошибка при открытии последовательного порта: " : + "Error opening serial port: ") + ex.Message, ex); + } + } + else + { + throw new InvalidOperationException(string.Format(Localization.UseRussian ? + "Попытка открытия последовательного порта может быть не ранее, чем через {0} с после предыдущей." : + "Attempt to open serial port can not be earlier than {0} seconds after the previous.", + OpenPeriod)); + } } /// diff --git a/ScadaComm/ScadaCommCommon/Channels/TcpConnection.cs b/ScadaComm/ScadaCommCommon/Channels/TcpConnection.cs index cdc24d84a..47d2bbc80 100644 --- a/ScadaComm/ScadaCommCommon/Channels/TcpConnection.cs +++ b/ScadaComm/ScadaCommCommon/Channels/TcpConnection.cs @@ -47,7 +47,7 @@ public class TcpConnection : Connection /// /// Максимальный размер считываемых строк по умолчанию /// - protected const int DeaultMaxLineSize = 1000; + protected const int DefaultMaxLineSize = 1000; /// /// Периодичность попыток установки TCP-соединения, с /// @@ -88,7 +88,7 @@ public TcpConnection(TcpClient tcpClient) if (tcpClient == null) throw new ArgumentNullException("tcpClient"); - maxLineSize = DeaultMaxLineSize; + maxLineSize = DefaultMaxLineSize; connFailDT = DateTime.MinValue; relatedKPList = null; diff --git a/ScadaComm/ScadaCommCtrl/FrmAbout.cs b/ScadaComm/ScadaCommCtrl/FrmAbout.cs index 4caac161c..6f96a6a2c 100644 --- a/ScadaComm/ScadaCommCtrl/FrmAbout.cs +++ b/ScadaComm/ScadaCommCtrl/FrmAbout.cs @@ -37,7 +37,7 @@ namespace Scada.Comm.Ctrl /// public partial class FrmAbout : Form { - private const string Version = "4.5.0.6"; // версия приложения + private const string Version = "4.5.0.7"; // версия приложения private static FrmAbout frmAbout = null; // форма о программе private string exeDir; // директория исполняемого файла приложения diff --git a/ScadaComm/ScadaCommSvc/CommLine.cs b/ScadaComm/ScadaCommSvc/CommLine.cs index 46072a89c..b961c9e77 100644 --- a/ScadaComm/ScadaCommSvc/CommLine.cs +++ b/ScadaComm/ScadaCommSvc/CommLine.cs @@ -1,5 +1,5 @@ /* - * Copyright 2015 Mikhail Shiryaev + * Copyright 2016 Mikhail Shiryaev * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ * * Author : Mikhail Shiryaev * Created : 2006 - * Modified : 2015 + * Modified : 2016 */ using Scada.Comm.Channels; @@ -982,7 +982,7 @@ private void CommCnlBeforeSession(KPLogic kpLogic) { log.WriteAction((Localization.UseRussian ? "Ошибка при выполнении действий канала связи перед сеансом опроса КП: " : - "Error executing actions of communication channel before session with a device: ") + ex.Message); + "Error executing the communication channel actions before session with a device: ") + ex.Message); } } @@ -1000,7 +1000,7 @@ private void CommCnlAfterSession(KPLogic kpLogic) { log.WriteAction((Localization.UseRussian ? "Ошибка при выполнении действий канала связи после сеанса опроса КП: " : - "Error executing actions of communication channel after session with a device: ") + ex.Message); + "Error executing the communication channel actions after session with a device: ") + ex.Message); } } diff --git a/ScadaComm/ScadaCommSvc/Manager.cs b/ScadaComm/ScadaCommSvc/Manager.cs index e58e552fe..55fcda28a 100644 --- a/ScadaComm/ScadaCommSvc/Manager.cs +++ b/ScadaComm/ScadaCommSvc/Manager.cs @@ -45,7 +45,7 @@ internal sealed class Manager /// /// Версия приложения /// - private const string AppVersion = "4.5.0.6"; + private const string AppVersion = "4.5.0.7"; /// /// Имя файла конфигурации /// From 4e117ae4c66a72c489f67082d5df440a83734534 Mon Sep 17 00:00:00 2001 From: 2mik Date: Sun, 8 May 2016 00:40:05 +0300 Subject: [PATCH 028/382] ScadaWeb5: load view --- .../PlgScheme/plugins/Scheme/Scheme.aspx.cs | 2 +- .../ScadaWebCommon5Beta/Shell/ViewNode.cs | 7 ++-- ScadaWeb/ScadaWebShell5Beta/View.aspx.cs | 38 +++++++++++++++++++ ScadaWeb/ScadaWebShell5Beta/js/mastermain.js | 14 +++++-- ScadaWeb/ScadaWebShell5Beta/js/scadautils.js | 15 ++++++++ ScadaWeb/ScadaWebShell5Beta/js/treeview.js | 5 +-- 6 files changed, 71 insertions(+), 10 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.cs b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.cs index 59b7c3988..861dcaaa6 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.cs @@ -23,7 +23,7 @@ * Modified : 2016 */ -#define DEBUG_MODE +//#define DEBUG_MODE using Scada.Scheme; using System; diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs index 676233508..147dfcf84 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs @@ -38,9 +38,9 @@ namespace Scada.Web.Shell public class ViewNode : IWebTreeNode { /// - /// Шаблон ссылки на страницу отображения представления + /// Шаблон ссылки узла для загрузки представления /// - protected const string ViewUrlTemplate = "~/View.aspx?viewID={0}"; + protected const string ViewUrlTemplate = "javascript:scada.masterMain.loadView({0}, \"{1}\");"; /// @@ -67,7 +67,8 @@ public ViewNode(ViewSettings.ViewItem viewItem, ViewSpec viewSpec) } else { - Url = VirtualPathUtility.ToAbsolute(string.Format(ViewUrlTemplate, viewItem.ViewID)); + string viewUrl = VirtualPathUtility.ToAbsolute(viewSpec.GetViewUrl(viewItem.ViewID)); + Url = string.Format(ViewUrlTemplate, viewItem.ViewID, viewUrl); IconUrl = VirtualPathUtility.ToAbsolute(viewSpec.IconUrl); } diff --git a/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs b/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs index fd1305cd8..1fc7ae083 100644 --- a/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs +++ b/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs @@ -23,6 +23,8 @@ * Modified : 2016 */ +using Scada.Data; +using Scada.Web.Plugins; using System; namespace Scada.Web @@ -33,9 +35,45 @@ namespace Scada.Web /// public partial class WFrmView : System.Web.UI.Page { + /// + /// Добавить на страницу скрипт загрузки представления + /// + private void AddLoadViewScript(string viewUrl) + { + ClientScript.RegisterStartupScript(GetType(), "Startup", + "scada.view.load('" + ResolveUrl(viewUrl) + "');", true); + } + + protected void Page_Load(object sender, EventArgs e) { + UserData userData = UserData.GetUserData(); + userData.CheckLoggedOn(true); + + if (!IsPostBack) + { + // получение ид. представления для загрузки + int viewID; + int.TryParse(Request.QueryString["viewID"], out viewID); + + // получение информации о представлении и добавление скрипта загрузки представления + if (viewID > 0) + { + if (!userData.UserRights.GetViewRights(viewID).ViewRight) + throw new ScadaException(WebPhrases.NoRights); + + AppData appData = AppData.GetAppData(); + ViewProps viewProps = appData.DataAccess.GetViewProps(viewID); + ViewSpec viewSpec; + if (viewProps != null && userData.ViewSpecs.TryGetValue(viewProps.ViewTypeCode, out viewSpec)) + AddLoadViewScript(viewSpec.GetViewUrl(viewID)); + } + else + { + // загрузка первого доступного представления + } + } } } } \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js index 5b3499482..8d1b54759 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js @@ -1,5 +1,5 @@ var scada = scada || {}; -scada.env = scada.env || {}; +scada.view = scada.view || {}; // defined if the current page is View.aspx scada.masterMain = { // The left pane, which displays tool windows, is expanded @@ -19,8 +19,7 @@ scada.masterMain = { // Choose a tool window according to the current URL and activate it chooseToolWindow: function () { - var rootPath = scada.env.rootPath; - var explorerVisible = rootPath && rootPath + "View.aspx" == window.location.pathname; + var explorerVisible = scada.env.rootPath + "View.aspx" == window.location.pathname; if (explorerVisible) { this.activateToolWindow($("#divMainExplorerTab")); @@ -113,6 +112,15 @@ scada.masterMain = { } this.showHeader(); scada.utils.exitFullscreen(); + }, + + // Load view without reloading the whole page if possible + loadView: function (viewID, viewUrl) { + if (scada.view.load) { + scada.view.load(viewUrl); + } else { + location = scada.env.rootPath + "View.aspx?viewID=" + viewID; + } } }; diff --git a/ScadaWeb/ScadaWebShell5Beta/js/scadautils.js b/ScadaWeb/ScadaWebShell5Beta/js/scadautils.js index 880ae0449..669346cc0 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/scadautils.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/scadautils.js @@ -74,5 +74,20 @@ scada.utils = { } else { this.requestFullscreen(); } + }, + + // Click hyperlink programmatically + clickLink: function (jqLink) { + var href = jqLink.attr("href"); + if (href) { + if (href.startsWith("javascript:")) { + // execute script + var script = href.substr(11); + eval(script); + } else { + // open web page + location = href; + } + } } }; diff --git a/ScadaWeb/ScadaWebShell5Beta/js/treeview.js b/ScadaWeb/ScadaWebShell5Beta/js/treeview.js index 28f98ff5c..0ec149045 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/treeview.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/treeview.js @@ -9,6 +9,7 @@ /* * Requires: * - jquery + * - scadautils.js */ // Rapid SCADA namespace @@ -59,15 +60,13 @@ scada.treeView = { var link = $(this).find("a"); if (link.length > 0) { - location = link.attr("href"); + scada.utils.clickLink(link); } else { var expander = $(this).find(".expander"); if (!expander.hasClass("empty")) { thisTreeView._toggleTreeNode(expander); } } - - $(this).find("a").click(); }); } }; \ No newline at end of file From 01209ff0bb36d4150a2ea86abb6bf02e56397021 Mon Sep 17 00:00:00 2001 From: 2mik Date: Wed, 11 May 2016 15:12:43 +0300 Subject: [PATCH 029/382] ScadaWeb5: views --- .../Shell/TreeViewRenderer.cs | 14 ++--- .../ScadaWebCommon5Beta/Shell/UrlTemplates.cs | 15 +++++ .../ScadaWebCommon5Beta/Shell/UserViews.cs | 61 +++++++++++++++++++ .../ScadaWebCommon5Beta/Shell/ViewNode.cs | 30 ++++++--- ScadaWeb/ScadaWebCommon5Beta/UserData.cs | 7 ++- ScadaWeb/ScadaWebShell5Beta/View.aspx.cs | 27 +++----- .../ScadaWebShell5Beta/css/mastermain.css | 4 ++ .../ScadaWebShell5Beta/css/mastermain.less | 5 ++ .../ScadaWebShell5Beta/css/mastermain.min.css | 2 +- 9 files changed, 128 insertions(+), 37 deletions(-) diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/TreeViewRenderer.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/TreeViewRenderer.cs index 14e8bb83c..e134638c1 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/TreeViewRenderer.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/TreeViewRenderer.cs @@ -108,11 +108,12 @@ protected string GenTreeViewHtml(IList treeNodes, object selObj, Options options IWebTreeNode webTreeNode = treeNode as IWebTreeNode; if (webTreeNode != null) { - string selected = webTreeNode.IsSelected(selObj) ? " selected" : ""; - string dataAttrs = GenDataAttrsHtml(webTreeNode); - bool containsSubitems = webTreeNode.Children.Count > 0; - string expanderEmpty = containsSubitems ? "" : " empty"; + bool urlIsEmpty = string.IsNullOrEmpty(webTreeNode.Url); + string nodeCssClass = (webTreeNode.IsSelected(selObj) ? " selected" : "") + + (!containsSubitems && urlIsEmpty ? " disabled" : ""); + string dataAttrs = GenDataAttrsHtml(webTreeNode); + string expanderCssClass = containsSubitems ? "" : " empty"; string icon; if (options.ShowIcons) @@ -128,11 +129,10 @@ protected string GenTreeViewHtml(IList treeNodes, object selObj, Options options } string text = HttpUtility.HtmlEncode(webTreeNode.Text); - string textOrLink = string.IsNullOrEmpty(webTreeNode.Url) ? - text : string.Format(LinkTemplate, webTreeNode.Url, text); + string textOrLink = urlIsEmpty ? text : string.Format(LinkTemplate, webTreeNode.Url, text); sbHtml.AppendLine(string.Format(NodeTemplate, - selected, dataAttrs, expanderEmpty, icon, textOrLink)); + nodeCssClass, dataAttrs, expanderCssClass, icon, textOrLink)); if (containsSubitems) sbHtml.Append(GenTreeViewHtml(webTreeNode.Children, selObj, options, false)); diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/UrlTemplates.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/UrlTemplates.cs index e9547854f..4421e0a1f 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/UrlTemplates.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/UrlTemplates.cs @@ -31,9 +31,24 @@ namespace Scada.Web.Shell /// public static class UrlTemplates { + /// + /// Вход в систему с указанием ссылки для возврата + /// + public const string LoginWithReturn = "~/Login.aspx?return={0}"; + + /// + /// Вход в систему с указанием ссылки для возврата и выводом сообщения + /// + public const string LoginWithAlert = "~/Login.aspx?return={0}&alert={1}"; + /// /// Информация о пользователе /// public const string User = "~/User.aspx?userID={0}"; + + /// + /// Отсутствующее представление + /// + public const string NoView = "~/NoView.aspx"; } } diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/UserViews.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/UserViews.cs index 7cf6704a9..5673843bd 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/UserViews.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/UserViews.cs @@ -42,6 +42,10 @@ public class UserViews /// Журнал /// protected readonly Log log; + /// + /// Ссылки представлений, ключ - ид. представления + /// + protected readonly Dictionary viewUrls; /// /// Права пользователя @@ -73,6 +77,8 @@ public UserViews(Log log) throw new ArgumentNullException("log"); this.log = log; + viewUrls = new Dictionary(); + userRights = null; viewSpecs = null; dataAccess = null; @@ -115,10 +121,36 @@ protected void CreateViewNodes(List destViewNodes, List 0 || viewNode.ChildNodes.Count > 0) + { destViewNodes.Add(viewNode); + viewUrls[viewID] = viewNode.ViewUrl; + } + } + } + + /// + /// Рекурсивно найти узел дерева с непустым представлением + /// + protected ViewNode FindNonEmptyViewNode(List viewNodes) + { + foreach (ViewNode viewNode in viewNodes) + { + if (viewNode.ViewID > 0 && !string.IsNullOrEmpty(viewNode.ViewUrl)) + { + return viewNode; + } + else + { + ViewNode node = FindNonEmptyViewNode(viewNode.ChildNodes); + if (node != null) + return node; + } } + + return null; } + /// /// Инициализировать представления пользователя /// @@ -129,6 +161,7 @@ public void Init(UserData userData, DataAccess dataAccess) try { + viewUrls.Clear(); userRights = userData.UserRights; viewSpecs = userData.ViewSpecs; this.dataAccess = dataAccess; @@ -143,5 +176,33 @@ public void Init(UserData userData, DataAccess dataAccess) "Error initializing user views"); } } + + /// + /// Получить ссылку на представление с заданным идентификатором + /// + public string GetViewUrl(int viewID) + { + string viewUrl; + return viewUrls.TryGetValue(viewID, out viewUrl) ? viewUrl : ""; + } + + /// + /// Получить ссылку первого доступного представления + /// + public string GetFirstViewUrl() + { + try + { + ViewNode viewNode = FindNonEmptyViewNode(ViewNodes); + return viewNode == null ? "" : viewNode.ViewUrl; + } + catch (Exception ex) + { + log.WriteException(ex, Localization.UseRussian ? + "Ошибка при получении ссылки первого доступного представления" : + "Error getting URL of the first accessible view"); + return ""; + } + } } } \ No newline at end of file diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs index 147dfcf84..d612fd45f 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs @@ -40,7 +40,7 @@ public class ViewNode : IWebTreeNode /// /// Шаблон ссылки узла для загрузки представления /// - protected const string ViewUrlTemplate = "javascript:scada.masterMain.loadView({0}, \"{1}\");"; + protected const string ViewNodeUrlTemplate = "javascript:scada.masterMain.loadView({0}, \"{1}\");"; /// @@ -58,46 +58,58 @@ public ViewNode(ViewSettings.ViewItem viewItem, ViewSpec viewSpec) if (viewItem == null) throw new ArgumentNullException("viewItem"); + ViewID = viewItem.ViewID; Text = viewItem.Text ?? ""; + AlarmCnlNum = viewItem.AlarmCnlNum; if (viewSpec == null) { Url = ""; + ViewUrl = ""; IconUrl = ""; } else { - string viewUrl = VirtualPathUtility.ToAbsolute(viewSpec.GetViewUrl(viewItem.ViewID)); - Url = string.Format(ViewUrlTemplate, viewItem.ViewID, viewUrl); + ViewUrl = VirtualPathUtility.ToAbsolute(viewSpec.GetViewUrl(ViewID)); + Url = string.Format(ViewNodeUrlTemplate, ViewID, ViewUrl); IconUrl = VirtualPathUtility.ToAbsolute(viewSpec.IconUrl); } - AlarmCnlNum = viewItem.AlarmCnlNum; Level = -1; ChildNodes = new List(); InitDataAttrs(); } + /// + /// Получить или установить идентификатор представления + /// + public int ViewID { get; set; } + /// /// Получить текст /// public string Text { get; protected set; } /// - /// Получить ссылку + /// Получить номер входного канала, информирующего о тревожном состоянии представления + /// + public int AlarmCnlNum { get; protected set; } + + /// + /// Получить ссылку узла /// public string Url { get; protected set; } /// - /// Получить ссылку на иконку + /// Получить ссылку представления /// - public string IconUrl { get; protected set; } + public string ViewUrl { get; protected set; } /// - /// Получить номер входного канала, информирующего о тревожном состоянии представления + /// Получить ссылку на иконку /// - public int AlarmCnlNum { get; protected set; } + public string IconUrl { get; protected set; } /// /// Получить или установить уровень вложенности diff --git a/ScadaWeb/ScadaWebCommon5Beta/UserData.cs b/ScadaWeb/ScadaWebCommon5Beta/UserData.cs index eeb63e3b0..59215d36d 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/UserData.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/UserData.cs @@ -328,9 +328,10 @@ public void CheckLoggedOn(bool tryToLogin) // переход на страницу входа if (!LoggedOn) { - httpContext.Response.Redirect("~/Login.aspx" + - "?return=" + HttpUtility.UrlEncode(httpContext.Request.Url.ToString()) + - (alert == "" ? "" : "&alert=" + HttpUtility.UrlEncode(alert))); + string returnUrl = HttpUtility.UrlEncode(httpContext.Request.Url.ToString()); + httpContext.Response.Redirect(alert == "" ? + string.Format(UrlTemplates.LoginWithReturn, returnUrl) : + string.Format(UrlTemplates.LoginWithAlert, returnUrl, HttpUtility.UrlEncode(alert))); } } else diff --git a/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs b/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs index 1fc7ae083..aa9a289f3 100644 --- a/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs +++ b/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs @@ -23,8 +23,7 @@ * Modified : 2016 */ -using Scada.Data; -using Scada.Web.Plugins; +using Scada.Web.Shell; using System; namespace Scada.Web @@ -56,23 +55,17 @@ protected void Page_Load(object sender, EventArgs e) int viewID; int.TryParse(Request.QueryString["viewID"], out viewID); - // получение информации о представлении и добавление скрипта загрузки представления - if (viewID > 0) - { - if (!userData.UserRights.GetViewRights(viewID).ViewRight) - throw new ScadaException(WebPhrases.NoRights); + // получение ссылки представления + string viewUrl = viewID > 0 ? + userData.UserViews.GetViewUrl(viewID) : + userData.UserViews.GetFirstViewUrl(); - AppData appData = AppData.GetAppData(); - ViewProps viewProps = appData.DataAccess.GetViewProps(viewID); - ViewSpec viewSpec; - - if (viewProps != null && userData.ViewSpecs.TryGetValue(viewProps.ViewTypeCode, out viewSpec)) - AddLoadViewScript(viewSpec.GetViewUrl(viewID)); - } + if (string.IsNullOrEmpty(viewUrl)) + // переход на страницу отсутствующего представления + Response.Redirect(UrlTemplates.NoView); else - { - // загрузка первого доступного представления - } + // добавление скрипта загрузки представления + AddLoadViewScript(viewUrl); } } } diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css index 042bce9a9..6b1b561f5 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css @@ -144,6 +144,10 @@ a:active { background-color: #0073aa; color: white; } +#divMainLeftPane .tree-view .node.disabled { + color: #9ca1a6; + cursor: default; +} #divMainLeftPane .tree-view .expander::before { font-size: 10px; } diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less index 34831cf7d..5817d3b17 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less @@ -172,6 +172,11 @@ a:active { color: @menu-item-selected-fore-color; } +#divMainLeftPane .tree-view .node.disabled { + color: @menu-fore-color-dark; + cursor: default; +} + #divMainLeftPane .tree-view .expander::before { font-size: 10px; } diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css index e6157581e..04627c9d6 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 250px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;white-space:nowrap;}#divMainHeader .hdr-btn{height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;display:inline-block;font-size:13px;text-decoration:none;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenuBtn{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;}#divMainUserMenu{float:right;height:30px;}#lblMainNormalViewBtn{position:fixed;top:0;right:0;height:30px;padding:0 10px;background-color:#fff;display:none;line-height:30px;opacity:.5;cursor:pointer;color:#9ca1a6;font-size:13px;}#lblMainNormalViewBtn:hover{color:#00b9eb;opacity:1;}#divMainLeftPane{position:fixed;left:0;top:30px;width:250px;min-height:300px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 40px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainLeftPane .tree-view .node:hover{background-color:#191e23;color:#00b9eb;}#divMainLeftPane .tree-view .node.selected{background-color:#0073aa;color:#fff;}#divMainLeftPane .tree-view .expander::before{font-size:10px;}#divMainMenu .node[data-level="1"],#divMainMenu .node[data-level="2"],#divMainMenu .node[data-level="3"],#divMainMenu .node[data-level="4"],#divMainMenu .node[data-level="5"]{color:#9ca1a6;font-size:13px;}#divMainMenu .tree-view .node{padding:5px 10px 5px 10px;}#divMainMenu .tree-view .expander.left{display:none;width:0;}#divMainMenu .tree-view .expander.right{display:table-cell;width:20px;}#divMainExplorer{font-size:13px;line-height:13px;}#divMainExplorer .tree-view .node{padding:7px 5px 3px 10px;}#divMainExplorer .tree-view .icon{width:21px;}#divMainExplorer .tree-view img{position:relative;top:-2px;}#divMainCollapsePane{position:absolute;bottom:0;width:220px;height:40px;margin:0 0 0 30px;padding:5px 10px;color:#9ca1a6;font-size:13px;}#divMainCollapsePane i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapsePane:hover{color:#00b9eb;cursor:pointer;} \ No newline at end of file +body{margin:0;padding:30px 0 0 250px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;white-space:nowrap;}#divMainHeader .hdr-btn{height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;display:inline-block;font-size:13px;text-decoration:none;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenuBtn{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;}#divMainUserMenu{float:right;height:30px;}#lblMainNormalViewBtn{position:fixed;top:0;right:0;height:30px;padding:0 10px;background-color:#fff;display:none;line-height:30px;opacity:.5;cursor:pointer;color:#9ca1a6;font-size:13px;}#lblMainNormalViewBtn:hover{color:#00b9eb;opacity:1;}#divMainLeftPane{position:fixed;left:0;top:30px;width:250px;min-height:300px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 40px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainLeftPane .tree-view .node:hover{background-color:#191e23;color:#00b9eb;}#divMainLeftPane .tree-view .node.selected{background-color:#0073aa;color:#fff;}#divMainLeftPane .tree-view .node.disabled{color:#9ca1a6;cursor:default;}#divMainLeftPane .tree-view .expander::before{font-size:10px;}#divMainMenu .node[data-level="1"],#divMainMenu .node[data-level="2"],#divMainMenu .node[data-level="3"],#divMainMenu .node[data-level="4"],#divMainMenu .node[data-level="5"]{color:#9ca1a6;font-size:13px;}#divMainMenu .tree-view .node{padding:5px 10px 5px 10px;}#divMainMenu .tree-view .expander.left{display:none;width:0;}#divMainMenu .tree-view .expander.right{display:table-cell;width:20px;}#divMainExplorer{font-size:13px;line-height:13px;}#divMainExplorer .tree-view .node{padding:7px 5px 3px 10px;}#divMainExplorer .tree-view .icon{width:21px;}#divMainExplorer .tree-view img{position:relative;top:-2px;}#divMainCollapsePane{position:absolute;bottom:0;width:220px;height:40px;margin:0 0 0 30px;padding:5px 10px;color:#9ca1a6;font-size:13px;}#divMainCollapsePane i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapsePane:hover{color:#00b9eb;cursor:pointer;} \ No newline at end of file From 90b2e37bfa8a5b2bbe37f4a5c3d1fb830ffa5218 Mon Sep 17 00:00:00 2001 From: 2mik Date: Wed, 11 May 2016 19:57:30 +0300 Subject: [PATCH 030/382] ScadaWeb5: no view web form --- ScadaWeb/ScadaWebShell5Beta/NoView.aspx | 18 ++++++++ ScadaWeb/ScadaWebShell5Beta/NoView.aspx.cs | 43 +++++++++++++++++++ .../NoView.aspx.designer.cs | 33 ++++++++++++++ .../ScadaWebShell5Beta.csproj | 15 +++++++ ScadaWeb/ScadaWebShell5Beta/View.aspx.cs | 8 +--- .../ScadaWebShell5Beta/compilerconfig.json | 4 ++ .../ScadaWebShell5Beta/css/globalvar.less | 3 +- ScadaWeb/ScadaWebShell5Beta/css/noview.css | 18 ++++++++ ScadaWeb/ScadaWebShell5Beta/css/noview.less | 20 +++++++++ .../ScadaWebShell5Beta/css/noview.min.css | 1 + .../lang/ScadaWeb.en-GB.xml | 4 ++ .../lang/ScadaWeb.ru-RU.xml | 4 ++ 12 files changed, 164 insertions(+), 7 deletions(-) create mode 100644 ScadaWeb/ScadaWebShell5Beta/NoView.aspx create mode 100644 ScadaWeb/ScadaWebShell5Beta/NoView.aspx.cs create mode 100644 ScadaWeb/ScadaWebShell5Beta/NoView.aspx.designer.cs create mode 100644 ScadaWeb/ScadaWebShell5Beta/css/noview.css create mode 100644 ScadaWeb/ScadaWebShell5Beta/css/noview.less create mode 100644 ScadaWeb/ScadaWebShell5Beta/css/noview.min.css diff --git a/ScadaWeb/ScadaWebShell5Beta/NoView.aspx b/ScadaWeb/ScadaWebShell5Beta/NoView.aspx new file mode 100644 index 000000000..384521011 --- /dev/null +++ b/ScadaWeb/ScadaWebShell5Beta/NoView.aspx @@ -0,0 +1,18 @@ +<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="NoView.aspx.cs" Inherits="Scada.Web.WFrmNoView" %> + + + + + + No View - Rapid SCADA + + + + + +
+ +
+ + + diff --git a/ScadaWeb/ScadaWebShell5Beta/NoView.aspx.cs b/ScadaWeb/ScadaWebShell5Beta/NoView.aspx.cs new file mode 100644 index 000000000..5ea33218a --- /dev/null +++ b/ScadaWeb/ScadaWebShell5Beta/NoView.aspx.cs @@ -0,0 +1,43 @@ +/* + * Copyright 2016 Mikhail Shiryaev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Product : Rapid SCADA + * Module : SCADA-Web + * Summary : Nonexistent view web form + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + */ + +using Scada.UI; +using System; + +namespace Scada.Web +{ + /// + /// Nonexistent view web form + /// Веб-форма несуществующего представления + /// + public partial class WFrmNoView : System.Web.UI.Page + { + protected void Page_Load(object sender, EventArgs e) + { + // перевод веб-страницы + Translator.TranslatePage(Page, "Scada.Web.WFrmNoView"); + } + } +} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/NoView.aspx.designer.cs b/ScadaWeb/ScadaWebShell5Beta/NoView.aspx.designer.cs new file mode 100644 index 000000000..82c43fb3c --- /dev/null +++ b/ScadaWeb/ScadaWebShell5Beta/NoView.aspx.designer.cs @@ -0,0 +1,33 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Scada.Web { + + + public partial class WFrmNoView { + + /// + /// frmNoView control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlForm frmNoView; + + /// + /// lblViewNotExist control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblViewNotExist; + } +} diff --git a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj index a5acc8ddf..32c335186 100644 --- a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj +++ b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj @@ -88,6 +88,12 @@ mastermain.css + + noview.less + + + noview.css + treeview.less @@ -141,6 +147,7 @@ + @@ -169,6 +176,13 @@ MasterMain.Master + + NoView.aspx + ASPXCodeBehind + + + NoView.aspx + View.aspx @@ -245,6 +259,7 @@ + Web.config diff --git a/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs b/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs index aa9a289f3..8dce574e4 100644 --- a/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs +++ b/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs @@ -60,12 +60,8 @@ protected void Page_Load(object sender, EventArgs e) userData.UserViews.GetViewUrl(viewID) : userData.UserViews.GetFirstViewUrl(); - if (string.IsNullOrEmpty(viewUrl)) - // переход на страницу отсутствующего представления - Response.Redirect(UrlTemplates.NoView); - else - // добавление скрипта загрузки представления - AddLoadViewScript(viewUrl); + // добавление скрипта загрузки представления + AddLoadViewScript(string.IsNullOrEmpty(viewUrl) ? UrlTemplates.NoView : viewUrl); } } } diff --git a/ScadaWeb/ScadaWebShell5Beta/compilerconfig.json b/ScadaWeb/ScadaWebShell5Beta/compilerconfig.json index 56fa3e74e..c522e0fb5 100644 --- a/ScadaWeb/ScadaWebShell5Beta/compilerconfig.json +++ b/ScadaWeb/ScadaWebShell5Beta/compilerconfig.json @@ -18,5 +18,9 @@ { "outputFile": "css/view.css", "inputFile": "css/view.less" + }, + { + "outputFile": "css/noview.css", + "inputFile": "css/noview.less" } ] \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less b/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less index 3cb9a0df6..a47d51ec0 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less @@ -1,4 +1,5 @@ -@content-back-color: #f1f1f1; +@content-fore-color: #444; +@content-back-color: #f1f1f1; @panel-border-color: #eee; @default-font-family: 'Open Sans', sans-serif; /*Verdana, Geneva, Tahoma, sans-serif;*/ @default-font-size: 12px; \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/noview.css b/ScadaWeb/ScadaWebShell5Beta/css/noview.css new file mode 100644 index 000000000..ca2ab08c3 --- /dev/null +++ b/ScadaWeb/ScadaWebShell5Beta/css/noview.css @@ -0,0 +1,18 @@ +/*Verdana, Geneva, Tahoma, sans-serif;*/ +body { + margin: 0; + padding: 0; + background-color: #f1f1f1; + color: #444; + font-family: 'Open Sans', sans-serif; + font-size: 17px; +} +#divViewNotExist { + position: absolute; + padding: 10px 20px; + width: 100%; + top: 50%; + transform: translateY(-50%); + text-align: center; + box-sizing: border-box; +} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/noview.less b/ScadaWeb/ScadaWebShell5Beta/css/noview.less new file mode 100644 index 000000000..7e1e420c4 --- /dev/null +++ b/ScadaWeb/ScadaWebShell5Beta/css/noview.less @@ -0,0 +1,20 @@ +@import "globalvar.less"; + +body { + margin: 0; + padding: 0; + background-color: @content-back-color; + color: @content-fore-color; + font-family: @default-font-family; + font-size: 17px; +} + +#divViewNotExist { + position: absolute; + padding: 10px 20px; + width: 100%; + top: 50%; + transform: translateY(-50%); + text-align: center; + box-sizing: border-box; +} diff --git a/ScadaWeb/ScadaWebShell5Beta/css/noview.min.css b/ScadaWeb/ScadaWebShell5Beta/css/noview.min.css new file mode 100644 index 000000000..be06413eb --- /dev/null +++ b/ScadaWeb/ScadaWebShell5Beta/css/noview.min.css @@ -0,0 +1 @@ +body{margin:0;padding:0;background-color:#f1f1f1;color:#444;font-family:'Open Sans',sans-serif;font-size:17px;}#divViewNotExist{position:absolute;padding:10px 20px;width:100%;top:50%;transform:translateY(-50%);text-align:center;box-sizing:border-box;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml index 5144c1e8a..52f3f27aa 100644 --- a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml +++ b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml @@ -32,6 +32,10 @@ Remember me Login + + No View - Rapid SCADA + The requested view does not exist or you have insufficient rights to access it. + Reports Configuration diff --git a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml index 77b40afac..30fe4f882 100644 --- a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml +++ b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml @@ -32,6 +32,10 @@ Запомнить меня Войти + + Представление не существует - Rapid SCADA + Запрошенное представление не существует или у Вас недостаточно прав для доступа к нему. + Отчёты Конфигурация From dea8503df870042ff3b3198cdd5c8442b93d4647 Mon Sep 17 00:00:00 2001 From: 2mik Date: Wed, 11 May 2016 21:23:49 +0300 Subject: [PATCH 031/382] ScadaWeb5: view title --- ScadaWeb/ScadaWebShell5Beta/js/view.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ScadaWeb/ScadaWebShell5Beta/js/view.js b/ScadaWeb/ScadaWebShell5Beta/js/view.js index d96c73193..72dcf3059 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/view.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/view.js @@ -7,7 +7,14 @@ scada.view = { // Load view load: function (url) { document.title = this.initialPageTitle; - $("#frameView").attr("src", url); + var frameView = $("#frameView"); + + frameView + .load(function () { + // set the page title the same as the frame title + document.title = frameView[0].contentWindow.document.title; + }) + .attr("src", url); }, // Set page title From e952271784dc5ecca9cb7f3b7660f558ab126079 Mon Sep 17 00:00:00 2001 From: 2mik Date: Wed, 11 May 2016 23:42:03 +0300 Subject: [PATCH 032/382] ScadaWeb5: displaying --- ScadaWeb/ScadaWebShell5Beta/MasterMain.Master | 2 +- .../ScadaWebShell5Beta/css/mastermain.css | 2 +- .../ScadaWebShell5Beta/css/mastermain.less | 2 +- .../ScadaWebShell5Beta/css/mastermain.min.css | 2 +- ScadaWeb/ScadaWebShell5Beta/js/mastermain.js | 41 +++++++++++++------ 5 files changed, 33 insertions(+), 16 deletions(-) diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master index 69a95c6eb..28f493f9d 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master @@ -22,7 +22,7 @@ diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css index 6b1b561f5..d0941e428 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css @@ -94,7 +94,7 @@ a:active { left: 0; top: 30px; width: 250px; - min-height: 300px; + min-height: 250px; background-color: #23282d; color: #eee; font-size: 14px; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less index 5817d3b17..da2a423db 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less @@ -114,7 +114,7 @@ a:active { left: 0; top: @header-heigth; width: @left-pane-width; - min-height: 300px; + min-height: 250px; background-color: @menu-back-color; color: @menu-fore-color; font-size: 14px; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css index 04627c9d6..8e772baa1 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 250px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;white-space:nowrap;}#divMainHeader .hdr-btn{height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;display:inline-block;font-size:13px;text-decoration:none;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenuBtn{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;}#divMainUserMenu{float:right;height:30px;}#lblMainNormalViewBtn{position:fixed;top:0;right:0;height:30px;padding:0 10px;background-color:#fff;display:none;line-height:30px;opacity:.5;cursor:pointer;color:#9ca1a6;font-size:13px;}#lblMainNormalViewBtn:hover{color:#00b9eb;opacity:1;}#divMainLeftPane{position:fixed;left:0;top:30px;width:250px;min-height:300px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 40px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainLeftPane .tree-view .node:hover{background-color:#191e23;color:#00b9eb;}#divMainLeftPane .tree-view .node.selected{background-color:#0073aa;color:#fff;}#divMainLeftPane .tree-view .node.disabled{color:#9ca1a6;cursor:default;}#divMainLeftPane .tree-view .expander::before{font-size:10px;}#divMainMenu .node[data-level="1"],#divMainMenu .node[data-level="2"],#divMainMenu .node[data-level="3"],#divMainMenu .node[data-level="4"],#divMainMenu .node[data-level="5"]{color:#9ca1a6;font-size:13px;}#divMainMenu .tree-view .node{padding:5px 10px 5px 10px;}#divMainMenu .tree-view .expander.left{display:none;width:0;}#divMainMenu .tree-view .expander.right{display:table-cell;width:20px;}#divMainExplorer{font-size:13px;line-height:13px;}#divMainExplorer .tree-view .node{padding:7px 5px 3px 10px;}#divMainExplorer .tree-view .icon{width:21px;}#divMainExplorer .tree-view img{position:relative;top:-2px;}#divMainCollapsePane{position:absolute;bottom:0;width:220px;height:40px;margin:0 0 0 30px;padding:5px 10px;color:#9ca1a6;font-size:13px;}#divMainCollapsePane i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapsePane:hover{color:#00b9eb;cursor:pointer;} \ No newline at end of file +body{margin:0;padding:30px 0 0 250px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;white-space:nowrap;}#divMainHeader .hdr-btn{height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;display:inline-block;font-size:13px;text-decoration:none;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenuBtn{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;}#divMainUserMenu{float:right;height:30px;}#lblMainNormalViewBtn{position:fixed;top:0;right:0;height:30px;padding:0 10px;background-color:#fff;display:none;line-height:30px;opacity:.5;cursor:pointer;color:#9ca1a6;font-size:13px;}#lblMainNormalViewBtn:hover{color:#00b9eb;opacity:1;}#divMainLeftPane{position:fixed;left:0;top:30px;width:250px;min-height:250px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 40px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainLeftPane .tree-view .node:hover{background-color:#191e23;color:#00b9eb;}#divMainLeftPane .tree-view .node.selected{background-color:#0073aa;color:#fff;}#divMainLeftPane .tree-view .node.disabled{color:#9ca1a6;cursor:default;}#divMainLeftPane .tree-view .expander::before{font-size:10px;}#divMainMenu .node[data-level="1"],#divMainMenu .node[data-level="2"],#divMainMenu .node[data-level="3"],#divMainMenu .node[data-level="4"],#divMainMenu .node[data-level="5"]{color:#9ca1a6;font-size:13px;}#divMainMenu .tree-view .node{padding:5px 10px 5px 10px;}#divMainMenu .tree-view .expander.left{display:none;width:0;}#divMainMenu .tree-view .expander.right{display:table-cell;width:20px;}#divMainExplorer{font-size:13px;line-height:13px;}#divMainExplorer .tree-view .node{padding:7px 5px 3px 10px;}#divMainExplorer .tree-view .icon{width:21px;}#divMainExplorer .tree-view img{position:relative;top:-2px;}#divMainCollapsePane{position:absolute;bottom:0;width:220px;height:40px;margin:0 0 0 30px;padding:5px 10px;color:#9ca1a6;font-size:13px;}#divMainCollapsePane i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapsePane:hover{color:#00b9eb;cursor:pointer;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js index 8d1b54759..456e1a9b2 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js @@ -5,13 +5,17 @@ scada.masterMain = { // The left pane, which displays tool windows, is expanded leftPaneExpanded: true, + // Fullsreen mode is active + isFullscreen: false, + // Update layout of the master page updateLayout: function () { var divMainHeader = $("#divMainHeader"); var divMainLeftPane = $("#divMainLeftPane"); var divMainTabs = $("#divMainTabs"); - var paneH = $(window).height() - divMainHeader.outerHeight(); + var headerH = divMainHeader.css("display") == "none" ? 0 : divMainHeader.outerHeight(); + var paneH = $(window).height() - headerH; divMainLeftPane.outerHeight(paneH); divMainTabs.outerWidth(paneH); $("#divMainContent").outerHeight(paneH); @@ -96,22 +100,34 @@ scada.masterMain = { // Hide all the menus and switch browser to fullscreen mode switchToFullscreen: function () { - if (this.leftPaneExpanded) { - this.hideLeftPane(); + if (!this.isFullscreen) { + this.isFullscreen = true; + + if (this.leftPaneExpanded) { + this.hideLeftPane(); + } + this.hideHeader(); + $("#lblMainNormalViewBtn").css("display", "inline-block"); + + scada.utils.requestFullscreen(); + scada.masterMain.updateLayout(); } - this.hideHeader(); - $("#lblMainNormalViewBtn").css("display", "inline-block"); - scada.utils.requestFullscreen(); }, // Show all the menus and exit browser fullscreen mode switchToNormalView: function () { - $("#lblMainNormalViewBtn").css("display", "none"); - if (this.leftPaneExpanded) { - this.showLeftPane(); + if (this.isFullscreen) { + this.isFullscreen = false; + + $("#lblMainNormalViewBtn").css("display", "none"); + this.showHeader(); + if (this.leftPaneExpanded) { + this.showLeftPane(); + } + + scada.utils.exitFullscreen(); + scada.masterMain.updateLayout(); } - this.showHeader(); - scada.utils.exitFullscreen(); }, // Load view without reloading the whole page if possible @@ -159,7 +175,8 @@ $(document).ready(function () { scada.masterMain.switchToNormalView(); }); - // show menus if user exits fullscreen mode + // show menus if user exits fullscreen mode, + // in Chrome fires only if the mode is switched programmatically $(document).on("webkitfullscreenchange mozfullscreenchange fullscreenchange MSFullscreenChange", function () { if (!scada.utils.isFullscreen()) { scada.masterMain.switchToNormalView(); From 0a86d202489c214b539162a9b9313349d8da5904 Mon Sep 17 00:00:00 2001 From: 2mik Date: Thu, 12 May 2016 17:32:53 +0300 Subject: [PATCH 033/382] ScadaWeb5: WCF services and JS --- .../PlgScheme/plugins/Scheme/Scheme.aspx.cs | 36 ++++-- .../PlgScheme/plugins/Scheme/SchemeSvc.svc.cs | 32 +++-- .../plugins/Scheme/js/schememodel.js | 111 ++++++++++-------- ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs | 95 ++++++++------- .../ScadaWebCommon5Beta/DataTransferObject.cs | 74 ++++++++++++ .../ScadaWebCommon5Beta.csproj | 1 + .../ScadaWebShell5Beta/js/api/clientapi.js | 16 ++- ScadaWeb/ScadaWebShell5Beta/js/scadautils.js | 10 +- ScadaWeb/ScadaWebShell5Beta/js/view.js | 5 - 9 files changed, 257 insertions(+), 123 deletions(-) create mode 100644 ScadaWeb/ScadaWebCommon5Beta/DataTransferObject.cs diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.cs b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.cs index 861dcaaa6..4aeccf85d 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.cs @@ -26,6 +26,7 @@ //#define DEBUG_MODE using Scada.Scheme; +using Scada.Web.Shell; using System; namespace Scada.Web.Plugins.Scheme @@ -42,19 +43,20 @@ public partial class WFrmScheme : System.Web.UI.Page private const string DictName = "Scada.Web.Plugins.Scheme.WFrmScheme.Js"; // Переменные для вывода на веб-страницу - protected bool debugMode; // режим отладки - protected int viewID; // ид. представления - protected int refrRate; // частота обновления данных - protected string phrases; // локализованные фразы + protected bool debugMode; // режим отладки + protected int viewID; // ид. представления + protected int refrRate; // частота обновления данных + protected string phrases; // локализованные фразы - private AppData appData; // общие данные веб-приложения + private AppData appData; // общие данные веб-приложения + private UserData userData; // данные пользователя приложения - protected void Page_Load(object sender, EventArgs e) + /// + /// Установить или отключить режим отладки + /// + private void SetupDebugMode() { - appData = AppData.GetAppData(); - UserData userData = UserData.GetUserData(); - #if DEBUG_MODE debugMode = true; appData.Init(Server.MapPath("~")); @@ -64,8 +66,22 @@ protected void Page_Load(object sender, EventArgs e) #else debugMode = false; #endif + } + + protected void Page_Load(object sender, EventArgs e) + { + appData = AppData.GetAppData(); + userData = UserData.GetUserData(); + SetupDebugMode(); + + // получение ид. представления из параметров запроса + int.TryParse(Request.QueryString["viewID"], out viewID); + + // проверка прав на просмотр представления + if (!(userData.LoggedOn && userData.UserRights.GetViewRights(viewID).ViewRight)) + Response.Redirect(UrlTemplates.NoView); - int.TryParse(Request["viewID"], out viewID); + // подготовка данных для веб-страницы refrRate = userData.WebSettings.DataRefrRate; Localization.Dict dict; diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/SchemeSvc.svc.cs b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/SchemeSvc.svc.cs index f52132b0d..5950b6503 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/SchemeSvc.svc.cs +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/SchemeSvc.svc.cs @@ -45,7 +45,7 @@ public class SchemeSvc /// /// Базовый класс объекта для передачи схемы /// - private abstract class SchemeDTO + private abstract class SchemeDTO : DataTransferObject { /// /// Конструктор @@ -119,14 +119,14 @@ public ElementsDTO(int capacity = -1) } /// - /// Класс объекта для передачи изображения + /// Передаваемое изображение /// - private class ImageDTO + private class Image { /// /// Конструктор /// - public ImageDTO(SchemeView.Image image) + public Image(SchemeView.Image image) { Name = image.Name ?? ""; Data = Convert.ToBase64String(image.Data == null ? new byte[0] : image.Data, @@ -178,7 +178,7 @@ public ImagesDTO() : base() { EndOfImages = false; - Images = new List(); + Images = new List(); } /// @@ -188,7 +188,7 @@ public ImagesDTO() /// /// Получить изображения схемы /// - public List Images { get; private set; } + public List Images { get; private set; } } @@ -206,9 +206,19 @@ public ImagesDTO() private static readonly AppData AppData = AppData.GetAppData(); + /// + /// Получить объект для передачи данных, содержащий информацию об ошибке, в формате JSON + /// + private string GetErrorDtoJs(Exception ex) + { + return JsSerializer.Serialize(new DataTransferObject(false, ex.Message)); + } + + /// /// Получить свойства схемы /// + /// Возвращает SchemePropsDTO в формате в JSON [OperationContract] [WebGet] public string GetSchemeProps(int viewID, long viewStamp) @@ -245,13 +255,14 @@ public string GetSchemeProps(int viewID, long viewStamp) AppData.Log.WriteException(ex, Localization.UseRussian ? "Ошибка при получении свойств схемы с ид.={0}" : "Error getting the properties of the scheme with ID={0}", viewID); - return ""; + return GetErrorDtoJs(ex); } } /// /// Получить элементы схемы /// + /// Возвращает ElementsDTO в формате в JSON [OperationContract] [WebGet] public string GetElements(int viewID, long viewStamp, int startIndex, int count) @@ -280,13 +291,14 @@ public string GetElements(int viewID, long viewStamp, int startIndex, int count) AppData.Log.WriteException(ex, Localization.UseRussian ? "Ошибка при получении элементов схемы с ид.={0}" : "Error getting the elements of the scheme with ID={0}", viewID); - return ""; + return GetErrorDtoJs(ex); } } /// /// Получить изображения схемы /// + /// Возвращает ImagesDTO в формате в JSON [OperationContract] [WebGet] public string GetImages(int viewID, long viewStamp, int startIndex, int totalDataSize) @@ -308,7 +320,7 @@ public string GetImages(int viewID, long viewStamp, int startIndex, int totalDat { if (i >= startIndex) { - dto.Images.Add(new ImageDTO(image)); + dto.Images.Add(new Image(image)); if (image.Data != null) size += image.Data.Length; } @@ -329,7 +341,7 @@ public string GetImages(int viewID, long viewStamp, int startIndex, int totalDat AppData.Log.WriteException(ex, Localization.UseRussian ? "Ошибка при получении изображений схемы с ид.={0}" : "Error getting the images of the scheme with ID={0}", viewID); - return ""; + return GetErrorDtoJs(ex); } } } diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js index 7562a35cd..73107f276 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js @@ -101,17 +101,23 @@ scada.scheme.Scheme.prototype._loadSchemeProps = function (viewID, callback) { dataType: "json" }) .done(function (data, textStatus, jqXHR) { - if (data.d) { - scada.utils.logSuccessfulRequest(operation); - var parsedProps = thisScheme._parseSchemeProps(data.d); - if (parsedProps) { - thisScheme.loadState = scada.scheme.LoadStates.ELEMS_LOADING; - callback(true, false); + try { + var parsedData = $.parseJSON(data.d); + if (parsedData.Success) { + scada.utils.logSuccessfulRequest(operation); + if (thisScheme._obtainSchemeProps(parsedData)) { + thisScheme.loadState = scada.scheme.LoadStates.ELEMS_LOADING; + callback(true, false); + } else { + callback(false, false); + } } else { + scada.utils.logServiceError(operation, parsedData.ErrorMessage); callback(false, false); } - } else { - scada.utils.logServiceError(operation); + } + catch (ex) { + scada.utils.logServiceFormatError(operation); callback(false, false); } }) @@ -131,11 +137,9 @@ scada.scheme.Scheme.prototype._viewStampsMatched = function (viewStamp1, viewSta } }; -// Parse received scheme properties -scada.scheme.Scheme.prototype._parseSchemeProps = function (json) { +// Obtain received scheme properties +scada.scheme.Scheme.prototype._obtainSchemeProps = function (parsedProps) { try { - var parsedProps = $.parseJSON(json); - if (typeof parsedProps.ViewStamp === "undefined") { throw { message: "ViewStamp property is missing." }; } @@ -161,16 +165,19 @@ scada.scheme.Scheme.prototype._parseSchemeProps = function (json) { if (parsedProps.SchemeProps.Title) { document.title = parsedProps.SchemeProps.Title + " - Rapid SCADA"; + if (window.parent) { + window.parent.document.title = document.title; + } } - return parsedProps; + return true; } else { - return null; + return false; } } catch (ex) { - console.error(scada.utils.getCurTime() + " Error parsing scheme properties:", ex.message); - return null; + console.error(scada.utils.getCurTime() + " Error obtaining scheme properties:", ex.message); + return false; } }; @@ -190,19 +197,25 @@ scada.scheme.Scheme.prototype._loadElements = function (viewID, callback) { dataType: "json" }) .done(function (data, textStatus, jqXHR) { - if (data.d) { - scada.utils.logSuccessfulRequest(operation); - var parsedElems = thisScheme._parseElements(data.d); - if (parsedElems) { - if (parsedElems.EndOfElements) { - thisScheme.loadState = scada.scheme.LoadStates.IMAGES_LOADING; + try { + var parsedData = $.parseJSON(data.d); + if (parsedData.Success) { + scada.utils.logSuccessfulRequest(operation); + if (thisScheme._obtainElements(parsedData)) { + if (parsedData.EndOfElements) { + thisScheme.loadState = scada.scheme.LoadStates.IMAGES_LOADING; + } + callback(true, false); + } else { + callback(false, false); } - callback(true, false); } else { + scada.utils.logServiceError(operation, parsedData.ErrorMessage); callback(false, false); } - } else { - scada.utils.logServiceError(operation); + } + catch (ex) { + scada.utils.logServiceFormatError(operation); callback(false, false); } }) @@ -212,11 +225,9 @@ scada.scheme.Scheme.prototype._loadElements = function (viewID, callback) { }); } -// Parse received scheme elements -scada.scheme.Scheme.prototype._parseElements = function (json) { +// Obtain received scheme elements +scada.scheme.Scheme.prototype._obtainElements = function (parsedElems) { try { - var parsedElems = $.parseJSON(json); - if (typeof parsedElems.ViewStamp === "undefined") { throw { message: "ViewStamp property is missing." }; } @@ -232,14 +243,14 @@ scada.scheme.Scheme.prototype._parseElements = function (json) { if (this._viewStampsMatched(this.viewStamp, parsedElems.ViewStamp)) { this.viewStamp = parsedElems.ViewStamp; this._appendElements(parsedElems.Elements); - return parsedElems; + return true; } else { - return null; + return false; } } catch (ex) { - console.error(scada.utils.getCurTime() + " Error parsing scheme elements:", ex.message); - return null; + console.error(scada.utils.getCurTime() + " Error obtaining scheme elements:", ex.message); + return false; } }; @@ -285,21 +296,27 @@ scada.scheme.Scheme.prototype._loadImages = function (viewID, callback) { dataType: "json" }) .done(function (data, textStatus, jqXHR) { - if (data.d) { - scada.utils.logSuccessfulRequest(operation); - var parsedImages = thisScheme._parseImages(data.d); - if (parsedImages) { - if (parsedImages.EndOfImages) { - thisScheme.loadState = scada.scheme.LoadStates.COMPLETED; - callback(true, true); + try { + var parsedData = $.parseJSON(data.d); + if (parsedData.Success) { + scada.utils.logSuccessfulRequest(operation); + if (thisScheme._obtainImages(parsedData)) { + if (parsedData.EndOfImages) { + thisScheme.loadState = scada.scheme.LoadStates.COMPLETED; + callback(true, true); + } else { + callback(true, false); + } } else { - callback(true, false); + callback(false, false); } } else { + scada.utils.logServiceError(operation, parsedData.ErrorMessage); callback(false, false); } - } else { - scada.utils.logServiceError(operation); + } + catch (ex) { + scada.utils.logServiceFormatError(operation); callback(false, false); } }) @@ -309,11 +326,9 @@ scada.scheme.Scheme.prototype._loadImages = function (viewID, callback) { }); } -// Parse received scheme images -scada.scheme.Scheme.prototype._parseImages = function (json) { +// Obtain received scheme images +scada.scheme.Scheme.prototype._obtainImages = function (parsedImages) { try { - var parsedImages = $.parseJSON(json); - if (typeof parsedImages.ViewStamp === "undefined") { throw { message: "ViewStamp property is missing." }; } @@ -335,7 +350,7 @@ scada.scheme.Scheme.prototype._parseImages = function (json) { } } catch (ex) { - console.error(scada.utils.getCurTime() + " Error parsing scheme images:", ex.message); + console.error(scada.utils.getCurTime() + " Error obtaining scheme images:", ex.message); return null; } }; diff --git a/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs b/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs index 8aa815dd5..3d1e99354 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs @@ -16,7 +16,7 @@ * * Product : Rapid SCADA * Module : ScadaWebCommon - * Summary : WCF-service that provides the client API + * Summary : WCF service that provides the client API * * Author : Mikhail Shiryaev * Created : 2016 @@ -35,7 +35,7 @@ namespace Scada.Web { /// - /// WCF-service that provides the client API + /// WCF service that provides the client API /// WCF-сервис, обеспечивающий работу клиентского API /// [ServiceContract(Namespace = "")] @@ -43,14 +43,14 @@ namespace Scada.Web public class ClientApiSvc { /// - /// Класс объекта для передачи расширенных данных входого канала + /// Расширенные данные входого канала /// - private class CnlDataExtDTO + private class CnlDataExt { /// /// Конструктор /// - public CnlDataExtDTO(int cnlNum) + public CnlDataExt(int cnlNum) { CnlNum = cnlNum; Val = 0.0; @@ -107,10 +107,10 @@ public CnlDataExtDTO(int cnlNum) /// /// Получить расширенные текущие данные входных каналов /// - private CnlDataExtDTO[] GetCnlDataExtDTOs(IList cnlList) + private CnlDataExt[] GetCnlDataExtArr(IList cnlList) { int cnlCnt = cnlList.Count; - CnlDataExtDTO[] cnlDataDTOs = new CnlDataExtDTO[cnlCnt]; + CnlDataExt[] cnlDataExtArr = new CnlDataExt[cnlCnt]; DataAccess dataAccess = AppData.DataAccess; DateTime dataAge; @@ -123,13 +123,13 @@ private CnlDataExtDTO[] GetCnlDataExtDTOs(IList cnlList) for (int i = 0; i < cnlCnt; i++) { int cnlNum = cnlList[i]; - CnlDataExtDTO cnlDataDTO = new CnlDataExtDTO(cnlNum); - cnlDataDTOs[i] = cnlDataDTO; + CnlDataExt cnlDataExt = new CnlDataExt(cnlNum); + cnlDataExtArr[i] = cnlDataExt; SrezTableLight.CnlData cnlData; snapshot.GetCnlData(cnlNum, out cnlData); - cnlDataDTO.Val = cnlData.Val; - cnlDataDTO.Stat = cnlData.Stat; + cnlDataExt.Val = cnlData.Val; + cnlDataExt.Stat = cnlData.Stat; if (dataVisible) { @@ -138,25 +138,33 @@ private CnlDataExtDTO[] GetCnlDataExtDTOs(IList cnlList) string textWithUnit; DataFormatter.FormatCnlVal(cnlData.Val, cnlData.Stat, cnlProps, out text, out textWithUnit); - cnlDataDTO.Text = text; - cnlDataDTO.TextWithUnit = textWithUnit; - cnlDataDTO.Color = DataFormatter.GetCnlValColor(cnlData.Val, cnlData.Stat, cnlProps, + cnlDataExt.Text = text; + cnlDataExt.TextWithUnit = textWithUnit; + cnlDataExt.Color = DataFormatter.GetCnlValColor(cnlData.Val, cnlData.Stat, cnlProps, dataAccess.GetColorByStat); } else { - cnlDataDTO.Text = cnlDataDTO.TextWithUnit = emptyVal; + cnlDataExt.Text = cnlDataExt.TextWithUnit = emptyVal; } } - return cnlDataDTOs; + return cnlDataExtArr; + } + + /// + /// Получить объект для передачи данных, содержащий информацию об ошибке, в формате JSON + /// + private string GetErrorDtoJs(Exception ex) + { + return JsSerializer.Serialize(new DataTransferObject(false, ex.Message)); } /// /// Получить текущие данные входного канала /// - /// Возвращает SrezTableLight.CnlData, преобразованный в JSON + /// Возвращает SrezTableLight.CnlData, упакованный в DataTransferObject, в формате в JSON [OperationContract] [WebGet] public string GetCurCnlData(int cnlNum) @@ -165,21 +173,21 @@ public string GetCurCnlData(int cnlNum) { AppData.CheckLoggedOn(); SrezTableLight.CnlData cnlData = AppData.DataAccess.GetCurCnlData(cnlNum); - return JsSerializer.Serialize(cnlData); + return JsSerializer.Serialize(new DataTransferObject(cnlData)); } catch (Exception ex) { AppData.Log.WriteException(ex, Localization.UseRussian ? "Ошибка при получении текущих данных входного канала {0}" : "Error getting current data of the input channel {0}", cnlNum); - return ""; + return GetErrorDtoJs(ex); } } /// /// Получить расширенные текущие данные входного канала /// - /// Возвращает CnlDataExtDTO, преобразованный в JSON + /// Возвращает CnlDataExt, упакованный в DataTransferObject, в формате в JSON [OperationContract] [WebGet] public string GetCurCnlDataExt(int cnlNum) @@ -187,12 +195,12 @@ public string GetCurCnlDataExt(int cnlNum) try { AppData.CheckLoggedOn(); - CnlDataExtDTO cnlDataExtDTO = new CnlDataExtDTO(cnlNum); + CnlDataExt cnlDataExt = new CnlDataExt(cnlNum); DataAccess dataAccess = AppData.DataAccess; DateTime dataAge; SrezTableLight.CnlData cnlData = dataAccess.GetCurCnlData(cnlNum, out dataAge); - cnlDataExtDTO.Val = cnlData.Val; - cnlDataExtDTO.Stat = cnlData.Stat; + cnlDataExt.Val = cnlData.Val; + cnlDataExt.Stat = cnlData.Stat; string emptyVal; if (DataFormatter.CurDataVisible(dataAge, DateTime.Now, out emptyVal)) @@ -202,32 +210,32 @@ public string GetCurCnlDataExt(int cnlNum) string textWithUnit; DataFormatter.FormatCnlVal(cnlData.Val, cnlData.Stat, cnlProps, out text, out textWithUnit); - cnlDataExtDTO.Text = text; - cnlDataExtDTO.TextWithUnit = textWithUnit; - cnlDataExtDTO.Color = DataFormatter.GetCnlValColor(cnlData.Val, cnlData.Stat, cnlProps, + cnlDataExt.Text = text; + cnlDataExt.TextWithUnit = textWithUnit; + cnlDataExt.Color = DataFormatter.GetCnlValColor(cnlData.Val, cnlData.Stat, cnlProps, dataAccess.GetColorByStat); } else { - cnlDataExtDTO.Text = emptyVal; - cnlDataExtDTO.TextWithUnit = emptyVal; + cnlDataExt.Text = emptyVal; + cnlDataExt.TextWithUnit = emptyVal; } - return JsSerializer.Serialize(cnlDataExtDTO); + return JsSerializer.Serialize(new DataTransferObject(cnlDataExt)); } catch (Exception ex) { AppData.Log.WriteException(ex, Localization.UseRussian ? "Ошибка при получении расширенных текущих данных входного канала {0}" : "Error getting extended current data of the input channel {0}", cnlNum); - return ""; + return GetErrorDtoJs(ex); } } /// /// Получить расширенные текущие данные заданных входных каналов /// - /// Возвращает CnlDataExtDTO[], преобразованный в JSON + /// Возвращает CnlDataExt[], упакованный в DataTransferObject, в формате в JSON [OperationContract] [WebGet] public string GetCurCnlDataExtByCnlNums(string cnlNums) @@ -236,22 +244,22 @@ public string GetCurCnlDataExtByCnlNums(string cnlNums) { AppData.CheckLoggedOn(); int[] cnlNumArr = WebUtils.QueryParamToIntArray(cnlNums); - CnlDataExtDTO[] cnlDataDTOs = GetCnlDataExtDTOs(cnlNumArr); - return JsSerializer.Serialize(cnlDataDTOs); + CnlDataExt[] cnlDataDTOs = GetCnlDataExtArr(cnlNumArr); + return JsSerializer.Serialize(new DataTransferObject(cnlDataDTOs)); } catch (Exception ex) { AppData.Log.WriteException(ex, Localization.UseRussian ? "Ошибка при получении расширенных текущих данных заданных входных каналов" : "Error getting extended current data of the specified input channels"); - return ""; + return GetErrorDtoJs(ex); } } /// /// Получить расширенные текущие данные входных каналов представления /// - /// Возвращает CnlDataExtDTO[], преобразованный в JSON. + /// Возвращает CnlDataExt[], упакованный в DataTransferObject, в формате в JSON. /// Представление должно быть уже загружено в кеш (для ускорения работы метода) [OperationContract] [WebGet] @@ -260,7 +268,7 @@ public string GetCurCnlDataExtByView(int viewID) try { AppData.CheckLoggedOn(); - CnlDataExtDTO[] cnlDataDTOs; + CnlDataExt[] cnlDataDTOs; BaseView view = AppData.ViewCache.GetViewFromCache(viewID); if (view == null) @@ -268,42 +276,43 @@ public string GetCurCnlDataExtByView(int viewID) AppData.Log.WriteError(string.Format(Localization.UseRussian ? "Не удалось получить представление с ид.={0} из кеша" : "Unable to get view with ID={0} from the cache", viewID)); - cnlDataDTOs = new CnlDataExtDTO[0]; + cnlDataDTOs = new CnlDataExt[0]; } else { - cnlDataDTOs = GetCnlDataExtDTOs(view.CnlList); + cnlDataDTOs = GetCnlDataExtArr(view.CnlList); } - return JsSerializer.Serialize(cnlDataDTOs); + return JsSerializer.Serialize(new DataTransferObject(cnlDataDTOs)); } catch (Exception ex) { AppData.Log.WriteException(ex, Localization.UseRussian ? "Ошибка при получении расширенных текущих данных входных каналов предсталения с ид.={0}" : "Error getting extended current input channel data of the view with id={0}", viewID); - return ""; + return GetErrorDtoJs(ex); } } /// /// Получить метку представления из кеша /// - /// Возвращает long, преобразованный в JSON + /// Возвращает long, упакованный в DataTransferObject, в формате в JSON public string GetViewStamp(int viewID) { try { AppData.CheckLoggedOn(); BaseView view = AppData.ViewCache.GetViewFromCache(viewID); - return JsSerializer.Serialize(view == null ? 0 : view.Stamp); + long stamp = view == null ? 0 : view.Stamp; + return JsSerializer.Serialize(new DataTransferObject(stamp)); } catch (Exception ex) { AppData.Log.WriteException(ex, Localization.UseRussian ? "Ошибка при получении метки предсталения с ид.={0} из кеша" : "Error getting stamp of the view with id={0} from the cache", viewID); - return ""; + return GetErrorDtoJs(ex); } } } diff --git a/ScadaWeb/ScadaWebCommon5Beta/DataTransferObject.cs b/ScadaWeb/ScadaWebCommon5Beta/DataTransferObject.cs new file mode 100644 index 000000000..2ff14c7d2 --- /dev/null +++ b/ScadaWeb/ScadaWebCommon5Beta/DataTransferObject.cs @@ -0,0 +1,74 @@ +/* + * Copyright 2016 Mikhail Shiryaev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Product : Rapid SCADA + * Module : ScadaWebCommon + * Summary : Data transfer object for WCF services + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + */ + +namespace Scada.Web +{ + /// + /// Data transfer object for WCF services + /// Объект для передачи данных WCF-сервисами + /// + public class DataTransferObject + { + /// + /// Конструктор + /// + public DataTransferObject() + : this(true, "") + { + } + + public DataTransferObject(object data) + : this(true, "") + { + Data = data; + } + + /// + /// Конструктор + /// + public DataTransferObject(bool success, string errorMessage) + { + Success = success; + ErrorMessage = errorMessage; + Data = null; + } + + + /// + /// Получить или установить признак, что объект получен успешно + /// + public bool Success { get; set; } + + /// + /// Получить или установить сообщение об ошибке + /// + public string ErrorMessage { get; set; } + + /// + /// Получить или установить данные, запрашиваемые клиентом + /// + public object Data { get; set; } + } +} diff --git a/ScadaWeb/ScadaWebCommon5Beta/ScadaWebCommon5Beta.csproj b/ScadaWeb/ScadaWebCommon5Beta/ScadaWebCommon5Beta.csproj index 5b186a6c4..6cacc3e76 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/ScadaWebCommon5Beta.csproj +++ b/ScadaWeb/ScadaWebCommon5Beta/ScadaWebCommon5Beta.csproj @@ -49,6 +49,7 @@ + diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js b/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js index f0f03c668..070cd58d0 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js @@ -54,12 +54,18 @@ scada.clientAPI = { dataType: "json" }) .done(function (data, textStatus, jqXHR) { - if (data.d) { - scada.utils.logSuccessfulRequest(operation/*, data*/); + try { var parsedData = $.parseJSON(data.d); - callback(true, parsedData); - } else { - scada.utils.logServiceError(operation); + if (parsedData.Success) { + scada.utils.logSuccessfulRequest(operation/*, data*/); + callback(true, parsedData.Data ? parsedData.Data : parsedData); + } else { + scada.utils.logServiceError(operation, parsedData.ErrorMessage); + callback(false, errorResult); + } + } + catch (ex) { + scada.utils.logServiceFormatError(operation); callback(false, errorResult); } }) diff --git a/ScadaWeb/ScadaWebShell5Beta/js/scadautils.js b/ScadaWeb/ScadaWebShell5Beta/js/scadautils.js index 669346cc0..9701e5859 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/scadautils.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/scadautils.js @@ -24,8 +24,14 @@ scada.utils = { }, // Write information about the internal service error to console - logServiceError: function (operation) { - console.error(this.getCurTime() + " Request '" + operation + "' returns empty data. Internal service error"); + logServiceError: function (operation, opt_message) { + console.error(this.getCurTime() + " Request '" + operation + "' reports internal service error" + + (opt_message ? ": " + opt_message : "")); + }, + + // Write information about the internal service error to console + logServiceFormatError: function (operation) { + console.error(this.getCurTime() + " Request '" + operation + "' returns data in incorrect format"); }, // Write information about the failed request to console diff --git a/ScadaWeb/ScadaWebShell5Beta/js/view.js b/ScadaWeb/ScadaWebShell5Beta/js/view.js index 72dcf3059..b716ca31a 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/view.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/view.js @@ -15,11 +15,6 @@ scada.view = { document.title = frameView[0].contentWindow.document.title; }) .attr("src", url); - }, - - // Set page title - setPageTitle: function (viewTitle) { - document.title = viewTitle + " - Rapid SCADA"; } }; From c9341c536eb66239b12e7c60b853290a507dff9e Mon Sep 17 00:00:00 2001 From: 2mik Date: Fri, 13 May 2016 11:24:03 +0300 Subject: [PATCH 034/382] PlgConfig: active users list --- .../PlgConfig/AppCode/PlgConfigSpec.cs | 8 ++++++- .../OpenPlugins/PlgConfig/PlgConfig.csproj | 8 +++++++ .../PlgConfig/plugins/Config/ActiveUsers.aspx | 15 ++++++++++++ .../plugins/Config/ActiveUsers.aspx.cs | 16 +++++++++++++ .../Config/ActiveUsers.aspx.designer.cs | 24 +++++++++++++++++++ .../PlgConfig/plugins/Config/WebConfig.aspx | 2 +- .../plugins/Config/WebConfig.aspx.cs | 9 ++----- .../plugins/Config/WebConfig.aspx.designer.cs | 12 ++++------ ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs | 17 ++++++------- .../ScadaWebCommon5Beta/Shell/MenuItem.cs | 4 +++- .../Shell/StandardMenuItems.cs | 8 +++++-- ScadaWeb/ScadaWebCommon5Beta/UserMonitor.cs | 1 - ScadaWeb/ScadaWebCommon5Beta/WebPhrases.cs | 3 +++ .../lang/ScadaWeb.en-GB.xml | 1 + .../lang/ScadaWeb.ru-RU.xml | 1 + 15 files changed, 99 insertions(+), 30 deletions(-) create mode 100644 ScadaWeb/OpenPlugins/PlgConfig/plugins/Config/ActiveUsers.aspx create mode 100644 ScadaWeb/OpenPlugins/PlgConfig/plugins/Config/ActiveUsers.aspx.cs create mode 100644 ScadaWeb/OpenPlugins/PlgConfig/plugins/Config/ActiveUsers.aspx.designer.cs diff --git a/ScadaWeb/OpenPlugins/PlgConfig/AppCode/PlgConfigSpec.cs b/ScadaWeb/OpenPlugins/PlgConfig/AppCode/PlgConfigSpec.cs index e1ce7bbdc..2390ab7cd 100644 --- a/ScadaWeb/OpenPlugins/PlgConfig/AppCode/PlgConfigSpec.cs +++ b/ScadaWeb/OpenPlugins/PlgConfig/AppCode/PlgConfigSpec.cs @@ -80,9 +80,15 @@ public override List GetMenuItems(UserData userData) if (userData.UserRights.ConfigRight) { List menuItems = new List(); + + MenuItem adminMenuItem = MenuItem.FromStandardMenuItem(StandardMenuItems.Admin); + adminMenuItem.Subitems.Add(new MenuItem("Active users", "~/plugins/Config/ActiveUsers.aspx")); + menuItems.Add(adminMenuItem); + MenuItem configMenuItem = MenuItem.FromStandardMenuItem(StandardMenuItems.Config); - configMenuItem.Subitems.Add(new MenuItem("Web application", "~/plugins/Config/WebConfig.aspx", 100)); + configMenuItem.Subitems.Add(new MenuItem("Web application", "~/plugins/Config/WebConfig.aspx")); menuItems.Add(configMenuItem); + return menuItems; } else diff --git a/ScadaWeb/OpenPlugins/PlgConfig/PlgConfig.csproj b/ScadaWeb/OpenPlugins/PlgConfig/PlgConfig.csproj index e10f9f6c7..c99b2c9a4 100644 --- a/ScadaWeb/OpenPlugins/PlgConfig/PlgConfig.csproj +++ b/ScadaWeb/OpenPlugins/PlgConfig/PlgConfig.csproj @@ -59,11 +59,19 @@ + + + ActiveUsers.aspx + ASPXCodeBehind + + + ActiveUsers.aspx + WebConfig.aspx ASPXCodeBehind diff --git a/ScadaWeb/OpenPlugins/PlgConfig/plugins/Config/ActiveUsers.aspx b/ScadaWeb/OpenPlugins/PlgConfig/plugins/Config/ActiveUsers.aspx new file mode 100644 index 000000000..0a5b0527a --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgConfig/plugins/Config/ActiveUsers.aspx @@ -0,0 +1,15 @@ +<%@ Page Title="" Language="C#" MasterPageFile="~/MasterMain.Master" AutoEventWireup="true" CodeBehind="ActiveUsers.aspx.cs" Inherits="Scada.Web.Plugins.Config.WFrmActiveUsers" %> + + + + + +
+ IpAddress: <%#: Eval("IpAddress") %>, + SessionID: <%#: Eval("SessionID") %>, + UserName: <%#: Eval("UserName") %>, + LogonDT: <%#: Eval("LogonDT") %> +
+
+
+
diff --git a/ScadaWeb/OpenPlugins/PlgConfig/plugins/Config/ActiveUsers.aspx.cs b/ScadaWeb/OpenPlugins/PlgConfig/plugins/Config/ActiveUsers.aspx.cs new file mode 100644 index 000000000..748a27e86 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgConfig/plugins/Config/ActiveUsers.aspx.cs @@ -0,0 +1,16 @@ +using System; + +namespace Scada.Web.Plugins.Config +{ + public partial class WFrmActiveUsers : System.Web.UI.Page + { + protected void Page_Load(object sender, EventArgs e) + { + AppData appData = AppData.GetAppData(); + UserData[] activeUsers = appData.UserMonitor.GetActiveUsers(); + + repActiveUsers.DataSource = activeUsers; + repActiveUsers.DataBind(); + } + } +} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgConfig/plugins/Config/ActiveUsers.aspx.designer.cs b/ScadaWeb/OpenPlugins/PlgConfig/plugins/Config/ActiveUsers.aspx.designer.cs new file mode 100644 index 000000000..8c5e85c6b --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgConfig/plugins/Config/ActiveUsers.aspx.designer.cs @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Scada.Web.Plugins.Config { + + + public partial class WFrmActiveUsers { + + /// + /// repActiveUsers control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Repeater repActiveUsers; + } +} diff --git a/ScadaWeb/OpenPlugins/PlgConfig/plugins/Config/WebConfig.aspx b/ScadaWeb/OpenPlugins/PlgConfig/plugins/Config/WebConfig.aspx index 3ace9cefc..0293c9334 100644 --- a/ScadaWeb/OpenPlugins/PlgConfig/plugins/Config/WebConfig.aspx +++ b/ScadaWeb/OpenPlugins/PlgConfig/plugins/Config/WebConfig.aspx @@ -1,4 +1,4 @@ -<%@ Page Title="" Language="C#" MasterPageFile="~/MasterMain.Master" AutoEventWireup="true" CodeBehind="WebConfig.aspx.cs" Inherits="Scada.Web.plugins.Config.WebConfig" %> +<%@ Page Title="" Language="C#" MasterPageFile="~/MasterMain.Master" AutoEventWireup="true" CodeBehind="WebConfig.aspx.cs" Inherits="Scada.Web.Plugins.Config.WFrmWebConfig" %> diff --git a/ScadaWeb/OpenPlugins/PlgConfig/plugins/Config/WebConfig.aspx.cs b/ScadaWeb/OpenPlugins/PlgConfig/plugins/Config/WebConfig.aspx.cs index 168e7b3e8..903116815 100644 --- a/ScadaWeb/OpenPlugins/PlgConfig/plugins/Config/WebConfig.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgConfig/plugins/Config/WebConfig.aspx.cs @@ -1,13 +1,8 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using System.Web.UI; -using System.Web.UI.WebControls; -namespace Scada.Web.plugins.Config +namespace Scada.Web.Plugins.Config { - public partial class WebConfig : System.Web.UI.Page + public partial class WFrmWebConfig : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { diff --git a/ScadaWeb/OpenPlugins/PlgConfig/plugins/Config/WebConfig.aspx.designer.cs b/ScadaWeb/OpenPlugins/PlgConfig/plugins/Config/WebConfig.aspx.designer.cs index 34f89ad6a..36ab5b20d 100644 --- a/ScadaWeb/OpenPlugins/PlgConfig/plugins/Config/WebConfig.aspx.designer.cs +++ b/ScadaWeb/OpenPlugins/PlgConfig/plugins/Config/WebConfig.aspx.designer.cs @@ -3,15 +3,13 @@ // This code was generated by a tool. // // Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. +// the code is regenerated. // //------------------------------------------------------------------------------ -namespace Scada.Web.plugins.Config -{ - - - public partial class WebConfig - { +namespace Scada.Web.Plugins.Config { + + + public partial class WFrmWebConfig { } } diff --git a/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs b/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs index 3d1e99354..90ff84c75 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs @@ -244,8 +244,8 @@ public string GetCurCnlDataExtByCnlNums(string cnlNums) { AppData.CheckLoggedOn(); int[] cnlNumArr = WebUtils.QueryParamToIntArray(cnlNums); - CnlDataExt[] cnlDataDTOs = GetCnlDataExtArr(cnlNumArr); - return JsSerializer.Serialize(new DataTransferObject(cnlDataDTOs)); + CnlDataExt[] cnlDataExtArr = GetCnlDataExtArr(cnlNumArr); + return JsSerializer.Serialize(new DataTransferObject(cnlDataExtArr)); } catch (Exception ex) { @@ -268,22 +268,19 @@ public string GetCurCnlDataExtByView(int viewID) try { AppData.CheckLoggedOn(); - CnlDataExt[] cnlDataDTOs; BaseView view = AppData.ViewCache.GetViewFromCache(viewID); if (view == null) { - AppData.Log.WriteError(string.Format(Localization.UseRussian ? - "Не удалось получить представление с ид.={0} из кеша" : - "Unable to get view with ID={0} from the cache", viewID)); - cnlDataDTOs = new CnlDataExt[0]; + throw new ScadaException(Localization.UseRussian ? + "Не удалось получить представление из кеша" : + "Unable to get view from the cache"); } else { - cnlDataDTOs = GetCnlDataExtArr(view.CnlList); + CnlDataExt[] cnlDataExtArr = GetCnlDataExtArr(view.CnlList); + return JsSerializer.Serialize(new DataTransferObject(cnlDataExtArr)); } - - return JsSerializer.Serialize(new DataTransferObject(cnlDataDTOs)); } catch (Exception ex) { diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/MenuItem.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/MenuItem.cs index 84dafc230..5016585dc 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/MenuItem.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/MenuItem.cs @@ -152,8 +152,10 @@ public static MenuItem FromStandardMenuItem(StandardMenuItems standardMenuItem) { case StandardMenuItems.Reports: return new MenuItem(WebPhrases.ReportsMenuItem, "~/Reports.aspx", 100); + case StandardMenuItems.Admin: + return new MenuItem(WebPhrases.AdminMenuItem, "", 200); case StandardMenuItems.Config: - return new MenuItem(WebPhrases.ConfigMenuItem, "", 200); + return new MenuItem(WebPhrases.ConfigMenuItem, "", 300); default: // StandardMenuItem.About return new MenuItem(WebPhrases.AboutMenuItem, "~/About.aspx", SortOrders.Last); } diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/StandardMenuItems.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/StandardMenuItems.cs index 913baf9f1..2e7fbf9d5 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/StandardMenuItems.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/StandardMenuItems.cs @@ -43,12 +43,16 @@ public enum StandardMenuItems ///
Reports = 1, /// + /// Администрирование + /// + Admin = 2, + /// /// Конфигурация /// - Config = 2, + Config = 4, /// /// О приложении /// - About = 4 + About = 8 } } diff --git a/ScadaWeb/ScadaWebCommon5Beta/UserMonitor.cs b/ScadaWeb/ScadaWebCommon5Beta/UserMonitor.cs index 913fdefbe..340b78f20 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/UserMonitor.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/UserMonitor.cs @@ -27,7 +27,6 @@ using System.Collections.Generic; using System.Net; using System.ServiceModel.Web; -using System.Web.SessionState; using Utils; namespace Scada.Web diff --git a/ScadaWeb/ScadaWebCommon5Beta/WebPhrases.cs b/ScadaWeb/ScadaWebCommon5Beta/WebPhrases.cs index 27e453274..c06972b54 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/WebPhrases.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/WebPhrases.cs @@ -57,6 +57,7 @@ static WebPhrases() // Словарь Scada.Web.Shell.MenuItem public static string ReportsMenuItem { get; private set; } + public static string AdminMenuItem { get; private set; } public static string ConfigMenuItem { get; private set; } public static string AboutMenuItem { get; private set; } @@ -79,6 +80,7 @@ private static void SetToDefault() SaveWebSettingsError = Localization.Dict.GetEmptyPhrase("SaveWebSettingsError"); ReportsMenuItem = Localization.Dict.GetEmptyPhrase("ReportsMenuItem"); + AdminMenuItem = Localization.Dict.GetEmptyPhrase("AdminMenuItem"); ConfigMenuItem = Localization.Dict.GetEmptyPhrase("ConfigMenuItem"); AboutMenuItem = Localization.Dict.GetEmptyPhrase("AboutMenuItem"); @@ -116,6 +118,7 @@ public static void Init() if (Localization.Dictionaries.TryGetValue("Scada.Web.Shell.MenuItem", out dict)) { ReportsMenuItem = dict.GetPhrase("ReportsMenuItem", ReportsMenuItem); + AdminMenuItem = dict.GetPhrase("AdminMenuItem", AdminMenuItem); ConfigMenuItem = dict.GetPhrase("ConfigMenuItem", ConfigMenuItem); AboutMenuItem = dict.GetPhrase("AboutMenuItem", AboutMenuItem); } diff --git a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml index 52f3f27aa..8ae554c78 100644 --- a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml +++ b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml @@ -38,6 +38,7 @@ Reports + Administration Configuration About diff --git a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml index 30fe4f882..ea15c5b1c 100644 --- a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml +++ b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml @@ -38,6 +38,7 @@ Отчёты + Администрирование Конфигурация О приложении From 3b05d06c891178e7ffd01afa9f2231028c1879da Mon Sep 17 00:00:00 2001 From: 2mik Date: Fri, 13 May 2016 13:28:09 +0300 Subject: [PATCH 035/382] ScadaWeb5: start bottom tabs --- ScadaWeb/ScadaWebShell5Beta/MasterMain.Master | 4 +-- .../MasterMain.Master.designer.cs | 8 +++--- ScadaWeb/ScadaWebShell5Beta/View.aspx | 7 ++++- .../ScadaWebShell5Beta/View.aspx.designer.cs | 21 +++++++++----- ScadaWeb/ScadaWebShell5Beta/css/view.css | 24 +++++++++++++++- ScadaWeb/ScadaWebShell5Beta/css/view.less | 28 ++++++++++++++++++- ScadaWeb/ScadaWebShell5Beta/css/view.min.css | 2 +- .../lang/ScadaWeb.en-GB.xml | 4 +-- .../lang/ScadaWeb.ru-RU.xml | 4 +-- 9 files changed, 81 insertions(+), 21 deletions(-) diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master index 28f493f9d..8b8871247 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master @@ -45,8 +45,8 @@
-
-
+
+
<%= GenerateMainMenuHtml() %> diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.designer.cs b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.designer.cs index d7d32746c..cc770f860 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.designer.cs +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.designer.cs @@ -58,22 +58,22 @@ public partial class MasterMain { protected global::System.Web.UI.WebControls.Label lblMainNormalViewBtn; /// - /// lblMainMenu control. + /// lblMainMenuTab control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.WebControls.Label lblMainMenu; + protected global::System.Web.UI.WebControls.Label lblMainMenuTab; /// - /// lblMainExplorer control. + /// lblMainExplorerTab control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.WebControls.Label lblMainExplorer; + protected global::System.Web.UI.WebControls.Label lblMainExplorerTab; /// /// lblMainCollapseMenu control. diff --git a/ScadaWeb/ScadaWebShell5Beta/View.aspx b/ScadaWeb/ScadaWebShell5Beta/View.aspx index 51fa1f5b8..0764ebfec 100644 --- a/ScadaWeb/ScadaWebShell5Beta/View.aspx +++ b/ScadaWeb/ScadaWebShell5Beta/View.aspx @@ -4,5 +4,10 @@ - +
+ +
+
+
+
diff --git a/ScadaWeb/ScadaWebShell5Beta/View.aspx.designer.cs b/ScadaWeb/ScadaWebShell5Beta/View.aspx.designer.cs index b5f5191f1..06b2d5622 100644 --- a/ScadaWeb/ScadaWebShell5Beta/View.aspx.designer.cs +++ b/ScadaWeb/ScadaWebShell5Beta/View.aspx.designer.cs @@ -3,15 +3,22 @@ // This code was generated by a tool. // // Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. +// the code is regenerated. // //------------------------------------------------------------------------------ -namespace Scada.Web -{ - - - public partial class WFrmView - { +namespace Scada.Web { + + + public partial class WFrmView { + + /// + /// lblEventsTab control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblEventsTab; } } diff --git a/ScadaWeb/ScadaWebShell5Beta/css/view.css b/ScadaWeb/ScadaWebShell5Beta/css/view.css index 6dedaf930..5de4a32bc 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/view.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/view.css @@ -1,5 +1,9 @@ /*Verdana, Geneva, Tahoma, sans-serif;*/ -iframe { +#divView { + position: relative; + height: 100%; +} +#frameView { margin: 0; padding: 0; width: 100%; @@ -7,4 +11,22 @@ iframe { border: none; display: block; overflow: hidden; +} +#divBottomTabs { + position: absolute; + left: 0; + bottom: 0; + width: 100%; + height: 30px; + background-color: rgba(255, 255, 255, 0.75); +} +#divBottomTabs .tab { + margin: 0 0 0 5px; + padding: 0 10px; + color: black; + /*@content-fore-color;*/ + background-color: white; + display: inline-block; + line-height: 30px; + white-space: nowrap; } \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/view.less b/ScadaWeb/ScadaWebShell5Beta/css/view.less index 4b0b20180..d01422a0e 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/view.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/view.less @@ -1,6 +1,13 @@ @import "globalvar.less"; -iframe { +@bottom-tabs-heigth: 30px; + +#divView { + position: relative; + height: 100%; +} + +#frameView { margin: 0; padding: 0; width: 100%; @@ -9,3 +16,22 @@ iframe { display: block; overflow: hidden; } + +#divBottomTabs { + position: absolute; + left: 0; + bottom: 0; + width: 100%; + height: @bottom-tabs-heigth; + background-color: rgba(255, 255, 255, 0.75); +} + +#divBottomTabs .tab { + margin: 0 0 0 5px; + padding: 0 10px; + color: black; /*@content-fore-color;*/ + background-color: white; + display: inline-block; + line-height: @bottom-tabs-heigth; + white-space: nowrap; +} diff --git a/ScadaWeb/ScadaWebShell5Beta/css/view.min.css b/ScadaWeb/ScadaWebShell5Beta/css/view.min.css index 1f3b68043..227afb691 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/view.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/view.min.css @@ -1 +1 @@ -iframe{margin:0;padding:0;width:100%;height:100%;border:none;display:block;overflow:hidden;} \ No newline at end of file +#divView{position:relative;height:100%;}#frameView{margin:0;padding:0;width:100%;height:100%;border:none;display:block;overflow:hidden;}#divBottomTabs{position:absolute;left:0;bottom:0;width:100%;height:30px;background-color:rgba(255,255,255,.75);}#divBottomTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#000;background-color:#fff;display:inline-block;line-height:30px;white-space:nowrap;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml index 8ae554c78..afb9c5cc5 100644 --- a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml +++ b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml @@ -13,8 +13,8 @@ Logout Full screen Normal view - Main Menu - Views + Main Menu + Views Collapse menu diff --git a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml index ea15c5b1c..0d607ab1b 100644 --- a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml +++ b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml @@ -13,8 +13,8 @@ Выйти На весь экран Нормальный вид - Главное меню - Представления + Главное меню + Представления Свернуть меню From ef3fc670326adc4270500ce7f24399320a7ae1bf Mon Sep 17 00:00:00 2001 From: 2mik Date: Sat, 14 May 2016 01:24:10 +0300 Subject: [PATCH 036/382] ScadaWeb5: UI design --- .../OpenPlugins/PlgScheme/js/api/clientapi.js | 18 +++-- .../OpenPlugins/PlgScheme/js/scadautils.js | 67 ++++++++++++++++++- .../plugins/Scheme/js/schememodel.js | 8 --- .../plugins/Scheme/js/schemerender.js | 9 +++ ScadaWeb/ScadaWebShell5Beta/View.aspx | 6 +- .../ScadaWebShell5Beta/View.aspx.designer.cs | 9 +++ .../ScadaWebShell5Beta/css/globalvar.less | 2 +- ScadaWeb/ScadaWebShell5Beta/css/login.less | 9 ++- .../ScadaWebShell5Beta/css/mastermain.css | 7 +- .../ScadaWebShell5Beta/css/mastermain.less | 9 +-- .../ScadaWebShell5Beta/css/mastermain.min.css | 2 +- ScadaWeb/ScadaWebShell5Beta/css/shellvar.less | 1 + ScadaWeb/ScadaWebShell5Beta/css/view.css | 52 +++++++++++--- ScadaWeb/ScadaWebShell5Beta/css/view.less | 63 ++++++++++++++--- ScadaWeb/ScadaWebShell5Beta/css/view.min.css | 2 +- 15 files changed, 213 insertions(+), 51 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgScheme/js/api/clientapi.js b/ScadaWeb/OpenPlugins/PlgScheme/js/api/clientapi.js index b991c38ee..070cd58d0 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/js/api/clientapi.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/js/api/clientapi.js @@ -54,13 +54,19 @@ scada.clientAPI = { dataType: "json" }) .done(function (data, textStatus, jqXHR) { - if (data.d) { - scada.utils.logSuccessfulRequest(operation/*, data*/); + try { var parsedData = $.parseJSON(data.d); - callback(true, parsedData); - } else { - scada.utils.logServiceError(operation); - callback(false, defaultResult); + if (parsedData.Success) { + scada.utils.logSuccessfulRequest(operation/*, data*/); + callback(true, parsedData.Data ? parsedData.Data : parsedData); + } else { + scada.utils.logServiceError(operation, parsedData.ErrorMessage); + callback(false, errorResult); + } + } + catch (ex) { + scada.utils.logServiceFormatError(operation); + callback(false, errorResult); } }) .fail(function (jqXHR, textStatus, errorThrown) { diff --git a/ScadaWeb/OpenPlugins/PlgScheme/js/scadautils.js b/ScadaWeb/OpenPlugins/PlgScheme/js/scadautils.js index 5ec8d4efe..9701e5859 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/js/scadautils.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/js/scadautils.js @@ -24,13 +24,76 @@ scada.utils = { }, // Write information about the internal service error to console - logServiceError: function (operation) { - console.error(this.getCurTime() + " Request '" + operation + "' returns empty data. Internal service error"); + logServiceError: function (operation, opt_message) { + console.error(this.getCurTime() + " Request '" + operation + "' reports internal service error" + + (opt_message ? ": " + opt_message : "")); + }, + + // Write information about the internal service error to console + logServiceFormatError: function (operation) { + console.error(this.getCurTime() + " Request '" + operation + "' returns data in incorrect format"); }, // Write information about the failed request to console logFailedRequest: function (operation, jqXHR) { console.error(this.getCurTime() + " Request '" + operation + "' failed: " + jqXHR.status + " (" + jqXHR.statusText + ")"); + }, + + // Check if browser is in fullscreen mode + // See https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API + isFullscreen: function() { + return document.fullscreenElement || document.mozFullScreenElement || + document.webkitFullscreenElement || document.msFullscreenElement; + }, + + // Switch browser to fullscreen mode + requestFullscreen: function () { + if (document.documentElement.requestFullscreen) { + document.documentElement.requestFullscreen(); + } else if (document.documentElement.msRequestFullscreen) { + document.documentElement.msRequestFullscreen(); + } else if (document.documentElement.mozRequestFullScreen) { + document.documentElement.mozRequestFullScreen(); + } else if (document.documentElement.webkitRequestFullscreen) { + document.documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); + } + }, + + // Exit browser fullscreen mode + exitFullscreen: function () { + if (document.exitFullscreen) { + document.exitFullscreen(); + } else if (document.msExitFullscreen) { + document.msExitFullscreen(); + } else if (document.mozCancelFullScreen) { + document.mozCancelFullScreen(); + } else if (document.webkitExitFullscreen) { + document.webkitExitFullscreen(); + } + }, + + // Switch browser to full screen mode and back to normal view + toggleFullscreen: function () { + if (this.isFullscreen()) { + this.exitFullscreen(); + } else { + this.requestFullscreen(); + } + }, + + // Click hyperlink programmatically + clickLink: function (jqLink) { + var href = jqLink.attr("href"); + if (href) { + if (href.startsWith("javascript:")) { + // execute script + var script = href.substr(11); + eval(script); + } else { + // open web page + location = href; + } + } } }; diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js index 73107f276..5f78a3b93 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js @@ -162,14 +162,6 @@ scada.scheme.Scheme.prototype._obtainSchemeProps = function (parsedProps) { this.viewStamp = parsedProps.ViewStamp; this._expectedElemCnl = parsedProps.ElementCount; this._expectedImageCnt = parsedProps.ImageCount; - - if (parsedProps.SchemeProps.Title) { - document.title = parsedProps.SchemeProps.Title + " - Rapid SCADA"; - if (window.parent) { - window.parent.document.title = document.title; - } - } - return true; } else { return false; diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js index b4dfbcdfc..c3da905c7 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js @@ -120,6 +120,7 @@ scada.scheme.SchemeRenderer.prototype.createDom = function (elem, renderContext) "height": props.Size.Height }); + this.setBackColor($("body"), props.BackColor); this.setBackColor(divScheme, props.BackColor); this.setFont(divScheme, props.Font); this.setForeColor(divScheme, props.ForeColor); @@ -134,6 +135,14 @@ scada.scheme.SchemeRenderer.prototype.createDom = function (elem, renderContext) }); } + // set title + if (props.Title) { + document.title = props.Title + " - Rapid SCADA"; + if (window.parent) { + window.parent.document.title = document.title; + } + } + elem.dom = divScheme; }; diff --git a/ScadaWeb/ScadaWebShell5Beta/View.aspx b/ScadaWeb/ScadaWebShell5Beta/View.aspx index 0764ebfec..a1d1e81f0 100644 --- a/ScadaWeb/ScadaWebShell5Beta/View.aspx +++ b/ScadaWeb/ScadaWebShell5Beta/View.aspx @@ -6,8 +6,12 @@
+
+
-
+
+
+
diff --git a/ScadaWeb/ScadaWebShell5Beta/View.aspx.designer.cs b/ScadaWeb/ScadaWebShell5Beta/View.aspx.designer.cs index 06b2d5622..ab2095d59 100644 --- a/ScadaWeb/ScadaWebShell5Beta/View.aspx.designer.cs +++ b/ScadaWeb/ScadaWebShell5Beta/View.aspx.designer.cs @@ -20,5 +20,14 @@ public partial class WFrmView { /// To modify move field declaration from designer file to code-behind file. /// protected global::System.Web.UI.WebControls.Label lblEventsTab; + + /// + /// Label1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label Label1; } } diff --git a/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less b/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less index a47d51ec0..cadd3f3db 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less @@ -1,5 +1,5 @@ @content-fore-color: #444; @content-back-color: #f1f1f1; -@panel-border-color: #eee; +@panel-border-color: #e5e5e5; @default-font-family: 'Open Sans', sans-serif; /*Verdana, Geneva, Tahoma, sans-serif;*/ @default-font-size: 12px; \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/login.less b/ScadaWeb/ScadaWebShell5Beta/css/login.less index c3bcc4ccb..ad7eae620 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/login.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/login.less @@ -1,5 +1,8 @@ @import "globalvar.less"; +@form-border-color: #ddd; +@label-fore-color: #777; +@input-fore-color: black; @side-padding: 25px; body { @@ -34,9 +37,9 @@ div .alert { #divLogin { padding: @side-padding @side-padding 35px; background-color: white; - border: 1px solid #ddd; + border: 1px solid @form-border-color; border-radius: 1px; - color: #777; + color: @label-fore-color; display: inline-block; text-align: left; } @@ -51,7 +54,7 @@ div .alert { #divLogin input[type=password] { width: 265px; margin: 0 0 20px 0; - color: black; + color: @input-fore-color; } #divLogin input[type=submit] { diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css index d0941e428..93e070076 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css @@ -101,7 +101,7 @@ a:active { overflow: hidden; } #divMainLeftPane .tool-window { - height: calc(100% - 40px); + height: calc(100% - 30px); margin: 0 0 0 30px; padding: 5px 0 0 0; display: none; @@ -191,11 +191,12 @@ a:active { position: absolute; bottom: 0; width: 220px; - height: 40px; + height: 30px; margin: 0 0 0 30px; - padding: 5px 10px; + padding: 0px 10px; color: #9ca1a6; font-size: 13px; + line-height: 30px; } #divMainCollapsePane i { margin-right: 10px; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less index da2a423db..c13446095 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less @@ -3,7 +3,7 @@ @header-heigth: 30px; @left-tabs-heigth: 30px; -@collapse-pane-heigth: 40px; +@collapse-pane-heigth: 30px; @left-pane-width: 250px; body { @@ -20,7 +20,7 @@ a:hover, a:focus, a:active { outline: 0; - color:inherit; + color: inherit; text-decoration: none; } @@ -93,7 +93,7 @@ a:active { right: 0; height: @header-heigth; padding: 0 10px; - background-color: white; + background-color: @menu-back-color-air; display: none; /*inline-block*/ line-height: @header-heigth; opacity: 0.5; @@ -234,9 +234,10 @@ a:active { width: @left-pane-width - @left-tabs-heigth; height: @collapse-pane-heigth; margin: 0 0 0 @left-tabs-heigth; - padding: 5px 10px; + padding: 0px 10px; color: @menu-fore-color-dark; font-size: 13px; + line-height: @collapse-pane-heigth; } #divMainCollapsePane i { diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css index 8e772baa1..dd072af90 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 250px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;white-space:nowrap;}#divMainHeader .hdr-btn{height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;display:inline-block;font-size:13px;text-decoration:none;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenuBtn{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;}#divMainUserMenu{float:right;height:30px;}#lblMainNormalViewBtn{position:fixed;top:0;right:0;height:30px;padding:0 10px;background-color:#fff;display:none;line-height:30px;opacity:.5;cursor:pointer;color:#9ca1a6;font-size:13px;}#lblMainNormalViewBtn:hover{color:#00b9eb;opacity:1;}#divMainLeftPane{position:fixed;left:0;top:30px;width:250px;min-height:250px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 40px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainLeftPane .tree-view .node:hover{background-color:#191e23;color:#00b9eb;}#divMainLeftPane .tree-view .node.selected{background-color:#0073aa;color:#fff;}#divMainLeftPane .tree-view .node.disabled{color:#9ca1a6;cursor:default;}#divMainLeftPane .tree-view .expander::before{font-size:10px;}#divMainMenu .node[data-level="1"],#divMainMenu .node[data-level="2"],#divMainMenu .node[data-level="3"],#divMainMenu .node[data-level="4"],#divMainMenu .node[data-level="5"]{color:#9ca1a6;font-size:13px;}#divMainMenu .tree-view .node{padding:5px 10px 5px 10px;}#divMainMenu .tree-view .expander.left{display:none;width:0;}#divMainMenu .tree-view .expander.right{display:table-cell;width:20px;}#divMainExplorer{font-size:13px;line-height:13px;}#divMainExplorer .tree-view .node{padding:7px 5px 3px 10px;}#divMainExplorer .tree-view .icon{width:21px;}#divMainExplorer .tree-view img{position:relative;top:-2px;}#divMainCollapsePane{position:absolute;bottom:0;width:220px;height:40px;margin:0 0 0 30px;padding:5px 10px;color:#9ca1a6;font-size:13px;}#divMainCollapsePane i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapsePane:hover{color:#00b9eb;cursor:pointer;} \ No newline at end of file +body{margin:0;padding:30px 0 0 250px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;white-space:nowrap;}#divMainHeader .hdr-btn{height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;display:inline-block;font-size:13px;text-decoration:none;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenuBtn{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;}#divMainUserMenu{float:right;height:30px;}#lblMainNormalViewBtn{position:fixed;top:0;right:0;height:30px;padding:0 10px;background-color:#fff;display:none;line-height:30px;opacity:.5;cursor:pointer;color:#9ca1a6;font-size:13px;}#lblMainNormalViewBtn:hover{color:#00b9eb;opacity:1;}#divMainLeftPane{position:fixed;left:0;top:30px;width:250px;min-height:250px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 30px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainLeftPane .tree-view .node:hover{background-color:#191e23;color:#00b9eb;}#divMainLeftPane .tree-view .node.selected{background-color:#0073aa;color:#fff;}#divMainLeftPane .tree-view .node.disabled{color:#9ca1a6;cursor:default;}#divMainLeftPane .tree-view .expander::before{font-size:10px;}#divMainMenu .node[data-level="1"],#divMainMenu .node[data-level="2"],#divMainMenu .node[data-level="3"],#divMainMenu .node[data-level="4"],#divMainMenu .node[data-level="5"]{color:#9ca1a6;font-size:13px;}#divMainMenu .tree-view .node{padding:5px 10px 5px 10px;}#divMainMenu .tree-view .expander.left{display:none;width:0;}#divMainMenu .tree-view .expander.right{display:table-cell;width:20px;}#divMainExplorer{font-size:13px;line-height:13px;}#divMainExplorer .tree-view .node{padding:7px 5px 3px 10px;}#divMainExplorer .tree-view .icon{width:21px;}#divMainExplorer .tree-view img{position:relative;top:-2px;}#divMainCollapsePane{position:absolute;bottom:0;width:220px;height:30px;margin:0 0 0 30px;padding:0 10px;color:#9ca1a6;font-size:13px;line-height:30px;}#divMainCollapsePane i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapsePane:hover{color:#00b9eb;cursor:pointer;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/shellvar.less b/ScadaWeb/ScadaWebShell5Beta/css/shellvar.less index 92fbe391b..04914fb34 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/shellvar.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/shellvar.less @@ -1,6 +1,7 @@ @menu-fore-color: #eee; @menu-fore-color-dark: #9ca1a6; @menu-back-color: #23282d; +@menu-back-color-air: white; @menu-item-hover-fore-color: #00b9eb; @menu-item-hover-back-color: #32373c; @menu-item-hover-back-color-dark: #191e23; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/view.css b/ScadaWeb/ScadaWebShell5Beta/css/view.css index 5de4a32bc..22d8687fc 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/view.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/view.css @@ -1,32 +1,62 @@ /*Verdana, Geneva, Tahoma, sans-serif;*/ #divView { - position: relative; height: 100%; } -#frameView { +#divView iframe { margin: 0; padding: 0; width: 100%; - height: 100%; border: none; display: block; overflow: hidden; } +#frameView { + height: calc(100% - 207px - 30px); +} +#divViewSplitter { + height: 7px; + background-color: #ddd; + cursor: ns-resize; + /*display: none;*/ +} +#divDataWindow { + height: 200px; + background-color: white; + border-bottom: 1px solid #e5e5e5; + /*display: none;*/ +} +#frameDataWindow { + height: 100%; +} #divBottomTabs { - position: absolute; - left: 0; - bottom: 0; - width: 100%; height: 30px; - background-color: rgba(255, 255, 255, 0.75); + background-color: white; + color: #444; + font-size: 13px; + overflow: hidden; } #divBottomTabs .tab { margin: 0 0 0 5px; padding: 0 10px; - color: black; - /*@content-fore-color;*/ - background-color: white; display: inline-block; line-height: 30px; white-space: nowrap; +} +#divBottomTabs .tab.selected { + font-weight: 600; +} +#divBottomTabs .tab:first-child { + margin-left: 10px; +} +#divBottomTabs .tab:hover { + color: #00b9eb; + cursor: pointer; +} +#divCollapseDataWindow { + color: #8f8f8f; +} +#divCollapseDataWindow i { + font-size: 19px; + position: relative; + top: 2px; } \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/view.less b/ScadaWeb/ScadaWebShell5Beta/css/view.less index d01422a0e..94e7a15d0 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/view.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/view.less @@ -1,37 +1,80 @@ @import "globalvar.less"; +@import "shellvar.less"; @bottom-tabs-heigth: 30px; +@bottom-menu-back-color: white; +@bottom-menu-border-color: #ddd; #divView { - position: relative; height: 100%; } -#frameView { +#divView iframe { margin: 0; padding: 0; width: 100%; - height: 100%; border: none; display: block; overflow: hidden; } +#frameView { + height: calc(~"100% - 207px - "@bottom-tabs-heigth); +} + +#divViewSplitter { + height: 7px; + background-color: #ddd; + cursor: ns-resize; + /*display: none;*/ +} + +#divDataWindow { + height: 200px; + background-color: white; + border-bottom: 1px solid @panel-border-color; + /*display: none;*/ +} + +#frameDataWindow { + height: 100%; +} + #divBottomTabs { - position: absolute; - left: 0; - bottom: 0; - width: 100%; height: @bottom-tabs-heigth; - background-color: rgba(255, 255, 255, 0.75); + background-color: @bottom-menu-back-color; + color: @content-fore-color; + font-size: 13px; + overflow: hidden; } #divBottomTabs .tab { margin: 0 0 0 5px; padding: 0 10px; - color: black; /*@content-fore-color;*/ - background-color: white; display: inline-block; line-height: @bottom-tabs-heigth; white-space: nowrap; } + +#divBottomTabs .tab.selected { + font-weight: 600; +} + +#divBottomTabs .tab:first-child { + margin-left: 10px; +} + +#divBottomTabs .tab:hover { + color: @menu-item-hover-fore-color; + cursor: pointer; +} + +#divCollapseDataWindow { + color: #8f8f8f; +} + +#divCollapseDataWindow i { + font-size: 19px; + position: relative; + top: 2px; +} diff --git a/ScadaWeb/ScadaWebShell5Beta/css/view.min.css b/ScadaWeb/ScadaWebShell5Beta/css/view.min.css index 227afb691..586c3357a 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/view.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/view.min.css @@ -1 +1 @@ -#divView{position:relative;height:100%;}#frameView{margin:0;padding:0;width:100%;height:100%;border:none;display:block;overflow:hidden;}#divBottomTabs{position:absolute;left:0;bottom:0;width:100%;height:30px;background-color:rgba(255,255,255,.75);}#divBottomTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#000;background-color:#fff;display:inline-block;line-height:30px;white-space:nowrap;} \ No newline at end of file +#divView{height:100%;}#divView iframe{margin:0;padding:0;width:100%;border:none;display:block;overflow:hidden;}#frameView{height:calc(100% - 207px - 30px);}#divViewSplitter{height:7px;background-color:#ddd;cursor:ns-resize;}#divDataWindow{height:200px;background-color:#fff;border-bottom:1px solid #e5e5e5;}#frameDataWindow{height:100%;}#divBottomTabs{height:30px;background-color:#fff;color:#444;font-size:13px;overflow:hidden;}#divBottomTabs .tab{margin:0 0 0 5px;padding:0 10px;display:inline-block;line-height:30px;white-space:nowrap;}#divBottomTabs .tab.selected{font-weight:600;}#divBottomTabs .tab:first-child{margin-left:10px;}#divBottomTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divCollapseDataWindow{color:#8f8f8f;}#divCollapseDataWindow i{font-size:19px;position:relative;top:2px;} \ No newline at end of file From fb2993fb1ed4384b51ca5597f2ff487b8852efdf Mon Sep 17 00:00:00 2001 From: 2mik Date: Mon, 16 May 2016 13:24:16 +0300 Subject: [PATCH 037/382] PlgTable: initial commit --- .../PlgTable/AppCode/PlgTableSpec.cs | 117 ++++++++++++++++++ .../PlgTable/AppCode/TableViewSpec.cs | 66 ++++++++++ ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj | 113 +++++++++++++++++ .../PlgTable/Properties/AssemblyInfo.cs | 35 ++++++ .../OpenPlugins/PlgTable/Web.Debug.config | 30 +++++ .../OpenPlugins/PlgTable/Web.Release.config | 31 +++++ ScadaWeb/OpenPlugins/PlgTable/Web.config | 13 ++ .../plugins/Table/images/tableicon.png | Bin 0 -> 168 bytes 8 files changed, 405 insertions(+) create mode 100644 ScadaWeb/OpenPlugins/PlgTable/AppCode/PlgTableSpec.cs create mode 100644 ScadaWeb/OpenPlugins/PlgTable/AppCode/TableViewSpec.cs create mode 100644 ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj create mode 100644 ScadaWeb/OpenPlugins/PlgTable/Properties/AssemblyInfo.cs create mode 100644 ScadaWeb/OpenPlugins/PlgTable/Web.Debug.config create mode 100644 ScadaWeb/OpenPlugins/PlgTable/Web.Release.config create mode 100644 ScadaWeb/OpenPlugins/PlgTable/Web.config create mode 100644 ScadaWeb/OpenPlugins/PlgTable/plugins/Table/images/tableicon.png diff --git a/ScadaWeb/OpenPlugins/PlgTable/AppCode/PlgTableSpec.cs b/ScadaWeb/OpenPlugins/PlgTable/AppCode/PlgTableSpec.cs new file mode 100644 index 000000000..1af9ac370 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/AppCode/PlgTableSpec.cs @@ -0,0 +1,117 @@ +/* + * Copyright 2016 Mikhail Shiryaev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Product : Rapid SCADA + * Module : PlgTable + * Summary : Table plugin specification + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + */ + +using System.Collections.Generic; +using System.IO; + +namespace Scada.Web.Plugins +{ + /// + /// Table plugin specification + /// Спецификация табличного плагина + /// + public class PlgTableSpec : PluginSpec + { + private DictUpdater dictUpdater; // объект для обновления словаря плагина + + + /// + /// Конструктор + /// + public PlgTableSpec() + : base() + { + dictUpdater = null; + } + + + /// + /// Получить наименование плагина + /// + public override string Name + { + get + { + return Localization.UseRussian ? + "Таблицы" : + "Tables"; + } + } + + /// + /// Получить описание плагина + /// + public override string Descr + { + get + { + return Localization.UseRussian ? + "Плагин обеспечивает отображение табличных представлений и событий." : + "The plugin provides displaying table views and events."; + } + } + + /// + /// Получить версию плагина + /// + public override string Version + { + get + { + return "0.0.0.1"; + } + } + + /// + /// Получить спецификации представлений, которые реализуются плагином + /// + public override List ViewSpecs + { + get + { + return new List() { new TableViewSpec() }; + } + } + + + /// + /// Инициализировать плагин + /// + public override void Init() + { + dictUpdater = new DictUpdater( + string.Format("{0}Table{1}lang{1}", AppDirs.PluginsDir, Path.DirectorySeparatorChar), + "PlgTable", null, Log); + } + + /// + /// Выполнить действия после успешного входа пользователя в систему + /// + public override void OnUserLogin(UserData userData) + { + dictUpdater.Update(); + } + } +} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/AppCode/TableViewSpec.cs b/ScadaWeb/OpenPlugins/PlgTable/AppCode/TableViewSpec.cs new file mode 100644 index 000000000..134d420ba --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/AppCode/TableViewSpec.cs @@ -0,0 +1,66 @@ +/* + * Copyright 2016 Mikhail Shiryaev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Product : Rapid SCADA + * Module : PlgTable + * Summary : Table view specification + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + */ + +namespace Scada.Web.Plugins +{ + /// + /// Table view specification + /// Спецификация табличного представления + /// + public class TableViewSpec : ViewSpec + { + /// + /// Получить код типа представления + /// + public override string ViewTypeCode + { + get + { + // TODO: заменить на TableView после добавления поля ViewTypeCode в базу конфигурации + return "tbl"; + } + } + + /// + /// Получить ссылку на иконку типа представлений + /// + public override string IconUrl + { + get + { + return "~/plugins/Table/images/tableicon.png"; + } + } + + + /// + /// Получить ссылку на представление с заданным идентификатором + /// + public override string GetViewUrl(int viewID) + { + return "~/plugins/Table/Table.aspx?viewID=" + viewID; + } + } +} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj new file mode 100644 index 000000000..115e2456e --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj @@ -0,0 +1,113 @@ + + + + + Debug + AnyCPU + + + 2.0 + {7AE30E67-D10E-44B9-AC7A-81E21C8839E8} + {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + Scada.Web + PlgTable + v4.0 + true + + + + + + + + true + full + false + bin\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\ + TRACE + prompt + 4 + + + + ..\..\..\Log\bin\Release\Log.dll + + + ..\..\..\ScadaData\ScadaData\bin\Release\ScadaData.dll + + + ..\..\ScadaWebCommon5Beta\bin\Release\ScadaWebCommon5Beta.dll + + + + + + + + + + + + + + + + + + + + + Web.config + + + Web.config + + + + + + + + + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + + + + + True + True + 64113 + / + http://localhost:64113/ + False + False + + + False + + + + + + \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/Properties/AssemblyInfo.cs b/ScadaWeb/OpenPlugins/PlgTable/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..a5445efec --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("PlgTable")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("PlgTable")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("7ae30e67-d10e-44b9-ac7a-81e21c8839e8")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/ScadaWeb/OpenPlugins/PlgTable/Web.Debug.config b/ScadaWeb/OpenPlugins/PlgTable/Web.Debug.config new file mode 100644 index 000000000..2e302f9f9 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/Web.Debug.config @@ -0,0 +1,30 @@ + + + + + + + + + + \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/Web.Release.config b/ScadaWeb/OpenPlugins/PlgTable/Web.Release.config new file mode 100644 index 000000000..c35844462 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/Web.Release.config @@ -0,0 +1,31 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/Web.config b/ScadaWeb/OpenPlugins/PlgTable/Web.config new file mode 100644 index 000000000..c72873f34 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/Web.config @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/images/tableicon.png b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/images/tableicon.png new file mode 100644 index 0000000000000000000000000000000000000000..1b61744c55b4dc28cf46307bbbfa2deaf9084b7a GIT binary patch literal 168 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XygXeTLn>}1{rUgjo>{e_v(f1QQ}Ukr|MOIqb8vAPEs|hh z?QwbazyCb<|A`_Gk}fOo6jU@%obcUkuYts#15Mhkg4`19Hs(w$5)1{q&) literal 0 HcmV?d00001 From 129c0fad7e9c019594b5131456652c6dc69bea10 Mon Sep 17 00:00:00 2001 From: 2mik Date: Mon, 16 May 2016 13:24:47 +0300 Subject: [PATCH 038/382] ScadaWeb5: add content entity --- ScadaData/ScadaData/Client/DataAccess.cs | 59 ++++++++- ScadaData/ScadaData/Data/BaseTables.cs | 10 +- ScadaWeb/OpenPlugins/OpenPlugins.sln | 8 +- .../PlgScheme/AppCode/PlgSchemeSpec.cs | 2 +- .../{SchemeSpec.cs => SchemeViewSpec.cs} | 12 +- .../OpenPlugins/PlgScheme/PlgScheme.csproj | 2 +- .../Plugins/{RepSpec.cs => ContentSpec.cs} | 27 ++-- .../ScadaWebCommon5Beta/Plugins/PluginSpec.cs | 13 +- .../ScadaWebCommon5Beta.csproj | 3 +- .../ScadaWebCommon5Beta/Shell/UserContent.cs | 116 ++++++++++++++++++ .../ScadaWebCommon5Beta/Shell/UserMenu.cs | 2 +- ScadaWeb/ScadaWebCommon5Beta/UserRights.cs | 50 ++++++-- .../ScadaWebShell5Beta.csproj | 2 - .../images/treeview/scheme.png | Bin 233 -> 0 bytes .../images/treeview/table.png | Bin 168 -> 0 bytes 15 files changed, 261 insertions(+), 45 deletions(-) rename ScadaWeb/OpenPlugins/PlgScheme/AppCode/{SchemeSpec.cs => SchemeViewSpec.cs} (97%) rename ScadaWeb/ScadaWebCommon5Beta/Plugins/{RepSpec.cs => ContentSpec.cs} (61%) create mode 100644 ScadaWeb/ScadaWebCommon5Beta/Shell/UserContent.cs delete mode 100644 ScadaWeb/ScadaWebShell5Beta/images/treeview/scheme.png delete mode 100644 ScadaWeb/ScadaWebShell5Beta/images/treeview/table.png diff --git a/ScadaData/ScadaData/Client/DataAccess.cs b/ScadaData/ScadaData/Client/DataAccess.cs index 117a0222a..f25c6857b 100644 --- a/ScadaData/ScadaData/Client/DataAccess.cs +++ b/ScadaData/ScadaData/Client/DataAccess.cs @@ -118,7 +118,7 @@ protected string GetRoleNameFromBase(int roleID, string defaultRoleName) dataCache.RefreshBaseTables(); DataTable tblRole = dataCache.BaseTables.RightTable; - BaseTables.CheckIsNotEmpty(tblRole, true); + BaseTables.CheckColumnsExist(tblRole, true); tblRole.DefaultView.RowFilter = "RoleID = " + roleID; return tblRole.DefaultView.Count > 0 ? @@ -213,7 +213,7 @@ public ViewProps GetViewProps(int viewID) dataCache.RefreshBaseTables(); DataTable tblInterface = dataCache.BaseTables.InterfaceTable; - BaseTables.CheckIsNotEmpty(tblInterface, true); + BaseTables.CheckColumnsExist(tblInterface, true); tblInterface.DefaultView.RowFilter = "ItfID = " + viewID; if (tblInterface.DefaultView.Count > 0) @@ -253,7 +253,7 @@ public Dictionary GetViewRights(int roleID) dataCache.RefreshBaseTables(); DataTable tblRight = dataCache.BaseTables.RightTable; - BaseTables.CheckIsNotEmpty(tblRight, true); + BaseTables.CheckColumnsExist(tblRight, true); tblRight.DefaultView.RowFilter = "RoleID = " + roleID; foreach (DataRowView rowView in tblRight.DefaultView) @@ -274,6 +274,55 @@ public Dictionary GetViewRights(int roleID) } } + /// + /// Получить права на контент по идентификатору роли + /// + public Dictionary GetContentRights(int roleID) + { + lock (baseLock) + { + Dictionary contentRightsDict = new Dictionary(); + + try + { + dataCache.RefreshBaseTables(); + + DataTable tblInterface = dataCache.BaseTables.InterfaceTable; + DataTable tblRight = dataCache.BaseTables.RightTable; + BaseTables.CheckColumnsExist(tblInterface, true); + BaseTables.CheckColumnsExist(tblRight, true); + + foreach (DataRowView itfRowView in tblInterface.DefaultView) + { + int contentTypeID = (int)itfRowView["ItfID"]; + string contentTypeCode = (string)itfRowView["Name"]; + + if (string.IsNullOrEmpty(Path.GetExtension(contentTypeCode))) + { + tblRight.DefaultView.RowFilter = string.Format("ItfID = {0} and RoleID = {1}", + contentTypeID, roleID); + + if (tblRight.DefaultView.Count > 0) + { + DataRowView rightRowView = tblRight.DefaultView[0]; + EntityRights rights = new EntityRights( + (bool)rightRowView["ViewRight"], (bool)rightRowView["CtrlRight"]); + contentRightsDict[contentTypeCode] = rights; + } + } + } + } + catch (Exception ex) + { + log.WriteException(ex, Localization.UseRussian ? + "Ошибка при получении прав на контент для роли с ид.={0}" : + "Error getting content access rights for the role with ID={0}", roleID); + } + + return contentRightsDict; + } + } + /// /// Получить идентификатор пользователя по имени /// @@ -287,7 +336,7 @@ public int GetUserID(string username) dataCache.RefreshBaseTables(); DataTable tblUser = dataCache.BaseTables.UserTable; - BaseTables.CheckIsNotEmpty(tblUser, true); + BaseTables.CheckColumnsExist(tblUser, true); tblUser.DefaultView.RowFilter = "Name = '" + username + "'"; return tblUser.DefaultView.Count > 0 ? @@ -327,7 +376,7 @@ public string GetColorByStat(int stat, string defaultColor) dataCache.RefreshBaseTables(); DataTable tblEvType = dataCache.BaseTables.EvTypeTable; - BaseTables.CheckIsNotEmpty(tblEvType, true); + BaseTables.CheckColumnsExist(tblEvType, true); tblEvType.DefaultView.RowFilter = "CnlStatus = " + stat; if (tblEvType.DefaultView.Count > 0) diff --git a/ScadaData/ScadaData/Data/BaseTables.cs b/ScadaData/ScadaData/Data/BaseTables.cs index e71eb75e9..ec0630a01 100644 --- a/ScadaData/ScadaData/Data/BaseTables.cs +++ b/ScadaData/ScadaData/Data/BaseTables.cs @@ -165,9 +165,9 @@ public static string GetFileName(DataTable dataTable) } /// - /// Проверить, что таблица не пуста + /// Проверить, что колонки таблицы существуют /// - public static bool CheckIsNotEmpty(DataTable dataTable, bool throwOnEmpty = false) + public static bool CheckColumnsExist(DataTable dataTable, bool throwOnFail = false) { if (dataTable == null) throw new ArgumentNullException("dataTable"); @@ -176,11 +176,11 @@ public static bool CheckIsNotEmpty(DataTable dataTable, bool throwOnEmpty = fals { return true; } - else if (throwOnEmpty) + else if (throwOnFail) { throw new ScadaException(string.Format(Localization.UseRussian ? - "Таблица [{0}] пуста." : - "The table [{0}] is empty.", dataTable.TableName)); + "Таблица [{0}] не содержит колонок." : + "The table [{0}] does not contain columns.", dataTable.TableName)); } else { diff --git a/ScadaWeb/OpenPlugins/OpenPlugins.sln b/ScadaWeb/OpenPlugins/OpenPlugins.sln index 5215742bb..b0fa7843c 100644 --- a/ScadaWeb/OpenPlugins/OpenPlugins.sln +++ b/ScadaWeb/OpenPlugins/OpenPlugins.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.23107.0 +VisualStudioVersion = 14.0.24720.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlgScheme", "PlgScheme\PlgScheme.csproj", "{36CBF606-7F85-4E46-BC7A-C86CACF675B2}" EndProject @@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlgSchemeCommon", "PlgSchem EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlgConfig", "PlgConfig\PlgConfig.csproj", "{BA16503A-0E79-42C1-94C7-F690A0DA145E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlgTable", "PlgTable\PlgTable.csproj", "{7AE30E67-D10E-44B9-AC7A-81E21C8839E8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,6 +29,10 @@ Global {BA16503A-0E79-42C1-94C7-F690A0DA145E}.Debug|Any CPU.Build.0 = Debug|Any CPU {BA16503A-0E79-42C1-94C7-F690A0DA145E}.Release|Any CPU.ActiveCfg = Release|Any CPU {BA16503A-0E79-42C1-94C7-F690A0DA145E}.Release|Any CPU.Build.0 = Release|Any CPU + {7AE30E67-D10E-44B9-AC7A-81E21C8839E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7AE30E67-D10E-44B9-AC7A-81E21C8839E8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7AE30E67-D10E-44B9-AC7A-81E21C8839E8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7AE30E67-D10E-44B9-AC7A-81E21C8839E8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ScadaWeb/OpenPlugins/PlgScheme/AppCode/PlgSchemeSpec.cs b/ScadaWeb/OpenPlugins/PlgScheme/AppCode/PlgSchemeSpec.cs index 2da0b44d6..ff5ae726f 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/AppCode/PlgSchemeSpec.cs +++ b/ScadaWeb/OpenPlugins/PlgScheme/AppCode/PlgSchemeSpec.cs @@ -91,7 +91,7 @@ public override List ViewSpecs { get { - return new List() { new SchemeSpec() }; + return new List() { new SchemeViewSpec() }; } } diff --git a/ScadaWeb/OpenPlugins/PlgScheme/AppCode/SchemeSpec.cs b/ScadaWeb/OpenPlugins/PlgScheme/AppCode/SchemeViewSpec.cs similarity index 97% rename from ScadaWeb/OpenPlugins/PlgScheme/AppCode/SchemeSpec.cs rename to ScadaWeb/OpenPlugins/PlgScheme/AppCode/SchemeViewSpec.cs index c4af6bb1b..e9a4cb76b 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/AppCode/SchemeSpec.cs +++ b/ScadaWeb/OpenPlugins/PlgScheme/AppCode/SchemeViewSpec.cs @@ -29,28 +29,28 @@ namespace Scada.Web.Plugins /// Scheme view specification /// Спецификация представления схем ///
- public class SchemeSpec : ViewSpec + public class SchemeViewSpec : ViewSpec { /// /// Получить код типа представления /// - public override string IconUrl + public override string ViewTypeCode { get { - return "~/plugins/Scheme/images/schemeicon.png"; + // TODO: заменить на SchemeView после добавления поля ViewTypeCode в базу конфигурации + return "sch"; } } /// /// Получить ссылку на иконку типа представлений /// - public override string ViewTypeCode + public override string IconUrl { get { - // TODO: заменить на SchemeView после добавления поля ViewTypeCode в базу конфигурации - return "sch"; + return "~/plugins/Scheme/images/schemeicon.png"; } } diff --git a/ScadaWeb/OpenPlugins/PlgScheme/PlgScheme.csproj b/ScadaWeb/OpenPlugins/PlgScheme/PlgScheme.csproj index 61e527bf5..0a241d2f2 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/PlgScheme.csproj +++ b/ScadaWeb/OpenPlugins/PlgScheme/PlgScheme.csproj @@ -100,7 +100,7 @@ - + Scheme.aspx ASPXCodeBehind diff --git a/ScadaWeb/ScadaWebCommon5Beta/Plugins/RepSpec.cs b/ScadaWeb/ScadaWebCommon5Beta/Plugins/ContentSpec.cs similarity index 61% rename from ScadaWeb/ScadaWebCommon5Beta/Plugins/RepSpec.cs rename to ScadaWeb/ScadaWebCommon5Beta/Plugins/ContentSpec.cs index b3166319d..fab4880ca 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Plugins/RepSpec.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Plugins/ContentSpec.cs @@ -16,35 +16,46 @@ * * Product : Rapid SCADA * Module : ScadaWebCommon - * Summary : The base class for report specification + * Summary : The base class for content specification * * Author : Mikhail Shiryaev * Created : 2016 * Modified : 2016 */ +using System; + namespace Scada.Web.Plugins { /// - /// The base class for report specification - /// Родительский класс спецификации отчёта + /// The base class for content specification + /// Родительский класс спецификации контента /// - public abstract class RepSpec + public abstract class ContentSpec : IComparable { /// - /// Получить код типа отчёта + /// Получить код типа контента /// /// Используется для предоставления прав пользователям - public abstract string RepTypeCode { get; } + public abstract string ContentTypeCode { get; } /// - /// Получить наименование отчёта + /// Получить наименование контента /// public abstract string Name { get; } /// - /// Получить ссылку на стартовую страницу отчёта + /// Получить ссылку на страницу контента /// public abstract string Url { get; } + + + /// + /// Сравнить текущий объект с другим объектом такого же типа + /// + public int CompareTo(ContentSpec other) + { + return Name.CompareTo(other.Name); + } } } diff --git a/ScadaWeb/ScadaWebCommon5Beta/Plugins/PluginSpec.cs b/ScadaWeb/ScadaWebCommon5Beta/Plugins/PluginSpec.cs index 81447b7a3..e10740d16 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Plugins/PluginSpec.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Plugins/PluginSpec.cs @@ -93,7 +93,18 @@ public virtual List ViewSpecs /// /// Получить спецификации отчётов, которые реализуются плагином /// - public virtual List RepSpecs + public virtual List ReportSpecs + { + get + { + return null; + } + } + + /// + /// Получить спецификации окон данных, которые реализуются плагином + /// + public virtual List DataWindowSpecs { get { diff --git a/ScadaWeb/ScadaWebCommon5Beta/ScadaWebCommon5Beta.csproj b/ScadaWeb/ScadaWebCommon5Beta/ScadaWebCommon5Beta.csproj index 6cacc3e76..fdac36c0c 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/ScadaWebCommon5Beta.csproj +++ b/ScadaWeb/ScadaWebCommon5Beta/ScadaWebCommon5Beta.csproj @@ -53,7 +53,7 @@ - + @@ -62,6 +62,7 @@ + diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/UserContent.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/UserContent.cs new file mode 100644 index 000000000..1c765916b --- /dev/null +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/UserContent.cs @@ -0,0 +1,116 @@ +/* + * Copyright 2016 Mikhail Shiryaev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Product : Rapid SCADA + * Module : ScadaWebCommon + * Summary : Content accessible to the web application user + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + */ + +using Scada.Web.Plugins; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Utils; + +namespace Scada.Web.Shell +{ + /// + /// Content accessible to the web application user + /// Контент, доступный пользователю веб-приложения + /// + public class UserContent + { + /// + /// Журнал + /// + protected readonly Log log; + + + /// + /// Конструктор, ограничивающий создание объекта без параметров + /// + protected UserContent() + { + } + + /// + /// Конструктор + /// + public UserContent(Log log) + { + if (log == null) + throw new ArgumentNullException("log"); + + this.log = log; + Reports = new List(); + DataWindows = new List(); + } + + + /// + /// Получить отчёты, доступные пользователю + /// + public List Reports { get; protected set; } + + /// + /// Получить окна данных, доступные пользователю + /// + public List DataWindows { get; protected set; } + + + /// + /// Инициализировать контент пользователя + /// + public void Init(UserData userData) + { + if (userData == null) + throw new ArgumentNullException("userData"); + + try + { + // получение спецификаций отчётов и окон данных из плагинов + Reports.Clear(); + DataWindows.Clear(); + + if (userData.PluginSpecs != null) + { + foreach (PluginSpec pluginSpec in userData.PluginSpecs) + { + Reports.AddRange(pluginSpec.ReportSpecs); + DataWindows.AddRange(pluginSpec.DataWindowSpecs); + } + } + + Reports.Sort(); + DataWindows.Sort(); + + // добавление окна событий + DataWindows.Insert(0, null); + } + catch (Exception ex) + { + log.WriteException(ex, Localization.UseRussian ? + "Ошибка при инициализации контента пользователя" : + "Error initializing user content"); + } + } + } +} diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/UserMenu.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/UserMenu.cs index 7537a83ae..fddd01b6f 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/UserMenu.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/UserMenu.cs @@ -65,7 +65,7 @@ public UserMenu(Log log) /// /// Получить элементы меню, доступные пользователю /// - public List MenuItems { get; private set; } + public List MenuItems { get; protected set; } /// diff --git a/ScadaWeb/ScadaWebCommon5Beta/UserRights.cs b/ScadaWeb/ScadaWebCommon5Beta/UserRights.cs index 0843c0297..38a373172 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/UserRights.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/UserRights.cs @@ -37,17 +37,21 @@ namespace Scada.Web public class UserRights { /// - /// Право просмотра всех представлений + /// Права на все представления /// - protected bool viewAllViewsRight; + protected EntityRights allViewsRights; /// - /// Право управления для всех представлений + /// Права на весь контент /// - protected bool controlAllViewsRight; + protected EntityRights allContentRights; /// /// Права на предсталения /// protected Dictionary viewRightsDict; + /// + /// Права на контент + /// + protected Dictionary contentRightsDict; /// @@ -70,9 +74,10 @@ public UserRights() /// protected void SetToDefault() { - viewAllViewsRight = false; - controlAllViewsRight = false; + allViewsRights = EntityRights.NoRights; + allContentRights = EntityRights.NoRights; viewRightsDict = null; + contentRightsDict = null; ConfigRight = false; } @@ -89,22 +94,24 @@ public void Init(int roleID, DataAccess dataAccess) if (roleID == BaseValues.Roles.Admin) { - viewAllViewsRight = true; - controlAllViewsRight = true; + allViewsRights = new EntityRights(true, true); + allContentRights = new EntityRights(true, true); ConfigRight = true; } else if (roleID == BaseValues.Roles.Dispatcher) { - viewAllViewsRight = true; - controlAllViewsRight = true; + allViewsRights = new EntityRights(true, true); + allContentRights = new EntityRights(true, true); } else if (roleID == BaseValues.Roles.Guest) { - viewAllViewsRight = true; + allViewsRights = new EntityRights(true, false); + allContentRights = new EntityRights(true, false); } else if (BaseValues.Roles.Custom <= roleID && roleID < BaseValues.Roles.Err) { viewRightsDict = dataAccess.GetViewRights(roleID); + contentRightsDict = dataAccess.GetContentRights(roleID); } } @@ -113,9 +120,9 @@ public void Init(int roleID, DataAccess dataAccess) /// public EntityRights GetViewRights(int viewID) { - if (viewAllViewsRight) + if (allViewsRights.ViewRight) { - return new EntityRights(viewAllViewsRight, controlAllViewsRight); + return allViewsRights; } else { @@ -124,5 +131,22 @@ public EntityRights GetViewRights(int viewID) rights : EntityRights.NoRights; } } + + /// + /// Получить права на контент + /// + public EntityRights GetContentRights(string contentTypeCode) + { + if (allContentRights.ViewRight) + { + return allContentRights; + } + else + { + EntityRights rights; + return contentRightsDict != null && contentRightsDict.TryGetValue(contentTypeCode, out rights) ? + rights : EntityRights.NoRights; + } + } } } \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj index 32c335186..767f23498 100644 --- a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj +++ b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj @@ -111,8 +111,6 @@ - - diff --git a/ScadaWeb/ScadaWebShell5Beta/images/treeview/scheme.png b/ScadaWeb/ScadaWebShell5Beta/images/treeview/scheme.png deleted file mode 100644 index f3eef656b750f75f674f16b3960242c163527372..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 233 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx;}931;MLP~iB|!~f&I`>!UYtfw7~FZY}^^S5{) zmGEo}(=NNM*D|LDam_lw?DJ%MQ>12<Xnweig-%d!J&37l*aGUD&)|_U{MI>qcweFokV5 fT<$uDiHBigku#T)^!6h_XEAuX`njxgN@xNAm@!v! diff --git a/ScadaWeb/ScadaWebShell5Beta/images/treeview/table.png b/ScadaWeb/ScadaWebShell5Beta/images/treeview/table.png deleted file mode 100644 index 1b61744c55b4dc28cf46307bbbfa2deaf9084b7a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 168 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XygXeTLn>}1{rUgjo>{e_v(f1QQ}Ukr|MOIqb8vAPEs|hh z?QwbazyCb<|A`_Gk}fOo6jU@%obcUkuYts#15Mhkg4`19Hs(w$5)1{q&) From 2100a19bb74dab37551fddad2070f794c166be05 Mon Sep 17 00:00:00 2001 From: 2mik Date: Mon, 16 May 2016 15:56:59 +0300 Subject: [PATCH 039/382] PlgTable: development --- .../PlgScheme/AppCode/PlgSchemeSpec.cs | 1 + .../AppCode/{ => Scheme}/SchemeViewSpec.cs | 2 +- .../OpenPlugins/PlgScheme/PlgScheme.csproj | 2 +- .../plugins/Scheme/lang/PlgScheme.en-GB.xml | 4 +- .../plugins/Scheme/lang/PlgScheme.ru-RU.xml | 4 +- .../PlgTable/AppCode/PlgTableSpec.cs | 14 +++- .../AppCode/Table/CustomDataWndSpec.cs | 32 +++++++++ .../PlgTable/AppCode/Table/EventsWndSpec.cs | 67 +++++++++++++++++++ .../PlgTable/AppCode/Table/PlgPhrases.cs | 57 ++++++++++++++++ .../AppCode/{ => Table}/TableViewSpec.cs | 2 +- ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj | 8 ++- .../plugins/Table/lang/PlgTable.en-GB.xml | 6 ++ .../plugins/Table/lang/PlgTable.ru-RU.xml | 6 ++ .../ScadaWebCommon5Beta/DataTransferObject.cs | 3 + ScadaWeb/ScadaWebCommon5Beta/UserData.cs | 8 +++ 15 files changed, 206 insertions(+), 10 deletions(-) rename ScadaWeb/OpenPlugins/PlgScheme/AppCode/{ => Scheme}/SchemeViewSpec.cs (98%) create mode 100644 ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/CustomDataWndSpec.cs create mode 100644 ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/EventsWndSpec.cs create mode 100644 ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/PlgPhrases.cs rename ScadaWeb/OpenPlugins/PlgTable/AppCode/{ => Table}/TableViewSpec.cs (98%) create mode 100644 ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml create mode 100644 ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml diff --git a/ScadaWeb/OpenPlugins/PlgScheme/AppCode/PlgSchemeSpec.cs b/ScadaWeb/OpenPlugins/PlgScheme/AppCode/PlgSchemeSpec.cs index ff5ae726f..416f53dea 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/AppCode/PlgSchemeSpec.cs +++ b/ScadaWeb/OpenPlugins/PlgScheme/AppCode/PlgSchemeSpec.cs @@ -23,6 +23,7 @@ * Modified : 2016 */ +using Scada.Web.Plugins.Scheme; using System.Collections.Generic; using System.IO; diff --git a/ScadaWeb/OpenPlugins/PlgScheme/AppCode/SchemeViewSpec.cs b/ScadaWeb/OpenPlugins/PlgScheme/AppCode/Scheme/SchemeViewSpec.cs similarity index 98% rename from ScadaWeb/OpenPlugins/PlgScheme/AppCode/SchemeViewSpec.cs rename to ScadaWeb/OpenPlugins/PlgScheme/AppCode/Scheme/SchemeViewSpec.cs index e9a4cb76b..4c8e8e028 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/AppCode/SchemeViewSpec.cs +++ b/ScadaWeb/OpenPlugins/PlgScheme/AppCode/Scheme/SchemeViewSpec.cs @@ -23,7 +23,7 @@ * Modified : 2016 */ -namespace Scada.Web.Plugins +namespace Scada.Web.Plugins.Scheme { /// /// Scheme view specification diff --git a/ScadaWeb/OpenPlugins/PlgScheme/PlgScheme.csproj b/ScadaWeb/OpenPlugins/PlgScheme/PlgScheme.csproj index 0a241d2f2..efc72f4f4 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/PlgScheme.csproj +++ b/ScadaWeb/OpenPlugins/PlgScheme/PlgScheme.csproj @@ -100,7 +100,7 @@ - + Scheme.aspx ASPXCodeBehind diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/lang/PlgScheme.en-GB.xml b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/lang/PlgScheme.en-GB.xml index 8b3ed5c91..0148f197f 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/lang/PlgScheme.en-GB.xml +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/lang/PlgScheme.en-GB.xml @@ -1,8 +1,8 @@  - + Scheme loading failed. Try to reload scheme Error updating scheme data Reload - + diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/lang/PlgScheme.ru-RU.xml b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/lang/PlgScheme.ru-RU.xml index fecd748aa..0d0afa947 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/lang/PlgScheme.ru-RU.xml +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/lang/PlgScheme.ru-RU.xml @@ -1,8 +1,8 @@  - + Ошибка при загрузке схемы. Попробуйте перезагрузить схему Ошибка при обновлении данных схемы Перезагрузить - + diff --git a/ScadaWeb/OpenPlugins/PlgTable/AppCode/PlgTableSpec.cs b/ScadaWeb/OpenPlugins/PlgTable/AppCode/PlgTableSpec.cs index 1af9ac370..11a78be1f 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/AppCode/PlgTableSpec.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/AppCode/PlgTableSpec.cs @@ -23,6 +23,7 @@ * Modified : 2016 */ +using Scada.Web.Plugins.Table; using System.Collections.Generic; using System.IO; @@ -95,6 +96,17 @@ public override List ViewSpecs } } + /// + /// Получить спецификации окон данных, которые реализуются плагином + /// + public override List DataWindowSpecs + { + get + { + return new List() { new EventsWndSpec(), new CustomDataWndSpec() }; + } + } + /// /// Инициализировать плагин @@ -103,7 +115,7 @@ public override void Init() { dictUpdater = new DictUpdater( string.Format("{0}Table{1}lang{1}", AppDirs.PluginsDir, Path.DirectorySeparatorChar), - "PlgTable", null, Log); + "PlgTable", PlgPhrases.Init, Log); } /// diff --git a/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/CustomDataWndSpec.cs b/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/CustomDataWndSpec.cs new file mode 100644 index 000000000..82be8e5dc --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/CustomDataWndSpec.cs @@ -0,0 +1,32 @@ +using System; + +namespace Scada.Web.Plugins.Table +{ + [Obsolete("For test purposes")] + public class CustomDataWndSpec : ContentSpec + { + public override string ContentTypeCode + { + get + { + return "CustomData"; + } + } + + public override string Name + { + get + { + return "CustomData"; + } + } + + public override string Url + { + get + { + return "~/plugins/Table/CustomData.aspx"; + } + } + } +} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/EventsWndSpec.cs b/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/EventsWndSpec.cs new file mode 100644 index 000000000..4c8b92401 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/EventsWndSpec.cs @@ -0,0 +1,67 @@ +/* + * Copyright 2016 Mikhail Shiryaev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Product : Rapid SCADA + * Module : PlgTable + * Summary : Events data window specification + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + */ + +namespace Scada.Web.Plugins.Table +{ + /// + /// Events data window specification + /// Спецификация окна данных для отображения событий + /// + public class EventsWndSpec : ContentSpec + { + /// + /// Получить код типа контента + /// + public override string ContentTypeCode + { + get + { + return "Events"; + } + } + + /// + /// Получить наименование контента + /// + public override string Name + { + get + { + return PlgPhrases.EventsTitle; + } + } + + /// + /// Получить ссылку на страницу контента + /// + public override string Url + { + get + { + return "~/plugins/Table/Events.aspx"; + } + } + } +} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/PlgPhrases.cs b/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/PlgPhrases.cs new file mode 100644 index 000000000..98e20bbd9 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/PlgPhrases.cs @@ -0,0 +1,57 @@ +/* + * Copyright 2016 Mikhail Shiryaev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Product : Rapid SCADA + * Module : PlgTable + * Summary : The phrases used by the plugin + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + */ + +#pragma warning disable 1591 // отключение warning CS1591: Missing XML comment for publicly visible type or member + +namespace Scada.Web +{ + /// + /// The phrases used by the plugin + /// Фразы, используемые плагином + /// + public static class PlgPhrases + { + static PlgPhrases() + { + SetToDefault(); + } + + public static string EventsTitle { get; private set; } + + private static void SetToDefault() + { + EventsTitle = Localization.Dict.GetEmptyPhrase("EventsTitle"); + } + + public static void Init() + { + Localization.Dict dict; + if (Localization.Dictionaries.TryGetValue("Scada.Web.Plugins.PlgTable", out dict)) + { + EventsTitle = dict.GetPhrase("EventsTitle", EventsTitle); + } + } + } +} diff --git a/ScadaWeb/OpenPlugins/PlgTable/AppCode/TableViewSpec.cs b/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/TableViewSpec.cs similarity index 98% rename from ScadaWeb/OpenPlugins/PlgTable/AppCode/TableViewSpec.cs rename to ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/TableViewSpec.cs index 134d420ba..1ff08ad9b 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/AppCode/TableViewSpec.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/TableViewSpec.cs @@ -23,7 +23,7 @@ * Modified : 2016 */ -namespace Scada.Web.Plugins +namespace Scada.Web.Plugins.Table { /// /// Table view specification diff --git a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj index 115e2456e..4afc7c0da 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj +++ b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj @@ -58,11 +58,16 @@ + + + + + - + @@ -76,7 +81,6 @@ - 10.0 diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml new file mode 100644 index 000000000..65b0509a2 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml @@ -0,0 +1,6 @@ + + + + Events + + diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml new file mode 100644 index 000000000..3eea0959b --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml @@ -0,0 +1,6 @@ + + + + События + + diff --git a/ScadaWeb/ScadaWebCommon5Beta/DataTransferObject.cs b/ScadaWeb/ScadaWebCommon5Beta/DataTransferObject.cs index 2ff14c7d2..fc7326ff8 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/DataTransferObject.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/DataTransferObject.cs @@ -39,6 +39,9 @@ public DataTransferObject() { } + /// + /// Конструктор + /// public DataTransferObject(object data) : this(true, "") { diff --git a/ScadaWeb/ScadaWebCommon5Beta/UserData.cs b/ScadaWeb/ScadaWebCommon5Beta/UserData.cs index 59215d36d..cca417fa1 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/UserData.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/UserData.cs @@ -116,6 +116,11 @@ private UserData() /// public UserViews UserViews { get; private set; } + /// + /// Получить контент пользователя + /// + public UserContent UserContent { get; private set; } + /// /// Получить ссылку на настройки веб-приложения @@ -151,6 +156,7 @@ private void ClearUser() UserRights = null; UserMenu = null; UserViews = null; + UserContent = null; } /// @@ -264,6 +270,8 @@ public bool Login(string username, string password, out string errMsg) UserMenu.Init(this); UserViews = new UserViews(AppData.Log); UserViews.Init(this, AppData.DataAccess); + UserContent = new UserContent(AppData.Log); + UserContent.Init(this); return true; } else From 641eda4f40972a57a8555ed5fefc5dcff30645c6 Mon Sep 17 00:00:00 2001 From: 2mik Date: Mon, 16 May 2016 16:45:29 +0300 Subject: [PATCH 040/382] ScadaWeb5: content feature works --- .../PlgTable/AppCode/Table/CustomDataWndSpec.cs | 2 +- ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj | 2 +- ScadaWeb/ScadaWebCommon5Beta/Shell/UserContent.cs | 9 ++++----- ScadaWeb/ScadaWebShell5Beta/config/WebSettings.xml | 1 + 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/CustomDataWndSpec.cs b/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/CustomDataWndSpec.cs index 82be8e5dc..09d749f8e 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/CustomDataWndSpec.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/CustomDataWndSpec.cs @@ -17,7 +17,7 @@ public override string Name { get { - return "CustomData"; + return "Custom data"; } } diff --git a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj index 4afc7c0da..ef0cb7104 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj +++ b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj @@ -48,7 +48,6 @@ ..\..\ScadaWebCommon5Beta\bin\Release\ScadaWebCommon5Beta.dll - @@ -71,6 +70,7 @@ + Web.config diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/UserContent.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/UserContent.cs index 1c765916b..c02ad1b01 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/UserContent.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/UserContent.cs @@ -94,16 +94,15 @@ public void Init(UserData userData) { foreach (PluginSpec pluginSpec in userData.PluginSpecs) { - Reports.AddRange(pluginSpec.ReportSpecs); - DataWindows.AddRange(pluginSpec.DataWindowSpecs); + if (pluginSpec.ReportSpecs != null) + Reports.AddRange(pluginSpec.ReportSpecs); + if (pluginSpec.DataWindowSpecs != null) + DataWindows.AddRange(pluginSpec.DataWindowSpecs); } } Reports.Sort(); DataWindows.Sort(); - - // добавление окна событий - DataWindows.Insert(0, null); } catch (Exception ex) { diff --git a/ScadaWeb/ScadaWebShell5Beta/config/WebSettings.xml b/ScadaWeb/ScadaWebShell5Beta/config/WebSettings.xml index 53c43394f..410fd8b73 100644 --- a/ScadaWeb/ScadaWebShell5Beta/config/WebSettings.xml +++ b/ScadaWeb/ScadaWebShell5Beta/config/WebSettings.xml @@ -11,5 +11,6 @@ + \ No newline at end of file From c730fb9793f6718be67e2b41fb3b6b941911ff01 Mon Sep 17 00:00:00 2001 From: 2mik Date: Mon, 16 May 2016 23:05:01 +0300 Subject: [PATCH 041/382] ScadaWeb5: shell development --- .../PlgTable/AppCode/PlgTableSpec.cs | 2 +- .../PlgTable/AppCode/Table/EventsWndSpec.cs | 4 +- ScadaWeb/ScadaWebShell5Beta/MasterMain.Master | 2 +- ScadaWeb/ScadaWebShell5Beta/View.aspx | 4 +- ScadaWeb/ScadaWebShell5Beta/View.aspx.cs | 26 +++++- .../ScadaWebShell5Beta/View.aspx.designer.cs | 18 ----- .../ScadaWebShell5Beta/css/globalvar.less | 4 +- .../ScadaWebShell5Beta/css/mastermain.css | 10 +-- .../ScadaWebShell5Beta/css/mastermain.less | 18 ++--- .../ScadaWebShell5Beta/css/mastermain.min.css | 2 +- ScadaWeb/ScadaWebShell5Beta/css/shellvar.less | 12 +-- ScadaWeb/ScadaWebShell5Beta/css/view.css | 54 +++++++------ ScadaWeb/ScadaWebShell5Beta/css/view.less | 66 ++++++++------- ScadaWeb/ScadaWebShell5Beta/css/view.min.css | 2 +- ScadaWeb/ScadaWebShell5Beta/js/mastermain.js | 66 ++++++++------- ScadaWeb/ScadaWebShell5Beta/js/view.js | 81 +++++++++++++++++++ 16 files changed, 236 insertions(+), 135 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgTable/AppCode/PlgTableSpec.cs b/ScadaWeb/OpenPlugins/PlgTable/AppCode/PlgTableSpec.cs index 11a78be1f..8988256df 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/AppCode/PlgTableSpec.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/AppCode/PlgTableSpec.cs @@ -103,7 +103,7 @@ public override List DataWindowSpecs { get { - return new List() { new EventsWndSpec(), new CustomDataWndSpec() }; + return new List() { new EventsWndSpec(), new CustomDataWndSpec(), new CustomDataWndSpec(), new CustomDataWndSpec(), new CustomDataWndSpec(), new CustomDataWndSpec(), new CustomDataWndSpec() }; } } diff --git a/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/EventsWndSpec.cs b/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/EventsWndSpec.cs index 4c8b92401..3f3a478b0 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/EventsWndSpec.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/EventsWndSpec.cs @@ -49,7 +49,9 @@ public override string Name { get { - return PlgPhrases.EventsTitle; + return "Events"; + // TODO: вернуть обратно + // return PlgPhrases.EventsTitle; } } diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master index 8b8871247..6c2b1000a 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master @@ -54,7 +54,7 @@
<%= GenerateExplorerHtml() %>
-
+
diff --git a/ScadaWeb/ScadaWebShell5Beta/View.aspx b/ScadaWeb/ScadaWebShell5Beta/View.aspx index a1d1e81f0..c783ee18c 100644 --- a/ScadaWeb/ScadaWebShell5Beta/View.aspx +++ b/ScadaWeb/ScadaWebShell5Beta/View.aspx @@ -9,9 +9,7 @@
-
-
-
+
<%= GenerateBottomTabsHtml() %>
diff --git a/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs b/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs index 8dce574e4..c4f187d2f 100644 --- a/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs +++ b/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs @@ -23,8 +23,10 @@ * Modified : 2016 */ +using Scada.Web.Plugins; using Scada.Web.Shell; using System; +using System.Text; namespace Scada.Web { @@ -34,19 +36,37 @@ namespace Scada.Web ///
public partial class WFrmView : System.Web.UI.Page { + private UserData userData; // данные пользователя приложения + + /// - /// Добавить на страницу скрипт загрузки представления - /// + /// Добавить на страницу скрипт загрузки представления + ///
private void AddLoadViewScript(string viewUrl) { ClientScript.RegisterStartupScript(GetType(), "Startup", "scada.view.load('" + ResolveUrl(viewUrl) + "');", true); } + /// + /// Генерировать HTML-код нижних закладок + /// + protected string GenerateBottomTabsHtml() + { + const string TabTemplate = "
{2}
"; + + StringBuilder sbHtml = new StringBuilder(); + + foreach (ContentSpec dataWnd in userData.UserContent.DataWindows) + sbHtml.AppendFormat(TabTemplate, dataWnd.ContentTypeCode, ResolveUrl(dataWnd.Url), dataWnd.Name); + + return sbHtml.ToString(); + } + protected void Page_Load(object sender, EventArgs e) { - UserData userData = UserData.GetUserData(); + userData = UserData.GetUserData(); userData.CheckLoggedOn(true); if (!IsPostBack) diff --git a/ScadaWeb/ScadaWebShell5Beta/View.aspx.designer.cs b/ScadaWeb/ScadaWebShell5Beta/View.aspx.designer.cs index ab2095d59..52ee57706 100644 --- a/ScadaWeb/ScadaWebShell5Beta/View.aspx.designer.cs +++ b/ScadaWeb/ScadaWebShell5Beta/View.aspx.designer.cs @@ -11,23 +11,5 @@ namespace Scada.Web { public partial class WFrmView { - - /// - /// lblEventsTab control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label lblEventsTab; - - /// - /// Label1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label Label1; } } diff --git a/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less b/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less index cadd3f3db..ce6eb9cda 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less @@ -1,5 +1,5 @@ -@content-fore-color: #444; -@content-back-color: #f1f1f1; +@content-back-color: #f1f1f1; +@content-fore-color: #444; @panel-border-color: #e5e5e5; @default-font-family: 'Open Sans', sans-serif; /*Verdana, Geneva, Tahoma, sans-serif;*/ @default-font-size: 12px; \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css index 93e070076..0c6128578 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css @@ -186,8 +186,8 @@ a:active { position: relative; top: -2px; } -/* Collapse Pane */ -#divMainCollapsePane { +/* Collapse Left Pane */ +#divMainCollapseLeftPaneBtn { position: absolute; bottom: 0; width: 220px; @@ -195,16 +195,16 @@ a:active { margin: 0 0 0 30px; padding: 0px 10px; color: #9ca1a6; + cursor: pointer; font-size: 13px; line-height: 30px; } -#divMainCollapsePane i { +#divMainCollapseLeftPaneBtn i { margin-right: 10px; font-size: 19px; position: relative; top: 2px; } -#divMainCollapsePane:hover { +#divMainCollapseLeftPaneBtn:hover { color: #00b9eb; - cursor: pointer; } \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less index c13446095..386d8ed6d 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less @@ -3,8 +3,8 @@ @header-heigth: 30px; @left-tabs-heigth: 30px; -@collapse-pane-heigth: 30px; @left-pane-width: 250px; +@collapse-btn-heigth: 30px; body { margin: 0; @@ -122,7 +122,7 @@ a:active { } #divMainLeftPane .tool-window { - height: calc(~"100% - "@collapse-pane-heigth); + height: calc(~"100% - "@collapse-btn-heigth); margin: 0 0 0 @left-tabs-heigth; padding: 5px 0 0 0; display: none; /*block*/ @@ -226,28 +226,28 @@ a:active { top: -2px; } -/* Collapse Pane */ +/* Collapse Left Pane */ -#divMainCollapsePane { +#divMainCollapseLeftPaneBtn { position: absolute; bottom: 0; width: @left-pane-width - @left-tabs-heigth; - height: @collapse-pane-heigth; + height: @collapse-btn-heigth; margin: 0 0 0 @left-tabs-heigth; padding: 0px 10px; color: @menu-fore-color-dark; + cursor: pointer; font-size: 13px; - line-height: @collapse-pane-heigth; + line-height: @collapse-btn-heigth; } -#divMainCollapsePane i { +#divMainCollapseLeftPaneBtn i { margin-right: 10px; font-size: 19px; position: relative; top: 2px; } -#divMainCollapsePane:hover { +#divMainCollapseLeftPaneBtn:hover { color: @menu-item-hover-fore-color; - cursor: pointer; } diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css index dd072af90..18b07306e 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 250px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;white-space:nowrap;}#divMainHeader .hdr-btn{height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;display:inline-block;font-size:13px;text-decoration:none;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenuBtn{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;}#divMainUserMenu{float:right;height:30px;}#lblMainNormalViewBtn{position:fixed;top:0;right:0;height:30px;padding:0 10px;background-color:#fff;display:none;line-height:30px;opacity:.5;cursor:pointer;color:#9ca1a6;font-size:13px;}#lblMainNormalViewBtn:hover{color:#00b9eb;opacity:1;}#divMainLeftPane{position:fixed;left:0;top:30px;width:250px;min-height:250px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 30px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainLeftPane .tree-view .node:hover{background-color:#191e23;color:#00b9eb;}#divMainLeftPane .tree-view .node.selected{background-color:#0073aa;color:#fff;}#divMainLeftPane .tree-view .node.disabled{color:#9ca1a6;cursor:default;}#divMainLeftPane .tree-view .expander::before{font-size:10px;}#divMainMenu .node[data-level="1"],#divMainMenu .node[data-level="2"],#divMainMenu .node[data-level="3"],#divMainMenu .node[data-level="4"],#divMainMenu .node[data-level="5"]{color:#9ca1a6;font-size:13px;}#divMainMenu .tree-view .node{padding:5px 10px 5px 10px;}#divMainMenu .tree-view .expander.left{display:none;width:0;}#divMainMenu .tree-view .expander.right{display:table-cell;width:20px;}#divMainExplorer{font-size:13px;line-height:13px;}#divMainExplorer .tree-view .node{padding:7px 5px 3px 10px;}#divMainExplorer .tree-view .icon{width:21px;}#divMainExplorer .tree-view img{position:relative;top:-2px;}#divMainCollapsePane{position:absolute;bottom:0;width:220px;height:30px;margin:0 0 0 30px;padding:0 10px;color:#9ca1a6;font-size:13px;line-height:30px;}#divMainCollapsePane i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapsePane:hover{color:#00b9eb;cursor:pointer;} \ No newline at end of file +body{margin:0;padding:30px 0 0 250px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;white-space:nowrap;}#divMainHeader .hdr-btn{height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;display:inline-block;font-size:13px;text-decoration:none;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenuBtn{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;}#divMainUserMenu{float:right;height:30px;}#lblMainNormalViewBtn{position:fixed;top:0;right:0;height:30px;padding:0 10px;background-color:#fff;display:none;line-height:30px;opacity:.5;cursor:pointer;color:#9ca1a6;font-size:13px;}#lblMainNormalViewBtn:hover{color:#00b9eb;opacity:1;}#divMainLeftPane{position:fixed;left:0;top:30px;width:250px;min-height:250px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 30px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainLeftPane .tree-view .node:hover{background-color:#191e23;color:#00b9eb;}#divMainLeftPane .tree-view .node.selected{background-color:#0073aa;color:#fff;}#divMainLeftPane .tree-view .node.disabled{color:#9ca1a6;cursor:default;}#divMainLeftPane .tree-view .expander::before{font-size:10px;}#divMainMenu .node[data-level="1"],#divMainMenu .node[data-level="2"],#divMainMenu .node[data-level="3"],#divMainMenu .node[data-level="4"],#divMainMenu .node[data-level="5"]{color:#9ca1a6;font-size:13px;}#divMainMenu .tree-view .node{padding:5px 10px 5px 10px;}#divMainMenu .tree-view .expander.left{display:none;width:0;}#divMainMenu .tree-view .expander.right{display:table-cell;width:20px;}#divMainExplorer{font-size:13px;line-height:13px;}#divMainExplorer .tree-view .node{padding:7px 5px 3px 10px;}#divMainExplorer .tree-view .icon{width:21px;}#divMainExplorer .tree-view img{position:relative;top:-2px;}#divMainCollapseLeftPaneBtn{position:absolute;bottom:0;width:220px;height:30px;margin:0 0 0 30px;padding:0 10px;color:#9ca1a6;cursor:pointer;font-size:13px;line-height:30px;}#divMainCollapseLeftPaneBtn i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapseLeftPaneBtn:hover{color:#00b9eb;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/shellvar.less b/ScadaWeb/ScadaWebShell5Beta/css/shellvar.less index 04914fb34..133207b1c 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/shellvar.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/shellvar.less @@ -1,9 +1,11 @@ -@menu-fore-color: #eee; -@menu-fore-color-dark: #9ca1a6; -@menu-back-color: #23282d; +@menu-back-color: #23282d; @menu-back-color-air: white; -@menu-item-hover-fore-color: #00b9eb; +@menu-fore-color: #eee; +@menu-fore-color-dark: #9ca1a6; +@menu-fore-color-air: #444; +@menu-fore-color-air-light: #8f8f8f; @menu-item-hover-back-color: #32373c; @menu-item-hover-back-color-dark: #191e23; -@menu-item-selected-fore-color: white; +@menu-item-hover-fore-color: #00b9eb; @menu-item-selected-back-color: #0073aa; +@menu-item-selected-fore-color: white; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/view.css b/ScadaWeb/ScadaWebShell5Beta/css/view.css index 22d8687fc..5cc2ec636 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/view.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/view.css @@ -1,62 +1,70 @@ /*Verdana, Geneva, Tahoma, sans-serif;*/ #divView { height: 100%; + overflow: hidden; } #divView iframe { + width: 100%; margin: 0; padding: 0; - width: 100%; border: none; display: block; - overflow: hidden; -} -#frameView { - height: calc(100% - 207px - 30px); } #divViewSplitter { height: 7px; background-color: #ddd; cursor: ns-resize; - /*display: none;*/ + display: none; + /*block*/ } #divDataWindow { height: 200px; background-color: white; border-bottom: 1px solid #e5e5e5; - /*display: none;*/ + display: none; + /*block*/ } #frameDataWindow { height: 100%; } #divBottomTabs { - height: 30px; background-color: white; color: #444; - font-size: 13px; - overflow: hidden; } -#divBottomTabs .tab { - margin: 0 0 0 5px; +#divBottomTabsContainer { + max-width: calc(100% - 40px); + padding: 0 5px; + display: inline-block; +} +#divBottomTabsContainer .tab { + height: 30px; + margin: 0 5px 0 0; padding: 0 10px; + cursor: pointer; display: inline-block; + font-size: 13px; line-height: 30px; white-space: nowrap; } -#divBottomTabs .tab.selected { +#divBottomTabsContainer .tab.selected { font-weight: 600; } -#divBottomTabs .tab:first-child { - margin-left: 10px; -} -#divBottomTabs .tab:hover { - color: #00b9eb; - cursor: pointer; -} -#divCollapseDataWindow { +#divCollapseDataWindowBtn { + width: 40px; + height: 100%; color: #8f8f8f; -} -#divCollapseDataWindow i { + cursor: pointer; + display: none; + /*inline-block*/ font-size: 19px; + text-align: center; + vertical-align: top; +} +#divCollapseDataWindowBtn i { position: relative; top: 2px; +} +#divBottomTabsContainer .tab:hover, +#divCollapseDataWindowBtn:hover { + color: #00b9eb; } \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/view.less b/ScadaWeb/ScadaWebShell5Beta/css/view.less index 94e7a15d0..717e87c30 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/view.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/view.less @@ -2,38 +2,34 @@ @import "shellvar.less"; @bottom-tabs-heigth: 30px; -@bottom-menu-back-color: white; -@bottom-menu-border-color: #ddd; +@collapse-btn-width: 40px; +@splitter-color: #ddd; #divView { height: 100%; + overflow: hidden; } #divView iframe { + width: 100%; margin: 0; padding: 0; - width: 100%; border: none; display: block; - overflow: hidden; -} - -#frameView { - height: calc(~"100% - 207px - "@bottom-tabs-heigth); } #divViewSplitter { height: 7px; - background-color: #ddd; + background-color: @splitter-color; cursor: ns-resize; - /*display: none;*/ + display: none; /*block*/ } #divDataWindow { height: 200px; background-color: white; border-bottom: 1px solid @panel-border-color; - /*display: none;*/ + display: none; /*block*/ } #frameDataWindow { @@ -41,40 +37,48 @@ } #divBottomTabs { - height: @bottom-tabs-heigth; - background-color: @bottom-menu-back-color; - color: @content-fore-color; - font-size: 13px; - overflow: hidden; + background-color: @menu-back-color-air; + color: @menu-fore-color-air; +} + +#divBottomTabsContainer { + max-width: calc(~"100% - "@collapse-btn-width); + padding: 0 5px; + display: inline-block; } -#divBottomTabs .tab { - margin: 0 0 0 5px; +#divBottomTabsContainer .tab { + height: @bottom-tabs-heigth; + margin: 0 5px 0 0; padding: 0 10px; + cursor: pointer; display: inline-block; + font-size: 13px; line-height: @bottom-tabs-heigth; white-space: nowrap; } -#divBottomTabs .tab.selected { +#divBottomTabsContainer .tab.selected { font-weight: 600; } -#divBottomTabs .tab:first-child { - margin-left: 10px; -} - -#divBottomTabs .tab:hover { - color: @menu-item-hover-fore-color; +#divCollapseDataWindowBtn { + width: @collapse-btn-width; + height: 100%; + color: @menu-fore-color-air-light; cursor: pointer; + display: none; /*inline-block*/ + font-size: 19px; + text-align: center; + vertical-align: top; } -#divCollapseDataWindow { - color: #8f8f8f; -} - -#divCollapseDataWindow i { - font-size: 19px; +#divCollapseDataWindowBtn i { position: relative; top: 2px; } + +#divBottomTabsContainer .tab:hover, +#divCollapseDataWindowBtn:hover { + color: @menu-item-hover-fore-color; +} diff --git a/ScadaWeb/ScadaWebShell5Beta/css/view.min.css b/ScadaWeb/ScadaWebShell5Beta/css/view.min.css index 586c3357a..4664d6beb 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/view.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/view.min.css @@ -1 +1 @@ -#divView{height:100%;}#divView iframe{margin:0;padding:0;width:100%;border:none;display:block;overflow:hidden;}#frameView{height:calc(100% - 207px - 30px);}#divViewSplitter{height:7px;background-color:#ddd;cursor:ns-resize;}#divDataWindow{height:200px;background-color:#fff;border-bottom:1px solid #e5e5e5;}#frameDataWindow{height:100%;}#divBottomTabs{height:30px;background-color:#fff;color:#444;font-size:13px;overflow:hidden;}#divBottomTabs .tab{margin:0 0 0 5px;padding:0 10px;display:inline-block;line-height:30px;white-space:nowrap;}#divBottomTabs .tab.selected{font-weight:600;}#divBottomTabs .tab:first-child{margin-left:10px;}#divBottomTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divCollapseDataWindow{color:#8f8f8f;}#divCollapseDataWindow i{font-size:19px;position:relative;top:2px;} \ No newline at end of file +#divView{height:100%;overflow:hidden;}#divView iframe{width:100%;margin:0;padding:0;border:none;display:block;}#divViewSplitter{height:7px;background-color:#ddd;cursor:ns-resize;display:none;}#divDataWindow{height:200px;background-color:#fff;border-bottom:1px solid #e5e5e5;display:none;}#frameDataWindow{height:100%;}#divBottomTabs{background-color:#fff;color:#444;}#divBottomTabsContainer{max-width:calc(100% - 40px);padding:0 5px;display:inline-block;}#divBottomTabsContainer .tab{height:30px;margin:0 5px 0 0;padding:0 10px;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;white-space:nowrap;}#divBottomTabsContainer .tab.selected{font-weight:600;}#divCollapseDataWindowBtn{width:40px;height:100%;color:#8f8f8f;cursor:pointer;display:none;font-size:19px;text-align:center;vertical-align:top;}#divCollapseDataWindowBtn i{position:relative;top:2px;}#divBottomTabsContainer .tab:hover,#divCollapseDataWindowBtn:hover{color:#00b9eb;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js index 456e1a9b2..4a86ff648 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js @@ -8,6 +8,31 @@ scada.masterMain = { // Fullsreen mode is active isFullscreen: false, + + // Hide the left pane + _hideLeftPane: function () { + $("#divMainLeftPane").css("display", "none"); + $("body").css("padding-left", "0"); + }, + + // Show the left pane + _showLeftPane: function () { + $("body").css("padding-left", ""); + $("#divMainLeftPane").css("display", ""); + }, + + // Hide the page header + _hideHeader: function () { + $("#divMainHeader").css("display", "none"); + $("body").css("padding-top", "0"); + }, + + // Show the page header + _showHeader: function () { + $("#divMainHeader").css("display", ""); + $("body").css("padding-top", ""); + }, + // Update layout of the master page updateLayout: function () { var divMainHeader = $("#divMainHeader"); @@ -19,6 +44,7 @@ scada.masterMain = { divMainLeftPane.outerHeight(paneH); divMainTabs.outerWidth(paneH); $("#divMainContent").outerHeight(paneH); + $(window).trigger("scada:updateLayout"); }, // Choose a tool window according to the current URL and activate it @@ -60,42 +86,20 @@ scada.masterMain = { } }, - // Hide the left pane - hideLeftPane: function () { - $("#divMainLeftPane").css("display", "none"); - $("body").css("padding-left", "0"); - }, - - // Show the left pane - showLeftPane: function () { - $("body").css("padding-left", ""); - $("#divMainLeftPane").css("display", ""); - }, - // Collapse the left pane and show the menu button collapseLeftPane: function () { $("#spanMainShowMenuBtn").css("display", "inline-block"); - this.hideLeftPane(); + this._hideLeftPane(); this.leftPaneExpanded = false; + $(window).trigger("scada:updateLayout"); }, // Expand the left pane and hide the menu button expandLeftPane: function () { - this.showLeftPane(); + this._showLeftPane(); $("#spanMainShowMenuBtn").css("display", "none"); this.leftPaneExpanded = true; - }, - - // Hide the page header - hideHeader: function () { - $("#divMainHeader").css("display", "none"); - $("body").css("padding-top", "0"); - }, - - // Show the page header - showHeader: function () { - $("#divMainHeader").css("display", ""); - $("body").css("padding-top", ""); + $(window).trigger("scada:updateLayout"); }, // Hide all the menus and switch browser to fullscreen mode @@ -104,9 +108,9 @@ scada.masterMain = { this.isFullscreen = true; if (this.leftPaneExpanded) { - this.hideLeftPane(); + this._hideLeftPane(); } - this.hideHeader(); + this._hideHeader(); $("#lblMainNormalViewBtn").css("display", "inline-block"); scada.utils.requestFullscreen(); @@ -120,9 +124,9 @@ scada.masterMain = { this.isFullscreen = false; $("#lblMainNormalViewBtn").css("display", "none"); - this.showHeader(); + this._showHeader(); if (this.leftPaneExpanded) { - this.showLeftPane(); + this._showLeftPane(); } scada.utils.exitFullscreen(); @@ -156,7 +160,7 @@ $(document).ready(function () { }); // collapse the left pane on the button click - $("#divMainCollapsePane").click(function () { + $("#divMainCollapseLeftPaneBtn").click(function () { scada.masterMain.collapseLeftPane(); }); diff --git a/ScadaWeb/ScadaWebShell5Beta/js/view.js b/ScadaWeb/ScadaWebShell5Beta/js/view.js index b716ca31a..5397fb59a 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/view.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/view.js @@ -4,6 +4,43 @@ scada.view = { // Page title just after loading initialPageTitle: "", + // Minimum height of a view or data window + minWindowHeight: 100, + + + // Get outer height of the specified object considering its displaying + _getOuterHeight: function (jqObj) { + return jqObj.css("display") == "none" ? 0 : jqObj.outerHeight(); + }, + + // Hide bottom pane if no data windows exist + hideBottomTabsIfEmpty: function () { + if ($("#divBottomTabsContainer .tab").length == 0) { + $("#divBottomTabs").css("display", "none"); + } + }, + + // Update layout of the master page + updateLayout: function () { + var divView = $("#divView"); + var divViewSplitter = $("#divViewSplitter"); + var divDataWindow = $("#divDataWindow"); + var divBottomTabs = $("#divBottomTabs"); + + var totalH = divView.innerHeight(); + var splitterH = this._getOuterHeight(divViewSplitter); + var dataWindowH = this._getOuterHeight(divDataWindow); + var bottomTabsH = this._getOuterHeight(divBottomTabs); + $("#frameView").outerHeight(totalH - splitterH - dataWindowH - bottomTabsH); + }, + + // Append updateLayout() method in the message queue. + // Allows to avoid Chrome bug when divBottomTabs height is calculated wrong + enqueueUpdateLayout: function () { + var thisView = this; + setTimeout(function () { thisView.updateLayout(); }, 0); + }, + // Load view load: function (url) { document.title = this.initialPageTitle; @@ -15,9 +52,53 @@ scada.view = { document.title = frameView[0].contentWindow.document.title; }) .attr("src", url); + }, + + // Make the data window, that corresponds a clicked tab, visible + activateDataWindow: function (divClickedTab) { + $("#frameDataWindow").attr("src", divClickedTab.attr("data-url")); + $("#divBottomTabsContainer .tab").removeClass("selected"); + divClickedTab.addClass("selected"); + + $("#divViewSplitter").css("display", "block"); + $("#divDataWindow").css("display", "block"); + $("#divCollapseDataWindowBtn").css("display", "inline-block"); + scada.view.updateLayout(); + }, + + // Collapse a data window and release resources + collapseDataWindow: function () { + $("#frameDataWindow").attr("src", ""); + $("#divBottomTabsContainer .tab").removeClass("selected"); + + $("#divViewSplitter").css("display", "none"); + $("#divDataWindow").css("display", "none"); + $("#divCollapseDataWindowBtn").css("display", "none"); + scada.view.updateLayout(); } }; $(document).ready(function () { scada.view.initialPageTitle = document.title; + scada.view.hideBottomTabsIfEmpty(); + scada.view.enqueueUpdateLayout(); + + // update layout on window resize and master page layout changes + $(window) + .resize(function () { + scada.view.updateLayout(); + }) + .on("scada:updateLayout", function () { + scada.view.updateLayout(); + }); + + // activate a data window if the tab is clicked + $("#divBottomTabsContainer .tab").click(function () { + scada.view.activateDataWindow($(this)); + }); + + // collapse a data window on the button click + $("#divCollapseDataWindowBtn").click(function () { + scada.view.collapseDataWindow(); + }); }); \ No newline at end of file From c78a63693f17db1e4b80badf434611b77fe1d346 Mon Sep 17 00:00:00 2001 From: 2mik Date: Tue, 17 May 2016 19:24:16 +0300 Subject: [PATCH 042/382] ScadaWeb5: layout complete! --- .../ScadaWebCommon5Beta/Shell/UserViews.cs | 24 +- ScadaWeb/ScadaWebShell5Beta/MasterMain.Master | 1 + .../ScadaWebShell5Beta.csproj | 2 + ScadaWeb/ScadaWebShell5Beta/View.aspx | 9 +- ScadaWeb/ScadaWebShell5Beta/css/view.css | 12 +- ScadaWeb/ScadaWebShell5Beta/css/view.less | 14 +- ScadaWeb/ScadaWebShell5Beta/css/view.min.css | 2 +- ScadaWeb/ScadaWebShell5Beta/js/mastermain.js | 52 ++++- ScadaWeb/ScadaWebShell5Beta/js/scadaevents.js | 16 ++ ScadaWeb/ScadaWebShell5Beta/js/scadautils.js | 31 +++ ScadaWeb/ScadaWebShell5Beta/js/splitter.js | 211 ++++++++++++++++++ ScadaWeb/ScadaWebShell5Beta/js/treeview.js | 5 +- ScadaWeb/ScadaWebShell5Beta/js/view.js | 136 +++++++++-- 13 files changed, 461 insertions(+), 54 deletions(-) create mode 100644 ScadaWeb/ScadaWebShell5Beta/js/scadaevents.js create mode 100644 ScadaWeb/ScadaWebShell5Beta/js/splitter.js diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/UserViews.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/UserViews.cs index 5673843bd..cb5f0bdff 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/UserViews.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/UserViews.cs @@ -187,21 +187,33 @@ public string GetViewUrl(int viewID) } /// - /// Получить ссылку первого доступного представления + /// Получить первое доступное представление /// - public string GetFirstViewUrl() + public bool GetFirstView(out int viewID, out string viewUrl) { + viewID = 0; + viewUrl = ""; + try { ViewNode viewNode = FindNonEmptyViewNode(ViewNodes); - return viewNode == null ? "" : viewNode.ViewUrl; + if (viewNode == null) + { + return false; + } + else + { + viewID = viewNode.ViewID; + viewUrl = viewNode.ViewUrl; + return true; + } } catch (Exception ex) { log.WriteException(ex, Localization.UseRussian ? - "Ошибка при получении ссылки первого доступного представления" : - "Error getting URL of the first accessible view"); - return ""; + "Ошибка при получении первого доступного представления" : + "Error getting the first accessible view"); + return false; } } } diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master index 6c2b1000a..cffab8c16 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master @@ -18,6 +18,7 @@ + + -
- +
+
diff --git a/ScadaWeb/ScadaWebShell5Beta/css/view.css b/ScadaWeb/ScadaWebShell5Beta/css/view.css index 5cc2ec636..21d2cb1bd 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/view.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/view.css @@ -1,10 +1,14 @@ /*Verdana, Geneva, Tahoma, sans-serif;*/ -#divView { +#divViewContent { height: 100%; overflow: hidden; } -#divView iframe { +#divView { + min-height: 50px; +} +#divViewContent iframe { width: 100%; + height: 100%; margin: 0; padding: 0; border: none; @@ -19,14 +23,12 @@ } #divDataWindow { height: 200px; + min-height: 50px; background-color: white; border-bottom: 1px solid #e5e5e5; display: none; /*block*/ } -#frameDataWindow { - height: 100%; -} #divBottomTabs { background-color: white; color: #444; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/view.less b/ScadaWeb/ScadaWebShell5Beta/css/view.less index 717e87c30..3179cbccf 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/view.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/view.less @@ -5,13 +5,18 @@ @collapse-btn-width: 40px; @splitter-color: #ddd; -#divView { +#divViewContent { height: 100%; overflow: hidden; } -#divView iframe { +#divView { + min-height: 50px; +} + +#divViewContent iframe { width: 100%; + height: 100%; margin: 0; padding: 0; border: none; @@ -27,15 +32,12 @@ #divDataWindow { height: 200px; + min-height: 50px; background-color: white; border-bottom: 1px solid @panel-border-color; display: none; /*block*/ } -#frameDataWindow { - height: 100%; -} - #divBottomTabs { background-color: @menu-back-color-air; color: @menu-fore-color-air; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/view.min.css b/ScadaWeb/ScadaWebShell5Beta/css/view.min.css index 4664d6beb..67064b8a2 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/view.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/view.min.css @@ -1 +1 @@ -#divView{height:100%;overflow:hidden;}#divView iframe{width:100%;margin:0;padding:0;border:none;display:block;}#divViewSplitter{height:7px;background-color:#ddd;cursor:ns-resize;display:none;}#divDataWindow{height:200px;background-color:#fff;border-bottom:1px solid #e5e5e5;display:none;}#frameDataWindow{height:100%;}#divBottomTabs{background-color:#fff;color:#444;}#divBottomTabsContainer{max-width:calc(100% - 40px);padding:0 5px;display:inline-block;}#divBottomTabsContainer .tab{height:30px;margin:0 5px 0 0;padding:0 10px;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;white-space:nowrap;}#divBottomTabsContainer .tab.selected{font-weight:600;}#divCollapseDataWindowBtn{width:40px;height:100%;color:#8f8f8f;cursor:pointer;display:none;font-size:19px;text-align:center;vertical-align:top;}#divCollapseDataWindowBtn i{position:relative;top:2px;}#divBottomTabsContainer .tab:hover,#divCollapseDataWindowBtn:hover{color:#00b9eb;} \ No newline at end of file +#divViewContent{height:100%;overflow:hidden;}#divView{min-height:50px;}#divViewContent iframe{width:100%;height:100%;margin:0;padding:0;border:none;display:block;}#divViewSplitter{height:7px;background-color:#ddd;cursor:ns-resize;display:none;}#divDataWindow{height:200px;min-height:50px;background-color:#fff;border-bottom:1px solid #e5e5e5;display:none;}#divBottomTabs{background-color:#fff;color:#444;}#divBottomTabsContainer{max-width:calc(100% - 40px);padding:0 5px;display:inline-block;}#divBottomTabsContainer .tab{height:30px;margin:0 5px 0 0;padding:0 10px;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;white-space:nowrap;}#divBottomTabsContainer .tab.selected{font-weight:600;}#divCollapseDataWindowBtn{width:40px;height:100%;color:#8f8f8f;cursor:pointer;display:none;font-size:19px;text-align:center;vertical-align:top;}#divCollapseDataWindowBtn i{position:relative;top:2px;}#divBottomTabsContainer .tab:hover,#divCollapseDataWindowBtn:hover{color:#00b9eb;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js index 4a86ff648..8d0336e05 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js @@ -33,6 +33,11 @@ scada.masterMain = { $("body").css("padding-top", ""); }, + // Save the left pane visibility in the cookies + _saveLeftPaneVisible: function () { + scada.utils.setCookie("LeftPaneVisible", this.leftPaneExpanded); + }, + // Update layout of the master page updateLayout: function () { var divMainHeader = $("#divMainHeader"); @@ -44,7 +49,7 @@ scada.masterMain = { divMainLeftPane.outerHeight(paneH); divMainTabs.outerWidth(paneH); $("#divMainContent").outerHeight(paneH); - $(window).trigger("scada:updateLayout"); + $(window).trigger(scada.events.updateLayout); }, // Choose a tool window according to the current URL and activate it @@ -91,7 +96,8 @@ scada.masterMain = { $("#spanMainShowMenuBtn").css("display", "inline-block"); this._hideLeftPane(); this.leftPaneExpanded = false; - $(window).trigger("scada:updateLayout"); + this._saveLeftPaneVisible(); + $(window).trigger(scada.events.updateLayout); }, // Expand the left pane and hide the menu button @@ -99,7 +105,8 @@ scada.masterMain = { this._showLeftPane(); $("#spanMainShowMenuBtn").css("display", "none"); this.leftPaneExpanded = true; - $(window).trigger("scada:updateLayout"); + this._saveLeftPaneVisible(); + $(window).trigger(scada.events.updateLayout); }, // Hide all the menus and switch browser to fullscreen mode @@ -134,10 +141,18 @@ scada.masterMain = { } }, + // Load page visual state from the cookies + loadVisualState: function () { + var leftPaneVisible = scada.utils.getCookie("LeftPaneVisible"); + if (leftPaneVisible == "false") { + this.collapseLeftPane(); + } + }, + // Load view without reloading the whole page if possible loadView: function (viewID, viewUrl) { - if (scada.view.load) { - scada.view.load(viewUrl); + if (scada.view.loadView) { + scada.view.loadView(viewID, viewUrl); } else { location = scada.env.rootPath + "View.aspx?viewID=" + viewID; } @@ -145,8 +160,15 @@ scada.masterMain = { }; $(document).ready(function () { + // unbind events to avoid doubling in case of using ASP.NET AJAX + $(window).off(); + $(document).off(); + $("body").off(); + + // page setup scada.masterMain.updateLayout(); scada.masterMain.chooseToolWindow(); + scada.masterMain.loadVisualState(); scada.treeView.prepare(); // update layout on window resize @@ -155,27 +177,37 @@ $(document).ready(function () { }); // activate a tool window if the tab is clicked - $("#divMainTabs .tab").click(function () { + $("#divMainTabs .tab") + .off() + .click(function () { scada.masterMain.activateToolWindow($(this)); }); // collapse the left pane on the button click - $("#divMainCollapseLeftPaneBtn").click(function () { + $("#divMainCollapseLeftPaneBtn") + .off() + .click(function () { scada.masterMain.collapseLeftPane(); }); // expand the left pane on the button click - $("#spanMainShowMenuBtn").click(function () { + $("#spanMainShowMenuBtn") + .off() + .click(function () { scada.masterMain.expandLeftPane(); }); // switch to full screen mode on the button click - $("#lblMainFullscreenBtn").click(function () { + $("#lblMainFullscreenBtn") + .off() + .click(function () { scada.masterMain.switchToFullscreen(); }); // switch to normal view mode on the button click - $("#lblMainNormalViewBtn").click(function () { + $("#lblMainNormalViewBtn") + .off() + .click(function () { scada.masterMain.switchToNormalView(); }); diff --git a/ScadaWeb/ScadaWebShell5Beta/js/scadaevents.js b/ScadaWeb/ScadaWebShell5Beta/js/scadaevents.js new file mode 100644 index 000000000..ceb5983c2 --- /dev/null +++ b/ScadaWeb/ScadaWebShell5Beta/js/scadaevents.js @@ -0,0 +1,16 @@ +/* + * JavaScript event names used by the shell + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + */ + +// Rapid SCADA namespace +var scada = scada || {}; + +// JavaScript event names object +scada.events = { + // Page layout should be updated + updateLayout: "scada:updateLayout" +}; \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/js/scadautils.js b/ScadaWeb/ScadaWebShell5Beta/js/scadautils.js index 9701e5859..8b86dec24 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/scadautils.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/scadautils.js @@ -9,7 +9,38 @@ // Rapid SCADA namespace var scada = scada || {}; +// JavaScript utilities object scada.utils = { + // Default cookie expiration period in days + cookieExpiration: 7, + + // Get cookie + getCookie: function (name) { + var cookie = " " + document.cookie; + var search = " " + name + "="; + var offset = cookie.indexOf(search); + + if (offset >= 0) { + offset += search.length; + var end = cookie.indexOf(";", offset) + + if (end < 0) + end = cookie.length; + + return decodeURIComponent(cookie.substring(offset, end)); + } else { + return null; + } + }, + + // Set cookie + setCookie: function (name, value, opt_expDays) { + var expDays = opt_expDays ? opt_expDays : this.cookieExpiration; + var expires = new Date(); + expires.setDate(expires.getDate() + expDays); + document.cookie = name + "=" + encodeURIComponent(value) + "; expires=" + expires.toUTCString(); + }, + // Returns the current time string getCurTime: function () { return new Date().toLocaleTimeString("en-GB"); diff --git a/ScadaWeb/ScadaWebShell5Beta/js/splitter.js b/ScadaWeb/ScadaWebShell5Beta/js/splitter.js new file mode 100644 index 000000000..aabcacaf6 --- /dev/null +++ b/ScadaWeb/ScadaWebShell5Beta/js/splitter.js @@ -0,0 +1,211 @@ +/* + * Splitter control + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + */ + +/* + * Requires: + * - jquery + */ + +// Rapid SCADA namespace +var scada = scada || {}; + +/********** Splitter bulk **********/ + +// Splitter bulk contains the splitter and the resized divs +// Splitter bulk type +scada.SplitterBulk = function () { + // Splitter is in resizing mode + this._isResizing = false; + + // Current X coordinate of the cursor + this._curX = 0; + + // Current Y coordinate of the cursor + this._curY = 0; + + // The splitter + this.splitter = null; + + // The left or top div + this.prevDiv = null; + + // The right or bottom div + this.nextDiv = null; + + // The splitter orientation is horizontal + this.isHorizontal = true; + + // Default minimum size of the resized divs + this.minSize = 50; +}; + +// Get minimum width of the resized div +scada.SplitterBulk.prototype._getMinWidth = function (jqObj) { + var minWidth = parseInt(jqObj.css("min-width"), 10); + return minWidth ? minWidth : this.minSize; +}; + +// Get minimum height of the resized div +scada.SplitterBulk.prototype._getMinHeight = function (jqObj) { + var minHeight = parseInt(jqObj.css("min-height"), 10); + return minHeight ? minHeight : this.minSize; +}; + +// Add overlay div into the resized div too allow receiving events over iframe +scada.SplitterBulk.prototype._addOverlay = function () { + var overlay = $("
").css({ + "position": "fixed", + "left": 0, + "top": 0, + "width": "100%", + "height": "100%", + "background-color": "gray", + "cursor": this.isHorizontal ? "ns-resize" : "ew-resize", + "opacity": 0, + "z-index": 10000 + }); + $("body").append(overlay); +}; + +// Remove overlay div +scada.SplitterBulk.prototype._removeOverlay = function () { + $("div.splitter-overlay").detach(); +}; + +// Enter resizing mode +scada.SplitterBulk.prototype._startResizing = function (x, y) { + this._isResizing = true; + this._curX = x; + this._curY = y; + this._addOverlay(); // allow to receive events +}; + +// Change splitter position according to the cursor coordinates +scada.SplitterBulk.prototype._changePosition = function (x, y) { + if (this._isResizing) { + // resize divs + if (this.isHorizontal) { + var nextDivCurH = this.nextDiv.height(); + var prevDivCurH = this.prevDiv.height(); + var deltaY = y - this._curY; + var delta = deltaY >= 0 ? + Math.min(nextDivCurH - this._getMinHeight(this.nextDiv), deltaY) : + Math.max(this._getMinHeight(this.prevDiv) - prevDivCurH, deltaY); + this._curY += delta; + + this.prevDiv.height(prevDivCurH + delta); + this.nextDiv.height(nextDivCurH - delta); + } else { + var nextDivCurW = this.nextDiv.width(); + var prevDivCurW = this.prevDiv.width(); + var deltaX = x - this._curX; + var delta = deltaX >= 0 ? + Math.min(nextDivCurW - this._getMinWidth(this.nextDiv), deltaX) : + Math.max(this._getMinWidth(this.prevDiv) - prevDivCurW, deltaX); + this._curX += delta; + + this.prevDiv.width(this.prevDiv.width() + delta); + this.nextDiv.width(this.nextDiv.width() - delta); + } + } +}; + +// Exit resizing mode +scada.SplitterBulk.prototype.stopResizing = function (opt_stopResizingCallback) { + if (this._isResizing) { + this._isResizing = false; + this._removeOverlay(); + + if (opt_stopResizingCallback) { + opt_stopResizingCallback(this); + } + } +}; + +// Bind splitter events +scada.SplitterBulk.prototype.bindEvents = function () { + var thisBulk = this; + + this.splitter + .off() + .on("mousedown touchstart", function (event) { + if (event.type == "touchstart") { + $(this).off("mousedown"); + event = event.originalEvent.touches[0]; + } + + thisBulk._startResizing(event.pageX, event.pageY); + }); + + $("body").on("mousemove touchmove", function (event) { + if (event.type == "touchmove") { + $(this).off("mousedown"); + event = event.originalEvent.touches[0]; + } + + thisBulk._changePosition(event.pageX, event.pageY); + }); +}; + +/********** Splitters processing **********/ + +// Splitters processing object +scada.splitter = { + // Splitter bulk array + _splitterBulks: null, + + // Callback function on exit splitter resizing mode + _stopResizingCallback: null, + + // Create splitter bulk array and bind bulk events + _createBulks: function () { + this._splitterBulks = []; + var thisObj = this; + + $(".splitter").each(function () { + var bulk = new scada.SplitterBulk(); + bulk.splitter = $(this); + bulk.prevDiv = bulk.splitter.prev("div"); + bulk.nextDiv = bulk.splitter.next("div"); + bulk.isHorizontal = !bulk.splitter.hasClass("vert"); + + if (bulk.prevDiv.length > 0 && bulk.nextDiv.length > 0) { + bulk.bindEvents(); + thisObj._splitterBulks.push(bulk); + } + }); + }, + + // Stop resizing all the splitters + _stopAllResizing: function () { + for (var bulk of this._splitterBulks) { + bulk.stopResizing(this._stopResizingCallback); + } + }, + + // Bind events for all the splitters + _bindCommonEvents: function () { + var thisObj = this; + + $("body").on("mouseup mouseleave touchend touchleave touchcancel", function () { + thisObj._stopAllResizing(); + }); + + $(window).resize(function () { + thisObj._stopAllResizing(); + }); + }, + + // Prepare splitters to work. + // stopResizingCallback is function (splitterBulk) + prepare: function (opt_stopResizingCallback) { + this._stopResizingCallback = opt_stopResizingCallback; + this._createBulks(); + this._bindCommonEvents(); + } +}; \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/js/treeview.js b/ScadaWeb/ScadaWebShell5Beta/js/treeview.js index 0ec149045..c28772d18 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/treeview.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/treeview.js @@ -15,6 +15,7 @@ // Rapid SCADA namespace var scada = scada || {}; +// Tree views processing object scada.treeView = { // Expand or collapse tree node _toggleTreeNode: function (divExpander) { @@ -56,7 +57,9 @@ scada.treeView = { // go to link or toggle tree node on click var thisTreeView = this; - allNodes.click(function () { + allNodes + .off() + .click(function () { var link = $(this).find("a"); if (link.length > 0) { diff --git a/ScadaWeb/ScadaWebShell5Beta/js/view.js b/ScadaWeb/ScadaWebShell5Beta/js/view.js index 5397fb59a..30fe3b57f 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/view.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/view.js @@ -1,11 +1,14 @@ var scada = scada || {}; scada.view = { + // Initial url of the active data window + _activeDataWindowUrl: "", + // Page title just after loading initialPageTitle: "", - // Minimum height of a view or data window - minWindowHeight: 100, + // Current view ID + viewID: 0, // Get outer height of the specified object considering its displaying @@ -13,6 +16,62 @@ scada.view = { return jqObj.css("display") == "none" ? 0 : jqObj.outerHeight(); }, + // Load data window with view ID query parameter + _loadDataWindow(initialUrl) { + this._activeDataWindowUrl = initialUrl; + + if (initialUrl) { + var url = this.viewID > 0 ? initialUrl.indexOf("?") >= 0 ? + initialUrl + "&viewID=" + this.viewID : + initialUrl + "?viewID=" + this.viewID : + initialUrl; + $("#frameDataWindow").attr("src", url); + } else { + $("#frameDataWindow").attr("src", ""); + } + }, + + // Load the splitter position from cookies + _loadSplitterPosition: function () { + var dataWindowHeight = scada.utils.getCookie("DataWindowHeight") || $("#divDataWindow").outerHeight(); + var minHeight = parseInt($("#divDataWindow").css("min-height"), 10); + var maxHeight = $("#divViewContent").innerHeight() - + parseInt($("#divView").css("min-height"), 10) - + $("#divViewSplitter").outerHeight() - + $("#divBottomTabs").outerHeight(); + + if (dataWindowHeight > maxHeight) { + dataWindowHeight = maxHeight; + } + + if (dataWindowHeight < minHeight) { + dataWindowHeight = minHeight; + } + + $("#divDataWindow").outerHeight(dataWindowHeight); + }, + + // Load active data window URL from the cookies + _loadActiveDataWindow: function () { + var activeDataWindow = scada.utils.getCookie("ActiveDataWindow"); + var thisView = this; + + if (activeDataWindow) { + $("#divBottomTabsContainer .tab").each(function () { + var tabUrl = $(this).attr("data-url"); + if (activeDataWindow == tabUrl) { + thisView.activateDataWindow($(this)); + return false; // break the loop + } + }); + } + }, + + // Save active data window URL in the cookies + _saveActiveDataWindow: function () { + scada.utils.setCookie("ActiveDataWindow", this._activeDataWindowUrl); + }, + // Hide bottom pane if no data windows exist hideBottomTabsIfEmpty: function () { if ($("#divBottomTabsContainer .tab").length == 0) { @@ -22,16 +81,16 @@ scada.view = { // Update layout of the master page updateLayout: function () { - var divView = $("#divView"); + var divViewContent = $("#divViewContent"); var divViewSplitter = $("#divViewSplitter"); var divDataWindow = $("#divDataWindow"); var divBottomTabs = $("#divBottomTabs"); - var totalH = divView.innerHeight(); + var totalH = divViewContent.innerHeight(); var splitterH = this._getOuterHeight(divViewSplitter); var dataWindowH = this._getOuterHeight(divDataWindow); var bottomTabsH = this._getOuterHeight(divBottomTabs); - $("#frameView").outerHeight(totalH - splitterH - dataWindowH - bottomTabsH); + $("#divView").outerHeight(totalH - splitterH - dataWindowH - bottomTabsH); }, // Append updateLayout() method in the message queue. @@ -41,46 +100,70 @@ scada.view = { setTimeout(function () { thisView.updateLayout(); }, 0); }, - // Load view - load: function (url) { - document.title = this.initialPageTitle; - var frameView = $("#frameView"); - - frameView - .load(function () { - // set the page title the same as the frame title - document.title = frameView[0].contentWindow.document.title; - }) - .attr("src", url); - }, - // Make the data window, that corresponds a clicked tab, visible activateDataWindow: function (divClickedTab) { - $("#frameDataWindow").attr("src", divClickedTab.attr("data-url")); + this._loadDataWindow(divClickedTab.attr("data-url")); $("#divBottomTabsContainer .tab").removeClass("selected"); divClickedTab.addClass("selected"); $("#divViewSplitter").css("display", "block"); $("#divDataWindow").css("display", "block"); $("#divCollapseDataWindowBtn").css("display", "inline-block"); - scada.view.updateLayout(); + + this._saveActiveDataWindow(); + this.updateLayout(); }, // Collapse a data window and release resources collapseDataWindow: function () { - $("#frameDataWindow").attr("src", ""); + this._loadDataWindow(""); $("#divBottomTabsContainer .tab").removeClass("selected"); $("#divViewSplitter").css("display", "none"); $("#divDataWindow").css("display", "none"); $("#divCollapseDataWindowBtn").css("display", "none"); - scada.view.updateLayout(); + + this._saveActiveDataWindow(); + this.updateLayout(); + }, + + // Load the specified view and reload an active data window + loadView: function (viewID, viewUrl) { + // load view + document.title = this.initialPageTitle; + this.viewID = viewID; + var frameView = $("#frameView"); + + frameView + .load(function () { + // set the page title the same as the frame title + document.title = frameView[0].contentWindow.document.title; + }) + .attr("src", viewUrl); + + // reload a data window with the new view ID + this._loadDataWindow(this._activeDataWindowUrl); + }, + + // Load page visual state from the cookies + loadVisualState: function () { + this._loadSplitterPosition(); + this._loadActiveDataWindow(); + }, + + // Save the splitter position in the cookies + saveSplitterPosition: function () { + var dataWindowHeight = $("#divDataWindow").outerHeight(); + scada.utils.setCookie("DataWindowHeight", dataWindowHeight); } }; $(document).ready(function () { + // the order of the calls below is important scada.view.initialPageTitle = document.title; scada.view.hideBottomTabsIfEmpty(); + scada.view.loadView(initialViewID, initialViewUrl); // arguments are defined in View.aspx + scada.view.loadVisualState(); scada.view.enqueueUpdateLayout(); // update layout on window resize and master page layout changes @@ -88,10 +171,17 @@ $(document).ready(function () { .resize(function () { scada.view.updateLayout(); }) - .on("scada:updateLayout", function () { + .on(scada.events.updateLayout, function () { scada.view.updateLayout(); }); + // operate the splitter + scada.splitter.prepare(function (splitterBulk) { + if (splitterBulk.splitter.attr("id") == "divViewSplitter") { + scada.view.saveSplitterPosition(); + } + }); + // activate a data window if the tab is clicked $("#divBottomTabsContainer .tab").click(function () { scada.view.activateDataWindow($(this)); From a482f57e8f8b78514c77010f9937e1ac6b24ad82 Mon Sep 17 00:00:00 2001 From: 2mik Date: Tue, 17 May 2016 19:24:39 +0300 Subject: [PATCH 043/382] ScadaWeb5: the same as the previous --- ScadaWeb/ScadaWebShell5Beta/View.aspx.cs | 36 +++++++++--------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs b/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs index c4f187d2f..23beb8b68 100644 --- a/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs +++ b/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs @@ -36,17 +36,10 @@ namespace Scada.Web ///
public partial class WFrmView : System.Web.UI.Page { - private UserData userData; // данные пользователя приложения - - - /// - /// Добавить на страницу скрипт загрузки представления - /// - private void AddLoadViewScript(string viewUrl) - { - ClientScript.RegisterStartupScript(GetType(), "Startup", - "scada.view.load('" + ResolveUrl(viewUrl) + "');", true); - } + private UserData userData; // данные пользователя приложения + protected int initialViewID; // ид. первоначального представления + protected string initialViewUrl; // ссылка первоначального представления + /// /// Генерировать HTML-код нижних закладок @@ -69,20 +62,17 @@ protected void Page_Load(object sender, EventArgs e) userData = UserData.GetUserData(); userData.CheckLoggedOn(true); - if (!IsPostBack) - { - // получение ид. представления для загрузки - int viewID; - int.TryParse(Request.QueryString["viewID"], out viewID); + // получение ид. представления для загрузки + int.TryParse(Request.QueryString["viewID"], out initialViewID); - // получение ссылки представления - string viewUrl = viewID > 0 ? - userData.UserViews.GetViewUrl(viewID) : - userData.UserViews.GetFirstViewUrl(); + // получение ссылки представления + if (initialViewID > 0) + initialViewUrl = userData.UserViews.GetViewUrl(initialViewID); + else + userData.UserViews.GetFirstView(out initialViewID, out initialViewUrl); - // добавление скрипта загрузки представления - AddLoadViewScript(string.IsNullOrEmpty(viewUrl) ? UrlTemplates.NoView : viewUrl); - } + if (string.IsNullOrEmpty(initialViewUrl)) + initialViewUrl = ResolveUrl(UrlTemplates.NoView); } } } \ No newline at end of file From 74a0d2ac25dd1397cc1f56827528a60b2174c0ac Mon Sep 17 00:00:00 2001 From: 2mik Date: Wed, 18 May 2016 01:04:16 +0300 Subject: [PATCH 044/382] ScadaWeb5: splitter fixes for iPad --- ScadaWeb/ScadaWebShell5Beta/css/view.css | 8 +++++++- ScadaWeb/ScadaWebShell5Beta/css/view.less | 5 ++++- ScadaWeb/ScadaWebShell5Beta/css/view.min.css | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/ScadaWeb/ScadaWebShell5Beta/css/view.css b/ScadaWeb/ScadaWebShell5Beta/css/view.css index 21d2cb1bd..2756fb015 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/view.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/view.css @@ -5,6 +5,8 @@ } #divView { min-height: 50px; + overflow: hidden; + /*for iPad*/ } #divViewContent iframe { width: 100%; @@ -15,8 +17,10 @@ display: block; } #divViewSplitter { - height: 7px; + height: 10px; background-color: #ddd; + border-bottom: 3px solid #f1f1f1; + /*pretend splitter thinner*/ cursor: ns-resize; display: none; /*block*/ @@ -28,6 +32,8 @@ border-bottom: 1px solid #e5e5e5; display: none; /*block*/ + overflow: hidden; + /*for iPad*/ } #divBottomTabs { background-color: white; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/view.less b/ScadaWeb/ScadaWebShell5Beta/css/view.less index 3179cbccf..a612b05b4 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/view.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/view.less @@ -12,6 +12,7 @@ #divView { min-height: 50px; + overflow: hidden; /*for iPad*/ } #divViewContent iframe { @@ -24,8 +25,9 @@ } #divViewSplitter { - height: 7px; + height: 10px; background-color: @splitter-color; + border-bottom: 3px solid @content-back-color; /*pretend splitter thinner*/ cursor: ns-resize; display: none; /*block*/ } @@ -36,6 +38,7 @@ background-color: white; border-bottom: 1px solid @panel-border-color; display: none; /*block*/ + overflow: hidden; /*for iPad*/ } #divBottomTabs { diff --git a/ScadaWeb/ScadaWebShell5Beta/css/view.min.css b/ScadaWeb/ScadaWebShell5Beta/css/view.min.css index 67064b8a2..ecd5d147e 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/view.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/view.min.css @@ -1 +1 @@ -#divViewContent{height:100%;overflow:hidden;}#divView{min-height:50px;}#divViewContent iframe{width:100%;height:100%;margin:0;padding:0;border:none;display:block;}#divViewSplitter{height:7px;background-color:#ddd;cursor:ns-resize;display:none;}#divDataWindow{height:200px;min-height:50px;background-color:#fff;border-bottom:1px solid #e5e5e5;display:none;}#divBottomTabs{background-color:#fff;color:#444;}#divBottomTabsContainer{max-width:calc(100% - 40px);padding:0 5px;display:inline-block;}#divBottomTabsContainer .tab{height:30px;margin:0 5px 0 0;padding:0 10px;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;white-space:nowrap;}#divBottomTabsContainer .tab.selected{font-weight:600;}#divCollapseDataWindowBtn{width:40px;height:100%;color:#8f8f8f;cursor:pointer;display:none;font-size:19px;text-align:center;vertical-align:top;}#divCollapseDataWindowBtn i{position:relative;top:2px;}#divBottomTabsContainer .tab:hover,#divCollapseDataWindowBtn:hover{color:#00b9eb;} \ No newline at end of file +#divViewContent{height:100%;overflow:hidden;}#divView{min-height:50px;overflow:hidden;}#divViewContent iframe{width:100%;height:100%;margin:0;padding:0;border:none;display:block;}#divViewSplitter{height:10px;background-color:#ddd;border-bottom:3px solid #f1f1f1;cursor:ns-resize;display:none;}#divDataWindow{height:200px;min-height:50px;background-color:#fff;border-bottom:1px solid #e5e5e5;display:none;overflow:hidden;}#divBottomTabs{background-color:#fff;color:#444;}#divBottomTabsContainer{max-width:calc(100% - 40px);padding:0 5px;display:inline-block;}#divBottomTabsContainer .tab{height:30px;margin:0 5px 0 0;padding:0 10px;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;white-space:nowrap;}#divBottomTabsContainer .tab.selected{font-weight:600;}#divCollapseDataWindowBtn{width:40px;height:100%;color:#8f8f8f;cursor:pointer;display:none;font-size:19px;text-align:center;vertical-align:top;}#divCollapseDataWindowBtn i{position:relative;top:2px;}#divBottomTabsContainer .tab:hover,#divCollapseDataWindowBtn:hover{color:#00b9eb;} \ No newline at end of file From effe407eb44a444b4a92806d47791979ce48aa76 Mon Sep 17 00:00:00 2001 From: 2mik Date: Wed, 18 May 2016 10:22:46 +0300 Subject: [PATCH 045/382] ScadaAdmin: create channels name length fix --- ScadaAdmin/ScadaAdmin/AppCode/AppPhrases.cs | 10 ++++- ScadaAdmin/ScadaAdmin/AppCode/CreateCnls.cs | 43 +++++++++++-------- ScadaAdmin/ScadaAdmin/FrmAbout.cs | 2 +- .../ScadaAdmin/Lang/ScadaAdmin.en-GB.xml | 2 + 4 files changed, 37 insertions(+), 20 deletions(-) diff --git a/ScadaAdmin/ScadaAdmin/AppCode/AppPhrases.cs b/ScadaAdmin/ScadaAdmin/AppCode/AppPhrases.cs index a77f207a7..6d1b4f31c 100644 --- a/ScadaAdmin/ScadaAdmin/AppCode/AppPhrases.cs +++ b/ScadaAdmin/ScadaAdmin/AppCode/AppPhrases.cs @@ -1,5 +1,5 @@ /* - * Copyright 2015 Mikhail Shiryaev + * Copyright 2016 Mikhail Shiryaev * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ * * Author : Mikhail Shiryaev * Created : 2014 - * Modified : 2015 + * Modified : 2016 */ using Scada; @@ -114,6 +114,8 @@ static AppPhrases() public static string CmdValsNotFound { get; private set; } public static string CreateCnlsImpossible { get; private set; } public static string CreateCnlsStart { get; private set; } + public static string InCnlNameTrancated { get; private set; } + public static string CtrlCnlNameTrancated { get; private set; } public static string NumFormatNotFound { get; private set; } public static string TextFormatNotFound { get; private set; } public static string AddedInCnlsCount { get; private set; } @@ -280,6 +282,8 @@ private static void SetToDefault() CmdValsNotFound = "Не найдены значения команды \"{0}\"."; CreateCnlsImpossible = "Создание каналов невозможно."; CreateCnlsStart = "Создание каналов."; + InCnlNameTrancated = "Наименование входного канала {0} было обрезано."; + CtrlCnlNameTrancated = "Наименование канала управления {0} было обрезано."; NumFormatNotFound = "Не найден формат входного канала {0}. Описание формата: числовой, количество знаков дробной части равно {1}."; TextFormatNotFound = "Не найден формат входного канала {0}. Описание формата: текстовый."; AddedInCnlsCount = "Добавлено входных каналов: {0}."; @@ -452,6 +456,8 @@ public static void Init() CmdValsNotFound = dict.GetPhrase("CmdValsNotFound", CmdValsNotFound); CreateCnlsImpossible = dict.GetPhrase("CreateCnlsImpossible", CreateCnlsImpossible); CreateCnlsStart = dict.GetPhrase("CreateCnlsStart", CreateCnlsStart); + InCnlNameTrancated = dict.GetPhrase("InCnlNameTrancated", InCnlNameTrancated); + CtrlCnlNameTrancated = dict.GetPhrase("CtrlCnlNameTrancated", CtrlCnlNameTrancated); NumFormatNotFound = dict.GetPhrase("NumFormatNotFound", NumFormatNotFound); TextFormatNotFound = dict.GetPhrase("TextFormatNotFound", TextFormatNotFound); AddedInCnlsCount = dict.GetPhrase("AddedInCnlsCount", AddedInCnlsCount); diff --git a/ScadaAdmin/ScadaAdmin/AppCode/CreateCnls.cs b/ScadaAdmin/ScadaAdmin/AppCode/CreateCnls.cs index 7b2669f1e..f8c0955e6 100644 --- a/ScadaAdmin/ScadaAdmin/AppCode/CreateCnls.cs +++ b/ScadaAdmin/ScadaAdmin/AppCode/CreateCnls.cs @@ -1,5 +1,5 @@ /* - * Copyright 2015 Mikhail Shiryaev + * Copyright 2016 Mikhail Shiryaev * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ * * Author : Mikhail Shiryaev * Created : 2015 - * Modified : 2015 + * Modified : 2016 */ using Scada; @@ -326,7 +326,16 @@ private static DataRow CreateInCnlRow(DataTable tblInCnl, DataTable tblFormat, DataRow newInCnlRow = tblInCnl.NewRow(); newInCnlRow["CnlNum"] = inCnl.CnlNum; newInCnlRow["Active"] = true; - newInCnlRow["Name"] = kpNameToInsert + inCnl.CnlName; + + int maxCnlNameLen = tblInCnl.Columns["Name"].MaxLength; + string cnlName = kpNameToInsert + inCnl.CnlName; + if (cnlName.Length > maxCnlNameLen) + { + cnlName = cnlName.Substring(0, maxCnlNameLen); + writer.WriteLine(string.Format(AppPhrases.InCnlNameTrancated, inCnl.CnlNum)); + } + newInCnlRow["Name"] = cnlName; + newInCnlRow["CnlTypeID"] = inCnl.CnlTypeID; newInCnlRow["ObjNum"] = objNum; newInCnlRow["KPNum"] = kpNum; @@ -342,26 +351,17 @@ private static DataRow CreateInCnlRow(DataTable tblInCnl, DataTable tblFormat, { int ind = tblFormat.DefaultView.Find(new object[] { true, inCnl.DecDigits }); if (ind >= 0) - { newInCnlRow["FormatID"] = tblFormat.DefaultView[ind]["FormatID"]; - } else - { - writer.WriteLine(string.Format( - AppPhrases.NumFormatNotFound, inCnl.CnlNum, inCnl.DecDigits)); - } + writer.WriteLine(string.Format(AppPhrases.NumFormatNotFound, inCnl.CnlNum, inCnl.DecDigits)); } else { int ind = tblFormat.DefaultView.Find(new object[] { false, DBNull.Value }); if (ind >= 0) - { newInCnlRow["FormatID"] = tblFormat.DefaultView[ind]["FormatID"]; - } else - { writer.WriteLine(string.Format(AppPhrases.TextFormatNotFound, inCnl.CnlNum)); - } } newInCnlRow["UnitID"] = string.IsNullOrEmpty(inCnl.UnitName) ? @@ -390,12 +390,21 @@ private static DataRow CreateInCnlRow(DataTable tblInCnl, DataTable tblFormat, /// Создать строку канала управления /// private static DataRow CreateCtrlCnlRow(DataTable tblCtrlCnl, SortedList cmdValList, - KPView.CtrlCnlPrototype ctrlCnl, object objNum, int kpNum, string kpNameToInsert) + KPView.CtrlCnlPrototype ctrlCnl, object objNum, int kpNum, string kpNameToInsert, StreamWriter writer) { DataRow newCtrlCnlRow = tblCtrlCnl.NewRow(); newCtrlCnlRow["CtrlCnlNum"] = ctrlCnl.CtrlCnlNum; newCtrlCnlRow["Active"] = true; - newCtrlCnlRow["Name"] = kpNameToInsert + ctrlCnl.CtrlCnlName; + + int maxCtrlCnlNameLen = tblCtrlCnl.Columns["Name"].MaxLength; + string ctrlCnlName = kpNameToInsert + ctrlCnl.CtrlCnlName; + if (ctrlCnlName.Length > maxCtrlCnlNameLen) + { + ctrlCnlName = ctrlCnlName.Substring(0, maxCtrlCnlNameLen); + writer.WriteLine(string.Format(AppPhrases.CtrlCnlNameTrancated, ctrlCnl.CtrlCnlNum)); + } + newCtrlCnlRow["Name"] = ctrlCnlName; + newCtrlCnlRow["CmdTypeID"] = ctrlCnl.CmdTypeID; newCtrlCnlRow["ObjNum"] = objNum; newCtrlCnlRow["KPNum"] = kpNum; @@ -434,7 +443,7 @@ private static bool UpdateCnls(DataTable dataTable, string descr, StreamWriter w } else { - writer.WriteLine(string.Format(descr, updRowCnt) + ". " + + writer.WriteLine(string.Format(descr, updRowCnt) + " " + string.Format(AppPhrases.ErrorsCount, errRowCnt)); foreach (DataRow row in rowsInError) writer.WriteLine(string.Format(AppPhrases.CnlError, row[0], row.RowError)); @@ -733,7 +742,7 @@ public static bool CreateChannels(List kpInfoList, bool insertKPName, { ctrlCnl.CtrlCnlNum = ctrlCnlNum; DataRow newCtrlCnlRow = CreateCtrlCnlRow(tblCtrlCnl, cmdValList, - ctrlCnl, objNum, kpNum, kpNameToInsert); + ctrlCnl, objNum, kpNum, kpNameToInsert, writer); tblCtrlCnl.Rows.Add(newCtrlCnlRow); ctrlCnlNum++; } diff --git a/ScadaAdmin/ScadaAdmin/FrmAbout.cs b/ScadaAdmin/ScadaAdmin/FrmAbout.cs index 9442de825..093a3692a 100644 --- a/ScadaAdmin/ScadaAdmin/FrmAbout.cs +++ b/ScadaAdmin/ScadaAdmin/FrmAbout.cs @@ -37,7 +37,7 @@ namespace ScadaAdmin ///
public partial class FrmAbout : Form { - private const string Version = "4.5.0.2"; // версия приложения + private const string Version = "4.5.0.3"; // версия приложения private static FrmAbout frmAbout = null; // экземпляр формы о программе private bool inited; // форма инициализирована diff --git a/ScadaAdmin/ScadaAdmin/Lang/ScadaAdmin.en-GB.xml b/ScadaAdmin/ScadaAdmin/Lang/ScadaAdmin.en-GB.xml index 6398b50ab..54c20cd29 100644 --- a/ScadaAdmin/ScadaAdmin/Lang/ScadaAdmin.en-GB.xml +++ b/ScadaAdmin/ScadaAdmin/Lang/ScadaAdmin.en-GB.xml @@ -120,6 +120,8 @@ Command values "{0}" not found. It is imposible to create channels. Create channels. + Input channel {0} name was trancated. + Output channel {0} name was trancated. Input channel {0} format not found. Format description: numeric, number of decimal digits is {1}. Input channel {0} format not found. Format description: textual. Count of added input channels: {0}. From 9d8247638a7be790768e90156e6794beb72fba87 Mon Sep 17 00:00:00 2001 From: 2mik Date: Wed, 18 May 2016 13:26:58 +0300 Subject: [PATCH 046/382] ScadaWeb5: view hub or api --- ScadaWeb/ScadaWebShell5Beta/MasterMain.Master | 6 +- .../ScadaWebShell5Beta.csproj | 9 ++- ScadaWeb/ScadaWebShell5Beta/View.aspx | 2 +- ScadaWeb/ScadaWebShell5Beta/css/view.css | 4 +- ScadaWeb/ScadaWebShell5Beta/css/view.less | 4 +- .../js/{ => api}/scadaevents.js | 0 .../js/{ => api}/scadautils.js | 0 ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js | 74 +++++++++++++++++++ .../js/{ => controls}/splitter.js | 0 .../js/{ => controls}/treeview.js | 0 10 files changed, 87 insertions(+), 12 deletions(-) rename ScadaWeb/ScadaWebShell5Beta/js/{ => api}/scadaevents.js (100%) rename ScadaWeb/ScadaWebShell5Beta/js/{ => api}/scadautils.js (100%) create mode 100644 ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js rename ScadaWeb/ScadaWebShell5Beta/js/{ => controls}/splitter.js (100%) rename ScadaWeb/ScadaWebShell5Beta/js/{ => controls}/treeview.js (100%) diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master index cffab8c16..34f552eea 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master @@ -17,9 +17,9 @@ - - - + + + + - + - - + + + + + + + + +
+
+ + + +
+
+
+ + diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs new file mode 100644 index 000000000..523a110ae --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.UI; +using System.Web.UI.WebControls; + +namespace Scada.Web.Plugins.Table +{ + public partial class WFrmEvents : System.Web.UI.Page + { + protected void Page_Load(object sender, EventArgs e) + { + + } + } +} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.designer.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.designer.cs new file mode 100644 index 000000000..0fbb4fe57 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.designer.cs @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Scada.Web.Plugins.Table { + + + public partial class WFrmEvents { + + /// + /// frmEvents control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlForm frmEvents; + } +} diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx new file mode 100644 index 000000000..2bee24e68 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx @@ -0,0 +1,23 @@ +<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Table.aspx.cs" Inherits="Scada.Web.Plugins.Table.WFrmTable" %> + + + + + + Table - Rapid SCADA + + + + + + +
+
+ + + +
+
+
+ + diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs new file mode 100644 index 000000000..2a9740e29 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.UI; +using System.Web.UI.WebControls; + +namespace Scada.Web.Plugins.Table +{ + public partial class WFrmTable : System.Web.UI.Page + { + protected void Page_Load(object sender, EventArgs e) + { + + } + } +} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.designer.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.designer.cs new file mode 100644 index 000000000..0e3db0941 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.designer.cs @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Scada.Web.Plugins.Table { + + + public partial class WFrmTable { + + /// + /// frmTable control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlForm frmTable; + } +} diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js new file mode 100644 index 000000000..3e9616d7d --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js @@ -0,0 +1,2 @@ +$(document).ready(function () { +}); diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js new file mode 100644 index 000000000..3ee02a781 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js @@ -0,0 +1,29 @@ +$(document).ready(function () { + /*$(window).on(scada.eventTypes.viewNavigate, function (event) { + console.log(event.type); + });*/ + + $(window).on( + scada.eventTypes.viewTitleChanged + " " + + scada.eventTypes.viewNavigate + " " + + scada.eventTypes.viewDateChanged, function (event, extraParams) { + var divLog = $("#divLog"); + divLog.html(divLog.html() + event.type + " - " + extraParams + "
") + }); + + var viewHub = scada.viewHubLocator.getViewHub(); + + if (viewHub) { + $("#btn1").click(function () { + viewHub.notify(window, scada.eventTypes.viewTitleChanged, "new title " + (new Date())); + }); + + $("#btn2").click(function () { + viewHub.notify(window, scada.eventTypes.viewNavigate, 100); + }); + + $("#btn3").click(function () { + viewHub.notify(window, scada.eventTypes.viewDateChanged, new Date()); + }); + } +}); diff --git a/ScadaWeb/ScadaWebCommon5Beta/Plugins/DataWindowSpec.cs b/ScadaWeb/ScadaWebCommon5Beta/Plugins/DataWindowSpec.cs new file mode 100644 index 000000000..182b7e65d --- /dev/null +++ b/ScadaWeb/ScadaWebCommon5Beta/Plugins/DataWindowSpec.cs @@ -0,0 +1,45 @@ +/* + * Copyright 2016 Mikhail Shiryaev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Product : Rapid SCADA + * Module : ScadaWebCommon + * Summary : The base class for data window specification + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + */ + +namespace Scada.Web.Plugins +{ + /// + /// The base class for data window specification + /// Родительский класс спецификации окна данных + /// + public abstract class DataWindowSpec : ContentSpec + { + /// + /// Получить признак, что содержимое окна зависит от текущего представления + /// + public virtual bool DependsOnView + { + get + { + return false; + } + } + } +} diff --git a/ScadaWeb/ScadaWebCommon5Beta/Plugins/PluginSpec.cs b/ScadaWeb/ScadaWebCommon5Beta/Plugins/PluginSpec.cs index e10740d16..f6a0e18ad 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Plugins/PluginSpec.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Plugins/PluginSpec.cs @@ -104,7 +104,7 @@ public virtual List ReportSpecs /// /// Получить спецификации окон данных, которые реализуются плагином /// - public virtual List DataWindowSpecs + public virtual List DataWindowSpecs { get { diff --git a/ScadaWeb/ScadaWebCommon5Beta/ScadaWebCommon5Beta.csproj b/ScadaWeb/ScadaWebCommon5Beta/ScadaWebCommon5Beta.csproj index fdac36c0c..eaab9f059 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/ScadaWebCommon5Beta.csproj +++ b/ScadaWeb/ScadaWebCommon5Beta/ScadaWebCommon5Beta.csproj @@ -52,6 +52,7 @@ + diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master index 151072a63..c481cfa0d 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master @@ -19,7 +19,7 @@ - + + - + + + + + + - - - diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemecommon.js b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemecommon.js index 4baade201..2c295662a 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemecommon.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemecommon.js @@ -6,7 +6,8 @@ * Modified : 2016 * * Requires: - * nothing + * - jquery + * - viewhub.js */ // Rapid SCADA namespace @@ -118,4 +119,7 @@ scada.scheme.calc = { return this.isTrue(comp1, comp2, cond.LogicalOperator); } } -} \ No newline at end of file +} + +// The view hub object +scada.scheme.viewHub = scada.viewHubLocator.getViewHub(); \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js index c3da905c7..357ad15bd 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js @@ -8,6 +8,7 @@ * Requires: * - jquery * - clientapi.js + * - eventtypes.js * - schemecommon.js * * Inheritance hierarchy: @@ -138,8 +139,8 @@ scada.scheme.SchemeRenderer.prototype.createDom = function (elem, renderContext) // set title if (props.Title) { document.title = props.Title + " - Rapid SCADA"; - if (window.parent) { - window.parent.document.title = document.title; + if (scada.scheme.viewHub) { + scada.scheme.viewHub.notify(window, scada.eventTypes.viewTitleChanged, document.title); } } @@ -266,18 +267,28 @@ scada.scheme.ElementRenderer.prototype.bindAction = function (jqObj, elem) { var props = elem.props; if (props.Action) { - jqObj.css("cursor", "pointer"); + var dialogs = scada.scheme.viewHub ? scada.scheme.viewHub.dialogs : null; - jqObj.click(function () { + jqObj + .css("cursor", "pointer") + .click(function () { switch (props.Action) { case Actions.DRAW_DIAGRAM: if (props.InCnlNum > 0) { - alert("Draw diagramm of the input channel " + props.InCnlNum); // TODO: use SCADA API + if (dialogs) { + dialogs.showChart(scada.scheme.viewHub.currentViewID, props.InCnlNum); + } else { + console.warn("Unable to show chart because viewHub.dialogs is undefined"); + } } break; case Actions.SEND_COMMAND: if (props.CtrlCnlNum > 0) { - alert("Send command for the output channel " + props.CtrlCnlNum); // TODO: use SCADA API + if (dialogs) { + dialogs.showCmd(scada.scheme.viewHub.currentViewID, props.CtrlCnlNum); + } else { + console.warn("Unable to show command dialog because viewHub.dialogs is undefined"); + } } break; } diff --git a/ScadaWeb/OpenPlugins/PlgTable/js/api/viewhub.js b/ScadaWeb/OpenPlugins/PlgTable/js/api/viewhub.js index 4f3510b86..c9b8ebaf4 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/js/api/viewhub.js +++ b/ScadaWeb/OpenPlugins/PlgTable/js/api/viewhub.js @@ -3,9 +3,7 @@ * Author : Mikhail Shiryaev * Created : 2016 * Modified : 2016 - */ - -/* + * * Requires: * - jquery * - eventtypes.js @@ -98,7 +96,7 @@ scada.viewHubLocator = { if (wnd.viewHub) { return wnd.viewHub; } - wnd = window.parent; + wnd = wnd == window.top ? null : window.parent; } return null; } diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js b/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js index 1a69af76c..55dc7bc47 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js @@ -4,9 +4,7 @@ * Author : Mikhail Shiryaev * Created : 2016 * Modified : 2016 - */ - -/* + * * Requires: * - jquery * - utils.js diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js b/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js index d9543afd3..e00d34a28 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js @@ -3,9 +3,7 @@ * Author : Mikhail Shiryaev * Created : 2016 * Modified : 2016 - */ - -/* + * * Requires external objects: * - scada.chart * - scada.cmd diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/eventtypes.js b/ScadaWeb/ScadaWebShell5Beta/js/api/eventtypes.js index 63aee2e22..5b5272ec2 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/eventtypes.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/eventtypes.js @@ -4,6 +4,8 @@ * Author : Mikhail Shiryaev * Created : 2016 * Modified : 2016 + * + * No dependencies */ // Rapid SCADA namespace diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/utils.js b/ScadaWeb/ScadaWebShell5Beta/js/api/utils.js index 5a2893531..d23485855 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/utils.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/utils.js @@ -4,6 +4,8 @@ * Author : Mikhail Shiryaev * Created : 2016 * Modified : 2016 + * + * No dependencies */ // Rapid SCADA namespace diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js b/ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js index 4f3510b86..c9b8ebaf4 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js @@ -3,9 +3,7 @@ * Author : Mikhail Shiryaev * Created : 2016 * Modified : 2016 - */ - -/* + * * Requires: * - jquery * - eventtypes.js @@ -98,7 +96,7 @@ scada.viewHubLocator = { if (wnd.viewHub) { return wnd.viewHub; } - wnd = window.parent; + wnd = wnd == window.top ? null : window.parent; } return null; } From d1a781b0c5db85bda34c1eec191bd055f814c3cb Mon Sep 17 00:00:00 2001 From: 2mik Date: Thu, 19 May 2016 15:38:12 +0300 Subject: [PATCH 050/382] ScadaWeb5: select view --- ScadaWeb/ScadaWebCommon5Beta/Shell/MenuItem.cs | 2 +- ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs | 3 ++- ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs | 8 +++++++- ScadaWeb/ScadaWebShell5Beta/View.aspx.cs | 5 +++-- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/MenuItem.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/MenuItem.cs index 5016585dc..26ad13e45 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/MenuItem.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/MenuItem.cs @@ -166,7 +166,7 @@ public static MenuItem FromStandardMenuItem(StandardMenuItems standardMenuItem) /// bool IWebTreeNode.IsSelected(object selObj) { - return selObj == null ? false : string.Equals(Url, selObj.ToString(), StringComparison.OrdinalIgnoreCase); + return selObj != null && string.Equals(Url, selObj.ToString(), StringComparison.OrdinalIgnoreCase); } } } diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs index d612fd45f..c3bdc2de7 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs @@ -152,7 +152,8 @@ protected void InitDataAttrs() /// public bool IsSelected(object selObj) { - return false; + int selViewID = selObj is int ? (int)selObj : 0; + return selViewID > 0 && selViewID == ViewID; } } } diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs index 45c45519e..efca21e5e 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs @@ -45,6 +45,12 @@ public partial class MasterMain : System.Web.UI.MasterPage private UserData userData; // данные пользователя приложения + /// + /// Ид. выбранного представления + /// + public int SelectedViewID = 0; + + /// /// Генерировать HTML-код главного меню /// @@ -65,7 +71,7 @@ protected string GenerateExplorerHtml() FolderImageUrl = ResolveUrl(FolderImageUrl), DocumentImageUrl = ResolveUrl(DocumentImageUrl) }; - return treeViewRenderer.GenerateHtml(userData.UserViews.ViewNodes, null, options); + return treeViewRenderer.GenerateHtml(userData.UserViews.ViewNodes, SelectedViewID, options); } diff --git a/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs b/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs index 172af9fc7..38c029457 100644 --- a/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs +++ b/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs @@ -63,10 +63,9 @@ protected void Page_Load(object sender, EventArgs e) userData = UserData.GetUserData(); userData.CheckLoggedOn(true); - // получение ид. представления для загрузки + // получение ид. и ссылки представления для загрузки int.TryParse(Request.QueryString["viewID"], out initialViewID); - // получение ссылки представления if (initialViewID > 0) initialViewUrl = userData.UserViews.GetViewUrl(initialViewID); else @@ -74,6 +73,8 @@ protected void Page_Load(object sender, EventArgs e) if (string.IsNullOrEmpty(initialViewUrl)) initialViewUrl = ResolveUrl(UrlTemplates.NoView); + + ((MasterMain)Master).SelectedViewID = initialViewID; } } } \ No newline at end of file From 7f10076175e0a69b7b7d0259b173d049d5515ce9 Mon Sep 17 00:00:00 2001 From: 2mik Date: Thu, 19 May 2016 15:42:57 +0300 Subject: [PATCH 051/382] ScadaWeb5: select view node --- ScadaWeb/ScadaWebShell5Beta/js/controls/treeview.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ScadaWeb/ScadaWebShell5Beta/js/controls/treeview.js b/ScadaWeb/ScadaWebShell5Beta/js/controls/treeview.js index c28772d18..a678f6e04 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/controls/treeview.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/controls/treeview.js @@ -60,12 +60,15 @@ scada.treeView = { allNodes .off() .click(function () { - var link = $(this).find("a"); + var node = $(this); + var link = node.find("a"); if (link.length > 0) { + allNodes.removeClass("selected"); + node.addClass("selected"); scada.utils.clickLink(link); } else { - var expander = $(this).find(".expander"); + var expander = node.find(".expander"); if (!expander.hasClass("empty")) { thisTreeView._toggleTreeNode(expander); } From 059bc7e5a7191443feb5026c934c68430e928cca Mon Sep 17 00:00:00 2001 From: 2mik Date: Thu, 19 May 2016 17:59:46 +0300 Subject: [PATCH 052/382] ScadaWeb5: check logged on --- .../OpenPlugins/PlgScheme/js/api/clientapi.js | 12 ++++++++-- ScadaWeb/ScadaWebCommon5Beta/AppData.cs | 8 +++++-- ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs | 22 ++++++++++++++++++ ScadaWeb/ScadaWebShell5Beta/MasterMain.Master | 1 + .../ScadaWebShell5Beta/js/api/clientapi.js | 12 ++++++++-- ScadaWeb/ScadaWebShell5Beta/js/mastermain.js | 23 +++++++++++++++++++ 6 files changed, 72 insertions(+), 6 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgScheme/js/api/clientapi.js b/ScadaWeb/OpenPlugins/PlgScheme/js/api/clientapi.js index 55dc7bc47..22405bac2 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/js/api/clientapi.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/js/api/clientapi.js @@ -56,7 +56,7 @@ scada.clientAPI = { var parsedData = $.parseJSON(data.d); if (parsedData.Success) { scada.utils.logSuccessfulRequest(operation/*, data*/); - callback(true, parsedData.Data ? parsedData.Data : parsedData); + callback(true, parsedData.Data == null ? parsedData : parsedData.Data); } else { scada.utils.logServiceError(operation, parsedData.ErrorMessage); callback(false, errorResult); @@ -73,9 +73,17 @@ scada.clientAPI = { }); }, + // Check that a user is logged on. + // callback is function (success, loggedOn) + checkLoggedOn: function (callback) { + this._request( + "ClientApiSvc.svc/CheckLoggedOn", "", + callback, false); + }, + // Get current value and status of the input channel. // callback is function (success, cnlData) - getCurCnlData: function (cnlNum, cnlNum) { + getCurCnlData: function (cnlNum, callback) { this._request( "ClientApiSvc.svc/GetCurCnlData", "?cnlNum=" + cnlNum, diff --git a/ScadaWeb/ScadaWebCommon5Beta/AppData.cs b/ScadaWeb/ScadaWebCommon5Beta/AppData.cs index e28d471b7..26f2e5c94 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/AppData.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/AppData.cs @@ -415,10 +415,14 @@ public bool CheckUser(string username, string password, bool checkPassword, /// /// Проверить, что пользователь вошёл систему /// - public void CheckLoggedOn() + public bool CheckLoggedOn(bool throwOnFail = true) { - if (!UserMonitor.UserIsLoggedOn(WebOperationContext.Current)) + if (UserMonitor.UserIsLoggedOn(WebOperationContext.Current)) + return true; + else if (throwOnFail) throw new ScadaException(WebPhrases.NotLoggedOn); + else + return false; } /// diff --git a/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs b/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs index 90ff84c75..8f755faea 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs @@ -161,6 +161,28 @@ private string GetErrorDtoJs(Exception ex) } + /// + /// Проверить, что пользователь вошёл систему + /// + /// Возвращает bool, упакованный в DataTransferObject, в формате в JSON + [OperationContract] + [WebGet] + public string CheckLoggedOn() + { + try + { + bool loggedOn = AppData.CheckLoggedOn(false); + return JsSerializer.Serialize(new DataTransferObject(loggedOn)); + } + catch (Exception ex) + { + AppData.Log.WriteException(ex, Localization.UseRussian ? + "Ошибка при проверке того, что пользователь вошел в систему" : + "Error checking that a user is logged on"); + return GetErrorDtoJs(ex); + } + } + /// /// Получить текущие данные входного канала /// diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master index c481cfa0d..decf8f4c7 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master @@ -18,6 +18,7 @@ + diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js b/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js index 55dc7bc47..22405bac2 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js @@ -56,7 +56,7 @@ scada.clientAPI = { var parsedData = $.parseJSON(data.d); if (parsedData.Success) { scada.utils.logSuccessfulRequest(operation/*, data*/); - callback(true, parsedData.Data ? parsedData.Data : parsedData); + callback(true, parsedData.Data == null ? parsedData : parsedData.Data); } else { scada.utils.logServiceError(operation, parsedData.ErrorMessage); callback(false, errorResult); @@ -73,9 +73,17 @@ scada.clientAPI = { }); }, + // Check that a user is logged on. + // callback is function (success, loggedOn) + checkLoggedOn: function (callback) { + this._request( + "ClientApiSvc.svc/CheckLoggedOn", "", + callback, false); + }, + // Get current value and status of the input channel. // callback is function (success, cnlData) - getCurCnlData: function (cnlNum, cnlNum) { + getCurCnlData: function (cnlNum, callback) { this._request( "ClientApiSvc.svc/GetCurCnlData", "?cnlNum=" + cnlNum, diff --git a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js index e178ac1e5..3e02dc9e5 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js @@ -2,6 +2,9 @@ scada.view = scada.view || {}; // defined if the current page is View.aspx scada.masterMain = { + // Check user logged on rate, ms + _checkLoggedOnRate: 10000, + // The left pane, which displays tool windows, is expanded leftPaneExpanded: true, @@ -38,6 +41,20 @@ scada.masterMain = { scada.utils.setCookie("LeftPaneVisible", this.leftPaneExpanded); }, + // Check that a user is logged on + _checkLoggedOn: function () { + var thisObj = scada.masterMain; + scada.clientAPI.checkLoggedOn(function (success, loggedOn) { + if (loggedOn) { + // enqueue the next check + setTimeout(thisObj._checkLoggedOn, thisObj._checkLoggedOnRate); + } else { + // redirect to login page + location.href = scada.env.rootPath + "Login.aspx"; + } + }); + }, + // Update layout of the master page updateLayout: function () { var divMainHeader = $("#divMainHeader"); @@ -156,6 +173,11 @@ scada.masterMain = { } else { location = scada.env.rootPath + "View.aspx?viewID=" + viewID; } + }, + + // Start cyclic checking user logged on + startCheckingLoggedOn: function () { + setTimeout(this._checkLoggedOn, this._checkLoggedOnRate); } }; @@ -169,6 +191,7 @@ $(document).ready(function () { scada.masterMain.updateLayout(); scada.masterMain.chooseToolWindow(); scada.masterMain.loadVisualState(); + scada.masterMain.startCheckingLoggedOn(); scada.treeView.prepare(); // update layout on window resize From d26038b19046d2962a5db493b75436c030baa398 Mon Sep 17 00:00:00 2001 From: 2mik Date: Fri, 20 May 2016 16:32:51 +0300 Subject: [PATCH 053/382] ScadaWeb5: improve tree view --- .../ScadaWebCommon5Beta/Shell/IWebTreeNode.cs | 9 +++++++- .../ScadaWebCommon5Beta/Shell/MenuItem.cs | 11 ++++++++++ .../Shell/TreeViewRenderer.cs | 21 ++++++++----------- .../ScadaWebCommon5Beta/Shell/UrlTemplates.cs | 5 +++++ .../ScadaWebCommon5Beta/Shell/UserViews.cs | 2 +- .../ScadaWebCommon5Beta/Shell/ViewNode.cs | 17 ++++++++++----- .../css/controls/treeview.css | 3 +++ .../css/controls/treeview.less | 4 ++++ .../css/controls/treeview.min.css | 2 +- .../ScadaWebShell5Beta/css/mastermain.css | 8 ++++--- .../ScadaWebShell5Beta/css/mastermain.less | 9 +++++--- .../ScadaWebShell5Beta/css/mastermain.min.css | 2 +- .../js/controls/treeview.js | 18 +++++++++++++--- ScadaWeb/ScadaWebShell5Beta/js/mastermain.js | 11 +++++----- 14 files changed, 87 insertions(+), 35 deletions(-) diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/IWebTreeNode.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/IWebTreeNode.cs index 8e67393bc..bbdcf25ff 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/IWebTreeNode.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/IWebTreeNode.cs @@ -43,7 +43,14 @@ public interface IWebTreeNode /// Получить ссылку /// string Url { get; } - + + /// + /// Получить скрипт + /// + /// Скрипт имеет приоритет перед ссылкой, но наличие ссылки позволяет + /// открывать страницу в новом окне из стандартного контекстного меню + string Script { get; } + /// /// Получить ссылку на иконку /// diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/MenuItem.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/MenuItem.cs index 26ad13e45..d24e70554 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/MenuItem.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/MenuItem.cs @@ -67,6 +67,17 @@ public MenuItem(string text, string url, int sortOrder = SortOrders.First) /// public string Url { get; protected set; } + /// + /// Получить скрипт + /// + public string Script + { + get + { + return ""; + } + } + /// /// Получить порядок сортировки /// diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/TreeViewRenderer.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/TreeViewRenderer.cs index e134638c1..0617bd79e 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/TreeViewRenderer.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/TreeViewRenderer.cs @@ -64,6 +64,7 @@ protected string GenDataAttrsHtml(IWebTreeNode webTreeNode) const string DataAttrTemplate = " data-{0}='{1}'"; StringBuilder sbHtml = new StringBuilder(); + sbHtml.Append(string.Format(DataAttrTemplate, "script", webTreeNode.Script)); sbHtml.Append(string.Format(DataAttrTemplate, "level", webTreeNode.Level)); if (webTreeNode.DataAttrs != null) @@ -83,18 +84,17 @@ protected string GenDataAttrsHtml(IWebTreeNode webTreeNode) /// protected string GenTreeViewHtml(IList treeNodes, object selObj, Options options, bool topLevel) { - const string NodeTemplate = - ""; + "
{4}
" + + "
{5}
" + + "
" + + "
"; const string IconTemplate = ""; - const string LinkTemplate = "{1}"; StringBuilder sbHtml = new StringBuilder(); sbHtml.AppendLine(topLevel ? @@ -128,11 +128,8 @@ protected string GenTreeViewHtml(IList treeNodes, object selObj, Options options icon = ""; } - string text = HttpUtility.HtmlEncode(webTreeNode.Text); - string textOrLink = urlIsEmpty ? text : string.Format(LinkTemplate, webTreeNode.Url, text); - sbHtml.AppendLine(string.Format(NodeTemplate, - nodeCssClass, dataAttrs, expanderCssClass, icon, textOrLink)); + nodeCssClass, webTreeNode.Url, dataAttrs, expanderCssClass, icon, HttpUtility.HtmlEncode(webTreeNode.Text))); if (containsSubitems) sbHtml.Append(GenTreeViewHtml(webTreeNode.Children, selObj, options, false)); diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/UrlTemplates.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/UrlTemplates.cs index 4421e0a1f..ac4a2ade8 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/UrlTemplates.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/UrlTemplates.cs @@ -46,6 +46,11 @@ public static class UrlTemplates /// public const string User = "~/User.aspx?userID={0}"; + /// + /// Представление + /// + public const string View = "~/View.aspx?viewID={0}"; + /// /// Отсутствующее представление /// diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/UserViews.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/UserViews.cs index cb5f0bdff..739067e67 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/UserViews.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/UserViews.cs @@ -43,7 +43,7 @@ public class UserViews /// protected readonly Log log; /// - /// Ссылки представлений, ключ - ид. представления + /// Ссылки на представления, ключ - ид. представления /// protected readonly Dictionary viewUrls; diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs index c3bdc2de7..2ae749937 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/ViewNode.cs @@ -38,9 +38,9 @@ namespace Scada.Web.Shell public class ViewNode : IWebTreeNode { /// - /// Шаблон ссылки узла для загрузки представления + /// Шаблон скрипта, открывающего представление /// - protected const string ViewNodeUrlTemplate = "javascript:scada.masterMain.loadView({0}, \"{1}\");"; + protected const string ScriptTemplate = "scada.masterMain.loadView({0}, \"{1}\");"; /// @@ -66,12 +66,14 @@ public ViewNode(ViewSettings.ViewItem viewItem, ViewSpec viewSpec) { Url = ""; ViewUrl = ""; + Script = ""; IconUrl = ""; } else { + Url = VirtualPathUtility.ToAbsolute(string.Format(UrlTemplates.View, ViewID)); ViewUrl = VirtualPathUtility.ToAbsolute(viewSpec.GetViewUrl(ViewID)); - Url = string.Format(ViewNodeUrlTemplate, ViewID, ViewUrl); + Script = string.Format(ScriptTemplate, ViewID, ViewUrl); IconUrl = VirtualPathUtility.ToAbsolute(viewSpec.IconUrl); } @@ -97,15 +99,20 @@ public ViewNode(ViewSettings.ViewItem viewItem, ViewSpec viewSpec) public int AlarmCnlNum { get; protected set; } /// - /// Получить ссылку узла + /// Получить ссылку на страницу оболочки, содержащую представление /// public string Url { get; protected set; } /// - /// Получить ссылку представления + /// Получить ссылку на представление /// public string ViewUrl { get; protected set; } + /// + /// Получить скрипт, открывающий представление + /// + public string Script { get; protected set; } + /// /// Получить ссылку на иконку /// diff --git a/ScadaWeb/ScadaWebShell5Beta/css/controls/treeview.css b/ScadaWeb/ScadaWebShell5Beta/css/controls/treeview.css index 1faa6a0de..80969fd30 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/controls/treeview.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/controls/treeview.css @@ -3,6 +3,9 @@ display: table; width: 100%; } +.tree-view .node.disabled { + cursor: default; +} .tree-view .node-items { display: table-row; width: 200px; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/controls/treeview.less b/ScadaWeb/ScadaWebShell5Beta/css/controls/treeview.less index 7c80d49b0..e2cd9daf0 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/controls/treeview.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/controls/treeview.less @@ -4,6 +4,10 @@ width: 100%; } +.tree-view .node.disabled { + cursor: default; +} + .tree-view .node-items { display: table-row; width: 200px; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/controls/treeview.min.css b/ScadaWeb/ScadaWebShell5Beta/css/controls/treeview.min.css index b9cdee224..7b6cc41f1 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/controls/treeview.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/controls/treeview.min.css @@ -1 +1 @@ -.tree-view .node{cursor:pointer;display:table;width:100%;}.tree-view .node-items{display:table-row;width:200px;}.tree-view .node-items>div{display:table-cell;}.tree-view .child-nodes{display:none;}.tree-view .indent{width:20px;}.tree-view .expander{width:20px;}.tree-view .expander.right{display:none;text-align:right;width:0;}.tree-view .expander::before{font-family:'Glyphicons Halflings';}.tree-view .expander.left::before{content:"";}.tree-view .expander.right::before{content:"";font-family:'Glyphicons Halflings';}.tree-view .expander.expanded::before{content:"";}.tree-view .expander.empty{visibility:hidden;}.tree-view .stateIcon,.tree-view .icon{width:0;}.tree-view .icon img{width:16px;height:16px;border:none;} \ No newline at end of file +.tree-view .node{cursor:pointer;display:table;width:100%;}.tree-view .node.disabled{cursor:default;}.tree-view .node-items{display:table-row;width:200px;}.tree-view .node-items>div{display:table-cell;}.tree-view .child-nodes{display:none;}.tree-view .indent{width:20px;}.tree-view .expander{width:20px;}.tree-view .expander.right{display:none;text-align:right;width:0;}.tree-view .expander::before{font-family:'Glyphicons Halflings';}.tree-view .expander.left::before{content:"";}.tree-view .expander.right::before{content:"";font-family:'Glyphicons Halflings';}.tree-view .expander.expanded::before{content:"";}.tree-view .expander.empty{visibility:hidden;}.tree-view .stateIcon,.tree-view .icon{width:0;}.tree-view .icon img{width:16px;height:16px;border:none;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css index 0c6128578..51e72111f 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css @@ -146,7 +146,6 @@ a:active { } #divMainLeftPane .tree-view .node.disabled { color: #9ca1a6; - cursor: default; } #divMainLeftPane .tree-view .expander::before { font-size: 10px; @@ -161,7 +160,7 @@ a:active { font-size: 13px; } #divMainMenu .tree-view .node { - padding: 5px 10px 5px 10px; + padding: 5px 10px; } #divMainMenu .tree-view .expander.left { display: none; @@ -177,7 +176,10 @@ a:active { line-height: 13px; } #divMainExplorer .tree-view .node { - padding: 7px 5px 3px 10px; + padding: 3px 5px 3px 10px; +} +#divMainExplorer .tree-view .expander { + padding: 4px 0 0 0; } #divMainExplorer .tree-view .icon { width: 21px; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less index 386d8ed6d..1f4665b25 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less @@ -174,7 +174,6 @@ a:active { #divMainLeftPane .tree-view .node.disabled { color: @menu-fore-color-dark; - cursor: default; } #divMainLeftPane .tree-view .expander::before { @@ -193,7 +192,7 @@ a:active { } #divMainMenu .tree-view .node { - padding: 5px 10px 5px 10px; + padding: 5px 10px; } #divMainMenu .tree-view .expander.left { @@ -214,7 +213,11 @@ a:active { } #divMainExplorer .tree-view .node { - padding: 7px 5px 3px 10px; + padding: 3px 5px 3px 10px; +} + +#divMainExplorer .tree-view .expander { + padding: 4px 0 0 0; } #divMainExplorer .tree-view .icon { diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css index 18b07306e..48075fb2e 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 250px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;white-space:nowrap;}#divMainHeader .hdr-btn{height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;display:inline-block;font-size:13px;text-decoration:none;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenuBtn{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;}#divMainUserMenu{float:right;height:30px;}#lblMainNormalViewBtn{position:fixed;top:0;right:0;height:30px;padding:0 10px;background-color:#fff;display:none;line-height:30px;opacity:.5;cursor:pointer;color:#9ca1a6;font-size:13px;}#lblMainNormalViewBtn:hover{color:#00b9eb;opacity:1;}#divMainLeftPane{position:fixed;left:0;top:30px;width:250px;min-height:250px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 30px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainLeftPane .tree-view .node:hover{background-color:#191e23;color:#00b9eb;}#divMainLeftPane .tree-view .node.selected{background-color:#0073aa;color:#fff;}#divMainLeftPane .tree-view .node.disabled{color:#9ca1a6;cursor:default;}#divMainLeftPane .tree-view .expander::before{font-size:10px;}#divMainMenu .node[data-level="1"],#divMainMenu .node[data-level="2"],#divMainMenu .node[data-level="3"],#divMainMenu .node[data-level="4"],#divMainMenu .node[data-level="5"]{color:#9ca1a6;font-size:13px;}#divMainMenu .tree-view .node{padding:5px 10px 5px 10px;}#divMainMenu .tree-view .expander.left{display:none;width:0;}#divMainMenu .tree-view .expander.right{display:table-cell;width:20px;}#divMainExplorer{font-size:13px;line-height:13px;}#divMainExplorer .tree-view .node{padding:7px 5px 3px 10px;}#divMainExplorer .tree-view .icon{width:21px;}#divMainExplorer .tree-view img{position:relative;top:-2px;}#divMainCollapseLeftPaneBtn{position:absolute;bottom:0;width:220px;height:30px;margin:0 0 0 30px;padding:0 10px;color:#9ca1a6;cursor:pointer;font-size:13px;line-height:30px;}#divMainCollapseLeftPaneBtn i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapseLeftPaneBtn:hover{color:#00b9eb;} \ No newline at end of file +body{margin:0;padding:30px 0 0 250px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;white-space:nowrap;}#divMainHeader .hdr-btn{height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;display:inline-block;font-size:13px;text-decoration:none;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenuBtn{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;}#divMainUserMenu{float:right;height:30px;}#lblMainNormalViewBtn{position:fixed;top:0;right:0;height:30px;padding:0 10px;background-color:#fff;display:none;line-height:30px;opacity:.5;cursor:pointer;color:#9ca1a6;font-size:13px;}#lblMainNormalViewBtn:hover{color:#00b9eb;opacity:1;}#divMainLeftPane{position:fixed;left:0;top:30px;width:250px;min-height:250px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 30px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainLeftPane .tree-view .node:hover{background-color:#191e23;color:#00b9eb;}#divMainLeftPane .tree-view .node.selected{background-color:#0073aa;color:#fff;}#divMainLeftPane .tree-view .node.disabled{color:#9ca1a6;}#divMainLeftPane .tree-view .expander::before{font-size:10px;}#divMainMenu .node[data-level="1"],#divMainMenu .node[data-level="2"],#divMainMenu .node[data-level="3"],#divMainMenu .node[data-level="4"],#divMainMenu .node[data-level="5"]{color:#9ca1a6;font-size:13px;}#divMainMenu .tree-view .node{padding:5px 10px;}#divMainMenu .tree-view .expander.left{display:none;width:0;}#divMainMenu .tree-view .expander.right{display:table-cell;width:20px;}#divMainExplorer{font-size:13px;line-height:13px;}#divMainExplorer .tree-view .node{padding:3px 5px 3px 10px;}#divMainExplorer .tree-view .expander{padding:4px 0 0 0;}#divMainExplorer .tree-view .icon{width:21px;}#divMainExplorer .tree-view img{position:relative;top:-2px;}#divMainCollapseLeftPaneBtn{position:absolute;bottom:0;width:220px;height:30px;margin:0 0 0 30px;padding:0 10px;color:#9ca1a6;cursor:pointer;font-size:13px;line-height:30px;}#divMainCollapseLeftPaneBtn i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapseLeftPaneBtn:hover{color:#00b9eb;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/js/controls/treeview.js b/ScadaWeb/ScadaWebShell5Beta/js/controls/treeview.js index a678f6e04..93812cbcf 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/controls/treeview.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/controls/treeview.js @@ -59,16 +59,28 @@ scada.treeView = { var thisTreeView = this; allNodes .off() - .click(function () { + .click(function (event) { + if (event.ctrlKey || event.button == 1 /*middle*/) { + return; // allow the default link behavior + } + + event.preventDefault(); var node = $(this); + var script = node.attr("data-script"); + var expander = node.find(".expander"); var link = node.find("a"); - if (link.length > 0) { + if ($(event.target).is(".expander")) { + thisTreeView._toggleTreeNode(expander); + } else if (script) { + allNodes.removeClass("selected"); + node.addClass("selected"); + eval(script); + } else if (link.length > 0) { allNodes.removeClass("selected"); node.addClass("selected"); scada.utils.clickLink(link); } else { - var expander = node.find(".expander"); if (!expander.hasClass("empty")) { thisTreeView._toggleTreeNode(expander); } diff --git a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js index 3e02dc9e5..035af25d0 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js @@ -3,11 +3,12 @@ scada.view = scada.view || {}; // defined if the current page is View.aspx scada.masterMain = { // Check user logged on rate, ms - _checkLoggedOnRate: 10000, + CHECK_LOGGEDON_RATE: 10000, + // Delay before redirecting to login page + LOGIN_DELAY: 3000, // The left pane, which displays tool windows, is expanded leftPaneExpanded: true, - // Fullsreen mode is active isFullscreen: false, @@ -47,10 +48,10 @@ scada.masterMain = { scada.clientAPI.checkLoggedOn(function (success, loggedOn) { if (loggedOn) { // enqueue the next check - setTimeout(thisObj._checkLoggedOn, thisObj._checkLoggedOnRate); + setTimeout(thisObj._checkLoggedOn, thisObj.CHECK_LOGGEDON_RATE); } else { // redirect to login page - location.href = scada.env.rootPath + "Login.aspx"; + setTimeout(function() { location.href = scada.env.rootPath + "Login.aspx" }, thisObj.LOGIN_DELAY); } }); }, @@ -177,7 +178,7 @@ scada.masterMain = { // Start cyclic checking user logged on startCheckingLoggedOn: function () { - setTimeout(this._checkLoggedOn, this._checkLoggedOnRate); + setTimeout(this._checkLoggedOn, this.CHECK_LOGGEDON_RATE); } }; From 91aca0ea51089dce3fbf0e99bea5cc1e26f00ac3 Mon Sep 17 00:00:00 2001 From: 2mik Date: Fri, 20 May 2016 16:38:18 +0300 Subject: [PATCH 054/382] ScadaWeb5: minor update --- ScadaWeb/ScadaWebShell5Beta/View.aspx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScadaWeb/ScadaWebShell5Beta/View.aspx b/ScadaWeb/ScadaWebShell5Beta/View.aspx index 4ebfdf327..70383baf3 100644 --- a/ScadaWeb/ScadaWebShell5Beta/View.aspx +++ b/ScadaWeb/ScadaWebShell5Beta/View.aspx @@ -1,4 +1,4 @@ -<%@ Page Title="Views - Rapid SCADA" Language="C#" MasterPageFile="~/MasterMain.Master" AutoEventWireup="true" CodeBehind="View.aspx.cs" Inherits="Scada.Web.WFrmView" %> +<%@ Page Title="View - Rapid SCADA" Language="C#" MasterPageFile="~/MasterMain.Master" AutoEventWireup="true" CodeBehind="View.aspx.cs" Inherits="Scada.Web.WFrmView" %> From b9b4312d8b3bb3271b0d00594d0988ae104a4e93 Mon Sep 17 00:00:00 2001 From: 2mik Date: Fri, 20 May 2016 20:06:35 +0300 Subject: [PATCH 055/382] ScadaWeb5: tree view fix --- ScadaWeb/ScadaWebShell5Beta/js/controls/treeview.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ScadaWeb/ScadaWebShell5Beta/js/controls/treeview.js b/ScadaWeb/ScadaWebShell5Beta/js/controls/treeview.js index 93812cbcf..ac3d7ab42 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/controls/treeview.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/controls/treeview.js @@ -68,7 +68,7 @@ scada.treeView = { var node = $(this); var script = node.attr("data-script"); var expander = node.find(".expander"); - var link = node.find("a"); + var href = node.attr("href"); if ($(event.target).is(".expander")) { thisTreeView._toggleTreeNode(expander); @@ -76,10 +76,10 @@ scada.treeView = { allNodes.removeClass("selected"); node.addClass("selected"); eval(script); - } else if (link.length > 0) { + } else if (href.length > 0) { allNodes.removeClass("selected"); node.addClass("selected"); - scada.utils.clickLink(link); + scada.utils.clickLink(node); } else { if (!expander.hasClass("empty")) { thisTreeView._toggleTreeNode(expander); From 73574817527b3b7b84e0ec78458cd9ea155de3e2 Mon Sep 17 00:00:00 2001 From: 2mik Date: Fri, 20 May 2016 21:01:11 +0300 Subject: [PATCH 056/382] ScadaWeb5: js fix --- ScadaWeb/ScadaWebShell5Beta/js/mastermain.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js index 035af25d0..bb0d1bfd9 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js @@ -115,7 +115,7 @@ scada.masterMain = { this._hideLeftPane(); this.leftPaneExpanded = false; this._saveLeftPaneVisible(); - $(window).trigger(scada.events.updateLayout); + $(window).trigger(scada.eventTypes.updateLayout); }, // Expand the left pane and hide the menu button @@ -124,7 +124,7 @@ scada.masterMain = { $("#spanMainShowMenuBtn").css("display", "none"); this.leftPaneExpanded = true; this._saveLeftPaneVisible(); - $(window).trigger(scada.events.updateLayout); + $(window).trigger(scada.eventTypes.updateLayout); }, // Hide all the menus and switch browser to fullscreen mode From df2d51f81daac6926cce899979093c035ab031ed Mon Sep 17 00:00:00 2001 From: 2mik Date: Sat, 21 May 2016 16:47:52 +0300 Subject: [PATCH 057/382] ScadaWeb5: images --- ScadaWeb/Res/newWebApp/document.png | Bin 0 -> 2860 bytes ScadaWeb/Res/newWebApp/favicon.ico | Bin 0 -> 314279 bytes ScadaWeb/Res/newWebApp/favicon.png | Bin 0 -> 82426 bytes ScadaWeb/Res/newWebApp/folder.png | Bin 0 -> 168 bytes ScadaWeb/Res/newWebApp/gear.png | Bin 0 -> 8093 bytes 5 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 ScadaWeb/Res/newWebApp/document.png create mode 100644 ScadaWeb/Res/newWebApp/favicon.ico create mode 100644 ScadaWeb/Res/newWebApp/favicon.png create mode 100644 ScadaWeb/Res/newWebApp/folder.png create mode 100644 ScadaWeb/Res/newWebApp/gear.png diff --git a/ScadaWeb/Res/newWebApp/document.png b/ScadaWeb/Res/newWebApp/document.png new file mode 100644 index 0000000000000000000000000000000000000000..fde4aeae28ce573cb2d61dbf7d983b6b28f4d14a GIT binary patch literal 2860 zcmV+{3)A$8P)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z00012Nkl;3Nk{~FbE0?;&lNd77RBWSu@FA zz~(}d!vNh779#aABC-EdH5NtcffNa00030{{sM18GaIB;N5oLdyddh0Fze9}rq(aCosM=t*SbFMpa)uMRQo2s8j6~#6q zielGY7k}RD9YyibJBwo9eXGBp!~G`~#n`dc--k36m#^Pcw6<11?=_&f=XC=LH~YWp zqeU_I@PWlvtZx&vdb>J>({^j^|XsFx@gafFTS{V>ZzxS zXP}Lah^PAtSdCDoL z9QWPte)p6sue@?S+AS6?Tv&`5Gp0E1xZ~D2{`lj+bn2<6zLRTKy8QCXKmUh6{Gs#e ztFNv=$MY9nc;N{ropjPQU-`;cIu1Vg;K~nw_`@$BbkIS?{Q2{X9d_8kHvGNvqaXcf z<`q|5(F)y9uva**(2v(wXuopcfd^I&J@nA&w7Ki~=bvB9n>Vl6XPVPyFp~fBV%NZ@h7jzy0lRqwc-;-eSWIH!R@y zsGDxOsq&Mb{G{@`-~F!rU;p~oNnEqub=O_DE^VFw4~wMd&wu`N<)=UWY3DC~@r#9E zdOyc^{_>ZUensz`jEJIKlD#VuIJJ3 zJ2}4c3rkp3#@j=%kp&ufXj+ar%WqHeAX9XE;}{U+Iog|a*S z^wX0ZZCm+x4d0&tU-DK!cL9w%)9+NT9Xobx<=fx>cG^!k;e^V`C!bt_hR135Pwdk; z_q*?oA3r|bPrJqMfB*Z>LvIKA(Q)F5Ctl0_$A0Z=Uz>3B(MMO5J@*`R_St6-rr)=t zs|V1oI-l;xRtj)g6S|H+=bUrKg6&$;1s&(0n-$uf&%N(C>#Va9zqS56`sky@+T-D0nGN>t}Fv1@0{Q9}WfXuvZ&&O6xkv^a`+?e#s@5OyGPEcrIqnoY||kYF!NP z`n28A`?=~UI;npA6kQ*PJ{9=1BnNC{BYf7#&d$yO6DCZk)l|*zz_)>((f1dS??Cl7-)!gHbI%=!KYT0KZj0Wupl1cw7x{Boilzpf9v1bhbNaR&J*!}Y-B4s^P5 z!37tj_VAVIXq(dPZojsT{#3jc9+Ln5>Q}#7IqImR=3{FgfTkq#U_f7IEaIL$pna0< z;oFkG(WimYQtg9ZqTN2gG4X=!&j*(eplbzs6wRk2ezkbwi6@$9>nr+iu1o$%UTk0g z22NA??Np%n@4U03 zKiA*sHy?iZ;d}1C|Nh-?zWL@=@V&#((_PTZe;C`)yX1FoyY04gy*w)~a9V_3{1!i5 z5c3Ao?6TI4uH}b8&g@zf(?A^w6eb=N(lM4KKK}_2fx?fPvVoJXi^8$ECx}d!1 z=LO_An(KbV`O{#~arfPKSB$5&FE$T8_~6~NySgzuw&$OJ-oA$2ci(+COM`L4_yi{D zTwiaWeBZ|L5wDRabsT!rQMzm&Ebk#st^r2TeB#6Q@vApWukG2sJLetH+eUjogx*~^ ze>!wVJMtQj>Y@4wjx*tVPyLO)TAW^Z;RXHMJD_)}IzSugK72?gx^N!XzYZLlkyn8{ zF94%?w3&Pixj~BA%GUPf9X=l5d{5-R8n_RKhIet@UGnX9#(i@PXqW-rJK#gQczxi3 z2i8Vk-ogG^u=xfw-Kw1A+4hx_{K3x*WO5hu-buUDwJmq`MSD?ZT-yO&W9g?Ep1S(* zM4N!8g8XJDuALe(@@OC8tju%UQuqB{`jwy8(f@Sxc5nP%A*QjZd+=Sr!|q@{OZ;t5 z`ozeiGB+k0>+vaxe({lps$WfV&NQA?8=0qr!EmJ?#~L zSkVvS6Vo-=;S{c)A%@bRz46<___K|;KbkMdgQR^kczRkoiCy~h$~V68jfL#f@L`L- z{N*oK4nO?xl&45P_}mPi+i~0-OfH0`XSr_@ZSTAJ=9?E_*VR5bXMyd$5k8+0W3G|@ zspJ^PfbSdO;S}@iBab{X@gtqIc>=T+)i^qkYqr2wZp*!%ceU+_c`^7jVPhYM{(pk= zgXra6&pr2CvveT0O^!YG*vGg($yooyH3!+YvTJ0k%Ag5bSRWnQ(EH4Ju#wa^{CrHSW#qHy3TYR*D&|wA)rmdDuv<+?CyN zVA>o2_Hje^3-)1PcMNiL95WLgdlX%GhZwZCxA(E-#%@Em3T$XsbVk3Kd>whR^1KAy z9fU7ztf4+$W&gEk%@MGtZ<2>Ou1hva9`Sl)^YdelIc6b#p%WS1ivQk@V;UpZf?&#& zDTQNb^mujjc|-hCGi{_x8ABB0k=p8c=CZ-NwCI27|6yeDHhg&tG5#IUdAI)Ji(mX= zR}RO$x4_3b#=7!mErEPb+PA_NTt_}R4*NV0yx#$?Ly0Q|IIadwuzg+1dF9<4QXj3a zhSmujZ^ZviuLZD0A?cf@J=jsE>k8j*X7*J988X_p3LDt@W*^{; zD)aAWG0=YDX9MtSwO#E>S5%kk! zU+(3NdWH5?U{BlAcAI?TLlV}u75&>LGn7PLlA2V;SPyl)WqUCnhBYEcn06V}=~dG{P*>#5wo3I1j< zHa&=IH-e_)!EL7U6MtzIqkHbTC*dGX;wt_6Q0SRYj@^|kIIagL>n@w}+au7HYD4B5 zV(h(+U#Mf90e&s{Sc>lpYmOr++Mp-f7D%VNEw3h!`9o#=bIV3 zY>vFY4j*^m%Q~NY^2wwF=8E?3eaPr3`rfpb{J_PWVcR>f6s@ zztRF0)9`0Iplj#z+ju^As`L6duubo_lPfy2h#QGvEvnKM!wIWcUw9(FK^Xx=EnG#N99F( zk!Ht5>XPU5c@OgaD*U~HIM7U7E|B$Y$|86-rb{!wCEK@u+ftXLAKg3$8dgGIx=7G= z>30yvllZ<6L zcinX!G~gEwRv+z4S$e;H+pgcK-!U$=Ut^DSNRzZluljG_L9@7U&2R7zt8s5vpZSTl zr5z!+OT5o}UAzaqT-!k|yr2D>SLT~FqBk)S z2JhdNZuMA9jpO8ImBS7@Eag(9H+i z6MVEn!@AI#cb(4FOZ?ti*y%m;F7EPfPRLkWI-Wz$t>jnZzVxLpby%B}7W89W|JZUtjdK|NUnySlF6Q=+p5KFS-kmYT z5bSakeEiKE*IZZG$eTQ(50_!T1Mn-UeQP1uaue9B4OXi{i#6q?`t}Rs3O=O3myE(@ z|3F)dxPB4%{n9!gd?w#@zySxCFCRqToz!;|M(Tq!qhF^%SDI(4*+~yyE?BT2)o-x> zHL?9qL&tvT!*G09iZR}f?TPK{z~!;v-M-zC?N_8zJMOOyENs6CJNlmaoxE#b9Ov-) zf6$Nq>bk34$Ajp^TcF_%WIGAmA4FG7>#&rc!Ae&A#*KO4|-2X0#Q< z+jcF6w-#w1!O?wm8=t2a1$77Z=GJ0tOBmym-F##WZ4n|`~O8l_{*|HF1uo?~ti zxsEv(K4ucXZC5WY_8VahG^{V#bI(1k!5nA&&1v^s`m6EkyEa#jYte;c1Y*p}zw`Suh8?0&8Kqu%{k4v&2{jf4-#Xnp$5t6=vWJ1W1W6O?*Ab%a1Q!7 zi~Qy~a^c-+dw_E<{k1Wsay`Tlbo}}lpQtac>ssKIB z_Ujn0uBQyhMVt>BOisKnIpb62K-T)LEfIrLZO0shSoj=c&pnL!uFq&>bKEP<_`x?5 zuTH@y9s+%%$cqPp>k>_2yt2^fS=E&qtuA9N#{`tdcMD`(ack-k6k<&b8{zc3)uY3|4J&zo0H!vSc zu9fl-`NVGDgMLg4pA*Y*)&Cl@z-=&?HG=u#b`rjMf-VD0FiHx6)@s#%JIk{aLSBt%4 zjTmQGe=v76H&SP)?;S=@5MOwpk2DnJso$cJIc}al!JVjmn#Q-PENa@ z;dL_Ou}O=~hqF59J{(9;I6zYSAy<_}Q*8rRT~) zT-2wqTlvr~@HsyR`xgCdBb&7wYD5F^(KL5LyLum3m9duxF;^eNIOs3qZ|)uV+m^aW z`^lzkUt3VN-mkpn!5v*#gpd7%@zMHGqZ|5LfbE*JdzJkt_t1N>*QbcP=hE$O@@D^m zec&%|(yy+dm#KDIrr$o)DX;N<<)H5HT|4xD3g44_d871eE9hhaA6tUg1L~)AXqW1} z@l<=YFZ--Vzq(Y9esPao{H0(1#K2l0F(>gsPH{2x3~t1BbN;LqfPwY$|3UVXq(!{N zog6sjWXild`sL3(Y(rUzg*qR!hyGYsk$!DKyE5iE_C=1f7`K0eI{22rBD%ym42X!AgMP!2i$(x}eydxh)Lxv~)_G4eNkp1bGr zf=p^I^mL5_HJick@O|~sxfb&9gpvK`^xGcwY1FN95&O#QOVJ)*7>nR@5P9eK;UQrk zc!-4sTeVz=|uGxc7dAin5yvNh=s-y{9lQw2L}W&UbdBkKUvEv_BJyuz>b31SjD zqMw$2^twX5IO$C-?87%h=M&V^HpM@j17AU0jJA9w~B|bIL8NMRuSLV`>f4dy) z2S8WAT=(dh(Rw1+o-D@U%cM!2l73`V!T!5)kuvu7D|XQTYjo%$^&#wB+u-{y?9J!# z!D+rvUlILzzcLg<=Hae@{^mwvy*gIiPuHjEd-Mh3rcLTwf_`z#bw}EehvT3p(T(rn z^ET)BkDy5ma_q6+G?rx@Q<`^DS99Mf{l;YS+*`;MSIyVK))PRL_CXwftxl?s;X9>Y zoi)xlHzzHDJ?C?XL$}i3q2zXNz|S?qzw>1Gs1M>PuQ~SCKOBpThd#ON`=#GLu;FR& zu#4l=o;K^i$Z2bi<843a54o$e>J@WWi5}Wth!43OAN^jwTSNZyu^6%KkJ6FrpO^;r z(vNK?9>gi+tqyvR_9brU;T5cDTg5r&dfT*V(~9ZSr?*f~I1gW%?AG?gPWts3v{S)9 zrFiIfV15;O+-lfq&1UKEP_9`N{D;p`@1F-|ZHBS&{<^&JQL3(rgm7Dats|Wg6 z_7}kQCiL=RzP}J$|AK5Li@A75-mLEzkFX;#mws(PyH!8o;Vfh}#I>&7ZSqmwo3O1R z=<1r}T^k|yeevQOEF#Z*6~-qgZB z&zrs0c5FLn_h`H7fAr^gfq90D(XXNCKo^bOo#?OOp_w>&zIr3J+M7Bp4UPxZ0Wq?_ zz(1!yubHSnF>XfhEet;JeT zb`HYYvg`M~M*id%{F1DyKJ^LOJRIM=5$*4WU%G&O0>5?2o4fiYUeNv&@_UpAHC z|8BC$Cf1F8F4DEpgP?gEbaM>X-b0%U&37x_$gq^IA!j<{Wr8y%)!u7|yK zYV2_)*Pe`=cBSoi<1@~Jw`tbVtbzNyS=6(&8SPM9v7t%$g00YzCFn6m(SH**u^x7^ zH?$wkdX%mGj(lK=YwLZGelejAv?)36ebR5g+MgJTy}E$zjwd(z9OLc5){LspR1Ac! z?@PCOr0!@JIri!`d_P9J8=~v=$XKTDoil?DpWWRB-}#VwsNQRT$`t=_E12&hE{>ty zroF%W%93)QR%t!Nv1;k@d`_@hVEd*huxN9>J*>LTAC@v)D6 zEa}{eM!)OLlp8v9l5rw@rhbQ5)QL-&0Asf-;1RNyssboqF5`o0)3cJXneVvN@n!#uW+phu4LTNWQ( zzvSmOkCocTCcgJNk7+iSV|u!YHqw#bI@8ne9$VV@$oFY^6zz^kw~VbGSw2<0rRzAK zg-&c-=V~eM8||^BO}f&?ajYkNpXeO{seTbh?n|Gcr{Z-rj_FBbuJhQ&eIDnh)w1a)%3u$t~s; zuar$$k|P)R_fO(ytY4)Xku@)OYfTjl4|vN?Mw`! z_GHdM>^KeHo2ZApB2>V98h!?#_dj)vQs!O${mweE_CWsl2YdI#wlEIb zjQo0I*C$vLSWz}|P;=9vjBjsr?5(WbL;pROck6c6rpO=K$&C)M4%OYd4DEw9eE-1O z;CCWpqG$0bXR$GGC3j02Y9UD;btw6<&wLBGh@=X&^B zrC$E)xJCZS;r|~w|L^e|?eTnpxfpTeS?b6~!2c?Z%(wI>M#epO+KjPWs?T;Od+{+A z!2eV**@c*~lpkcy$2B;EsDFPPeR|BgiM0vG!H$O=(>tbQ9JBzvJB@kIn$LXoC(n7? z`f}|4ZP@h;^*q=6vh3kE@o%k-I`H?A@nGk#VsreAJXd3UG?q4}Ic}5pe0`Z~gIt$r zorZDHeDd^Tw1rqx8g2FVhz_UcxRK2U*ymf|V>NZqxvuUu#sH3&KR_K|K~J)`_Km)z z8YURb#3oM0me;o*d_#Ihk$x|u?zhl2X^y?E-6(tOIP$LiT^lF{+632+XYO~aWw8V2 z{22caq8{`~Y87`=Yk2|e|3bdvyKgpQJUXym-5b~}mY*P&Z+}gq9F0MZdzjhoL+=d=?T5s^#;99beb)6p73;gON=z9}m>+Zx<_c#5w zwHxx6!{B|oxa(uwsqeIt$398!dM^C!Mywl(Y?93?GwoM>MV9BIGbzsr{&SzN-G}V0 zhnojPJ=xm2wN2|P)@JPAdQsGlYRO*S)dTAS_<(De&tDlH>hf6c2YrnjG<=3WXNtRg zlgp%frL?(QyM#8^+g=3@AEjQh7BRR5f4(QY{0W;`U~DtbQ}+6MKg+i9#cwc>unhSE8$M-iQ^I!7YG-JNHF5kvD^Iv(5I=uY4TYt{?_`Vts6UO>G>$JJ- zcc~Z$~gE|&cSoe zr?nn!Lkz9`SUYrH!(D8_G3kT%_`ABN{H?*)T2rhZh==tfYG`Q^W7e!$J+Zsg?_g(r zeRalce^=*n*{hQw`=C$WFaO%TJd3L~@BD{-dDIrf z$$S0XdUq|^%e#IN+$S)Hux;4zQVjO+h3gB5vuoi)?hc=q+r9Kiue3{(ve%xKdE~O{ zxxdMyw0rC=|Js@U#a%4K$2PP#eL_4-5qdBG*5H}z{~0zvIL2Q+u)EaHjw`soiT)18 z?&cE5QjN8q?3J^!k9elcrBi&QwI}}7BW+2&S9kUI%01^la8UPb*Y>HOBz>2EdDkwG z!A$6Xk9FU^_`Lwb`6cVn`1qe_`}Qwg-N{}ZkauM*Pww6EFE0M>b+!0+E;jh~=w~s} zKDGKgR9cOKf-2d+UM%nI-Ss{DJFZuv!(l*?+Yh_hW z{@wAfuGQk-?~S|q0q?gzZNVH++sF4X^m*Z}wVo00EfCI;V)y1UyP%Ix$$u@oSMRlZ zZ6N%($DIG%_NCd+!GGMNZrO+Yb;rMYsGX=Y`X1$=pK|Pj?sdZZ5$Hv)o(-sn4Qo4+ zFQA|O=yRsDs^1}d-FwpW^Q0k2+dPDZ^gfy z3Wf(0a}P$2-{Je~X*;bYvW>{G^?%}#^KU<5r7lu8PVb!59~!Tu#s08O$6gDW_ng64 zZajQEfPP%hnC(L59lyrn_llm3c1eGW(Pt<*KdO zmwc)l@}sTmhru`Lwb$hOk@N0(#9xp2hoxRV0Ili$tBfIs;9K5=t$za@dsI5KW&MqP z$)7aoZ-W1bed;DPEP0>B{yy7uZ*=oo*G4cOvLo0(XWz~Q zPX9pX*K9=ZgN``5#s&Qwg8f~EuS)tQ{}KDt;mAdd#p;^zs1e!AyE+O_$t9T(dpT9ZPv4p(zcJm%_qmH*9deEt#m;Z?U(q}x9|J*057a_ac_We#= zq)#5}rz@HtCJuI|s{kXq*T`v2`^TkwS3T-NMOMGlw*^6z+ zUX1MrpOoUQZPnsm+Vo5K5PhKah-P%Nx8hHjRrx;-`6r#y*22E!N#Acif*()(`)rWk zmwB)1!(8n0X0APq9Pst{ux97f_-w7VA=%5naz(EbkHLS$KIJd}V(r+3{O~@;WUq0p zd~Y|OM`K?J41Q<6A>NVKcrK6XnfyBrGY2x}1pgk9LkHJ9jqO~5o_`QqPtO?{*JI<} z>!n*Ba=of0dwC*ukP4}_M;C3+Id|6QE*#oJ!14<>!{p)777{dj1PDo zy1U|x_F3lNoJ5(ZbMozuZcHXF9L4;^7T`D7cxt=ljhy>Z^+UZ1yBAmOO1szRDR+5? zpOpLS+j!QrD<6^n@SD;5>qzCeC?vuy<;9pF&188Xn zgER0=E2E!vd5$08*| zRd_iZjHk)F7)PGvdGH_pS^AWRG>M72hu-~|_L>@*|MNP0+L7qZUoKG2U}zy*e^`5A9w{K0DvOY}>I< zG?G$T{hOGPHlNxk$bm;otEVIpC_)Og;?m-v-Nz@f9~h;{&X<7*E?Xu-ygt z@=nI3iGTG%`?qiLR{xbtE&j#8Ym8lvW0BN52^u)T{Xh|6b?y5&Pv=nQJSd_wpaQW?x>*{j;Iz3(&sQd+d9| zdt6%`ldeJ>`>V3IKY5M5m5qI?@BS9MpM!ttQvTYra#aWF@$dKkF8_J#muKTt&U?@x zJ<36xy^r~u@z61vd6I_SRTjL~I+91}BZd&iu9AQI(jTjr_UE}6^oD=+zfu0}BQO@* z(D~p!a1NTpSwF&j{!P%k0eV-9t%kpIT^7D@dt%fiv6mL*8M+*`5b4Y@5GUm?U1j+< z!hh5lZwDHhTka#5H4U0*N%+IjeVo=beZE_QM9yZkrX5*Kwo!+8pJ{bOwU3VwTD zT$F=YNR#+@ZNwI3uO2ct_&E1%jh}6p=eW?gPcH15>-UjM%@*tEN7}?!*{ky*`&h4* zuhVqhj&t>{-E)nizs0k}IXsrr?%sKRFwd_7*?>ua*Rdl^l$W|W3z%{C| zZbdxALLCt^ueBX*L0pxiGK9BF;d>QqFXia+ATQ81kUU^BsWUp_hUEj&~Hgsy4 zcv#k(sjrGbGZ=h?T>78**$(NIZfI+#mhmU*klTrqxnix&ILhw>xc8j!6WYCTqipxe z-W}gEfta%e{=Qa|FY0&Jq@iD1cpW-^lyydEdz_lt$;8NYh!5%adPI4z`4=|uZg`#< zK0joyuMgQroZ-3$>1SQrUQxD~D<%%F#eDHdawGqbQ$ysTBd)6&PHg^jE_-uB{fa(c zxtr5Cu7dv?sK>4BeXoSgG4zK%^daA8yFc-Mk+L^WQTF@DVI4lNsI2svF2+&*Z|$`i4<3b_uR#~?;CL_NzZ>B9c>KVQ=)^!U z>2n{A+^A0;!=Fl}vlv}Ej!2)|c_V_qaGXCp9fwcnf5Gs1cq^|O;Mg*jR}Y-o&VRLS zs&qEF)}x7UkE$M}qPep8E&Fk7sVsg%x_lg7y>2-7yA6k`{7Ap)bJ3FCr(j2|{3jc} zZ$6ILEH%!=1IyQ~v zIF>IYV=+I3zqa(nnCcg!OUDt_&%>+NEq!cF_m6-dw-Kp>jS$UPK8@y`8e@yrVl@A% zy|sz=Pyl~x^WtM0|E|Z+^C8n?`{>0-1>|RsW74s+>&Qpn6R|IQD|^>_B6>ee=I$4F zb4?ZGHxV0GBj4YJ_;ECL^E>wY@Tm`yPu*vXC5D_!EcqC7Tf>>d>NY=laXEN-_6Fu@ z90RVy@!y{T>pzmmO(d6DKl z%@O29zo4cv$Nb4Lsq=BpT{~|~Zk~3}{mz^kIXpuh;B6ONaKWoK&^P#>7m-<>xgxAi^thpxBY zdawGRJ_y46g*xm8jEx?0yr|yS(|xfI{FRY@%JDQY`!?oqUrXJh4-_nUi}9$s59HXZ z=c<-$>E-c>Jkxo=t;uzs)%VxKUi}a3y~Z^%;_uuMb~Bst{|E73%VYz_B5EnVFXwaA z)i0v%dKz_himCuELfor&7o zW#BW|dTehu*Co4QLnEnYpGj?UZq)72jTz)-H&P$n7ugKA2L4KsgM9Fut?#w@Kj$xE z{gqgUzmH>C_r>0|O3ojetKx&6_B~G2KY9f}&z*>;HTa$B;}&XU9nQJ<{I9$7jn1#r z*KB;{rL2d4BiFwoY$4XHlY4&CnxUA6o(JyXclYt^3g`Nqzs=vv;T*4ZZP)uS*KyyIohwkfp%z<1xV=jshx|w`xE!RAEw~0M;JGbxdItOLrx)kQd z?jas*-`DrDbf+IL)^Fq)>AhCecK+r(OF?BP$0h2JO-u7l6F>bi!3 z%=>%{nLcdX(AT?;#PtK}x7e3;U+mR?cV(cg^ncjdZ}CB^IiIgg`+EbUf_m5hc-#X0 zdmnkhW~`&||K;_mYwKp@6|ZHU50OwKPM0 zMn`O1^8!x8=<^8j)=Bz$*S6?$a@`ku-^Wze{lMR&@^DQXwl;$~(s$c-f8&qObsdQB z`9A`E`8RaWB_{nF?0nzwp45WZgodVwsf`GP{MxbC8}PkPIX7#KEtf%#zxjSGzpw6l zuXB6i?|h#--%sQIUy;`wf}MSU;|F;57`!gr?xh--N*LaKqr{7?w?b0m$=4R>+b+eh&!dmgcN09$*!Cd-4=;~1f z=yZV}*cKU1HiqW-=h!!*`&Iv+d=R;4s*frUY{UOWG}oLS8|idTQrcs!Mp^g8KG*$R z?>#pbD;s?sW3zk7pEk6H)gPNS1?FPE1^LO1#wOQX>NBl9y1vA`#dB>LdH5bf|G&!F z*!XVbBEz_5DciJNVx4Qi)VMA z>q^cWSl<(Sb4>Zjbw6TmPr5Ju#tL%+^4F8(#l1f^e7yftV?F4BVm&G7l= znqsft7yo!xBK*Jg3uWNg)%hCdZS>QQEz}v;Vw%T??z>hia-Q7pi*ecSgzo#BvSjV( zWY&=FHS_ ze0wCh#}43j8M2xwPGYGp<=E?Q)OGQW7%SF|?2g5(3F(i_sk8y<)nC`s{YL%1*efSx z4==x^Zn28v=jFYrtL8eys_pS981cjvQ) z_EYA4{o#9JP6V9J)jo~0q5I+YYw5mxs(0psVj+&=88rg!Lto?ADSV8&Z(gHc5wkMh zeh$pV+P0Oi{Y1X0+-=*K=utY%hs56dqg{1Df2scuztgDSH|8oI+h*)`J7cfc^jF*^ z#<@QD4e0zh^8;mGPxtj9W!;x=dDeG`y?BeU`mN53xx1gmy^ML_ogZy8@E2S07YpSn z=F*@Kj5wr?=@*rW*V|*yvDW`7zdt^| z-B{IE-4|Q)6@9t*o5w^A&F=$`&~Y)2J;&bjz+c@Le|0&>-@GTsU!SMXQQwUL>WTM+ z9Bfbdh-vtpT5;DnEcWi^7sSQK(fh5OE9j3+{P*2=-vU~{2yGqa3!(eQ(>&)1-B$E?=to!CKq5B@Si_m%T2)!=*@>~Y>@YnaK)|inK+PSv&G z1JS=P;d^H5e?s@=N&b4P`^JFCYt(Bo*Dou#M)A*MulAe z9zzD9`(kg5)?ZTV{3Z3am0yf?%RUR{d>edTLagaNTb;b(qAeVu43jMyiZ#_=mZ{9}E ze4EcxUT_2Y9w^tlpx`~&E3mtJY@F6S}Nk9wxQC&s4r@DJ=8!C%{{ z6?Dxqn+gVYn##$V--JJr_lfFlXyo*Cw28x(8q!Om4DU~ELb{c`daky58Al= ztGnVQzWG>J?0tt5^^r8@Qt!oIe#Bqdh5rftZxny^(tF&MzwOwj`YZlo+9>|=;XR@M z(kE@|zwuW6(N?ShI1aTx#$Wy)qJ5D;zl{NE$$GmDj*h->3 zbW0l$8}AAI?+$-)HI}=}v+>WJ^LDxq`#&-*(!+YGdC9%{Na@;)+->;1X z{-GbgMxbh}1T*U7)c~<8+r#)#>CzSwEvr5^tEavyci zoJspvme!{kkI&;di-)kQpD?ff1#D|q_}vzMx5XZJ;k$hpub)GX_zz;#?2v;v8aHzp z1pdk<$3Jod>GD4LV(uxuo7Q9Xxtlca_)e1J>XMN<3+Q+i2-GX2J(9Y<=ba{;$r~NV~l-}D)oiNd;Zt0tI{a43o;jiAx zgEB;?)V+4zU3eE*f1bI_jkWD~UOm?pdEvKa{K2}=wl{73L7R{^@v;AqL5_dqp3*3N z+NN|FW4urQkDp2Ur0rRwP!6`G{Pc6So!gD~*>BZW6E^05*1c>GSv-Kx+M+M(RD)-5 zDZphrYB|r_kGgLk`W@ETr1!>(rSyeuhknUpj=#LRd%g6LU!?oNdk!?7&RBmW*otp- zU-#%W=v={?kah5pM>Fo8AQt+IkU=f{LqBr-y+^&`og3-h-1wO^{#5skAIeVNB4+EO zLT=t`zp?w-U97YZY75_iR-X&)(*|z^diFN(*Y|eKr|=vF%~q1`YM<_n;;(*qKWnCx z;WXYi@paCJ_9f4&u7_$yPxituJSGn6rSyn@o_qS8_(yCKM|Ht_tQDfKNvD{zPWg{D z0R5JJHDY%7Ec+;9pUc4Ct;K*}D>Sa%7x00%@VzM(rD;AM$b)wOQg=|`b2*@1pty3}9&M>+SGwlegXT+MGg+eotTUcl`n}eSKo$?dpf}H1-hp6Mn?{#6$2F_^V%$ z-|2tkQ#{;>RY`U<(8>NI_Ep&XLf#VhptJg|0qF2C$e>+%r6JFKLXUGl5;$qw>K1W@ zW-9uPWJB7Fx*z^Z9=xX~-49<2ULC~ang~sL!u%4{_X-y#Z<(yM%hZ@446jo4?Kc%v?qMBKHaW%P~|v6@Ozu=vMe3 z@5$vAe!r~qepV)-`(7u{w#)eR=Z$a>Sde$DjQrn%-#>|Y#3|;SVz2HSQ}AaALUT2F zlSk>)w|0lWc_wo8UUmN)>S|B;s~5^j{8_tL!5^mo zk)plJlky6`->B|uC*Gr6@ZWcJ$3qnd-}Sx;`L@qP{R-SCLQhBJ`(m$7=|7mWNj}nb z4f@s`|2+59$L7A@ae^B-J;A=EcrEYEdKvn$26=3&`XP@AHX?pG{^84W{N+V`*Jg72*7vAG;ddJG`=R^x z>wVzb8u(Y~Yr+q|7A!x4PxuY}P84rt<*q+e2D$Ewy}P-wn|XrQhW|14MsBUW=DEN0 z+m^l`|Lp%W8`GcbxXEq(fA@Q#Tl18;KDaylgIDo)-aXH)mqqu*K>We@*VS?30O}N* zqw5DV*1sLPX6w(yCuE%4qy8{--}NN!;X}-QyvN)}`;Ocv?7NJ={!1*tqyrfo;FwTd z=#P#1i_c?hM;x1s98z6F{3G`lQ)!oX?K1F}SMzCTOgd&P_B!=hUF)sxivjnxV^hb_ zU=w-Jr)hs2eJ;{BXf# z*Czh#+pteyPdP&^{6pUYe|1D21Al1jliye8XuF-5>HqBf9Pw^2b*|6igVVFd67n8&Ddp>OJE z@Ep0nxsUkk|IrV{mc|0T(S6&bz1iq$D>{E5HvW2=X@Z6V-*YUs(4OnQwxF!ax*s(* za~^%ZxJ#G2`Gm1o{G~0%zIpC%yWxMtzDif}1LlPI)$a&CwDpS&Y?uAR>L`*LCp+OYp~-@C@TjjFZCem-Qg> zu-xzG_*3IfzF+*c+Z_Mya(`(x7w0)JWq2Jnw2Jfn%X_P0FYm{(r+mx4<-4r^d9ES; z%E>XJys2aI8ZjdG{odyeo^#O8yBHH4M_k+%pWH&MTmtvtk2=D6`hf7$^>n|S^XT{0 zd$DuJu2TI$9WgG4FN@q$xrY8n>{Z6QUsBgm#YSJ1s_vL7w$Nn1jJH-D< z{->U}%eVSbi|^1m@mD{@-}(dNrX}KT)eaWno1cP@i>Wp3O-{W5-SjvIiCvr~|I#8x zwR~XEQA_v5Ufa}{%9Ax9=U^hQ4*Vnc*Z-99*VoFU@N<-ZmFsl^-u9vUK_yyaMIr6cg^ zjo%OK#b5n(3}7yAJQa6!tBk+$&GC;o7P$s(I*+<7zNldydSX;rg8Rn!wc8x$5&u%` z*Y^i~p%>v-^vPlK+B5SAX`E>8sXm6^@p^p0pODK2+HG$)?0qj}H(g&K4UPJJvCr}M z+!$&uZ!RRp;ah8sy~6hd{-ICGfm+@#v7>c)wy?MCEOmW#u5vKG<22grG>6oe=kY0G zo;g9_ANmlvKRS`-A=NGIRs6k{I6aB)-ifUI--=81-_sYw zFYp)N9Dn8InAg1D^UxKq$2a|gK2}1mJwaXbcYQ6n_bFl+x)}Y}^ZRAp5Bxo3UZpG|Mn-%0exQ!e!IZmKv{t)@S8%dV;P-j^3y5BClPW{PFYnxr@=8zre!-#QX`2k7wai7NJ9(>a=t0+N1ft za}x3w@={jni@rx)@hFzv<^Hwsmq&epGKlqR`0Gw=Xgqnz)!;CO`0zPscpvum9_aZb z`!~^@pQ8s)U^5HFN?KxUV>|hryKP7AFOKEdr|&iP%A$<}~Y|}pMPi)0sy^((PNFMY_fqNXKQ~jX7nfL(z7g;kp+aDsR*{0*lPkriB zzQ69x_^#IQkme6i1KS}cI+zl#)vqIx%Ls5=g0DzE89?J z^>TmhSidUv`FJbGK5Vp_%RYlYd@nRClOL)HbT?mW8|LW5kDVABj@RdyH|ck!Tf22v zf5gx|$6p@YyVHO1S9aQvK1CfDyIc-puYbug&(8yYWu@%AH*&Lb?l1ku4)@3j1NS&e zZ`h}_*%$5k|Jk>~ZeCOwFt)e9f%w0(j0Nhx?=*^kT-y`=`XI6G&i8bOzxpdp;;Pz&T>pEE zf8?Ipk@|1j_9d=myvv`xM*8F>coTcukv8)*#>3Bp!6$@E-R$qpK8yj3BX&gQ6OCQc zDNpK$ep}ii2a;dEmj`+A+I$cGa{Plw?L7DW>YVti-#zJn#OkPx+g8vdJ>uzZ|7G0$ z95jX=h3*G`ksJ7o63^-0MI6|YcbWCO46Lmqg8}%UGaRdEyYe6}Vx8-(*r|8YDyH%h ze8sUh_($&3DE`VT^j{j(+t|fXnx&&2{z049X_vM1LSN|ng^2OL0+Zp4U;EVr7^4-` zceh|HHqJVP^sBqlCr_alp{pL%L3ib$9tHLugU7I+dVG5Svf!@|i(Q?tAN%eJ`=CqR z3f(W~1U{$E^Ln$9?WfmWcip0q+ErhK6#k7m*s6!Md~A-t@(lgA-A4RR$S20oeh%!# zb$RK&a<&iOZAngggdl=Xk68Bh0_u%EgXI4#_%vhR`R4iJR`&b3?u)aw>H7$Mr<2ds z`dqp10*hUlh^f9wy;nDLKdX#vGxWF~{$gOw$o9<5?bqFNvA2EO7W>fsM)Lih{Gs&7 z6Z$`u@x(6l(OYO6x?Z0~-DwzgxT~y_Xt(krePzEdO`-eZEPZ0{d#8M^**)H`X#SvV zLN3Zo{noA{c6e0JY&Y~euoh$cP{uy5Xgltni@oBv&?EcuO`S?HFM^k%y-tl5@b>H`kjF-9?B$kulGGr z%1!y?yWjbn*W|n3d7bhQ7k!qoM*OrX?FjjHa(onAf6w_O{i3ptygGp2R7hb{T6y4c6_y3!!_>bJUI#a;h21)9&rk8aJ{k)hQ2cS9~ez=obcZtdEX z*XvWmuXd;V+8*!f=%l|NGPgCPzv>XICGZ)I*I`=^Io=B0SEnN1mtXBs|4{b(wRGQi zyu^EGeeaI%0rMS5@&3Db7n1VvUC3VVdr87>w0UI{eoR^G`_+5(S$$A1{0y#h!SNbu zCGRG0ZlP|NbQGQnZ2oUM?{SajxoXkDQHl9w1q29LHGd4PRM3)lXI+UZDaX1VLV z4VrUO=P#It*`Il?Mb@lxzh6uDr7hR}cn5Q?`|5h^;_G{-#6Eu?p6BsiFyE6M@8*$j z+tQzD|LVQEsQ&5`#K?H-&i9>+5l=xshcjaMsb~SaMt!&hqaDQ#YTx*@Lqt^VGHCp{*Z~T7fzL>dd8{|&2$)T=b z?*4Fc+5e-C@degleFr_g70edOqyDFs?nitm>%K83*L`KHz9O5MVEInu(J(~Hz&T#l zvfBMS!)*mo^)S5 zF%Jp9A32WrFlL?%-iK4a8^*kHqM$mb(8Rp+`pD%_Y~nHRHzp_tbzdKq>%ML0x?lGD z#!dYY`agxS;#-*8YJl6NFR9u@6KfS#rJb#?rIFiiyKNJ)So-3A_=cRnKwk^6`V{%X zY;zxVEBwCpUN7ef-PcF!3y|NxS!3}r#&QF-tMV3W(TR`y)9wN>&;7o-Z(d}(wpq{b z8!wSz^4sYDlh8d1xt6cLeu5Xns@HeRIxS_w6_L`|5?dFOAZL-$3N|tV8bbvcbC+q1HWJH$)t` zknz_dd90=T`UG=vcljrun1}wIhrVx2e$k`9G$%wx&CFq*;g~jZ&w9FF_WPmx`bozi z=)@mcvoZALj=y@#oV)Wb>rwl>5I;Cy8R*k<-4DNDenUKc2483)U3Mn2Hd)v@Vg$-l-k7U<4r~Bo6Ki7Tn*9Osp^bT9sUcOScYQ8dv zn7lo{*m@%+G)m@pDp&*d5rDm zSmm52_xtL;b!6iY_&<+r?uZPQm7M>&`;{T%*Mc$6XzCJ^#NYVRi0(T#VNS|DKqttt5 zS)X%EVLdWxoZaK4> zHVZh8M?TlGe~`Mx%GB%no4o&u_N~F!TrZG}jbgP3JVmIUC=%HT~zj zwHTAW_TI6@23_ZiPx#CCUFVg`f8O$!okLsK_w~-3vuil-YEvG^Ry*+f);i~H)$he& zdi7l1;dJrF5C8eM=`GoLQzEXV&C3>_k6wIX`hLt}%F}t367Qw8vEE?&kky*b+X~P} z#B9Nym4-L3H2 zs^2T5TIUL^wR(+;Rr7rGfBN#8vDx);>i1penPSm?RS|bkg4Od*CAe7RO_f$?j`Qj8 z#rdj=yM08uenhJ`vT^U2IHxa2UHpAzY!j?i&s&@SbKW)_@C1Qv{)qpaSH@)L>TC5H zFU!u`mpB*L#am|rJkCdZvKg$8m3=g(sTk=QTT-9Jr?0TTu&m-V@tne%)CzZEY93ssh5a(d3@0-`^l|Enn{v;<{#eKJ3WIlrWe0f|%sC8XE@@k8 zQioEjx`sUMN#>5{kQ;ZncEXyjwRLySJJ}bJ(@p2vyP4zqBs$RCU+Z{RSm0jfgz>Df z_U?S~NXGxiGF~0WIH+BDTRU@&iE|^?2VA@E8eWgqYn(T+X5(BvbB6z7zH%GBPwl_vQ}%M<7LOn%G-5&u3z#wUw7Bfmv>~I#?<(MPM#q=i#CV6 zn9o@MR|u+~n5@>z2VmddaSfUCf!emu75iMeyYg4Y?x6>9jPG3A?Ajaj=4qZIc&lsn zUfouF1XwfTI=nTg3*6v3DQ%vdFUjAd@~@a*lll6yJw-p-! zdM!gle zO>~~ZwVL7kL$1m={C$4RZC^j(eXgyd-aCi+)4j zjguLJSeJJGpbx#hvTvd0qYVwGp8rSZ<9+{D$Te&{m%Hcjz1OKLA%A6W?x_EF4B>Ml z)B@)4Uc>{cGFxUa@$WtZVSW1j$oB!_R)_0p9Jlz4q0fa{mv*g(&oQ8ri-SRxhsF4MfJHapZ}tMoz~jwi|`Xus7akfonu}9$K9*G zV6h~Sr~g}KOXT0?`dD-S?&RNy?JIlzh|e2qo6HeTpvJU2I?^ZHtG>hkIkzRY{0sa` zdWOfbs`VwN z_SUO&+xMBnuzzJA&qI5yIVtOj7vcv_x87;n|IM3rMXlF$+8@F9&vEP*viDe)fBe>6 z+>d!dW$(KreV#!d!1s3}`%%m-Hw?M4npnRIbJB;SYmZx3P)D7cQl9$vdbS_35Bcx6 z-+t+O{Qy2>Ht!~o-fx&JqA)puv;1HOs;JGIr2b6Nh4+P=Que9Ji`pQW)){jT#ud^dr-{TOU|4WCu6 zg?m*VO*||5X5L?L4Y9u6v7NDA*_qqBCLwI!oUa`B%eEi#kE73`_*sQun zc7;&^biH7GdJ8hxnfSUd{%TKXT@!4Y`|?a+J;r!{jWTFPPG?%L)nAw89-ozePqrVv z-*&?G^|!7YAqPx;0Dmx>@!;juZg!yWf$`n|Y19|<-5_Gb9$a%jIYg&=<9w2`_qm9^ z+J3wf#du(jpiYwa&PVq9y5@;~>%sJ*eJ>5{uGQWgd7MI=8;`y(AlG@Gbs5)VKS%J} zR;;TZO8i=8nyYOL!A7s}nIGjB^3IRTV*C1SvDSutuF;+EIvKw`MD5`uWc@+v58FV? z>p9;XJwKc=X`BATT8{54Fy4pXuVwqSa=wWBVe=t>eS>2|pa0;Q{(01pK50(!Qp=xr zOG!gBynmD&=YITJd(<-Yb^1AC-hBMvI57M%`Ot@`d9TkseXeT}TMPIfMtr`Sbz5od zpUXYYbKB2zerpBR`i!UH_s#oq+qVYe7(n|MbM4uDK%L+^>%%kgD^JqLL$v)UZO$=n znCJQ2pmiPPzpS>e{FS}CV+Lsk^Jn<~C2s|TmsI}Nw2)&9zWxB>@KfrudamCyR%x5Y zKgT>({qN*_ChIj?!Tm6DnJvBFXPSGnQAa5lQ@;k;++iKXv0TVJj@{Y5vbWY3eqY(= zaldT))&m{8xt7q~@rJsfZwMdZcjkHV{vV$e>Mrj0*7lXZSo(i|s7GB2ZY%f4+(e^t za1KM+kw+iG^JkN-U#RQKUOJV(w&}B*&ds`8r%@Nog|LnJ{Pr+*b~^8*+MO}hDzw+F zKF9B@)82@8Go653X1dl-+YY(MF}MACwZ5?ZT5(_7*XM`rI~LTh`@EgJ$d~e#r~Lgl z^4Fd1N6r`W58JNg_rvz}`(CfEq5E^O`_KBEta!W>8#!+37XSBg;{7CPQ`dbC(R@ri z#6^6xPiv{lN!!tme3sPTt#4r`9r%f7$+dpLc>cra)mreiL~M{x?6pAOS10!EgT3BD zdkb9e=Qu579>-d??|4tYA9f$IkMVx!K;-`61JnV1zqHGPyvUP%%Y%9@&+5H?zsgg$ zwx8Gfl)tjCC4ay3cjYg>u3e=Lcst{e4PL4mkg`AzU3aq`{NJzawP*caJ=@o2l({w( zaWwL3F^w33-b}@>{t>;}pS2gG=xbngp0~w1EPne}$YG{>WjogDly%5Gj((?4Q0Dsj z+|TFoR^~nJkrOx;P#5Gu9niNadub>3CEf4I_M^@(X0>X4^>RMtFMjBL2f5RC#l?41 zzZ4r|Ti=H?6yJNHH7x5H^4*B-8zYUS;%Mv?Q^#4_n(M zOQ&lkJ=LsmF{l zo+0+_;CrRC!xwwAPhy+v?%vPwdGmZ@U2gm4eA((Khx}bWv`zhh zSVYcO9`BdqzBV1cKev0&bHDF(>KJ3r3eP$J3>mE4+kAO>>4~*3^0d|PfmbSf^F8&x z5!*NJmu+8Ji@W$LH|uZuYR5Q!)+eYx-sktqIhVbkjp^EYZs%or#b;$1@(#bF4(f-L zqdrO9)IZs`y6VyI%)PwF`=dRN(XVZ&2VxPjFW36|V*Bci7%}FXPTu!n->LUf**=j8tz2VWhBmLi5BZ0^dwnj8{5jOXKV&bR>YVW^#(nakU-Gm1q2A=aA=d+am1F<%98bu}*?M#jndis&EtkK3yD#!L_mfX|bwhcV^}x?wXO5ib`;EkXuh+&L^HJ}) zpSj1)dizexl?~8uWUn> z9^FI6x!mJCev5k|_UH1CxL?cvYyaxCdoKUb1NoIl`^k0Gcw#-UH@2_*#SpveWX$JU zCX1U@0$ad7v^|+Z}@NHi08TN z#Xn>o$B?~8uXoSoAGu#y{`z?3DnH@-m3=Mws|S9s&P!L&ET8%r>m%wwcYfdBmAyVg z3|Kojk=VP_OEu;T`;VNHy5nBtH{;E5&2{@?`(YoE^Vf^}<^W}X@4aHH?A#lbzk1-k zVf)cW`2Vu}tuHCZdh!pOuXj{mY}0<#19d>1b6l%@>y7#R&E4@c^Sl-27cU0;Vb;N3 z>Ww)UeZU~(bpW}|OnrdzkDOmD%C@gP#rUZ=wr{L2kLATLe+tiLih*$h3$pDs{@`}$I5je&-wk{v9&&rwcpPZ^WRgQXM3r|pZb>; zba4&FUcVs!@6-pVtJcKjTb#;qUwoC9HWyfQXZvN@D?9JC9b-tyK5SndQ2wC|a*-YX{0-JdFR+7V#i-KwqENdz8I7zxkEVmK zfnmdj71SfPz(3yT_+OtEwLblxb|Zde+gDD?rMtLaOZM8izOVmR&tZ2R0*oUn>$(+~LFV$R6J-u??d7pju zasB=O;vb*W2WbDf?aOaD=T`>WfVOY#CXf43>(TG|yL;qK#*cdPFWY`C*{chU%3r$6 z_P?z1*B=^3w1@Bmkq?Mni`{!Z>UnROn!INz$>tw>w-d88qX(GNlPGt_1M z-LlwztjAF=)C28P9qKRnM=qiL+lPIH{LTI3C363;|A_4o>uVjoChT9`v5mmRe!>RK zZROECO5bm-*VwQ8)fMJ@<`e(FfbOqw@>dsqKAyFYtK+LKBnMn*?MFVt_Qkc4xUcQI z>-+V49<^67XhiqHX#lbi1HNu^~Ery);M8tDYNM#aW-H4C=*w$GK7C z^C&jTqL%!{&fRt!jr}42i2Y^xi@)@S{A-QUw>k|VH@gd=({?vk5KOJF{jR& zO6UCASnsj(iu(MgIxuR~DF1)g?#TNA^8on_+ZStj4WAc!A9a3fysl~I8Mg}0#ip8v z@-POM<*z=uD}QC*sQkk(g#07-vmM*3C4c=`ZvX0a$iJ4)&%X=bANFrMwkJ;^fBV(` zV_rl5=XxS@e`_nwBYO?cfmiSWm+;KPYUW)lS{|TJ3f<)Y$9w|0PnCb|Nt}DKedW$` z0u|N(rg<)}4?C$P|FHdf_TQcFx9!k@hy!s9od|tUPTH(?8L@v^Zsajuk{?Pd&{ULe4ENU+&@eIY*v(Prl~Fz0caGP& zeAaTit|{%>fU*yqa`}swvI+Tz?JK{Kt9^AR|NJ-oQFps-*|z;klX1Ke`RBQxdZYe` zMaW;CLjLk%{AYbr(tp?Xn(t`)&aXMgqCYhbpaXNM1^$Ry@SD+r5v>1O9lTd%AIv_0 z{Uz56|Gy@*t%X0o7|a&Rt2jojC!gO}*7yt8HKg5hu@0QHzrafWTu=VF?CqZNfY_x0uT-pD`fL-~dcU_U7iD1XO>&atR>`U%$lE@BSu zN#+CZVLib0!RXcDM`ACT&5DZhicPIx>iTiS@Mor6}qk^Oe4r zV+4Nd24yIZ;vce)xNoedzUjJ#bTkKwF;Z^-+DdQaA2M`oWt-*~9_?2es`*Dd`ZtZa z<%d~Ab`$tt$NmTO_7-x{d-!b|DTFgT7@L9~yOrni(_>0@omFwBh zp^kJcw)uH<M&YGSKI!+sZoR9>-epmoNRA_sEm_to$QC($`d<>qz#XFR-4f?dNN%>_eSW zm$VD@N4nH$#}clUP(So1>WJ%xs<~Asd@mr@PUE?We}Vn)s40CESq<@j-7N17YzuH* z4_mq`DZ5e`bDxZ9RiL zPsLa6NWH@6QM>hrRon2ImC%Q6!0bq{y#qb&(gXWa2Kwe)_Vwg1U%C9{Bjj&ej)C=s z+5~Yv*^6`QjtLxB+lPHA{}@Z?OG1~zml*%lr--@QUD%xZVa{c~iH=PK`wwy7a`yw) zg&4E0NzA%gKGn~Vy|!u0XANrwd#mugN3w-n{^BHWwd60K?vek6{KZfk5<4+O2gP$1 zwEO~H+!a~2^kw~o^RDU(`nmxXh&^#c(Ll)X7($Uk_?<*!c+K5~22FS5Qr@gPS2=3KF{z&1md#7J!NcEqD6 z*{ip)n->uiri0UamiPa>aIFybvM&DSPHEEj>w}C@<}1qHP5W2x<*PgS%V)@5+``v| z4jA9{CvL>@3NdXmx^)O2SB`f@ED!yzzIR{%zU7_h@tx)Z>VcR>K2T5o$}DU)Uej#zxWy7%q6mIDl$*+Yf<*gd-FXOJMFYn0quLB1CIu_ zVk#f{fw2Fc#{MCH^~fAqnVA36M`Fh~rr%Jf%C;{q;X}d)HX?uR(d)fddEf*7OpJT& z^1{jgp#b8jE_t;lv zUtTON)sA@t?GIpV^$C333(C)3c_{lv=KsHG&mustzS7s7|=52KBc zf9PVZqx~p*_wbwA4&#JL_?+FBVzLikK(cc|{vW2D82^vgr-kh+fAceZbK=dTzeP?{ zmVX}mbN?T?pYm6KjrrMj4d<_APkyKmEHr$J&tTEdDvUEOk`8TGe)VU{De+u!@|OqO%WPlA@_`pa_CjDkj&snz9r2-0Mx9B# z9^O~~!!w<(;%{<9CX)C#zvJcx=_S%u3>93tJ^67QN+T+(%yqoY< z!|@|KA(NjW>p9v(*uM55K6&hCZA;=S$4LJcc~W=smv(i_968&3Cq{aQM2|!-rwhz4 z=yM?QI)NH~yLqT%Y~xm0{`v)Z3j2@vrM~3yH&$A^2)*W+n`9@E^Dk@M58IF2K^&3) zwY`A^o{MTOJczN?7UV3SCEj06e197HJC%Lp{MxiE77ao^e0k{-r~Bzup1FJvD% zzqW5(j`4Wn$r#}0i2XguUs~P8EqFIicC2Sx>^EaC>u8TJ_C~Iz&JCvSbY9dP)lFrd z=j@@=G4@jq`d{TAxG8(>-`cIYpPRX$_~}3LdzU5di(%lYZc!t?rXFsPkZg*$K8W$i z#^ee|l9ODIpPQ=h@P2oBh#V&TzP2yz?(#%UF!7|_%4Z|;FXw*JZr);yG>3Gjy|ZX| z;7cKY@o`N&K44#Hn&+IBd5Ls}{KNjW-&}^ZA!<`b9MlQzG?3Rko)S${S)N!R}#OU z#I`%7SHBf|=%BL9ZC~3C+gJAPBmS4iev#+ObI_T~-+aP4nfV{J-kD(k zK4k#i!N!YvG#4TUt&d*ctxh_Av%Vg6ja>fn6E<9yf8b`Wl-IPJ$12~a?bWk;zwgQR z!xtg{d9-h>G~tJAMl$9&lKA`wWH`;Xl(#w)@=zAa&ptwbBY%;HJnowhyW>Anyrv&( z^T`MGRsOa|{*inDb6C%T`;OrLqRKq)tcu+*Z0D-~&)%JY-I7)1;lDl6P0I{2BM1UA z3W^32u?eUck)Wuck^~WGMnMn|73c;*R6s(4#whAv6BR`x5;Va$fD;IaGYT>^E%Vgf z^nCjc|Mz$LyqDc&o%0R%yHj(|^X#v_I;U#a-fO+v2%W(>=k$l*Sscrzh16&&0zK?qR)NW ziIc~%OYt(cV>7mo%M|+Wn1*k2efD1&)&Dn6cD?cqnT`A}p3Jk4Jqr&8K9m^X%fq>c zRp;ZyB0Kt*dys!zeCUh}m8t#Lj_I-XQ~mR8bNcT&ThTv^(LbG$U+v$1;`K`slmAX~ zGWW?^BkMKplyS@7xemLmZeX{unDhOv4V$(7>fd-@{D%YRUtPV%=>IpPe>^GgZ@lFm z+1UQ#9;s0$hx7j^oc?c^@&^m$=MTJ(_Ctm+Um4O(Wmw-?ze#50fAJqKp~rH@W^BK@ zr?2W?9l6#=x)8OawKFN3|tUA{kw43q92D-@4ePihPTP~?@|^qyYbE_ z!&Nrau|NB-{>e-HC%1t!^^?4|V*8G-yrQowVl(fJ%w7}Dd|+}``2H$uhQp%|i@q)( z>*{6B_D9?{Zt1vX>i4UATk^0jLk>Xv2S2?I`**FW{_Ce;5{BC!=UU$f1L2k3Y5SA% z+mY7)-6GR->7UN@x%w}sjrLEMqYaT^0( zKNOz)lGJCt_^h0gPkVHZy_o*n?%KZZ$iHnE+g-nVu|K5GAs{YLwri~VZMs?&gJY3AGgi5{o-%V_UpG+**-p#)07*9YioY>k$sG@AN`NM zA74)GpY9d*MK16atnbcz;^Tt}&n6kWbx8k@e0Ry0#lG&5?fdy_@!G+R{mGDDqj&xi zmSBBq`%65q^>@d&{!(H@_kMg~+O+WN+P0NX;=xBnM;FucPTD@*w}@lqN5wW`8rNxz z?EkIkU;p{cVlF=NHzThbBo^38w-4%AwjVj46u)&SlcHgvNvEFH`Iv+b9ojfddbOHOG+Wx`#{py~b!nNGE zHQL7SYR7eDr|oULGMDS) z*X#4E`)W{Q3@xYr??;TUmA&cf5b2 z#2v5Y&%`w1+cBdi zoFD&n)9}vkiQQqeSlx{6PsjVK^sjI7V)~~zuKs_F{ffW(|1tKj{^`D@_7ptrja+^` z@%K$*J3Atmxk~7s>|--ei=Hl_E4GT?;CYN|^{cV}RR42he>P&B6dPeHV#4a*RvqyZ z?}(qfeR3aj&D+*@>CZ{g$&2Fy_ObJ}b)D_kUseBXzxH37AGh@Z_$~3qVqTNI*G{JP z4};o4{|epD>ECZ{Z)AFD^zdUDgI_Opxh+|&J1OIFx<4`T*(0K(kC=y`E4fH*Fs2pP zme*?k)3HDMr~i>l#WG??wxM40evT>s!+H9;FBn<814LVP>Jz)%6&&9uvilHyR?aY2 zjxAS-RXSFw?)k0io}Nd&``>qd555-pO!5-v=-ZtBYx~u|vEQhFvVcwP^FMwqZ=}wP z5);2T_IabobH{waa*p}b_~u6jzwfsO$@)xNdg{90aa_|_s`}Rl?XFBm{gYwVPcQ0J zuW^5MZ@aXCi{huhDe~O0e!-AfCjRUO>DTM&NPo!Hb?5y4obA)`obE^c`(G}Y?iGD* z>|a@JH1@CVtA7}P5A`VOiL5S3Jn&1&34A$jOW(F_Dmi~4>+t?fW9!d|jy`HWgMBK- zA=a`exWD>uTgv}S{Vgy%r+>ble`FhQASTeaVgMhe&llvFuMNI;RR8)IJ-ct~n-X(= zlAhQ*9LQ0Ow%=G~E4E+zr*HToKe4U+9UHIzuMMxVf4U#_KVm>Vc(?o;-?A@l{fEf% z$%)TyblcnBc1toNlf)9vxmJA1H^p!LZfx(;KCeSgn_TF1&a4FXY6F8g>$KtzH z++R+p{>7L6Cp+;{jD?)};OYYwBDc>Z#{P})&Ua=_)2$Ngo||ul)iL^>{0z+BDr5BT&G_;+fEy!~@9c|o}DGI<;A6Q@*vbO|eR zdg52|ff;%1Vy3vEe|_`2bDZlqw!0DkHzpbFf7E^dR~J3FZ?^Z%+m9|TiX7h>S^re7 z`{?AOz9x9MXUgppZ+&5Kf0vXqV&~tL{{3kB^G7-M)A4ot_`A+);6FC)UO#23|7q;k z+)w9aw3GhJsp=y$@>IxhjQffM{E_j}-q@b_@6^O=t5kVV-!E<8gv8L-PG0K{$*bKf z3oE`t+&8};9uo$vKk z5eL;)R{8(U>Yq<=EdNXYo=XV}y*CpUg0;&cPwU%05M8`8^7h{D_ojRz|6dmWypNBr zp7?leq(kSA&wQYNKA`qLr+=8N{^_&zvVXY3_q8MPsm{l(eyKZr_OaOFkzYP7<+_Q7Zl1i?o#Qv|9sl|r!N^k*Km1@gm;LqrP#8dT8$M~N-Y6G_LP+d>A`ssZ2 z>O(l>t+B7WJoKRt-SJppC$Q4_k<2|mFh1ii;urR@b$ZuN+~@zsl6X*`tN-eLYX4-Q zO!eM@8!~9hL zr-7VvW_ZZ{oKRq|rArmraAq#yR^({_Q%25Y_<(%1d zl2>@GoGHIg_w-(D>#O}1x*YXSeoA#b>b?K>w^9F<75lILYyYeCk6X9G|Kr$Co#R-7 z{^?Ac=o#-R^bQMK)jgT$Q~8XqIxFv%i+%h_`1Lj!b02m3he_DX`0(tE1@`fK^sMi6 z#kuK00=wf4;x5|D67{O@E7T+f|(3 z?<3*MdMUYT{HHww)jRT!ZEj8%3eNAMk4BXfy(mn`&8-{`46TK{07 zw!a!K5SO^#j|s!eW~q_;~38i_Wp=m?@h6_+a&jT#MQqX|DBNegYS#& z?~(7rclt(8FikFWQ(mr4Y{|4mUtwhx1}d*n|BhEb9n8tB*E$&ePsjfHF)gl5hi&^D z=l47KnD(W%Us%8PvTLmc9rjCp^+?)3WyGe2-r@`^_k1Ll1mx+<@!%vyhCMhDJy9WvV~``0FQ6{r3mi|f(fc6om@ zwyzCjBsc6GLWu?bA!EWj$JZV){n!5_?te`3zn7Zd*o^J7#l~RcoDJS>(O3Fp>j$fU zb#2%FolEwWzj|8yHtPOhZJ(V?eb{LG?Zfi@F`o|C-Y@b#V*1bgpl8M1AnPdJV10wU zPi>$5*7oc7*$g?dd3rWSW8RKm>$C8ShtAx^#W4(%DITM1V_WY)k>~PGtiE&J_hWH9{E!j8Y|I2>tN6dZzxH2U&gpZC zfwm7m$D+;pKu7wh9o0ST*T>Io%VUnO{-+pL@0{PS?Xwf_5lnl3Epq~=A1VJIpD*Wg zTIPTMyEQ{{e%rNu??It^c`on9>HBeD!Ea;Hf4X54{Qp@ zzd*0>rLJ-iEVjTPOsGRY=pNtH_Wh4L)S*7dzyk~v3+!SiY+pOPLpa+%O`iQ0Sz~k9 zb?<_m`_DR{=VzVoUUR;+eRjY$*kt|KoZla1PuB8V?A|*P>D_zs#JS#`+jnOA%`tE? zmVT#>Vnv-(ox+db700eCj%EMl8opkC^sAf=(~b%LF3)%G4E{ct^Y+0E?2$#eU>dvl zt#+c$+KTIpe!sjAV`P%GtrthuPlyekU|s)___!}EvER*O`){(g%{+(x@rCWvR{TC$ zPi z>*D>BvKISDsD9DUtot?Q`?B==T=RVVTjO8YE|;+xeIYlpB-4#-zcxxvqwZ__^iQ{R zT;1E!|5$tn3$RiwkRc4zcd$uzk9))kYz!~q>#6O-v$n_fFHWrc1Lj~UV5;}2Qv9BB zVq|u+OxKCr?i)M)vFP?K>BA*(sV{U53v_Rb zmvC6NpNQ_Co_jT}lN|fn>m2GI6XSR$#*M;1ZnGon`;5+x%)d39^MK^L?wxfH56E}_A#u|g039=|vi;HTYb)O6f5k41`RN~DMgMP){of*f@1Rw&Zr0+$Y?6*R=KI#7zfX|4vr0 z%=w-WKlJ#F`*HF@|CLd57mZ)YbF_W7#*XNoe-jHEgV785iWkH8yWN@@JO7f(X08u@ z;}5g`;Pf2-)cEL&)H%j{t8Cx3#1I?#eV7*0!nL+$`?_4$J2U2*^f~ce?niMS`HjQL zuM_`^0qB2~?Te|V<9+hy_t_%-^Q~g6>b9|awb*cF?+U3|gY`2ji? z4{X-&uZ#Kh%a|6fjTO_zpGvz<`CQgNz4#=Kxo2`8eVYW{J;+zXNn(i|7iQZgR@oReQkB_)bGpB@Nd3{ z)x^5b*DgHxx%?pmCC^`ZK<-WXOMXDShQHVOed9g0Pw)PBY{rj!qr0~xzI#~eyoNOe zTlr{5Vy9i1gT8+_=)>x#`_20ORWW~UU%&XV;KzG2o^<-@r*9gYTj|HKc5K)ECAaZ4 z$pikK`(4-y-@lRV%W2`%#E+K-Kfj&0{_Yv~ZCkGRI5v5MtUY@JUn17;c%SW$<9&XA z8uPP#?H0T8WBh(?-}sgfi~U~|U-yt;cH8Yd)^xm4dzbToCuUsufMDQF!N6Yf;s@9j z-%20iEcUJ#vy*L(JwM}`=Z5QVQa;(}daHh4o_|8(<}-8Mi*fzB@jm>m8}GAyaUa{) zA9zX3dVbb7eoe;g$Mrubg6tpp$Vcu8WiuQzEgDfF5>`sq~`kgo5uX3?ZaZn`)t4C{n7U6e|he? znd`o1+P?ipUQN%00l9(b^E%PNgEKb#<-`OZPJU=FTfzt8`|#&wdFIq>1i(9%cJ-+k6^6$$4+foX1U$)S8R-^Jw1E>0Rz}7w=5G zUdHBM6~25$=7--JEPOCy#lMW+pPu`KZ>A59-N}#of;aD||Cadk&va}l=AYWWT+f*6 zTjlqSYj6QB$vMB1XG2~mecH)pUoGpRi{(9G*9gDgD)GU+VxPCm_b2VN-bVrEnP#4E zcBA<2*Wr8^=p0{t(CGL1BV!h^D1O3KwSBxVzwI~q!~Fh>Y5S?>_>X^%P6_n=5aFCB zMc=#e_GtTJk#)8&=EL`NkNeGS8>40{A)a{->>f(|9-UG>c3MR@8BgxYnDmaZ?Y|HM z(Ept6)2p~-mF?qx`Zx9q|6iVY$HzSCQI9&V|DzlZ?u!l%PKfw$Mt`d>mLVXE?`&IC_EtJyAR9%&_BO#yw_aMX!~?uzfbqI zeSW|gBIAj3Gv|Hx5vObobo|atRE5+{ePU~!c^!s$r?tN#hXdWp(;&mC* z+(5m@KS#d=a6ocUyONXq=J@^#<$>7$X#36eRQJaFVn6W!y~9n`kzA7PZ|50a_ttCm z9{(I060lA+@6fnO))V|ze866F0&)SkfPTewa@5UJ^Yh|G^F}%LisTUA5#2u_dF0R6 zl-x(`@CfN9gyY(Cg=EhSzC3_tSNhFa*xot=+$zJ z|8k$84t(-@X;WIxHl^A8-8*B@p7jTEu?T)BTXqwaow+jXE* z`TYcyp7Gh=ckR!Yw;uO1*VIPlr(I{AbJhJb&R#SsmrYCZ`*l+m4N9$@rn0{;-;Vox za_MnDb*p;ualPzc_v~Rm$DX*{U9{(%?qsXI)BQINzn_ucr~79Qzn_)gx3ZtZuTtM+ zeq38UN z-`|+OXDl$j*k4>|*k8!&tOIhm@;bx5lNP{U{Ilyy@$S#(%KdW|o6qjQak78*WPh={ zklW(F6DIp7P4-Wn?4NPve%g2C^_Q*4{-R$eP4;iS+&^R4|F(L$AL(9M->t8dSHyhf z{!*S1^OgJg=bZeP&Do1DmiowHSMH}n{>lCuv&rX5eSUWTN_}VloW;+}Kc7|Om0zUV zRCkk4?4SCl`&T0H%8U4Y+}GX%``@`xNDXDz3Hd?)mWH$I)Li)1**|C5-s?v6 zXIy3f`~~hm`}>}S{O!9&>iVG^?);PVVIjNhr~Xsvp!&L{o3H5uM|vA5Vi zYury=3mNzK{b%R0Z~W}>``>u#Rrk-&mChM{-;*=Peb-5&&amxgqy04cj2>${-A^aZ z8h_9JxErM|WcAtacBRoiU%YGAu5@PC*|Gc+cHJib<#WFS9_Y&bwGls)-=CA;9-H;9-}t`wz3*-x z`N&7EpYu;{UE6BN@so~A;4n#`IL-Eytn;{Do=biIjHjNQ{Mr9Vp7qa?Z-0O0-7m

;YFj!R%m z5|B^Gb8k)#XWu1jfu5av1AZ|$es|`HK9k(x-aene*4#nzO9$>hlY_KxeY$&ld!I_| zLQc{g<9~DSOV$}}sZGZZJ}!YnEdhDZ%&ni8dDB}3^N-Iw$y<^yy)^gQ>~p^@JezB^ z*1|lC^;G6klVe*vAILMIJtxS%c~!q#dux5TIaW52|If?%P49C(CAqjm-KFF8AD6(U zB#?Eh;``GQw?8Bp{a@kn^KiF&jjatdpX9y~_dPU9|__dEqVDU`D z-1G2d8RPz)c*nCl#PZ^nmeB^b!u`d+bJ&mj?BPFawJy!Qp-+qtI3dp%IIe}`5;!6f z5c>p+yAqFodt&@g!oQfO@Aia$*f0LK!~JZa=NTKjcQvv9zRVZAI^*D*B`11B`hNVP z$0e|g{Z9%;pO*3IMRGjW&BDELZt*|HKIL@VG1i&>-{XpXTXo7E$s;At^vTREJSJ^e z%!?h@#&HQ8ZV4>$e~pCx&r4i#xo3*FPh^aHc7pqBKelo~?pbvYd-h+QHr(j=IQVdD z{`mDsVEG)b8)j_!tLCoovKR&CNA53&m(!Hj@u4*&Hl?niX3 z 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 O0000#6^`Q%2TQ`GoDEj0}|c zfhb{%ifHbqij5?du8KYQ?jI1Jd}-edWHw!s8^M*GO9kgIwoji^K3O^0oOt(k>)MCL-acWm$dV`g_Iy>blcZC5={{Ac zXxVY9!oNEV7Ww?`Ued+-!ehtf6gQkW7BH>^R=wbJw=k z@OnQe+DPGZzj536u$$Jzppc^SEadxom9?}qr7XJg>UI53S|?iQQszj})!QDcx9j0=g+|xo%*k2Pii=L0=wQUVsaKyFb&ZDG|Gs;r3#*0Gqk%NfY~ih`>!pI* zVS8xydD%;z)|_^#USO{<5bq`bnCi5 znszX`%Y66CL0W$}Q4@vRy5FsPwCJnb5xeh${85$fi>0T?IqQ;Do#Ii9B&PX__uWcG zB=4$QN$TA7+0yE5~`rRVWU`b5aPpSa~kl z^Dshrn#o>5<# zGt7}l0cLofwiG$<{xWOKblVKwm{_q6vxnV-N)q+r@|$R? zi1#B&REpQy%Nu_uyH>4mY!ImN!?3X@b9HNY9E!? zDOtky&X7KcLkil$~sF zvGF$JIRC2r2s{ z#qSq%J-0#`Gm)F4oCjN$-ZnR2*YEPd^>t@l^z9kt^p3a1I%>(r+|*$zi2FVSxteq1 z2s^g$gW$=cWtH`k8XzM91IZNOcStOWjR=n|hXaU%AK>TaO6PxjF>lW?(Y5ozRu(Rs zmYmeL7GQv>&RAfDt8CH&*(yc|zAH02OhwA}YA}&{d zc||9MDFN4iO2Ph?`I(a0v1QiRZ-R!lA&s^;h!@bSae=#AGb$9pYU%1-)=0 zQbdQSuJhk$OBD7x)(z^H@2I9Pt3iCtNRxP_e7Y+3Wn)S5T^(~bZ1$4q?$$D-PRuO8?;O*xaJY$aKpc9pCr`k^Ba}2E~c}3|C za-JV#oD1E^ZZC#OHhE8JyK{U2Xx^8sUJel={jBGqk2u}HVjzu=G&$ydwURql6s>94 z7Jyt<&POt=<9bqF)mX{S@ z6>cT_Hqo6%vs8kqdTJ0Qx2#Cw&U7cZ^dJ|dT}Vfa`)iFynT$x?YwdH@dLKCtSa=UNDVu+ zBJc~W+qmvSTqx{bANIQPZGl9qln0nGVjnjs09Cq~1>6%NQL&|CimgfRN)0OjvO43MbCk7=lI}Qg;^LGN!kLqE2?VW>xn$ZX9Lf=R8Pi z8N}hI1t(+|M`bBdNgWB$oTcb{qIze@xL_3iVbNx_(3^4(RaOBGwK8vA#}5@swT{xi ze9uEdz9mSQzvOGq@p?@Um0GK2(`w_h?0%oydX<$!`>XPBbptYJLUH1mQfpFb1eiKMIea2`}FXni$gzpS1FHy97Z9?#(n`>DfCVFh z%GKUD<&dt*B5R5`y6;umT79Us6vFLC#GzKxt59+ziS{+0CxR7&yT)58ks{?R%=HLO z*A-_C)$}Nc(WT23{jc=txOfMLx$4QdE(35fR3ul%e~WEByFreo$og@p(`5 z4TfoC*yhS33UBmI7#K54!8RrTHioVvbgF+-Ig@Ll) ztboNS_7vyyPE(33WyUS+;E+tv62c_nRTMfx3+&Do9*T$2d}EsQlnoKEOI7ixZ1Jy;{j#3tKcED)WC4jG!d|cv|G?+pemPN=i&PG{+)$*Ol)p7eMkGU@ zs7e?a$YXEyD*rjBwJ)SZ&+EB~9E%HCRu1O?Id-(ZzwtjE*Db9&(_^T72Cu(c=ALy} zcf~#Ay{Yn-qTUB2SqbysXv-ufsa5@iIC2u`?UQ-KNqAi@b3~Z{lO1Cb8Yy4TgT0>R z>rzvBir4e{|KtuDnqRN5Te1p;pdXSnPB^clZxA-{rOkGIZHkuV@8ugArW*J*o+<1p zwz)AMgVuN%BR*{;7P44bPyI8SaKOb+7%34a4%?zZSFMyn4A~QRulFPihC_QENI>F9 z5?QTJr)_io)bS&c7|>c=BUG#BHAxDag109q9$fA`@kih&~0%%3%ig{`Pnf*Z5O{U1aoF=xnC+M^6{Gu^Apl5ka7bVQvvU% z?Ph`|bmlCUWW?l5;jAQt*h^b<1E?+|TG_jM93}6WN)s&`odQIFV%?$l*$Qdfjd`l_ z4&)0ShUJ=g?E#8u#whN`T!j|lFId;8ncWF0oM4Lj$0vrHmv`SSS)7CiueRL z!n>_M17bAs?$BdV?KgPTm8XKFY18~_Rf~v0A?#ae4iy6wea=RQJjW<9m}HVO;7aKr z`21gBb>c?KlF5{Kw|<}-Jphr4<})=0HNwRz?6pbgZGmNFiz%I>_UUHEeG1S0!(k)t zFx{yRzW_4CpzGm3l+*VMp5bAu0^d#3O-krlrrl9N{lcOSnLni-1O;wPaF>$ zIw^EsJ0!%RtyX&n!e_^`XkOsTY&=y$A4;LEiTLNbY%jeOsfDo_EgZZjeY5Ad#w3df zpeD!M<4UeZvVSX2Sz}u1_Pi>G5}$qX!d|HBxTS77iA;@ltpy__K1~i_Sg67dFyiCD`oC_;pd^@xIU*eih+eo81bKK7E@P@zs4Xez}M`i7vT) zK68`Se#(gQvu}TUeM)8@b)Bm(oB>f15;0>y3ld_6Ujs&9-BWEar%|OrmNNfGq;Ro* z*^xGe>Q8%>#{GHjbGCp9)NCWRc_<=SQNhDMr9?=w*k12gb`%7Fw#baCeziI?5vVTl zsBGCyy=`BFf$!SArDhuMxPNIP_Egsv(~+rpG}pB^*$!7HR@8I7h>SZ&%I_}+70SPQfGM^NZDqJDKzhy?haJ~91-0n?uw=viZ?au$(2*7 zaIkSKwGqZ(qahVJ2O)hyO)MhuR)QsW9F7>GBaX$qiHdW-&)pty0l|$2V$Z4Z8bcRw z?T`OC37(D>Vo&$s1CRp(ZD*Xsj!X8O!`aL7ef2E~^b7eoIJwt?jzC{I!%3jUoF`sl zQ5i;}WM~n6G+%pb{#A485miLtlUOjuW)i!8JsNysC*p{K zE%y^$^8gKS1egd_dR)Z!O!j7{JeJ?Ic3^UpFpLdHK+Q;lt6%0fVui15uLJq?wI==t zLOT7hr2J5jasW`OR{*vczWiSbytNG>U=scysvng2o2#MylVa?Aq=xPp=^+oYV4OHJ zJ87gnojxP|4ZvhRz^HP@q#=ki^o4@N_s>pzbcCTy;v}2(G8VS@Ot2aGB*l%D+-@Cc zkz23J3tm}>^o?eiF$uo@6R(asl0RAYj=#K`7+sTnORUa8>D1aFPg$Y4H;B@mqhy~z zw-AAvptSUN!*6jKgr0YUB!P+xA(Dq8I;}b89p>}<+u9ipADcJBSQh&PcMzI>{LA&D z%g-5T{#Zo36$63a0AT})Ap!i0Ch>}kVu2(WgZ|(|I!AP8l2RcthsApH6U{t@r(DlM zZaZl8jue1m!3|tmFAyg?q9?F7L-?d$>Z8WdyfQxT1U`GN)Z$h=BE7Gcu?Bx;$eXkW zppF>nsuKj^2QDIdsxbe>EaekpPb>}IyHNYx1=b-jpKg?AYstrWUwhRjr!VPbD3%cO z5mBVLmUt`WuZ&iOaU(@O&fy)$8e8jrZlknrDbT2Ik3(6te?wPg64Ixz&zS2}r=w?K~u2DebFDa&5a^5G2Qvn~i$%)ApbXI65zG+=4Z3^XS+5h(!HJy`3 zs3q;T{iY&?LuAo`o6iuAoa+;*tl$vTxo>`-3P=wLLM2hZqszg`4I_S-J515WWP~f( zmWWKDXpsnB?6_N9$~1sdmpH0aY~Tc&#DNH)@I`RDsHHWNh;Gyfk>rAnClJ68ZvG+t z+;fcQ?VP;DmAVc8#1Qa{xqD!`$#fYpCHbGl+L8dbxwg~R@R|n`Nt(3Tsz$F6HFz%o zX+C+P#8>_IJcNaD%t>rsCG+3{(ms0L0@6)g3vw*pbfg&Ywl+D4LoC2PQJbxhP3>Vf zu3196Eq5X>XXzD^BD5Mj2j7|8Ueqe;{$ zUUA!s1ToL3q`@e=r5k}=6X*&e)7(H1)4P(|58Ynb4BLDTJ^du3$PXKkH>BY)Q&kz+ z#bsr5r6Q-^FeTh48fR;h5G+}$0{3|d@xohgW4~f102(MkM3TpeT>r z{3D3B!q=i{o+ff308r3+OEoRP`W!cj0o7$yS5|AKUQ@{etUFXhaSLtaCxw4UHYCHR z#gFs&g%yF+jf*Dpz0)o$=0m7AfT(hk55?}oF*`paC=*GY>yMU485uR_J*RRvtS+St;le0fK(ypy&fjZ!xm{Ws*P9UWfKenBl3$SAeSS`BbJC1^u_rn?wfpMJ)t0~+sb^q1 zk)55CovN6%-b|Y`*=QSRf2Z}Aa_W}OwCfZ`<|a#rBo?~>LV}o;-X%z4C}jo-BZm$# znlw5|hOJ}fePQhQYfb62L@S@fy3o@xbCmdXbenof>{-2eTD6(04}$Pm5A~QqjRcGag7Rsx!LiJK*iQ*H+4VU# zJucmS(v*~3wZ>R*hJ0Ulpw{ud@Z*qvCZiY<y$S%a{$ zF@d{y#vQcHTQHJt>55FXxi7<2f<@&VO~Z6mCsU7!5j3qfuL=Hz?#2e7GUaN8k(o=l zUlquuN3r&Te2@}`oWvOX@#_HgCR@a(KcD!h0#hUT@hr*ye96is+!k5&x{r#-);Pvq zOqOA3)K^CS^!#S5gxF$|#mTL;e;1<`fThi380Ht#EuE-jo-tpB#k)RL{|!OJqFB4! zvQbP1H`e(tTcqkY>S8C5l!m?oHT2sSA7Pa8GQ3Bx461fzUCP@w;oGjV=*xB2XbR^R z%JGNNdX%EdJE+(>rq`vb<2z`*r3vh zA`j@~MHlev#*=uB#QC`?Bbeq0+?xBYK1D3{%IRz-ra0ON_Y~K&oZL&-iDZl-x8y9J#abX+0 zsY2yUgnVN$mD|Y!|Mg1d0+^dqwW}2>@Q-!8soKmjF5l`w2M5OT#fT1o>RL*Nz#$_g zxIXNhv9K4+?ta}WehmvqCWrxO_dV)dcqYblTLyUL=ZLTd1KflA`~ZHc;z^9r5)7Vs zLw?2ll{%kd=OxDU zenq2$X5&Hxj*=5)-kgs@P_Z)K|D!P=s@arlPo*D+HEr76MUJ=GaZSkc_85ysTnmdA z0g{aEQ08zo^R0yyh9m#gcm}sgD}&**Q7*??lbZi8S`K)QY=# zsguR)Hd05G;PQp0v8^KvMg>U7DE8f`xB5NFGG7*aYVUb2O%S$HaTI$0`WI)RtJ!D% z@%d`pHo(7GfhA)BAj+q_8moK@6uHKkj0Na{vEM-G*g9fn;U60BvF6y-$oE<0=n)bv z+edy3b_ZL=l~C={K`~Gn{Zpm`@CQG0+gIxDskw;Jkw9Gf(l}fJIgl-V z9`)!Ej+jzLj;<&>?X!U3|WJ|4QCsyPB=uNP*a5f)kaaM5gBSc@Ara*R{NIs zR{;*v;nlHAr|+WR(VZXtB{bl<iO+M^zVmc8hzqEjmm6$2# z9AIzvw)OL1@n3JOTMnV=m~!f;lALXfdpfCdr31teeRg@?kbvN9pkWO|bPMDFapAqi zr?`KA?UtBeoYWkNS(f)Lvy86Y2BB0vu=zPEhpa8MmTR962O#>l1Cj(E4JRSQGAQSf z9ep5gF{=xcFx$_gmt6vuT>d#zF&x&V!~=WM0iNh&c3Ox+upy*bnRX^{+MbW<{gFiC zzy)&&l$kC0g=E5LRQ2TBT27YCOhEjXq8GUA+Y_?UF6LW2N*SYtO7Ci>lCP<)wz5e9 zx+Ms2`qw`j3^?Zmv_7wLk%banVGrb+W8C96_$1h@lVC$aJW8EF;b^~u{rIKYw=a5~ zQPmKJ?})jVltf<+lwJKeBvKIQ9`V0zbwY!W1v(S(z~S@E@o_Huv~h+G!Yw98uB$+l zJs5$`Q$w=7OY@<7P=&MPED`6otq{>1GQE?!nw2^;75BB^*&j!iAJbjz3uq4Mt#ZtO zMS6OIW-MTdi7?ebut*w@^`cxQdm|xUevJmyX{Zv6N}@iZzc<33F#S|>y3B+!2T3N~JQxwjM|h zxzwTUHx;%o^_~M&|Iw-YOpI{f8=_v6DzZ|JxJ%i)X{U>t^^`M(TC9sUUck5HkAB_r z%=83q6bpaS1E1e9lH2q@BrQohd`%BG7|9N^bV}sQV7@#56_?@!86?n+<9hj~z7B1V z_Zf4DJFX77`bK_XB~!g*(`GH%Lpofv`&Y0LspU!A6B`Y%nGIf5C;4IlMN-#gepjFi z>%TwgG6IE>GyYNA2rl4svB$h<;081t0pOM?3DS=FX{?mx6EKlK_LEfF zLnhJ2P~7EMy!>*!<)oaYs@=ho4&ggx^?+~GXJ+yK!H$X*#sY0L`^u8FX$>eDZkCa4 zd8BOV$H5Oa`+!5%CbufPHvO;fgMH$J-%rPU1Z$Pl?DhE!l9Q>R07+-||8^bbiE07xwp5Ry{7=9)J}GO@zpaG2X5yUl~QmP653G|Dwr`Aqiz> zx%OWD!MWybQ!{Hj@wCzTe8|H$oRW$&LvD-*Vt|HsBKx87|Bi8!aE1bie6D7oLhMjF zL5%D;OBpfd*ho73A8+f8FI6yO0g%XEwpb2nrw#n{tIUzU-cbx6j<(B7QsGce_N#iW zR&eI$L*0RiF~NavyQ9BYLo$Nnc(5tDmxIK2TGSd>3c1(S+Bkc*Q%{m@Vwfwl3P`Eq zibLTtZ7H4MU$@_JnYQCVWB;w<`a;9k@>@d|%8VuzLwc1mh`{|9J{i-(?2b2uqBTp; z+l-)Gmo-2(YG=|$(r$WqA-ATDc9l(+za7+k%K>}C_MIGvGKvx0 zhuvcrckk~q9z#V`5oR@${8KnZ)vefEBI7n4`8~nv>}|SX0vEd(3ZRs=pT!Z!#jz=?d~t*Ish330BE=UG z6*5BR7R|4gbSNS1Z*6g-a0;DLfpniK;oMM7{>s*pU_mUexv3KVObt42q@E18@iU@i zjPQki%VAI9ZT%vPu2`~I5oYPaR`}j!jrq1P{hGE)fuDT7B~Wp!qN9s4>;}u2c0g7^ z&-l!|+f0QNUaqosB*JEXxgRE(R7R2y78>OMy<~)# zyU5V6Kg1e=VJmOPsh;jIaIsvxu}^EDo!eD?#}~Q|XbO)=H?ZtTNvm7|KP=D@{0FXi zceNws8?rv=cqhl3y0b6$TsQ@A5GIULY^-i%k>f0-*WkL2zj)nP%NqWuZ2f_WEti2U z2eG2>siu#M*^MAZK1zxd9&b>_+VzkI_oKz#(0h_@QK+Hu{5EzsNvwaLG{-Z^NEQ@HrHqFYzju zs&bzTP1fN{InrG?`yh#Qj^6t_M$xkXiUpJXJoH+Dm=u_lG$({CET<+`gr@>kLD0qg z@ZuVHp-$$}WFrT*S^l}hbO?UkhlMwSE?~AHn*aVtMbg@VJl)`#@7ojc;RW=`Hh1U( zUtz(+s0$CC_|F=m#vk{vscL4m>A;#zznTGOm44tC86zOmt*4Dh`7jq#Ec01;`35zC zsS?~s$?s!(>fZ)LQV!$skHSdhBaPI5{dsmDt`DYsxiybSlPxmEZ`BrZQ_3Pcihx<) z{yCI*076PjJSGmaF+GlJ21T6yBOt1N8ltq)QB)owMGT11AYw9AHBECHL6Zcrs_R$n zHf;(D^F)~-t`9rORU3qlT9}$V=+1F)l9JtdDVZu?EGr|g{2PD^PzW@@kww|$4%6go zHxqCGg#B@J@cXuN{Kyw{_-;J7tOU7oi`0x1h+01ml}ji25-xk&@{!aD^*XS2vc?lo#)nY17fP_uZYO3tAL_~)3T(2us zv5!>)=l1Nuu1<#T(47$HU#iOsuTpy@rUIb)ZhJ0{aj@NkL`g>qXP)1+K29+9SpwyI z$5$KPwQ$9)9@|>bLbnnCqsijNF=OC9elYTg!^h;e%UK)e;g|kuWUKEWd7u(1a;c0w z(~M5&bR4Jw|6bjjvPEY}#QDg~sp|j%w`CU{+Gx*&=>eC2L?Y$fYkz%=TqZLANshU(I_N0c>MSq}(?u z_@WFKl6Q!+_C*;RjWY+LR>qxmfWgT*86KZl0Xq5;S;=nyQp_{%rBXMxffSUx^ddkd z39l?wE#o#_W_l@Wa)tmRvrmM|@Qu7rI6()CyT%Rze$HHb&Y3`g4A042>&&vT_hr{? zNO321E1P{?gj70iBR38|O~z~5>6TE()t*6UeV^iAGJ=T7Og+bg2JHN6>aI z4{<7BZi4Xdlrr-HvBNWni2=w@AwpSB=-x;i{O7+myZ3>vW3so}eM{L&y;q~-KO!~A zvW;hQ8dn_0g+YD+!Y&0bhY|0%5G`hY922|pcvr>OGVHOcpc-+mhXIw3r(FMH?&Q`{ z;j8s94W?DO^ipgM+=b|4Vvf;AH2F6s*7V7(E~mZlTv);Gsg{TSN43@-#rsI|jhE`J zG3kBYGZ)FOsNRWz#h1mQ>-CfkAni1=B-X- zn|l3-pB9c)HRUrh>TbDADl?@*s)OH!r`SKjZJfw#t0Zr#`SgR2c}_{#EWl2OV9Lz6FwG6`mA(^D4z zExTFWEmhOW2J$fky|k9|90izUEKz)~Gn2sYLY+K)R6@eCU3L#1Pi(8w{iE8o@mgMv zT#Eqsa!olSU8I;cV7_J#9)G{qtB4!);g_Lt<+=}}Qu=-Ei$%@)tODuI<0cv!A!~{! z3}sl`M9#$>KYQ+236?|vIX5#wRWLFOL5hjn@bs5_B{gYrxA;{~e8J}KZ1_+7Hj4>kF!}*z-2a+xK_frP(7!)(zBjSb6z|N{73A7ze$Ob5q)*%D5U0<(GbR2) zwk(~g0sW&wE!G8?kzN;-Bb(4^)|w30z^t^t37!m&8xn}d@plrJUrO~Cb!HxBq2sk{QTFjZj>yG?X%;E(H%v-0 z825I*;!2T#SJW{ein1#Fb5?JuG}zcy({|X|G2|f49F#il-f$))!Hq~qI(8N1MO$MaA?6auy#DHeQ9w~Hgp zx*CU%FT8lL{ZsN#^_7Q|N=7IKVv!@JYhcC}D!&27eo@0LkyI@)JDp4@oPd=Fh-+Lp!~UpQV96}*yte+G;7 zwwu>uYf&W$q7l49Yf%dhiLp(`ESaqDHkzC0RC#x@{PJS_yvOun@T;AA9a{I1Ae+K3 zM2rtW2j`TLQ2!GY5f3vM_bay`+(uNH_QZ-OzV}vU;QMBI)bm#lF#D^@*ZIyf(laxj zO#JB_X5JQw!9uqcloxVmgGQO%%I6IpbNj@YA{T=ld&UTkPKZ%o6;8rmDAVL~o(4~2 z(%DR4_ILvnA?AV;f2=*!DZg#_z40H^5AE=eR7ckc2GP?6w{P z4c&|M_&zMNN`1$_+!Y zW672peBR-M`Z$X_yE_Ars&vbE#X;4X+MCZ%VAXHv|0K1osNG%%%*zd;!^Xi6i9Ope z4W)o?K#{!PlihhPH!?q^n@G6_CQ?}xgH->8#)Mf0`HA^&`tYQQldpJ=eZeRn?}qo9*haOZtpsjKQ`u>6R0`@@Paaef|?| z-hV~+;aE2M3+QrmA?Z%4zv+|xr4$50g^Q{hi+=-yUx_BDRNpIw9p=(_(eU-nsQ_I@ z5}1nEwCnfxBVo>Lox7=jnDt62Lhc_qC9>?g>$0AnorBf9#v=P~D$i9M2PP(?t>YjIYq% z>Xz_p-n35$;mT+hfsl7#7G<8_em$M1N1qGOBKUDfC~O?*m@OM%EV@q)bo<-9{#2~? zt=-aBo2SQLW|<)PtG;0) z+Q%L&A1H?D5=^v*Z#uNr>(c++Xx_A5>99nZj_>rW2jeW43e#)^E?{=d*PAH8(`JLB z!$W+=8KGT(iSx8w20nioCyy>=ViuW1Lh4UW5WYRD*kTDyKLo*Fm$I-jE0wXu{i{GS z*zr@pt|sE>tI}KCOK$^KqCeJN@qVtlAV-2JTS}F8+Bxb;FSOHT zE(NA!^E!0AoI`rYK;jNr*6Y*_Xr` z$Li1)Ny>pdrxlw%9X|>V%ct-6BA)rHnH$g&<+{|xR4o+LyR7lTF!}2BKSdFhEX_Yp zL%cNfg%fRKEE`gUL#{Z`SXWl9^KZUjJi9Y)2=%4IRQ}x`H?ips@R_smDqwPSg30vm zkd{B~H>8*!`A(aUe0H+~FdwCx(3tj%VAtSzGFoFiNU=1m$2|f0DqzNfvY;W?k$kxS zV2mq!*vj-<^YaYhRyRGcCS`qJGs3O-La^H}^)uUeQX64UY=?~Z50vc6zcd=&^v#k^ z2Qo#vZ5A!pxJ)_Iw|I4Di5xU5AIa5E1PYinbgqWStkVT`JuQ#=H~ohPF3Kqvocls# z5Q-EKN3kMnP7$thnzN~GEcuvDRPTh=+D;mmz6f1UNb0~mn8BlD#`Nzlm3a8{91~kG zL1>`q4WI^q_yf=~sO5N%+eF{MW#Rj>1X(B>2#G5Ju{ME!&K*E-+D$y-w}rH@-*L9%{p7-*%! zUSD;=eMT@Yu-B2rC-e^ZLxqUz?SU;+CevnQ`ESr7rHaJ&h)+9$$TdkKu((N+$aN0Z z$?mCr@siIp46IC@#^uwVNSs>za_7h!t-6n)kXB({a9d&PUDg0| zjAlI+@^+XzyRK^df6WF+t?U1-xIwh;l=z5_xYbFjH4-fq?faMnmE|N#HofOCq{(Rm zuk)CPYO#LzO7wxE`h&Fn`WYm8i>7gCJirF;drZm|KI<)gjT%ap`Ak3e{w5HuKRK~@ z70lc|U$-~yeQ5b4WcQ|o>Sh6pvSAhGQw+?`%I7W|pGP#EnNSf_d}KkF$m7!AFUHaM z#cR8YcKU90M{LiTZ(_Y+H`9?Wj%U1AH!;xkNRqmxTLm~esw4XVN3;-NgziU2zDg=c zl*M6log*gRQkqc$`G8O3a47nyW}yg--oZ}f6b~HUUH$`(XUkxbvl5}# zqIO^&!ws!14UkJ*lN8U-x%y-(kaV-o_bhw5*40PH(9*utLOW-%aGS4P*O%;EL=zP- z{T@qPXh8%vu{;_v7DV@y;m#y)MwJVwFdOc$Y{y5C2t@7etn zD<^Bke7V2MdLuI=dt1Hhyc4?P_s9P#Z>dx1mzfk6>8GgvPRhj);^lUu%B17w3{hp`pDs=$omF(>$4< z3*!kf2(Zs}ia=s{@gw`B6=LQHC2|#d!AC=Re?B_Z1|TnM28;%a_+z2DJ7zAeT#>>A z0g;_adG?jP6yqrK|N5bEb;bG+jbXyH(UVa{!>onIGQH&B7gFm_F)IOz7uE$w@1=O* zSFaKD^S0lkf3I(TeLT`Jc$`cIJW!gG*(m{p%waJdYaKcI$mqql=kd3CQ547Q7g(B$ z?dt0W+ZQp z9<^E}>lbOb6za?IcO)c~F8(|`9d8NHXt)cc`zkt-Fb)$-YIkObyCNurAB&^5&#i%2 z*;|6|s1Sy`DI6;hV`8WthpYfXQ!BUK#&Ox7u)E$o>6e;|nSoKI^F(+Dk@#}YuOE!WQ@rpbTf^&P8LKl5_#IVn;N<2SOTd{^MU@yeCs(jEuiLU@eT(k z=)%ZblU)UwCg=i&xjj)-6YsZAy;1k1gXOPsgFBDL-c$7L!z}D`#4h0VUGw zQ)3>djmFE38B3TVu=G^YGAtrys}kg>zCm!sG$0Z2o`+~v%Y_`_&*42Yy83~cl`j0Y z+xUg-A`=VnU!ii=s=#?Ss7t_RnGenos|%L0eLMSW>*J_XLCExZ5<0HJOPGKpR3AWA z#5IL+-IxmBSY8D|Enu#|^*8Sd2js{3x!~1nYqwI<|4W3=3PLs7aWFIe!#n<3~f3bz-((MyN8o*`$l}f0` zZ?c<{`tAhLE2x-$6S=c~H`OvT;D-$(Jl_1kFUZEWe3wpxG!tN_Q-iOSXD!wmv_GS?{n*O#I4nU>61YEv zh4wx^!ZMDU?l39Nx;kNTv@=g}BXAKT7qi=f?Y-f2s|=R<)u42Vdkpm2>+vK(4Q9)7 znv`902DI+`zTNrSF2Z&JQYFYS`y)dyEaw#lPq}4s9yfcj)-@4YePZ$p?mc2kXzBBJ zP9t#KagEgF`--zTwW9(nadnHK1UOlgiNs=NupzFe85%1IFLGM@9$mxGD%B>lKXGZ# zw(kJMrgMau{^F6sp9zcCew})5FF`61RK{$fEc+9HZ3zHKHTNDD&GF; zXt^H@3%-s$k<|$H$O=tan_H3e>3<)y{8Wkojy#XY5<2h3W%Q18?&gi!+k9}N*uhGO z{Nmm3xGC1%@y}FjLhxMy(rVKuiqh*8eHrn^alZ5N3C5eQsJDO7KGy@T9Y=a=PG|KU z-9axhP-${!h+qx7z7veX{w5DlPUFYksKry=Y_SsknyCJHvKWqvX{<@ALL=^~0^-5E zvF%ve-1_TXx#Fl#7@xd4T(u^5D!cllJquH>&v6?T9n9-8dK=gJC1hPQE!5lW5Ag#) z_ai2Mo5l}Jj<>`sY#OY8N#QjEz+5FL!$Ijl6nR@fynm%5{Mq1XN_5+a_>uO9xyRo@ zt^lBv3M@^0ln$84a9wq`Ci+p(8H?xVFWwJ*&nC@en_xM?zNOF0R+b#+bEw@;)ICy{ zr(gFHEH`#Dy!~Gg82#>s*2WX=tnV++Qu)eWvq3a0P7BQ1V7PJnS@!xJ=B--`RGXk7 zn!|SxdT)6h9+G_Vw3hy|=9Zkevq&$JVcMdborYoj*}mNe;~8;D=3$8*F5hz!vltiU$m2 zP)v2|gM}gQ?rh%7R%)^|B>tfuHu~Ch5XU!P!V;wSB?hmT$-e&Q5?0iGfE+~$X0e1V zz!Bkk4Yxf>D6DfAo>p52^hRwGIHulGdgxAzx>1oEv)W5UVyg^D@;^o2r zqv$OB;rs(Qu1(i;*Kpc&*Epw-!}Qej)Ujz}a+_qNXom*qyDnoZlfLDk)le=cL1({GxeZnMzt%gHRQ&)bG$A_x4Jke)Fr zrVsrz0qi5ma($WiCg{M7_kU!;QUbk%I~6SQUF+pJv z{*plFzyIn^b_RrAu7B*4S-7WDnWcb|M#Mi>*dhPfG2p5nD@LyfE-lxS{3a{HT;HMBV zA~a^N=7CzJab5WM4|~$nZ&$ojmzdGYEQZ@mD;TnazlF7d7 zB~xF=(RzBY?;eoI9$h7Mwe0VsliPQu-8)cjikWL*B~+>}wxP>3baJ?Q~3` zv^JO)BXqrHfGJ=RP@#$KB^F{1}@A#OhoD&_d5yqkii}_fVIP zD;?8w!b~TH73Z=F`Hx{^0%*Pd3H4Z9+r$=hHI}e+BhQMALmd6`cs20 z#fPjm4t8U8`Lx+mrOPA$jfgxEM#NfFop}9Joz&`Jc3QZ!btqfNdAf9)Gw0>ed)J0+ z-mHYS*>dfYUeDr$Gi?b_1E}Hm)LKP2(WM|-U(2>1WW*ry?h{b`I50bB4pr_E4%2#K zgc%{hu*q5V0@pNtv4VNjxN)-jm*gI%+RANnbHt)$EIsd{P!& z)lsJ4=23Ja!c;2-FH_^&Cn58rL5CgJgQR$Wr2vC3 zZL5Sjcvm3KeYJ))7B~(dvarH0mJlt@Jd|b^E}{=9wV)$+G2QonWO3fFZIkiH_H@Hu ztGpu0J2@APIW$MIAb0?a|13pU3w0WX+o(O|^7g3FeWTOGZP-L3HsznAxWuPXFpJPD&r{c0T9YpQ zKx1%i4S51{G|xp;rEM6P6Q_`>7R5@SbNMS#10)>1`w>;(qp+-NU&V=y;5LnE4R)We(8ajw4U^ft5-^3iBFL9?b#8gmH=x8=v$^bDHE0H)(X>UkKp|3V#l!QQ zn$*!AWahteTqB#B6wT;w^__Rt$C_fnk%xMZ8gM(Mp^)6PmWuCFu7uU+A4%V0+uUlI z4lA)18l3|+v9=PYoswxE7H5pG16qRYT%a{59p?b7Abh8XxLIlnJ`LIT8^tRkix`{s#QpPQE0Z$Q4#~L$RKZZ$?-Q| znckZ^3B@7*m`=FgvrzXsadBFV>-TaqaPJ}f*H6Q85zQJ^Ooa1??^g;5Ch4H*y@(mf z8ktObyTnwq#`UVU#L7AOibNdV`rkOnw(J~)BNhqO8m=^O9^h$rKV)zjN0=E3aA7Z` z@rbzo{KS#SwUFv{!n|Rw*)&I?1B0YGeQK{9mpO8X9S$16 zuV%KII6srO@N}cG$D>9?I7+25_+I`EZuE8cipuG)L3;`%Ng;E=i+Nogs($&=erhP6 z1Db)37n4~qbb@p7O~=`70aPN|HPC4sQ|1xlk3iitpEPemPF^diziWBg#rnjK>D>H@}MH~&SEN2Bq*+! zjA^6)=%Snxdzhg7>ErWEx6i48hJWYY$p2ZVB=@Lr7X)?EPBiSm(vSw|r9>*c zX3@#XV54acDDp}!Dy$%7{d4gse7xwde6##rka6f2AtfOLJ{hYlU8(`*NW zCqi#kUq}F^5+2mbdckeXw3r1%b6ra@lyz_l$ZKcmK^US1-U{{IIP`51 z*GiJKc16j8u$T+T2LmJ>GL@`L{9Uz1y)9mf?uZStVd8=o6P*~O$DvPu^$O;^g5FKnfS*Ib>t*x;1Jvxk_i{UoBV%VGh#%-v7-CSMe^!jDQ4C_IzMhaY8ZsC;dj9EC+E{v#+PC;!uM9534X6F9@ceYzKbih%&hL{B6*nRL z9Hekiut%LA7jFxZn?6b_u2DS4WPmZdg6)Stmdck7RmP@o%|R6o5ta;c^6 zi2ah^xIy;`I{uR#P~r)vg{A5%mzICK z`<5$hDVfVke|W8xHR+TD8wW;9uBAO!a{lH?bc&m9Gf$HfOu7ZaIYfTO$4TWT zibPhZvYjh&!$w8z*;9P5t_L}nI$sOGwK~nK4*pppQ_8-A4yZ!C#36aPr}v2=|JWnE z!RU?%oIDe!!=&l(UJ9tUy?I&qfDhV%MDN$@7{z81GsJ))BVJJi+Nb^#lOFG}cJcF0 zIb1y434MG9g&FOtXa|^?F5jOw+%N@|zCPpj1QKO=wDiRf9N0AueL}?b=W_%XzB$)5 zmf#J}69?G3??;-b0p^f@!G80pMAy@<^4z_JOoXniX1dYU5w$EF$9F;zvlyfVPhkdQ z#CmfVA~-BCsx%_i?^6jRk}bZ`gC>;p{`Q58h6yP?&=(z%UnVX=+;AsXxM7&@KNVyX zO%FL_yQj@syXJ#AD$2$BP3NwXg_yZ>$J83q+~qD+tn+M4#(?>*04LB{berRE)`@JF zC#%Ax9^GRSz5u40)dB$avD|o@PgqN8mo+cQRiEZx3zHIn=f>YeH%}K!ntm~tLTBX6 z;)A80gM{jgLEnGtWZx1AaqbRq zVeyAG$8Yf_RcFhu-Z*xZs1ZofqA_)xp(VGG4os}iQ+fR)UPzS{x{X{4-m}VciL!}7 zg{tCBM5rE3iz z9yt9+3BvD(&4=uz(-lijanypjzK{1;jX=<0ZOmQnsT<;_a|N_2m>|8M7%{5MvQ3>y zAoj$SgXN&HrFfKW zpI$~dNA1DT+uc_Y%)S*9aL&Gn!jhEXCL^MfWpB-Z12}6RWJ>k5Y`%<zMbZQJ*KAGvAxX6d^J^EKHrPpTaefu#XP_#w2Y@>oz2WaKcSA$Q z*sI8KGHLl|!UH>5oq0FCw|IT?7a-E+;d$TGi~QKpx~!&LlL=`J%Y`%%E%F^eby@x& zMy3NlcpIjOSEs{!Z!J0OIXh18d%Da>Aq_R8Y@{+{53JJIG*Hq=$XkQzF?OMU=?p? z?~H&1)p)Xz`odxH6*NZ;f_HT{cV3`oVwMMw z`-{1?Pq+KNBCPj9*KmT2qK>=NPItiYj8=w3bmWrjd_U(Tkjwh)q*C{h z#DRP$x}Lv23m*$9iX$55qTg9=^|DHxFJ8%~SuN@o9;E2Eh6vTjO>wlbwxc&!d+i~l zdjI>Anfp810+m8ok-^fMc;`#*=C4*CrXG(qO+Rr*Nscm59}i$`QH4|y{i(UH8N6!g z%qYAfd9ix+_a%;5ydfKW_v@(&c{8)^j`t-D8G74Q^8W3Le=1yt%loWmEO2yG?y+e; zSomXkh$9&#q=J#Sr4C@xG#2aR_}4Afw^tVZr8bnQFPbdyn|P5f7uN8*S#vaxfuci8 zQ@&`~yfiEtGbO zbWR;M_w$FhX8ab#nFFtrkM-b9S3}Z(*%$$FA~{ZP=QQ9(8dZO``~2j8K~2%Cj~uTg zcFXpvBy_;4y5NH~BLzfq#B$4)cU!a!Obg@4W5{^v<<3all$W9#6Oo!4i3BUwNQzpC z;0v1xv(&J>)@&fDKR2+?J{aj)E;;GLcU%`BQY`H(uprj6y#5PNP#Nd^N*xlS+XYjI z^Lub6z#ynEmXk6X-yY*{-a@K?KCnDIEDGn(L^1@M!dZT>%d;pnN~;}xy})7z$I0s5 z5!f&OJe(g?(hsV+>rH4x{83S~4LKWcL>O2)`JbhU){8Eoqaa}T)yYN^vY&d&;F>A%QucC4F zHU+Qm0#FhEi7aV>H%>8)Y{`@4X2*x9Hb4e(&k7eyk+la=!R@Ml_aoyD2Wnu2Io&CN zNU#DTf6u&O#~kO1eCJtT1Wccc1h_NBNqtUnx4C=gx8a@dG;D)LqvUc4{FgeV^rTQ7 zcgJ@%m<6+z0Y|WZ$6z_(0MaY;@^U#xfSWqFIHuyZ1%x;jityhSs+k2{%(z&P*p*=J z&!>rQFGc_RRE^E=iwQ5GulD2i{7UuYif}uvEX#E|b~QMmA#;(#@R~P0^LyU9K}vEf z_1p;K60hH0H_nM==_<--itn>6&pMCxPl_xGm7MGPrE*+O?`Ps_DKwM}p!H;YfbQDh zas+(5Un>tG-YG*?gXdmA3c23ND5izbTB)x%-%2WY?*T)Sk^n1v;=C6c%U|y-!K{zY z^l!S=X;?P?iH5owN9N+$RM;X!wo!YJi>;*P@}Y3pp8JWm5#pKa@5anneu?fV@3OC- zO4&4g4yJOTS$w>pCeZGdt3*w6Ha;`>{MX>e=^-;aNb<(~=*{KcTIa@TyLw-`kmQI% zdDyq=sFWwO*k7epJDF<~GztDp*Jo{w5sbEHzINb#J0+sqF|gusxMaBy=C#{1t$aB7 zwM*7aOeWN4uOLeK=a@(9PI>#~qFd;XyA0hu#yo&#t3w~S`f%C!`h-NgSBtC;7cU~e zGZ6Qq9%g0Tn6y&WW=7l6RBw)cd|Jj|`h8X}u;rB2h>r2ugxmP|7crNvSLmVE6K{jf zdN?STY#&LE>Ogg%(MQOYXDY=#8lWSQ-^g3TTXl%-IZqbFK^Fq{R8(Tmj0HZ^tW_GbJGQFHyAmhhi

9%Fdg8IaE;?YjB0B=A>^BlaNd8OR%toCy8TbB7VfEG7ue+p!rq(9qZuS?@io z72KkfaGi0NKONAP;$XSVE!L!7K6}NbZcO5skj}KPe<4o_jJ$VKXpAC?|QN5?YRfV9ZP1FQ|5U3 z6xZCCj+QvAxZTM6T*TU=blSaLuN{7HYBYnXgO)b@hYTwCyz!3q$^hwpATDNjC_SO- zNPiAaVZmZ*X*lV)Q{Lig?#QYG;ue&h6d=jILo^1*k53{?zGLX%6%n=c_t$ym=pmUh z{hJ-ie0r)4S`n2|RcZu%=ZbnqH9ih|lAHsox-Vfl{mq2FAG{@k0W7&+Z5$+*r-;GM zHH3Q(R{r!otg%L@qQXfsOqWs2j*|3eyKau_T+tOOBd2_B-9W5W3117(3zJ14Xx=)HApA?5L@9q zGmNYpW%XN-uAE#cUGlHEJt(59MDCiphrnivs@|vmUKBJjvFWU}EDlFF^XlVfltufY&0dGr2RhWK*T#V;3tx#^DD6@c{40sAo}t2IraMF z?=qAmZs<3a!q)-giKdMi` z{W@5$-ZnJq0BmNCX*`3d^cMaq>wmIuu8Z$KMRW~9IacE3 zZ}73^{nJBzG-T7aQ1{I>LCabLK*4gHANC{310furq=y&+ENr@&tCr!K!OVn;!|x1X z*jZ1nBc{8BDa~}SaVPPGqiWLh*+zUky~@ZYbwx7u;S$jgspI(b z%Kr+KiTnEYKB0k$PD_!txjd5&gQjv^@s||hFJ3`^^YYL6@Mj9Qd*3Kr@d>5Btd;F8 z90l`trDU5L4GiCz5?AA2`&(ub(=91;$iyWV;yicX>b64Uq7q6sckHBf$%F*v@H@)y*wtEa1;A~rIF*wVDUYpGAK`m-Im^z0YLDX5U zrtL;@gfN`nmZ-Hd%g)MxLyyX{RQg|2vf+p?a)=vf3h_Yh&%jUA+5Gh6!@<_wvGvEs z=M0B8CM6GPg%W+(rWfo%VG>IKpZs2~@h*iNMt3|4%{x^O2E@{ESg)?01A43LuHj1YK}E#jte}uidVobWxL@R209~!z?ucDyXzNx$Iv<0LK*1{r4KmUJ zC+jEB#%i9QtiuCd+kAX2<6O@2b2^3%^CH<*MUC1!z=-F`uc*t5Bl zv=nylVZDQ+{G@cMBMWGe_rt+qCgzl7cRySh<)2y&=`%=z4P>Noom?6?o zlt`nZXi8^n(~aA^ZKnPv4J^b{&4bN{_@%rUnzl&rV`Pzr(4n9*{9`U*P-s_u;`ruL#MKXW?)BpY?c5KdDXmyQmeWOPlUxo6g! zj(5S9#GKa=wMM}2dXL-1Cov44(Mjp+cMtRZx*tk6NX5C}k%yftz`AZ zaJxF4lHm<}Ne;J#kRMc9EY`mHnAd6xDfrO5ZRuKOj1E1yyvy{2?f zkA@yjI`~lcTD?P~J%a??0}*9 zFHMSVZ&pSCOHFPVdM^@+DI^nK7quktKakGK?WaC(9N+j6rta_#I}Dv*WHA`pi)Fp@ zIkTG6|1LO8w~-)#yX$qen_LfVAoQ#_10}(1hbmn+3F)}!g59STV`a4a{@6FVojm&+ zC~i;M?uYksXvqm$1v{S>i^1G3y2c!H^q-NvI!+ zP9B`eLb#N|e@>|)Z4T?*XLy1d3`zN(A(oV}QONnANYlUjTw(ljRa^J|pD$l?yOD7E zcDqCP(!g>EmqNopFS!PBeG3^MMtYwW&8ksyhw|Q;oC_C%o*HJB|9RC2aLc@KO~}dR zqkKj>_9B)A*4um~M_WV5=W|Kb6Ld=IQp`@Me)>1AS8$CB0~(AX)Ni_4UZuRs78LSQ z25R|guscd)U50i&nTmXp^fNz;Fn5OqE?y5#z;J+oF|tmiB;*5!r(ie3WQGlpnI)-6_0->mrKi37czQzh1ffKmhMnRH)>5|^Kr zK$9q!*oU^QLdxnfMX7yA3bd*y&;5N(U+>oja&}kmX~v-Q$q-M%a|M|n!339SttTC= zZX~*sVyk9~7^eeEmw+hdnwMcvxv4oncAw~JmT4xUc#I@a?}++c~?X_;vPzHy16ut`i@_QV&)P(?P1r5*!|rc zM5zc_d1ld&H#wI_vpC*vI5DKN2qkV5xSn2E6Pz*2;hTd`J2k9ri`gq2AlD(&qTxI@ zY5$ls&uKC9EZ;VNppm>MepUC;2_&G2^00cbLULg?M8g}7K5qM*MyO=sa=k?scy)1E zu9TCChYPe44t#1c`B@z9B6hX>ZZ{`xO%wI}xD^1viU+Y(mJHvt<%zGx?c%2C^%1pf zVNTr*GD0h2%xY5bR97@&Az@rwnJn>Pv(});tW5DsrSMyx51-1(?2&3JU!}OzsNhS$xMwE@KJAiWmjKsA z^v~p$_|f8sL=A z|H*ZrhI2J1@GaBPOo_fdu_{{Cjrm!BpC@e1Z>BhG9~=1=MA~XKrR#nSGWq4-impok zrtOsA1lTE3I3iYpdo%pv@Uhq<`c7f0`o{?`b5IHoMz`w*WEqukt1=N-OAT@%Wfcw1 zYpv&jooQG3?{)tdjI$`D1qvZ&n}N#K)8Ub?F>#8pk8i79_#v`gHoklw49(qeMK&4r zkb&_vi`FK7Os)?x*5yr&)o!>~^i_E3Z)o&u*Q;^tgPrSF<9NhU<;1a+3*^(MIYR4F z4AZUmERie7P#u_42{o$0unYA4M+tQeN1NBH$gl%@;6q;TNlln23X0-B{i5_@3MkW_ zjRAPSfPls~8hA>^*~C6!y#<`&uG?9?^zmmvA1cFU&e@Sc)NFyMiK>VmqGIavrpph^ znV0EOPXcbMj(u9x#hqj?ef<|U?A)sMZgJrM=_WsPS;z`|l72O;^?7^r;dBW^1k6yM z4S(|1hd9+pnBXV*^?B`AgvB8=XVQ<)U;OrqXWhUpGK=O^b3xW-)rfU}aMndX{iV2l zDfdv=Ebok2Bc--CRGV1q-&vVMmxIb}jg~oxnc0?~URAJL{`Fn*Fo`s0db3FCpQZnI zVsf%tfFFyMFjs#fvz3okWsxNF9>?)QkvOd*KkyQ*=QQg=caIzDZFQ)0{s??gK{*PJ z>pfpTXEK$a^h?0F0B!D+ZeyLu5owQqyYXrHw6kChL?li}V`M z3x|aJ0;9Y2qzEsEBv%PYkrW5NG$jt8lxFGWxVM!y&Qw}9o{Hj_8daKty=c+G4)V3Z zpX2;>j!9Oewvto^@ja54E_}gc5sWc7UyM@ZOhqY+8H#hgA*gGi0W^$hN1-_mZ#72a z+e&Dkr~G)FjM!)L*qu%5&_oj1+KY(4csI!JdikX!qNFs4YA+jS-0InVn!k$j&;cEf zr9cU({$`gydYvj8p81U}g>+I_rHa`;`NM%p2K65bnbN)gXfj{NMH1z++rGETE17m{ zpia^2>@JB2_!yR$ja6u5%Y-P8Kn|MjKTBg==5h_*UVzS+fhnn5s!ptJIGrgm?7|&S zo!qqU(ehdV0+Q@1%1(hB9YjY*R5k;~E&yG9&3i!Rp(M}~GfhS{aitSaIv*cMzwFBY`F%*T4D7bV+KIxGXVXI0e-n|;<(e57!coKmobo_ty&*7|%2><$; zATmRx<@Dks>uYo?a{RDl{ccaCa~?aYdxKqymq{(ugvXoickh=%FJ+b+2HnmlJdazf z{O06-+=nDCH4K#YcWLrFG#gT?JJXM1ARDr$zd0ktwZYtC2?-xUf4l_OQ$rgs0Jpt# zrJ`&xkZ*4=@>{;yLECTRP;Epv(jT5)nu;X1_Z+q{>}4#IRnK`4U_6+LEiOV!#wY@H z8Eo`x=M`v>fd_88-jjYGe71QchPFV6gUof+T*f5+f8MHwvE^b-cpqzqW^??sC=~5T zmG0HCUTi@jPXs0xt#E1mvveMpkfKarV$8=1WJoF5)b-V#V@_TJO^GhUGO} zXBo$lri*B@q3I}ZneAS}uR_ol?X?(}jn#JmCUA1_?3z1jEQArThW;L(jVJJ$BoiNC z<+ra_N?;6WiVYt1ep1IDh;u=th*(hIA-MYcA|%~bKg<6*Y5(hSP>*sG>Q)azTNfIb z&-@2PyqqzL^(c=1%D9F^HIv&00Q%RWz8b-*fxvnP)|_CLt^~5d#L5s4%+Dw>TVDUO zI8=R##5_QuOZ_U@e^s7jFQa4cIcZ!vPFk&!>|#u( zfJHfpstu8hd#}=`h@L!Yt2)$Z@;6X$1amMi*%Z~pmn?p94~S^qqnbY>R`fk79b)oi z^lII7ZP)_~>SBU)v}6MSHQm3&tk6v1-=7}c)))!M1zV=M-4IUKQxerE5h*1&&g7ye z$3ldNiZ=u1*AL9lu|U)nyEz6s87OUqr0^>YTf)j|d8b(5JAD+e~p zJF;6jyO9R$5eV9d@mT~%p&o+%9v^+B1jHpvL@Z{p68R?zT+szo+rs53^Ur`9+j(ZpV z?#s)39)0iq5DVi~$HUDIQn4NMsm4MNMYKpY)jr+*jX8UWhAJmgnoJ`%JUB_3YZti^ ze{S#SnbBXnilF>??Fh2{|ASlbxh_^JcT*NTfbU$ zOm0V*UPU@qx3}#0=qabbte3z~X$Pfmsn=l}f0DdCs?SWE^tRM2qmsEtqb+U5 z5w@EUjZZ~gwjs77)_=>N>mt8PK*OxMD(KgfW4b^-a2x)D;xgfdh6*7zby<98dYbwrkjikm_O&-4qDtpv~lnh4=@sv4*Z{UHDLdt^{ zRGBsw6U4Tg1+1K6SA6y~v~)a)tj^i>XqyI6(H(&b4{_(~(pYV8+XBH^A1%^mHY0_SkUQel`avG@y`W9X~A&MgR%) z78js)@7}FkFSibZ%7h-dnqG*lp-&}eSo-hpCp0nexF-h~oUMi#WFerO>O1L&v2cnv zNM=>NOhJ)+zf>cx`YL01P)p3wP*@_;H8OO7#K~Gf@#&<<`EZd1d4g*XzO|Zjrl`|Y zE8|M*55`-MLpmV}NrEn{c-rU1;0l~fv=&Z~7&>EUSCDlI1ix#@@`V+8%G)VcCD>$S z&20V|`<@TZupj>5_lxCA#e+Vi|Kp=j;>T z6As{lt_sa(ocAov&N%~xOv1%NIPFQn9NQ*bn3ZvM8#JE|R)TyZ$%gD>ichs%(I6U^ zR6Al?yv=ivI^t0D5tZYfY~qD26>@5p-ge1EQJEr`9_}rkj)r2s=zI*0au9V1x!>7b z(;4y3PV}oSstxSJ_ZbP6{cr_pBS%(8wm-5dlI0H;6#WpvjfA3M@?9=H@;^@um3brr z1N59w|56^C8&VGY6h}^Q_w5b*FD%irztP*JV3r{U={AJD>*KE87=GcVD}OGI=v7KfAvM&agQnsd+61G<=+wGt(wac|0jI>`2gt;^(UZe(_(Yf$ey* zjQH&J1>fuNo77#h&IdopuFkl3yk_jrwPI6(n9F@7xd%e?-e!(?W%W+ z!_d3j!@-`5|7WS&Fp=A7rNonZ*ei!W{=~NhL)BbV@DRPd6|p8}dXvB;m@DoYa@a-( zr~OX{NZU82at=uKoijtyxZ&|t@(6mp#rV{2z3B^m({~)XnJ6)PiKJMS)HVZaml({C z5>3L_v73iX2lE`AtDk?d%jEIn;tOhidV6=&NK(qdJ<(_Le&MSQK5g`cOmFT5OCpQH zT>j@1c1DwXU}_FOLHN%6pwE@A0-#lV@5b#9x3w1)WWDW>10(695zxy!T3VEUTl&Wj z*q>XVx{V-d-uuGZ*Yk$zrTK-R1BZuUaVLU)M*qEn$IZg>op&Az+BFY5x8)50bw?y*rJF{MW_rLh-7fPJ{umhTRRNac*_XeGXJ@NNZtvih? zlKIjqSohx^2}7~H$Gw~Zcn$;D;72`rG^5{gjdr2lbD`X2T&~YReE!=wNk?A%ON02Q z-+m4_oWmMBc=$X z;I8WBQZ%6^>xin^*Th|_)9Jxj@}!mvDMb$o$4#5LCqmLk&Am;E)KAHOcz-IK`b}Fv zMtVaIqaJ6w(~5y;lM~&eJ?VB#f1j~Oy^cWvpHRZZ&SQ>o+yeI3ZZcH=Xkt-tHw9Js zQrxtX$emAMMSbN(E*3M|@$Q&9sm|%yH7pbV#PakR75(S0tQY3hb1xq= z(}IMhw>lq;#eOOs`(eX*FO169&gor}q~^6ZbHDVo2oT1nCUgRuWlN9%*<^gfthl#~ z?%*1yD)`10akLO7T75c z;Y$+Ps@QmBzLkGEiqQ;8GtGi=KXiF%8JX+0y9}LhrdXh|gD*>taCb=KRESw;b;#Jk zi@G7go$Cw|%N{)nhrrb+u~nm^D0Mkq4FR@T9}60ir)rpXIheb(O>~ z`c%u}_3@S~C>S3(A8aMZWV&?uQsp;Ufha7t2YtGaYv#w-j5Ar4ZdD}wb_p$) zB5O|3l35IT9(>jUwN%j6*XG7b2(Z{lnfWCKBJNeKr2OkQDWg%=M&dLejt80vgdz5T zaHq0Z#LO^gm zp+drW(Ys_K$e8X)_J^A}UEAlUeAXhK6E+v-PO~9~1nw@`d#K((B5) z!_Bv_cu}I=T!7K=0Z7^0OIE(%)R?7MeuGeDkq9vqEmvrQb}qVC&6$1Fh;5e1Se5Zx z$?VSc#561qlEkvXz}m5u@U>BB#h$#c(1A_S2u-jYk67#wiofXRPlusNch`TW9|1ss zly5gbXNH{P4xG6HI;_|Bt~^=P7xDX)yGsD~d0o4f&Wc0HzVG{qKrZ`y#Jzg;KxaPt zgJTSu1X_;$h#^c{N?^XWzGm3~z7Yp;X+AfoFq(-hfG%}7_dZ%x#u4ght;s$mx6o+G za`)MU_CRja>9nU3M=kRo-Gs$=_JPS^;y3AK4{GWr^@DVSkE^!qd)QKB+;K^Fd6a8_~(1jmswZI1M0GE zPc-rI$P0|sOpACgucs6vC;z~D*+)KO zjsV(xOXTb0lGwsL?GrBg#L$7=*&vpb`1eCZuOv63*_pnGI+1f)cpCKg9U?5{*lk&Z zBv#!psNb<{*o9&hJk@jN)8nl-t!HGR99AV%kDE3qXgS8Npw@h!y-s!Wyzo{(-DoL< zD&nyCCh@#SrqrrzEZuzQJxx!V_Y;VQ5GPN$&Doc=G*`*(>Vlgg2Hv46Ben&ekstM& zNOOWNn~x@3q7MVFtN{+<&{<}9D0NCaLwhEBhPjutyM45#q4le0&)(=sK zhQob>C*r{ZYWpT`1No-a()tq_=vy{WMEaJ*f!J?md}{>Ax32Vln&%UZ$xL?BK^hk{ z=V5Q=wB2Z6e)ucC2QhjgJVO7{xiSCY^4PDFe)8exvDdV9-mjm#D%j7e^=BQU(Qr(C z_DSD0mwjEVzN9~}3!S#*73TjjBiK8QN`e|cau8L~ow!N{D+fvz;M z^?8v$oZRaPceC1FlR1ko1Qo+*8~67EdHrHN&pM+8@CpMjnp;(1sr$242_kYdsWJJE|3k)_;s+AjGoh#7hd20(wnqYAk`Ip@O<-5GG?2X-urHH1~*F;Pn zKBQ=Dg!Frf5Iy@1tl9LjE%1@?m?b_j201xd@Hh(Ph(Kpe+I-9)og_sA<<=xwek<=C%oazof4G&oiw%ylN${8%sA zBAO(};Ga?h6IK8#1qXT_ZCzyZ&2gDIhB(9GgI3|J-Jn6aA-z7bhPz&)82ai%B~Rml zIj33J^G&On)2?Ttv+!5zGTt`vmQ~L)891nS90!@OB!x0m)-ierxO>f8IDs$(?WkO{ z(Pj|sFeg_!)BSoB4Il1YPtPm6qwiTO-RzVZwimurpLaO2K&iF#>G}`|o!0)q8>4Ni zjY=QJgw)1-432;shUV*>DS6~#l(6ozqJJca4}7mHA3u8^UsL~aSaQg6liwG(cwU`v zl^=7js_jWS@=k-FPpzMDz53R3Ng;UQKL92{*}iw);s3Mu9^kfB)!F|M5{)HFEEF$- zfN(EeKm-Iu5rV`h1SQzhjES0JNun`^XiPB1SdzyQMG2OuSV3xNB3+6UvBXBxfEquH zCg%H$?)Uu19e2*T#$0Rdz0bXIKF@mgIeV|Y_9}CZcZ_#5zvws)ixuJKskoLQ zO@6xQFG$;QNkjcHV50ey19VZDN{3LS6oO1NQ|r3z^7r&*D{^^6tun%3Q;oF(Y}vC`(FzmsbTBI^g5 zG-5ef8W)hrbt{F+Makh=VL0S^|EO_Xu0ES8XOw#RsPk#5wi}?L(CS>2oJu=QTa@If zME$`6+HSk;ej!c1*V*@_fqJW_#ri4j-&_`i@)ZFtoJTI{l)1?{@H?d_ zIgcj4lMcKC2euDVR!=TKpfo|E^jKH0LCZvMrQgN|B^41>C5TdJwJ0~4%cU-*V^D8n zfKgw77I&^(l3di47l2cc`k$vh^{L#Wf7ooZ&2F{M{G66J=3vo_QezRyKLa}ea9Ax) zt(P@?MT8vB&e|Gk^{BPGZ&NRgbZS-q@m5=H^$kGO)q~Fe3BOMsQG20 z)Ns`A+vSVWoK)acE%aKIA)~GkJI<@pjAaI;l;0a(+Ab`6vTT%7<9$6GJLl_BGAN3drJ+5rfeD;vF6mT><9uE#NNVo{XZZ@8uGXX$ zN9x-a3;~NiU^3Q1nB`!?0Y0|ncd7Xq8xykcBJXppeM^}1{Y_gSHkZp|EgpqaWAPky z)KOF^cum^=T7bYjM9W%d@=ot-E>UvGfPx~7mFOZCk*G5kv$%7v$%a;2AVOP3V0l4< zQ+SLZ1Lh3BmjgFd*iPg6DFt^q8my(@N+85wU-^HA+}8-$bOjbffF$z$l6If7b~LXn zFipB{HYj>NVpHv3(t1BADK9UwR<3{A&mDsW zZ_gtMFEpE%AOHYETlOUvgrG7o^8eUkxDjR3&xwdXC=z_onjaKN&EOs?+&_0u?C)|v zRFyuuetf$Z=Jr>=`c>Xs4mzNYo1FQwdIXC2adM@Y6l9>rZ%PHQRtM_?!SBPPxnAZb zpXK~Cas$|cxj8yb?G_F`_~7+Emk%4djn*SI@VDS|DFFuxEtJ4i&ut%G?ZK!PAkYOE zeSJeKDgQ;KO9Vc(()SV|$~+!}R$D1`-*SS(0*)>FVyOwVD2fdxZS4a|P+rBt%pG4X z0$3y-Y=*W!)<9!qlOtE{9>kHeaBL2U0&y4wCgik6sv=bCw}l7m5#f-FK;oWQAJf$L zcyZbe-+JqbglcoEJ^9NYd|MUM0FHvttn&; zVqvscg1Hvvs`{VC7R+zu?Z~3%d{mw(N6Yek6_hRIj&UsL;aqW1)n_YJzZM-=YE;ME z1AN$r`S1Oi)^b8QD&D`z1ZN*Re#YYmr3A*5YME&2;!THAFfG>;<=0!d{2Cb7{5%$f5awuFNz*%K`3agCragT>a)XI2 z>asM!hYs~*A?MAuBevnuoTGIJbKTbZkR^mrVH*-KAud0cZSJWQ+fOEqbJF%(STmx} zy9$x=itmeaj#9&Y*I8{nclnE2gK_M7%;o%)=yK?>R%Or!H1J#h@-LHiK2K%+b8k@y zB%~D>fDw?R%ut4yTcW+ZKZWa2ReWE~0AbG@;e2ud49I!80dje10yg=2yq`%{k?OaR zb5;GS9wYr0-Pk+!$n5NvOa+ULc0@ExHAt6KfgDAggB zd)Mb(HRN(F9BgIq97S%yPwg&RuFF<9(Z*c(#y7t4T!lu$p|T9#3+#e-_daUDML&MR~ZmJ#^umA?G5x&aaj(Uu#-Dy!ei;ElWUG&UKC9>_V3pcNo^7-nq1Q0PzMPy zdn2dmkA=Ww@v{JJ^#Bd-DW5a>mEG^Hl-B1fY630PI!$dt9t%;&XW;`rU3{$#v6}ACWo4v*x>=(o*TOT$KY0Ou4XUbx+3XDfxorB0$;7Sx(GNeO~L1isEQ1 zx^QQK!?&?uWxC7qvAl6Kk0+Y5Qv$NCUOth1A^gO9 z(B#pGpKDf@kI$sfHHcFnOR^|_k`~ol5@6gw?-42jbPWK8C?F;}+;VO}OR4=rZbYd# z=D&gm)TZ0gZ=P1mffwKDQ<~pfJ*Auvgo-^c3q*zM{21~Gpe!|`vsNo|fcq`rA43_8 z&h-+Iw3gc`{U&o!mCI{>A4|REs`+=V2j2!B1?kK)&z#_jwR%ePQYjqFfm|sZtP-M> zb99%i5!=cjXZ7SF>tuj%eAQ>m1-dAq6%Zaduj;<|*Xr4KCKhrqHzfaCjx4{)g|*^r)-htF;JbuR2!iPHpJMB84s`X_6x>RaSR39M@1g>Y5%jZ$wP8*(l1 zX&IIQ(D9fv*IaWA|2{|bY)-J#{g&VZ`E&N!XP@Lj z-B?Yk-8Lu?NeG2qKu-(YG2L2wS+no zm;09st@gE|&?1yb)<;Y11Fq4~>%JD3ZFOCu9<>j`lrZXK=HHOc>s`8cY%FX9DD(z` zjs-^(tgeCY2ms*NfKYA#9sofJ&}#SD3UwBbqTeLAeLe#VnrmP&4hgMR1W~RD7#gI_ z^YFkLsFwYTw4DqqkC;DD6}Ol@_ZgYW4JCX%zM5>n`TjF3*x@-AC@^>d0Z$ zzWd$pX5OBZEa~+$7_53MY*h>J)ERO_QJy}SfNNqN^4xO%M)`=ih8&eA+M4|Umt2`1 z&P$=yxn4fr*MsF|lUtD#7o-pF<^4MdRU7FxRAA4Gf@@7o9r}B+XawF}4XT(d%B&uX z@>?X;X8CjLy!KQZn}S!MaMIs@oaWbm*v|z-0wER@^cw!4uGYZ<7(fWY3=L572XP)A zglw@_?^CfFpIxYgR_CJ6Sd5LWv9kk31)<}uE;L9>8A2Ob3#;K7A19FVbJtUF{04p^ z!mpLA+VWeiaw8WSJ+=>5zoFWdr4|eJ{th|tav z)=7K(So-@yDL4{#9D`Txl{7X4IGR6y;}0rDy4e9m0HuWGKtw<0UUrgIU0K6z;U;oW&{#;Y7P~=cce#jaw z$)ZSOIs~I_ukDjJT7;W?QZN#p{^JgxEREFONKM8PJ^OZj98!mF3@|F46N|%kZOzd)%qIY3)v8rLN)z{GX>t94d1s4{s@k`; zf>hR>g$NbLJp_b2XymRL0gb=}uyVl#C=DE!h1TWE;5=u6|bbuOPt zi{1(xA7s!&vAI-AR4va%X#zO45x_R-yf1LHhD+CqlJ;RhbX|(<&6ULQ)SL2X`qmN5 z-CG6Pq;q&$+U3vXFH~r?w`|nn24M1*4d=*w=CfQ;Pp%cvfRfAki8RO58ar2?O#u!y z%SzN9*Pv2ZKInm&dNc>|!k@)x7i^2=Qv#A)$n=nIs1X1g(%v`ndrYR1zsF8AwNT)J z4C!7uwuRG&fA>~|uD{D+o5178X=1-V-GeVE!B6wd(!AD!@>UR>I~pCypX!5B8+?Z_ zQbh~p2FK)?luGqzL2A$p&UYsj4L3$;HNu58nJfOtIa(i6Ly$pj+^E8%IqFHzgJ**V z4$UH(KGMa?YhLr^-!3*~SW^ZsU6QVXgew zbXl_z?Pe>pj^+31njo!C#lGM)CM~66qMkSRV8l@4r^sR{l~SMQ$Sq!P9Wz05A-cX+ z*tCY#?P*c`Et8VT%fwXGbUwErWP=!F30V+gQ6Ml0NSIO<)xo|j6cz$nUv89IH?Ux> zz~}sZ5CFo9b5jN6AvCVC+5JEu*vLBWU5zqq7-SCKWW%m=0HkM0$sJ#2E3%GCk)4rT zpO(*~b>HkO+Pq|5&flX~<{;McklKNkme>D0JLb>%=bwMyG`DX{=krI*)jjvz^A9{l z$o$~hd&`@3gtdZVkW-F$Urpdjej3{v`>~IGY!54tW>3 zgghF2ja9W8knzGpMMW*ZSZZ%zAyv$nx2U4B_f2>|1^1lu%Yjc6Xlm&KW499N`aKKe z-rtk6fUtZtKl`4O2OzzNuucHBsH%t^LI6qSB%u)CT}+;oGIE z2lKLi)I6@llUu%6mQQ3cEUF-A)EbhOs6c}nU4aHPb(|9iz1(krD$7_f&;-)v1VY{) zq~G%XAgmyS0gHu&33u)-f*e(d6~y_K0vJ@wi)SVwk7k*3{sMX|M)n^POv2n?ap$NE zgo@QxJ;gRc^yGWB_H+J9|64rIRt6$>yeu5OO+J_K5tEQQ+s zwyaKR;&N|!Af+Z?t@Kxof}2+nRb7`2G0%mXiw@d(=bcf_|9lES%LC^%(K76d#w99F zAS6Yo<@9W4=( zGw>VUQSwLRhVDaaF3*^GZGG->&7Wk0H)@t2!kcy6hb_tZe|!kGC}#DLA{y>}?|a|U z<(FUnxFq~Plg{(qxEQBa(a(zxWBu4rW>1#^8l|e%fQ0}m7l^>Q=<`~4QUMS1n7>QC zfdoEpM!U3{l+E)gfkm#;bt@nYrJlyH1p3l?;>Xo8JLl)In<8q4_L=F^U~g9U3*hZ) zAHf8in()*Tc~|MUfB=?|G%xN|#2;jnR1yC22Wim~`rI|}^>B{0)(y~f3QWk+ZDWL1 zv#@hn?0}o~G1=c5qi>NJf5-JjjtR(fKe~b(BJ-)_>x@*c9!k;a_qhMeRcpyxFt%%1 z4I^u`V;>91gT2lJ%bxkoTqED@)?06V<|(I~0-)dz`dSk7&&4ucvu4dMx#KH9AQwEK zYgdFE>n>-lD72dWYT@<@t!BN{tRsQX+5DilbaIt(EePdW*0$#S2(#}eybo4(9b=9g>-%1OG*4UjVK?#2*KY|!- z&00Yvx*YCU!ajJHN9J)*5jg3?(l8P}9+eJ#t0^C?+R3aDb8vetg_7k{xTJJ}!bIZU}0zD}DK zYAvx;2lEfME7%~Klv?u#bqUNNRz&2xqI-YNdLh)>t^?;R3+!&0llm8byt8v4a6cqlKZ?CvMnW>`DKd(d*IO=N3lUr zdsoHSYpi4m+4EAsld4xIOK9nz{^_4cKX_95|0`)bKCQQ(P0#9hri}j`fJau(w43mH zz{7easHPTLt;AEP>zc)*IX_9yNPq8XdsYZce70|7l61xW_pR%;s7SW1A?;t+opaV| z@*rS5Mc0R~9ufsN#sabZi*gl_?f(eNsbl*6_86*wivW*k?`YV&?6S);=WO$XEEQKP z)dK+WoA@ePTQ>kg9$-jv9ji4q){QR=$$jA(?b;eKw7NF_BeIrdg`rGDx}3IBWPL!a ztNvRTkSV~WR;@$#Mt+}pDSJNG%F7wePxTx+@4WMtPCxy0e4c-r{>Oy>)1|fb<9^-J zX@Cm~Rxa;{Ii}Xwxx66_Ra{`cvOY`o;&e>0MqJ6Obzp1x*rrKcwbx31pooPNq2O|w zP{hwv^9l6@CkxQl;weSCscWbD#kcrx#_sD=0=`I#hCcotgniQJZ%q^KCAnj@vzF{8QR4hmBBcZ02AtZ%D1N@%5CVOJOE63}O)eZS_D^6Y?`E zHMX+qId31Vz(Tf^nh#W&gU0=a7V0g^c-VE3V^3PT5J!DEEpwe`!M!f^wyjlRaSU857yVi z^1dImZA+odJ4AZy-&08)-$Ei6T0V`^dX5%?DqEK*Otdx3d>_%38_;J2C2&0WY6T=j z{Xw==5A+#e0su`Ld=&?uv;WW_1D$uS#+I6QX;^R?P`|BZTom0A*2JeJL<^X<_>H2}oA2Y3 z%j{RdKt<7v)&8I*B5rITQlyQY)V>X~fF=o04*WOv`}%GPgmB;k2v`>pzXl5fI*$F> zBBgq2ts7VzEY4C9Qlf^in1;5-X2Isdv$+NfJPoa}#~kqRw?uYSch8}7x`nl<{FMP0u~|+=rcZ+G(4nb@P|$?VA#y z9HStFTvclgtQ|F_V?C6*{Yq)_^eq2<4@;j(Zb3#IXiexxXt=1qsB2QTa;q(qpify0 zY3o+A&OLA*ilS&{$1+3t>+mfU{Nj}5ldhcu{cY}865b_EUbUlg{djT%Y5)oi zDvm|rL$kF-TQ^{V0Ya*F?6t@tL!s5V`dNUFZAUy()#}zkmf-^;JF4?+WWDrRx24wE zt-uvw9hln@@Dc6C-02FKnH3n-EWxf&anH)|KbFhe-~RTc1Vj%`p!C56KIf--x_5fE z=P#}8cD){$D72cnoGU_V*%H>CkIvL8I?X0c`RJ`ERf<<{h$4|*kTL5SdF`6znV}*u!(=}8UP3WAQqMU zK?0%H)(tAvL;D2Zx?zma>Rj{?`4P5KvF$EAN2`Z%S=#Oq-5J>-OSU$jTdP`^tcjZ6 z#`}~aV5R-kMAP~+bF`aPL`uN82n(p@PcDIwlqp3nQW4~$i!P#?!XfFiYtlVAWNB@6 zl7CFweXXyDc?@Xflqf&vbBednbV+V@?e~?-?C@Rh>0exCG z5IMxLhJb)72m53``KzpEX=%9`hE|uxm!muKY)W}FefsXy=(Ik5)2@6UeNQ<{HqkU^ zfyrsEXvcXggHbsVy{{IS)GQC>P1exKC!dV}=XaLY)@6JVb5*t5Sd)A2y>}IOH?g6pHrcU96Z-B8=>$ZJKu(5SbVx+ch?l1C%7 ziIVI}fdpOhW%gj#8;`GRY1{LbX?bW zwK4zIJP!+pQa-1hcG}$&AiXZFna?Nic>;dX0}eP~ui7!WKb~$=Nh*QQb<$q!=AEiO za;=ot69JtZfYkC(qFirFB5U6Y2)3uaFDv6M1ZlOJ)I%oan04P;_P5VRPSzaY+WJ>h zU&X~~yI<=9(!48a`?4CM{vZmL63tdC)e~vm0QmK7pRD?AIo}`PIHV%>jK*i9J1X11 zmaEnkI^884OX&qI0YzQm-3kyY^>tTIeIoi$oe5SyG1LNQO{+<$aOZNheTM2Gm1Hi%` zBr9V`>xMosu_=k`ONWds;32uIdFj`m5ES>gH~z;~x2_MAKO) zgo}_9J}1k{G_e5W#gQ|Vl)uM+_`@IGCn--)m?|r;OWWOboN3U}I>*PasXB9;Sbo=Q z65=(z->0S6XUl`I1v-WC*y%WDs z0-+Zzt@R82PA;@s-~*;WgWFGUW?{;FmL>xor#fCOy8-GIcC8Yj63?qOMy@`03Dzor`d@6yy{LK0VfA()}x0Ldv~~mde2wgzv?+Zm5;& zu^`kE@~m=1PLyjP&1cU%@W2DV1Cr?n z6{Rq3+q&!o?bAxT>%A5s90jQ;HP;1fCCyDjsH@IWuJZ3nGO?q_)qJoa<|P1{wgLF% zv9DK-&|0Y;wWE$4i^ag)_MzFOQaxOMq$0OhR4n}v=`FMZ#<4B)$U2pWRJD#$aFOG6 zC7=4w1td}B!UE5jMS$mF73F+b-paGG&ThHF$vl4)KAW>LB~VJ> zWAl8xqq>hK(ptYQLOrw$(QQ^k%!ONyLde&?eclrn5aW4YjPq(bbu*c@%Ei|+Uaz(R&kjYVAMnjgyWST zQrlf0lHIE5^aX`J`wgYvq}_WBFGsma7ghbZJt^b zbaVT!WQ%9W_pDr#%=aK@OFcGUcQHD;@0ECqD6s zlSK%1As>)!Hm4$WEFdj#+t;Q~Ew(OSQk3T%TFWi6DEv+DdT73KL@OWU{IDg=cqr3xvzdLfDVbKs$f3$VBK+V34T~%gUE^-BlBGO zfw|zD9NpD5?;@?QOKUuuh+4ClTEgkI|5%FctgW9e7NkYi$T+dahqZ*54`2kvc$1>_ zE-?yx4{(~y7IjF3i%VaEr}&Ni_uu~;&wAFgCJR?deKs0MiIyyUzz3QGAQBInb5?ax zbFHO#S~UJrEAPGzLYrvXHMkF@JfFVc(wBl8gD3V{POikxloH!&p3+iQcumlAUXlDD z@(?J2)76!6tn((10y-j^yEKPEx$P~Yxi`5A&$ax$mXbLo`4Or6=HD*?lVpPJHxpUc zwmWf4nXr{+@sxZ&B{|?LKBzR2_X+8KfBfSg2S*OkdZ6W2YUxQ&deSB>pz2Vlx`aHG+q^@dS%;h(j6+LM!;A*K#_#9vqt$i= z^a@uWGS~VBA9V#Iw5&6=rEa0!#-LzVG8Gp|0Jith)2|A+!Izrm&5OlUV61GS^4gi` zAYxiymbM)dD1GE9PkGAHp@$y&eTprj4ay;F{X#-Lz=z}VszgqfKrmKwSYv; zCPU|NfCvL+A~X2V_YiU8t7&_3+74T_YSl^Uz=!R;^Ul{Dc;JDPs=t7iEFl}B%}~ck z7809n)6i<^L;xpZ8AXecMm43VhGgqQwVJ>48eR8*31h9@QoawbQ%(3oi`MJ}{4Eq% zSBrpoX{1nb2)6}+&^Z`m(qv1U0!$)Rty||@t%pykvvG;Oi_|<*yO8L*Oz*3(4lbe)F5(+%)09y{BmyYkfYx@LOS=cg;1|Jb$yzHajovzt^_gZu`6= zjyU2wM<0Fka?}n(sAv5`YUhei2oSQ-V!3iUCI3v#`iXF>L+YK`zjgjyy*wP;Es>$#8d4(h5XEh!6a^kbaUB9gUfzg$yYGGPd-SqW z{*7;ZBV00sFJmE1!%g3s-r9Qe%{PA|0K~>Z0f@Xg>N!;a2!WvR)s|?sKJP~pPNP_%trL1^Q(=BRpwv<8f|67Hk+qwN^t;yTKy|M z$8vccn{X>jNUv&BF$+t*wtjPYJdxmtE=#86`QsPNMY{EE=`wFX8v49y-_N-uz?vlU zU$_iDP-l^G;e{7YqJKoCY3-$rgYCZMmRp|vPt@B(YY9E=O18s=KyKgg&2-UEfWK#j<`pg2+e_t+X0KlMz^ZvfNjjjD?v*w;mNJ*H_|hwX5B{dlNjmgPz0#FV6`NLQB*VWr@yX_wzal{e-_o+{P>e{5Dsc^DUa@LvPk<3w(Sc)~tbIR)g!{x)n-as%mW(@t>^pYswXV?HaNIh>o+Zuje^9XF!N541mCh zgE;TU!(1q}LKK??ByIeDE+8N$oNBEXx&&c~T8oy_>8fxow?GT{M0|d&l~@)@x8>@) zwE$AdaL{`<-+VJKFA0GUTAd=+0H4~!p=v2*Q5CE#W0I*w&3|tyDeM1I;a_XuyHmG7mM4$D-)^-Y+Q#5;mS@^WoN0%kUX1 z8F(o1jsWjt^`OPc$Hf4M7BNt6h#=y8qI0ria6$jd8ZB8p0wR7h(ptPLLbRnsP0RX^ zfRz=RQ6)r5n7{4~MTzVyb6SFxeANpvCu!Us0v`b7fe(COiEdlRIwBZ>ec0DL6iWC$ zCvE#~wbfQHOeeGNnl)=Kgp%7~haHytu-Fpz_QY*7FI#)5t6^Q~W^_&Jh;z_|6L$`P z+k($nwbo|wy{^f{bK;rhd_BI`$D4FG}%fF#oAtwWxxjRB+%~ zj6lVqFl?hh8CgB{RSAXa&Kk|+_W?*PbF~B-QLCqBjp>@A;G^p2US^f7oiS28W@QZx zX}w*tmMm*!TQE!&n)h~s&lOi(G0_TT`IN#CRk8%DN6Dp`arNbwUw&!&{Oa`iV^*zN z_0&f_>QT2o=Q+=rlrj;O!voDtX4_N67xatrtu>%&>X5VYx*gH0PVsN{1$$j=`h1 zf|V#9TjR|KAeaD)y`uE)ya#Q8m2=SY?3trjIl1O=4|>ppCf7}W`zS3t=1|LHgk!Xp zog2HR?XS}H-CeYj_4{aJE^FP}02D9O$DWTZl@W4nw*61o*Q0kYsvB8n!9+^MQJ`Q z(MQ~Kgbo^SuC$J&;HC?iIrhMn20Wa%w%DVDql(CGs> zT@?_ba?DcmZVo)T3O*NHaKXeP@syDtXn1H0>h)9F7sO*9)qXSM6)22pdsCXs+pJ!_ z`h8T@-gn=9m&@8~;+LY%a@J7GByEj|lMrsnoJUJuDZtamw6^;y>%J6ftxXjX(seq%yEZ7F8-omFF8;H(;Ls(1um}5NoYR6huH=$1SxO z8Kb#eD@|8YMr+K6qHd$gigaXa+wOen!_4gAveLel&)6=mI-eZqFu6*{hpS|?3 zhdpc=*Z?A*-07_mn@`g>yfK;`-4bEMHFPDWj@ep!mRc8rhi;r`ygAUZ+q6LD^Hvh@ zkkivQv>GddSJgzc_oOtif_VO4``XtQRxp&`$65)1;LFlAZd$T>L>z^YGiWD8)dpM)2O6~PCJ4DPxMc<4?C zm8Qm6xkxw`I-nt|rxZtj%PqG|G{xznz9?<`rR~|f?Y7&u4m#+dlh>?S^DTf#rF;~i zPY zwG(q-DsZXt@z94pbiL29hPs{yG#&R0U(>A1^5h0sR=9J@EP5 z=RP;FV$`RnmUNY@mF3XtlDT^I)mJZ<HV?jiT_u|yxb9F0*;7Vyd4qj^wqLndHRN3LM;Rhdl z@C{FU+S4YP=4hR0F-nk&^bjh&Un`BlXaOH-oGmr5YLjqm3a(GoABu{lI6Efru}f>i zco%B*w1!rzNG?{2X}0+Pn+oZDYxPKrR?jPF=h}dZLwN&)qM`d^3m%o~No(iL0u<}( z@rkC>-YnUMd$k~gQk}#u<*BQDP9%?~OUNVdjEt!cu3i1SYP5YQYTO`@LyIG#yQUw29@knRraZuFz@=QYGsMx(#sRftG+r8W+Erqcap;B&)$`9_R2` z0zUeV?3uL9-+R=f9`)Vd{LSAaxZ!&wup$Ih^svUwM$G0C{nP?J2(ReFn>RJrhkAoL zlEAE;<5Y>k-u}#@u7P>LJZrs|%rOKF3Rez%a*CM+A(St)y;|Za_Yx2S0Ei0hqo52O z>0A#_+Y?!nwj-qlAiF7ts6;yru~PcbX}Ry@0t-rO-Be-^J!9s*WLAzz8Ny7gG^%cB z%{Lp=F_i_palkV8eDaf@oLC@$NiDRR+B{e~ia^UfFn$b;P;^`Ac=(r`RkO|@l8yzR z1H#XL{__*+!dOgiURoQn{pV?MY@NX8AC5TUh^1r|Ef;{yC9n1=)noT-N#uiO$%VVl zf`S{gjcTy=o%;lYT-g&*xp&fk_##}U+a@(NdYC$T@ zW!3{0S{H3MgK<2R8$c`Y$yq(DArvlAjeRNW+B|EW?_GfjU28>jY&5}?XOnA@5?$Bx zv~tD(nZ8Vl1s%ONbvwW_|8Vjyc?bv-fTs(5purScUD}VN(~i*UrLTVVtCK}p(neYE zOo&`aEP)OR19#NmcM~Z6Rr>Akrtkh?0>(WP2%YoHXFhZ3kV6hxuE$oYhTUTIEdSv8 z?Z>M;zX!m+yy}=%OrK*&yPOsZY?-i!&3jQzn^-k}Z%Rc?7L1?k>i=U0>j?j2X}}gl z{6PQ^`vUAE7Yr?p=cjFNzz1d0TROgh9GZVL$}O7X<1Lj4utcNeY7t%9zeM|&EFW99 zETY8r?ZiAwP#UwVa?T2}vf1|fLeIjsn0!jKnYia#kO3js^7F87E?o7FJMNfFURg$^ zx>+n#RZqwbYRaKi<}53y2`r*q*GU8ofHtN2Zdh6yK6-Sr>b`u;F~=9YD?4@AJmF00xSxxE>Rd9Px(3} zI*o~rc2^2{1$eNWa@xpeKJ%H$_i`Y4%{A9d7Hr1a`WP3>B0KQF1Al0RrEU3L(yB_1 zLEY3Yr?Lk3jcf85;bWEq8mpX81f&38g!I4u>%X?lZ!Dje&1*6BX^qW2;<|XQxp@X? z5H9VlaZkb32FGPCK-1O0n{^2tj<(JEn}I-j6*$M5W9_SKj@E>K<-Tw(eHy6sQLbA0 z`jo7iu6d4t%+=|uS;IADrWAZolPz6!CKqY_v;-IUd++ltr+*W*{JBj?j)M+T$aHDo z6H+JB4Ba&u_;3InaonW~WAd`_N)A5JB>n*_rw4HJLV*R0Hj*jdvpka4_L_A5FWrCt z{nz7b6_n6Alz13cGt+eqDSJP!;DhL7=NZv~01<1^2NhV_O?`YKl-rPeo34^NrT0r< z)K%Gf8nhI)e3ai5tx~U{MF{XI2ECIWL477aI_t#n`}JCjg!R!O+S8LQ$! z>0w@Z<&_ibq{Oje*`QVCd__uF&3%S7Rj_*2(Sp-?y<^RqHCGC7>|_x5wDe*}u(jlA zaBa~9*je^iAM3kZKLUwo-#9)s>xF_q@6#F^1qY=p=f8n+@lkKA53LEl>#d?F6~{gA zQe-$b3Jm_B5n4CACIQONDP&#%%z^``e~upYWLqnw@p4b%Kcsw@BsuOy?R(2YA9FQ2Vel%N&vuu z=3(=jIeHI1nGq&;LlrdepJU9=qN{_x_)n zW@95cTIzMFBkb5bc^?XHtO?CM_`(;y(E7E9r|n*|0v;3s=20sEBVe&?c&5yGf3N(! zWnOKw%{J=*(UALyg`?^bUGs=*!ynYsl0GtRho5`yxs&y;NF?8>R924y4SbSxZLPG1 zNY`j5f84Il!;wfll@N;N_b|BMp0VkhH`vEQnrGr^>36$8-S+SC?N(sV(l~xH-@BsQ zNEAh=D_`NI}b+iO-68%3dKc<{`sT{9=6p|h4kRcq|sci%m0IeXuL4V`9g zDdBWTfPkoL)kQ1~Km&gezuQ$?E=PUhr&3;zK*oZNZSbgy23-lCT^7n1^RcC&T-Wa+ zAia`Nvx-rIxfitr(H0#=dUAqi<_$@%i4u!4mS}+E8Bze+yoAz7HMz9&7`1 ztM)u#99RQr<-xqOKTg}OtXo+R%KIB#2r_5jnF+plK~Q>LcCQrdR{re!?>Nlm?}56b_9xmW%K}uq#f!=G;Xy7KoJq zq7+)q;wxE92n$h%r6JHnJLDP=PSZwU7n3|_bAPN5i9UQ~`$xxi+ih2UaD6nzz7*Zi zio_Z$NGyYB-p_R4_&agzg~apd6I#u4N4V^^+ivT9j!?JCml6~0{FVj{W!9R|+NG@M z+-d_LWQ}yRZonb+N95vJWy@~X5@KN4ixO|4(n=PPeLqs!AVOV5>&w~q(99*?u<_P1?bMPC2>1Zh zr3p1=80(l8PDIcsU!-ABsq^hqAR0XShE`+spycGf%R2Bu1PFLb!Px*B!j>QMkcW)= zoB&hiEW)pr*vx`Qu>!0Lts9O{b9Hmp9126u%JDVJtkpB5vPQ1%n|&{mT$($Fu0+(5 z<;68eDL2h6`7xrs!fK)7rMi>R?=PBqs{xP_O7)DHKhwz3Ut6B5r2~KB6Q5Xaja>?@ zM$oY6Ol$0{M#;ivzmhMA#gq%J=6g&K7P;3XMK9F4rveD#;MLC935KiF_M3a}z4v+# zyJY>8@_ksK(Na6g_i3TtB2K<}0#j~5GuiMVH0J#;B2ICLfKW)$Wmw)+}iSkpjcJ#lx z^>SZpNu^`VvDPl9C18nIJbZ`iK^{f(d$=cnj}NoY$pcx8xzvr79%?-X{U%wUb{lNm z>ogzHn*N{)J_u@6yR8X#9Q_J1DA8&70kC>X+9;NdS~%oBlRw7~a*8-^EyzG6dYCW- zB%{Sb$iL-cu`DtR6qPS;Tyo<9p{X){E-KmAHL` z#x%5gSwo~QU4{-pmAMfh^DP)k5ox)}!8x`>93kjRfgVGFrqxt$q5mQjB!ag`@BtIx zl)ejw-|SnzP3@q4?iusJD_+b?8(M8CFf6`3_uO;&yCv;+%)vV4+&lb1JEyh!)TFRI z5~U5H+g1KW*1RISy5_T{wWU?qXoij>d`X3A9zD4k%S~QEW7Fx}MV`6r-#+JXOM9HZ#bq?T2TEq{OOGd@~ zEm=ew0uB}>lZ*vgiGbQYSwG*AC89|nFe*(R`wDof$4O+MEpt9CzNMkbU#%@0`c8D_ z141que(uH_U?y9?#IuW*#JAs3q++f$HbCR&p9K@ulBL8P*`@Zd;|d%*|TP&pg{Jl+KZBik3s-xvJGg%^w>LLTFJ4 zS4z9>$mcp2)_@EK_G4|(^fQg~()NQQ_y8DPq199=P;2ax)dR4wPf6fI+3{KKbDW_i zt>pUaub(XV8t|Z{OrzPBk58UZDf66jNgCwGyzpu*xdXtpQg5>;7&*jDAF{aZKGCGT(Zfg`;jZTXkpZB;f?vv(>_j!5R9m^Q-@z$30 z@07r%RYwQ9*4q_g-Qs`Y zJIFc7o?6g_+y}@r?qOFk(9-?tYGfISc$;^$TWjbw_l$eQeYJCkwU&&Ow8~ z*Y;O(a;v7O(CS>ME${o**no`yhUaA|8z^F;m@5JOt(Rl=i`?iEpKraEex$Ww|prh zUrS6@q>13l)_?;%qR~p0mQ`!)op#!3z0VcQ;uR|UxAh%zZLA4Z;m`tOVD%~Da!%tQ zrhEeB=Y8lyADWySidt7o2>}m+LJD9j!1S3zkpiS8fT>wMC4f={D+(uaL@mSp`jnN! zD@rf((B`bv?m^%CvFK>Fp^ApWt8Gag?)6k@yESbu_tfwv0t_W`mV7i9Uwme*jiGEgl z=q@^J3~FeIf77?zE?G!9C6Z^udXu%|6(H;9&tC_PvkEMf$EsUkJPRH4eX&}u9wMZx6;){Ce;KM+o(s?{vy zk{@WqfurVA5`d9Jubk;CEfOnHLN@2florPRgdx_ft5&D)P{C0GuBcPDbUc&@d?5Co z2xxL(q`#+@B-!~FFRd-bvz#^E^nm~8eOo*gJcz{ z)w@6~nn~Yt;Gp{bbbh;?b=FzCa-ArVkw{IRjev*mS*5X$MG~EBxp~#qA0{%-Yhc8) zSM$DHO*;!fQZ%6LrC7Aqa*NPu5wI!IbuC~vE9=K5mCCOus}Wf~T@Ts%jhg@%UG>}W z^$1i-R*%{tl!6R!%3^Be@^;7x;r$$ig2e?d-$%>sF;1{nTZDEiJuEa}ap%5TumvC% zJeoBRs`NWUC)A>QgGh)g06`91+q}$MJ(i1S@ncmZ(9`&B>-=xI2UtB=OgW#SFPS5M zj;(HB!H#qQMagL&(X9w9TisyBxjy@%_g!|`WiL7BoO32~4nfdGHA_gaKLG~0?xcN# zPi7f1TvKjp-?!y-m`|Exo(k7{dXC&1Um!E$k7=RSR>Jxm3Qj0HtZ~~Y)2x@@&4Le9 zn%cD(7uHNz$dE+OI*=z&@!AG`zv^X-_gFTi`GJ? z!GS4tL%JugzT}ciHofYqt5$I@S%VP`Se!h z$T8M9bIB`(TBAHqEJ5a|{6F?MHLIh`;;G%w2nDw&kgClM?w6JOI@jqsqt@2@vf!hz z>5{Jp^j9E4iT0wpbgeZu+HNTbf!v}J@G$soXelNP9DB5QXU}mK$Im_JmcH}?6=}lW z&y$<<_^Vv2+0it{lHX>Cleq8aSRDvz>h@s`+qTUDAqzy_5}M|1JvjaS!KJmuNcZkK zXIsC`hfqr~^6vvMn(sVI`#C}hpaDn~S}p4a8ry=KRkm8%t=EjmwEaWc{vathyPtH@ zNsq;nWDT)KBJ)Q{zyJ@=A{t=OTHLm8&`ZsS6Q$<-^G86c^$ORXquZo(Drks#VXJvZ zD7IEGihO?vaL*cmWc6Ta+j%5|C5;#7{5=!!skO#dKc8A_Y&3lJ^U0a|>JFJ}jm_Y= zaI0MrIcSg29B3r?j(m05k;ZOZYc8I@6%(G6NVLX!M8KrQU)1N`&WonA zzh&{mrkhO5>3n=?ZU3I^*aS@=$AR-eScIx%P|B^r8bK?pRjo!lELk>Glm=AL-YARH zbptsTc{^yIY$K1&71Qr0QCDl=TrDfKN@cfupO3YGq;W~8&2kcrot)O^T;-rd00Kbx z9jy<{T0FT;ku}U0ir^+;@jFXv!#Q7&o5u>Ro-T-W2H@utO|4?XIm#Er^TK*zE(16M zGSPET)YjKygA8C0^n7~^m0&pj3t#xc*{7a*>d$h|R4yl4-2qVK{;CFHq+sYQm6%8* zW%T`;K$r6y#ZrY*bL&y>^US%wxzagN>a4X0aH}6^9@dXdiLm!f_g*moG8`0O1!hcX z>6caKW64LO(CS(x0|0}ETB6&~68SvR)e)+KMGlxn>Z7&p9~elOTe%-K-nhjl!e@Y4 zj#suJouY-Hau8Upk#A!NjlRbn(jpm33Sq!JLeu0P*eEv}jNmtQ66Ls;Uw%1$qX+In^gXr(R-O-$hoZGD z(0I#-eVL0qPnC)3qRVQXSxe#AUByV}NwfJjGV;UPzDM%wDAfjVVeQy^Bsn%+BY&5+ z&sN!?TmS$!UR4TmEe-5`SRo~=25nOlorjNkR?9|58%8_DT4158EpUz6!fJxvps-_C zf1i#)S%QbF%|j%Qz?W#x31+h6S@2wkt)7tk9?t^b4|HegxVhiYapR@;Y7?-JFp7S! zrop0xW5p-|(3d#z5rTeW{&AmalD6%(+pbx?diCk)8J?Do_a|vFe1XsQ*kg|@Wo!Y% zhi_xuDB+ZOAzB&XB!E(o0dsKo-FIK6*le%@$I1PEWndyb4F>S`+iySg`s=TM0&@u3 zkU1~lYbi@3s!b7?tyLlV&Wl_=Nba-9d}^8RHmOwo!Ytk9*ERp696;$}!Pa~(_Paj+ z$6lY8A*IhE>$>z_ZEbO#x%?#cYEpS2<>n|!l!2ngzIu=EOzQ>0ZNA zzpm3Yk$aP5Ko(_JXf^xP%920);SXQ-9ijcfqbqo!HlRV+u$)-96a}s2bRe+U2*dXJ&dg_hptMkrG-$ND2gcJoYXl|pXC++4-nc0DvCwWQY>?pwVsC1 zGm-OVa-)^=yb;{YDTTv&pz$DNyKXXHIR=*0cH3?Dgai;zNcZ&hY3{swX>G|FvaBbO z0RWN@t%ho`1!(O&#m`8`TT#DKzBmTL%2qN^o{iXnQc+uLoFFSOsM3ZX>-40wegA?B zF1QERrUJP-pH|lH@YDdg6n!mDlbJPGEUB~IaF`S_wjzobm& z0H<_JZ}CNx+Z@mcOq4^&tI`~FKiC&#SpVyD07sUNz(^pY`^T}ZUs`|jTS;dBFc|{~ ztv{J6^KIZm5+En0GCH}7Mq`A-CQ*+G-BmVz-+lL;XkL_90fbnL{6-fYr_PdCHT;In zG|vt3?Fa+*xAaM1{3r>H2iC$UZOCO*81qojin09kP)k}AuiO)?AL^lLQY->+<~amF zqNtcts)%lBk}d!e9mf950j#Mlw%FoD>AC(!`iKOyU!U^OZ%fbR?gAoNHnyyerO`Mi zSvb&a{B3&S zuSIz4esPWpEEvnL*Jr-v)<*9A(i-vL*Wsy93)a74d52Ke~T1SSY^guSh@Zi6(mNOFH$K_lQo5Cbe~ zVUC44rHwz7K;x|R-Vsm+|Mg%0bz7P%nAta@@GwVt4r;-`9K@1ExC2PGZ%_IhboyS> zqBpto&O3jcz6D1Esdis1%fQCFbu8(tL-nAA=_0|s8&9T%1dkhOXGB0ZH zMHRI^f0zE}ne(im;E`i$b5f$_xK}6=6h7vCQSYVA17PkmOIOLiqTk}2q!`Ka5Y4Xl z)c8;H-&!%cCgcjQqQiV}l%@A`d4zWD2%G>H)*bU3%TaX#xtD29-=04E_av{=%g;kK zQQQ#6{6)?4%A;P={wIF}CQ3k!j0-NfU~=(TO?)p`JTy00eB1zjPj#0f1EIk2xgn7# zBCH@?m-D3@p0=}3KKbOyO_q5p4T6UdSqNRAqR@7Ca>*qVD!_u_ECHu4 z!1QVF9eH01hsCFm;#}!_A1!1Tn5SD*FcR*o=ePue=7ft;)_q_4O8$Xyn zdrDfQe~^CXOX)ZF=9v;j%KXBrWlpfDB}Dor8Ed|B9Qk(y(qm9BO^bknO6RjbLZ0tON{b+1p*Fa9 zv`D@mZNLS!!EfaHvg8K-BaT1i>p|>G5&-UwP*8m$JwzslXrJ0-k1QnX&+%NbkCu_9 zgl0^hl-0tbRN0sm+^oT;>oaMFJP&IL=71%an~~%CEqF2im=m&Sq1>fSUz0}Ty5VP?WX6B@~c3l z9M!G+%{t?r;X;7L5g3#}uV(egdO(gT1%>la+itCE9fQC5ObrwP1gs)iNnRN#^$L2HE{ z5SD~Z-=3_YP0~92agTtm82DIgL49--t{4FY1Uvx=d`AhW5%9u6%x@w9*lJj>(0#iVmC`l^I zgOb4ie2!%cUBNs=pyZ;6`htz0Tgrh#uwe0UzGH$;tK!iDI<3lFiDvVk%d+6P0Q9nY zN_F2? zAX@;HbRPeiKD!t1b05m^HTD`)WfpegOV zeni%^T7Oj09|S(u;Yw&Kw2>MyML+>cOW(K=EFpHOdgCFS>OxjuFv6wv;7_#OT# z!jJCy#Wb$G^2+-lT;!}1jRDZwYbuwdFlJB((yi( z{{GdpeNgluvd=~Z+dv3EN^R$73O*VaUU=c;0qF)uK!HU<zr0{$ny2bh#v zF60K*8k2$FE8NJlxR)oLbkZ{jdayvn{=Vc;&BF1()CEvHT(l8u{p5hhM!(?*B}&kn zy18?yaqnaLeP$hW5CI{Dn%ao4NXGbB0(qV#?=9#cpaeR;>hi2cA3tIj6HsCcS^^L3 z+Eck96)~0-o6ED&_plT$y6B<_yTJPL{+t@cr;uaTGJZpZII=|Lne@EmXF*9pIaUCH zKIfJ5Rb_M(&ZMCJFrC}aD19V-|Mc|z&68Dfe!9j#ozh_!r|;h{9slI?_i;;Wo3{OI zl4<|%0S|ZpCKlTB57Rl`C0*N3a>}~5Vg!1AaQ@sY-{p7EoC5g(geZrHUk6JF0O4k1 z1w|+|O41X(#c!#9O$5BKoY4IFo+=)c>dblJ1dq%2T*cxeLH>8V;~jTp0oworUYQ^z zKTi&5qJaq3pTlpkKztaj)ztLL#6|l-GxM}c#K}9v7Ze4tF~Fy<=57VakgCcnaoBs0 zkV60fII4f=6{~qyGmg%uh{e;#>hV5R)&^DrMu75stRm;Zoc)nyMBXYqITBc4sV`&_ zz{9I79sM@X31JQ3U@<`Ju^+6;O*h?i_L?Q0sKY&vI`{V!_af#RlVeE=QB$SXQ<^fw)JDbGta8Nvj!Z6 zObc{aYiOhbpHkJ0d00JtES|`k=^J`&tx#l>YUtU#FzfHLv=Zxy&}spBbiFH}1ErQ6 zFGcn$!jNZ%LXM)GWY=|R-@i;Cv?_t(`RN@0Ey=IvrtSE&t}jlnRDC`%$+Oo_sWL2@ z&!%%bBgv;brxe+pN&elL6!!m|o(u0onf++S-JNGUuZZ%(oPwfK1?#Afg+X*1z@QLo zz8hH>gf;R#ewPVTYRTXS))U`TD0sA%+by@;a+kFKi<7DO+K+zpqs!KstPCZS+6bf; zO{@i!6vmB)rtsk|=Q3Iq+_c(H^iB5bOTo3$aIMzQ82*Y&LXxXtx$CCjvYr3QP(bas;Ns)*Z|F3q@yNdF7Q8McCFf%Y|0^4Fv!NTo#X>k)_Xf z-+gx)k9{a@&*3e8K5c}$e%Ur7%#Bo0RSukC@yu3v`J63 z0rdur1}%gpp{T4bY7FpzdcvO}jb4^cZJ=%No79%$d}VcY0N2A3xczqmuJo%3fTxv~DA{|0B9i@-Y{o$T6+_M9$gv98(Ie$w*j{;PZ-C zykc_kpg7bN+O?u*REECGlHqfxF$AQ(FYUYr3Vfyxo`8uQbzR`a?*W)hBK$h|ZUmNv zP}6@-$9~~iXPvbTT7_*8#r~Mkmc(h&z)~efP9K3CcGvWjc5gOkg9%yu9L^d@Vpl zRBDha5)Il~(H4Jh(ehwu)TYum@QSrMZ+-wIL}#h465o%6Si`^aJ${$(MSB5}SETrJ zzGu5hF0BnMJo!y;dedZa+uBM9ejX~5*Oz0ruU*k>t$9C|YT^2%!ITOX+H7mC0bd)h zvP>6|>oYd(Hioc`^gGFwm$yodrNC@S`dW2jKX(+wVuMGtNmG_XTKm_|yQ^W@BB{4N zAk<<3b^XorQ6(1L>s)IE?aOl^2N79gg@3J#j*@thOEkZPa>L3IAfY_q`$_VIHUbx? z?bO`;L%{$brI0HJ$UZw2_tda$JuEL)^#`@|%OO-+Z2|zVX#F;Rmj@$1 z65ov^&%xpHJ=3}Y>3rj^C-*)J$69(d?Hs^?|D}CI zDavcsblipvK2{ULJHw z^^&QS?gx>@xi;<^L=sziO;)hAddAMRS+U?Fw5HN4)&VzO+-fA;**t_wyS0K(Nzk`+tId7KXQTVj`98lJIsaJ$(2$78A&J?w zgl*O^%Bd^`pCjkNhYdduQL}20W-Z>_xk!0<#3LRtv3QhFtmm7+;s>i%t$HGiT-x3p zy`MH2nUTw{$$=G=d$j)L2I2Fm4a`D9q#4A+%Ly9w2Qe`yE6?K7_ibmq(%>QEhvIjr z-=+{#6#^^`zyyma3Fd8+h44=aIPY$Ac@!ZcKabaXYar3$RE;`a?V_>e&V2%^{@0}E z{s8Psq?>oSojzdH_x&*esS8xP?nf6$5|Evr{jkVIdfPm2Wan;AHX zPE3>X@bk|2J|CwrNe!KC$C^_UjVkRXG3 zC7Sj<-q3T6{&s6Wo)t@z%plyl6?k+nVNcjft<^06sZ~3{Nj4(4WL?>o2<&^n0S8Rv zpFj>pc$&OU5l#|AzpWykk>QnY-++)o`ir%MV02Wr$AZxYo#%stA1bZ&BF;PSyonIR zzarkX#T=xh1p_OE8y^iDOn@?(1o9(gS~vVw+TQWH*S&5z3eHxFhH3+hO3vm8brxx6 z6?xCU4=_q2inQA0Jn;XZ(M9XN4F#N8hFi~qcI%?0y8I_Cv{ z?qdOUDRZn5WQHv+n=3CQ@IX%CBgN{Gf>-nLNCAYUp#tKDhhCXZ)tTco{~`R!*kabq3=18 zYY*UslS;;;8Fl3=v}&qDeLk%g#VoY`MOqROAgD#IjrAL|wtS+Neru(WoYljcR?**F zN`g&I(0ag;kDmu+G#6T(J8$LU5LS(x6Yy{hWKgn7zMlT24Z;8QzNK90qtf8qd_t+u z^HFh8J2r|uA5dKnQDLA_M8%4L62s)gq!kSpE!D$5IPnmid|wenUB8c(fIkS0fiP?N zq0;-W{NfkCc<8C8p1SP7*Wh9Dd%1dQT?G#NPz2Eh=3i~ZoiDeOOF&i2Y3`x;=#^0E zx-XqIYu`w%LO`o8DEeEi=QV~ExhTKS7&$+seppHd7jt2Qb9_@l2K!^BP{ja-K#Fab zUsnmg1Xv+w)aXs?FM$tqSlHxhr4{l2WA+V4|t0`Q}kPqLnNOB%v^Xaz3tvOQpI zNOcS7^zru$q2x-!u+$h$i&6{C_lBP(;EZkgEJB#h!`IUn%vvJu=5haKnP}QYwXrER zBDW6wI$_PD6eClh+;a8sO24gol~_2)CCM_t1UZ-F(q-u@RF?i>Rk%*&O$~(XIkBHl zcn35+7^ws4hCc3Mo!D3m+XB{>x}N)_-Mt^K{jViuiN-`g>Y?oOD$z_B0*)J;#zeuNgUP5$&R8-Le-Ob0+;IFlJot{#?B}NKz2~2Q{{BSb<@`KMB=1j& z_20u1B~PJ5zb@u`&Pw$gjZDy$9sJ$CI)^NQY{b~%xsY`uS7{vfa9 z14cFqPS+Z#7PCQtj|Nu8?z`_k3I9WD&_&Cyq7oHd0}#VRg`cDxj09=aAH;*eAB3|F zYesaEYu&JA+CKP!4}4%1z8`yTqV0T(glHZ@>;J7_6J3-(hoPn|>zb7r-ID(^7S*<> zzCL3L*NC$`_Y9&GS{J~r)X?m{eSP{gm($#5KBI_Au`0EkwgMhmMSua6l`2x?te)Jt zXuTo3@cCHq!Qx0t^^4PPH}$=T`u(oZY?N-F(*qc7RCzw7BvUF*13~}?J{4}16>3a| z$i&BtX8-%9cO4Ee1{#ety!w?XmrBSgGKz=y#+ z7VvZ_U^Y-kejZil_l!bqZL($nJVLCu+;Yq1qMKCAzkO}CA zP;eU>_;gWeeSAKxzM-yt`uKJFaO?Y5(S+e6cK$C&rj_77u3@_f|LZbMaKs+Nx(On_Z> z*=6!d#cP#x;nkL`iJ(2&1FuYw*47P*KuOvOfb-6aFTQv+(Q!-+OMS>O8R^pz@#_qs z;D&TPwTr`slIDbx_W_B5RETHEH8aaZlARNI?nKzn#nT!I!n6 z*Lb^2A1&$f%P*gVX2XecQ@9COFbMrFe+I(5o;&%c~=GAzQsmMz8|a@OQqFXFz9a-1hhc!=p5QSt}hs^1k0zdl@YN( zTB;kgRy1g_cIF-N7Ny`KU?j>sH=lFY1@K^@K5-3o3x#Zd*A-Wr~sXq$=JP&_L?elq7IViO%@47$H01GI2wp^8uTtx%< zkYPZOL*CEXwPyYC_&$PNWqHKROsfz)Z{Bk)S4>n?`i4FR6$X0z;~ziaC(k|i+{pvO zf>9e_{*OAt4msqI325QZ(Qyi`8;(zA^#f?}Uu@GeS1uL4}#r#|9KkaJSs>!RRVsI`^W*Jo@iV{EG^m($fs9o7J5(Nzxm zv{)#WzbXo@>H0_cB_h|#o0c`@Bi*3>iOxj{mZgOZ+H%V+iEuk@B`880axu|$XgAuz z5_$INPk(yisOvijNO=0wpFYvDNe)FDfkwj=hMrTjR@Wdw^@&e>;^Zc)9XH2H-+%4L zKK8MD5ngDUN2$uK&wQ@YUtMj8YnkaS8SGsYTx;P;ZD@$rx8@5f`r}&)E*ID_PuixH z+L@KmY_Sv~g1!ZGBKbipHPN(X&elG&)|whSMuA6BYyc1WJYoWJ0ZY+p6qe$%X&byZ zt(bDmqSpoTG#VMaLD1Ls<`Y2ycS_@=p@ZBT00&2%%JlSU-2kPx`R1EH{d?d0-g`Nw z&F&TO$W-^1N-apBR$`+Ts9>unlr*W99G{jPAp0Hlm6yiXBK+WtS+Ke0S&_e2dZKA- zN7FHa4_X2gT7P$AXszJaiq;V87kb|2=ym-zN)*6D?jOo%iFK%1I=S`7x*&)OcLH)o zV1(RC3i{FM&yOwrbc|>D*nfyz19%8mg!&pXjy&?nr2`KQk78P~oM}iT$ETGb&Z?UxeIY-Y?Q9&)yh1LgTDUMb_KZanN zx3yIh)U9CK7c3)1K>Jocm}h~}K1zfy8>K>_xq59yVKuFIPe2mDgR-1Eb_-qUi}tEW zuiitsn<~67T4}<&5}38&fsY2cG|*_!XH+-nJID}z%2S@Qv})C=357=HF#yDd<4jt6 zW&G8Te)OYDuevB~bx`d#B zyOvuOrFn+XR%2T|eJrg$Kp62M_Mzy;@`bHbDpmek)-vGZ6G&@=?_sf{%wqXOYw*=5 zZ2uX|j3Q>0us*G?_Ue^%KVg-)2OAyuXi#2<@;3N`2ukQX_S|#Ni9ZO39w3Ad2LO^k zNI?p290Je!`q#hy5eh9-Z8$~7__7o^>%dR^E~=t$NVW&TF3T!nMRf@hEo50htpdja z&=3dr*n)2!f-?ddwN?l%=N)OMJrs0Sf)6M{S*m1?Dm6!aJj`)4ecS@b4+~f=5s0<- zSPud?4I`U&SxJJM`{*O#HY)I8@X(>+c<@v=7%~n$^w6bW`ITRpOdg7sS}HCBZpWW- z#u*cbqBM041g))J-C~WDXsswk6ZxI!??=z8)*r?$=vDyIcV#`b2>wMyTg?-^`dUko zXtp7A-8`(vSq!x|u|C>P0X3~uKne%ILsI(C`&If}9Yk#l1Eq1K@r{*sKO?m#v`L{`DP1!BHiFAP1;8G#ad+Z++`q_e(%@+&};GKd)Cx1}#Y4 zO>2P#kphmLAkoK3;w#P2%GD`tDB*xL99xN6$G2l+U>fEQ zJ`5(+KKtx5sp*C#G-Mop_~G>J**$4F&m*kbMz%?)=E917KbzX;xyctTb078VbbW7@ zg4l*qTOUyCIjBZnOD1(!7;x+Lt<-3(p;fat^nN~3ictO?w$^!wk_pv@#RJtgM7b@^ zr`+@8obf*s#%+~1{!W(H1MYL5`#iUFe9}jJu}@zHD+fl%&YyzMfI%oVmJr=U34`o0 zsO@ucdU(etOX_aad9&1!grVrANU_hZ0-?3ReHz)ihId|98LpU2gVUO2_n85Mj{14FVLR-48ILn!o?vPr;|hp!*-?acGsG1hC-v zjkF!~hBv(7m$~@@8a;S5l&U!uYhcrr+?k`uh5$pP6i9nEOuoC<^9(1%;sRlrhkS&1~IbTJ!k$Qm=8>vv;_rBH6iMg)o z`nG-~*N9S_#=icX01>a`dDZ}V=&c^U=gVaICUPaKZ)2eAHkrjiX_df(BR?oG@wQFd zU%vhAZ(r`trwPpv@YDo+l;UaWBv}Knu3&;LYbV-6v#TliM+@ep|AyY&t|$EdQX9#|yU zdn*$fFpbxGx#>%WLEUjLYR>{NA_?D)Ty4z~bFncL7u{r52gd9sKM2#AXtv7uv-D8! zdDT@{t)pR!I%4zDZLKDD3)uMAoRhtlcN4Wv`h4EUpJYp!D5JcO|3#F@hSIus9+ci9 zf)Jt>+dM#7K?Nu`SUk{dEfou_aK-mQ?w=xP7g}}u;U47`|WqV>s{+4Ia*@X$lufIMw3H<#j|tvLm1}G=IbYuF4Uqrd!|RNg?Rs@;4MqXjZKi`dBZ~ z-(N)U9#69=6k0S}spWO;nwf{}Pnrcj9%!vv+v~BdpAws)+#+O_54)Z!(oYp7=B*yC z!BS})#|I(+u-?~`1Dc$5!+tIG9RF8?j|Qz9Xc7k%hek0SSJ4BA$tMlShgEmw^OOpT zcJ)fBSyEk;TFwFAT3oaShB?bB+Jd>}uTq(kz7~)LGM-OsRfLsJtF_h&i|i8#UTCFU z7ac~e5NbdNdPyMClEcH?K`@hSP99Ac@XXtXhJdHK3RIY_U_ad#G`X7x4E$9dRQSFH z9{|gP5%LfakDTkq(#vPA;G-J{6^Gdf2&HlVGtWG8y&EbgcbA3I63G%3Hqk<&egE$| z-&qNA-w#Mhe?xxLQgO4eel{dft5w37rR6j)Ad*Fcg#+k75%ITgYS<}orj@ConTU)9 zc*y4&)8ZL2*DS!|gTf-Sk+=K}gOh;gDBr0$2S&dB8onM6G`SuuVoPIp#GCsT$9~4+T)# zbHbWQ_v20J&wVVYc)yMZiyRo)=-#feq1SR-5#Xb-%{JQ{fZ1(nEOjaDniwrPJ~jHQ z3mj@-*Rp@)+#`!5+Mp~_g+Udu`}%>rU_n6HfP@GIwdgGUT3x?BpwDwTf=L?Kb$eHw&OW zFz`J(0@wkF+3Uq>^ng_gGT7Lxo<)Ptz3;f=jq=av4 z(x-CF(E0%$s5X*FHTM=7DjsE12)kC&sm3%aZ$DWtHt;}^x^@7z-s+j=4XrbBJ{^U! z`>~-%y)6=aezN73TmCYF&-YQ+1aE0A;QM>z`h7$~o4b1c)P6Vje+ykUlp<&q-Z?)I z8fsq(&I%0xNMZ8xv}EQ5R((KcrC56<&_NSdk0$~Nuy%ZX#TF%mYvGvCR9HKJ58A(I z+OYt4mP!og3D9uv_MG_}5KR`z{k`=);Ryi??1fss6Gtcrk zUBAIOvEI~@1JFU3@@gX@TWCNj9~>9I4o-UnxT=*5rP5~Q6DlzdYJ%y(gf7o%->zyHtV0C{AW2GAPf+(mIkOzm9$N+ilI8VVi- zzY+x(1+11Xss0|z3c@N?Y5buAutvqrTiYKJOb`*qLn7azq1C0}gq9pQC5d9)05${@ z*wE=d&)t^E0dz>#l(iE98o$q&)vWs#x8;7|o5qi0Bj1S9OMv}H>Ca!+cerjFy~aKs zjIacKeo$$YiYadlxju^qpEps>S`SnY6;$`uPc)gcHOVt#Ep^pgiv%x3YSKjtpO&tr zR+p|%fP)H?BA8m8%n=ZpcQ8R8ORKLxVwTknx_%>aUYd^xK&T&tqsrp6hE98H2f)H2 zP<=M_nX=k@i{)1WtT6%)qV*fq-r;kf``kLYZM;t%%QXH%pyK^HglqedY!5u8`^|4| zC``eI2tHrI9OYqnVQ#IT(oMJi8ENRVYOyec@@o0r=Rn+lYApdTXCwaFS9vm@uMwB{}W zx@gd`&l-Vftysc;v37Wl^2PO~Rc#9dl0_*B3IM4ZwS;tQ`JmkUV?D>dN#I-y(P;!u zSYH_LQvo}H$_~D|fv-r7j2jwSy|I+mSrOoK&N=5?eaIn)eBKLGCU6(^7BQ)F0(*=W z3(>ru-1l2Ss1Ja(QV+Eu-vF8z03>aB41bUpPIjA>MjK7;9J91dpK9QJ@_%fkD9=c~ z991Q-ZPNQH5VCX-oH*0*>mYy?LY=EbAGx+gS2?JGxa0vzr>e-HbDkCsJt_UW9KObT zzJ&t)eEi4-8RYVY1YR4{SSa|wp(4=ILptHX4}S1^LQyw6;*;qr78sdaQEDv;DlI;o zuJek5Q3NDgf+oCmiKd4J&8BXrV^DD=i)fL;sI}ri3s|*UFs1L=-~olqv!(zWs5K%N z)zeAzTi;3+()88w6M7B*4t@>Rs%>*$vIa{{@y5z)8iQgh&1bHgb`M1EDSt`(edjyh zIVionLg3+BAwbvpz=K>=Z|#~kCaY)R;6w7(>eZ`xP$&KBum0+KCa4cQX!V=aw9P)% zrfVS6I=Nf4*)jY<5(>6RV)XdAdk65~I}ix97ME-Vp}vA9BGkr8X6+M%(Asj_f{iRv zg~%&f%|@*aiCpu77lBFHsY*n!AqOM*-;by~ua$K0bg3OH;1`ef=#kQtr-J;LkCk)Nqzl z!%WXwC6!9PQ9l>`YvlS{?^nwlF43B`Pc*QFg^21kLamCW^yYFs86DiWS1}e^K1mcjrx!&@>*E}NT zeu*Z?{hvEtiL%Mj5V_C&;^?9+a=(v2LQr7xqp`AJ$U{*1dX)jmX=Tv@oEG48EE|w8 z59j{Sq|Lr9w}-UquI(e>5SS=DT>&2`5724cb6Xg8EHJi#0ti=VCVV?YZ{fGd70&ugzLhPvH$+bx&f;eP-+yr!B2owQ6bSop+urBw1l;f?syramVfJxlMK9)B^Jp!cs{!K*;Tv+b8!Mw%}>$ z`_bR6eJ6MP9C+qHOXsd`l7I|a7fF6B3~5jT9XMz#Pv|pW%(YLnk%S-%l8r^GsKt4A z+00oFCE$qGpz5@DK zy{01fyAMq~6nt{)7Ucl&fAW)`JOO|E9QWRPZ%m;B({@#>)sq`yC^p71cb^Y@C^rWMt2Y;A^;9orY(T`qlF`^Mz&NEW>rYlFMPi5?A>xI_ubp1?hyOLlN`9SMP(c@c8qw;b zR`}QoOi6Zd9{^=S%LNR2zG>a@#w&RbYBJgy+lOUqY^=bu5X z_{pTk(qT~mKC*tS;I;lV&ug*!&k*h#qt%-<| zQBw+Z|7A_+IoUZQg#o2N_0{}2jBVXmpp?%BfRS|!wZ`8$>bI1;7O`+zte$A|$JQ1d zL#a2r7Mahu3<#96d4{~d-+q%~*LOkbBk-eZsG#frCaY&cbX`QkEnNjeR9&~8p-Z~E8|jjilRrunRtGkiV7OA&1JG#4E!*2s_8u2MS`8i&FG^e>Jy>9gE1kL&+3kNMEz9!cvp}7>1Bu;cDkfF?$<$v(6LNMF`4(8 z>vvWYdZ7?a;$GfG!^<5Tkwd-1c}a7zFo$ybf?FCkeEq|LH`{sn_Xu7WV~CJ&Npp%s z{uI9m2|(U)-GZv-1NnKREbH?O`}54qKl&K*9gTyqKp6^W3aZEVYq2@T`VT_xt8MRn zBv582Y^PC+;Yf`b6r|(5;B}UC6ddJ;{iu#E-FDu;KlmZQ075*o>E!T6qBngD7S03F z1b)$*F`3TvjX~*1%j@x_+Mg4ZJ>gFARzG{8mvw$nE3>TO{H$G#*mfE11t1A7>E)aO zWMALAKVg8zZgCpmt?S6Ij}MdSjh#jzA9J~HazCmQI3dbm8+k#MLOsw%Z#y>vy+oXQ zf-{J!&prnt;nMqP&v;Z0gh3=ow0X^$U>a3Vwck^kMS3v=+f15C#-EJnRk2C%VY|*t zW9Z4J>EQD3piic@_N~?MuDh*xufr`PInE!j5TpKhxJ$KDD_qmiB?)gs6`0W%qI{N{ zTa7b1q?GM*jLn@#J-?@_y<{Yi8X-?s&vpKKpZ&(-1 zEu^bQKzMRJ5Rm%}=<=u@4)n8K5w#{CNwj@jZ8-MmMP^V;+W8P&53)v$$>ULNMo}nC z`LN>Mvufe`MxJ#?YtgBPQFd9%*d~{(nig_pALjQN6V5|7h!Gn=L;E8xXxSGtRHpTuZ(}H$_ZWzY@9k;IZc85 zJguIDuRucUCfev>$hR1V=A6QOP|Z->GPw&kxtl(HIXUH;eZ?#nb#&f5s~8&3&m|Z! z2|AB4`N0xQ`Ghd7{d=fTdzE$`EU`eLsj%Q!D0n3d?*cdmm%!~;fUVN-RTYx#c#t*R z-v^_#Ip2Y|4K$R$x_MPhG9co&zU@uy)Wk9^3pOGEH8zx-qKaeP^mo;vt4?HJ6)>*TaU$R$>?ZNp~n4Td?VSqCKmHhQ?Nb-6u zk^D@1{b`5)wx`%^pWkYm<-P%quyrDl&bJb^z^>u_qIdg8L~!~OVx3#f1CrnNt6>R; z#OKFojNOe;4C8lzl$?qna6CFQ(S77rUQql`KkORtV`254Z<$`9Fq5u_sMVeW@4;i z|H2#!=x*bFMv`7C7P*^RhZc4*!AJ=sE>PKH-diOr&l0yPWHvj?Kl+R9&e|f2-j#08 zx|d)d0Sds4PvkEM%%mJ2hx=`Iv;9usbjfd9hS=<{v_ zgT`M;bs>uXvTl`gWHIt{u6WvYEo7bkNPsvar--qGQNB^a8d<-#AbGA z&a0d!5nh)6L}gs4?2u?<{I5s!#a|h)c9r!=`>9leT7}lyvV#u-WpLH0ly-+SbT4Fb=Lwi+wYz zT`Bw%wZS0-Asyof!@C*v+C-lvx{o4`OT@tocr6-!05#iAG>bD7!!8GorRMo&RVBoo z$Ha#Zz~1Er5~fXtd!gHepTAO<+vKKUoT?eizDFqj<~BMLN`95IJ z;U4G)sOFz`0U_g*;q`onMnR%KVqyp&i4_14&CQmu<+g6_gdj}8-8UqC(_{>D#lzyO zE*13Db;-4Mwr`n-lU(cyBagQJ=wE+J5+#}!&PdgLoZB3wpt0$>LZ+_4SMCpCx{t71 zkI@xk`}O&89B{bM(BS&ygh=Yr-5*i;;0-JYFOGK_#=MdM$u^O~oUk|_R^e=wY4U$6 z$B`}uP0=Z+QYoYx4Tn|jou|{la%}ePkF#FS5_s~qC(KUjiiEYg-a!Vll_QkVRlq`~M}$F9!sjF5g>x_4iUWetPcFv1*vvw$ay)heu?K7yd$!T=RENNFWo zMnn`ls1;#P8MT3xl$EwB4o)j2QgkG6`ZT3X)PU`mKDJ1)4PjS_K(oZ zR?68lRDWR|=vUKRbHm(HjTS$ED0=0tr|KPezb*v6`eox!)-U*~Wc3A}9P0nb^8OBO z{Qda)begYNMobeNlhOoIKa%djbAm_8SeZeNb`mU{iWTDl52Jb@X{An->l+=qv$=`* zY#Jwwcxb~ve>oF&d6QFq^#e1#44JA^ZYlxelZ15(lIVBZw=PJZIHdoGf9JG7h)Sqe zq+ddN+GuKO5;G?&TSJ3Q%3pi#!L1C#EOS!bdi0ow6CAhF*ZBRWn`TPWEw3C`i;Q3@ zgUbrVM~y{s*||~4dRZd3L`Uen_NmPOch<4~PIFTexr!#K0}}og0-eh*VMKbYCyJtl z3^ojjoUO~DLR8-(nu;XS7dj+-2ooSaRmy++YQ{(0xW&# zL@RUI)@64Y63IDP*9;$OTpEx2j$m#%pW{l(7T1B`8Od_3#Zc#sACdJt({K#`HtI9l zu7O|oe@z892jko+rtaN$#(l!S95sYsf?CnLrYiXQHT_?!3m&RocEUF`sAkF}Kv`9hVGYx(D~V0jS*2QvG0>>+H%v=9;q1WQp- zs=OFIxNT!bsc1YWKr{0hSp6qBw(Vrvzo2dJKImw2$u#yGa%-=Q5R}zx7EEY73?1*R z(ITB6lt-EG_?~&Ts~YQ)s$m|!l- zHBdUC)>=ECg-GTxwGQweujNXb4h>UFVXbuEo)CN+Y3atz+_!8Law(S|6O7f1`BjP}FrJgHKkwcc27Uqx zAG-PBYspz%aOZV2Uzyjde11)Iir>g69t+nchL#=DFTX_j-A>2Lyt!y&KVv7u!c9%& zN2Uwm`oR{gqNc0$eL2QtrSy0bVaUAo@k*orIeX&u5!Lw6cQxACSFLh+2B_k&|GrFm z&(U)IZqO#lI-!IL@onK|ZW$OqH=%Q@ZAu|?F=X{kxYl1f_HRi~Gwk1w9%?9`X#E%S zWfKfsb2{)H?i&OU)vGl%@`>rCx zr!Crjp*C8R0iz>GG)QTBv#5Fg+`ouwQ4H_kP0E03Gy<(9*jQve#+M^2FP34Ft2cdS zHzKO=QN$M}FPU%B+iKZcrA?Qgu?P2AC5=^&>uN@U{n=SQvwie^CoU)2^M*u|fk{Ul z&!MZ|-yGc$qdXX%qW3Rp>-5i?h+j0(hY5RE{~^#8s{mHy>YGoDL6T|5N`7}j9+E5m zlxNGoST42yvJcxQ8mHXfEflZU3V%)-@SUN|_Z*F)R!>C6gg#MRHXPc_a4W!?P5T#9 zE7B!gYjVd+XFKLg9^g@iK);%ndaQG>($H5nLYyr{+k?9u&XP$zrrtyOtzNzZn*KD% zsvojQQX*w#I?JfStl6t5hCOAC>?1LFvy==073KNx{f^I zlZ)Z5x`-~xkpPaj#4H&E_Kn9nZO)A^pYsmOJ0|iDFP}v8ZVSUSGd}q&UN_sFmm=b> zk71P3nq@-?-<>w+5d_TRGheUItxndLFRMH}Uil8XK0jQ`jRrpoZL}xS$OaA$z7!jM zzdoAY*c7;bolh&a0gmb`AP{X^$fLNDHkuGw;bIIIm@*o2J0Ix&)88$-TscjmD4>Wp zIL`XHva(Wu3`#5(_~d-lKAEx-gPOGe^mtLnKhnX6KHE=eHQ}oC?)sbO=ic>tMUQ#L zetF5jOGPWH0lbw(E6PrBgs-jTD%htqrup86$F+DjlVNek@E>U5>&dOF3bgN)GD=wp z*ka5q3#qp6Z7>j_DHSU%t~7$;#ZWgu z(TZKa;bSpD7)6@()?M(N%3uH$FO6n8D?3IGwMJIbdm}Ynw(&CO^-e~}F>upTQA9l0 zL7TUOJD%xQ+B^>l9&S&?-mGWY- z#{HHjG>gMhNWP~L&i;#%e0#K2YknldLhZt(B;UDkq)cxe?|B@yTX;cNVbLeddER-pO&YZj#z#(f;k%8{W|ee~T))X|k*>LF6ESS=jFq(&E#ti3 z9eyg7{+1z{J|tkT%1)?0s3dapM;gjy?b))Q~0)V^O_V5}9O0_`<7vonn;uF%!Of-&I^pm5MyM zy=hm-?O9Q|d0S;H?F;Zz27Wfc_SfEYclmC{@xN?`z@y5KrLi4;lDi5i!LG56$t-ON zuvnvxbE47+%jOGHpY6*x41RoYTx|+y2c-NpsdJ2{NQ{m|h)>3t0E_lGo{o7*BHooC zHQPW%e9AJj9J{?joMJ2Don*%nQS?wMn|Mnb=3H*r;wEY~s1H=Hn)ZXVBNKJ}vmiwe zFEgAWJAyvym@Tbj6KscZ0yx_trRTUuaqEDz0-{@VQx1qZ*%a{i(+f`o0WqGPm+6Itg&jJDvi0;}+?9N43~p25Ni;b4SaJ+FQR_CSf&rHrU)5>dg8Ou*Y7RLneQ- zB#;b{Im(~^NJ~p&uQqJ0V<)=LjB98A*f!G`I6lsymV-N1(D|+MwKTwcaib^HnZ1z! zbIA zf79u3M@Pqwz1;%lojSNF*0^=Js5yD+2`-I?Y-u)#2{K5Fi>P$rN_*=&LWLFQbd%MY z6Z7G^n1@zj?V?UYJ;kqM1OabigA5z7-(8`{s=<`cf7{gbk>VweHg8%TuJ)#boJ<1x z(u5rJOhi54vR_jLM@bSn(&DT|uemD2B);2Mt{Co*3-gTWkac@Z1>+JK@w^c~A?0%* z6sb;IbxUP?9zER?+U9!cVm*DJJQZUCacIG&FOIFRqn9i_zLfsyi&&SlEr_59Y@wkw z;P!^G-!lkRAHXP>3z66RD58}6-Aumx^D~DYMEdXUbip|1tC&up47(H%ciWDSSOL?b zuiF5x|26vO!^Gg=KW@)fVK+F&D9zWhA;0OA4}+LzueJ~1uIfM5PV+}Nys#DC1Rkk* zrL8(vR52WkKA-0k{=$~+%m}c7Rm8$kqZ_Tk)-XtPaWM(My5fsvf`ka}0^OjI=hd_< zg|&%^ww(&`y7BLd{4BQ9<|n~_PR*s`zDbOH;z(48;mBV{ZXJB)K*ubz#VTT12nly@ zJ=kDqsDmwzN|xvgbLOwMc!#FtJ3$e@ zn;+Y~sO;Z@fT$q#dwsrj-jIE28!1uCk8gC`OTBF58hgJW@YsdtI>@r>$hyv9bz>Z8 zss81l^MJ)oPbFuB+eXX$vAQTgoILrQX}v=IohTgx4haexC6R#3MmY|>B4xqrY|~NA z_F|_!ZI=?8JO~|MqC}_4Fhi|?vb`uSz2 zci*>wH>aai(?Km8A*k{a?_|GKr&HXwDa^`KDQ&5xVbQ)1>Kq5 z%pXNz<+zPm&u#1UpMa`WbIViE_RdokZXvZ^J3yJZBVVJSs5ko=86Y+x1NqR=TumpQ zw|DCVXWjNr01AN*35^M}IMe75^Yg*&8^k#rVk!gt5zpx+_!`1SrOY zz=OG9=Y$AT*$jd58=QW%!QsaC(5~bB(asKk#ahjK08>(Pe}`+5{M5uWZ~b^m zgG%sLk(uVB5dZtE$hZ3O8w?lH$KivedxR0$X473UonomWM2|S{h1|A=!)yWTi_UCL zi@~$?u7>S)BO^1Z9h^dAF3i=g%wZ-j1Gi#CF;i}FI)|rvW0!zY=LW=Y~YiY zRnIjn)|U-0v?_XzocedE3(V$0>8t;oSuE9a{dT=o$xj&^?*lanu>AApREo+=O3G<9 zxvR6ef7*ddv6N3MZf2ybnkI@@Ah4F4=6Iz0GMun-$JjaTlvW8VFwj&0XobF=E z@8s-3mQBRMpEzDSfWO>bn}DiU{$7+tyJ8Qi-V1(1PE718mTi!%_L7@hv(qnYVhz9d zxi?i3^miZ*cnI$H_LwV5oGVhv#gnu&sN{FJ6PRG>P3nnRLYvMKHT@jdrVnjdG?J*= z0m~%!VP6BykRTM2!0v3Vlkq!xD_s*t*r)zjRT|NHa1`Rb3^VO$<>hKCGE|E8QH~=x zVL6AjRNqn2y4X`#L!vRDRUA?bo;3mz_g^d^83d6){*YGf<87lZnO#_PRKxgU-RLR7 z8S z3b(zrRgklJ^tJedsG|FwMC6@3oq@J+ymW9WH$5+$E2b`N9}Xo=dM@%i4jH7Fj+HbM zDYk##Z?UBp2yqfJGs)YpvKDKEe875@O0-}iltdxK?A?;4E*-Ix)Gm{p;7s1pg zUU-C4!lsn6gTOUmc;|XfnS7IJYO=s`#bPi7o7?Ce2!57S96@2nncsx5{Na6Lj%NRw z_Sj>eNH>0K(EX`q*Bf!J?n0P>`=&L*c}X}Z;~VV9T%)6%a>i0+GOkNd$%3^@Y7O+q zaZHmlqg}}5wXd3G*3x^y*UwTj(s7?eMG;25?|eU_5&h*YO-wl`hfpp^3N$METKS-X zxN6=7-JShmk5Ov9R~QZLk&Vodr+yudE7(iYkPIL;Cwk;Y`a(|M;s26k&AjtyUOq$eq2|6qLSeZGdwj- z$JmEXkRZ`kbYb(6oGM6GP&iv0?xy2sTAzbN9pu{KP$mdV_dOvWTIBWqhY{+@QvF#& zk>98=Oh|7jL%wh(IcwZI(8$I=S(l1@wXxDf1q1LdQ4&+1QQSoqv zU^%0$_8b0`4udz;_f(nK&USVWy?{}=jv7it9e=vhf!Z5VS3fqn7Ia;V_=|=OR)&U^ zHP+KSbtfirj zST(SpK=z7>HN5a~HD^Hvx1Vb}COKsaiLpaz{-vj2kHdvyOW2I&Sszly^dEe4-_1nJ zWjM9eH`zRdy$ynEPYmMkMv@sW2P{^;)>tmVbT?T4%dj0&>*$The-tr#94IDUJ!*{J($qR+>SSI%C0%%8;dd>Ec>CENxFRG z^+r8PO_4M4$7^oBE6;Har}$(L(#}w=o(gS7+56MzTZzTbwr_$+Cqv}G-TA7n*%Ud; zYio*rGu2;LXz8^8^wd^;&&=+SamCBJV-|H`_l@&0g z>Vy%ER9Fr|Q{)w>I(B}7HIUpDeYxAjsp%#NQ$eCLIVLpYvXF_NxvJENt82&+*Hdj7 ze+h_{TfNKMZSnRacv)YlG69}`dBy(Y!^;l%w#ck^3Y&m9*?F?AxBnIqWO12z1Ae~ra>Z*Ay6B6;ez1>f$?xvb z%A-D(TT<|>gg+38d0LT!;!ErD2 z=!=%nyw@%lX7a!1y|Fmn)E1CAyJuXq@lNw?9`{mmij5F*Ppe}W+P2O5B2b@UJF9|d z9a=YuP5P1^YASxU18$H|_BBGRU`WNyM9S%YXF^1V%f10S{3l2rb(HALETC2R%OWk} zArrxpM>u**1o3JcJV^bN0Svx{-NTfYqXYl0{6bMe+?7pOqWf-_h|^puDhG-BH03QN z2*!VTkREltR?@$5?zbhoJl;i>037gM>+$4SHKM3PRH>aBL#YFEgyiaJ42AFdDi*9UFTa)0}tltp(p zy8DaAADSu7w{Tpc`A_Q5tKR-nAAoNJFLJw$QJkY?W@fH9kbK;MZz3fPI!U~jS`OG! zV61J3BbUxycEF$VKxi+ORsN9Z=Oc!evRAdNMX{gAsR$po%S1RO@SNhb+6w&7fYsB z^8E$b1*o-k7gQD_--iA3j$|%Q`LbS5>a51zfj*4tL@$1KJ()OSus2x0F1A_2P9eCh zCK4XSDD<9FlYNgUs>2+y^H5+|!@b`F+fMcO&8)_UIqX0C#ZYBj(P&}{$r2Te#S{Ws z(AmBGW#l!c@3DPz&a)oW^CyTR83%;)g@oH04KGK`4AgL%02tO=9;Nt?UiXT(u;=|n zf^3jVXG7)jdjQE;w8WBkjqHn3?@hJi<^63&Quyc}8B|6OCtKexP#aGO>z zI-0+&Fe1=>*kO9mhZNzfj<#3n+s(?gGOuOATRBEwzNVZ13gNQEhtl17yL7`!I3&P| z+Ee(md`mtyL;c3C8p_uC)&w4<8v;*v1WUk4zm&)>5q}9wY9)>g1GBp!kUld?wNkw_ z)8%)2Vv@~kzu=>2sam@@sjL-9GH&!3iKiPv-od#>_JkHC6zBa4*BNa%!Re* z7XBoeW774y%L|FOtOMu;Kq5U_;F{{*wbF>~DEx>wu=N?soBpvBK3zgIlvOP8F-k(i zoj1V5%R$U3`f_Yi_;P%8yjw4l8kG|_%rm`7wLxm27DF_8N|XZ@24sq8zgVS|(wMHv zrNF@<>H0Ws3q!C#0UUbc!GfuSif6skZ0=`8FuAxq)h2qM?{wO7)0)FBbiLjCG=bHm zQ~Bz!0h;D5oUU6JFuA#t_lwBjG_n{Q zJGUY0VHiDOuSO>oI7|2N!3~^)U`$K+Y+xU}=&`41uz|wI?Wqv?Eg&$;kH)HQ2q;TK zfiZvm4@!$mtiPwvaSY{>1}I8V*HCwZf-qT;DAYbuC@QdWAn70-qlZ7|ZMAxhYve{R zmf0O&;zjZ1Zb;G#a?&*Q@yO1U%D#*Z9>Q5T5)cm?-Nu+a=Zpsk3qe%1$dUr>#ZP$R~Ak+;gNd2@>aKWH3CdRGwQ&tWA%8HW6);C|IZJFIop6ygWX%s74HM6W))QALP;l5)< z>Vk8C2blxGPAdt6FV*^I(X8u7fPhMgCVcsUL8RKV~_58R)A<55Z z%R__r41Cr?qs961?^$~n2H8B=BD5sk6Bkj}EVCtP5{eNr%2J9b~KQS~7jT$>8`nm812~+XE(tR5EuO zg>tI6JH`f-W1ja7SZL!wWRD1O&^|FrKl>O=M0eu=5lsG;kHu&f*w!^0ZV#<93Y%!u z%Y%xsJpV*D*}he8l7=#xrR1$LlRyYLOuMuA(A_V);RY~}ddR^zAqdg9IMS_oSB(@X zi#9uG;o4ubx_$B&1^DlU(`O|hxl<1Q98_vwg7elumQYMp)weR4kPHhxT6PrNhpoHw zP4DqcUY7IZ9*C(yEDm%HHFqYR!O_qd3XBEqEWQdI{dy|sh;UbU%`&<6f0~!6H%RFa zcaT}Oxxd&xbZoje%g9wlkmpIIX6cn8KHe>DZk^z51vhaubK=OpcUtd!RyN3{odVTs zmDoYxDBmNCJ?!O^y-p)iUjFun1w~?_?O+X;)Xnd{`R$8ItKr_{@3mHU3`l+$>sLVTlBiaj_Y5i zf&AMpHQX;h=8Pj8yY3X?HgK#?lO-KK-|q@c88wA)om=hez;b*?miNhB+&>2CQjcs! zsAAI)T>-27cK^$rpU5o1WI3wfa8*IlFrtp*uVEsai_?J|rC(H}VGICHKA!9x!Q=hS zpEf-GU%+Nz)>jemPe$j&J618`fpWa%Y6gpf+*^Aa2|mp^1s_5MT+OFKkYMY!4F*4I zubw;AcD?QcP_Lp52Co4n1mQy;;ldm9BC=-OprYv`eBfDa9Cn_5d(heAsr<_7hC<5CA^ zVL4yUg$P4?rZ4RsQA5*#X%GzM#UbX)H?6)mx%kUKeBp>8jTF_yQ6Fb=rY06dOL&Ld z!wU;E7aw@^ih|Pqu~?X>Lnds$AODi+fvw?y?qj&Fvb&h%TC@%A5C=GRlRF;*a&qTClRL9gBJB0$o|1#^=?EPN3gI-mv zMr}MFQU2?d2$BH==PSSEpK^~5T-0bmvhyaP+HV!Z&8#>1TYA<9P88(27*XGVgVMin z{d4NtbTluG5F}2<4T6_RUYtej!bk;kc-B&Y5G0vq43%>z#m0eV*;~Jw5>B*~{VP5s z?;G3!TVFmW<{?-!U^j7?wRq#Rs%AFaC0SALj~_*h|2kc=dusWfI44O{1W~98R(I)u zlnye;%8>x2SzC}QW3Au!u0XdaKx`32^>v~fe{A`NERbwmPd*+EdD{IhRDj_TYp%O- zE8~pgSNK6YXB7$;(!7nva}%b$#M+cyH?t-xdbgzzemCn}`UIZc!o4^rkWM*Q*yQuK z%8M`=TI0=AcZnq?8sOANS5h$MeLb%tr2NaGRv&tu{?4q}^&a5dJN zuAn;Z){$$;BEi3H>6S$DNdd}0Dc5aI+}u@VgT|ku0qSmgN@_P6EK_rvgp4kP(YgsM z+*#NGtGFkID~9#m*rvqHA}I_EP=B{1)V@wegPZWUEja}Rh29nqW61C>hC0Js!&&Sc zv5+QIsUiY|1TBy(M?NnKkwk*S(|=<@lS8dS=xoxd_OBahYr>lY?$qy ziDX7-3{lr!ChJB}9njp$+*a-UXTH(N7itm!(1mF41*M7|*IH^}!!E{cksh(|WWxy6 zTqng|{p-Y{MHG-Q#iRNfmJ%-i_77drA!p2j1n@_%@1B^?-K|t1eUrSOg+9Hm39gNcYh+1 z!;)1|%XI!3wvKq+Sw-4eU|Edf^fsO&at0o$Jo8N)hD53CIYo;hakwa4hf!~WV7hTH zzfPX^a;Ffn;WidMIHG8GKM`Vc(u8YAq^J1>kghL|Qpp1eUoG-S8@x%dfkEsrLyDqy zSEjIdsdtH2A=SKXeX4E(iD6GbT3M?oB#izG4pd^j*uefe~_qu7!c?l5=KPOPRv-r+-i&(y4tR?K`s{Hg&+melbH4 zjho*`EJ_W-Y(b?Q#K@k{OF?~~-ylP_>9Xo5L_S59^%1~f76wa$VahOeqaiR5l=0pcXh|OHco}|6V?W|9qU9fZm&JJ2SpUh&r7k4kq=zrt7lf z$heKWJ2$R%HrwlWPvsQ0B7wYVO*=#oOl0)dfhs$?1}SUU^Z8+S!9|kNc{#`!658(= zM^-jjL>a28WuHNc*NTvg8l2EurbpN}oiDrloBduzdg=dTkM)aB|MhA&wq^n=`!MC$ z`mA1HwrX~QE~&CxxU#zmsP`WTwGkL6JfO?-vM(a^G{Yd})|`)l#5st@4SX6`F>$#l zx0*~h+AjR%oZ``rBrC>tuLu;ttP)fcP9J$YE55YG*%VX44o2pDR#k9Drk5cE^0}mW zwn~c3s5U1Be-L8$&sf48Omp#Ef2hnZ81LtKj}1Iy&ITueT5;@iOL5@y!?Q2&a-qE9 zzH!ufH6ZEc^xp-xJF3Jd^c)N87G!RV%_RFf}kLL^Go6ttBnn}ozYk)Uq#DhzH} zimFV9%-LEm$@^R zpQg-K+*D0-M2bjOKaY%GSQ_I5fGJRrkk7>yA_sqBRwz^4vFe&nYB^S3%LLG-^aH4Q z5S{xL2D5s5Tp(WqC8%YIA_!ZNk-Zsds?UE{0b*qmo&?J*nux3{o<*laBVm z2gmt(3u)P?=T)6)m3YFiqJI(GjO9k65Gvn_a_iHJrwIZ|MzTLU=E^>~eGZa-V1LG= zgbVAS6v5p^LTls!S=(`h$kJsp%7Td^(GBpIi_s$&3_Cu=obDxwPV$qiO|Yu)V}-sm z(mZMUx2<`fnE(AptVh2)9$SI(5UQq`PM(;r3sz-OmgCRjy#k{p*Ukg!Ux z0ueJ^#E%c<>9A?pD0dTv>n@~4Owm=I*(!)o$#_V45TRHoNBoKa4F$y1CKmlI>0Kyqat{3;qa#@Mff*rqb}XFm>&YR#qJivRa^qIH@aS8tM- ze)K)u91BUHsDS$OGPt?TpHMhxGydvMRv1T>`9L(-XR~gH%ttd0RkL6{KT4OHFJuBh zQ`DwqMFTgBa=FfMs*OG~ugO|4_4ubXXYFk}IE3&Kly#!Vay<$Q1Ir*n%tb{?m?4nB zj3X9D0$7yehpJOtIu`0}Nb65T#>|gr-;lXLM;F-gU~8_F#C1d(G-!Tj0XJ7vAxhiUs1=6 zb4}xRI31VqO^jaL!CJMo)H`>M!rXqfxh=TX7s^2v=O9qTvzeJwqFN(t#I>o@3FdjI@i_03eNiP6K*=iw~VvJT9|1XZ_L&W;K5gR^z1h7WyyoF1PDa)qeYi&gW z8E)x4u)wRs*Zo5vv7fuQRgX!?bWnj0a!X(kLwQBSH3i@Kf{;6L4&U2mCHeQJTr;Sx zusWDTg_0Js1;vm1REaStfDk}vd|;#=(&dC_L1nBU^|m(N9GDLr3%Pa(%)}^XXWx)c zZ=&91xG56SKuF3+7<`Uuhmy3hYh#uFWj;H2=+>!H7T@!r`1V`aY+1!+^u`Qt3Qlr@ zFgWT1mOhDoJZr%w*>hydrOzK&Q%H0AZy7Dg6ezokU)WWr*Q1*@1sEOU`ve6DQ1B@* zfDJ>$<*?#lAVc;i>Jk3k)c~g*ecTcH8#PUkws=@=w=fgO7I6zySVnq|aEG%S@O&7_5T;MX5$J%JY%bwCBgbHs4c!E5Q%%Lxd4H&I z!L<4s$uuxfq#JR&Kf9Y$R8$nS1O()J%jq8W^{?K_o0E1x2&iXk&ZukkI+!bkB0vJqLMdn!qlnyB z*b83k(ZLnCFBQvT;sXrP6jp4Mm>Tc(dOrvtKpCYciq9;X5T#NQ01=PGYWoestx3gi z0_S)_6L>JhO`F1ou&=wi5UQYhrR=}n;SyP(?L6t2ck?_uF$VW|dfoMNSi5MvDymIfYJh_hhl!l)5ydwwRs({XvtCpTp_AGK|&Q(t8A6WKg`3!p>QGR(wecU zC%JhYzE(v1si~HqtYIHYSypD1+{GmU=+lb^|JZOhyk0Kf9qYdw!^e`U?C^_!fO3W6 zjf^DLkg3^nFgytw*6R{%BqCbJQmA#{->gvrBAHs}VG+>Mxhw`_t229FrvSPBtLVx@WET)C z3|YpcApu&9hSz_>&L8DIQm`~ z@nLX++ak3F>5w@|#^1Wk+Cun2@lx{fkLiC+5zRF?GKAN$k;_;GE(8&jgCMvF;rr_J z^0eiWjE|8&L0c)O=g2u*O@r2J7yjD775beYK95s?93K)P6HcGk&*OrHCL+P zo&HzYWu?J(k(k4y9Z55J59gdmavVj9m?2ldQ;~q96>C~3>bFh9z*Xu@Pe{2!Gm?5 z&i`%+r_aDqgG{qSiup*!dc#q1&L}kviiE{k$Djpe^JPEN;2F8bTpb~<*%%SEX2Rxl zq5mTJ2%VBvV|5WAXm^Scz4O>ReYo1ctnTo;eL>XNIQ$0}!ua`J=r#!;LAqBu`r*p^ zk0jB_5s~E0f?yQA7_AABi^Ni>Vqv#SuhSdPcvZy|tt>~?JXp{i5FCF0xL1a)&Uys; ztx0jzsob!MKq_vcc;6Jkp+TO)5<$E@TpDc5D0sX$8Oaj#?v9PgKb>Y=setMvJF-kj zLY%AGAcKn=f%!xbt9D5g!W1G z$z8ib*VWYB{SsZ{8>+s(e!Om1;Pc?DvXJ8ofZTs1hmGNPlo31mSCjs&pA_|5188w= zcYsVSiA}U`A$S5R3<#95|5M%CJOS8V1I5t52Z5{%_S(%KA5j`tM^jmmde-c7s79F*TvBUM_)>y7~>c|nhQSo-i=jHh$Hj`(WK&6$&!)}QX8#U^c@VL)I%4u8{$@=>Iw z2V)M)VN5X+E5A@?Y`{r~+5~+7S)~EnK{|05RP~R1yA!DLInki8J`AyMccG?X4NG3q zWQzA&E0Uc=;acVD(u^v@sh)=m15*cY8DC8xV9@S9qwD8>BqtE?k&{-Is**4b`9CBd B%whll literal 0 HcmV?d00001 diff --git a/ScadaWeb/Res/newWebApp/folder.png b/ScadaWeb/Res/newWebApp/folder.png new file mode 100644 index 0000000000000000000000000000000000000000..ba71e3ebc76460553690d96bd847d03c2283a2b0 GIT binary patch literal 168 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XygXeTLn>}1{rUgjo>{e_v(f1QlhVEY_5Hql>o2pgvPwQ= zdvM{*i6bXu8hk(9|G)orU`)f|ZT}rwx9~RyGPkud_At+zsbR*TaDd?phlm(Mw}U9q OR0dC1KbLh*2~7YeLpmD( literal 0 HcmV?d00001 diff --git a/ScadaWeb/Res/newWebApp/gear.png b/ScadaWeb/Res/newWebApp/gear.png new file mode 100644 index 0000000000000000000000000000000000000000..a381353ba59a592e13dbed06a0814c50ad772bca GIT binary patch literal 8093 zcmV;OA7bE%P)?5Re9d$KIZ+(ykE)W zISF|IfrLyFpu`{uMx`#$R_(GX8ka3v4cb;(s|&g;NVRqqBv!Pj6}2Eyh_ECiU`Yg$ z2}2SlkI5utGLy_R^PW5Rb^3pMpZ}bF&OP_e42jWR*Wpgiz2}_0zx_YH|FN$|EEW?p zc^QTwB9Vv~AD^2tOy1J@^jpe&g~BdgU=ZOrbC@|S5(Vya2f#C}W*ykT-ODng-{)Bx2L}__$~C3IM!Avcusl zlAhq~?(XitE-5Mb2`-p&_&;bP!;$t4hj9v_cL6#@W$FHDYr`xwj%zDJT~n+J6y zNX-C%X{j01Fm@HG&8ob3xBq>Cn5Pip(lv9U1^a7nrr z(&PM_)iD$biWBYaQK)!Y<3Z2M&B+n7XV1B4Y8<*aQe_WjE&Uy&_hWSQW|Be}XGof# z^bHRWe~whRbYfycs=5L$AwTm18VDHjGuXrh3+CTAtEA)+<#19Tiu0zm28{ec0-eqP<@iRm< z?jk_8(f20()Jb{MfR`fOLHDZzfR;L2z!?@`Zd-_R4B+74(719AhOhT8DJj0_<|y=b zJplo*i6m}24L%QiDi)2|DmXGSEV*J?S?RZF;+shgKS-Ah_}e>E@}LFnEzE$(}Wp(4_Jv zCL;1&kc_xUC^dwN0D!Kpv!b-L^v2TCSuLbGAEmGD^xlhY5x5?ifjXMthLMpG+heNl zIFiN^1|n@3%AqKyBnfccK!1M-V6&}Nw8;?J6&0q@IA>o|BXPDVo%ey{d&tf{F|ek8CFO&B3C;tKvMU3T&QX zBy5#-B+vPGz<@1`$B!RxGj*ZBU$ymhB8LF{eY`@Gk;$2eUyy9m-{;ed1}mjNIt?>m zlT#J8;XNrq#J|sY_RHLc7INPe`RQBNP>fV;vS~yXZuoV zjVaYMY4-K?$6#Aof@jT|C5j4*ru4cr42+?K#sEOhPaD8sc=~3Lq^r2Q{oJNSix$yf zg#}bVsx5=Zk%nmoZ7{*j;9yRjdP@+9pDHXYnoH`|%@P%hMOTsMUEbN*DFC2~bCvWd zHFl&8>S}>*EkHm52s=trT(W+BQxiF{7x8S0qopmDiyKUPE78YXSmH|)N9F4stCXdPCGfWg9!iaZtbc*8Q zk((eT_#A+c;~)<@C{OK6&e((U`vz4uwBGC4jSvr|;UaiZtP$c66M+^NK63kgE>BloXnp z3M^dV%m?udEG8C}u6tJ`65W}T zlhy5`v{eM$dOAKI429Ph78J-uo0y2ocPoSxXV0D!rDdgCiV2(Sob1n-Sfz9TmX?*x z$Y_?9mc9DQE3dSYs9&b5ty>BMmh_k|yT}r%9E4#3h((D@FFZLJ5pW=ge<7_n5B$It zL22^xaz%cAfhZ~}l3cK+vdHq>ZsZFOZ!rW9EkWJgEG=c}r! z9-KFCu0Xg2Nx%w1nh;%JAUJSNe|HY$MFA23!>W&ukI8%-&PmmT8k@pi2$n)fX>M+w z1Q5iyq@+XwsuBc2IlkQ7Y-vYnQTCDW>m7CrwR)TZu67E5NC|XNs^Al9v&WAN6)LI@${qs$ST4{9xFOD zG>o{?6{mWokW}aqq9vcMsj0E00l+C*VY*Zgl7Is%jnxHk;AgqHIaWepQr?4sb3CkR ztDC<1RyLaKVLnt$O6KtJu%&)cOM-=qfb-(QvGE-E;DiML!}B@wjb}hA$wOBXE*pt{ zJ?_=YkT<}`3-$y90~opk(mfy<-J`pA|MnFk$=5)FVzH#=k(CtURY0B&1A$~?rO9^E z#PcLrIH$bu^wRwL7+G5Jq6mi;v{o(nm}0KHNMF9?35!mXtJa24+cBNY|p_qDg5{N+_wt+G{4sjcFFuuTZj zu!>|u1ZV*U3wxvI?ELIgCh&Bv;juxIVIr!l^Q3K!SP>TZCWn4%flb$fIZ_Cg5VWLF zEe^C19=X4-zxDE!D__7IXPAvK9P<0O zF%2~~ZFN#0Vqt=I)Pn3xK|xTO35=IPbv&rymekpygq)!JlUHUl0bD!f7P`I2TfP+`X zGhiQCmHlmDK+>VZhYwRInE2_k<;x$5+9BozCRBN_#l*Sr?1}M-o9TgN9A;sV0FP-# zdQc3H-E)JeOGK*5nx1p`+mSv;I-ME|*HvKObZX^i3n2{WC<@uXzxj9XdC%rg!qZE$ zbuj>RLl9U_|NRa*im+PE0IfE!a!ek~G5vsaz;e`@xgJLexCcuTlWX0E>B$B!aNa@{ z+eqA>otU2lf(pI#(o5r;H{be&($ZO@e9eqg`aYA+=RqgpiJPLy=(mae*Qp{;LaHi4 zAN&o^mAPgXN4RhyeY@0G#RqN6(|$fF)&M@1ESDCbuS#p;>zQO ztmHxJ;y&}tj#znl+2+d1%028fj12gMJaei>Qc(rxvG>tinaZFfuAo(0!32{AQ&pH- zXgQ6f%kU#Lr2q}5kqAd+89aO+8JctF&dGP&1KwY8IhAba?d`QgEpSvAc?QdID5k4@ zB%K{q5l2E}4QlK!zW9=;udjRf>Z`AQL6LBG_ku&4Oq;)ktPkcS6UlFq(Y%&CPz5-f zvS~2F#kLCQhLi`-ybuq<1R;Hp7~PorIS}@|n8?V;sB}&+_zewn&Qg@}D&6OKl7Qn_ zFp|a~1@s>p7#NsG-)}?${YkN?y43MiCj9KTe`IAvlx%g`P5id zMo6X3Vs=MFh)#qlYL!!-4#fS7J+Ff^Woe)M5r&JFy(r1wRqgrY*$cIIf!%6*Y)dJ7u7D7 z?J@BFYRr@Y&n+pLbzdS8`vy&H5Jub~-0rfZRh8S00u}wemB4ra_XqDp0z_Xh&S-n> zAkG0q?u<~m@MtZNvq?Oe6i^Rw8p@|Gmr`>`*G^x`!h}eG-&j3iE8>tj0U4P@G3sBq- zeTxJ`4x~9wTrn!aGG&OUCpJ>%5{t*)r;6JNySCuOw9dN&s9QBvT_h>Ema9viW90q$ zBuU&aNi`mT?VD+@N+Kb%+z$|NUsQmGq?$S7_Xm$vmut)n66|SCZq6+oW9q}4EHiiaO|4D{Z&zu$_VNF5YJUv@tpMp;txqU z{)|4ZnG!%{`@B+TdVn-F)o>=L)Yr88qxBRM!Ydv>PlO{B6U!~cW8T349CT2rJfxYD z3CCw}7+y)I<5@b@teio}!wLNym|^D<8E^&>g)1V`AEvSVH;wfW0eL^_#c2V|q`r>i zB|UbL)lo^Hs;eYjD$4{{2Lj)-By|^EcgF9zs^V5)OEjxJz@0m{!4EvSAf%d4C@Bm~ z(Hs!4rB3gn;JI?)9(08ktCSY)?wp+L19bjbn)^GUEUx>}g(2NGsftu@Wrlwk!0o?2$<=De9x$ex;fvAUjdQnaP z8=qm}Xi-ZD%$o?*=V-1iG>5Hpb*T(N$rfzy>^vh0sk;Nz^cir{#n1SAdNx|m)p>0o zCpFa^0x6kjK5$J{Ri#+4V1b~RR4iY<-0ri<@XA(z=+SF>N8%09+$PAs4ud2uE$&=e z*azqo0h#zfpuWC-YggCV=l1U1iw;kTxXPliG@2<=s2}=+Q@`iU7$ zz`IC3hpC5O>3FN2_v6ppTIWyMoXokhp@dUCMR3~!gl6}$WlL|S_iLVh`suIl-1+># zi4!N}#8Ir#r7|68=@Op0B(edA-6Y->a~IlE4_pJ4N8AgK$wuEFJPAUGD|(s2IX(O1 zsBa?0T%~EQk~Wr$P!#f+0gc(eO1l1cbX?im+WMu(AAfw`-o3vQ{r&xxd(X1fR(^7| z>7o#)@MB5>jPF`KFz?S)Hb}F7M5et|OK;e&-_lh#4-b!A12CM*S$Z<91E>o63C;HD z5tuuGa98kDW;-yQLJl3L>gwtqTD^Mp`wui9NN^IA<3%R|@VAqkq`>zOai~6&$Vcfu ztGt!COpX@Uqv@x;y%mk zF;}cuAr>!ge7U%!_{q-BE~`yV*)A@|_}fAEM9|U!AZYSMH2H_oF*nm{4AiWvt4m5x zZ*TA2Cr_SS3n|hUArs}%w@;vUoR0sdv*-MrkG9b633)<)hr9M1Z8)JQqq z4RA3K&Zu}LPv?W2cPlF^lg@+yTtnY~8o17MrvSz?GBR>^OUoPgBN>W(ox>Hq86h)e zUL3>Y;BlvdXljndeGgzj_t_sxm-dEwTz>gVQBhGT@+k(DNdUS>c|}EZgf3>Pfifmi zS&%?XL?_ef@^iR``vd7&O$5d>1j?V%N9U&Q)0UOHlA)oYtp^Vt{4(wV!d+4_%d*|U zDbxFEYlZ>=*E_qP^etpbyT?bF7wnt^Z*K14DAIHNdeWIDZO|T-Mhug(ENywcHJ(rjivYUSSj9m=De zSbO^kar*S>)?07A3-xs)EgXzi`S;WK*pjWprUtNKaY?xzEA@PaV#EQM}W78&;j zb9=CINP!?taMS4M2x=_vg_@l@bxKOh@bHja?70ndq|~8Thb73p^D{LMiSYvjUTB1^ z?6L9O0|#CQSluWM^!4?2w6&esy<)|3v2x`~(cFCCf5Tbfqj%hXyLf8*_G1lm8j_G; zN#US=IHuxYG|B5}1)fC9A9x&dK(;$YMa6P;F*x4m>rO6Ye!-(cBAiAc!|qhTr0`x| zUMBy>!5t9#PAP90!CiVa#anN6$Z=sYl;g>g_kI5P-*glf7JZK9^xV}~UnS0*IWw_; ze{-_5w3L{~V$s@q1RmGsfO@Je)2LYSX4od$9WomAOn{`ET{g zu=mrldv}t!zZ=GsC#ZlU9vE=%8izW-p#W%EwGWxo9FV)+!|Bv=cWZggOu7b4KA@uA z-Q9x1P$_L(yMf73Hq#(SasIKLJ9o;zp;cd$bc zK(ELK`WDfB0rYEIwFXps{`}zA;k8u1OO|WDt{OnXBAUozIOjNz2*;pGjYe8$;s=)A zt?KQgP6wQ`I0`JN?5nT7Dz8EKs3@1C?wB{I8?g{mg`rN0ZqcGfv1ZL`X^W?l$dF&} z8$C4K-{1c=lDv5^yvm51Qp((+PQX5|BrRZlM;T}?Vx!=#Z< z5#z)nL+Y>t3+w_dabO1SI<;XJ!bmFrf~0PH5-V@rtG2dA+IUHw8ebr%(O$ zn%>^Nj{`jRNcuKY_m4mrw)CNghC?4^Q^NKJL&fw1X^ZFS>LXKi%QBC{b2>UYL`zGH z=;`T`;bKKag_J%o)8GXFogw=5UK;0o8o_b5h8PsHmn#V_oh1{?e)c@%SA{29(nEt8 zqLnF5SN3#h4DZeixTH|uv81*Th_up|>0CM>bs7Ul(cXSi+AdbjIdf(UZqN0BA9s)u z1_(P*r4Nym{*$+LSS4G9xZzK30J)aUMrv3T&eKTOh+8;M6e;EYM4c9=@ zAaPJxV!NG?}+hWO9e9ApdJ_lw~pvrYI%W_NfA~@zuwnu%KT(aP4&z4 zzLCB^<*#r!EQA)u`|D^d6&Mhy$edlfcD2r#(~!sElMWmt6&q=IR}j)XJw3hMbnP~} z`FF^GcVTe!_j_92X!+Z^y1J%o2}s1vern8}UJAIVeCLgbut!`0barj3{%P6M7f)D{ z9M__jgjID*+B@U@Ocof!?Af!?CA)_1|JqErv@d{qv(M?%9UFi9+n2TzH!4srJt4Ur z)CSBQ*^;-&-4FElzfAVzUnwkl8CeY&bbJQ^IXdj|ownm`cdT4_`S&+$SeKWH%Q@<6 zYCt4yfG`GLP`yIOUHy&(#^pWue*R&llEUihYH-4bX{>*hNK8MT%eYSj(m*Qm!n$?q zZrinM_e1mM&0SPlS|-I89BE)+V02)h|A&`fe#MT$f`X?}c4SG?gL+6o4z2Qn1q**h z-0WNQoxF5hP>&{52feWPqvJqS8(c!PwYA9w1UOJ*(iA%r#5+gTwl2d;t|n8Q36pDt zpR@oUp+)&B{k!|30H{EWLyDfKmAUfh(PIyiRDFn4dv|_*!LL@XT=4=uuno2kxi(j* zWi>Iv?g2pL9ZBW#2L}hk%q?}Q;YPya6G0+1I(Z^wa>K6M~j3!?n& zv(MePeEITw>E98yhs5Ld(wNQ@bNI#FxefSjAnC20pD!@1-KYHPmu^YKFOu~4_f1@P z-G*4Y){YC_>SfW3Zoe*iuR113t&SZ#Mv`(!SI-IjVAkILf0YUowuDjb?R%Kot>XkrN zH{==td{<|uIdA^_C%K)N8#YzKAmN*C!gq^{i}sPqui3x9`JX7-xQmE?0Yx?k7cN}z z3zE=(p$KO@7LUE{IbSpY!97V;ByFjys}~t|Oy;4asCS(D6`8nQ7tW)MxnnUL;vDihjOKVdbL~GglMge|g@#c`|$RcBJn20HE5$ciEE5>}K!`6FS#* z_UthNq|vX~?v+G1ArNMACifjX*5-R}NIOExOo;&XI||_wWCl)n$^IW4Kn$*Q)+|X| zFxe?u=K4t`-vWRt8}8q=z#t-dKy;;xXvu5oO0c-k`t|Fq;{K(5T?7D?8WO#b;nY-h zW{_S2x}~|f`Ns9@*9)Cfrf#Pd;`s67a7y1SEX*H?${)HleA{+s&#p%#altBi$pM5a zAb|<$GM$njfT1Yj{%za7YY@fw*i~1pngx4=yrj1RhaZ19e!Q(`&6+jaU Date: Sat, 21 May 2016 23:35:05 +0300 Subject: [PATCH 058/382] ScadaWeb5: add favicon --- .../ScadaWebShell5Beta/images/favicon.ico | Bin 98182 -> 314279 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/ScadaWeb/ScadaWebShell5Beta/images/favicon.ico b/ScadaWeb/ScadaWebShell5Beta/images/favicon.ico index e8c7615cf93d912e05f498c79f70df437f0dccf7..317b50e37ee5b3db8e145b1b27e093279c5ab5af 100644 GIT binary patch literal 314279 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

;YFj!R%m z5|B^Gb8k)#XWu1jfu5av1AZ|$es|`HK9k(x-aene*4#nzO9$>hlY_KxeY$&ld!I_| zLQc{g<9~DSOV$}}sZGZZJ}!YnEdhDZ%&ni8dDB}3^N-Iw$y<^yy)^gQ>~p^@JezB^ z*1|lC^;G6klVe*vAILMIJtxS%c~!q#dux5TIaW52|If?%P49C(CAqjm-KFF8AD6(U zB#?Eh;``GQw?8Bp{a@kn^KiF&jjatdpX9y~_dPU9|__dEqVDU`D z-1G2d8RPz)c*nCl#PZ^nmeB^b!u`d+bJ&mj?BPFawJy!Qp-+qtI3dp%IIe}`5;!6f z5c>p+yAqFodt&@g!oQfO@Aia$*f0LK!~JZa=NTKjcQvv9zRVZAI^*D*B`11B`hNVP z$0e|g{Z9%;pO*3IMRGjW&BDELZt*|HKIL@VG1i&>-{XpXTXo7E$s;At^vTREJSJ^e z%!?h@#&HQ8ZV4>$e~pCx&r4i#xo3*FPh^aHc7pqBKelo~?pbvYd-h+QHr(j=IQVdD z{`mDsVEG)b8)j_!tLCoovKR&CNA53&m(!Hj@u4*&Hl?niX3 z zPVv8Dp4hWIcT{R=>y4tzJoj-|?)$!Paz5vp z?{)t;d^YAU_DB2|>vP*F{_8*LPt+~9#Kz2r!GGr5F3dY89BE=Ly7zwdrzIA@RPM*K z0vfLzO76%1=7T+-%zf#3CdEgBr*DlN9#I@E^8vf^9E_W1ecsEmhUr4jNU{b*-paEx zvJFevtuuJ5B}Ya@;QyEW2y6Uj&v41zRYv<%**2B@_zAOPG86U z<1th2pNoAwPh1{IuGRX`tP|axckY~(_M9@l3*>Og6KB4$p)XFHaaeso{Ep}R@5=oSH^?*k z&&qu|=cXgLTRDjL*vZa6ICA4)^`8$4#+6#XsyTurL0B9T>B|Iyupc^L*Fu z4{lEBb3PBXES4BODV%@D;QmFi&yV%~1<#Mf3GgmAY;yV%%?=c=)5{mygQK9QW!EAkGJFS5SM^^SjzE(rwp?sL3Ho_qX$_=i8b zD~FG`p2NNG#$$Tk6#HY&lYbexU%cg>8hNMXn(Kecxa9tanm5^3V!!Lh-#k6{>wi=` zaCqY#{!q+Q?2p_J`}n{2fwBSbrDPvCM1RO2&j+|Le&AO!Cb(<*bK-FuIJ)qkb(p*I zzK4IExv!5{``|h8^i~`f+pDlYhkxf!@h|3|<9?VkhJ+V+CvimZw@;nug+Y; zZLIIu$@hWmv}``t>bviK@4G8&@4q#%_j$&@xfkVMV4n@F!hh|A)?r>oUve46a{e>3>+d)moV-!i|lKXU#|Z2HA%i+8~t9}^#}QV;Tcm`FJ>vF~#;=d{~e zG}}l12f$?JTwjy+-S}YN!>XU*aBE9%nCAqa zow?gjxwgI2=ajr&LLFZSyP#6re9x$gdO|0grH|FN{`2HJJ}bL1pI z{wdeWvl@Oj7}zKG2^S-;ufqO#%vP}9aj)w%?#KVIC|)+dDu-emlDz0&Cx7xyY2)eM zf4rT~vX3Vx&vT#5=l)!L(jWp4IQ6N~MIeKDRqli0+VN7*j-kN7X% z=eF{td<`FvIB0+7+I}l-yM?tDSNG3S7bhk5JT>K%>@V_QzCV(mjK##nHw_oQ9uLBV zJWa8^4*!jPM(!W+KaKnG{v7u=?uUJx44Yyg;{blZ^Rv>Xk7TXcOTw9Qh?}}k60Rca z>t+1>-ND=2V%JyjGi=K|8|)kF!v03wKNt7I{fPZl+|L#o8;PyhU&bEdp!1V|di-Yl zb0l%H-D^jWyYj4q6Vm6KC6@iJ#2G)7So?X&H-B5+i~RZNJ6kwnfzBhQQald@es+`Xp?-%>E7aYR}WUg&b zZ1g2d3~U!-N9}o88{(dl>n5N44Uzf(%J@=#(fe0+XMW_P@rAERUhH|{f&1p3r)xzH zJCf(via-(<>`M94;O-xdeVhA->z(BO5&vV{KXO0p<9_%z4*>thz_732?Hsc&w)KH< z$t$yF;~|OZ?-aQiN17+F?vh_UJ@3Z7SLU>TA!~6yWsJeTJaZ*FSgd6jxxaDGnDc4e zFYnWN-sxP|R=D4>wHbLP_|Ll1-I4us)7~S-e_O%IW{yo?7MzsrH4>}dJG}E>f~yZC zU$zeqjN_*G)P4Ew^33`FRrLMx%t1Xi?@+#N&SeLOTqfF@Ywk*1@vjqSyjP9_KjQMu zV1E_&H}2UC_c!O$xPRpSVjur&mwk4TWA^2|%hJY=#2@^5=5T*5?|}S~jBQ^KyLf7z zU-zm@2Ap( za&`Db{`zuKeA@%#uiuuK;tIAeUrf$o3~L7YT--t?eoI`qCqCg*(e>{{Z_mnj@9Pp9 zUN3bWGCLsi#AD{ZzB>8Fb8#ciJ>1;i*k_LWi+yX2;f5V(qht9C32A<+)vl?s@i7!n;g)4 z6UTi;;^3Ur{V(0I8RxTw_y>9Ui^6$-;vTK|%dgIwMK8eG)_8e|`%^MTyjy(m z%fi|JtSxL3Mpxm#*f0L4+&|{~@&7pHE&j{>&G(NvAF(}rjB!5>$MxlY^Hs$;*}*zl zk}27eFTQGrH?~cRHn8dM?+o3qC`hjxDG&_3h4a|CllAFJwOF zVc`(>R&2@+mTf&DG3RY!Lr)FIzddcfys?-#qq)yH{xANGkCiF@r?F3S{l)$`*W0`g zf785w$G+3phdjhRlah(tBkNBTrzWY<`<9vpw_g%yJ__3d7cWCbo!aEO7PVjevu?ye@R^SNd zTAMb9d*2oN<^JMd+#}E9+!61vJ{G*cu84^m7u6=j^?ZUjpFG$=xgT$o`}wV6pPcbc z@!z;#>@&^x)6-_w`5X-Q7yGV-2XKGpB;KFA?fu<%bF6+~DxAM7Yf^5Re*JR%>2BQ6 zcpvwa`y2bv5nYYk-`EE?&@Zmz@A-Y$hf6Vm0tb#`NAxL&5q{en{C+f8`=iXOeRp`} zU!`sGMhp8iw#xsf<=&>RN__UJaP5bJn|<2Erd)$waQSAjU(AbdoICP*alDh;j+2ee z`3AN?4(ztrCllB&_mlm>a(}T4|C{B0J^=qa-g&J>dpF7ys&{Kir>O!};ONhd%6K4_ollvC99! z+OFX0i-X%guFS}nzlFteKfJMh+)qb17yhSlf4RTdFZaX07(i@1=K0`2oZvUMqkcM1 z*_XO^hb#X+w)r#h34auvT#6qRyeZxmtB)~Fu|96aIF1w7h+T?3_*+*tvWK3#(Q%vk zy|z%_NX9sU4w~<8+%x7o8~bb~=L5$F6Zf+}??Mar8~45;_ru>d^E<~X^cQv;?mszs zlIKRom*WZYByad%$NlhM?q3)8(`REJ+yVdmKi-3T*nr9A6k%G-r2fXgu><+j1uyy@ zUy9S=dc?l}r?{8D5!>M1;&m%qZEVptMcbS2VawB^f9<2=o$s3Zy6)Pw6UUR4@e)2@ z10(iV<$TP&%*}W01os<1OU`q5o@w-?VCTfxz%hycw0&1NeOKazD;(+&|_1j(JD!ALIU2++R$yo ze|<*By<%NC&#X_nBIDi{1v{r6t9c*XpRuK9<=ixSdMz2Q;{M_v{}lgY+>iT<^UgogW?~3tt+d2HhYWaT^ z{@MH#|Fr=e!H1F8C^s^zEokFhANn2Jb1om|8QUIm;c#@^NNpRd~RF37u(&RZu`6KT&%xU{vYu_wE;40AxGRnUamdYhxSvv zB_`~et73oET<=!Ky;JVTfpVT%U%NNYoOo6E^E&Ro$D_yMC(o|X_RmYa{NIh~$xaS_ zGjTucugm$A`$zo4%ar@+ag6&%?l1P&;h!C_6?M$vq~F2zi0A%4#r(M6V{CT<|BZiO ze--|XbL2mpOJ*~Cf;O}-^@#Vm|9foDA7h`b^8bi`9Iud_QtpTU z(FSS<&aEBH_1AZP=cC0z#eH*~Bkyk%_d9mve)xwA&pQeC@6R(_UY}>bd56FOEFFvg zgkL=$_?FS*Ti{>pvl;H6#{Kk&&(_8LBlj2kbXM+%|B?GU?w#Vlv5()^aei?RuPw!J zF>O1C`F=MZ=iJuQ*nhM5$Kj2AaKx1Ri~nMOihnl1*Rm1yh>I%2+PN)xYNzAmX-4di z_@8oq_g8VhSju&?#(978gqMdipOd)Z6z`EamOqJ|Jtz4(k>~#;FKb8GxqRnN<$N0V zm-~x3y4ouD!#@5uHg@i)`~E-T-ZgvOIqbJB#rt%-5&q#&Y5s?=8*`uI_!l=!`M=}e zDgGP(tlB!Bk7bYTcemu~f8#HCQZ`UrcU!zqw|+18H{XN%#l+;EoX`Hu!@oJ}4eyaL z<1rie*Jj--%Z`%+Iy+;iEBF9&ieevQopG+$7^`z#jr-|g9ryFcurbH|9rJD`=hM8; zM)3~(!bGoE?ECI;;oo?t`Ti0A<^Q?Zf2;VfU8swU^}G1*mK_)y z7x$NM%l+b)&Bp!WAhuHdPPg#gc;s;79|p&Hui8NIU+j;u54mlZ|BHWH*mRv$z5oXp zzrpf|_38h`e)+%H7Z>oiOYZ-t{X)AQt#jRWw;IM=l<-#_MjU;+MNu`x)wzxdy5&S%{?SDkbY zgGy~+ivQx*);TTHcz?RzQ=zY+hB_#g4T?*C%n*7*wmG8YfA0eyhw zsSOnSw&J7_`|?O+fctY_-G>t=d_C-6-SE#2tl`L7h@0kJZLbOM?=$8x4uJp7#QpdV z?=|j$M>vEVvTuw^7tQ&|T~2d8BlqL|RXHAdfp_|@uUUovw&7s#UmGa)_09G4)Ad*J z{}lf)iu+gb|2oW%zfJKk4pR7IZ6Tl07m&N}rZ!OgPr09+$cLL-$@84{Cr|R1iTS_C zeDBrqv$X3IGA90-$n8&(2i?sF7z5AE^^!AP;zd`zq4}ze88CVna2INpWiR{!$mPz&TZT?$NkOq z!@QW^c2&F&%lOu{R^cB$TSn}+EpVYs!>^~R(kfBoi z*9Jz8-{}A1UmYX^|$d48O+81t8)J%{u|G1g!%FJ;-6lo_^)qXWdm$i zd<**{{>%NvKivik;{T5(@Bi;|&ebyhFaB+`&Kt!oKftfBi)Nhevp?pDFg&;UD%7C;nlLER^Pdn){jNe~SI`|H0sYj{ip; zkrCUT@(CGsj%<$m#Xssc@0<0W7v-4)XC_8CWY6y#F?b~ZU#{spAvw`I2Y;^@FS-ZI zIH10@+~3%zc*HB^U6>X3ko&s0AMO=#5TDRue5|NL{cyDk|D)e={kCDmKMpUik6b_Z ze~&Bw*Ot^fj(uHM+mzye#D2%W@YvXYjQ??pvQ_-|_>ueBlxr0GbX1O_m*$;uOaB+6 zb*)6XU)%%x#=mml*86A8+n$UKJ>Ts*;^eFQ$J%JNp8Gl<7W|&m_0QzJUGB%_)0|Jo zyv2XxA-1462Y-#&hqKxn{EgV}xML3g#kB1l=Ev`P?1=x`)rkMbKHAjSr~D6lQ~cKk z%Ktlwe|EJB|JuZcs{?+xMO$EgjQe5VdKha;tufDi?s7nH4L9BiKYlKJ*m=tJV*h7G zkLTh7`7<(}=6s65;tqbw{rE;9@41|hG4Hfk^S~DH7Czk!{vEqn{BL9fbNGj!DgV#m zf8_tUvG0`s%jLFXe82Aho-^{ldX5d0FrDbi?y2(( zZTHqGzNd?+{)+wTv3`gBwGHL}#$h{+fAzbT;=07f%7w-c?8*JVZ_a$!Lq3;mAZx$b!1c2h@Y&Jv zxwwM7D^D0$$NeMz#rU2ZpZ8%LDENi^>20bzI9i4Ok^4tmq~qeUI4!nEyifn{G2Qle zcD>mm_H(RrU0b{0Prt~l*gshQpW=TU`%Y!(`sEBV6Pv=ncQ=lOf57|Y{yqoIoH1YS z9^lx(-f-bNvnK981{0po>b-}H=XPg%wR8#k}@?jP?5m;a+r&;CqKXkX@q&q;pdmEp$c z1Q$=r`@bF(zwn^kAN+ufk?)dR>1~3W8>M*9$u&~sju!c&Y>zoR(PsCJB@gyx;f!BR z{Bt?ElP_G2b6s;eA9%q2bsV3-#$kAh@pb!C{5zixrubL4(%eDiMpoqr{me5}7jX?ti*FRV zgZtvY_rsb8vL2XR*o(Ch@dZ~zkC!D^ctOfPXWsPO*hL;M^4_;*tnkLn8~kc)8)%*6Mt*R;4)^`dakziV z|Kh4?{4*E(kND@0=)^Ov? zzCN+=1Hy0LlI^3T=f_0PFAk5sHa_wngON|i9`?8%Td*Aq&hWiHZpIgE7XMTEmH*2V zl@I%td!UE#_kltNa9{k3|EKo@*7xHaa;0~2V^ieW{GRazJK-}LXR#S$J|(uZFSc`O z?C`wE?!&QCx5A!ejRdM?5O( z{Li_@pTuXJ4{xIlj<%prc)VOc-8S~A4UF;sD*k6zm0M+2A6ego$KgLY?)|~rqRnA()$5>FQ9g`4X)3R(WPs$H@YEzeS`~smR-rUlD}9OCz8E5tc5;|5mWX>2bac1 z&JVBrb>gQVOU!iVrHl{A{z&{o-?@Ln^FyDIzFnlxSje4yZh;K(xs7A1X=OJ2nN zWH;i!+_hQ!kMU2jUw#|$?|*hlwtPJui+A~Le8e6i>&xP!-xeG^J#D>j{P4}v*KOrC z$Y^=)HDfR?G{P&vJ!3V?pd&z6WzyGxxj;7eRza9MJrD^U5{wo)@XzV2hN({2l z3B4$I9QKuR|J?JR*oiimw~GJ9s3SLx*B#?!wgD%2vi5K)12iUGe#k45rQ}@?2uj#o8`0Z|q^Oaa%C|lF0U+>7TfBN5rb0Ww}Piem9P7{m1ay?^v&+JkdJMBlV%reol^uMKF2oD=^; z$9z$2cVP<|hn$!1erc&p6P(L-A2>%E^(#mnP4=B8_{v@w&)v_mms;0nWzwzx-eP`&}9FKOH-f z8Ci{ZoZ{a&n13W6zB)2l#6r$1{x`$@z1C=JD@V?8;d2F=^LlinWpZ;a^;dQ^pTokq@Zc<7!Phrc?qT#N~d2b`jhu)d1_H^M*OsJvF;zxf${ z5?7F!!Ul{($GCrv`{`*MXEg4quh}a8i~SM*WMX{GPU8dqZ@B9ga`8vv6CX)$?hSZ( zbhJnRj8(?CN9-fFLZ0JCjtxJRv)19i+`m=)lRLff<`45BdJIBRPhu-n1dS zwkuC5r za#&-YbqZRx<&^l!2PO~uhl$Deju;_hV}Z@!zw#@0jQro2M1AU?;$M6{_1AJ0WDEZz z_iN8+XZ6k1$9B12yGHyM`|8tP`NZIEPx2Q(61iP-om>yaZ?QYLIW2MZ*=frnX087e z^Wc4LBun{(@gx5Oo1+}3_$SlKwK|;QAIFqu=%$5?6!VN@+_P*0NAy2ZZkci@L~~Ge zYE#OI8JB-c)`Wc2xKyEYMSsa{9shUiOMaDIap`<=DksBP$G-GOFLWm_Hh%bTakuL> zW^KN%*BQCrHAd{SHFzHFdENgb_MNM}>TBD@*yae5xBiW^^JWJn^lhA+c{}fpxi=qF z8S+ab_miV}Ch_C=kz=Nh$`SSzdTS|v)vp%&mGPASN9_AwJLGmIpS{`tKPh)eIcj)+ zD*lX(wC#Gq;7=vay#gl1C1Mmd0B>@S)73+mO z=1ur)daJ&s*r%6O+`kU*<8Q^jZLvS%U;DKczQS>DO?-RzsSFS0{t`RajP1QJT)&%6 zw1?bA?#KUAIg+P&M)I5DzkE{N!TuEg>}KS$^8YAvJYmc`>TjvX4`usJDMu~#=}r4G zZt)DJe-j!1ofwimlP`{e2{@d?e{oqE7XOu5W1l(xr$hBCcuNjw{D>v-kZaSGYg9j@ zeN`8|M)5pieBJ*&eiioB4|C)ZA9+sl#}7J`cyHasO|~b8!@n`{*Lyl{8o3|t6=M{6 zPu$Uuc*L3y*eA!1+32k@t?ssif82n>%wNuYel_3SB&A(CYPZ^$a?SYIvojXBgbm1h zkSo2zVewx%R+g*cpK?X{AI{(i|99+5kLnkM0_Bp*u`(<-Pw`o77MD}}^EK>EoH~BQv98a~rrbZ`zxCt);(5gQy8nAT z>=*y)Q|Pw%x6faNV=oHFKEHC?>8;#qWN_`wh1@UeeIJ{6<~K6;aK1RF<0X7C#{K1g z^VIM@e$1!qL&v`5tTFZ(`M>^~4vYQz@?u|ol?~aN!=L%QI^S8hxzi##Wc6ul%E^gu zACs8z)8uKbWc=>Jw~|fqKb2$oW0V_tkyG&rYxuuDyzx(UY0gmovmf!KwX(IZ>Zbar z9^ihAeb(W9{H@rxE%wzj#lA6yerWg7PJd-5m9XTUlM}1lIdi+;m^BVB%{b%T;hpn? z^*xoBIAf0c>!;;krej~ZQ1~uqP4(utDgGl!AXPh|f;rW_O8Kk71L zVB>(BW=`t0^78N}cgz-GcTSF@{EGj|3vP-{*z4GLj?1ijFke1?G8|dtl&0g}scpe; z@jPOD-Tys)BktE1wx;dj+P&fbpBZJgo&T5EI3d`-W!5EsUDjoNZ#d`A<8wb5KDeBG zihDd!?9Xw3V^!-tNBO1h#Trn4yRpxR|1tib#y-_!xu*Ighw={HihbBz=WUjAIKP)` z-;7-!YoBlsn>{5ud0y@z*xR+*Y(YL!%)?20#QY+@$-1z6q;C(6jDIct z{$MbC37M21V1J7LQC5xnaX=;er~4Zk`_u-evH!?lBlmB_|Kug#WQ;9e&Gxe0 zo9!#o*VEFL?TG1!o>LtKhYv~qLn;1?ePcAS4$kif9&uL3z6XPU{Z}d* za?)1$CV4vrcO`bXFxP!V`@LB?9WBRc_i_w&r^Iz{vu2iDjd8>xje$D$9q|vNt2lZy z_?N$!=78+eKVB*i7yqz7;(s09$KQ&5TkTQ@KQfK`r}%el@OokJ|E$gEYl(pqGLE@L z#-Wc+4(BIg<8Mz4bvb;KW!u#@*{=;0`<){hxx$!tDmK18MadV^VtfWDWmVk zUt|7)%`5e{Q~c9`v50#NjJffawa&4FcLx9W%J!Ix{ppm>#BJGLJAL{unbX^=k8(3& z5jo~5kBt7a_%ByXlDr%LG-jQ{|1|!e!+*KITlVLgI7+VC{bj-ZpQR1A7yFRKvG~#MloR96zCV4s z-1uHzSuRbCj{D0a<&bhmIion8!+&{g%w@no+nRD1pUQ`;7aq&~+r@veU;M#c`M*9VgQ_OKqbN?`@Z*%ykx9YCkUmfc2I{a5o{5AZ$u6v8EJBrP{A=tUql0&Cr zIwtm)_}>+so*DdIfP<}RW#8t~@%@PZ%CB;(&xF2xvf|0{!-rc_OCKX% z=(U(p_ePxW_hQg?%Kda&y}HgC{J$+e@vc?mQdTEMR$mYtJStrO(&UW)B7NH3Yig^y z)meV<|5QGXuYbf5tGIuRdy0Q!r)eG%CS9ZBwUPhl@Gr(3$G`MgUDjU9KelX6T)+nS zZR>x6kza^Ru751%ekMBQ1MU}JevYv{94eln1n0&7H1?@}$*cM;PN#moyf)(By#V9K zwavxRLuEhO)Ml|?EY=3J2`&}&O|f6B>Z@YkvGGMe7e9DCvI;kl(KW)e4^EEYH-kaX zVcJ{!Qm^xzUv4EI-&H>9)E4a<<%1vKzcL!fyv0B4iy3XnXu9qb9$jk{{^@SiU$L*f z)uZ#3DgMce%^4r^*-Jav7oPlYk;!$AW&G1^`k>q=y!KD&%ObB}tDodChkx=L^-D&@ z>Zsr9w($@ASEtjtMEE!N;F`6kVy0MD_hw3upW}XYEA;GI>`h-r9ON1oC*S?F$feKb z@JzNdvR>rxg4KQ6rKksnV5>0({QItaI`&1o8u!#c&T;=Z<}L5Tzha(pnmZ7$y52hc z^WF8|#s9S5|js0;H8nzVx2!69gCkO_Tm4_Q{I(w;KR?6 zfidtj_cP+Za)SRUzZL(ut-J>Rbn2Nv;|KoDv5-HR7yIgjo6X?fF>}~g*Btl5OmSLU z(4XX3{xW>`W#Re-_s1W8Ve*)-4Q3bE*G5|kHou;Bk({#bJtBdL;7M3_koXGD0#qteLD^*zg5SM8ToqD@f82n zEuGHAKlp#d%uaBBv9FEQYxN8>z7vZi#{FRCJ$^EN{VOAc)57`Xh|}Zqe3?h z$2|M!8%NnpA!AEdq!|MgAfF8Wi7eH>Ojj{HwvBme6Mo$}3|VQX!elJUT~ z8HfDar!p*mRBlsU!{Lbk=Ju-Na+~=?&xqu^@t3VJ zo!`m3T)tWSH|AkC&d0rq{n*`w;mqGn4D+M8#)D&X-l@drZAx#6E$-xA;1U09VC4SB z{rI5VuN|&mnKa)y#s6IFQ|y=fNBonWF(|%hG5^bMM*Nqz=&snOzv^%+_}33@u*S3{ zKH#E^yFKgV&v6WA&fyZBpKta^M4#^in-nhNJA}bPb2_KgV-p?q8fEXTg8> zS#ahS*$Ix_3jT}z;@&nGyFA!=Uoij2srP&0qrV`0d1^X18{@3gPvYD!iO=}k5&z}Z z`b1bNht}V;ALWDMznsw6r+%^A0Q<%Nl>6~N{2PaMEe`A}_!(c(S!1*E)`)+$S|3mz zt1ioDQ~uW%crf;G9di)u;JxpC@BfTUubud7od}Q0-_maD9&q6OQr@3(;A1=mN5y}0 z+wfoBsLZPGDc6`S;GL^?T9;!?>!~KnWihWx#xSWr; zpWkE)?g`Vj&ETI7tH;`{wio~PpSI+yXuoS4f8Z}AcKG+v-F*mlyYk(ZQg+}Zac|n} zT^g@UxynQI+zUXz<+Br9sc!4TV20xi4V9U^F*E#=KX3r zfXc&HMPhvGpYM?E-!=!CJ}h#|Y`b!-4;^Jk=jHzq|2Vt)9`Qfbv-|=4^V_&f@f{f# zH&g!ieX*ZdVs~`$@w`{$Z5f9>D;)iR^y5}({vxj5NhxNWa<`24{)+Ds%Z&0Uhmu8O zhT5g}H};ugA0KRo`-^}6GyZK6Ly9BK2biDLhK|i^r{ymCo9b}HfA!gZjNFg^^^5%) zXLMY`7QBlqws_-|ofP{at&6RuTqBryX2ve=<5`S(V-E{1fwRhS)OY#6_^+(U20p9r z#{cF25&vRa_t)^-EjUa(xsLn&Mh}S{E)DK~AvutTCI5X-{YbkHnY?0~UmV+fi+1bR z7)!%H43#&FlQ~(G`{%IVxNue6U%u^D3rAeZ>#WM?x(~0F4!OOU%l#2 z`$Wg|tNry^+8tZ`aBT5AqK7Sytqz&e)>OR2KK$P{`1xaF(9WY+gCp+=^Ns!I@L!)v zPL)r28~@Wo`M>(E%y5ReUzl%^3-UdTPkGX?fuJ|aAkCQdd7y&P27Ev*xNXi9f(8Vb)4TBWr+XF z8I@P#pDF%frE+TShpglf!WRd8H~%f4?YgaFv;p6_Mr6M)KH_a@&T`}Z^k?-IsDVLu^V0S+by_^-s>4D zeM#14@78{}n989Xuo3sy7nTpySNw~q$G95Cl@a&--}lyvo1+)^BoDc;1<&w+=`lw& z#{b%_tiu1u{l$NAZ>xW+`k1`vJF=$bUqxPvHRDGd>@y7r#~btBBJ+l?&zNbSag*Eu zJFwo9El=lA$WOr^mD@V}*XOUozi}@7^V`X-EQ-1Hk@NdMnd79`)t=bMPse9nKXVf^ zJv($?(Pw@y`b6rkscO665X3nvyprmiRZZ$D=L|iXOG;v|!)6&@UEq<6pQJ zgP7AcX2Shr3^~eglpEcbBgmt&=-79}KYc6kV;yaMwk^Hky*ciu57)`>f1UQ;D|H__ zps%Po`rMV+_U_3q{;hGR*oFNo+BfC?Vt=D$XC`5nRh zKQ(T|!|XurT|c_6aSs2LUB`UIfBoW=_nQ0R^Z9%D7yHvyeYP!oU@PVPZqqSrf7k?&!Msul{TQ#lJCcxqpm%4wm~z{ELC)(&$eyZ)0u+ zzNh$~#y%atY#0Az00Z@dctD8_d?Gsj&%xYH;^SAa%HjJuu|7LIG1z}_+VeMZFZGx3 zXCE)YzSw8vfBs;O|Ev2|+&AVsihq1ZH?G&B9sCaeS=@K4ZaNBv&P|;A;3WOW8(UMxSSmU{Kb-Y~_TAI^Vk_3j7_|GZ-kNR)yQvHwkug@3b($(mzaTZ+`^WCbCjlq9ngg;5! zZx{U@F*%B7Bg5N8ZhwVy;SmPtzgW=rt#JPs`%Lja#rwEl>^DCupJ48HEPkW2k-IjF z|MIxLjo6>VKRvM<*BUXx9uohZpLH6q%pB*P@h>bMu8(U)U)J}Wn(^&-CdPhuctP%b zA*XT$U1~S%m;W34;E!_1m^-VT!~Z({ItWzmdb}vAUdcKO30hzr5cq9VzzJGsTPRrjCo#mfuU<`Yn;)j;%>qXGgG~a#H4Y zZ<}J``EiR=>aTDU@uTiNyD?gZmzAsD*GR8g`O0=O>gV4U;M*9`6|v;FIl(10ls1b_=HSH9@{Se zkC>nS4-eu5KH#M?vE`Yuh5H4^x5^qczTw&_?n`uU zF5BQMJZtOv!TW8)hxZ949-i}_lX3r_B`^Jn$l~&jugWjvM2^K8I~=h;ZPzq@o5Meu zjWOaz_@_&H7kjn1x00PqaX;?U8_tM)KM;(4E&LtsxnI|uexd_*fP1c+{KD-MYkoug z`7<*cn3!dC5!m|LBkY=v8c? zF%td8HunWj7bIqUPq^+4Y5Q-d@2?DByg0IWR{no#_~UumdRNrX2TR^r_m8pdPv-c` zgQLZ}0vmIPL&Y3qqQIGc!+Nog`#0i$eqzLbd1J(Xb7F8i@_+N*?2+#1u?08P9!I>7 z|Er6QE#-4XIPsT*v%3d#hc%Bj#r|}EX$$NDPkmkro49RqZ(o@5fUKMN_N?)KbjB_( z3BUb#)+xU}vC|t8%l%bsa2&^t_@Bo99p6>YaIH-7KJNDzTlKjvtg!**$A0X`7Pe6Q!KFgi;%>*M zeS3nl-8ui_lnc|2%hHBDDf_fZy|%6aH_bbXr#t89e^@Q&!~Tf>a)0ywu3!G=pGN%G zcj9Dq!e(Wo?_(R-Tk+5TcHF@?D|EOK_p49e*q?SrCYOXGU!A!;{&5P!qi%n&3x0yl zo|eL&-!$#NS$z22Gaq^1*vJDjKlFX^A3vHrwdd{re9G^}R^A>v`OC~ZyeH)&v5~#( zf}bd+VZZo?{c@i8iHuvsMdV!_7W>uZI{d?Z%ZTd{@6-Q#T+g$04S0h2ANi3VSroRw zH%w!sRXD8uvSAoh=V%9QhwJGZ-vjTheu+u5z2=e`6Mr9s-iEaF@K!#METSvPQ-_>jBB*1t6I z-WeHdJUq7Xy3FBR5dW~4zadN5ua7VO`F`KCfwAxb_^SL5A6vnHF>PC%t=jh3;@@=@ zx`Z8efYbPaIa^>OV@{I(i^;|#Q#`5{KK;&q)KR=ow|-N~{~ePQ|E^d5A9Dw*@J|ln zmFE5%hw;uB!SLZTgOk(Dt6$we>E~jsVVsdQ>OY*g z>0=pBENlR$!2gK-=3B|0todJCMNSsxtH&dOe{loec5S>0Yjlg>*h5SG!iZD29&Ldg zDQv@U>X~AFZhwmZ+FE0u5&t9q;|}ef`W<+k;-60NPjed0YtT>ey>44>fqy*0-pPb~ zQpewi3%@GZI3)8ZN1IJ8ZGauzI5E@HGRFR3Y+#?UH2*5@QE)%`iaqV){En47W}v4d zg@4#D{&AY3EpUZraW3q^GEBmAOEKDUvu%%o=Q*6O`fiHB?3e%H-?hkxzl-3G=VG=j9i`vGB{X>QS##&?iPiB?Zi2shi#s0XT-yI7sqLYoVKjMFs zqieY?TYcdRUue58G4QV>2D<5yTo<%i?C#WYS<}J>PERcKg80BojEUtJV7=T=&h%gl z@ANSizma8qdG%TB+m86J9=e^w{PRIs?*#rw?9b`1+~4tUeUD$q|0Di!ZTY`r zrpielVU?Wm$;dB!uKmsCI!FAMLyG_UPS=HfMO*acxzBwrJGd;|_qxm>-J1=3F5rK; z7WMj>TmF^AKzogY@lNqy?k|_v`VQvDLU-^Yzf(kJ~Z_rw3RY?k-eVSknEv_;!q{Nfky z|Nig)e)+GxSsU^{5*ObvdwU^1m2dndX1W|HUvoufqD=_qCT1`|9Ld;NSJe z_trYvKWG5!mUNO+KpZ#qAe>^#-M+6h6JpADgzgovZ z@E_TGF2&d99`27=A7qY&+(+&osa zVRFj-hjQ#YhyOA6(^!#=^cVi;O8I3o+sd?ZuH1@${$|P>+C**&c|7Mi&*2C5MTUQ! z@$k0=AB%e{j#>gg$cDjx?0i?^pt~gh`wnAZYa(zEeUN)`SnQQ|YX`-`}Xl|HeOLRJn|Dn964}*mq3paQ-T} z(OvbYJ;lHFX{)tV-}imrw-^s(%>Cx955HUH{I9k?0RFQUXjgJT_lUi}4gbS`{Vti3 zW${=3=y;PZ$Y&GUy40SwX^j8N|5N;rvHy1B|DHdN zeO+UW|L6FB6?)aew(j{jNaA5L@jsLfb{~kY=`x*Ja_+P~f zBTtZP9KZ zZ7lciB>wporTus9;$NHEt~qWXPcp_emCa`HKb2Xzf0ew-U#?jWEB3W{lok6H2R-E} zPuU+^_;m7tj|*3xoZRR!kN+k2pOp1)k4WDCW8!{mALKmAwA@RMBi_cizuaG36;HOs zg5Sm>79n@_R(I2F@lThV)u(oicBySg1OD5FRa>%{VxK&=ga4_#%3GuUM*NR%Aw~yl=U`xT_3D?x(YI20W7$PQ@2ugWf0D z_<%i3i}UEhbw>Ob`?bHeW2<~t{EvKAJDubIRdN2@_nV3Rr`H`h!2jieVqZV?dAr!J zyc#ExS9Ldy6Q|ril~wgwoj&h*&)ff`Cq2nM1Lq|_{*4cL$U}~4{EK^h@*IHs=id2u zd0vG3+~qvUzp|_hr*S{*&v8Foz>E8f@P<-b=x??y>;n$i)@TQ=-!_c+r_b%`wAin1 z>#J(Bb2wl1-PnHhz=?W`@$ni{ySMMS#ys%49qgBML)pihJhd zME)a3)Nb|T2Y%oO_CN87PuzdjS!cZ~YXiPC{~y!%&pg*+zBk)Y|6eTbdoka;iu*gp zY}~UM?uU8UcaM?z6kGfC3v*lc0Hdx0AMCKUSe@1;x9ZcY+v1ddi+ws?#r1RFJAP_A zqdnKI$F1Y>te8(9^aoZChWo3p`pOaeu0clYDj&Qh2ZaCAhyR^1;`LJZF^m6> zdo!>7ysUS=6!*KQZp!`igFmL+zfs&zUa*b(U-Y6E$r~)zRk_DPVGF)z3y!Ieu;0RV z;0||>c%f5e6|c>W=jQmo+_qi(Ya6_`ullRabHy=iPTR;*+x4ONhg~?{3GP>?vQ9?p zxW61x?30h!2lr=P+}?2H^MapKk}EkT@eliX{)2nIZk}=c|H1v9qh!5pxdb0ohPb2o zf~|0WF^%(!|M<@R#!=Q+Dfa6Z95+|kiMrs8f7S*V(k`X8Ip%&E*W%j7we(uuZioNN z?ZtchS9_gq+ivG+w^+aJUWfh8x#OaBrS?+ZpZdu1!)9eP#y*t~>}$U{u*W^_ak&2@ ziIpD&`0kxO7C%ewKQZ_FKRWlke-ih5u0ZFzD#!8%osHO9#r-26;z0N)_ro~ehkf^0 z$R~>@t+TcDUHyV%=L!yDM{ov%bVFy_!zSQsqnyT8@IS@7>*`PKSzD&1zuEE!aHM_O zg+s`)+~2szz8FN?*e%{!hx_{Ia#iuKzA^SG{-^w~iu;TIV!!-P7q~z1&)#6-=YpHX z{lRc_tbMe3Iqtn~);j#0e81;Mn(wMSx5E9ERbywdZsP!WDIQ>VuEEyW1RG=*+BN24M%=f}j%&MNeah>j?Yd5{=l@j&Rd~^U;Oi}&v?c&7V|6CnV4r`6Kp|@1Q%?g zzN7zl-|u7beQgC!VX{13d$eC4Q*1j&8SC%={cXDKwba30;Dz0Zx$u7RkNd?T<$n9@ zP&?ES8`zuUKa=flzEROu$FLiCuN{ooAGx1BHD(>L-`I6CzH#IR{J<`s@PsGqf9g}8 z>REBWoHb%M&OIW>BJ>w+_P&AGz$sa~@xrY0+3P-E&ln(6vaJ6c<%c`)6n&04pUSEn zI_1w|4K|7^crN#gec&AaVIKD77$WP%Jfp3BzdN?LP)GmY$*p>~Qs7qK_&I)0yui=k zZgL_oax3?fDJ=V4d`Kp2Fu$J{Uj6mt;hvCZ@Vz{||IYk=DSpsz$MI|MOGk}=M(kH_ ze1=l}75mj;xnJ8lE^6GoeI^X{PE!~NrY z*U0_jn5jOq{*t`Ns&b+m`iFlqYn+VR%B`@1qu~|)=~cW_?l1Pg=X<{A0REr+rk zZE|k@rwhli8Rsi7veQNTRu%2n7r50=eP;h;K_+BV?uUJ{gF(gj>Nh9< z_V_%%_?E$z=Nexp_aFV+Bq&m-*bNO$tcH>JL)q>z96rW`{!b3+z%tw zHO!Q^%WJE+AI`t~yT5yZdDu7h;)5Um_{T4b|9x*Q3VY!@;6qU-y{#*?y}4s*SKVqW z+-W-=X4B#vb`Hzqcw^-L#yxBR-;)tpkVDShlkeY|oWl!}f4fuka+3AuK6IocH+TJf z_a)(|r)EC>O|i8PrX73h57h;y>CR_V#!*T3_?KF0RmyRi1$2eoIjP_Ii0{d_ev+%~IalU>UbX_kG>xbH;Vv*LB|KI_G>K zoFD2E>J)r$24$ZD^*}{zbbDR@7rsM%0>2OEy=Oqovk%66;4_@zoC?7|GJZn+A$|ku z4e=RU^#y$o?0eY1&=0~sfMYYn$3M&aVV^>u{(HV3!9Ie2I6mv?>3#Qt@~yT3#|+q~ z(5B!Eu@MAE&_7 z??UkXXRx!G76aV37x35fV4X4oaIHp=_X4axYzwSAGA6*X5&w_i|3}#0&il7wAL<#j zw-1a_24JlS2hJP+moK>91@wpw_W0ih{{v_Vp6!J(&+XX%Grk}36Tip%VV^_XK>Q%Q zAK^WacR+s&`waSI=*M6`Zs$D^vk32j{qZC0{|x`oCg5-Ih1|P!jDR+P@F!S4!l7W@ zU_GHz-> z|66W?dV|k*18rde{-Xr?et5nM#yk<;1ARY&{Xg>k&`)gl{g4wtUJQLC#6H9f?0cwF z$RVIVg}8$F`Wf#>d_R0gcndNwhBDZ{5X%Uz{|xqjHU=R6A1UY0`hGah{Tbd5@egeV z>H?gZdJOu21o-cN3nVt}0L~FG!TuYj;rT`bFz=m#z8^kQ7{)vi z{39_lIBsqqGvQnb(brbr4}Boi?eFn^i2a|%d=TFc{XXKeV4jd$L9PY+3i>QqKJ;a9 zyn!|b(?VN-*!~gsL3}^_eJlPUCg6RD??>hc|8Cz8^#|*Xj2l4L5dgDgkp|Nr$1=my#_9+<;Ff-xWX|8wAT4gZ;#&sN_LvA>=7BfJOte&~CC&-X+B1Ya;M z#0!jJKzoGsLe@Pu*L+r?9F9+LY=XWO)&u4b`)vC-fQ%1YvA>o7|JA-9))Cq)9Q(i+ zunPFP57hr(i6xjfc%gzh9uD9}ydbu573l0a=%deoYc4_V2lWDNA97EqJLpHCPk{ac z@TVETlk!1aTn*Os|H|23XuIH<&XD^s0`5}Psz-jBq5puPVQ?}u$c*0^B%!FaU*^z95Vg$*(K zS6{FUc&-V&qk%qW0`u*QV4mj%bX5oZ%pBm6tB8Nt)YBr+T^;B%8&FS1cpr#2{ngd{ zTk{9!*NN`5)dTv$4~&QW0RKCHp8i%`{#}KE=Xs)l*aHhV<5&&x z51(rb{XY`#MCQF)dH;6bkKiBi{SXfb?}t7dnd>3GAC9k(_ivB+K&}mO0ml+(d!Qbx z;4H-eh>y4e-0y;873{YkU7P(0f5!qoN&xsaUof|90%O54#2dsZVgm^N5$r<_40#Xy z9l`#e;r&1J{RoGKJO|n!jI%)fA^3;wgZu*MYZQ!Q=RrGv+O8k9_1`H&>;SxDfH^G* z7*7v?bq8hOZ%zQ+?Sy54e)@M7`CpY6@cmHtI3V_E5BUEqjQzl8$s;iz=&xYEK~4et z5%%ZSIWNL}5Z@2|Hp2V2#(W_5A(#JI%m?~f_&c22!m?rf3Gkgtun!eJUzG^NGX4}s zkp2XIZ6}!190l!31lSvcHVVszoM$WVfgE>h%pcAM|?lReV{%ePl9nS zUUXV#0ZMu{1=V^5Pz_*keCm| zKAc-Z-w!bgV+08H{~6wo_&` zgwO3|0sA;|VSOQ9p`VBNM|?kmeZ==8_=g;9d%PdoFywJ?JcIRxW89zN{jgn#?}y_l z^bat9V3VC-{1u1#hxPq+{bw|=x&DO?aHb1zHwxE&;l58e2S$89#6IHtk+oig_xzFX zN8uOe^S+P=!9LsS`w{N*Bj3N3_rpGhesXKfXFKnQd<5nL%K&q)RlrqxfIsvD zu?Yq+7x|ZcB5V_w*P#Fobr{4-YvB9@axBRAk$C@B-v4{PAKr)H|3|zZ`U1%Np>OyZ z???9Ez%c~kAH=Fkz+8pq-?jGjwKIQx_=h#HiGNgZet`=3qX@u*mf-VF;4>ef|3c=x zTYW#A$05Fd>pmZdedr^%#(cK>erOvoPlyQ^#|Jjo0=V!wP!FQby8Od!_|Xk;T?xee zcEdgN5Tg*g5a-)@{~!5&g!}v}ydQEqIG#dXY~mlT{qBV0@2~4WuK|cVcmW&W0ds*& zz=P)C^WNZG0FL7b_7UF?v5&-jAP?Re^Vy31t#e)&_kn4lO+oB~J*KlD22uq0&ielm z0lxp=s)$Wn!T`2q1US|NtRtLzY+dU`u>Y^be4tL?9OL(VKg2&AOJVtdhpmEnP9&(` zU%K}7Z&l|12bpc+1r@9dbAz)N&j1fvK>nvJ>^nHd|5@IT#C#Cm1HZ#D19E+c1u!p# z|4YCVl*J6nMTceo6&J8!$bo19-io2^!FnM3>9+23_!;kq>5)0_@5Ow!^M2^-VOt@; zgSHOWu;G}uIk#Q_drYkU%69)1y8Qnk&rKVE&%NaVar8V84_bj|0U%F-;{e3}&wT$@ z-VZr5#6EmMIm8E?{{gPQ0^<50Jo(8BtYz){tJl-PGwk48Vkel}M*{m@g;<6dL}EV3 zdh?HBKClf)%;!hEAKLRD`F@ChXzy@-561||zkp8W!1!vgsq6nAVD&%M#?5|#>*OMU z3&jH)n1s&&LgJ!u9Ds2f=udwZ?}xkx;s?$Rpxr?1gZm-&bHF|nNl*qp7-RnBdF~%6 zcC#(0AkKIS;IIR7c*w&b??JfF&wM{J=Y{tBN4_7%$9@#^fxaKXKa3Say90HY276A_ zVclW1nX8m<{5rf`C)~t^E$yjsedi0rii9^@X+xG5<#~AL#p`z5LAgLqG6)ydUxX$o@U( zFW{O3c%J~VuCqcyLjQdo|EosdXaJrM05KGDum*bx%vCGEo|Rc>18{E+>|f~jARmGl zfp~#0I3_@@yUF$8KF9Z94r&3OsZIy%3ZCWuri{P(%cd=30X}C3>h%TMC)|VZqkTS* zufgA;?}u&qneRt<4~z@Iwj#cNJMTw)KlB+eFNlAjFBtFQ0d@GRoBk{Re^CRQ*n$2U z6U2tt0C%wharHOAhmXNI0rYLq4j{*Yz7XO8#&;m!0mo98!MO1ToGmy4?*EVdpIF!i zP;V3vZ|4DZu7ucz90+oLg!jX>I4FbohjZSa`F=R&!Ep`x{;k-D@d0G53$}f`?}vJV z;||0>xUUe5g}dS2pI_Jijs`ZL54QvCo8SQFf6T$2p$sq{48ZXK`Z?I|(C0&62linO zfwk8tkX97n4;{wh|C1NA0r0{FYXO0P1FgV4$jF&9I2J(sLm9;Ak9a?v@4$HP);TZ4 z|5o0=-S;EB2N^42IS~KgdmfmV?*hKzKdF*mW&aZz*u)Wh*3k}N4{*&`1I%N?z}d++ zV4mC!_O0~-f1d~T@2CUbM7Ej!pD5|yd&_1$&;Xy)2RNOCz8=Qnx9{_T+~?2w{-4GB zxBGs0o(|du~DfqpRI{_|hgFAe-%8USk!kOQHD=NG7f7|dtLM<5=5#QTwWCyd!Z z-UE3*#6QCOq3?%t-tD{}iFYE{hujC^AKnZ1-T}K?0AuA9=pTTe_`90^tDIjNSdY1a zbqtiDp&@=S4{n6mhx`Zf{vY{%h<$|jL;NE#AB6Y(QOpOyKEywai9!5>a=O6W=qT(C zU<1E;<(CHjHVr_$ZT??NLg0fvLCkgm#($vy-x~9Qz8|g=Z1w$rCg$@q-w*8o&MDy8 z17JrjV16VB=GedR|F`x0uYwQ_KpOz@DipByh!8=NQocgZnDL8Xz-RSO0~7q*H#q|E(H;dxF3`eJ42M5Cd?z3fF>R z+;jU{FT(q``+gYnM0h`(_x>o}k8mG|fA~AxUkdkKgEf+95DO-T&yV_b{nEgHLIZFN z051%%uj2}s&rZXA5O6L4@ejEV!ux;Z`?s(4BG`xAXZxHN@_x8R2<-*Neqj6)%s+xb z*#uw=_)j$bSDC*wu&x0R1A_nWfClUd%7**#AvPgLhx62bg!jW(|Ic_o^!;!gfO|vW z^H;zavk2AuEnd*D0V4}t9Yf!rUg7uSIptOz`p3CjL6)%=y_mj?b44J<7!p@1_1C}91X4dAy9 z*$WNv3V9EVcW&qX+vEL+@88OM;8+0X$dF4xUIm{w1bqJ@;8zV`ZCnT7{y(-4>Mv2` zuT1|ZH30qZW-OE#JaZxt#D13Gxk&ho2#8%c1|aKQNX!SW^KRec2FCy>gJ~ddf@26g zR}XOyp0lzF^6UZg-T)vM0&&kB;LO?oq`rPt`v0B=pbfx12;hu^2zVCeJAnQ5XXQiu z!ubHS1sJ!4Yhy4r3VA%7AHx?MC*VFgxON8PNAMoVaUkyDXJA7UU>wN=`$ZXEIsF}}%b*56?| z5Z_z^^V?Ctn`*%NLMk{baT&b!fb(vs;Mp$!(>nT9`TyG*fO7$82jGktHTWMm+F&er z0Ae?}fdAwHUegHH!McF&?**?B;P)rM>oedwLtsBdKZtF91f2f`xW^mpf7Jlv2`^Y% zAO-nh!aRRnzclc_q5+6k@Iv;p(t$PF{ouLM;$WUE1LBs7;2oZUyAGb4tp(oIz!@%O zFn^W+&yHn)&m7&n|9_>q|8?~Q`rEix*Y`#t-+p_aKtXZ%?b~mkicnC*e*5;@C(uHg z-@bkQ1hW639G*~t4;9~j{hl0qdt&e%rvH)r3y?!_ZNL5*IXr-{{et2X0zTDv2O<74S5$xK?vdb_1cgZf$!)rv#*x}$YnF5 zZP$i;n-F~ciF^ar>pl0)JwKCg0QdX!8}e^jx}M(#-u`v@h9P~Q&xZW_`p~Sm_h<5N zdTPC%VjKE#{6-1j%OA*#ev-cHC-Qa6+WH&KM!7$ee*+PC-)8#n6tU@OzH9KN{JREk z^vm~r5phh8TPq1ZBkr$gn;cP4*LL});eFSz%_jUnzMj>04F>tbHX{wjF8Zw?*aoD* z4#)@a^A7+O*E>P}P#2qW@aZ?zLixts>)QHS z@Ne>U?S7R*o7`ycdT(@rtT(Q&^7Y>Pt)f7_@r4F_+PoFs0lsvt-}$}Vaa}G3Qfz71 z*Xn;K-z>`krvFW&>mvcoXuBLqvM&Ek5npu+v)Q7cuYIvyzFtZ2XPf*h7&hdNNU|>E z!!~)*Pvnk2mZScIa&#n{&3YoY6K<0uUulpJ+uF57QR@xZOoslO++nMn@F#MIZSu{| z02upPt{BW`Qx357t$Y*c4qN0jF#U!cLiKCDTeRfhCyaTY1;| zSD2+E^1~Lp_(r~NM;occwx(ZK=*G=ZUWDWWUC3s0zm?N`H5VxF`kEe8bQAL5%0a=K znL_zy5_p#w;vTlizvc+#Uvc|Yy2(!*w+J>ruB#BSqb)zLt8k0zke}971CgA$64Pxu!@8vWbazrm*i&<|Nf~K$1uSD&;^qUR- zP8FLC{?qc!27j0TW`lnw|E|58%ql&(`0wNn-^7g9-#AfjuWv>4P zngZ$8<(pOC__1r%jP4UbJ5A@8uhs zb^I=$jq3hPUIfz9Y$W;S@6Y62KS}?O%Aq^>QMphq_DxMTzCgVEOz!Z5^g!L4hV|`_ zK)zvE--=-ee5-!scLd<@`@Pfhk_5PWa6w%Nq@|84f-=BIRFoYU;BD@f7T7VXd_(%U z#Mv7yqc3g37!A%3uGu9Ir`X9yc07(Z==7BhtT_4(3y0xut|OfU@tNbz7)MoS;+rD7 zUl!gfP40+1mwo^ZN0>sK_n`0-3R)r(obx-*8*uvt51qetb6j*OF=>AFjB{1S0;bg95JEQN!oh?u= zcVX9;2hGV5;`+V)rJ0MT9{3x~cyb;wV*i*pdD9v7ZNpS=>dgD1{VSis6{ufWpKCvW zYxB6ZRr5w5k7L};v6;Ozi@8d=rL$R9n5~AiJ|Cqp(44S(FIBf`s1?k&~j}okTqXNCqZv0aG6U(&=T~c|HB>PCm75fsBepOwd-tIgBg+U&w z0X~U>#$--(tY4Y9PW1lTjJgR+F-Om$leFbNy3Qx`gQGoypGKa$S-o7=H-7JJ@YuVz z62kQa0!kAlUsiN4@hUR-$hp)k1XS@P)i7sf)KTvHr0I>e5H2o94~bJ#M2%b@H)#MHOHdgvtHWsw(#m} zJthWp+o0M7J$ed*9TGl?qbXwb?dJtWSSiJKzUx??kl*FWfF{h{;nE|TKxSA$vV#?8u^j+9QsI20==>py-%5;*<4EtrR4f`fi{87ZH z98AyBNzop~rA^ss`wYeCLhVAN1opw#J^T?v)TvYvuK2X|_d+X$C63)uO{HP7Yv45X zBJg$GeLw%wloX0-zt+MJiyz9;S#`8>Zd5BTtjL6RCPQgky6nYEvD$fA ztD>38m|ECW$I%WJ`6)#BCHSDR$qzq_C-hsnU0;HtXj;)N%&hrLOr5W&=<&&5Zc8?X z6Ichl+R^S;g zPsp)^7w?Ly*K%{Zu4y#iR^HJ|&XrC%48w%rUKO3PTLoGf|HJXa4r%u4-83af9n`h^ zX{ghpm8?k}(}%7+?SH6pzBvCi%_LEE%qi5v0sE%%)+$lh#l7S@ExHLKBMh~2O47CV z*R|=f5*eNqJmG{U#Qb!jvU)8veBtHVEo&NbZK*MbF$)PvMZ%Kl6OK3-irSc(s5j;l zI~=F+(f3KqMWjX-@fo1x8ho;zFI@h3*G)eBl1v&NR>3hwRD%10!yeu-#j)T4Q!j)$ z#VVo;QcH5nZ9glXjF&-I9$CWJ>As?+rFvwSKdKjQ0&{PXn(p;0T5|MGBs!E2(XPM8ht8JxH29j&_j zEOwo^YrC`Kk)QbKU3Zvz&r>R2Y&|(1vhw+k!_`2HjCUlD^SkIGma8%3JpJC$)ji}M z%e5WvlwUcjsTL!4h@0KQ#Hi%NQcq2E2Ikm+X-qSw1)3KumGM6&A(NO)pcQp<`>mSwF)wiUEAvW*DcJV6j1Vtmt$`Bt|+ zidqeRf~35fv`YiubyIenXDS(1Bw9VMmLhKSytu0MMCt_bwApcHQ|ds0Hza!wOJ<9G zi5RLmpYFf-Hk7WuziOJ?F^+_Ox&2lP5%t}$`G^>8+6=C0`a=Dj3&&LH=_fL>zZhd{ z6!&N$8|ZaYzRkWvSAJnLk@t_Dg8)8lLx?N%cRXadC4GQHbvdQZgHCg#=88JY4Q zn6Rs_ohQ8gnf`9=yy*kq9Sl6DrcyjG((Yk+zm6ThF&xe#jUIZ*i1;E+<63b|#~BwE z`C3{DpU5!tDW2KRpiq@;3|-MePIjWRTuwJKX{zuQ%L-c4y|o9QwpFmnj;E!n-?~0a z(|8i|g~hPLyQ`=N6qWDpIg5&MmqrB_ji7=`k|nmjmb%ljlcvV@ZAaTJT|NCd&&pN| zGR6UO@sZ?yO$*D@-n}JM(jk5~Z+|-IXqv70qItm6DuAH7kx;qPp=q+9734em)Zw=bg#N zP*N$GkL+VRSY4}=B#sQQ9pEJF)y&C`Q`7##_QMiNeP^& z7P7Uzt&aqj4(^x@M%8;f6X-fEuJ{JOwC02V!N{)3b1r3j4o^2Ui&KZb%)K&BQz+l@ zcGYSy&^!5Rq-{@Zq5X|_$77|A+~Aw2HWWDUa8I`!Ii}02j(JNAzCc!E1^YzZ`FZx0 zK$QFehio?9gK3`!4hwU)xReuCx*2k3qXZ^q`4D$axV2oFnoQg$w+;>Sv?JLkF=VxR?8zBnXPs>HLW4QH_O3Hbohwt7E1U;{Aqm3{4@+@>w zH7=L|%y>=A5HE?Yu)xJq{_VQw8Cw>%Vh^cD~ezb^`eRPKSS#A_mn=VnR`@0&NrQDy~ zSPb$=ey{Mzi@IH%?Hu{2Yl}sX&8bs&@FcIb<=bdan^eCN&v?gsUp3kqJ96anv|)^D zTa3>HMrPLADAAKn1vk;Si?uALSNA?B>?_V(h-T5=PmHs2sfF*J@ao{}rXy24c3L_1 zJVo@c3By~o{4C5@M0l5u&r1_|t(LOj?vOzx2)%4%qU}gWc=bazlb+h+QGXU=6=fRr zCo*d73Eg^o1B10Wo*GK>9d@Ev*cGw+NqZbA9~p(7);_0uRygEy4^ZP&_Of;?e+j_0 z9&f+S{eehcJ=~V*kbYsRRnL271$&e3UZc)>11zF|NAbHFqWN;mFz=@Yq&kVJ81?? z7n{y9?TEiqdTEDDAEnS9rWAF%M?`|<6Em~(BLcG_O2YA;?#9}26@JW~UdMe6yae~i zG8Nt!eK$tnxlluxu0xkMj!!#A^JTu|zU+I7s}WCy?vXHMgcy+Xp{WlU)f$>o5ilQ8 zx4yCyZV0e6bU_V)JNhCf1m?w(YtV*N$WlU2C95^i-U(!$E9~w~o)2F$ zjCri?c+#25rB_OvhdWp=j5j_Zqlmw3Zn^Jq>J*PoX;SX3@~9Bu*38X)4}m8dv37SG=1`MN0zZ0LbcAj3r>d+y0+d*SZsod;r7~0j*;4Q z*4pO}IGz!+H8!1L(e4bzHnaB~a#qlr)7^E>J$!rpL*m{->&b>1s#D~yN*>(DoY2mn zdw<(tzw?o254FkeV~|r7uC@o+s-!q#HtMlN*iLplsJXhE?a%`PADd@f!7H~$$YhCD zZil9lUudRbEHKjjqNilQ7@M+R&0C~==fPbKs!>iQ3}u3+@(VJDuH`tycv^B`20!{7 zX+P%EMDy{+aeg1eL3}RR2!`R76U}qw`hk>eByFZ6HrHCFJPmNs~!vACY!K6PzJqYZr1V?cZ(wQ7`9HM6yPHpH}))H9vo@ zGpQoM+QG-`i9C3QyZf;lqRz0M3T5W?wtw1!LzsD+IF_NrpJ+KV;O^g-puT zNSqCSh4H%gz?nCCPT0&R?dQWHljKN;CQKTt<-h}|`G>BodywXT2|QXf8C)(hQ|W8vE}~+IQiL z;$$B8<@f&db^MxG7%s2O4i&3#(^6bSx$tW5j+lsJZB&jsp0Pv_oV{P@bk9w~`8FN+ zc#Qs7$iY)*xtb$I%z9=5&e@)^cPx8n{-D5UJpBl_h*HA-+C$~}fd!H`Ej1X1$rC@~ zXv%TF!I7oj^_rhgG;%P}q;HQjig&um#8B{4t$V>_JvrgcO`)9PS`Fz|c_MGq>Vz#w z7^H8%DO_vP3BEM^QR?tpR>B8IEC$~%HF56bz2F&XaF+`0bQ6nf7D-HO;WIXYQYv2p z&!Clg*0m==eY3`8D4}GcW?d)o{QRzRXuJ@Apo^Z5KKY@>tGwyh+_5M|83Vidnjnde zBNE4-AM++Rk@2P*dK)%~wTq98MMpc9r-++?7lQ?hh1b~s&I;+h8vId@q6y*#!IPxL zvt=4~eyig17HV0g(cP+joY?7+3VZb0XLCkhOH6)h?potI^WuoTvkV;-T4P(gqX>^Y zA7`voc7SFxgVbrv%VTR;OqPmq7kIIl-Rl`DxD^OO8EGX#B(U0p74sL`CT5-(&A3kP z7Ev6R=PucO_<>2wr1@0wSe9F(sio=SMA5y{E51~fRSP$sp%)u!E>(OQ9Es`(!!X)+ z(CVpW%n;sfNjX!jqWkPgbj?=uW*Tyb#;@2Pyg$S(p{9)%g4!dY$fj6IC+#JIBVx`; zjlunjMB*s6dWss3O<)37*(Xi0d8<4t>~oo2NxPQM912jsdG$3{!gIDbme$oE_OT|Z z!+fR_W%RagRG3rv-YRn_cs1RnV(pU8j25uT(-LAda-J03+bhsHlNCyUReq?P2wm2-fybm~Ax zdcH>LWjmBJ9JaL9icx;X90U*2P|srOxA_t)unni>_IX-cwhgG33AweohEVI+Vmv=; zo1bh(dyz&|I;NRqR`PVb6eCf!>zq4t8&hGK*?TUE>Yb5$i^O|OZ%#eFdh2PUGJApO z6g@$)qwv9H`J{H-&yxqt1vt*pIbW4Eyx&{f zFv(>G7Z2{^Nthw(rRZwA#B1B7W}RQ(Q)MDj^-nw~5izDG{-VKMU4^`0xYOr^R;KO# zAWO+;zI0>1<-sCrKGXWWujoWIda-MS(azoOpT)oKaoV(JkQKGAXMyVm^}W1LDQoD; z)`EEs*Q`@<7Ewpv=@aMG_}mpD(LEMZ`RuK6HA;8a#Pj#!?j{M%5>K$pUdXK8Ha1Jn z6RR{;@3u~FD#a~`buKX{i+x)Z*DZf1S>6!j!+o@Y6<6oYDf2Olix~3ufAp zIiHQR*d8u3d7sU2ob6959cytAk_okAUCYGx_9}P$auc0)*XO=~{)1f;Th%-}mwJT{;O1l-02hUqH1=Sa!#00L%T&1*_ z`eOM?EuV)@++I$?#2NJo#YL~)fCuvd)!sO~{5XtXbaSVj!&`P6U}Iz+8{E<7QKW~8 zSKOEzt&S~k(bQkB)HWfBz7~fns2I)h^dMpEofKoIlkQHW4{b-^IIs?eTQ^>00ILT#@r7Q?Ul(?W)FXVN9JCsD_VrJV=%)@a9 z_o7EvbG5Uby2EZ1?yr77na851!rF`S>C)8+hh%d)H_Vi@-4YomQb!%5a8olKF{ip) z`ktFR%=-(<;nT|ZiFsd^^6kaBZY_WoCHcZATi@A@6c%`lZWyOu*>`QfY&pOnp zMmd}5)4y0m-!T!#mhXIghSI#@Xf16>#Ha_o3vH)ltWoM2^6L1{w}oaClhKKXAKay! zByq5DZxm^2_j`S+<#ojr6CN*7O1j8e^{2wmc4g1%%TUgpt--Eq%V4}KVBlIP$xu~Q z_Bm4YYAGG750Cu)6TRhyWTcaaPu^y~nL11)w-Ys#@t~C3$63LZ`G?~#nd6l(2er=N zdFtNj!m^j$_e#`!0F&xUCzX&2G3C3WQBTsCp@D*XWGU`wTKQ~g)h?>+OCLIRPBdqi za7UT9m4%qTR7*gz4(__wO?C%?(nco21l|jalzwJmr??QBTsy%tWJ5nVtjkc1bJz30 z6U!P49BV?o{b|O8O7Oxhb<5Zy6Ezg-n@{y0O6C>6S1bSWS%vKBH2UIaA}h{t=M@wu z&pwWwW@(qjt(7}o^`*-AJ2EthT9#f}JSbBZL5(xvzOeW6j<;1$&U94nsw!2}*z;ld zsl(DTZC&@Wk;I&7N3uZIB>^V%KndZ~eU3ci=@pSiBMWhJAE(qN8D9WQ`21^IhI&>YCMR#oI)nUnWs@Cn3i?d^UYi$-kQi# z@CRi=&86^T*p-Ec%yX48UXsyzGp`=$EIj(?ik2l~EeG#zxkNQ>!ec6OHxiSrJMxX{ zX;W<_>E)8?dd~DHXg#@muj*vPJg2Z|MoV~O(?itW8$Aw@iwca#V&5trc+Od8o9kDl z_{yjSJyr4?f9<|z;dC0Km1VM*kM{0*&te#?xajAlDml4g)*V~f;7HXpB0TUqnQ;w_ zIWJOfPLHq#@dy$0AG<{Vn#{COrK(+WFsTX)HG3bA~;C@eX7%V|o5c^+OBA_0!& zmbfn#84lFfgp|f5tIDaDBNxas%SOitcuuva8g|>t_(V6n zVo?r|3ZNsYxTJJzHGlFN>+Fj1AS=2+{-U(@<&z!umnKt{$j{XsI=|xsj_*}=nrj}s zsz&Z>ACBJ0mVF;}k~Ay#%u55OA({e0RbfAjc0H8g5r{ZNS~ zDo02fR9}JE`MLH(GwlKXt{$%rQi|h}FnF6p9d9X+2$VXU?fv9=)k_`wj}{f0bcQ5D z40XF*(xphR1Q0vWycF#=F{)xzv$rHFj0qwW=<(}Z2t#8_YG^shB>Y@ZBfP zF>z}aH zH@O7$WcqI!YWIX>p^nfgzaq0fYPLodnZ55|55`qyed3R2S#q@*17?mA&lk6dMv>W> z+LP#IB%F@vh;eN{yT|xLdPwNdV4Ke)l8+ymzF>rsSTfLGSV>JSe0^a&&g+gBKCPeS zsiqlr4`uz)%3<<%;cOQ@;-R?7aF+PeVFZGDC z5^2?LeFW3E$-8r9YMz&@3(@GsZ9f$>-#NX?P~%*iXO3QBxLQ$AfbV;4y7 zEMjg@s0OEz`a#Od zC!TKofeJKcMB~xQWx=!8We%K8$ho_-@#UnV$UcA0_i1TQG!OCateZ5cjXI8_M7*dW zN~G7tMknn_JpSu6mV+VD~IqOW6XW z^FirLT==KGEU__}lxZu*u&o@NIhvMJyit3Zm{LxWMRI2I&Fv1Va(Z+7l8pAS#xvXK zTgkIw*JU(V7}KMCHJ6)r64xvRimhUj-Dlq=mPa}-k?`a=!6y~0sq$;lu{;_IG|h*S zI}`VHaE0X+T5*SFF|m7i1RLv(KAn3pCDr%-?P9ZM=>?X{cT%th{C;Dj?rB{j2rP^;^o$OCEu9`hXnL*S;OXPNq zVL@+BYII-><05C_7@5kWKF!7!D&uzXaZbu7^>E3g zs?5aiAEDv4oi8bz^O+M@MH>lT>FTw4=5I=_=W?#H>H24_R@_`=F(I}jmpWE4k)+(u zH^X_z@b<+z)P_$oa-DyS=H7@Mdi(st0>i77@g6mO1Bp-P!Z5|d&+FUB+TXcGcqJky zXXdd|KG`wRV@|wf!S}<*AIyJISC9}Su65f>0+ipYGa`&VfTjDP!0ps4{L@wM3i>`q zBynXV>gs6g=th&B@<D(`%n(bK9s-YQb+ z6!A7*ZTTThW%_h_Xy@S-JipIxQLztI?tC9MB4ltV4K3sCLDDBx7B?vLSV#!H(?Wi}KTQnU#Z8mz7tVNrCLcWz_=RO7fGPW+Sf5JdbsVDNIVc3<;B1LKynjuAOFX zEs+UfI~6@3#OB9%q}811vZNumBh~qwOWNU4#cX^}iv$SyBZw59(#%37wWn$%|5$)d$W=LiUBs9RLVnA83f_d*6QwD zOnF0h+~qW}9F?T#^Il1KGo07p!J+lxmn%$gmOu1#G^^#V>2&L@u}>W3`f?Ys`!7e; zRhSJg>)pTZw=B;~XP{}THGd_z#$9_fX66WmS)}982AT4#_JSG)jzB_9H+z09pFoGCy;kAwQO)^U>A zK8E}CV_7Ut?hpor(|_}ok+iS> z6WQyT!FWv}UBwm6U#uH4+pDxKEEx6%Ui;GaxbywkDDwj9Z9db1K(UTh!?!cpv2{{e zp_ApSaR(#x@ZGu}hb604g-h{b(SM?Jib}gTP*D&+aT!IiK_icMjNyvS2b1okflAdU zW#`^ahK{W$zUEeX>kKFsvxTO!~u(aq`YRLdP!c(bNH)-e1&u3O?`%V=zRSPrYJ z;h3a<-_a>{_N>MmQ!|CuX%ZJJ$g6#D+edeY1goRd*M3UC`+O}~Kl&u;j>d{0AD-J@ zJvKu4UTC##Nh_p*I&|1CMeC)r8rxS;v~)ft2{$?%r5fe;4Pp1!>HO#r`b9ftL8X|B z{DAWP?sGJYGFh{)p5kh#IG=PrIw(ruxu^16y1jKbmjJ%$*gIEC21eNiGwMSA6W)9c zUTO8pEGl!B-2Un-HYDr=+0gBM?`cOcjU)UV1M38;oj2x zY7Pafdy(eKtuL$Ytd(IB+!XH2LZ2Tei|H1;7+*ns*q)K#lz2P1o7C|pqs)uBt`DQu zx0_#(To}HcqSBRmkekbqEok}1w4_SGz3YSrmO3W#asAYk2p9w-_w%q?@mNosRuJV$5fiu?ipc{54$Q%6SsFjsYGFm=mHQVQPOYRNnsu$XU!z=Cw z{45Las$WytE&Xvov|#9E_s}XGGxcpSnV)e=8O?Omde}arlEdAiAWfOx7)5(Rv;;+H z&xyH0i=m{|xs&LM)93_op}xDB$6V&voD_T$P#)0HN#HuTxIQ8?*Whr@r*{!v4GVDH z>xKVPd1+}i=5ba2l^!M6apn*i@eDr#;%nmBU)p4@s$_(6)2b>EmzUtTF?524ylWWi-i&|eg{USwY9NVqWOX1o}Yj(CZ723y& z#>u@QksJ;a?h!`XKZ+a-0{J4k-HKCC+ z6R~K=@w3)gZnGccXh;-h2^rg+3@0Iy_)^XFII)+tQM|P% z{I8j%RuevxFH?=3OfI_OtmlVnYTinEe6CHebNM|**n828#sY^^H`pd`r$3uE8Gpq? z;=?1VIyM%wGwnLX@$=$QJg;9D3e4x}Xz2GP8QkIZ78J{66?oOddyCcn>HvD&ZcHsn zUvXjKz~bk7QL!k*o~$e-pLiuBLDjSxpE-zmjW?TrU`7Q?za01Om~$^3De5!SG#;L`j?yWt9lD38sjy7B*_7hWn@sNxrF(#XNi%ZA zRZaJG&|^_vp?DhdBLnz*YCp2heRQwAcG3TxvABT8UOB=uI7xmcUK*YuA4=53A)>3g$=9N0By9CIX%zMb!_{0uKOjn|CdpwvNsyn6hX z1*Sc3T-n9EpHCg|rItlU_ec_A-&JVMcu4LX&#KxZ5y5-3EWG?Pv1eX+RiK+F+?+Le zE5}MI#f{G~XYASLt{wR$vj!!$_u=Kh*Gy#AMh`>+C(`&cLRJe;1~$4#9B`3;lE8jN zF#FQliPo#TY1$7(=AIL6ymFz$vB7zaqdNnKLw^UyFs?f089SOz)g#_1L-Eg^tvx1BbY&=9UWQ5vqNBv^)bZxhX#0yLy&7az3qN@0B!*=VzxQl&{nUs2Hy~ zk3{G0l@hwB5PspJQ=DjVkPUXM#!_QuI_v#6g^~gGWR|>5a*a~;o|bXZ2ghPEOL}c~ zIRrYK?>8Q(DNnZuq2Z&wCVa*uH5XrKnZKF@h3-0S2Zn48Wbm}d`rb7(p|`Zt=$S8$ z#?0=r@E2S}^<^iskzAtS!!5S>@aVYrQS-`v%dDlG5PRkf7RpkTjh@9pD`>18rVj0(y zQK)oc{({~59}*WeW>K9DdSqx8xM@dr54)e&y>ndcc`~lZgo}7o=#^7OVWJ17xSDKe zd>Psg$m~8hOlu~3$Itc=w*uXTisrXrXQMx8?ASwcUr_%}r2WbuL83z|59gZCoe$mI zSQD>(&=;}r(D$)MjrSa8!O7$A_>5yl^Tqe*xja4A+@f}c9SaI_ymlzkuS9z|qR^!q z6L-iRZXHRb$JLP;we!!B7u-9rQ_k$+tpL;mHT>RecU|`-@^fbO1o1nlc8oDs8;KuU zQr(I7+Eernvt3M<%VaLQ*)$r;Fw<$PAe*Y;>Em7JS09`iUsG!ia6B-Tu~yN!z`(Je zK`4N_AZD(mlRtMF|JV&u-vK3?*7L)L4T2@@tzfNdWleR7fHS=uWq;pN_G&{n>0wHI zqcQ3ru&!d}rPHv}TSj-;%gR&ru1Imkn&rE=X2nGu@RUNyi+{33E>K$%Y zr@!dp393m?W{ksOpwx0?a8AIi^>T^5sOTC(hT~U~E^6p1)O-Q!F+EeS;1NHgD^XgX zN%q#Wb2KN7j^6BVujgQB#BpxXTXE2p25>vqiwP1lr)vB)?AbJ} zTvJEKx^~T=zq+v3gA{Ajkrv%B({qe0>>dO2ZoP9hOt+m-+7@4vNT_p|uFVOqkkvsXmd?Lwvt8+$A-Knx& za?ZDFWv*VQTjO5vd?@6kbH3w{*?gX6B_ZdFD2v&r+I*Ls9#II?wPkZnz9d(ZA)gWV zLK({=#sjQQT~-oV0&~_}aUI5(rftpScdv5q zC3v?`LqhI%k9{`_Ls{Rg=XdX!tyr306UeYUlRotRqT^_@ubC%qCCWfpmUE|5oG)LK z5N(?fdrE&yc?FKIQg<*BKAGonf;Nh>>q?jju2U^?_{ko$XnV_5_glU(K5%yE1xVr?H*4B#C2|F{cya6o<<;JcQl#guR0u0J*$Z7tisjC>K6l1*bn%+{ z!3x)%&zxP^Y1l-b9;;c4U1*Fgw0)5wPRe{aH=vWIn#i}IJ^V>na>e7)z05n*_rEo^ z@XXswO)6Ewh#z?w%Nu8!uN3FnC07gU>B|AA4c(M-7ciK}65pgm4N~oNZ(6{p&V6Fz zXD3mnQ}-(KCAD|q%cbO4;QJ$#&H;EeR$A6oA zu0U5KvWV*rYgJ34WuSTz=iRJcmxY}YSO@qYNn02t-b$oYuI@T;CSQfq`XYyhe;;{S ziK-jtsBKyA<6O^amk$psyaY6($aEY#PD&D)^iRsXbO{LHpU6!sd~w)xWy(_d1<|R@ z+>unJ^20YV6q_Y+qCUzJeZI03mJ_#{vvBl`l>AJFrXz~%(pHf=buwEpgrRmIYR)#k^8WOVcBBrP(-~ zW+}uT$#Li!;uQo0m>*2ir_nmhCPvW+|_Z( z(dc(SB({G`2k{pZIV5iJ@8{c-&Pc3#!wNq$y1 zxa`(D2E%smB&#=>pYC`2!oE=25p-;ZAT9Z2o`-38Pm=mV9JykmHO_T+*SN+r_d*|B z%;oGlbm!`z`nh)~SN#;q3qL;8;MZSST>eZrCc^PzCuZpRvmv26cgNn|IN|pyd_evT z(?I0MqN~(umpN6iuP0bkHry;z&hm+V%Pe6a@G&_<$h3M-0^Ukkc!Qpycw)^}>{LC+ zNK>X-t;-)=v0WbpDBj_5$es-1Sox^>VQ2614H0$a{wsWMChBW@n0;gV-SbQ@`G{kF zta@Sd43FU)K7Y7v1sxN?FmD3i^Edd+Wyk9cU-g=cGE<(eX3zBtuUk;zlaAkeHzZ8{ zrk6-X8OSXm`|h9b7knnHQMLw z?IMC&xl6<${sCy|?N^WQ4HKm~ZAIwwu8uY=08_%v5Id%nxe)C|K}ob|AT?=L>#AOB zPX|?Civ`c=B`bg1ta^KEb#wC@B|GGa5A5(BAXwpuXrzqOFPy6ls8r=x*vrPxb5r<% zEG28-lsk^$bzHihi&ma|H0s5=*WO4(`J@{rpF?#D8dYDbLkrBWy4@-DjEk+8VmPC1 zf3>JedZPe^?W+$jaw1m-aXBJxw#RX_Q=#0ydU*P7&EqN2%lIru(zz5)*Ctbp%Hv`T z>^V!>#Z{E2#JhOG_Qs0!^CRpb+(p-(=Ha=$c@xMm#(}BWV_3WMVHaUv?#ih2E$`;H zt#c7dme1d3w#n^NXEeYTJJBw5Cys?t4ULD5eun`@>7$yNn4K?X(S*>@Bf5?kCM$?> zPX_7og_<5bW_qoQzz0{=Owfe=^aFbI2;Ay?&EnMS29Znev3eE6R+5xnb?WAvWIRr` zCSWvCnwcx7k%^+^F7!zxg=bh7p`4#}&$NmP6w(P`~qmnoz z;!na}2E;cWMf0{bYLC;jen*Ivh_}$lv!|Jh?k>f%y3~du`<<8t+Mb~_1L4LMXxBf# z%OB6boK#VO7TTvjbc?iVGSW_$O-j7@0FKBb#es}aT#}$b8qN6BTh?6ny!37e&foPm zeuZMV@T9KyVtO+fbHEWvC6ghtzAJfpQwg8hTUB{85=K+jcJjZK4I&&T?k!asw_To( zzhgJV$a3(E{52MpWa_7MK@1fM!i!fJ%&8>zO(%JuV-$a0GOs?6a*3QF_@Qi=^D}Lf zQ};{IQTthP!I*v}t~F(s$&oSTTGjqCHl6w+3v}+%)DNwFYrdh2?JO+4)qW0BMXe9^ z7^xWtD|*y5_4sA7c$+^T=FNPlcc=^PeYXJBev>ly zZM7fjG%sbIsMhGkxpIrX;!KgnDT^UlvzY%E10ek0DFFql3W%@f*3BHDcXx^;k~E_a z;%-B|77QTdKv?`c#|tbn=*k0|^$auTM!#DnJjLY?N4t3xi(Mozn>I!@xBSZC=Z}6y zewZg$6C99&Z2e_N-@NaJ8C@ex{zi+o%C63PcoB?{Y-SSK9Hi)0Ru!RSTNZKhgd1VoEZRee#+U!MzKOebCe6Tv3dX(vI_Y{!=O%7IGy!4iR>!!}DCbbZn$}DwN z@;>bP#oseb%H6C3bPh9%V;E`*-n@hkpC!~QL40`S=KWF{Uh=MuMaJH7a`63ref2f5 z0p?N`f^a&MKu}?paW`yYkzq5BcY(&vs20EiHO9!e|Dg0s5+A-%6lwI6uaI!Uj1fJNPEOfGt`_n5mY%q!!3dl ztl;eHf3Gm4S5{uQE2a(iukKFvcrw8YA%kuXmUrHt{|I;b?dm%N$c=Nqd1$>?#Uq4V zV1~hF8FY30I0~D;17-4sLSLr3?btf)7m98Y}7=Z*&XPH}(3enLlJF0_lxlIovgQT1KU*i}j znq>2$|4x}*+by=GVPIg8z#u`={iFx*t;P>^3^Xg*FrG>*6pEQo4m`;I-c9Z9$uDZZ z^Y8!jv4lV;r)=r0{%oP3$cCrbN3ec~{~P=i{EeUOb{1D<(OpYGHh&$(kRU6EYua@B zSaRQhm1>CM+{y9tE26$-W?tJ`J-42pP1M{H3SkD5lqtq)eqvhf{f+);%_B&$gPc1) z1r6@ykg~s`a1-uz1}TstiQ$sm+k7KDVT^k=X=|m#Tv}?=smWYDHOV3Q-Nt+-&16^& z_L%|ZE>*aj0ID;jlf-BWKNUd^J2*A? z1R6+b>In>P5rNPf@4j5dERl?!#rTWaq>_F@O{p`+wyp&zr+QCyl@7h=)qG^Hr9VP0<7sZQQE3RrKA1 z#{?^}*-h%ysk;R(de6i4+Z$g$AxpNoSEELaebn4)t)8vFjXh*AN!Q2k%D&I|u|C2H z=1EaU(CB4bh}O_A2{2gx?MHs&a6%wRyB@3`vJ5*vP;_g4I*?;5nBy0N#BR^V-RCdP zXfF{w9cy8_$=?{Mx;G6*!9@15ji)6;i;x3PyTCfUlSJF{=&gs|KdHD<@g z^Z26?u}nD-;t=%e2(|{lSfVfZg>uDtzHBCB zSWN{IjJtOu2f}O?9RL6t0~j=#W?YGqV0REmc_Fo3-9CvUj_xkYpgQlX9q zW7Fz+i;7|@LY84>BB)%M9}vCU2cZaA89I(zOW(`r54NsVr86f+LUYPjV}`@cmN?%! zk8w;gZ2D5_uhzC8?y4C;j?Dbgk6$sH&|Kp$IR2w!!2fJg4}@C^R02`KwF93i<4E1= z^Qtk`R6AL8h5TjL{qrzmGsbp6uprkVv5SKl_A(gf1Yf-&9y2189IQV|({+)dt4{3_gd3#HX})OU4~ zPqM>#Ns<({o4H^C(;RZkL3)O0iV47A^daq2-ERK2a#&qT*bB8!K7M%>C)GH99c%PR zMT_m)+};AKB>A@Eu-nC#tsuIbLc|<(d$oJ;(nTFL>Gc?jJWWLj{eVq@A#9RWx)M_9;ZsE;E}83RQs2F244@w?B~;K-%5vFuG(Zy1#&`68v-r%~b~86mFWN@|-B5R|cZxr|=A8 z^zL5Y6IOlBG_|u@!Yj5r!y!yX4mk=S#SjvM#JAh-L)|x6sRX$8vA;MpVKygT!*y@M zDj*ySQ}-hU9ovnH3)dS@BUcddT&XgUEzY;Dx!GMS)b=Dv;_scHvCIKVq0DI;NRvfk z)N%_eq)d;nA_kD72XFboMAc0+RPFrm^l$XYM~xaGNMO4KQM~7*kZmVmS05Erp}Qb& zud+u?{7m69nnr~K?$+?Zy1_PCCzM{|C!)LkwN?o|Gk^qs^zz4EJcEv=Y>}ppl(4>z znuO8NP%;!35TvO!XW~{%>~;a4wF56Am?0f#OM^p9qsl=agINsZY1|O2cL|WI-t^Er z4rc_;PTn#`P{td9(J7&zpiLnI30Hy?^{&z|+A&Bxxx*#Y*D3}m(IVuxIm8KtvDG8U zAh1cBr)xi?-X*{b9{8)tbtYPj_E!lnV#mFz`NMU^o!zok%`Zs0ld@s7t}Hts4DAWO zGByadO=lob{#jr%$x(%^!?PuhN=;TcO%k9-FF=m&zxl55s&MwTjaIFap}!Ec{eyA9 zuwS5&2_<6+k|aWQb-$rgUQjQ0tNjFnT!{RVBOF#(IvSXAdMirI_r0UB&?5uv{e|zp zXc`SIc0aLTxHs$w2L0_Q<9Jbka6WFrMM}K2-aimsUFn_yljK}?)_aQ0B!`q8Mby4~ z*m4H`{1$mZAG_8DdSC#VI(g;4-@G@g0||G_i0I1IFiV7|U4v19Xr&8{Ohhj^i<)BF z)N_YB=LPj95n4fp!2tO+Y>p`85BI~v)e%ZSdELUV$aLqotR8xi053jx`xATWpd?+> za`eZuLF>s9pGkBwk^EdOkV4iPI;U;>cg3wMSRMF_G4kY_%U_)Kn;hg|%pO4Ae=jID z%@G79wO5>;J<+;m0J(nh9pBwuNr~ppU*VA-C;Z#R09%(h(8vTv4}efq!D^wkNbgP! zw80XPqChraLTc35&t&Xk^5A?aG}uQ2SRAeXrX1+%rK^Ij8Nhn^eJ?nQhDJis{(dLI zXF!z&Knn>FKVRth&S7-vZh^DlH&+89Bo)HvTZfP1K%$dlh+y4XjS~Af)?z(jc!FDS zk|{Lm`r_-jxDQ?I09`Y{-I+&H0x;ajy=*a-!(+bPygu$+6yt7I7!?r}ms>LRvgl&}==(czdyX>u(VZt8td264~z+u@JeV1AQC zVvu9RHTr^Mzriu~F-LHPbMW~n&kD=1MGgTC%D-&=eBZklfG!z8RL}j!CTU{_vUgfL zd~w0xD5u9kP_1}iFc2#T95bsu6U0vjwYn0%Cwfl0$|=tXg$VHlf<^Xoh^i~0q6^zw zwbSpr%>k+i7S~l?(H(_W3SD~vnQsh!PX`)-60ogDcr+Ls75=SlfiM#|^8~pFx;>=( zEABgoi(6&=bmQ_#oG3+sQ8&^TB)&$CBkW@q8CV<}er}u$2)=!Wqkzr)uKpLjRH17I z5L3IpFl6~kAl|6+IunfvwweYC+uuN2$3oDX!Q>d+@PlvL6$h@|Bf$$+f8De{UX@QyT6a`t;^dw`tNt9mBAPM94G!P7C6dbrrZ_7DoLE|+I-BE zQScd^R7Z`V4fltj<#LTXbhJc=%lDO zlg8S#+I>gxP3mv^ zGVzRH08yE~b=P%29)hYHj#UXC?Qk%$8iI`Cvxz`ZWr;<~ZsPQSwR>hX_4v?9V1^Ro zjF3>C;yptnO%jv3>z#C?ciYR0aAsrNJpzjw%M__#A<^6bnTU-4aC&+09nnRrBJ}?t z=a8b9Uiw-2Xt!mbdV_8z*%kO-hvtS)TvHW}C#)h>)o{Ucs{Jbh*MwW`uFNzinWu(@ z#z5`z($)WR#UGyYolP5JM%i%NlTc{gWQ?>XjJ zb}M|epE~MoJ~52B3KI-tisbJ6J?}ZvACd>5M+ZYRXHqHTL(Jds#qi`2LH2W6K*KIPWMn3zQgUoPijGJL#7ODRo?pZy#+hd?B zklK+y{K9)4(KK{&BtzpP__y^r4X5hvA|)4@hTLMF^ThhwcJEE@6~~%y6I?R$=0{)k z@c29$8YxB?BhzLPxdbeCTMEBm>dL~!Ufj>7PJ=29m-G;x5RaQ4k4}UU`Hc@;sd2*` zFJTysOUfcsE8z2a~URM^)0_^yubepu!0zqIBCDny8i1|EK;ZFZg||<;t#Iu zb;kWNMH+4i9lq&^-<2)+-H8e~rEK_zzlfh_X<6~Dnuz1}Zc;%6yzbQOUwcDDP(%_PX zcRh8}14GkDHnX+mNRIoJv1D(j$?Xu4TeC%L8z62u zMprzUE;Gx&{Ix&0`}`^z338-b8heroWh$Y@RwxeYt6JHQ&BcYQgU5d0DS_^0AUH)WYE)f&22lDQ7XS)|hC0OqeOsc) z5r(js-CVeWyH-lEcv#g$e}0mWnT21RDv{X@gdq%M99 zM@Hie=P)?eY$n-_L1}&YXXV}ftsJ&01`svA`q=)7JHEZ=ps*;?V1P7WQKd%14aX!H zuWdZI9Hj1KfJ9@CQ*5M0@in@H*We#p-hE3 z38d>hfkxh*IC;+W)%T>|pWAO({o-9GS=?+}JD`=24d(mF4p*GwP6i2dBn=W|SZ5lO zNiC_B514sl%fdi0XOYL?1dD;u9{1evv;F_==LTyWV?nJr%Wst^#csnFI*b=$Dz+&aY`KTr|BZ>#}6ivZl%3wlrKEx8mwI0Dh(ufUfqPv z**dC0x;DDB`%vbg!6%d75juMLU@<$mz4)BDckdmql4dgrW?ZHeu}_u(#SqulV2k{3 zNT#Tgq-Zp*lv+PMfxaXgi6URP{q7B~+3=R~1q+%a$N0?XH_m_Xl6~6UVxfPH^;>lnb3XT!FM2RE~tY?ZB5Qbl$`NaD-e&d2SPitk-Q&~K|`cGI3N9_Rb4$G}usB`J; z&OaafJ2BOv-$&$c?0IG4xA$MvAjeitGTYo}s-0gFZfm?9AlblqXjqKQrashPrMiX* z$7=`&jD6aKO}#@TAUeu|MqpGh`}6P?kg_j-;-bG#-YSZnnE!$4mKLw<>;q)xZkKeD;Yr4;QX&+16yYb zPpzPBvpYWeoR3{JpTcB-ZH$Mqm%{Jr)b34Oh=I+9neq4X+5US~XC(u`Kq`%-9xwx2 zS@x~a9KGtWgGC*S0$V@NH&@a&Q5<;pMQ^z1&ViaqlFe)(>H6|4Hf;aJGFsAnXb{SHb~ei;AOh7q+fq z&QeHb<=-rg9DLdSLW2THjx*=(ix~Dp;$|)-8XMTdFm(!C@YU@9UW~J&Sp|j9@v5LFR5T57~h+|wN>P=dk|&* zY5vIgCm%n57M%$)9A!Dsj<`zek{su`k2OBek6&^y?>c9=X%B>>Zl59&xa5)hZ? z>p|H6R^6|t1gKNSCQ0FIMxPw6q5HSK@ws=Lv!r7)#4g6&68mAd)B#C?EnLA4G)%^7 z7ysSRJ9%;~!|G?L1VA4d5J;+#w6ONm!~U80{Y=W#u~^TZT92`%mLM#7Z{d*(e_p&} z^6HwNW*b?KQ)!ajlK_q+8(|w0Bxzt+n@(*1Na63r3S7}%;B1uurNqG`l6s`{dTWR} zTtQu|VNt>+$vIC9e6D|U?gCNFe{SGccKy>B4Fv1i#ZaJC5*wWnF5+@Fp;0Hdy!(6S zzj^3=Yfb*MRsx9f4}X;U^&&OQ=C(B+bWRZoUh}>M6)M;is=1HzlRmdsazy^gQey8* z4rS{UNOO!+?t~L0DKO3$X(SDjY(2Q+UkbN!xR)s8O5rSJ0N9eu*itj8kqx*}-9UKx zws&p`6Ig#__@CB7>xxI^n=W}-_U-$xT-Fn8BhM5yQVcRmjs!M!6808*%bjDl8Q)yJ z3ECZ;wG1#^(S@WD8wuzpKfwR&-CL!IO`6@M^=pKDu^>Oa;d^s$H2z>dB^YA}8-oM_ zn;JQDxVr5-<6ljFO{~d6@OU_D8Nj+baY)-?yV*CL#(#K9IBEh4mZ@Q}t+wk^Os`d| z1x#9hTfA`p&+X6F&@g}o8bfm8@bG8XKc0S2JaKyQc-Ari2hzvo4!gzPO5NB2svj<> zvw%&4!aa$9Slc~&wb{Y9T>Og@w>))DF`;E;b$B}W=$0deJH!vD_RII^9?n_@7)Wo^ zAH#M7H{FMV;?qN*NC}&~mH4DsqiQ^U3s~Ue@~-TK)lHf6>?emG<4KC*G;I?XhO?Fd zSRPw;EO*N-)GT`N1;XcT<}k52*M8AyB7``=0qHE6?$UH!8#r6z0Eqg+zh;rtn`U6A zm|*jpgkyplWtOlR8yVmq3#!aInfG5>`qwkvuhR`@Ed#*tzSI(! z?u=l#>P9@or-{u|Ml#CfKiGPj!Y|JpIExv8r)v)+z%4ErtqszS8Pts22Gf{G&Y#=e zA^ZP7BAm?(klQw*VN-6q$T|#RlS^ZfMQp}7|I7T_neNi*hqIXhrZzpDMpBOT13Ha< z&j>V`qK?LyLM{RzLtJC}Xk9+Vu$@Jky;y zGjR6K1pq?IPrq=~e9Kdts$zj9CK+QRBkl%sjv9#14{(yf<~fcqFcMvW|(XLdGnhuS>+;- zvlsqDFaUr(UjEe|z4V7e;yEXhm1eCjr^c64wAL#iZ@Ww)fI<7G_dWNPe4mRhoK5f_ zh5(isigL6-B7RGVrpN)avDEuGYgNI(nGDS@Q O0000M_x9~MyYK9IjrDbRrS7j&RrNOl5Wx=M=B7qDDLmZ@@EHJ@ zm^SJo!~o~X0O;s8>qG#5Q2Saj3 zB@14DqyBUYfb=ocAWB5Q1P&BLkb-n==wpI#RseAUL}A*XhaDF6AuA|k?Iu^SnBq9Ot;X#lojzGW{aJZj%vW@D`Cw3E$#Un-tSUV*4 zbQ6uZ*B5a1IK+lq$1N=U?591#t(h#~rKxZW_PE!Z{D@@%DIwHQ+1?5-iPD0-J<|TG zynq`iBjD{3zrG!z2pGKZx+Q@LV{dP_$qrOg3-;^w81$yy2xCZC14tv{USGG{=n!fbb=3-s^lh+jyceK$ z2@{5zL1Vvwm^Pb5FJO<5h#e(dF9C}lcm!S(p4)x_x6w%g9xs467x1fO|@%oX(Z`%beGlHUx2liJ38+aswU+RCiiysNJ9qe&v zfN>+gu>%#_M$?EFhmOZT5)uG?)T2L;n-#>4{a5_cKiLUGPB@Fms0e4$8VSip4n%+C zKAUv_5=O+ohLlrn=46{W$$!c5^tny`USK51<$(znFP{hNc|`b}XdM+%PO{z$CIG!; z|0So|z$4*ayc|;2i*Fa7G_3ggIvQq_vmt&a z1i}b@+8IDL^WPH|y$GTpyF$p&6BYm0?F?o^57M}i^z|_M8@qM0K8%*vPa@zjdN}k%FER_Fvpt9(NOqmR}xLT%PDRyck9w#4BS33NoT zN1Uj>(F6R^jtCfpz~UKD-itEKh(3<6DG>pSuUkFcupp=*LLI5GM0mtcIQ=k4>n47) zjw2l27(2a zbZ{a`bSi8fXJ}93NvIV9H(ICoG9G>nII7@}p0|!Q!LI-P zmuCVdy~bdY$BzKX=K&c3dQrZDU;%By6{4Q?btTm5ECQ6+P`Bt1a3lD+&Y6f%^GUsVJ=LZULFA~In;;EVW8VTr^2AaogDL209mUvpSO@6iH7 z%LC86k*GGY>zT;+q$YTvXtfBzG&+x`fr^ml@iRi+0u|(~kU{aY zKr(sgknTmWfMA+*9Tx48P6TYIIcfw<2!75p7M1@!VbP=_3YuI(hMuVSKReH48}E$6 zydumhnq2(BfWcc)EFr_`qtgsOkT`TA;CC&?3RO`97=68s;YX(#zK-9?h_DGG`gXQR zHGa5}KK#6l;Bzbni^1oB!J@~S3Bb2&0BA^%O+i$&mJ163N(TVQdN-gH)!)6_pyB!O zFt4#8%Z7AtBTz(uk9!CS#Uj5FesACbr57?hpmCEAG;f~){RcvzD1Q~+DhPwpy_;a9 zEC$Mox8V7sJD~AY0kqZRLFegR(9~1~V{IkSe)SZrUPuE@{Tf)P-vH~EGGP5m7Hko` z*SZasns>ln?=CoM-vS4n+u-#24!G#u0atx_aDRIrJdGZLsjf0u7^#Azp$d2!D}syB zbMQ8P20rgpz|ZUvxV(D~o>m%Y#9o6b)(CK>2Hl+9}GJzIPAkgwDgj%UVDDD}AS-*gfwl5(9L6n^a1lVXosI3-!w9$niTV05>*Mumv zi6R}eAjVM(Vx4p#0O<;FFoZC7V~B8i1FKvsY$BnDYSS`ZG>13o}z*ayh{Xb0I*wh$KR2p@yo5uX!;hIm6_s0*ZrIzjp; zC&-C#gq&DsNQ-cVjA%DV3iX792tUY-@`kK~bP$3N$c_zyOSDFq@6`9apl?i=?p)gn) z4nyUiV5A}nzSPCQKy?C)*Tg}8bs7xUC%^=PFO69+)|3Hbt(owxF&VzMq``b!1}t@E zLv?8$RF@S(V`U+9ROdreO&N657eH5CKD5`BKxch1bbl^|=DG@KZ>)lzwrc45TnF8) z4bb0M2ooJe(BE7R18tQs+EESDU8S(nod@5$%b~xc2}ZjcV5p}R*7}NI{Yw=L_cp+I zUn30lw!_#!2h0pK!P;;=tbc8W*`YT0Hqr%a;~g+LHUzVy{jfIK2S291!tC@gtW6KV z+V^popPz;G`DtW`XErwc<`bFCKkEN06;CM^9#bS}xcBJY{|R~31AA%rk=;ax+)fO! zh{69*LizwBcca87(P zNsC_U z&a^sUo#}#4VfaThdWP{)5FFG$gbJ-Qb2d|EW_ek5W@a=J-*^-{$o|2LuY9yFBcyz5 zYnRN*$;qLj?Ck6u1lfhz4Y{@d!7rZES6NxvSLtG(+1c4yl0P(4nV(;lk07V1DCQsR zF`(iyIXOH$TxlKs?OSV7WPC z#g#KdLw$WiLoSCY|Kv{^8XlfR>@(5g)AJb_KZ(VC_!ftUhXl}#-tWm}cyb1>zDT@d zIO2yFJ3`%pj(<&;Sf1_b?auUxz^w4RTH2%~!QH!TM7|0p_F z{15f$NSup48cpEOX8vs_*yBgXMY_W`>2DqQQhxSu*jXDq|6H!*^S^1nsQ;5zcB#0( zQG`-~%g~Q<{43=81=%(IBzD;L&rQw@N!!Of{E6Dkv+aioohkKsmG+CI=RGqwV^>%r zt?GU47jmPJ{hlyB6H&^@=lPjp#>ZzmPCky6Q?-@*iQCLwtO)!wK6id2r1p$Y^(!Rz zKX~xu!f$9)OnPs>@B>}7^uJR6jnL6EK0ZD*Gh(dzn|E@cHdbk+PTIb=jGEiDlQmKS0+FQ_-DNrQtugFVK^x~xjS*llY)=MX=VKrPJ0 z{ISJ?2L{JbGOy7)zR6#!SL-=NU?;TD>;G#PA%?-k_Q63UF@UY{RKAUNW4xYcljqn5 z`_jz9@?3+{uXa(v_;X9kzyOjM2v4js_r!We*2E`{Eul5IG`Bn+KQdqIpZXhrhGk71 zUSMEgY_KLWJTfsnJhJ8c?Ckv9{M^ELM$~Vt8%56qygwF#tiViI}k9xbR}iMmbrA@g+jduRP2IF^uLnV1lcZv^qz=|1w(uW3Q^ z7x)L)-<3pj6Y~U5Pdz=f_cn4)P+y9-f9@y$ zzrUZ{j8jQ}n=c|%#sf-5k0inD*%ffme*mWXPr&qzI(V3>fvc4^IGDc%Ulh-|zt;zM z8$)ohH3okNBM5Xhgb+t#2zNp;oU1V;x*9>emkFeJnt-2+1q8U5LYSK=iq)_XgCNMm z6h3;ILnMmPBE762+Sd|-J#65U4-Udn{FdnT4pKd^knC#;DZb{A?qdoG{x*=}kApB@ zM~FdjTxOsZ<17rkRLvGM}$VM?;On?K#hPXg{h$EzgIYC00DkNZ$V0k{BOReQ$`QqFE>INd3i(kkP>f>4vPcIgi*km1q^~p<4}MS{ z;|V1wUd%^23zGbyBHjb46TG1!$rq}UQGA!?56uW#a{W+@=Lh+Tfl!R96cnRVy*Mih z$}=LMHZ2^*fT)~ik5gDNcZjf+VOeOo6JRRH!UYgX)rWs42^U+VU)@%S(Xz z;&^B-jfeV@WcXZ~1g&`}jzn@@MR6#eOo0{@Pj*!&Lw#u)d@f0Yrt);CE6;|{mATMZ zodeBP*(k=$g06;K6lX?5ZxMo$c<3!lfc}bP7_3f#(V7(KuS|oX%1jh*X2M`i2K3kD zz;InU^w(vR!$eCN^rKjHxV;j3+iPK@tpdh6D`B#u3Pw6> zV6wXgrau?KbbC4Ub)p!zs~HA6n_;r69=`N+zzB+8CsF)5_N4*AXP6pnfuX)u7$59{ znch11_N5uVf9-Zezo5KL2mVsc*HB93K8Xak3nw05wid{(mRK zE)H&ry?g?mqN4xFpvDjUEG%pSd;(%>d;XLDmYbW7`w9_>*ljg6G4_-EN7?^WabD`C zfPld5+hX_bscCCJv-I5aC&Mc#d3kw(J8#ty*#V`mqbL7Ra9;k-ojc8oGoL&S4H4P3 zCh&Ln;FXxXlA_7ta)6qewz|E8v#Trnj^D)91Rf|UDScZ~BZM02NIXDj^SmW+Nj-XG zW|lT{epBSUgL8(nh1lOOsitgZX4d)3Nf+0);0?$M*t6$X6Y`OMX@Nh*LHpTJqKu3h zNpyF)k#ZkI$EFGVikO_m99*+TN5%L4qzjlOBqVVEtbv1AR>9Y=;^IFM0?`Sn34VY3 z*phF{vu4HrotYXpM#{X4pTU!&i z{HB7vS%@A`ewz1}LNFPh`Qb;iQ9o!=UYwQXJZj1Li{9Wj^nHF@ejh?;@74ZVkX2q6 z5OCh%7hS%sZD?q`|II$SeVoonx_r(`(n0JO8SV^!NB&=LZ*M?xFCqt&dkXwbL?utp zH-1e`P36V=ILnJ8vWj1m{^RE1t8+v`Q$kbVR<($T;(#oNEfjyq<0}aXO~nz2I$G=* zFna#?)j?oQNa!C5MD*UbZ;M?;|6F?nhK4q!PoohHp5&-jPQd*05daWStak99=s)VC z|1SCm&p#^!Kka{;?@Jr|Mws}wUjF8%{Pa;QjWxi*QV)FX-+;HhA-YS}f)G~|h;@7m z5pHh@K1s5h5x9PM2Vuw$32?K5P!B7J^)y3%hZ(vnHirnG_Ym%D3yH|r!220F{tRMyX8cCsckdE$)vx5*V#0Ij$>>xJSnc!Q*2fIMh$Bld9#Bg`W2}gcKv@^Q@ z^@6M@Psoh++3+8dLZCRp4$2~IAs_h+Md(hsFvb;15R}Cr-vRju#W7w`fP8`ccwZ=s z^MsmYAE;09fhOb+6eB;NFcsN)_1CT#}e1e9;B&aJ&LB2r}^c2TIV_71! zAZRa-hn_M76-m&A?0P-&2U?JAudm2KHai!(Dl(wGA{*LjvZ1pk3%ZeQ@2g0G!KzI^ z0B`@R)1j{+0|x7IV5BY^8Y=UUKY?sN!R{A8GqU}k>kHvCx>s)7wE0~P#mKgoLVH6Q z%FCg%xdJ*q*C0Dz3tjDX(2x9tL1fku7g5fRUzr82wy~?0O-x<25jd zZ21tf-(Qj4p6ITEnT|4;>8eJwdKm1fgTd}5_}X0uQ^=;T^i{%_zIGTL>VfJ0W|$fN z40D6cu-IDULKx3to-Ms zq&T(i|9u5<|M(dmJ`qqb#3 zr`ls&=-cfZWm+E}XHS;98x^^z>})zhkwq=e`ITqnrn!@g%E%5SM?+6373b{Z|5ALj z|H_%MQ5m89tjA2^TwQa-oi#SpfSfHZDkLG{oJoSKv+K@HW<2es7A`bEn~2`i)x?uz z$0j!+!-a%;nz|;KXvLlTd-RcYeIKiBf(tqK0_2&0XNP<==B~J{f8c&r@_d|s7V-K2 zyWh_@{lCrln$VBm?3fMue(Uk>DfpQ`0}t~T5M=!dB0gwCjFTQjIqO5TivffqdlrRk zSb`UdiT%tV-p>ls{L%MK6#r%gSwK#x6|z@2$PKfGoR1%n4YG&aPmYio;fB6h`a&MU z7k;vVyl^{|JD?bK!}b)%xDevfk~kEr#<@aif)|t|`a)$Q`qqfPH&!L19ND1iR6nRo z$H%L|P=Vsk>hwUUN)Lq^6ld0Cg+pV`M`*}Jwj=K&H0OVUMid`5qu8tseLHL`iiCD# z8#+s3(OI8Bi1*sj+1`a>wk~u|x1%$>3!UlR)hI`2cyDD2`d*ldVzvzEtwr$~I=hxfq~^X>n_U8gBnhrlDZ&r?j*x5LP*E(mzea0lPt#=7+7~9DFA_MA_N<@ zi{&^ze1KEZhc`xn{x~|hj|*Io-aO^$kJFHE5fKuQ+&rBQ9H*f~Dp_tEAn;4E9^Zuu zA_B4^R4CXwaad502A$f+crV(UA-5@-FG5idjm8#u6*8!*mSR-NtX()-al{4g;S_JDgD`CS*ucTD8ev|wKAXkt{7cxhbx>I^{>KRcxog+_W0!q(Rm0ZHDn3R1 zc=I}QIIo6dzGI~A$LOucj~@^IFv^`O<9hdlNu84C+shw)T|r}+#uRO@qXK|u12K7PLfBiG%h~W&yh5f+8vlwUt6ZNJa2Kwg#L`(V@k-imms1zv_-IHks~q6Yv2v9G!HwwtIHi;5(h0i-fHTP>m)~-|yRYrvzooG#UmDx+2rHeOJUsJ{mDyDok_m#YpvZXRX7(*0F>p}yh zKB|}j(q~>wm(L_{T<{8IA`;`FQ|6@$XjD^rokAjo?NS0}QVcT_DXUG|C-!&ny0b^|=>A@$U5Lk}oS-tA4(6 zKLol?5WV8!S* zo6P_hH<@d&*gs^&<#UgMa+bqA^@6h6o7KgJ+xl#@5eGcXmz`~ebn};Wzgu zhe0gmcA9o4HIK#{8|?_5!Q_nP>AsMW5(yy_ROgrFf6lgYyPC3z zhf%(yPk^$4<3b6mo2o!rxCPUe@W*EId~_-Sw(n%EJ9)6A)bkm1TV^h-*7weW@r@s| z6|XN!bLwj>*Jx~=t>rjKzt&^@!Nhp*bJF_r=ZA@bia|X~$i2sMl8!iS@7LR;ZEu8F zkE!2_`Ei4oJWA&!XVj0OdvX$~JwlYCL;6liiMcy4MBd31Y0{W2fuy91s>G~x6p2`3 zU}Ft17mPd)X#pPaz_-EibO#`d@}7meK&SmW`20jG%(FBB1E@-ijKH9 zTVm?uT7LK;=x$kKV;(MI>l_LFB5{0sRqg3ue#uU5Nogrb2FD_a#_gwSx2Rn4RRmRb zR`XsU!t5>9U|DykCaz#(3v>W|w|8xO_9yR8>wWs>IG-36_p#y;b%i0dX=Luo9j}WL z;THVT2ThhNPK{+A6!Id<6L_}%<7@WiA1>stPETdmUt@qat&W(Y^3y4Lt%|fd1#IV& zPwzLGPHs_HDPuHR}^;+ZSqYuJabax$Uycwy|8|;yMq~Bm~r)3(LDjG zcQR5T=EZb^E#N3y5%ZP?-YqJ$IaAD+PcEEiGO*>o zLP5k7O1hWWAz_;J)$yBbj^dZj*{4B*08g5p!$SqeT@t(3JavJnAD;eI!`P>8a*K+4dzH;07+YWr0;g_s2TD537yU%?GGpjSr6{(0;fzw$Q z4w&GXTI))=+SnKk6GoN<-T4Y}>NZ>dw&{`C`N>Q_oDSWZJNdBJfs^ZSJS9GyNbUp1O2W`7MF&HLI zHm;ofl^feNlD>_$;{*+bJ%kvv^u4vG_#?X$v#1mqNnhO&&vLeXd!#)hAw`7!+O@;G z{4027(tX!@gOyb^YG$85Jj#B+?gEdUD2x2ohx}z27CKBYlkGe1ZRR}1S~Qr#6WkO+ z+PW&UQ@-b$*Otk?UZO9(l;%)M-$B$s>Q=p-D1bN9a)EW4%lFg)u^#BpJDr^`@~p}Hi#5y;@={NS=pC8T)zzI~ac>*;c)U&;Jg4-e z%@?c?u`scw+dCP1i|ab1dBykWM@j0Vvs%e^d{}?|N}=1})(+4g zzj!(4mF}xcG+(YfIW6?u=?9!m8%DqGRF=Je_kp@7j`5Jo8reY=`@1P8j|pR3w==}r zEd}lHmTzrs&96UPssE*DXVa+B{V6fr4N56;OkjXHEtdtYm%OQIftrp3WoC_cOR9;s z%JZqil{P2KtCwaQB4ax`-mdPVZ%V;XU%t+=N*jC5YSsDT{NmM16hJI_^QQ1gtZy)d z%^*%pLSn~*08*^WS!&E9Pi!#a^)wto6E|4a#F>F zF8VJ`E}zO%SLCNWc(1E_?Vfwg%dVT3Ni`d+PZ~D_@iCF+T{oZ8VtBap!!~!-oS5Cm zGR#WG|A+|r7N-1gn(ar&k5DjTU5K$|hlyzR$q{SNnNyvc{TBKRr&Naw+x2sh`e)|sDl`0_P-DK3E2 z=~$YEJdM0pK|Ck-2X5;VH>1M(a?NSjo|T-SDAx0T{+S)?9dodgi*s*YpU<0JI+1=W zdfwgT&L-|0PMOkf5i?U3p;=QdsD$BL9cTb&s(dC@U`iJ?^eGIawZS!-vfs0p_) z9cdir*3uNF(2G7rSlXw2-r+5RQ5r7;yVR7!CRI-CPV2swXHlhYGVp|kIz4PFbfn}T zV0dJvzTQ4GzrHpa+QoI=@X6~6TI&19VO&Xuf|=)z?e6myJgP!JR=le;xX+&&Uzbe3 zNKRC{d`Z|*i?UQJkIka0Wy{EP5Z~?)hokj485WgcXsY4h31zp^z4@9r>PV@YfidfwFOAG7$K(%t37%c=fv zu92;+ofr3CiqradFwsN#DS45kHPd#bHa^3SlJChosNu3MBSVj6F`LWG6(Z5Vo5!nH zNe2BVmI5B`5-_VD4&q%P4*1-vFji7LyN%uMk!%f(B$ELeoE^J_9V=EMDfh9`(k5Ax zNqAOxD}*PIN`4&~xSyBMbn>#N)#?=%nFW%YIU*%nc;4NnenKXS)rhh$KQN}Eb<%q+ zpvr_ULoV&BvH5qaz37m&zBPo&160 zvIx22;5Ic3bNX!5BNu+@-VAcVoQgSZOu<4Rr)IpRK@`?>+lc%x0J9k)(4c=ygM|D{RvB z@{n<_SpsWrUVSI0sMYrEwOkr0wgwKfxG3?8#{F2m`?NYIOBgSaOF9>Iv`z38a7%v9 z6v(gWSDtSrQDp_g3D4oSnuk5c(@isOkL62)S$vZ@E`NS7y!bM=$Ed&H!Z+8k3p^2D zsa)c;)rBW7Ce-Mi?Q)~)oe-S&HnWw0NVfkT->6D`lbv$?i*bQga( z@SJXk6}4zy3a5-i=X-a7a^78o+YdAEwA{(jVLG*(79Vf%CfYLH%S!2xgVuZVS;GfL z`RiL4cLtG?E787I*(##(T#0-Cn%wNfP>;O4jI$>F)@n_9|BopIW6lBFcU@UIE(~~#(aIXkmUi=I04%u!)2dBj&I2mFIQ{GC4i{xs_`}^~$$Gq?HYk%W+ z+kd>^X3$QXkuk}MtQ(x{A$|NfPqXBS$=Vt|we`|w-;s3ZS-(ei*|gim{S%+- zw(?w{kbnAIG{Wopn-kPJcVq-eBQy;QPBXcd$w;r$6Md<=hRI{Qg~9H3AEDx|l~WNW z7ja`vgzmHB0`e2{nBkT8wyHv0tc&@{zS-^Cu?iubugg~|u7^lkUgs9~3NMx*hGAk! zuS1e2Z3~~i9!|b2WTL6NTG{jFdw*@p=$eM_dCj>8O3AA1(^XzxI8w(NNS?-~G%dFP?$BMDZS<$MH`h6}bw#8J?Q~b+*Vd3axtJhuZmf!C(Ji5>CemZ3>_3RE!mFFGg1=A!0 zEDxC7w)llo^H34p_QCFc%QU6&GDUcqF`(P-$%qtAH>Oy*fE$* zEw(b)X{w~!e}74JFPr(z07k?~l;&1sfB;4{cd6Qat+{ekw%wt z(aR#YogDW%bbPz{Nj1cr$y!C*zD^I#oX4$ zlw|j5=a$;%QP znG4e&c|}ha{e;B(fVaojq!W!%!F%tH%oAVKB=sv|65Mi_c*jwZXOp(hG@7);PWiE? zxdntS?7eqYZKr9rF!tjK!BWeyvld1QuPeuQu`hc+e=&PTLagMOdaRGH={7bqQj96J zZtaD{HsS%|ssv&3V_L-4;Y4!B$ahKR@%tMY>8^g=+oL~b#rU>&yd!3t{&SVsUE+OH z>T_gAYW?`FJu|E{FUGZu9hO$+23si>c}sqp-cHM64vynh5w=pIaoH^woYXQY#I&4L z?v?S_eedcUAGsI1B1Lz^KKENm==zc)Sxu)SahR81cGKa%5_>q8*D@zbN?LU8y{;D* zVjU8v(~vu`e*3997c<{ss?YRGLOrdmD>=zyEsl>ppQzuQ3)#tcLr~mis;Kvn(@DL9ZX2}|HzF}8hTk@ZF6%O%Q>oV5zjZ(m8}y*i5lF>2PQ zdQajGl(ddmfePU6a(aYPi()^r_p@Pp$(dM4YkM>K_WQXfc_sIp(ViRWi81&-`QG*D zqnso@<-)WdRm~G!RDS0(%x_fA_U0CiWgcM38q?YjSsFgyXSpwx$!jD#O@nI56g;8 zQop+G#D1RttBV~CqE3mUx0%j?ydBiUEwg!-R6Hu5T`m5!e(h-YoX?daMT`Z@)xI$H zyU~A%i{RWJ8ZnrDY;Ef*WW$JXZdE%WAyxm3S2OCQS%#dM`n@>yZ85{d4s`aGBdYXD zv`W1D^MjHU6?a9u7|EVl3Du}=Y8qYqltUuReO~12qPs(YZQ#R561eMU(T!E1GcPlj zxoj19h`LHSj&qx5nJ@z}JLe-=WiAOHk4ml&!N(NB7n6nbzeY##aSL z9&N?995FpxI^RjNo8-7pJvyRKZ!OBp2ARg(Nre9aiDfRCqX32DI>0Q zf}&D6;KaL1I!m4lX{WJc%i+sc%|#E{T$twF@|e_|o7%~noYX-!nuXq+obK=o+M;W3 z{Inkn+D+U}w45!q4|vixVlO+lY$W_BE=urAxb^{CvVfbcM@~fWu${Ta%qkMyT|ms@ zV;EiXS|h5!qeS~;_$^O)FC`;AozckJtm&$gTv;oj_JYQJ!j2T8Sd6YB*~I?n56VTo^276LczVVike~M z(xDS+dz7oi;*?U#t!;cj@`@OzD>_yEMKd&3nV8H`&E^lF^SP z?~J>glB11z{kE!2DTZkHlzNx825;3B<*7FU*(K8={|TB{-1M3_D9i2n4#2@bw7I*YuA zxmLV(N`ntq%Zsl(&aG$~Go-q}&qs3W6{YQoh2%YH)Dn#k0{WDwXO+dm4w66|1DU*= z61CL_)raW|-q+6kXmIymsSDP)*RWEkN=3HnZ>*iFPu*y?-gMx>w=X2xQ_nQDn26#U z;s-NXN$qMgw6fh_NW4qEUurg%hAWIS|9r-ix}hmbYPsPX{e)l>neBTYa>{*?l5C+^ zTIW{@O});8*}Pn1Z#}k0ZE;+SsWzvGt4)76bmhUtleWX3F7jAg-S9W3Eo-=PX6eP& zp}UGvM5UTCvCDLnmqtE_)7h5xwo$vO7itRf$JpHA35mEXrf>LokB#rvevU%V!NdFw z&zPk#uiu_45`oI2auRA*AyoqRC~_0*&suoN5?k?bqqTmabV>m{7nMd##`=>@W;%~)Y(GC&MzTJ2^++|diIRt1DhhT33+^c?DRGI(uBtF}9mx3=k&mmAvY zuv!z%``GgEk)-pAEUR^bS#mt@+hUH+$fa=V`rBw%)F}-*yb~6_se`?~&HOrd36r@n z-4Q9w92wpAOE@Re>om&Tbcf!nEL88Q@fCc3_aWmdF+BuxRV`dG<}rlOC|bA3$HWwJ;l0Yj1;qG+^F<>#vBsrf(xQf zJlJ70-P-U`Xl0?0YXBRPypl~FyxxB7-bLbtxx+S7ZxklOcfCSx2bQ9ztNb-*WH5unZ?YpnnJ0}{nPH#*>M&& zZ*Os|60=^R*v6UW!tmgD6Zc`d;_nGs;p6^Q!PGokoIhE)71u>Ahc6yP24!h==aL!o zlG%Avrjo(SayyT>JD4Aisxl$oHo+%YTENdbLP<<20ydw%lN1X?@XKFj=6*KsK@}iI z%1UdevitiEk#+|s(_*5byE|-U1I_Q}2sjy&-It)QEMcOr?QWe|pZ)&f!?hR=F2<)Z zT~|xiXIoxlz_34XQ7@}hPNDly{{z)!r|$1gijkQo&YT~c6kut|IC190uoTFb4DPNC zyLPg2Nl?X2s$7ix<2D|KCl;1o*d}heY{ezDCPnL+@9AMBmicoYYt#&FAJGkkz0)e4FIDpiP@6AH7ZAX!9w3JF3`j z?ARTNEg~lLhw1D^m~9w7%vBTzF)pp`dQvfWa^E^l1Rq}@A10KVemZNr)5mFsZAI~p zQWKYi<3*0T?!z!km={edt^{6=JitgYt((=sLUmBjLAL*-{qe4ohkETOuxNwJ-fB8? z{xm14)==sDGjb1mKD4Cg+1SR{cBFi`^9s~N$phl{9^3vhT&h~NA}37j{8>frWn*bh zE$3P`Pm_d!cB0!N2Q4{eM`rqSZtVCn?D@n>)3Bk6yW08wuFDb_a%%RQAK<8n`L}|T z;&tH$Pn(RAymmAlxJv)vyy@L+%iGx&Y==k4*e!ztE1o+Gi^%02tga(_dBhaO9q&E{-URv`O!yB z+Ox-M`XDB$iavFukQVznI+L9C%WHrYcSrf>tdpJ663(srTb;rZ3gAK_tRo* z`0bjgLhcBAo1FT~FXgJF>sRZBp5+&fTdq_MzvsH!v0FOk{f(!69^Af$MZ$-d4#@^V zVyq>_DbJjst`9k`i(8v1z3nc#j+4k>9zCOAX5+uU&wP){9Y6o#7awW;c6=xi zomZtPtRB_$>Xdzu}wi9hZeDT;X{Z_P%*^exLgzh+O$j}*s? zJDu1rdWn}g(LSSjr){@SgWc2nnNarBWyePC=>r-%ChW3Vcr7lbBPQ!tlsIRHnR$H`XOob%^&S zpZ0{(TBd#b$K?}i#(^;_`|sXAy3LZwVxB95d>4)OapsG+lS(CW@8VVv z?cVipr6W`nq9$0saPwuAuQ=*ca)t`o_MjqH>&kr*~PyMUe=t4vKN>o!F? z>^3e{eRxS#Hek(Y;;piu22Xaqn}KxA!tQISduTEYM8BO|mASH1uW!pQbyHTF)=c}B z5cY)i?Zo%4gRH#X9|ZS_-*htOjo=iy^&)$2hl>#pV{n?RH#yTT(fn|qq^ScQ2UU!+ zg>zD5xXfx!A3qh#V{tgZI<`d9R+IXW-7Xn5W=UDnK8z;qVA%D2$^)W#LE9^5w+STr z)?D3^XS{1>U`m9n+m~sZN6K)n@7%ZIV@X1E=_2$ktbwyiK$}H1m*7JT^TlWVRTX$Wp>X{eFrAV|96E#OaC()!m zjJXi-()BYsKdFP3ek45aoxTkW_uXCOVUZ4}B*b>Svm};5bN3F>#?)BFw zh02!DUK+dM-jRw5e~#lEQCF!fgY1R$^*mRvJ#1y7$;+Kx=s3c>+NXUQ=R(u7UUYqt ztTtvR`#Z*k>C79e3r~10pnK2Ifk!M~={?p~7Twu{o6h9$_gqPs8{x(r>|vl>AALn{ z%psX5Mjppb>+GYafiLX5GPSD)p~!M|GtmqTz-PD8xBs43)31v3CEGT z$KP$pV`iJTrM;-?RU`iJ^5XhKweq7yr(dd?QzrY~^OLFy`{rvk`^ATYq?VtjW~bPR z{wr8j`j{~#P0gK>1@YO5X3tOa)v2B8TRh02oPS3F1I=5c?+Wjf7mR(J)921O@^va*Xn9iVh*tD$SkTvl{ z6lvYdQ;XmF3u67x87)0f2{GC-bN=SbT0aHtYw~48@?xV zZIzt2Kd;m0=(WxAbP8X6R7=y*Cxfq# zZi(mM2JX?py{YcRrkBmxfAnOftkv>mzFns)YfVp7?Xv618~r|p?R|CsmU#D-m3rsO zxi8NpJ{@UXYcn=@x1-E^<@}DZWb15Ct8D#)Mn2R`3bIumpBye=3LnN=eds!e0`xm} zHZEy#_Zh!GIe;wxQ+j23^Jzgj&X9<>U@aLnoj@OR?jVNZf!zZ)rWAZ*B@NJ%tPgSomg_;{aP{Ft;qwIZg*PL z5NC89f0KQV-%j0p#brlS|7U|6EbEOshHcfJtP!!*nLd0cM`33u@KUr{!L!Lb7<2dO zY681#-sq@*d#W(AFsmX*NpI#s7HoDt^*w=1I$JZtAnomiq;_(J-HNtMIBpEF#5-$w zS)SNYvT6&DgS*~owFL0YMN~U}^AO$IJlvvkVOEuEemC~@>ro+E6&c&B#?g~jDVM4a zUJfVzYk;YUfBP+>>!tZ@80sTRO5IHPu+?sb(OSIAuZzW^&i)V`Bz8yo*olxFott%lP_S5S8? z3=@IXZh%3UA<0rCf~!h8lB3+Y7Dc!VQowU8h$O(V!PC`0y90md)&`WMhjcdmK_qB2Lgg3ooFup?x70M`>R2z8?f*9j*1k0kT z{(unRLI9Lur`yGNX#rWr3kRJRY&!k9(C7ukR^PP9DzXqKdTI=paoAYDaFg>LoH$1qMi>VH;y8h2J4oULX&mIG*2{Gs8re9GgT;k~8#C?B{@H&A zq|ZFMB){_FCV9VXn4;Sj0DGg5rCIXvy+QxK-`UyuuB%tqtcM?Z0LRWOf#jQ5wrdof znvb0z!}aYE`Xe9_5~&axcmp}O6;i@t3`ip^%(QXt%sC`UsV&j2veJeCOiY779<3O zlps<<2q1`JNJzEamm%$@Y6#}04Uc*rAQ;6wHCZDO~{fcL` zxI~X;S>!|=$+8UNaeyFD-v4^T!_M9g;&6mCNz7ZR3_t)izlM`1kN-ZQWH{Nq!_sFT zyK@V2e*Z786A6hQ{OIwb&EI+Y8i53U=*biJyp*|Z0gz0@P zRhlaZaQ5^W%+0h5?Y7WlnDHEJ4n17k>|q=Xk?|B99FWS6v@8NgW~loP9)9p)5Mp6_ z>jt7AfW>H4^G-PJv=*Fn*qKbMKbM6+jOO$|MyO1Cz7F^%7%Bn+xrP-vF|^*cogGdf{%Ul z3Dj#o9L8|%+A4;lAr=?r(X9EfB!%lzoNyahXnDBNZD4h~k2r}G15g22}aH5q>y>aE3F(aY*u{?g&e3l`V1y2wCj|K zQNH}5Y7U@k^8>}!W|UE!K6VV(uCHTfZ;)5`W$-LuZF2_?K5!nVjvYh2<^u)V&i50j zFmsh@s|h!#*b2lvu;2I;{gyXj`sxLQk|IZ`0;{D;4KhQC0Kf%D6vr5cA<`s;@B0XX zNUaBmBS?`=nOC-L;pp=6>+M$STeyelcb~qtuPO1v_krfubjt$Zm8+X1$+Abc2jibQ zf8r<&#{oDOICthWEQ^6L3rU(`I2>SacMqMJ4pvr{P;)(4jKH%koLT8$Wv+>hUV!!P z5F!g9;$1v;;SqG2Eo|)Uq2KF4N&&|7?z!04rZ120Gf|Ke;#dKX$Nlc4ytIwg#SSh zA`ByBT%cZavA(r~GzlQG6lTa)2%+{WsMWEwwDfN++fH#0(dWPG{$cc+(XAr@&TBWi z{U3Mh-Xk7iSY5r2C>Wt$_w!nAK+Shh^XHKy33hvZ3?Co|Ta*d$3$I=?LqN@w0kd?^_!~U;CujvE>nb%aP%wQ`+ zYqDUb$^+7$-P;>Nw&!0b>+1?KV!GK8+Jg%djMn zW)ZY?UVZMI0V__%YTu`yb?PCfHzOreABvBKkEIZHxtT+$(T^&+(85nwC<>zpX_kT6 zHqtahI36O5#*iBRYoSanIJS*er;{u#EdJ>OXV2a-hWmqO*9cIJ)qf|_EeU{EF0Iof zOFue};vYM8;wXlLKDyl<)I1kyl0gUwN+}%Mg5$W#ByemDh9g|PwhBTimY0{Xyf6=o z5C|bL91L)LWf^lbbJ!h?(d}-7XDJ9!sS8*2eUW`9G@&NEFDGmn$};kIrmwDCtDrC~ z+FsEUOPuXxq$F#D__vFfvJTX0<$ge(<~F27d8?A$C)A3uuunOQ(e#8CvBF|{ht z)c&MI8RbpN6rVCbV&Vz4f|k=i?Z@z%mv!7<|LIkK#K=Ze?aI7wC0x+sE~P}0sw_y0 zF-T&gHyk03L!@b=N3V?vJ zi87NKZjuQbzJQ$*TAh|NUMC7oQz;b6HfeFa`RkM!QXHFRd>BGOE9#;T5}=Ht-EQKP zrM4!cgrd{wpxthRQi3Q9L5awDO0R0#FWXKtJ%x}`73?gI?nBZL5R)OxyiMItSvh8o zRv{6KZs?F}#gS-SETx1L0+wx|UT+}bz*ct`wqv8->cFz+;rTuszYf3NgjZ{TSQcz4 z+1@z*^7Zwt-+yCAa7f^L-yskGW~9@2&76GA_`mz~Dw%ZX-L#GSp7j;IqX4+P6%qhA zz358-c=63O8mIiD!!Y@Q51cuM(P)I>V2D=3hs9WF2MY-U5iA-IDpIY6D1Oq&-cuhC~%P zl=mvHFP(+?@5E#Rt^EZG^-5^jYvVczfJ*^zwfoIXtBy{ykypv3Ad(}AQdRi0++ozb z6<|DpK3;W=?!){A2OHu+yRoXsz8ryyb|)1<0H7FCl`U8%6%|6)KpGNI8=3kwFlJ*E zW@xvXppFG%TkvWPxV{J1^l;_ek9>Dk(|_?}r`{v%e*gh6`J^1eUESLKVxwI@(QzDHTD=a(u?nxYqUtQ+ z83z{vnNR}3&NcsMKlR<9l%ekX7(@cyooysZtTa@ixlQ1mw{A{kJ$p%>_@lko1`<%r zU1fU{0awqTzygK|V8+O+tLh}Cgpk2G6*;8O$hRC*nQ00k0R%kDR7M~dC<|%Gi9S#C zG9QO>O}_COh`vsFLCmz?^0WyAQ<149_g0*MliQt>vTubrlE|Yo>K8^C9LGVe?t|Jk zh%Io^JA*--0*t|8Dhg7V5RhO}g22Nljh%}(w*JXur|rLZ;LOSExWn{AH*x5h3xEFa zuU{cQ`tkP;^8*JE0B3T|4*)N`wMOD3yD&`BzjEQsQH0|FqtOtpMlCN~p(jBJmkZXA z5}b3yG6okMwr%6Y@#C;8RpYCsl_mf^e7zNa@Yj{(hXhKIp8Jh2Oq@mUqcP!)j)QQLf}ZWy-oeja<>PJYjdX=Chb zQr*44xUq>`OlF=Km-1M7jrIYQkl%bZ<4KLJ+bL1n-*ugQqY@6DW@y*z;8uoAmPuMV zAxhm$`IB6)hMjT$$m-VaKfQeQ`d>eN^4Lw&f$t=l+WvNs2LSx~SKcHNz`ys|^Y@Gp zIg}5e?%MXnwVlt`8s3Amo`<#7Rah2-W!V+acj=1KjF$h9Yv?r10IA~PTyR9A7;zk{ zU1EvB{Dr+=_QA|3o6=<_`ZW4bp^En}M;98Z>shWfr>^2tqy-zdA|*OUC2csrv+hGh zC`@Q*6o-`~km%>j+ccg+--;H(WS_ET#TXccNG3#2B}c#;PhLbR|en=f(( zirbNiHYgZVATQ&d30sKqc z2L1Lou96>~fcEc1U;4z`FG2o|KfOpKfS>%#xqZ+3se6d=H66;cnr7n2FiyT_ae1Dm zafE)ahnnx@ySr#TP>maFMy^(>1=1|TXdGZT93hIrybxvHLeU;zYF5P@w78FE)Z$N{ zJ_%{_&Jaozx-cA7l+aS+PO}G{QrbNnH##TqTO;sO_pzYMRglR zzi82RX-k)ymgdiHGO?LwQ}*5b)4$P5ABv$h{T`(Hq2d(H7eJNcSH7zfDWQB<#rx0$ zqWWuDHeA<*?|EH9do2!&)g#%PIrCPm#kTd7<`t~orcJb;-+y>o^ z-Fc+EeU$L+rhQc8{pP>^FNt^$AtQN6P%Wf<4OR^QDHaD7zA)7BIbnHc+a1lW}zW=DCFc zM1{%9bE>~lqrYa&zk-U%Uz{|(0Lk!cl&C;`GsAF&wl=$>O`M4z%&!tC@-L>&GKG_Y zQc5H>aYF(yEerqzL4fUUA2+tU;8}upr-?CV;QG@1#g&W1lzBE9hloc59P7AGec=3= zPoFrtl;C~3WjZhd;6m27N27nYJ=1E?G{#=9uZmVuQffSEPJB~qlR_|{fcONIF;G@6 z3zpiUN40iW+B4r@HrFI2WUpvhm^zOXPQ>!}Tx$`Xo=W$*A1dwYK9M31!9WUBx2@BY zt1v;zGWwBa97&oYNmIm0j3iBvq$x74oT6ItP)cE0RvtFBVOtg~%D^ZAWu#mJ2;|N+ z!|AH-nm>c?i$+i7SQZ*u!z~!^Nx$q|(^G0lP~)BEwnVfLI7dWB3+8iha^C^-drp>b?i&*09+RpM2~3jsJFiW9Pt1?0uT11Ni`+fAc!& z4#yV~%04*LXka)RK#CNuQ!BN6&g6#TS5(o|6?I&pLP3+Mvx^f7=AO7-$JvYXCVnro z2&-y#8s3+&@uEgRRp}S-SDmZk+Pap$6apDn+gW9tLu4Fki!R0FIwUIdCn{74z7{FKd@BHo4Z~W;+@)I|)>E8=f2!M;50Xer++xI~;p0b@$`dP>K&ol|p z9ggx9YPm`>k)+lt#YB)OPuOR*IU!S%baEb%2~ISoT>=koBzZ~PW=t@5O$m1zXF~lTok3+pm1FWtwtSDk|K%|1W|-e7J+hTA$4`G)2uU)zR z%a^X*`1qLEtJ~QbLO%?!L+_*XO1Aq{6ZYKynQSZ!I zJPMG+F!Bq5&!@rW^dTVfSHFwVb0icf|wc>%~Zh?o*67k%I&qekbscL z^2#@q5LM@ZsNWc+sQVs#&quvpL$gswtyY8Y`|y1auHz__K=OSNgjB*nltQo^zA1cA zKE0L)roX0O(usjGgy-bBg-9S1?Lz-9E6zqK0YM}f2t3clY^#YVPO!P1VK9o(YTFoc zS?z-%KqLVi+lBNw_C|wK7q6}T!|m;zzqzn5zwcgwCLPvEcdJglU-{!#$(O(H;d@?u zOql?0ZUh97IKNz%0PvM(UnYYvIJ-BB9%wg#@pufzsM1EoL^Cz`B*Pk?^@0&SK}f|< zRVM$K>0`z9rMBO%N-$_Rp0m|jV`NT^zjyQ_d_Em9&I7$#k5uzwU znsIQRAu5C_XfzsVHXHDL5034CQKqym)c{*d*P98*eNdPtiNYsRX{rz!)sSeBt5h(%I;U*R z@GuxcUkV9M32aJGtNWNs+X$iryL$s9;}QIN6JaKdHc|K;KmbMw9LGiG)o^)p?{Bmk zwXXyCCAXSNw*TZ`{h4+^}g9T$O3?K%cb@|zg$HE-Tnw5q`$M(jgB^&2v~x; z>y=J1DOD+ILl{)1U*SAc?MPoTf{fR*@f@eXx3}!wA<*k+i16&Xw>WQeIJ%>gAoM; zq!7iu%6Nk6MWsN8InIS$kIFj(scL(3%*w@v`hz)r$}uma6tZ{+x>v46(Q4E%*J&dR z6AYsOGj;^W_7QVlwxzR=@<(Hq1;18DFHP8+tDFDe#n;|^>amB;e-m#*KmO@*Md9Tu z*T{Nz*BOV=O0(8@SP*$8j^pDNW8+56|JNV7@X)I_Yv=#`R2 zigb#;`r>8xE8lqbe_E*df9Lqp3|(28n?UE)W~d4Mr?jteb8Y`mD6Q#LbraW20|aP0 zd(snKT!Zpf#rZ{lkzcF&o$;)wSvMXB7>-7W!Wdbex~r3)7*k$*&v8I=hn=p4pfx|s z>laGnUq)Hc8KKs;K;{{MDr%5{bFL}}r75B?LJ&m=f(YX%LdH2<+eWkQqtj_&wmpNn zxmmQDO;tUZF;ziWO30!Nc@?F}QgthMtzl{+a|~7-O@;kgLJUgDggz>@lnKXm9)g5K948o$#@OrkF&d4F48<(Vz!-z;IB;DT zwrwj1UcRiC5?W*qmPkMq0IixuWk0191(PahsyzIH3rI+{jkm}JL7X5?VuWFgQ830R z2oc9AC?W7Y7c(<$EX>WR)r74ke9s57O5tN7f->L27}N=-pA_7MC*F9M`gxVrUG2~=Ibt;7e@S|xlmeej{8)BCIfxACS$YJg+ASD=%2}vyLo3G$ z*jYUYpe%$*2tmd6kilr?wD~P&EJnwd;-GDzd>)CA!mX(aeBQWMxznhtrq-d8#{YLWB_d2QQ=p|g=JZw z%mSqZahxI!$C&fz>SsQD;o+6V`9thPeE-ma*8-eds&_AM2LBfYo_yotmG5oTYnY#H z=i#gp320kyQp=i5UuXxS!6M2Z^T}oE%;Hmiex(p545puONR=&A8DAk$T0r4APB0h^ zv9q&_-e6Evre%!5v2FOihgz);*RjDYrP&?Fh2z-pTo;yY=V3ZEc{J?Tq#^8nW{sJ> z2dOr};DW2I!?H9lAe!bYDmjFZDwWpv(P_64Mj?j7G5W(H!Z<>*m0~y=qt_c^eqj#t zbF-+|>I#)mk_(^8HlhVpH%8eOm?k_3Y=!mJ&@AvQ)~jEtv9k_2gzK&0wDIgW+7P6MZpETCR#S^KUrLu zd(W8yhgu92W#ZM7r_cSbVVpj?dgI2T=ecM$dc5S=lPDeNnyVyX^JdW%b|6`5G6GCBd`J`*tP|WG5r7Ry?3}I z>0RIZdEW|Mo%_T|XYy>+?n=AbRR&=Ngb*NsYy_4Jm>CmjSDZaV1$?B*(x9vWTx_WvDOOuek|ZMM+Kg8U)M``IN=5Qs4maoEI4(*E5+G5aBZbf| zRxTo68fRiMpimwI%cbHTI( zFLuP;`%9OVbT2G*YYySxf9jc&|F+$3J9pi23@_)Jj5xOD77u`-!IR&~Y|Iwj00A6a zSmt8Lk$tvk`;nIampTi>KSx2y?0kue>2$iRZEUc)QK!>06KO2VrdZ5VEte@23MNC( zc5z)(#8N1j_`Ky9*{Nbh7)JO(pPt{R)9KRebnts-=3SDQ>_8zUNt_VIQ3}9GT-PS& zy10&oW4q)FUK;=R(zw4z-g8a0r7!?t6h$d>_X+xDtd`P%QuskYtJ$K_YSZia1}x0E zjE~jWJ3YJ&gaa%zbBS6#g+))g1^gE-@!(cs#5e6T5+UD78 z_4u;SBL!j%bmdoVa9MW-6!c00xspq zUC`;J&WWJg_?M49@$~nORV(7~!2?*9H2&GPBVP{u8)^wjR@SPwY#;Rzw^ghSK&2fn zf6Lm zd#0z@w{I_FwJJF`2N+9W;EoNvv2E7VC?>Ft>C@6IU{Izt+IVR|8SXd~>|3{O^U?t5 z$I$3Sbc2NXg(bfDh0n0Dw!*<81DId)H%+KmF=rvB;k3iA#n_Kn5ypucsGVp1Mp?ez|QQ zv&+z@%z!^L0$Xfjb_V-h+PK507%7bwZ8h60FR!w`u}K((rZ^>+W2{=GTq@$F2{t*` zp;#(XEEI7Zha^#SIz2WU4c68-Sy|tp*YnXzqqIrx3Btf=`Y-U1mj^8iOEv`CovPij9WO96r ziS$)2<|*V{f^sPp1U_LH;^sWYY~#Wd3k5bd>oi&|R@OG?^m_RHJ~IanuxEM-&-0L4 zqy7{2P*|eC+O_7P_itt!D31GfnmJf{CgC4*v9agpcwo}fske#SH00>acP;& zdV?^EOiQvto@%8`xm+6f_k}`%a>=0R8t8RvSU(B~uLMKX89}u=BJP-vbC|MRofhbx+6ot?e zLMJnE93GFt@XlU8I?-r%rrVuvUT96VT48$90DR>_9w{M+BK%YkBuRqOnkbBDw%e?& zZ?L{zrymA*xg1jy6C9k`&)$7|C>4qz#Gqhl`|=n~?;Z8;2hSs=fdJpOqGb9YwhIXJ zrNL>oga${yD}~7sxUdm&_WVT@FtcwTGZRI|^J!j?9=aH4^S9E&M5D|y$u6(nhuOIm z7FO#N3obcF)A0KY!ZK+%7OhdrfUnJZg9A11xkv6f_KKs24*s=?2way7kkNAso$-n% zzvro^pM77W(a7C>=kbyJfYGxEwj=YSXCKWX%$R-{zT4`DW=SgolTEj!E5HbFH3}vQ zVFpq*S}hhAmswliAc|vL$DvTjGghlnDiv`Y8!zWktyCzLjj!DEeOA{tnOj_BesLM! z4@}%&N;Ws^Y}Q+J{Vq{IA&SC83i$=MkpIsQJ^0|~s$*k~BS#Mpa#jHSn!6{j>O+3+ zjn zE-%hMY_hnrO0(HUO3Bp3I7g2hVrJ$5l~M^IAXZs7o*iuS5wg-vqxegf zQZVYrGs5U{+hf||?y;5mM?cGi02otVYZEfGt(4J~5fFoQlQI5?(8-oXwoe$tQoYBS zxn=r6%tYCy6_^C8EFXguS5X)&!0-ELzmvS|j)UL(+J_$a=+&O#i-m4T005j?>>Slm z@5i2a`q>|@luPo+!TktK63b|ljO}NWlx0$CUr9dgz%HP?n`t6_ zclZ6y!@l2teY4ZQtJm}0QnA3y%zpMwPf_-45(Qn~r_=2cM==@*gMhV-4VG3`*=)2x z!}xfWBQrD19Gsz6tztqi-S@SNGU@91&TcfG^EN3TL)(h#il zk3?bxxIHGlf)=T5z8X8(SsCu)NPmhCy@Vn=I0Y=;F#w2-)D zf$enlTik$b@kwbOT+1+jyQ&o^B%pP&%)v|r)STz zw$UJ$%aJI>>gqc6dXsLqM;Jw*G?9w2Eb$8~OU-}yryu*^H4m=*+{ra@rFr(F^x3DE zZP$@isl^^C;yZr~`N@ef4j-CfW_kk8hISCsYPIP1O+r(WBy7|- zSy)(Rd2O9Aj+w00ICA(9M-CrkVr&e@G8#2eL-ToCFkonjW!j&u(Vvk~45N=@aGdQy z+A9eFxKtQy`D}+fH+r5+DBbACu(00Y-25{AFkz}x+m`E-F3zEF*_t02%sGEH1OUzD^j1XrNLm z8~-`=@r#8#)mjzTb!awQoVzg33un*a2N90r(C+kDTic}B?hpli6v`y#BypIN_P74( z&;LK4+ReSY7G}R1{mGL{wr$BNSE>iXe&_X35Wa1#*}X50BRR8gABPU^XR4ATh@stX z)9LkwnylGoercJ-l~sCvz*wck9mkJy;`ni<$17L@LX&VeFzfXwP&cC8M=gLsCcp@n zZjbEjMB8u3b|&Al&&UUtJKj#m+`ciD3wc$ z*T#@iu)49ysTa<2VSWk6bx}#e^3n$NR*PP@N1SLt;rkJY#4mc@kze`X|Gm1~d-u7M zYeH-Ix_fVq5CHhp!<_Knvn;@*4i;P7+}t)brT(r&kn zuP!C+R+|fRbDX=lK-=%LcVe8o?>NrUBS)ARuiy%UpeK4HmcP>lY}>va;LD!-_D8nW z(bygBupF+q&(%KYL~wDX!G(oYw2;&al6tp;)}{h6YfWwhfJwvdG+T`2*!=p3?mckq z@S*-LFa5Tqo8kgw^jFU>9-X@|`@wdj{kBIQdVsv=5XUiDdgT@kpV^Ruix6yi6C=p+ zB|zWkpO^kEgdmC{);2a+m|vpaY$Al9SST-ppJr)g zjbfohtJ7g^b<-4?1TiX!(OS^$`)IBFaJz3GRhOu1m38 zN|_%P7MFPJsb^VU-K113vr%s`zr04h)gg>yloTi{q0ws-$3a-D6z~6PGykU^Ulf1* z_~K=k{#ze95WnGpedj~%{g&lb{@tF*+J6@4i%&drlBb_POEZA6LXK*+iX|l~NvM>| z96oZ0qlXSs&gWTgG&wzcj@fhP*=+St7{uKgeFUVSoEW}mw3DXa&ivO(mwac$zrU=T zaMg`wvjvp$Im*R6wgrBu@NCDJ{v*skdJM<1iIlLGHk#jmt-s}izxVh}Rk$~Uu75oE z4%F#(lU%Oom5PPIpwKN^UTm#HA5G@jK3#WMurk#0gV3PP{xS=25aUe~j)JhyM!im> z(IAc!@?MTwxk9B}#&KLqr4nPcDgcX1D?IVc^VFMd%H<*}YYkS{>h${oNoq38?fk&`x%pRjdOdmn zOYUJ`*@p3P8udCs5K$@?Idb?QLI}>y&9ho>AWok(Rg8C>V7!zYX!Yzqk0S2_rr+k9 zt8Lc6PN3dZfAcQ3-;hudP;j7J%;V)8dO-p*hif~8aeu)410kR_(vm14Y5Bojr_P+) zch`xdSL{Xj!QXpaXf!|ij)(M*yyL-}=K9?%y2Ju_>O$+9q|_7Vn}hvxu2aevi-#sB zCeo@fo#}A01jc%`x zZCMnHMGD0NQd;D4IjYqfAX!{q<;iDG((MNn3VBx7nyjxkhys(ZFNHuWO`{#q@k92E zS3mKQ|MIVY_1bRnkUsnLviQtX%h#m^-ty4?%Ql+%#Fyv9C%!zl<+$JY;DHri6n?8* zo%nYX<=k&ym|ygteEK;SnlTm6qE;I-*^O~Rp-|+&f&I+v+e^7nV0mMclc!E|;ldp4 zKp_N!{Ce1Ckm;R~csUU4*Wj1y%YSrTw8KHRZ?Ytmig~J~f~gFR5?sqdtC5|vehpbv zLrTI#=V#{^f9%O;&s_0&{>VEX)Ia);hi*H@&u9l1;2N)WI~?Yo#?o6tTKi=j^#T&R$qxWn+_W zKST+OL<Ql$#g;^N{mk3ID)t*(z_+iY&OSzoIY1OWmCTA_79yVs}H>@qc0n2lob zH?QsHY^6cm`Z{TJr1dCiy>*CXMsI)Rp0AFJ9?O-2WKed^DJ*{a^~zT3kys1(o%!bEIt7Pe>}^=+m%Va{lo2F zyo^A(X4>U3q=39*z+p#1KTL3J3$4W#H()TeNSg+y36jOt`g_V{&$$U#`!%CWT!3r6 z_b)qI(*S?(C;wj=gwbKwa>nyH7faeH2sF~ba|GaHl)2Ln2Tdwc6EahID&?H4q2ut~ ze3lgyHJdG-KRwIyFT6mj-KAVAF+T197Ez*@ELF(oODwLg@Ri4(q8|hl^944WUF!7? zNusb2M9Spxbo(Kj^$yjt7h3S9U;DZL>xL41-neb1{imK-6yNx=8*evtrJ45cc-7$q zc;*vdntgw*QC!U<(p$9oKowRjT zq&7B&(3&6!D3>Z6oH@Yc_!xc|F?(@=GqdMdZ}xy8(`&XW@5?FlYoW`6c-I{=xawxW z0G+vgq?y&QocSpfBmd1AZ_(BDLdFD+;9rQKATAGZ`j`68b?zw;$pM5&bBp zAH_sbLY!z~Wzvha2BA&o77ZXJN@-fH7K=;E)EiB-R*a2R*t=(na=AjKTw!`@8Z^9c z`V5OpD@;#K5k!iO^%ijynNe72B6%V-tMxX1te6_luO+c~|INBjqx6;sch>$|8^8W$ zpYLzJ{_a()F#eH!6P2Irw%ZrK^7xZnXvB=?EvjSVXf#n0Ggcep;LLue$7_U9%-q5v zbMx~w(x~_4V)|D#`LDp7SLJcdrBdfg+ppo+0w~&oVj+)X3xY62S~f{C%mFaH0@7|u zdMFT<*EioXF+P^R>b2jdbk!)pM^XpD*{*TW-|B?MuWAgmc`j3C*u_yMsuXvcf6_+Tg z(QGvkLNGNsMYq!<-~wyOk{BxXQN6cC32>l@)wrIJZt#|puJ{OvEljoJSuS^$=`N_j7TqUgC; z()i?wQpAxW3>AJqqT7#5ueW|gKT3!bGdxp}SqUlnFHI`1K*ET&9{`2})Zg@NYj@hL zuC3APba5Pq>4{0ECML*xdCC zTQFJ4&;G(s|HP*OKKt~F`0Ufyhv44`-4sA<8-4fRxVsmM+<)6YS$lt@(OiH0>1V0; zVDDIwVzHb8p@O}8_p)#AUTn)^X?301*$b?0wn)^@G5r}f-^OABnx=W<6bJoN$ zh$7b3H&|U=C+G*1ibbZUr>NGdCAwHjJ$X3w3c(P~jD6=^nmblP2{mWU)lq!nMW zidk#+h{A+w-VHt1dq?*BwAME~Q~nRXGB5t%8vXmXzv|{`|51APn;&S&TopDz%`5uTT0@1AKU#>NQan2QSwoI8Jkjjq8$ulPf*a@Dt*@vjFq zC^%3q70Kmnf-nFiVLu`a`uM#b{cab(+s1D<@LP4d%}si}rsenhQdh9GV>3B1POVnO%b8yJ#bS|_)io|$oM&vT zMigtB9W&BPXCugj$v|j#eA*o!&#|eN^B3OrJ>N5!4D*Hu_Ui2@{*~yHk1mKmd~_?b zt1Ogslj*nqa`w6w*>}I`{>^f2>gUGt_OCtr!kO^d^K0a6sE&=3b6k=np<1o5Z|@$8 zp2u3F!P(jKEG#bKM^`xT*HG)<2)QDH#>NQx0q4(OLTh}E@*%tJ^gCZTzpI6FHKFp+KRbQdNEo#W~hn-PA4Helb_L&fWZ&%Y5%j-@Tp)M5dTV zzhiROG&+e0!+>r-CW%6N?H0XOgLZSBcEh~G>gDKQy?4)?jJ0FvxDx_v!XRq!74{Gyh-z{g3^R zH@K%g-)kMr`w}dZ_uu9(rdQpwOWLoE;exIv13ns{i|R5o%g@wuJAg3 z-fclP>}=vcdbTcJetb+nH{aP(^wr&%+jC)Y?rj^5*0)75FV{|KqF9-7XUbE!RyNLR zWB{br1E$Xo0;2+A>wb_W`i*7+ZY>&;LXgA>t#*gi^-Y2xq*67NeZ^vta=A>UQZ|h} z7M5@v*Ob6^L!u;=`vP>VQd3XS?nNYtBIgKlw)~^*_hYxY_&3V*w}RfYiwU=}w*P9P zod593^76lW=G3`8uYKSs)!G7|=dJq}-7bgjgMuT>{ z4MI>Zm8e#$6bnWGxNeT6m5Vf6ZOWB0ovvxtX(3QTfRrR@gt6;K^m;~!mprGbvH!#N zd-9ftZr=fsAN{k_;@#h{mC1kgGcO2$zw?GWuX^48?NcYk_kH6%$;UqP#4ktD>K)IY zo&Djd$%)*3GZng(3c>msS}DeA(nV+9$d2IvB&QYlaG@31RKR^Wmf`r2d4<)aDbg`)c}hi7 zS{o#ig+`BjK9}|aG=nk`0+GH;DbOQ5{<5BcLTnwQwLQKhwr$S}fjXTo>l>SNdp`NR zN3CiAznqt&R4NgK0n5v)IF5sq62EVfiiCw3Vxkf(AxM;<-3y3Sf|O9m*^mCp`+xkx z&ArXHjz0R?(>u99`EHaAfU_RZ{}-RTg8eMAzG&*wOHc&2mj-FN@fW5(n+iJJj*xV$FV#deDsZ`9Am_ngwwEyxl?RJ}D zu}II4@cSV`3X&)px*G!efx!i|hP)$_oNNF3t+~}Vk3Ra@(?VlRd#yF^xdvw6iN60W z_YA)Ot~Wn;{+E8|vmaVqUq5m3?1dAre#v3VwF!EQK7xcwrNW-6DZ1Sr8}&LD=jNE4 zoMeC18CC~g=XKmt4%&v^bVHb5ZE$w>ET>PO=KO_=wAw8!JID0iy@au*)o$QOMX_4u z&cplWzwR}!diyIMdg)`g@-n~Lbcq1y`n{LV&ab@Nc03O6-%s9iO`Ki}0s%p4H)}v^ zL7bSzakWA|O{UR&Q%uyi=lyrN6(fCrLr9>$sND`IM?#IMRAT2>L?{tK=K7Whu^;PLN z{^G3ouB!lgz@QikxMAksiB3$)-|zbRRVUBR{^;Jld-KO9b1d6=*6W)TiyoC~jZ$fY z<@I%zS5{eHUuUAY57)l_#HCxAEV#|;r|HAO$|@&Mp5oN0=UHA|qu1-<=JM>>e~5Os zPq){`(LE+Ap1SwM;m^P6^{;*Fi6e*p+ROvUXp77C=xp6-`Qba_L|QY`(-iUrkS+>` zSXgLnp^<1|nZg+%5SC=TR3x9zo7whSo0Q$`HLw8WAoOos0y7VK2m=@lKuU|OsplX) z7|>~i05IvPtu|4dP)-MWxQ>ID3V}|yOTEz~pU>0pn+dz1(?SlF3YCN?flfa_s~8Cs za`w3}iqC&_ZvCaS+Z=)Syx~rLEhhf8($~NK^~rd({9zIKUpRGkR(C>}oSY&^px*Mq za&g=|QJAo{vB}cn5)D7uU3TCIZAbF2hc5TsEyqr=39VtJtvGe|9FIQs7*9U+BnwN+ zH0llV#VQAn+(E0`r`2v@CoK+)d-2!5_EjI8I(W~Uu7~;m({F!C{Kwz^(rudq0NVtB zCB%NqcHUGim%+->i%EK{x5>-n_WZ2-z8qy61nj$*NZv@F&)>L{g=wne`m zo36r`(4mwPDFo88==&i-7$bzlwj@%Dzxb68{*di>olUbyKG_Pbwy;%n+he&`RL z7QgTZPYZx|f9vbc6r}#ui}Q0UXP4^iE5XF%6y1J+A11hkGW|&L?3oKZbLupUOUwB2 zrDegFqZgozzKVWL_40{e7W@Tf8 zX0uLSbh-bIgUv@CdF6ZTsPo=;zTsup73R~obJkNzTU-FgmiMCEmspmQ_VfdhR@;p- zC|M<5YQFblQz#*%#I|j8inu5YAZW;J%X<6`5`!}BpSGUOEQHZ@q({yjCo!#dn{LlX z3NzEM;N{5|3go>!VHi@c*U5P~;#kvQ!{PZvPnUgtI)d^y4L+gW;v!y z%#UDht-i zFL#dr^xOYi0Q}hBdc}^H_aA@DX#Rilov*yDMZmxUcyxBN?Aqdh(kd@ys?kUH6vl5J z+|~4-iAso~Ov|R4+>!_>5z-oJ+{`MlPfob6+#_ZqT7-$IrI@{fnP`<^W|un!dWO^ZUO2Z}e+<=T|pZ*G`>TSY<2^ z(^JzVNrDi9nVAFZo!-O3$~uod`7E;+7w|*VO59w?4%c>#*Kl3jBQlH%S^=DyTj9y4 zp61CXpW@ubi!>T_66tdE_+2<&k;SDIRMe+zb$H;eqo(yY$9yN@L6^q3^*Ud@JW*12w|)R3z7CvS0f?3QR@MaL-T0M=xK&2 zzgZ(jV?kuG0!xBsTtksfe5N@I2GJve{}P(xD(h5RoMsWyi9mCW=C$SQ978 z)D$R1&b2P~!oa_loA=D_(l<55De|_s?GK zzFh5|{*&)~)$PyyjA8%+M<5Csq%Cd3^l4hI%)mNfCcS0i67SesPL?0=vf%_n(9P}9MharBykCYa9FOQecpMuacn-fEs1MePkK4;np%GSt=AoWQ9R_`x-0+n_n#F1_4l9L!iwMi zO<$i(6rJB}*Xw7`EpIT9hsnta;y6YK*t>5p`}XXi+39fR%q)v5t3(NDmnSnW7rPVb~K6jFnr=DkV?i{U-&*5WtvH#F9mR42`@~>Lh zs)OeU#;S#OwOrWMpqGF0ov+llyLrDG+A`7&K^_Fc%1UsDObtWk9wh;(d2N{5^a4u@ zdxSN#BFJj^)A8RUh(9oJ8HWA^LxCYpQBa~3{eD1{B-pl%n{%;k2iI}2ZJT~SpdW-d zwnG?&XbkgP(pVJcdQ6c{kZ81!SW;q3nb^{P=9mA=KfUJJeqU|+@E<>S&F1{y-{#w2 ziT=U2zVwoP_g3X!xk>-e&n?Uaoe(CcrYNKXRBP25Gy4x<3CY6ZA`1%(v_kXf6zQBn zZ3IEAwhr_J29G%$ORB1NdI78 zF=TMP&Bf4zSklmfFdl{^j$?ekPZUPv+#ESChhy6~u8U>a^nBmAA6gN`n5je=$T*UI ztdJzJX?LyBn(Vt#;ceA65CWXbthtJ#{H zTW>R66pW9Jqm{;WY^J9t85^t7Xtg;rdyb`g&xGDGs7D*d&xAmdq$9mY&$dkvTq|vf z0%-94K7P+fYeA`0;n0y2$XuRAvq{lOs1*gpa)EL=Prg#7P$;61ZV-k?uKy}td^8XM z8Y~1JjMVJIz&zg#8CPzI5DrZH)GElb1Et8yea*S#77Jk*9uy3}hY-ZE!teKq;{-RC zBcIFT=5pk6IRLudo|y=&6mb#{N{#^&<`de4gyM)W!3a7bactYSEc@hTe%4owe(6&u z2RHg7Bbt5`@cY4UddU?fz|Vi;sav9L_DxQF-id?HpIMxbw1$a^F{EXo5ll`_GIL-b zDpAbM&9k<;PO#mXO1V>|F2SgKkeT~85*i#Or9mrA&dX678)x5vgLs89NgOdzcBxi8 zN~Hp&Qh|J_Ox`Qt77D})D+t09Pd$5b*Z1(JfAg=zfB4NSYxp1ku2XxskW7J#E;JyTuN-)!m@2F+Znh5Mu4OVLRmK;%UB~CO`OE^ z12f?_mveD*F0Pxyb#o}K@%=tZLl|koSc8NliAWL#>Ax~fCywHH;L_wAxlt$*O45<|J6bl97IHpuAF|+Rg zm2!!8r_IXhs&NBICpYZ?(2ZgQu`+Y|20!^mtpN?vk`xLB#wI4%yZ<1zV>409Qz_>t z74sB|9=V){bZl(L!Ll5pMAPp1cWOPF%6gS^-AL~9Z9s!4fYO>cQMLwbdzh;~s{MBY zUkAbbEasbz^;Kl$y{5K53&RaVd!}+wrh>*Sh-5fAY~)0iFpDLMV!|LqNJ-xFa9ju5 zHpl9R5uKh-uOHI$Lv(Bc(U#+oL8qpXfiiH$I9m2OchEa`I|eFN*OES55=@W&PMYCOzQ72l{d`;NCj&chP2vkI-L$ZzfY_*js(}X zwnPL#U<;8>q%l1JqA133Y;w6A0?VW&W6|ja)LT7Tojze08!aQu1xo>zl&MuBiDE_8 zeAKcXq-||3&M%m1QGIQGw%W!1S7ZJgr62y52lS0H{?C5=3GuW4>xnDs-EVu-tLskO z|H5j0y%i)-uGWyYP0tS~6bejCOcF;4^Ye?WZ)^}=1yUUi0E}V;TfjoNW^2Hbq;()x ziX&-?1rN)TSm_$mshG)9)TL!C&>)VceZN0;E$92G-}r*~so(g*Z42~WiAF5|Il7n) zQajS5WAGHV8KarP)<#$eEzn~7pihG(Bx#s#Wp$N$+QzF?EHN=Y&gA3-m4Zjkk|+gn zdf1V)1EM4*P7+guq#;Z+aXMI}+3wK~LX)I|1=2!FjinWdu+lPR0|3T}8mz9=I+^?M zPydJp_^#IvHUCFHds@8vD$O*$=Z&{6C;zKTTcG*N(^%gAv(;YkuBEpB$W$qh>*Z-S zniTSRYPE3?g7u9JmRDD32Zxy~7_FXqWW!TkhBAvlrPhEY8D#ctxzsC=09;#QSr$qe z*U@#IE!M0MX_Hrx3IU-I!X^mg%0{z!7+AdO&DiY)-Nuv&0Ffk0D6Mx;W@2cg!SEf1 zz>O9|X)bgM-WgG0w7)^}TkSTpv*$Q7d!Dtmb%H3QR4B4%&osx69^v@$V@y>G!~(RL zE{>JHl+yHrkY>9}5QYSiHdTDKivYqX9%vFPHSvT_u>^%FH%=r1g%)PUm6R4jffQn6 z`>+1B&z=@9hD@%TMgQpSFWc3Rovam}Y1NZw7dO@(*jGA~t5#|>n{9+;Q7V`5yd2F& zlll2YRvLZAJP)`wvwmP{rqP46gk;OoyBawXY}>}ROg)I@TO2%#ajhwrz)2 zyIU$8EgS{DxZ896v+sV*i*}*NpZVCA1wfoAW&T$TO8FR0-`PTXBv~V+%IS03Zc*>g1g7}o+jokN#}lEMUegGDxuids~&mzf|?ch2N8f6rP%n%7RIR5ZE65EmZVPqvq zbl)y7@kLK*oDGpE9VbdBYUD0wzJ4}&R%sK)(IS1AS)4cff*4BE%muL1Ie}|g>e`loRR;TXGFMs;^U9H^@|Iu^e z!+-Sbj{9D&x8Toz;wf>fk^GxQV|n>G`n}Vey)FgU!pnIiaf0XNnVO!)act`KO*S_- z@uQJ2-w2j;Eu>R?AX3BiaK&S2qd*+T!nS3a!X}Y&wD!%c)4{w6z>*eGlF;e(?|ts{ z%^%HnJJO(gQy9f6iIr*Tls51L2B`c%E{N?399f(`E7-}@zextNEhGK=K}Zlr6pIBW zCMJl(K4(tPvRdzO^u#?(PET?A+-Y8zJ;z2X1z^+j3`0f=X<0aqGXQxk%Nj%oz<@bY z3ZxcjX|7MFrbMOzv{p#lN+S{yVTsn2ZqoO@=`KCWxWDwNlVUVxznyk!37*jpee-?# zhrW6DnR!>DpZoX|;^#hoW#H~s(B6vod|vYOMyDOv0!rmF8jw;jHddolD$)1*Y;M+R z^oQZV%(Pe6DGao}N_z3g0c`<}?O>V zgoLHVMS4M>L?!sK+6)-3q)S4to(AxNR(l*R*OG%`3TE1); zTmaIR=|fKQ4$@ZQLI|{!NWe3Wg;o9TRh z;P)OAANbwJuDY;a_3DTFc}qOsYB&5uLZwnpmx-oQu288|Nt9w`WrdAqd%&opL4iyd zj9LblqwSw%3L#NDWE7?(MY>Kb3p4t`w70wTm|_qevZQ4q0a0?m4}vX`fcO26za9*~ zoiZt>qgeS`(?O-6o0OO?o(xR-Ay8~sJ4>t?C4s<_L+PA(UDCF39EY5jqdng>COc-_ za*`;N%yHuQoy@Inu-WPmD{w53BM&!G2FcG{fb`PRK_uogN7~qyz_uhxCs-OtB!Xm^ zoCHW=qePkqVB4Lm-Kg&$slL0OTKii;|M1%`58Qq5e?2Y$e&p>B4)*!r?>#Ownjd|~ zLpy%&a@%*JvZJ4>``wLBq-uFDkL%{pS`P-YEG;dvxw%2B)gn=4EV(lfs7L-Wzz250 zYMK-mP+&0x3P(v=CgIFZ-9_7Wh6x7@b(;{GvIAjR1YuO%Y&MSrtD`4;->-jO{PVy4 zTK&)e_G@p8=YKOuzV|yG(eM3^N0jT@zSgu8REoT3Re)KMPFf5krTGk*rrMeV86Rqh z7fNCNoo#m=mwetM?|Im^jj(JJi&Yw>6M{IQS{=jn3WP~aqK3!G&X8$cX<1}dgtpwO zouw_2j!9&)EC*rPNLyk%2JjP7g0Rq}-HR+6gc$$lKlNK%md`JJ>ZHihaX)e$1vuO3 ztM23PU|-+Ns7Bt>r>sPsY5RTUITFwF&|2X*HWL%$cwU}Pr%S8VCP=RFva@U7#pRB= z1*7=`0mv3j|@r z*!UQhlN%KJpcDcH(xz0Ykat~#9El&NyGdzb+s*)Bv@9D5gYHGrvQ2fLu#lEUkf1PC zjY3#hBv_V(&H1DP?hd&)&>q0WPocfAN#g2sHe?Z@y1|=ntO}KlII4&eF?B z56!2q3jN4C9=zma*P`LCM&**X;)UvT%Wu8@V7cIxN<})IHkM^mtBp}86zF!lY;JDS z3eDin9lXiQ(g1QA!JaO+QzkUD1}%rkwv~kfZHeEH$T{wS=>;ZVMQTuK&k3ysj%5=> z$_m5q?rXj&ddu63f(8QM%`e-hKJ)ZKI|!rDk90nYB9ziNu9G6@Ba8@J9!Mr21jZaw z={xCmOG3$W$>;Mln=Oi^3WZ{YUbl_qI!Iem@?50rQLEIbm22dzp&OCiP$?yrZ4baZ z(`^W{$c1g&SdNWU8sP{;B9L05l))dgO2HtuW4t|epe zAAIYrOVRyW(Cc3H^6=OG_$#M4x+dy5W&kyc^5 zq)``PD`>a#zDXLT%JK$m+rhDHP!U=fi%_d%2v9L_XaRxaIz&N8*AE`Zj{EOF`Z)pc z6YqZAmS>o0`hW4BuhXONFDBaJmc?P*YIfS446$!_yR_RK!ZCjg4Ws9#O10e&S9hruUJ{ml&^B*}s39N~O%cu`$Y($`%|!rXY=!!^R^B ziL~T!A*2`MNR#gA3b3VvB_*C~!a+h>Skl7uDovvT0;FY4&tIJ1)eXx~e9f(y^!s9> zg3W9ahRvRWTrP*>*a!ihm!n#(VcQP9ZV%rdVyyZan14HM@eHnT>obs00!ZTmWX7iL zIBBNn5XhG4yh+D(a9s~!JNR*Y?1kASdwAZRPxxYA0b4>HiPG~ctE=+|CZ?yHoJ$yl zgh5E66oo>8yzAmvX?iB9md{*)QS&a9t|EQc7-2A3&e7bvpGK=glq4KFbQs%lm>jQC zt&U^a9**s@uWEd?E%N{deFJ0~>(9CqrKgk<*Ue#BX||UZXiH*S8l7l_GQ&n)$3_SX zArqv+^do?yDptb`kzLT=f7`9s=sN*C=>{S0N?kCl|aZG`zCALyT3riHEo-%d(Lc*isBai9!gJW5yKU7B84 zX}@Sdy=$c{7QmYy+~4@qrxrh1&FA)>K0o(^^+r?XT$f6vLg;xUNjf7S4Kfqo001BW zNkl&5AD7{=tV-8WIOfx$rDQVf93e01GACR?^<42e`sZG4NFQUCMGCF zF;Nr~7YdZ}4z4wf2*3~v$a1&``2eYl5GGJ?;ei8rqP_WnWssq^?a*ld0p37b6_x@z z=2%7;#ED8lB{6UdEXzU)fu#ikn>a}f&?f~N3&)W-X@-%sEmWMKu`0GDFPr)G|I^Nt z_dMT<^?DM;iSjhI<3K;ak`|ulkGrMW`e;YsPlygmLN2{Ob43t!%vOfBnf99-p45{hK|LQ-5;o;M8p) zgm08CS@hrfih}|0#ZNvuzgoyyXAkU~d}w-l^6iag>m{qJYpktrFflQ~SglHwR4C;= z3a*j1mKgE9varm^u?IpRhD4f*Ef_MoADhp1O6tf$I+kUllpbW`DaBxRz@URMvB_P~ z8i_K4O_W3n(@azffwUzg36|p$C9x}nC;*)-R(~rSYupIE|92k~p!vt&{_<ytM%Uc@|m;0`^5|MjxCVYGi%;E{!hwwsDdo7`^Ov9~lT z(OMHL%)^of>9rh*jWOkvP$TWl%)U7x$hi)d@Y6w|F$&=d$zfpCAj~(mLVxz-PYARr zvHjT;$oof^G3T$9{_(e8yZN6n|E<8RrB*AkQf(orNhwiUq0x9=p1hZ*+v(!_KE3Ex zUBQ*fu_2!?U^zBAipb~l=zhRva}%9Js3anZm6;DIO*FuyCz~~6Y6NVQwus_{MlW*z zKknW;&XTM??ERi|D%{++bN8g4nVsF4U6x%$TnX_cvl0kdwnUPQkS$x!mJP@j#tK+C zeEbv)ioi4Wv-E_JM34vzLKc#dkT9%RU;`|hW_M;MPxo~0+c#D^=lyZ2Zr{FhJ51VL zzu$anx^Cq6P}_53R;EB{=8ZPT=0 z`|UHqTD|^cz1jTJl}7uXW*B~;l=pw>@PWPeO-xKsZ#1|#JIAF<^Q^T(nxPf(d&7VS zseM_Y2iT|tqs`^Eg#L3}*RzEvv+P7|h_%L1SqUf~mDM0r*7)bB)b)o%7#siZc?y(5 zI1(Wp>mF3{I|1IBa{m7M_*VoyeyZ(^RK2jt6|@&SxJ!e(J7M=jPpK_!X}S27ZrEedUb67+(K! z+xX%SzwoFm=RFx~S#+d4;5zDXA@4k27;#rSjJ|z&Wo=}1Y@F)wD5E8hg4+|<&BprG z&ORxi5yBH^7dU?WNrVuLj0{sK7TLRJFO`DJ$+H&`2t1jfNDw+B#4Y?93DVo0=2Ff-|XtqoBW6U7NdS0)E0NAA7pz`lPyJ~I69^hEVJ zKbHnd0R8E#>#fk#+hHO_(nxgl#HE$mpUl>*p@a5kxVqcyk2b z<{`a0(5a!&bzPLBx&yDZwjFA)fhfipR2nL51AK%HO_r8|%XB^GD3l{@#bKoo2BJv6 zFiE2Fp!+{tX}32TvIdve0&z@RKu2SYR1(K^K-g5=oS#EV+s{F3O`KL@dnS<|An(GV zVZp7_dG?efwMK_0PA~BIiPJP{YmDYyCMr4duCmW&=t`Mc91E$+ndK4*LpLVHbsap{ zp_3SOVs`Pn{?~(#-2c^wANk9ZXD?Nr**Cp%7-$1{!^=0fyn5T;vwG+6eg0e8bCl4= zBnvCG(__`@xhRPr2@`W+b#3kUIzd=TiiNZ+*Y$l+fmHz<$01I0|Ft$maRlU1${|i# z7~!Vg02iGkIMNaU;mR~Y!6E0nQ0u?<+y4FwOmET5-ImX~ zT6ph04|YfI?XSBsW8ZGzdLVuVdEf0c1?^C4Bh!>xnMQ4To`+O&Q{2L}NP^b_zOpXC z(1CoRfSW6^)G(a7aGJa_?4KTI|MUd8yi2PS(@Ekq#M~}A%ZyFmzyL;PjL2r1dY+Ht zxj0Ixg+}zs<7du({##F;{hm9H&g__N`fR}!xxW6%tM2GmX=C8R2S5KF09Mp{WgDm*WbmT0P%nGla{-`;|)6_`Ss{wAM02nOsu-aJasI zi&~?Njx|zAlxc>Elcf8@H<~Kg^b@3aY_`2AM(_fG7wo=Lj zLu6w&HoO3y=i&K2%CV7~-NZ2l69FkK(yw)FTklR30futkW4vHR`0Fv4aau(v()OB% ztFWgu!t~e(=lAXB+_`g{J$r`Pm1Sm^W;r*%#=fZ$X7)}oH8w%5(PXU^;OFwVo}Z?* zLz2VH(>(ti#LtlB|arZl3d|R~h(>?PrkN|x2izkK6kA|J<;sbp6 zKfJ^I&R;xoZgq8Kt`o7}OLKuUHQ;*beZH~;KuU?$w&Fk>$3$UB!S`_89uJI2kvmmd zq)j8lAP~k-$$L1CBF2h!%5mt#dY?AYUBH*F!{a_{a6Lu*th4P_{q20^CQYdlgfSa+ z{|!<}T-U)-PU?o;nj=VI-N&a^V$PmD!@|M>K@gDhb5yHU#>U3jGda$lvUML{t0Z8| zhBTn0Wh_TV;K;~0t((U=zy2aF%wFQcg$pb#FEh7R$MKuI=#B&2cFSR&I={%$dZWuY z!Z4x}wCJ>21VNi92nd4?LAy<-)6TU!%^W>p=FcVQ5`YZpfAqU=HdztHAKL};_dwkU z^XS_8`u=LZvI*g@*KBl^HQ;!j6}P~80>Ut4sNmtJ`3kr$Cb5AX=)~rFOMwsylf)o2 z`GQ9==ds>wBhvQ`k|bX)`QODh1mOLjvA+6eBOd=s#QQ<;>mUEBFu>3L@LkvK*@Yt{ z(nw>3&>}_tv>&!=jT{kTO~TRO>Mo#;^G3UE30(c9pV?hkYkg%>mmtc)vGdz05*t$lCZx5104hE_KKi1Rea<@e+KG7B$Vk$S4VQk~its&6*_f3)@g|$kj&43U>lEeuL zL9viyxgMs3L4Z&MiGJRDe*4d-^AnSoV0(mr->0p+?n>kGuNMBzRdW6t2^PJDYh(OP zx>(zblmgH7@LaEJ3^be0h4m0FUbx7m`FVoSwiGUxON@_?GgKL()9G;W;zcf8yhy9v zLOF_&m%ofXrE8Zg2No$!i9w<_)Pu5bf1jX@g^^BhT!%ymDy1CWYLh@1 zbP}WEga9+-EB#X#{@qT`a$^wRT(yXQB``?0&l+6*{6E-zt1Bb$lq)?cjfixTa=sjb zw9tm*;CAByHXut1nr~>3kd3l*rPoLtJ zn{Q#fFnrxqfk6-((1|4mDy<;aj-b_Q(`+>nvP2kZI_(Z7?sThL8>7K!5}n{knG%Ee zN;;!Gqi`7k*h$pi4y2GRBjP8Qo90Dj-jAxp|DKP1MHt|nJM_IvjLu0V@~)7gld4ftN*rk$vIv3g=ByM- z&`k@rHWz3#>O@h5qa2EbBCU3t#f4eQIfZMgw0(!k(P8`-+{y#rJWiw8WPV|u`krBi zb2n}*dNDS0Np@mO;DR8)_kE%$Ar2!jNjH-O*YSjzoVgx_t$OXwAc|vz6nMUe=h$#y-}CW3AJ5sy^vqPmI02p5W}Tkz zlTTvKpFe|&ni%B~hdGicpc5rbO-(a%U=58eGu8-TBxec9l?0t#D_E~nl(wQ1#7T@y z%Z+vWXc*h?!WN*Bq1HTSvjne-0Q4h%23qTdpxyb_>gpPrsT-2}?MNu+Jbcfm*=Uj^ zF+z9*VL&_96pBUGYBl0m<0*+oAS4)VlYz3J64J8Z)ncB7)uwGKYBb7GL~)$=l>8;& z_1S#~A^uJ|_S+fso@=nXFrCZ6RVp7lsvr%*ehY!?xD=eUB)99@%F<$^B^X$d+EJh! z2gh}&RLXQZZNgR)U&bV{AdW(!C_=|Ei546_e3<#A8W_5H1>K9?KoA0iwxPea3|(TM z%UI*FAm#X^FPPdw1W+vG7RScNE(4+Hg))&;%8|@vjOD{OpfhKNsG+KlT*?hIhW{Qe@k#xRVh^x zS`bGuIb-7S*f%jdbfPiZrh7^WrE-Zd3NgyJ#XyDaW@MX8 z*b1;AEoYYT@+CBacdLXQ2~@BZ0OUdyw-;|(t{*Nyw%5K?2V`MPl;rj)Bn z#;~CRq*<6<@!s|D`Xc#^0Z&4ukWY<-is6xAD&;a!Z0kJ4NkSCIByogNf>Nc#=+rpn zN}0U#%+?mLMQkjA%Q6EKoutTb4S!@=w6Gxe*-#aN) zLn?WEc4__K#LSJ!hIAE_OJ(x8JdH+!L?9pB1y@!{+ z^7@47<5`8 zAbT3@0AR@HbG7l2>J>CcduHMK7s52U7KZTy^?LpLzy<_e?+@e%sOALaN||P>NgT)4 zDjP;L8%@TmB|P6Jj6(9RgVwS2^CQqAK}u)Q8`)Ft7ly{?o_D+CO9zE9DE)eeMV zfa^LG@`Y}<=zb`9uy=A2jBWDzf*0QfNt;I>c?@GB90^CJM>%p}g2f=gmx^k&O3B-> zDG0W>_;rF*xF-z54HdwEPU5Z#&?n!Z(I_d%dETWX2lxN)qXJwzh*g0&+3s{URDkO| z`?ax^h0EnKQc8kO0LB`WTJ1Kmu{MG*42f~EU4(2KFdL~R(^_vfU%+!6#wtZT&p`^6 zb|G>wLa9JU|JQr|pFbT~C}g*A+5Gt50(Mpp9H~a7a%ZHFDu``Govb@hX5sf-n-nBc zef_d)2-rUad#5MyeUB)PdBICx!QcP>SMgmhzl)c?@cA4)dKkZ0!gD;vN2iz>5_oc> z4cPT|H4lPBTNNOQE!WpZr-Z<|l)JqNGc`clBsAs721I3!7o>NbJYWCbjBx?V1Ll#4mS zFiInb1UfOa682Yf&TD}`*y)*m;}c&?Mg7fLcGn~8?nc9}d?f92mva2=IDZDpQR6u7 zJ~Sc3m=H!0o^QKCXKB9u7uCiZ18*9EoEqiA;u=Os_8qy6iPkFhcAG$W z6j1CRouHZ%c#3Yv;b+1CD6rXsaU7=wXh>6kv+`Fx6yBu&8H4LNiP6lmxjNo+3U)Pq zSw!DX@PW@hJgkH{Hab%NkEK#^92gAPNz+R zV@rjhL70?d7^Dz5%0UWAF6T2+&f&Pqc9f9xB9AfL}u@uck>y~W6+ zb~W}?U~;T#B>_BIrbs)=5sNZog#rhvg0gR;^dfx)ecY}Q3^pP#Qvpos;_JHpx=vJ` zF7}@i63=zldM?$inDAVIt^}|hmw)RHoIkCXH&QJ*Z=9Hz{Ebec3Wuhu))JhJ&vkxn zZJ^@A=;$cROUr~oNE{~!DQPt7v?^tW$|Wu>ETBb&#Dpm6B20`h8lfb{=xz}Qbi&kd zg@x57wPuWv5}`FhNIE*%pZDe4fOlWtx47Z3>rwxOAA3djoOkuCZ*}}%efhqe$mefU zt~(rc3_%#;dM*Xma_3gFh0%t5zCh01p7m)gg1HI~4@0MvqZ7k=ewb)vn4ugD=Pi+F zMz(i^FwczW-+e}d75`HgV4Nf*NsJ>hu|J*YJ)F`}Ds)}v9G43}SM%gIgt1&N32JKp zc=4BSx#^}4yztOy;T5-3nJn6mdLyH>TnQuPJmpG-D2}`F9l!X2d#X3)O|C2Sa`vsZZ?)_i zhQ}wSr0ac~qomVLJpjJvQ}!T;Y)LHTD2l}b`5g*nXI)vN#M%x#$9e}2RN=O9m@ZkQ zuLt&T)@JOP3b4;qfXo9Rr42(jI!2`R@48-q)IJ~p-*rPrIdeUixfV>gs_(QDbR~eR z?S%8YfBwx|_f3ub!Qp*-UOY2w13+R8wImhMZXlJ`28MhX9M~9L6{K|o4cZ7WlChy8Yt0Ues|loqLX1vmC&_r;lyo@1X$FxcmhqKG81wI<^zvc^B1ST~@tOkR_!1_Fx3VrO}2x!HHQ z>%sj8nNzL`u4yd)=!=j2@J$CM|Mq!@_PzMf$OiIf4MMK@2D^Zq1EbYp%H;|{U_(b; z*Ck0}YK;aZU)q%6Fe1hyF%qK;WVeWgEov!AlzPpt|Ms7} z3T$zWU;Fr1Zy0%d_s9NLy!&H+yVLdiIl@*Py?=XJH1X;sf>4{ArK6_e5L)dvQb{WL zv>;{>ki-e4Vu_JLPa5zJZ_}+PWCQAmG;|V66f#%xGmZS+c6OSy)?bX2Hot^4?b%>R zwDk%kNlct*94RRl3YU&QdA!9nNLtxr3^@0?NCQ_D2k_ayeNe?F_uhTe({H_H&#xNFJO)i&XBxez~)oP=Src^Fb_BW7dH^`ec=g~du z-R;i;WUMxdB052k5&%i3(*cuUpVWGm3|N)G7?e`v{oLtqJn%rk<>P#%m@&wHV0wOE zwKQa?d(E6^IiM;MF-Q9VR`a$;meKth?-gONGK|rU|v4&$H41zT4&!o+2ZD@I73`rXQP%ag} z@r?)XPq>P4?^)TE%({MQc7%)IE%{Z-+nmCmBXiv8`_>PpWv zNMO7u866!%YfZb|wq>ZKptfG47Qk?&Owb81!Uv&H%0)RK9EFr3Em=TpoVx>xVi-sk9%rD*3w001BWNkls~mw)N+*T##x$g5Zk7mt>~HU`927QfXSj@Y-}8z zXzKMkj^mKaAlug;!c0D8iPgp zYymsRS65eAU$2oQF*-@mQ7gTPCJI8Lb_d6C*tcgoe8~&${QSjp=YQ_ApSdSy^Y?+B z5qme>e=9=37TdOjEscPK=4!JYMeEIQHmVjYVBI}o{Olut1_c+U#>ZG$S)tWx62(L0 z@;U135vwb!jP4z!S}w8PY~fc6IM~W9N+=uCowgnq0uo{*FgjFZ|Ku>IE-r!NpruaR zN(-7{`0W)H{HBcDx3LTK-odDM8KmL3gG9WYCW~b7gxd;w?_&VbsI6Xh&OiOhzsZ%U1E-^YbMi2(XVSv#wj&TMBzwPwK5S{)?;jE!M;9aZmyKT&knzs7*}Pw)Qd--;_m`V4;M z!(SG^FgPMFV_?~O@9$p3?>(mf8iT;}{k;r+;0IrKo6ME}o^U;}I2Y3Cv>6^AX1ris zb}J3rBx7i(!cabqp378#?7{<{B2y1~6MHhm)odVk7FW`1WbQ@q?zuh4+YD`|p;l_a z*ILu*bkeZixSR8oHJgc|sJqW`96Id|XU?AGkw+e3VPS!xYL!mAiO>;_FcdtOz0=cY zZoB2?xBugR^ya@_U0oSOw0p;PH{YR`7!Mk?tq8%c5kG@13BbSnp>MnFLP@NbF3zuh z0Vn^@_f^Imoqm|-5dM>QV0v_jh4~U|YipEBC91^t=B3 zyZ-IpJoMlLS?8Y&2}R#}@4nr_AmU;Aw)Yc@LFaCJgD(8vpZSIug!sMizRx`P(*1kJ zK6LA$1GiQ@o0#!z`DnL-#K04?4W4}JDSY2&=D-X>2o@I?DV0jxJ~KuuhLu*#p0Yxk zHi9JeS8J4#L}6&Zps=+A!Z_yAa*fB&&l848%01FZFq0sHc)pa&z3ivn{C#J4@rYY} zoUM#I{RrKU=sN}WYaeCFvK&(W1Lt(e|yNk1$joI*MDZTmSLf z_7_N{sOOX#;7HgvTB0^N#idJ^SXo(NVq$_~smOYLozu%jZkx&z#16FpMshxa04X#W ziAgj{+0H@$Mo4nfW&gx5u9U1co9Ni29K#UDF>$OXqfY1k4}S6s-}@79xclM0N4ipD zO}_{^XxU`^%N0t}7$bvrv-cc&jvJ(tZYP>{In3X=`+NMEnZvIds#f;3JFu|0NEAm@ zDix-RwqwrndIzN(Mn*;``<6?`CatUA$L;$OSBcHF0{W3VBL<@9EqaMU?*r?UAuTi# zo_OMM?*IDNdH7r3qO~#yLV{mT#r?!0x5|+(6c`RarxeT4A_6gQotqxu&cIL zhQ-zO&R2(q+s(ujib~oK{T#x7jDczn_Uzfi`g)Cp#RdGFPj#qD9L3CEnx~jM$blg^ zvkvu0QS}`JF_0jYjSV2J`-ZWh!vdHXDPx3Td9{vE!iE()4$?&Uh5UFN1b_LvpZu#g z|DSL8p1-7LSnht^Plzwv^TAD%2BK{_>Z;X-H{+xr($$lFE z*Yjl*27?GfcL;m8z2$BH>P7jX;eY0Mo;ZIgVtsv`LZQHPwMfB*Q){qZUniIInJhcF z(sJM^We!@D_n@!f&*NKA8S333D zSL>@w^G`hZhI+G!QP^xS$FZfLWo}%G;1mDtqo4a1Z~4*h|6|gRIDhf< zWgq#`ul)^N{_(}0Lr2Bz=_k9t2P~I)wSsoDcVGW`mkkf8eQm%#(@P-U_#j`?Q5j6LP-CczGsh zt}TCQwN{@q;22|y+UOW#){`VzN9(3Z;?*E%pKsJw&VTdXznBZ!&8^h9tvL8rh~EEu z&^7w6za8}7d&}dmDi^>Qb9!ZY?Ng3hc=fTd33;Qc{#*;_wEz2oi6LsWNiJTz$lRql z4jeeZ*yt#Wi;J9|UFPj)-_E>gw_BQPW==^z}N>go!cW-JjDeV;vJ73LMx z>MgX?I8u=$5yA*m-XF>*`hy?-lP~%|_JiO3hk)oo_N|1a?)IS7$QYdfM0t2j)>oHc zu+RT8b%4zi?y~!($i2zt*83c$XVUz$xBko5j!aM7EtJdIxdv-%YmAPLa%e11!G#Mo zXw)02>(9ec)^8r^%@p&Oo>-0`T_Ny?Fh-pMKz}IBuV7 zgdEH{S9;qU51FxOBo7B>4$x>eSXx}7kS{PjJ&iGj#ib>VyAHSSA7Usa0%HXSA+0qy zEC1{&2dy=Z7xo7j6u zEL=Rj-G}YM@<#NX0ED)^<+{If`J(Jtjz1_gU1M}yZP%VSY1r6q&=`#xCykvpjcwbu zZ5vHvCyi|z6Weya^L%T)Yt6qiKW5FjvG=tvj5X<;g6D0Ee2@hR0akyf&*j5KDo^zX z7!?_Hm<=%)5oe+xQQzJ^z6{-wyt$b;;V^J(OHb^EfSb&&)^A8Fr)}95@t)v=6*itF zK|3FWy+uRM2;_bd2o$~MWH+9M2=y>Di+%h*{^GVT5L0HQ3_$gUkp=uHMVP05a z<(5$6q#J~e30T1_1LQW1ObZ0k!MDqH?W+dudfz2(_it)pPCLZD>pELU_xJxOOq7wA zpEjMdll3JR3x_7N?3Sy*xbB#I92$D27I@C95(T0WN%KCSPMZy)7uTe}9+Gixo6 zJ;Q_fY=fP66Ipz54G+h^2X)!vZJIiLG(Mi4(e;@$i#fEn+;L!CFFBaqz83<1#&>u3 zUeXaMJnj0+4#=$54z9d|-6C+3@u?0blhen6ry1$wIg<|OuWrQcmMh7)PWd-nH>b*fR6&mwo$>Uzx-SXR~~RVU9izD^pmzZGmE| zvFp+)`>oqPEkA+tks?s2n1hHcQ5GuZo14Jk?(XB_`!SOM_$ZCzEwmZS-n!fznai2Z z-|kB_!7ljQ=Vl};@H@{cXs3z)&UYwX?{%T%9H;ZH?1eMyHJbAuCF}&d#+`glg&O3g zW)ft5&Dz^xhvL1=g`SmFRNcXZn#k6>7tI%Lhi9Zg9OU^#cy{41%*OQg_(m@Ho=u*{ zn~DP-ju1hq;BYr%DluFVV)6qw02NLW-e)kbjTqlmb0-r+8|865GNu>f%6EH(aeu0lpi zjs_l$_ff8<+)5z8mcjvg>J(LN_2OBCQWR8-A7qu*g+okAkh&qF7P3W}pByTi$}R#9 z4jR$7@T#rQa7Y~2a(9~i&nQFaxJ!J%Mi1-pHV@7{Pm5JP!(G;T!l4Ow#+SovI1|)L zyiH+(`FdP%;;0uU_U7^Z{V)^SsYQpMj8VpN6Sw<@6L?!ohdu5y-NiSh%rqh(qN}m| zf2z|CDME_tug2|Zp`76v=r>1Rw9)M45(X)ukSF1ODpF$kJ=S?vA#h{!@tW27nBnr= z@~^~c8`Apg!gt@3y=z8-p^Z+|f&dz&*yxBEqwU0T{#!j>mv`zJz^$?UCLY z(eAVWcxY<5>|KI7i0E2|zrQsg*%z2iXS^Y2I0%z^R6g4m^(CRp(-!~vfg&3N0Cje2 z_0O?rfQO&GR^pBVJ+_00fx*Xi)W>d)_ucx(orn!VPUeIIcT>vSD&gRGVL(M>$Z8qP z+gj(#i;KXWiNM{z@7WI|(Zn0#6!wn`$`P7eKG{G_*H|}RbhLWPYQK}Jl_SmW&1seD z%ZPh*9C{_PC?9NlWnQI;&Okex;kgv#(o%%X544Q^5VCG!ikCuoUomQ3J-zIpAebIf zLr2F*IW*F34&f#2jaWdBCaNb|7Aq|_ve${r9OW$BbB5+j9p(q&(Hpo?JV*W z+ENJg?6LkfK;6Lz5ToBN*=+umV>`va92`tc0G^yP3nZ&@DRP$5Kbpwfvd<9pPo&I4 zoR8*_T@c&*iZdwVJ6d&j(wCijf5!ZCTiaWjHfW9>JyI@dXVj507kU_<&+K)G z&wW3*?d)*6iR622PNTEyW!W#eEdHPqx3RG?+mpgcg$etomR5#{c9)^NO?3lZh9%s^ z%7%`E?^MPG#@P$T%8sO}y9)!lGc)QdOMk}0So+LvkTp{0vvWW-3zSFS46F=3W8+|} zX_Wh+A))>@r6OWM2IjvF6e{UtHx{zkDjAtMI(usXmobboXY{jKx%_v3*>2MIT)o+nzX7narnXf|r0r(}x5g!TE$3BX z5~SrcJOnMvlsZMwVR(0CA`+Iv?lnIZi)h&SJwoCLenH{RCRg;&=n*WsB%F@z^NW|O zi>G%I_wKC>8!O(s$0kOYg>5E?vW5-0(-hE%U16d<9g6@I#HD|VxA-0>|Lt%mx3ejD z+JN(n8k1PZ$3BWW+ZD11GX#}nKRWI6y~-&kZ_B4MQp4J9Hnnm znj_mk5RtQi({+&f}IpmYkO-Rtm9rhrd0(B$^@ zL7z1M!9tS#K6F9L&tCWj>`g>`&yR2$9~%!$3=DTO1!sNl-e@B=dF<{YR?Vll&M~j6 zXJAS9r8B#XMlHY6PQKSypSQ8{uSqj-+E-aFBnZ8+#&weI+9lJavj~v!lw{G{)>ckr zfmvvffC-9tG$D|WMEF~sZ~Z7vwl?!pw$o$5kJ0Xe*x(Qr%7|%2R<{2*ntKpGF?y1N z%jKL9=n-w&+)#~3vNffeZMPuDjT367S*)|tbpIF@;r_<^(;|X<61mhq zkq3@<3aYM#qxn0x^&7!c2&bfW;22n{)TGqm@gHxmHoDAD@o8Yr_ENF(hVAJAZjM518hCf{YlaVU-Bo)G`DvOy?Au{)X}l-E?y(hd2JqVU?vfGZ2lNQrBzm)3+s5gnKT5kH8v`~9Dk8R-^73#-L69^FpfRN;G+tPWn>|(n$I;gAa{mN_5_aaHD1MIZ+bGNo13w@m;cU2(8 zm*W!tvfi&vTwMH|%l($d4aNx7o{yoZ6_&5JSS(>#!zd|?^{wuK!D;ecyFgt3ueZ+q z>2p|rY*GK0Kl5KFSCw;5d&_=tIobK`?HNJ)A03%RpX24HH*p}*vQxCk;4it7SKLum z7R?TPPAgqSGQhS#HXrv@ulteMhigUcL&d1kq`G1`v$T(4Dk1|}Gc6oXBZ^6Y0bh_o zDp4PY&fN1TCZ$8LMG;6>#9+bC@pHRNBI)Q#Pb!JL&cYuqhih`SL~e@&5zWAZzD z|JEO(rjp;f)(t`SgeWoYNKe&M{!hVF(X*vFiI-`_l^c79``2krWZMtXO9LI1?0Pd6 zMT>KMY9>ljg|U5hV=XraPshJ60B$a^kkoLN%Z8+v5ei;qZUE0A^x(~KN(e4~Rv{~| zzs_HUVeVnStsViNyN|1D`RWF0qgWcgPzM_TC5-_~;^P`79D*BX-<}uY&R4%dJT}Dm z{L&>Y59!2uJtkRyAnKy?%Yjbp&V)YhA`hMwL{^0|hgH{I5%kwva>&wQ{2hGmIejJ` zrhKkx%Y=$?#_(-j`cIo#Q5jFJp%l&7K#Ll-D(5EjWuX-dRiKj{10y3Kn|6KQKsSIH zJ(Ht%OwGywOss&h0U(eUXo_62XrPcCW=rHF(3dj4Wrd}kT`=H-CZm8=mr57vb8i!= z_&jwS_;;S5&zNHSM+pX)(hBi0li`*~E5Bc%G79SstrF68cVo zQRFq3Hv+NuDVKwT1285*A?6*bf!ZO&t=)0+CYq>n1VSL#^yxkroAfHJ+gMEWpJA_U zNS*Ix2wptk$=BuFGs1p6XsBc9?!4Z%nMX;9)FbC6<}X{?ZucRaw_Whd|11uazIGi zg|^(R)*+eSi8xKcxo-iLomgZ`*AL0in~MVPzpmH>{_)kK`s=(Eym&&n_@{vgk%&mg zu%Ug0M=7sb!Vd3Sb zqW%WC=UCld1#D`w#z-_EyTMH4d#I_i@NtE&3hs<0Onm?dx-X0>tOC<$HrhxEnv`WF zy?4ffw&;>C#hzchEVdx?hQ^JNA_+1`-%pIxb<;6mXD~*&Y7r1tY)t_-Nn=xw#D=@w z{&BTJ6T{*Ia!VxE3@8`2e)X)}eM8Dru<`Q>83297%g z6M2DSI?rEpl*=oOBsOwLATkhyE)qu<>dG24Dmw{i^_t>)RXPF2ak1B;ER5?U$vU-n zM+y7A2md`5jN!WDBC)$?Wy+Yw{5m}UyL8n__Dzm}`$acp*t<3V?^mX#b!P&Kr2LBE zl<-3(OJ3VrGCga^bsI#9m?KVD=b}(z$T;PN`&fs+qbJ)^P!->13$RdrTe)AE!x878 zw%Bg*-nnPlw~S@;c{H1?Id_`{Zc2>DrT~d`1)}Q1VlG7xxzb4b6)eJzIczM!Gz%Oh z9T@0hpR>5g;K>26v2b*c=wZh_c77qlHzdMO9U(iduEK@glEiyT*+X2Jnt_Aznfri{ z37%|;jc+_pB&6AGOpo2BowH<7w`@a*c6h9Maqku9KRQc-6x*_E_-KbvXDE1dob5B% zU54WnG9&ayOkr%<*dZd$dwCDquFu{K<)gyAD zL)fd&G2pYttb0BOXx;CRaXueq3>PJpw_Q~7c6fL`>Y5elG3)p_Sk6$iw1y2-IK6i+8;w!t6yIBSQ9{0 zhMis=@UYlHu|tfSAt5Svz>n zu~V5!&$2_BNpYGdPoT*SCuv3s2@^$yxjc~7UBOv=lZwDow zGJn=w$b^sZreFTwep1yV8zXA^NU`MaFSeTUeTqvqL12`vmzX1Pr-nXD=#Guk-}B}B z)6c$iemYM>DGpsO`(6(PBJ_*p#)e%hIJL( z00Ylqms3lJOS@!Xa#F2c4#CXN+ZU%t|@fjZ1Mbz=fcv_w`v^-XTE#JvyzAul8DfxnqI;mNHf=+#}}9M z1b5$f#f!4)ge26J1M<7%hWTGe|2Yx>YnO{vQMc7{hcS5-`)P5NtSED^Bm7%zWrJQL z@vL=2n^C>{U|G(pP4;?5i`z$s_94zy8?>Ldq<$|Os1db$fv&h@Ua`7{jdQJ$-ko6=413+l=3-1 zze)Hqt1wDS64W*U)4XvBEX=CM9zK$PmM=KxPwkIDo7RV~o&ylkKD;}A#yxpP%O$oh zxJQo*ht}CDLS^*$A}{dKh^lk-+%_ZEd^W{fJPXfU(v6eLoQnR^;lIVpr5!C-s-|7e zqJe0ZU{DWw3C1*_=yfep1c(KQ5fNidV|BF~`NWdsX@km;U$Fd~;iK}2mQM!5_TIx9 zx2l9KaXaamaLkVXbm>wccZ>C^QIzbZN=bg|5HC^%l3%AIHM7;xd9QE35W8(do5YaR2hQ}qzz^7)4ErWI{|>`FtJY3?S; zdyncT=Ra3*4cgUUW|n!@z1q7){VnNPl@Ee3_3V}%zw>a96q`%arj_oR)~u%8`xzHHV9h0f7e0W**kei3eCfE^ay5C7 z&jP)x%5zuy3eo0$LI@S9i^=CvdhpMW2OV)&0k;Dp7xY0uCAM9%#uhNPlv45V2SV4$ zJg9oB)UdKAo5G#Sj7fZys-S>Ss>5)o$z!7G$JoRKdN3IV5KxT^xaNSeq39U6?98%S zTDVny|CZbi1kweJN`)KLi++ApobbTZEMI7*wfRg1n?)M5C!rFUB9Ru{QG6~`-GK1k zdEdc15&x&-!Na`eGNo8#*f1M^l$)4==d*rfID9F5C~Si1 zR5LoV`Pg>wHtb0B2o-DB81pP(g3mY0x89V}w&*_L=99_utK@iNb)8LD5d6xYv+8AY z=JdyV^`VJycCJeZBnOQP@1G1c#_ro9gDxH@2tYE+}Km91=omV8vL$AXh+c2{551w}2ymGQYyaJ?-lYGSr-CrnR_ug`y ztKWz>H-vTy%&k#Niu&S7B;WL>q7X=8oaNtj%yZ&%R; z70;G27n=XRs1v0Qu zP*AwIxLuc*%nM2ENQV7)-+!+4T4G$%{1}*HNsO?Kp}=Bk%9ps_(z_>4IW&{7!_XPu zNbpy|!lwiY&Bx|?VrRllM@_B#j)p<^P!0lZZ#f572CxJhmj{Uz`28H|8_0vi{%hnlPSf+5+jBHb>O!eIt8=hkFW|Ip=y+VbnJ?Ws;mJp7=h*=h+ zMH8jHy&j z5LT}w4jS=Nm9wtp!-`w`;nkr=?_}y!4FVAec2}8qUAHA2G4Aasqu5?=oJgsv)n2ZV z5%A~;a>h*+Ty{>4J&hb0`99i9Z!m2hu!F7A6(;b&}PR0V}JcIVuRAS z7wgJvVf6YhK!I%`@CB!Rzr{-C*>%57dghl-OMvEer*`#jP37zL89_MS*-S@XgHMfK zFYHoufFlpqByU7olJI8P3ZW^dq`^KtD&N@Uzdv1@AY6>IDt1{B#*|*&=q}2x8vOpH zZWcglhP!8v_n-YnWavup9RYJH&JU4j)Ss5YPi|yL=}}blupJb<^X16f!cNhuxRV3$qXz$?d zaX*8;z752S22dz{i`cKF(j3cnC+A{tfbyHD8Z5B}nVISX+Kb3q17REL7;cTx4)qoqrL<^c#v~Zt$lMmb)2>yy#o{ zIyA?lWMOCM?waIZXz(C^CBMpYHx& zGTc#vo=lro!G2i$O5BWpwDkU(vBvGDP;5@9QSKr#*=4K*+dIp}@1GHrqD=F;%zM`` z)&2%U&nN0cJD*h{d8djNQW4Y2!?hOoGK)TZ8LUw(pYNjS9l9iazCoJ(g&3)LXn$Uf z0x75sSAeN93fYt~RK0+@ahopSDqW#+@-r~WbCM*6RMnj1$!ob|2aVNcYr$qf64EDbx6C2`Lrmk|obb zwB(`Dq=X7SLCR7{r1f~2vlE!I<1;%#5&TX9W#wtweB#Z83dGTefarY84pAw9SYk-O z`g7slYw>=l5smTX4Y&A^V37=ml&}_MwDnEka|LQ29k>l4!urDioI?sxZC~LtbMSb5 zb`l)&LcHYmJR6+eNW-+?#J6PvX*EU=m~M#sC#vkbC|rCc%+Bx>eGfDC4*4BN%BWs7 zDH>ofO_))>bGaqL<$8!#TwDyG5nHX+@Ds==NV!#=B)~mzcyfZ+;7*(3pBFYHHh|Ve zNXpJlnw%zNUjNtkHr4d6zH;|#<$=hoQ=yZ;zPc)`{X5?%)CfbaN)_U1*F&DUL();) zBh_GFA1)gs&5m$^*S`drN(*(&;4k(OOM)STh!_E`ug|;R{LoDC{mjxgBLPgLr zv9#WEN(Z!uP09?)KUO;gwn#UnUY`^4bA)@a5Et0?xJ`vD8PV$@Xm$1f zkKk;%@jp*ywAWYDNiU|X0Bw-GqNIyH+{g-Mh z#5;Gaxj(ZIy*}U!@a6M|`8R^m`ArvnQAgE{FxnJ-%R2|wO2KP)kZ;o+RP!_VoE+!l zXova0=ZCYPj?X0Hp?fDP%D%Fm$_$7MrRyu?*3C^W%+I!a1j_bn{XukTexgCvkvbzr;!*qp#qH5O6Sc}b8-g9#*l!Vz4RzpzzU8I zUT-{kRFA!j9l2214A4XG-nilBr;ySV9iB0Cv&$MfhBD_<*?%rHzDZ-gIYCjd;x^^T zu+_pze73{L5f4*{8?c7Er75X-Q5g)nHbmoC3WF}2&?W1F6V396pGyRjCZ*AB394b? zfm|}#&?emM&N@8tUO`jRxGOM64Ea{8^b*XX4PK63^lwp5!p>=KdTGm8^FUs42) zC4G9WAc*qDb(BaoyqY0W)jur`1O5AS}u%)#NXN zf`fhsExPPoUmQONOP9=&n~-K8&Xp99vCBKl=)MX}yZGTtK20uwuXNLY z!QCUgedYd*#20Ea&{>sQiRp_2`4R^vDKCEA)T9ah*yq?4{#r3nSQA${xn2h{S4gE; zNr--~fSp!mWQ<~3@-K->V+=oI**vCmMs+*%dLL*VW%*q`flF*P5i7)qqK?V24~!Kx z=tWF09ZO60mat1YsC6XMFs90Ktf;BZ`(BTIoK}+*IE7}8xU_tl5Padi__%*?CN%8#N>H*dwhQuHA^O7bHxQ&WDZ6p6)lY{cm<7Sw zzTb&yGF<3mu zedGJdfEvkaON9$IZ=5Rqt)P)A(#{(4+{4Se+9T*w*0v9{+CCion=feAs2cd-BD9)#is|!qDUEYm zmH+&p9E3m*L5RVLnUTQMVYe*WvfHcM+?>h{5?0X;Q5_(U6x8#q3`Fn7^_&bK4wN6) zMJmCj|9du>r`MohF}L-df&Bz)jxM6~H}=$Dh92{|Ep-M%S&VWx*a3d~y`;9!Cf9Og z-Mh~id%=Z9Q&!9NtA7^fjyxpfRpqUe8q+~y|2@+bb#KHHj5@xMt0b>QEm=Hi-i^e5{ajC46O0ym2w{+$*nMNU_YZ;w z4hw`>Nevn=oKoyH5abOeW>xE%LGW6Sog^=oZ)~j8s zkxzZgzNG1rIu!YEr*r$rc{>khy!TLcrZSlC$h$+)QearkjdC}wg#0Wbg+pqLK&p=~ zX&xWuDLF60LBm8B!=grsxnkLZrYsoiib>6o%0~L>7lLxKQeHP+!Pe-X?C+1*?>nKd zYfG)0D|1MhhRc7~bk^lzSP~R5=Hn%c_o!f~3(*i_@W^g!`iNjQBcchk5szU^R|6G{Q$?y+jDD|>{2L1Y-`g*H{H!Du|?@2{}SxxP~9WKNlpi9W^mW9?^gwf@sHAa}zy}{?9m`&F!lLm;}On+LCW}}LU zx*&+*X)g9o=?bVIBYQt=M{L~W@&vc)p?$q1Wc1EZAcxALIVuo&YM*NyK5J`1lhy-` z&LAN8(p7dwfdrLSb?!~*7T-a(PdGMYB#$>Z&THaE?;6x!TMty3gj-Ww84x3%rA~SN z2rV|xSjbOnN)Et4*8E|vD4a?ZB{$Cn*~a;@Ho<&IL{ zf7&~hqyJpqS!7Y*@1`_#W&0wL-~ivrSNj(_oCFFhsb=<@*3T`nbav5iSN(V5jGvI# z(OCA)4arVf-~)x#QJIsTp4muwPDH!SB>NTL|?dR2azO2@nJ)PDh&rRg@ z@z+XI4}X~`L-S}44owuWbDwlYL&jrV*{A_hPUJ&`6?!qh_~D|%yt~5dGqWOWsF}tv z{PmZgZ$84HMJHQNcl@0lab`}=5cPHx@I#zb+T}Vn6-tzzo;O6Y(aztxiJq=55Qunx z&q5RtT3z><4&H~}U5MPOGCIN#p2QpT9P!mCX_&TTi%YOugmR}&a8wFILlZTIrG?jx z(h_qKB5X{`%`>#r||+RpLf_{#rUtZk?aRuwgd$Qi}lJL>KtEM(Wm_6~VD~J1QVwT!ZqLun&6A znzz7jq_U1~DOY2{mA94DrVRiT#+a|kXkc9#+0nsosVNXn-;`@X2t?~}aq;`Ldmvmt z5hCq4-eP({JFud_t{4x5xc;!sI<_4Ynvz&8hZJE|gFgJCi5y6d?B@*FD8o>KLDOjNnvxd(u*DdrzoODuLULKXBQpaZ%i{!aWD{>@_s<82) zjm46>_`9MIaG@&gdvO};)S#)>fVe)h=>%U8Gc7yRedo&_;V{GKs}llT+4Oi-(*zan zBonr{owP!oDTpV+(^`3TPgQS$7DX2FDWA;JdnE(;|C z*a!3t3Ta<6=-5&Q_vS8A3+Sr0n1zSp0pJ)M7t2 zXG?+S`6|ticL36Ecy*zTT8mQLD8EnQKP zxwmBYI^m_K&W$QmpF_7S1*;wwm=L1(PaY{KJZ%i>qlItp_w(!5#}ac#Vxbk5E+EV^ zHdPF3QB-7+GGjPj^;C!>1GtQ!9T*I6@X+$RL;SK#PW|_|Ok0rI@$ah7)Cy~o{)zq| zEiJA*Rzi59l7V(|)|L?a6(ksnC=o^YbVpCaAHR}KQ{`y->+=`s*gD(<>C9jLF4x?F z3ovk&tHK4-zDBD6%-Ekh=tqCOof(AfSKpXlZFKpw=pL1U#IN79HQ2ZyEWiMc#d#C7 zzdtgxZ@*M)M7M8ToXhQGItE8gt`-_#aJTY}_-4KQy!{~?pXX%>l>cM|DQHVus9jWq z>p-g)yH>nE$@75?<=8PH*Jg_#E5zMoj5Ebi5%(Jq>^o`==S4cZAC!@#M= zE|I+r*^X2Q22|(PMtmLr*605DTrVf71I|`E|P+&hGGXD>n@e`3fuNS!%&(yCdL4H?pUM9|Sqe zyw$=;MHQ_(xxts3&5tr@cXevQhZvlPNEg|{EV$)Cj9YMcangHVq49n;LRa-q!;NVE z;phDBVQrL@G|Qn>{fAVbzq=Oo4|!Lr&2ZDO(YGlA9rQ{DLpIc+`u)tkCNbO{00h7% zV^*fMC0{Xv3O7zRF369Xzx(46`xiz5qSBCG&=lQ)dnHbQPz?n>2*!X{;soocf=E1u{7ni z1^Vsq(eEXS|1)q+7*Pa)IykSj3o=yyS;{^Ueq<2zD=N0SCFI~mRa3KXw+|Yih=i6M znI(=I0cjGqclUau$qY*!25<|NS^@7J-i85Td|Ws^uzo=zp<{EAf+CR67?rj%Bm&WI zFQ}VCk6g!h_$pPz-RTV$cDr4~hjK(bG^PqqiyB|`4vtoR-s4bp?&On=%Jy5OtVZW- zb^Uo*Y^Vpc!Bgu(|M7q*tyC?@Ue9;TNbS!j?{ zi)BOX=sThw%Ayldlo-X|qTk>0=k{gXhD9t$r(WZN4HOt4%E)LV>f0ml5EIo*HFA_5 zw`x=ikJ2t*%zC79=R(XN_IZ{cOue)_pq4wYSYc^)^`$g675YxFz_}N|KkGOA^ai4vIvIx@k_JyXSdxv z_?oA%h%A~RnyJC7@CeF}kQW0Da?y^N?^_UH3I&b&x{c%d7#G6A&EjWV0I|=LDsWA6 z-hQFI=C!ER{L%4Zx7h`ecra2;q+#dQ#~t_!Em8n$N0wdj@@LgNGqAT&hzWZwTOwsR zA{-{T;r5o*E}xN*g!6HLzvU;CxGL6&RP(LS#N=Z}{XI`=RR zjUS$}vj93M(lsGdglR>n84rDw+gH(o53^a*?5Zj}qFQa z5Dg;9&tzW7_Re(p37yOq@0#qp<7e(KmcZxbc z9#$t$nNqd1tGHL9XEHa7W4xYH5qu&}T)P(`S^u}WjOK|s5%L-;KvG`fhR zJ(5OWbXk(~wSw|%T5*`;*C$o;vBSxTY)}ihOA%BcyqA ztb_y%S4*$~E&)OB>6l&ax0u=Nd-{KyFigG%-!_}DnU@x!a#&w?qdja*KlpvoZYwuJ zWivSRUK-Dq>dh#gpPyr;O8Om4cr%;G5{7;4qKCsI*L*&_mYQrx0N}L7Vv#ZP#vf<% z(jdnFH+9?$t{|Ulr2gtcg*I(Y+i9)z#skHZd2Uvo%C#CJlTVSbAkhE@pEmVb&g+KQ zs2sBMD)J&z%lp020+V=(*A|uipi9SEK$Xc>d9ob5ItZFk;skl(=^H8KZryN8t7Vf6MkW}@RUe8KPIYC8V> zE;ZXfwyQTb#*nL|7HG6e-e(eiAQ$QyTO0ZOc(voY8z+8wSP-M%s5!9N1)31}@FdLP z`Q)<&&%W=kh|`*90Q}Vz(|O%=jBen5PL=_Eq=F+@1n5Wma4T9Xuq=(BT2o@zQT-`` zk+&`kc2`w8$FtPQYk$k%7Z(z=2O;K;dgrEI^bGXTmld>o1?x zN8s-s{ij+MXqH#@^HaY*1Tg*F)C@dVI%4-N{r5Zmw3I>%8auVmE^4@!8M_D*_)>3g zFEGw7Rryn@QqC*`<>lptOH4dqYJ>la05^v#ysXT?R+IA>F&0GiZLh=k5Q2*CLZ1;S zgq1k1F{b}CfZ~g?e?XQ0;Ih>f7(k8bHTer4 z%=c}|+TM(6yB#shSff3|v-E|P4{N4WK={D?)!@-W1$h*qWi`H-2H=l;AekFF6LuYqXn8VqA&goAkj zA=QB19e_oAiF}}otR}lheJ=KBc9m39dEmk7fnt2TH)CwY0yo1JI-GWfYht%@@nEl~ z@+O_fkgE4p3gx{sPOvwso8hE(SKV}*IRl5yM&x(~r@`4u1Bi@^hdXS&%>@`$^^yi| z{=1nROrKViIrjk7xA<;#KsH)#uxD}Hp76Whg>-UqavtcJo&EKe_vT^w_bUJ0vMJG7 zUa916R1I#}3#c<@B!eJQpspw-*>(KgsYT+iz7X+fw^`pDqq~NC{VOV&jb<1#p5T_5 zjT|?ID)r5FJ<{kNO_M9JeU2ug!H!t!`~dwuMoN#syt#1z?y$b+yAu)FK|lox=xybl-(jBgTcS@KeGxCt8R8 zmBPdb`??+-%7wCZ1>P3hRr7ZUA|;85PlxO#O}Fo>K(>QNq47^bi(3R+a@d_7{{M^} z4u!C5?$JA!CE%{pi(mV`HYHDup@7#8gzu1N7w*~NE5py};_03afp+B4U1r^W)j4q9 z`O@gpzGt)X@>2EId!y}fQ9RwF^}ru^ofmS|wG9c30D)rE!~iH2{CYB&aHz@Y(Ruh88wyEC=(`s2arZ#qf>e zckqtBw{8%&ugrvEn)ess&065hwxze)p#JtMb?BAzfuX6#Z@QOTcH@u{jG|F;gtcG;JpVT!UQxAiYBg6ISw7WZw(7YDeQ@Ny0PZ`oFEx;Rt(tff^dKh6Ror*8AG6!OVS(h<5Sg4rw`N)(0*nYfC9Ldyd zA#BuJoekc2o^WYz|EJ(F0TmN@)aiK(!j6f8DC1RnB6h8Wlk?)eahVv9*1kq3N~FGruqMJ&7NJV{!IKe^Hr{*hX)EmR zoart0adlL-eE2m91L`^M4RzAzknf`T`H9J^zY^?mBn|E`8=qQ%BPQ+=iQ$D^texUy z4rQC*f-=c@`(Da4p|_g8L9t`7C$Hsw^+UjcNW$DSn0T2%SCzhO#W< z{DI8sNp#Z-=QU zTSkTW@a_KNq($7YD%C7^2~@UJCu&{dZ^Q<>Mwf!`pVX3_M3ZbR6Q<3VyMKQX2Dj{e(~RD+giNLGt?WgGdfUW(uX9^p@`L-F*LzCz-HPG%vm9@| zEpyq|9d+=XI*ZEiXRI1%zNH)eUOag4N1hf9gPkHA^ltc#P1xkrM1R^PF@DaKnEUG= z$NF6Ul(T1i?5eQ+({3zyJub}gPUyNt30Hqpyj=Y2g)@rVwM2!rI}~%&$|<|%tl&6v z=S0k#KRssd!sBcunYOH@s@;31I`l->#c$gd`dDP?BjKdOiwYl1FFY(nl{qWZ$d%n@ zvtDe9+VHGC&fc;PkIWD1S;LGBab?!-I*Fm7Nq75l#GUJWW&Dlr-12IUsj*nh-w^~A zM~?Tj9!L|@tA3+-Cud51pg|h@WXRp2<+oMcKNy}WeJi#ieeoRS^=l{_&|((bP1%_G zd9A&^vhtSCW#{-Oc1-PwzMu|;)?*jDoc>(WxlaUt)nv8#sa>7Vo;-N)dWOipHi})^ zwtnfvV%oEzWjD&BW3Zs)&mnX+5*P5-?+3hp)XudLVc#tjS%Y~Hr* z1IDQ(zd8J~Xp83@;g7+-*G{s3yQ!PHTEmwra#KN0wEapewTiuu6ywO`9#+j`Ah7#{AOEbq_{)l!5Sk)=MOR) z#%(p9>N7N+QY;nsevWHQWDrBCY5~UZx3KI)_e~y4*=Npa9Oc97nk_$0VctV=sqTVy zjVjaa>oOG6-_xyoer!4IIE!L`OXX~j@T0<0Pu*uHOvs*ao$~AEfs3lc6)(aaZl12{ zf2h~p?@{wW>E$9Mo|P|Ayy@UUBmC4Y;dA3K-8siO+r&BjN53B1ouvm436&r8?@d_I z+H{+zWP8n^NOGxV+4gIj_y=OL_Bb{+d`?Ljk1=wOg48ahO+KUOB+b6VZJyz4YO`}& z-#k54x#!V$HZ*w@*0dkp@Jr}AncXvNZXazo-7O>6HkJ(WS5nFo;5o5-2E_{YZueDAAH?#G{Dxu;f?dAv(q$N zX2IFv1JlR0((L6O+@?+QSGm81qTlZzA{sjL`5a2=I?8IWbg7}ei+?NmY*KmW!JpO| z%l&k6@3~nqJ-Pg4lr|Gaf6Nc|okw^>Pn#NN^yLj188Ql-Klt(kh46H#Q~J^5*%!x6 zUN#ih(8rQH&`;yHdbe9HdEKqI;k3l=rgu#{{ca8&p4O7!Z_#u&>g9e^FpN*R8=o#Q zDU@1Ub^NplWg>HquSn*X$FT6vF}EpReAcy-719-AD-|$Ggos$X_nLU+q-APzwqZZ)@NEba~3eVEcsAo zkbB;0bD+yxW54hVk2U4>6FXj}KS<7!Q6F`G%XxMWZ_34ql)eTrA>R*u=QNER&X?W1 zK(X^>Df6Ewi=G{ur{Ge*-?9Hwy`k)oa!#<^-=63?diYg4J-!1O>Xc^l(DE_S6v8!41{!69lkJtY2!w>BzI!~>%T=m<{chgtg zSUPu4q4C8Jh4KDT6Ro_CP5)`}0sq39H#vz$FU?kc+I4T>p7pFN7mD$-Mtxt58^hoi z!>T2BS1++Y^{|-cmsMU76vLoY9MJYZq(1vJYt6a!t>e6Q+N3X*02L^2zj%vj!`kH8AF-Dw{(Ho!|!rjDU7x9 znVzwxXXf>9IFoy9A+Jt9fJ+HOIqn~P)oCxu`uJL@wor0*1_4|IToV+E>+W7o{PfW*L*WXwUACnn&I*V`o z?7T_)<SG*Z+b7ICq1>Z<=l*U7_%&ThnX@bE2j!e|4UyW^Sw)F-zs@ ziHhGEqc9vBtF0`7D%GY}EU-|NbkX*?FZuZ|^>|QFwyg(v)V?coosO z1!~0)%#K;>uC0<@-SzoUtyA;U#Ov1{W{xWRn7;Z^#g-+FA4YSW$JmrkxL~(F9VtDu z!G4_DSZaHmUgw9?O0U=8kJ8TiT;Q?r!0Gd=3>->~oc23EDXd$tTv%5AQ+x5X%DD^k zbqi9D^ZPrJ_wV9Gw;XOQb&S5>Z4-Wb?jnKgS-r75)nVBz z;XNtS`O??d`sq!z9{)IQg5l|TlUCY`M(Jd_telfNt0Y2p(Wcu496e2?Xt7^*XY6pl zG=Cvw&K335CwJWQxZn~sb&~g3ou{06F*d*5p$G|02#pDyvASyWRh6W`DJI<2KW|^i zJK|iR{@7>jhu(UyaMkl}r{0%)4XtuHGu}^sWVtwV=Kj%-sKMuw^2+UwE!MfnYhG=MzQZ{bcQ{&=Qf{Vcu4gw%k42N% z)+qbAtIKtt!sG*UcZo#xk2|v5#^VG}d-GIYj{$%8!1ZNOi{-sM^rdb0PWybjJbLRU z?WwSL{Y{;66^}`iRz96xW?*7DV{U5aLHGJKD=3F=#GQ{BFy$}B$@yUrCr6diqCcGI zxYJ z^W3uj>a{u3P9BxY*rqE+xj)lFGE1#`IVW=3i8a`gPBvFPOLk_STqU(?OP5t=*8WQU zgu2PanFX?%{!K>NPhZL{T{Uy^>`lt6eo@{%w))1jSTX&y*5#W9EQ8lpubNkTL`!0? zeb1Vc=S~W>Wkcscg;Px3MgG!qCO@X-2OZT}2N$oZa-2ooFuH3{C)A^m_TtIp+akZd zPsqQnYGL}yyD0zX(M4tj`=-oLmvK@5O;;o7=;=lKbzMzOy)Lb7^OdJ~{HAh2bmt+q z_}n#=;{`X0Wf%6+$MPjq&h6KB^&4ZQsd{OT#`YKa+U%nomm)RFt<8k*E`M(>e znj@^-+5T#pBs#`z58fEQmOWeXfjIv&=ZT;H&RYRH?N7Qs3j68a`e?1yH9OY&{UBXS z%{VH?QMvW_`uaTY%h&v_oVT!Jc^EWpy0&(SWB&cQ^T$_gnsQO5jC;cA_PkT>i^8oo zUcujD6^Rd$%PcFq*&4BR=Vqnx=%VSnHTB4mBTIE1WwM=b8QDBxPTx53qqY8p^S!Zw zv+*16)m5knh}x?R+!E#3O>#csnqLu2^=yp_y!GV$4%?$X+#81NyC@#d>?~Qp2IrgVVUEwA1^eR1>^nU;eoveho^Li zv_8%3A4tX!ku_vL?n-|ntv7E+id>7e*<`P(@f zoodkvr|yTXn|{4~^8Wq%ceO@$2EHqu_J*&p>Q>a4#;C)SqFloFFMBY%#H3;=tEI?q zXW3`>*VH^ZNi_=)kObD}}b52BpaoZETI;8+dAL&_V%)-zg?4~iw}OU-3Z3N8(OtMG}L zX}&S0d0^wv-Lz{@ul%ZPwPh?%`aXI%GSyrAFwo>@2W3v@W~tuIp|hVbeb;L9Mwg7W zDjwawo*Ba2Kk10OuPodSAFR$=)?;v{d7!4(C2s&kO*Zp~|^mhO^i zTVlBQ;8b2`e5aPE*tFZ@2S#rR<3FH`9V?OUdOmtxjD@ML;vlQeHZJQFiaV1|& zU#p!E3Sr*{WPz{OpM|lzQIegknmYUTl9-v>68Nw9i@-*6Q_na8~>+38JUiP`N@E2=Y`!tEYk)xe1Sqx3`4Nf@jKgn@n zQJC^7)vS|}%fl}l%~3c=kKTE1LchTel#?vDd|{>i9jgyEZBKZNok_fEV{?h>#|!CHuQ>}8D(dZs-p8? z<4SikzEbhImkL}^9Fq12R(SZreQ&3sYE6}y*mvuWxYvhu);R+rIj>b^rn_u7p|7oA zEb6vtNbhNw&HbA)e9t8%T6Mc67mjn_sy!)oaxl_AwrSh8OX)VXPY%9q7^ke;y{-5x zl~$S~aXI(y@x;MBMYn~2c&a~PR>JAceCfT`!>Q66#~eFQ)3EajWzf4`Lb+$Vu%u$g ze&zWGs>NCRjDKC=&=mD7VVioPw6MhD4_$fT8}f!{%y}}=cx-l3vh@D94tf^c_-={f z?BZgr)S$bEbHxe_Q%)@r%{@A9F{My^zWH-m<@1vq6?eajyyX#cvFh%Dp2*C|&-r_m z9UQFck5Dfie!lI8ZQG`AkSR#h51F>aOhsI5jOc{cd0E>Q+DTn|Imz(hLn*20{>Po? zt-QrkeX?D2|G3yD75PP8O-mdz0?#zzcc8IGewr;zGNZ6HEr!6`P z8)Xvaaoln#BEi+WMpZ0tWctiX&{jUBHcg~t^c2bcBD9BEN}c89l+(G)Jd-2w8RO|= zN6mM7w#0a${4U+T=!0+WwJuHE{&@1|{xUg6R8J=V^VNg5!ry$3cTF~`e0p~={=8Pd z`~YP&?^ofrk6!0ZD3dj&EzzkF9_`D0eox=^iVynp7djo#$_*~x;VryeM%-=t!s2tq z+wKNzxFg;7Xb!N0*EKNqYEM!zbf^6?`SB`0pDmHIn|*EwyOK95Wt6dPHt7yJlrAtP@i=?YZ@4m5Wa)n&Sq%jhdAtCrY)Q8pGMglWmE5 z38}ohy(RoLO&nqT6;a&|@p*<-l=B`tMefL+aIQFGIE!M%$Xs530ubJq>YN7l%bS+6)xRZT5y=+ zwfG12B)?IaLAOFjNv9fl4)&N;$qvZuQJOKTe%5A+(iH2(yB-N8))&b^=8DB7HxKq- z?#P@ILzA%ev)y^qXpxQL=#qi;N8WTz39gz(YqZFf{E3Z(UP|c=9i2MvBiy$IgBBt>t}BRk<62DK+b&DDkKFP2J45ucJG~ituF5H?~X} z1T812z75BD30~t^bAt=FITLyc&2uQ&efNeTw8Wr*d)2N@4&vhv$*2?*oD@oCvG*>I<#xt+ilCHTUkwW^Ss7P z@vex(z$yIQQwPTuq4O(jYDY^2S$RCj6FDLl5)#s-a|C}H>!3rA?}FF?S4Xi`s}zhk zRtLRo*(0{adRo2daEyeWi`(-s-5Y+o@-g`HrAbcxnHv5L`1MqR#rm6V>?ivcF1R8h zS(!I~+ye7}yImDV?|IS^xz26#eEYiCJ>rKv#<>q)o_l-i#AEaD z=iGnuyYkD--*_5}^O|1FSA9IXVAfLnh@f%crz4!sP4vf8Zt;%!T+#5T-)tahwPuR? z`%$0D@9}$}!p2A`8YB34XF^Uoy^F0|ZoSHSx~GuS&JxLlVY$09?Vg4XT{o2n${iBK zw%irnV4K8bih8x&T#sKgITwmMU6|f;uXu+|#>!peCLc-0 z{%J5M$Tnc$a~R*ocF&ep1%I*U+Y66zhrI)0FWI!5nj5sfYiH1zRfnd%40%3ug?;+R zd!_jSl6?VV>}S=>>e6TQ_3vbu=S#=DD%iP0sV}IoW7E-zkvU6F4INqi+06Lllk?Pd zE*Gdy_%B;r_qxO`_4{-2(;@r@$?KBOwOiMiw*0}z(;in@hL?`Fk1W5JrETaOr$kfA zsK1T-$JGPF%uzjyg*S;*9P!bn4K9j0_KUFSxO?KI^f3HzQK6~_8t5Bey>!tw@RT_E z>eb?1b&|Ko863kuKeu0esAkuO$qAz8&P~TJ<_hStKj#N5Op<&xHs*TB=AB!I)Rm)# z^}-i9?z%*?**=M~%7#Tb&#}t;Jj3h-C2*RmI6XK4}x&5KEr$krhc6J zLd=5JS37LCPCM;(_Dx~xt1Bln47XCOTIG8b9Nzop^c%fWhU90zwjMd)IBMx~-tT30$t<&la)K> zclK@U8}byHJoQ%KHLBg+SJ!UNX)-vR(7oN?-Ra5elsO8&HLi*4+=nO5_vGd0OLul& zi+VhTL_m~uWNdWVd8Y8$f}Q){NWw$t@_Y5Z>QiB1;YrGjmV+jFmqa(kvdwaZ<~GRe z>2mft&|w&*+*hygGO|f2r@GMT+G->59qOv8LL`P>egCt`Jw5j#7}Yhet*z5aN)ir2 zgS{O8eR{??Z@o^7x*~_y>wjF>U;oVJ)7E3_$H%$bz|Z%G&*oEvn3?O3(wYM_9{=(ypLPQk&v`~XKXvS9%uQ3WzzvTk{(=3F%#OhbgtH@dyx+| z-sn2DGs`zOjM~>Rmh!N)zpJq4u2Rwu1BUFu!XWE+2CC>rbP5Qcv%hcM&t0=h#aXv} zul_8;f1lhEUfOPIo7~9lv#2Y$Q>o}Jdg0JP5tV=j&2%sR;eE}#H4VpdbhCPRcNEw6 zHF96orMA|0I5@uJj1ill+P)y8_alm`yHjK*?mVeEO3kt<6Z&)Fb2Il<`HJTc##~9? z9e%;@rOc1vR-7O79fpNI?$B?n?saax)k9St(K(Nz(!QT@-uloPOM~me7(vpYS0x9;-ZaP4r!VkUBDaS^gS)(%3;zOz5zjc z;R@@!L;kYYJX;j=Ctcilp!HE-Y}tjN!q@Clg`S-v2Ndr%m49+9)*kiHEo(1xf%&_! zDwdl0)3=(}N++`lSoWEtnU^)SN{;t!%(NXnntN`(=mgQJDmoG6C$6N)zCQGEp3DKt zbB~-7Y5TLM`BU2OT5P@=-|XqN6?vT@f8*3Nnc!KMepX!-zVY+aX{o)Gf;P?DV%;gr zoFk*9>b8!j=(qMAoiWrZbH=_zC`^qr+IZrD3fTtwq19dP13`Jm!=|pLIHc7^ya=OT z=C+gdB>egP^$ca*MF+71*q?WD;DJtSBfLEE`L$Hz%VFi-_X!P|OWydLeQfw+xps*V zcm0WpTK?_QTg_KGcyTUf`rUaFzOR4oo$2FM+!jdcp3QzWYsI{u`%c7{QUSaDTgB{?mH9_>!=lc8G}-%)4}`$bLG zvtGyZ;;4ZAmq9@$h=wd#coQu4IH#a<1j;>nF4_@wI3Dnq&4Zqi$&N zORb_8gdC)@88fa{!cMs=5HjkVTuU!JYfQm(^61%L{14w#TaDM75bhxeQI_EU6pG3U zLG~Z{@Ae@!Wju$_MPIx(I` zZ+fhh?ho5&OZC2$mNGoNdkdjs48jCg3#}xb*H?xuKfLHQQhUulynl}vKHq{Mf zG?GN}Z$a?zKlLAvmp3}xMZ48l<_hRbUI5*f=fOzs2Hd}S3Es)Rj>UfW%1OPDR(yC2 zw8YPX(!-e3Ey(w82Pk`%XJy=o<)VLHMDgmDOyzTOkM#v_bE7@-X>|92BRA#z5j z<-5U;Z?1u*!Y#0Ve+P7t4wkUrk@?dB2p$da7`5Oqfki%RK z0!?MX&*U|PSiXfId>)7pgztQj7JnCga76r_>`WrDsS^mlj2k=VFy?y^2>yhx)*+BN zA#*_1(a1Xaef;p*ccoOClfRkVHE>oJ19$z`;Ar?hnP#ih>1QMf;noTe?Wh7VPO1VR z*HMCdHNkVbgRA#o9uWgdW>^h@O>i+()oU?^W;^!CnxwZVJFk|j$H zAW~%f2_G#(AhK#I0$C4ajSyPCYh6@VRSK~qoV_fNCp@&DLm-ZED2`zmj%gI)ljNxl z$y8n71b&2)cz38wa08Z?E+l#Be9gaHCtx`ON%$_=#{g`U?!nvJ2fuE*YFiE zUDO5mru!K}REnmKzY+VF@!b4;&&qMeDVI~5hfy~7} zu8Ydj{88Lsolb_cngFjtx*e3nIzlPK3Cftxg1iF9oX}<>eHmCd7vy;X-4^mAZGeY4 zxh}wZZmB!KouR6gu;0U-83hE)7~Zfso>@2N?mzP=sSh=X&}L%JW7=P{u!ClI)a z)DA0C!+nS&8TS#~M|hvCVX`NXy_(1*vW|aG>=5hsOy>`7i5|Lg5%$X6B{2>F4P1b_ zY=Bx0LMA{B2YL$Wkm92c`EgWeX{d$PmRcw;N{7s(;NG|}r~KGZ$JE@^@ajfx)NmIs z;>&y>&7bj_)a@>aM4&?#=44z_agHr@Wg)`Hv?nzw`X(Yoc%7_SGVUX|6W$@Z1;KwJ z!XNO@j-`r5+9(S6ARA>!Wr92M0OHIulX-xu6v14Q`6MuifWeKBk22|_(qoW#cE9ua zv!_IlAb9^7_YwIyGWLI11LT ztAl(_G%!M(gA7#Ptik>WUy?B*YmLb5pAeSbzjbLxT^_xk;42spoIj||_yVM_S_GUM zPdy2^cl``F+gr;moI8_@J>hpEZ%CUXxRXAJ{Qm>)`Ana=#SDib0q%rf$XIP|6Xy#t0rqCfvJxZvAz4eLT{1qTUqXj~2g1DV?|d$f72pXirC-`7 zYkUN7eBxX`fRn~kh<4Q)3b#|~39;9RJM-%)(guc!!LAMFKFK)PNmk3(eI$V8^&Yke#|8w)f{m_C!Jt;Z4%t@BJblRS{l?8XSAl zreI#l+zH}ulYhl zd-YdZ2t5eozM~f4a|7=279jonI@nqo_G%@_PXWd?&c&ShnGEEM6`hubNa((H5zU;NwY6EC9YQ&y0G z|H+{5%WFLUBu43x_{} zsYKn!5@CPsp$WXM|~dZXPSVv3zViu0@F=gu1Q@u>4T(W%>clQlT@zNP+C>|E*M+>fN1%I<$aC$yyy* z#{_qj*Dd)mkU(>WNbirs%n%nwdR*9@T7D6c_kWwhT@5Q!!dzjH>{q0n1|B2@SVCob z5H#mRK!}wRFgy(*J-9b`GSHq~b+nW~3t7x#b>ei_N@vmlE> zg+NzrNMi)*|6MzOe$I{e8|tjeg|6y67)F^;of8KRdhZ~@MH`BkR0uGC50Oq<5Jxo! zSM}$`Zkn>hC-85lqyVcM#D<8)edNoylkxuwo%Jw;drv}`Be+@PK9d+lZ2Ip?#VNF* zhQc&xE6;)M+5%`T<3NbB5fmn)&RoVuS+50Qj+zkVYzTIW4=XI>pOL-f-%jbl)+vb| zI%u0&03f~s8c5#N4A9*I-TV{?^E3gfqfSQ-n??5R?@E<9iG%e;Y!qR+&{4^S&gy*V zt}B3nrc%7m1%{s;M7n>32qy!udiS7HU-It1&HvdU7846%T?c8l%FtVui2GCHSG+$7 zaEIV{@d~gc_${9?hFjCkBD#US0K2L;E&IccWF*~;9(#=96`0y{$3hd zn(B5JaoPSA+|;`A>;$OJPlbjO4yq8@&{4yKv?yPoc~}D7(*hhnNP@=GOV;1pHYt}$ zb2w0t;0MVxH)u!w1A5#4f(Akd(tx~_7-$Es`fpGVwbUuh&m;QUcco%h_!MqZ*l>An z3RLsbP*=zh%tu8o3;gU2Ai&8Oe9TnQM|i7VQ}RBsjsDA28c&^_6lgP0m>LC{47A63 z+7QnX7@3EW{3{LJ&5(z>t+AFQM1=?3_%A#8ra(3`u)DU52X$rnP|xQ=eHrppMLz1u zEFYzNz+?mzAl(%NSI%zEVycB!+pw5ZPDA*#DV^9>kx3fUl4S zZr;SN{RI8sw&|^Ex!O7DvF@7ssvi9OQET%7~&)RAvY}! z=)MkMsv-%>&u_i?tA^`yVx}YpIXK7o*bEnQ;&DInLX{GA+8)9;U&o%%@GXqsj(J;Y zGO%OO7Zy!r7Ut&=-SxXtQ99EyJs}$H!5pY8%7Yr5gSxMDG$9RD#kmj_>;?H5Nx%s8 z0&g2#u-1~R@V3-eiSo3%!t}S7Wcb;}M0r~eCeo--oW(-ieW0Zh_eGSC(Aj|e-GqSG z1YbfA(lFG5`brNzBXiSH2gMmtkWROUTz35R??N04)faJo4fk_{g6woC%FO^iFB>Wf zbD^q)(7_YTM`L9%(v=JG^dMj-MB!Y-LlQj@Vne(jHrNB0q27=Z8wz>pagfHOK{`Tf zZ7B?PG(dk_E#`GFOyoVGgUC;ui?+%vwE1aJSDrJ}*<8_8m=QIYjuSRzK+;G z-<1+WU80g=!-12`1TH59X<$QXeiqIJ(t&ePS)2#er8pnRQ+4HqkjG8}R!kTqMuk8M zg9d4Fk&ukI$43T0D&o!0&%j}>!C|i!K+mUY=>1d!{jEqxdo2v3ebSDyEuS3)Iq0+F zW-tZ3n8it1n8XM>$;)LE8~(df1vlY(T)00Z$I&4xH4*Z&(x4zG9g6cfP|728pDgO}^04 zQC|xAX)#ceLWSJq5PEY%1F@_AM~TA>?qxCPkeQkwnE(6?LPshTB0rUIIZ%c?Rfcqw zf2E_UBwxT+^~hIbPB`(A0{olEny)E?Pqk&}r{F^y!nd@LIVsLcf*=obi1agWuP80} zx5oPKh^#Bgl1e~+&gUdTadte?5D2;HiI9u@#KUz#=qN@yM&_d&*GC2N6@k>4P&^lS z2T^_wg7reyN>eqVqx2hEzR=TwbHl|o;_y)&UOd0o$;n73KF$tS@td^AaBs0PYr=5bom)l_-D7eAFY4 zHB=O%3?@(_kipG@xj`KlWm%Y~Ej+k)8C+fMRsUCz|BFUGSMh#G#Wlf6j)$!DB*9z| zI!5NBFc;?o*Ew19EJnCMei6Q^!nIya_CY?{fw)e{enRpuz=OuhB8Ut310(G>ps%aU z%FgEetxw@E(E9F2Z53R#3>^Q|gjh&VVgLuPvk4vSWXR1(1}?4-9$DwyOejG*%6K_~ zIpHI35xO`@bfBT$RFa*9vY0E7V^z3*Dhg58$9*k1+#ce?-Q&1<+5Zvz>&x;BlH;Qx zJ~|u{ac!r@$3O}*5)yC^;3UUE9w!y)ND=VX7nzkI;4v;c0V2FCAjnl8m_c^UQGQlE z`S`3Dv&vJ2~i&0cs0_|-sC-FvgMcMyY=8v@fPkC}& zHSmN|1aNpI(?j*%TAcePLA4`$4U5|X9Ju~24W*Z1T+vjm?%q`A&$VJ;Tc|P^o#$O zIR1C~N(%R|^0n3N_eSus(dh|t|5%m5qN@=esm0$*{_nK;@6-Wc2?0ki-=H|24<@D&(HzLgj*X zRfKv4Uh?u$z$E=*UQTl6xIgBUulJZUM-im{HCGw;<+;k&_X6aZqYyKEUOoyj!<;G9 z_9Z81;JwN>`LeI~2T3`6uKZ12{`H>FN#1{(lXB!9zT|NvC;tR_!q*(WI`{3ikr7-uK5kYXIFv4=nW*!CqIkCe%r%GnQ%s+~gqKM{K~^Ts0G$ z_*VXZd;Qm~|HV^ZeaTsVNSl+MY`o0eU z8KOJuKpgrIl^>l10!{44L58_jRWb}tIY3`67=MOpPW3@Na1Y?c2>{G{&Y{$NdUx}Pe zlRgCfU@l1cG}k}e#|1i!qPywG#d{e*vcCl+2UsHxmJsh_21y~dkcwvrDUqH?yA#Bt zzLgy1gZ6m<+BHFF?}k8Hd?-|wWueWL1HSfJ@KIYq9y?luK+bIm4r38UBmALXyceV( zOQnrr4j(E~1FaxE)E<%pt$`ix4E$^sv^CeD{gV%+ISj~73W9WoKlbg1eTJa#0DS`p znW=Or=4C)>Mi|8UnR8#icz75aCjArNFTsIGA;J^nO!s#jV!bWz`kKD;475?pN(-`v z>!7h41aY0stc@%hLjHf4Ge_MeP?gY*~(^t1*8 z)z>`{evS_mq@Rt@JrXPOZU1B*C}|PY3rufgJX>^t!Wb7QKtB~1^UADf9Pb=l^EwSC zO5(p^(Vq~OAuRt2KN%`MzaHsj`Z+Bj6gaF%^v%Txa#oBVMEF{EyPB(W?2JE%O`0@m zBwk|V%%1Q9IX9+cr^fDM_?TC7am-5?9#Du-k`Ms(MM=o_AA5t$-w_@nxK0$AGMS7W zp@I0@$+#{EbkxpFiuOgni!TJZnV_y6gZ4l?`cI<4$xIDQwPY)0UOicfWr;i(iPa!H z@cWrha)jr*6kk(3QuhOXQUIQpML{9@8-pzsGDA$|N6Ha?AwYPCoRt!p{{z)p&69z0 zAOO!Y6va+LR!R)oV@$NC(jgkxiLshQr-k8%-B|Y9zJI45pHt|r`Z{=)0Ts!?P?Z*j zx_AsE`CASJnY>Xa@BbnZc|y)iCVH8@H;DAH!tI9sw2UG>CLi?+P@KK858d zY0iefuN89tHIZk+9wrJVbU%zQV>8iqiw8d1k$gOFhzYPA3UJWL2yy%Pbn}LFWV}e9 zWX_2UATTD}TIFiIhhAGb_Fa=f!*B<3R2_w}M}ZJ#q0knj`+5_WqolhV|5g9w6~+Cd zVrW{dKl&h2(9aSNwG{;ri}OLXR4MdzwjgsvcxUXg<;zLmyAEF+sa;t1Z~}Fjtq5 ziJ)2!lwiD2KAw{hKNBy50W@E0u+V;+gZ9z7f2yAuq`0mU43(x8{%&M^PlVg`JUyfr(F>5Kh(o|AFmQ%Me_`dfpi_Ujs3rKfK#6&@WkS9o;BLiwqlt)^6MVuTO+9W#N8ev}+` z92DlIK?(X*Gx1y|FO81-v3?}+&7*NDbgPgTP@Bzw zs!RryBi+2TaIjK)4sn5wP{@vj0;Ibz3+ZPg4oH7md=RL~OCSzU3!_7+bN{a2?|Gi* zX(V4sWH2Wh<8$JX{&*q^D%xlk4YPu@wD^dzIG}Bu4Y6x`nOV=(qlpY8Ol%ofJa41 z!ImsGLpjRZ$dXO>+?1OfaR&9jem=%w#D_Y9r-fpBxVzb}|5j&zQ#kegL!opfu|M?x z=c1L_ERn3Nlw0fsx<*uxs}`=U>lq2Of3c`Ls!IN`ojq0OHz#B7P=@LnLWUV9w2@SvRF4EjQA<;jc9xL z?0Zv$oyK+#BlHg#$_dWwoK+vLwpD%h(OmB4mhWxj@5(*Ad1*Yu+vrn_n-#JEeA zKibDWrVwKP0q7ndff@MKHYV|IbZ^uJ-B4zreTuPaj^>&JH!hz&gYAsyg@Pt|f3kyV zF513{K1Ps&_Gh9u_JQ_34`p&8%H#~(qcDDs_`%WM4o2HE6m@<&^mjL)|C}~VwbQe~ zMv2`u4T0=|zsth_Yqiy`+7dY4I?bFgJ8ahi@)LuxZYLgH*PzXlFvQOcYvv{gLS{-h zWC-j!JpV+yDlUQw8FZ(#lSd8^wq1ZgY&xP>6MKY`79F^V?xxoo>!yd_pEyD`%?|fc zKXi)?LM`s$9;V7}Th=W*_tT=e`z*Aiq;nE{hl#F|i8@m@>Tt%Ia^PX5V~S0Yu^~X( zCpHt&)hR5R$Ey_lE})F*iguza%4TJaPlB&`XKA!6lwu5jO%{nw4T3}$ zHA5^TNMlBiB4tTGvwoaAb9;W0|1hz82yf-%`E5mU288?BfSr*d|KPq|q-|pJ{H}+@ zQcVL3qVc;Eb~x1J#sR~_u$OKrPrhaOHWB`wv~1CwMQplbIccBRmAsr}XsqQ!PFf^T ztyH?MUN}o^dx0HAD!abDevC;qYO7(>pgt=G_^40&8_PtKcYl#aFIg~a8pb3R=cdL& zX;BtbH(j8s`q`_a%rM_Z3QU-J<+-)Uk-p7Y`v`mf&u!|EKfJLp6}sz-;d5;f#5(GOhlT{%Eq+giviB#T?Gnb0ry;-O;Q1Um zt3w+G_4mqR4%!R~sNcA+9jP$lSB|rMFO=qEn$l353ZHAZ(2KEvHHcGyi2_)>xuRUs^Yw)C_kviZ#i-@;-EMm&&3NeaXm*sDxMLByXoYQwEb-!>!dA4VmtfF zv!NaJ{}zmsY{S^YDvTA2NBcU`-4xFcQ4Vn!0=$W>o1c}8{;4v^igp1S)$*gSgN}Aw zh|7U*+a0-%@iaS<6@%Yf<;4m78;$u%P{&0*AN_R=Xlo>)&0NHZg&KYq>ed+Zg1SDj zDKTshyeyPrM%wwO9PKgL5MR4!R)}K?vE^&CnNXP-hd$y^0bddQy)-8Y=Q06pwgf1| z_nJztKv7zZ^H1&m`5nha`&BhN5^8c1F*c9|Wh9OrWn?Mphqa|#w1XLVE*OG5p9Klw zu3&E<+Y}k-MtmRNm8zKjdbJsJXfIBI!jyoa+|X{?=^<}(i7z?G#W_pz*_!uc=CDCq|qQ9yn6Ma_r zJ!*iRjvyX0IVxa|hn*hr*Zh|$Rq=sPisYp~=wFO*S3%wK%%ri|*=g#mr~qn^yRjB4 z%Ad%t?@lls@azXDy_AiVHWW#cxTQ&AZ6<=s2;dE~oI+7;LtUzOJOVD!-fA>--)3n^oPUDfUjIG^e{L$;NmVh%RiiJ`PEj+=M*fzs>DxZ558`00^6;hco9kwJ zQkRGHBrl5nx!%Zql(Wi>1aITvWPh{aalKcg-OopRtCZ*$XwT&^yzzSybBJ<9|BI3h;<= zQW+cPsa=zbG5qD|qpL@oy#j5++*r?{lpym)mcL0|q7TN&dl>>N#2#Wjjlln-9N4Nn z>@s!#I1;^ouAUBK5|aS*@p zJ~l#kMyS;pUOaxQ$V-HzK#O6jk<`EVXBh4pk_=CMwB205%UpTb+xYb%Qa?S^s)3h) z-#6m^Sc7!?o4xxq((bo;oQKxDSa+QPcBCsrqi*VN_7-eZ9yV~oZG7_My{n2?C|}V> z>Sg%4^V@nO*KuCjs}sEShS+p>h;q@u`m!}x--o=<2(y}<9%3`xfpLNi^n07gT_>#k zhlF)i$eeP}{~ztDIpk~dTEIsvU;VMEfo2Yc3Et?d%0S;hG=w^;51Gl|vNo2xzR*_n z;k!LM`e-qV#bdcn$KQ)79{fW!;gh~z_W65)H?xP?%#0Da; z9lFzp!F1FMGa}s~m*E4c!Ilv1tcl-UNezr3m=a*JGm~bsFDJ_JLQ$e$Kk->Lqn+Md zoCbCI2~dXm2N(UR>@b_*1P^T)nyumiSMBF}M(U5`IFFaIBJ80s(PyYMC3vtF$FnLU z9CcpvVHWaqtd|kuq;`I!{GaobK;uK{A!b==0mi?k27a84-)Xj`hvIpWkKTqvZ#~Hv zcioy0Tcz!PuJ?EMEI%U#%g;oxe*dma$8id!95o^WDqkg?sw#z|9*2q&^9zt`6e0p3 MWk$*mD^n={2eb0k&Hw-a From 3a1fec1d0c1ce7c08a17a538b6d55944253ef833 Mon Sep 17 00:00:00 2001 From: 2mik Date: Sun, 22 May 2016 00:43:14 +0300 Subject: [PATCH 059/382] ScadaWeb5: fix css for FF --- ScadaWeb/ScadaWebShell5Beta/css/mastermain.css | 4 +++- ScadaWeb/ScadaWebShell5Beta/css/mastermain.less | 4 +++- ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css index 51e72111f..7c53755a8 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css @@ -28,7 +28,6 @@ a:active { background-color: #23282d; color: #eee; line-height: 30px; - white-space: nowrap; } #divMainHeader .hdr-btn { height: 30px; @@ -38,6 +37,7 @@ a:active { display: inline-block; font-size: 13px; text-decoration: none; + white-space: nowrap; } #divMainHeader .hdr-btn:hover { background-color: #32373c; @@ -64,10 +64,12 @@ a:active { display: inline-block; line-height: 29px; font-size: 17px; + white-space: nowrap; } #divMainUserMenu { float: right; height: 30px; + display: inline-block; } #lblMainNormalViewBtn { position: fixed; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less index 1f4665b25..387745e56 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less @@ -38,7 +38,6 @@ a:active { background-color: @menu-back-color; color: @menu-fore-color; line-height: @header-heigth; - white-space: nowrap; } #divMainHeader .hdr-btn { @@ -49,6 +48,7 @@ a:active { display: inline-block; font-size: 13px; text-decoration: none; + white-space: nowrap; } #divMainHeader .hdr-btn:hover { @@ -80,11 +80,13 @@ a:active { display: inline-block; line-height: @header-heigth - 1px; font-size: 17px; + white-space: nowrap; } #divMainUserMenu { float: right; height: @header-heigth; + display: inline-block; } #lblMainNormalViewBtn { diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css index 48075fb2e..ddec08ff2 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 250px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;white-space:nowrap;}#divMainHeader .hdr-btn{height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;display:inline-block;font-size:13px;text-decoration:none;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenuBtn{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;}#divMainUserMenu{float:right;height:30px;}#lblMainNormalViewBtn{position:fixed;top:0;right:0;height:30px;padding:0 10px;background-color:#fff;display:none;line-height:30px;opacity:.5;cursor:pointer;color:#9ca1a6;font-size:13px;}#lblMainNormalViewBtn:hover{color:#00b9eb;opacity:1;}#divMainLeftPane{position:fixed;left:0;top:30px;width:250px;min-height:250px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 30px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainLeftPane .tree-view .node:hover{background-color:#191e23;color:#00b9eb;}#divMainLeftPane .tree-view .node.selected{background-color:#0073aa;color:#fff;}#divMainLeftPane .tree-view .node.disabled{color:#9ca1a6;}#divMainLeftPane .tree-view .expander::before{font-size:10px;}#divMainMenu .node[data-level="1"],#divMainMenu .node[data-level="2"],#divMainMenu .node[data-level="3"],#divMainMenu .node[data-level="4"],#divMainMenu .node[data-level="5"]{color:#9ca1a6;font-size:13px;}#divMainMenu .tree-view .node{padding:5px 10px;}#divMainMenu .tree-view .expander.left{display:none;width:0;}#divMainMenu .tree-view .expander.right{display:table-cell;width:20px;}#divMainExplorer{font-size:13px;line-height:13px;}#divMainExplorer .tree-view .node{padding:3px 5px 3px 10px;}#divMainExplorer .tree-view .expander{padding:4px 0 0 0;}#divMainExplorer .tree-view .icon{width:21px;}#divMainExplorer .tree-view img{position:relative;top:-2px;}#divMainCollapseLeftPaneBtn{position:absolute;bottom:0;width:220px;height:30px;margin:0 0 0 30px;padding:0 10px;color:#9ca1a6;cursor:pointer;font-size:13px;line-height:30px;}#divMainCollapseLeftPaneBtn i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapseLeftPaneBtn:hover{color:#00b9eb;} \ No newline at end of file +body{margin:0;padding:30px 0 0 250px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;}#divMainHeader .hdr-btn{height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;display:inline-block;font-size:13px;text-decoration:none;white-space:nowrap;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenuBtn{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;white-space:nowrap;}#divMainUserMenu{float:right;height:30px;display:inline-block;}#lblMainNormalViewBtn{position:fixed;top:0;right:0;height:30px;padding:0 10px;background-color:#fff;display:none;line-height:30px;opacity:.5;cursor:pointer;color:#9ca1a6;font-size:13px;}#lblMainNormalViewBtn:hover{color:#00b9eb;opacity:1;}#divMainLeftPane{position:fixed;left:0;top:30px;width:250px;min-height:250px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 30px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainLeftPane .tree-view .node:hover{background-color:#191e23;color:#00b9eb;}#divMainLeftPane .tree-view .node.selected{background-color:#0073aa;color:#fff;}#divMainLeftPane .tree-view .node.disabled{color:#9ca1a6;}#divMainLeftPane .tree-view .expander::before{font-size:10px;}#divMainMenu .node[data-level="1"],#divMainMenu .node[data-level="2"],#divMainMenu .node[data-level="3"],#divMainMenu .node[data-level="4"],#divMainMenu .node[data-level="5"]{color:#9ca1a6;font-size:13px;}#divMainMenu .tree-view .node{padding:5px 10px;}#divMainMenu .tree-view .expander.left{display:none;width:0;}#divMainMenu .tree-view .expander.right{display:table-cell;width:20px;}#divMainExplorer{font-size:13px;line-height:13px;}#divMainExplorer .tree-view .node{padding:3px 5px 3px 10px;}#divMainExplorer .tree-view .expander{padding:4px 0 0 0;}#divMainExplorer .tree-view .icon{width:21px;}#divMainExplorer .tree-view img{position:relative;top:-2px;}#divMainCollapseLeftPaneBtn{position:absolute;bottom:0;width:220px;height:30px;margin:0 0 0 30px;padding:0 10px;color:#9ca1a6;cursor:pointer;font-size:13px;line-height:30px;}#divMainCollapseLeftPaneBtn i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapseLeftPaneBtn:hover{color:#00b9eb;} \ No newline at end of file From 08c7f63b4a8f16bb42807395c101a19586e37232 Mon Sep 17 00:00:00 2001 From: 2mik Date: Sun, 22 May 2016 01:05:58 +0300 Subject: [PATCH 060/382] ScadaWeb5: refactor events js --- .../OpenPlugins/PlgScheme/js/api/eventtypes.js | 12 ++++++------ ScadaWeb/OpenPlugins/PlgScheme/js/api/viewhub.js | 4 ++-- .../PlgScheme/plugins/Scheme/js/schemerender.js | 2 +- ScadaWeb/OpenPlugins/PlgTable/js/api/eventtypes.js | 14 ++++++++------ ScadaWeb/OpenPlugins/PlgTable/js/api/viewhub.js | 4 ++-- .../OpenPlugins/PlgTable/plugins/Table/js/table.js | 14 +++++++------- ScadaWeb/ScadaWebShell5Beta/js/api/eventtypes.js | 12 ++++++------ ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js | 4 ++-- ScadaWeb/ScadaWebShell5Beta/js/mastermain.js | 6 +++--- ScadaWeb/ScadaWebShell5Beta/js/view.js | 4 ++-- 10 files changed, 39 insertions(+), 37 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgScheme/js/api/eventtypes.js b/ScadaWeb/OpenPlugins/PlgScheme/js/api/eventtypes.js index 5b5272ec2..77882f744 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/js/api/eventtypes.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/js/api/eventtypes.js @@ -11,20 +11,20 @@ // Rapid SCADA namespace var scada = scada || {}; -// JavaScript event types object -scada.eventTypes = { +// JavaScript event types enumeration +scada.EventTypes = { // Page layout should be updated - updateLayout: "scada:updateLayout", + UPDATE_LAYOUT: "scada:updateLayout", // View title has been changed. // Event parameter: title - viewTitleChanged: "scada:viewTitleChanged", + VIEW_TITLE_CHANGED: "scada:viewTitleChanged", // Before navigate to another view // Event parameter: view id - viewNavigate: "scada:viewNavigate", + VIEW_NAVIGATE: "scada:viewNavigate", // Current view date has been changed // Event parameter: date - viewDateChanged: "scada:viewDateChanged", + VIEW_DATE_CHANGED: "scada:viewDateChanged", }; diff --git a/ScadaWeb/OpenPlugins/PlgScheme/js/api/viewhub.js b/ScadaWeb/OpenPlugins/PlgScheme/js/api/viewhub.js index c9b8ebaf4..120de6d88 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/js/api/viewhub.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/js/api/viewhub.js @@ -58,7 +58,7 @@ scada.ViewHub.prototype.notify = function (senderWnd, eventType, opt_extraParams var senderIsView = senderWnd == this.viewWindow; // set main window title - if (eventType == scada.eventTypes.viewTitleChanged) { + if (eventType == scada.EventTypes.VIEW_TITLE_CHANGED) { if (senderIsView && this.mainWindow) { this.mainWindow.document.title = opt_extraParams; } @@ -66,7 +66,7 @@ scada.ViewHub.prototype.notify = function (senderWnd, eventType, opt_extraParams } // preprocess navigation - if (eventType == scada.eventTypes.viewNavigate) { + if (eventType == scada.EventTypes.VIEW_NAVIGATE) { if (senderIsView) { this.currentViewID = opt_extraParams; } else { diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js index 357ad15bd..c2a14c4f8 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js @@ -140,7 +140,7 @@ scada.scheme.SchemeRenderer.prototype.createDom = function (elem, renderContext) if (props.Title) { document.title = props.Title + " - Rapid SCADA"; if (scada.scheme.viewHub) { - scada.scheme.viewHub.notify(window, scada.eventTypes.viewTitleChanged, document.title); + scada.scheme.viewHub.notify(window, scada.EventTypes.VIEW_TITLE_CHANGED, document.title); } } diff --git a/ScadaWeb/OpenPlugins/PlgTable/js/api/eventtypes.js b/ScadaWeb/OpenPlugins/PlgTable/js/api/eventtypes.js index 63aee2e22..77882f744 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/js/api/eventtypes.js +++ b/ScadaWeb/OpenPlugins/PlgTable/js/api/eventtypes.js @@ -4,25 +4,27 @@ * Author : Mikhail Shiryaev * Created : 2016 * Modified : 2016 + * + * No dependencies */ // Rapid SCADA namespace var scada = scada || {}; -// JavaScript event types object -scada.eventTypes = { +// JavaScript event types enumeration +scada.EventTypes = { // Page layout should be updated - updateLayout: "scada:updateLayout", + UPDATE_LAYOUT: "scada:updateLayout", // View title has been changed. // Event parameter: title - viewTitleChanged: "scada:viewTitleChanged", + VIEW_TITLE_CHANGED: "scada:viewTitleChanged", // Before navigate to another view // Event parameter: view id - viewNavigate: "scada:viewNavigate", + VIEW_NAVIGATE: "scada:viewNavigate", // Current view date has been changed // Event parameter: date - viewDateChanged: "scada:viewDateChanged", + VIEW_DATE_CHANGED: "scada:viewDateChanged", }; diff --git a/ScadaWeb/OpenPlugins/PlgTable/js/api/viewhub.js b/ScadaWeb/OpenPlugins/PlgTable/js/api/viewhub.js index c9b8ebaf4..120de6d88 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/js/api/viewhub.js +++ b/ScadaWeb/OpenPlugins/PlgTable/js/api/viewhub.js @@ -58,7 +58,7 @@ scada.ViewHub.prototype.notify = function (senderWnd, eventType, opt_extraParams var senderIsView = senderWnd == this.viewWindow; // set main window title - if (eventType == scada.eventTypes.viewTitleChanged) { + if (eventType == scada.EventTypes.VIEW_TITLE_CHANGED) { if (senderIsView && this.mainWindow) { this.mainWindow.document.title = opt_extraParams; } @@ -66,7 +66,7 @@ scada.ViewHub.prototype.notify = function (senderWnd, eventType, opt_extraParams } // preprocess navigation - if (eventType == scada.eventTypes.viewNavigate) { + if (eventType == scada.EventTypes.VIEW_NAVIGATE) { if (senderIsView) { this.currentViewID = opt_extraParams; } else { diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js index 3ee02a781..07c12f9ab 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js @@ -1,12 +1,12 @@ $(document).ready(function () { - /*$(window).on(scada.eventTypes.viewNavigate, function (event) { + /*$(window).on(scada.EventTypes.VIEW_NAVIGATE, function (event) { console.log(event.type); });*/ $(window).on( - scada.eventTypes.viewTitleChanged + " " + - scada.eventTypes.viewNavigate + " " + - scada.eventTypes.viewDateChanged, function (event, extraParams) { + scada.EventTypes.VIEW_TITLE_CHANGED + " " + + scada.EventTypes.VIEW_NAVIGATE + " " + + scada.EventTypes.VIEW_DATE_CHANGED, function (event, extraParams) { var divLog = $("#divLog"); divLog.html(divLog.html() + event.type + " - " + extraParams + "
") }); @@ -15,15 +15,15 @@ if (viewHub) { $("#btn1").click(function () { - viewHub.notify(window, scada.eventTypes.viewTitleChanged, "new title " + (new Date())); + viewHub.notify(window, scada.EventTypes.VIEW_TITLE_CHANGED, "new title " + (new Date())); }); $("#btn2").click(function () { - viewHub.notify(window, scada.eventTypes.viewNavigate, 100); + viewHub.notify(window, scada.EventTypes.VIEW_NAVIGATE, 100); }); $("#btn3").click(function () { - viewHub.notify(window, scada.eventTypes.viewDateChanged, new Date()); + viewHub.notify(window, scada.EventTypes.VIEW_DATE_CHANGED, new Date()); }); } }); diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/eventtypes.js b/ScadaWeb/ScadaWebShell5Beta/js/api/eventtypes.js index 5b5272ec2..77882f744 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/eventtypes.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/eventtypes.js @@ -11,20 +11,20 @@ // Rapid SCADA namespace var scada = scada || {}; -// JavaScript event types object -scada.eventTypes = { +// JavaScript event types enumeration +scada.EventTypes = { // Page layout should be updated - updateLayout: "scada:updateLayout", + UPDATE_LAYOUT: "scada:updateLayout", // View title has been changed. // Event parameter: title - viewTitleChanged: "scada:viewTitleChanged", + VIEW_TITLE_CHANGED: "scada:viewTitleChanged", // Before navigate to another view // Event parameter: view id - viewNavigate: "scada:viewNavigate", + VIEW_NAVIGATE: "scada:viewNavigate", // Current view date has been changed // Event parameter: date - viewDateChanged: "scada:viewDateChanged", + VIEW_DATE_CHANGED: "scada:viewDateChanged", }; diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js b/ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js index c9b8ebaf4..120de6d88 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js @@ -58,7 +58,7 @@ scada.ViewHub.prototype.notify = function (senderWnd, eventType, opt_extraParams var senderIsView = senderWnd == this.viewWindow; // set main window title - if (eventType == scada.eventTypes.viewTitleChanged) { + if (eventType == scada.EventTypes.VIEW_TITLE_CHANGED) { if (senderIsView && this.mainWindow) { this.mainWindow.document.title = opt_extraParams; } @@ -66,7 +66,7 @@ scada.ViewHub.prototype.notify = function (senderWnd, eventType, opt_extraParams } // preprocess navigation - if (eventType == scada.eventTypes.viewNavigate) { + if (eventType == scada.EventTypes.VIEW_NAVIGATE) { if (senderIsView) { this.currentViewID = opt_extraParams; } else { diff --git a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js index bb0d1bfd9..522b12bea 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js @@ -67,7 +67,7 @@ scada.masterMain = { divMainLeftPane.outerHeight(paneH); divMainTabs.outerWidth(paneH); $("#divMainContent").outerHeight(paneH); - $(window).trigger(scada.eventTypes.updateLayout); + $(window).trigger(scada.EventTypes.UPDATE_LAYOUT); }, // Choose a tool window according to the current URL and activate it @@ -115,7 +115,7 @@ scada.masterMain = { this._hideLeftPane(); this.leftPaneExpanded = false; this._saveLeftPaneVisible(); - $(window).trigger(scada.eventTypes.updateLayout); + $(window).trigger(scada.EventTypes.UPDATE_LAYOUT); }, // Expand the left pane and hide the menu button @@ -124,7 +124,7 @@ scada.masterMain = { $("#spanMainShowMenuBtn").css("display", "none"); this.leftPaneExpanded = true; this._saveLeftPaneVisible(); - $(window).trigger(scada.eventTypes.updateLayout); + $(window).trigger(scada.EventTypes.UPDATE_LAYOUT); }, // Hide all the menus and switch browser to fullscreen mode diff --git a/ScadaWeb/ScadaWebShell5Beta/js/view.js b/ScadaWeb/ScadaWebShell5Beta/js/view.js index 19b065b84..1f172769d 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/view.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/view.js @@ -87,7 +87,7 @@ scada.view = { // Save active data window URL in the cookies _saveActiveDataWindow: function () { - scada.utils.setCookie("ActiveDataWindow", this._activeDataWindowUrl); + scada.utils.setCookie("ActiveDataWindow", this._dataWindow.url); }, // Hide bottom pane if no data windows exist @@ -192,7 +192,7 @@ $(document).ready(function () { .resize(function () { scada.view.updateLayout(); }) - .on(scada.eventTypes.updateLayout, function () { + .on(scada.EventTypes.UPDATE_LAYOUT, function () { scada.view.updateLayout(); }); From 84e6aa74a255d7ac9a9664ce8fa6d30ea0fcede0 Mon Sep 17 00:00:00 2001 From: 2mik Date: Mon, 23 May 2016 11:10:15 +0300 Subject: [PATCH 061/382] ScadaWeb5: iOS scrolling --- .../OpenPlugins/PlgTable/plugins/Table/Table.aspx | 5 +++++ ScadaWeb/ScadaWebShell5Beta/css/view.css | 2 -- ScadaWeb/ScadaWebShell5Beta/css/view.less | 4 ++-- ScadaWeb/ScadaWebShell5Beta/js/api/utils.js | 5 +++++ ScadaWeb/ScadaWebShell5Beta/js/view.js | 11 +++++++++++ 5 files changed, 23 insertions(+), 4 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx index 2bee24e68..e39d96bc7 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx @@ -18,6 +18,11 @@

+ + <% for (int i = 0; i < 100; i++) { %> + + <% } %> +
Test
diff --git a/ScadaWeb/ScadaWebShell5Beta/css/view.css b/ScadaWeb/ScadaWebShell5Beta/css/view.css index 56eb4977c..06d3dd243 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/view.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/view.css @@ -6,7 +6,6 @@ #divView { min-height: 50px; overflow: hidden; - /*for iOS*/ } #divViewContent iframe { width: 100%; @@ -33,7 +32,6 @@ display: none; /*block*/ overflow: hidden; - /*for iOS*/ } #divBottomTabs { background-color: white; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/view.less b/ScadaWeb/ScadaWebShell5Beta/css/view.less index 76d0db8d1..7e48c0db1 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/view.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/view.less @@ -12,7 +12,7 @@ #divView { min-height: 50px; - overflow: hidden; /*for iOS*/ + overflow: hidden; } #divViewContent iframe { @@ -38,7 +38,7 @@ background-color: white; border-bottom: 1px solid @panel-border-color; display: none; /*block*/ - overflow: hidden; /*for iOS*/ + overflow: hidden; } #divBottomTabs { diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/utils.js b/ScadaWeb/ScadaWebShell5Beta/js/api/utils.js index d23485855..3d86765dd 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/utils.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/utils.js @@ -182,5 +182,10 @@ scada.utils = { location = href; } } + }, + + // Detect if iOS is used + iOS: function () { + return /iPad|iPhone|iPod/.test(navigator.platform); } }; diff --git a/ScadaWeb/ScadaWebShell5Beta/js/view.js b/ScadaWeb/ScadaWebShell5Beta/js/view.js index 1f172769d..3b4864e7b 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/view.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/view.js @@ -90,6 +90,16 @@ scada.view = { scada.utils.setCookie("ActiveDataWindow", this._dataWindow.url); }, + // Apply additional css styles in case of using iOS + styleIOS: function () { + if (scada.utils.iOS()) { + $("#divView, #divDataWindow").css({ + "overflow": "scroll", + "-webkit-overflow-scrolling": "touch" + }); + } + }, + // Hide bottom pane if no data windows exist hideBottomTabsIfEmpty: function () { if ($("#divBottomTabsContainer .tab").length == 0) { @@ -182,6 +192,7 @@ scada.view = { $(document).ready(function () { // the order of the calls below is important scada.view.initialPageTitle = document.title; + scada.view.styleIOS(); scada.view.hideBottomTabsIfEmpty(); scada.view.loadView(initialViewID, initialViewUrl); // arguments are defined in View.aspx scada.view.loadVisualState(); From 71816a9149d936fdf77f24e67fbbc9fb5716a851 Mon Sep 17 00:00:00 2001 From: 2mik Date: Tue, 24 May 2016 00:30:53 +0300 Subject: [PATCH 062/382] PlgScheme: toolbar --- .../OpenPlugins/PlgScheme/PlgScheme.csproj | 2 + .../OpenPlugins/PlgScheme/css/globalvar.less | 3 +- .../lib/font-awesome/css/font-awesome.min.css | 4 + .../fonts/fontawesome-webfont.ttf | Bin 0 -> 142072 bytes .../PlgScheme/plugins/Scheme/Scheme.aspx | 18 ++-- .../plugins/Scheme/Scheme.aspx.designer.cs | 36 ++++++++ .../PlgScheme/plugins/Scheme/css/scheme.css | 65 +++++++++---- .../PlgScheme/plugins/Scheme/css/scheme.less | 87 +++++++++++++----- .../plugins/Scheme/css/scheme.min.css | 2 +- .../PlgScheme/plugins/Scheme/js/scheme.js | 40 ++++---- .../plugins/Scheme/js/schemerender.js | 10 +- ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj | 16 +++- .../OpenPlugins/PlgTable/compilerconfig.json | 6 ++ .../PlgTable/compilerconfig.json.defaults | 49 ++++++++++ .../OpenPlugins/PlgTable/css/globalvar.less | 5 + .../PlgTable/plugins/Table/Events.aspx | 2 + .../PlgTable/plugins/Table/Table.aspx | 2 + .../PlgTable/plugins/Table/css/table.css | 9 ++ .../PlgTable/plugins/Table/css/table.less | 10 ++ .../PlgTable/plugins/Table/css/table.min.css | 1 + ScadaWeb/ScadaWebCommon5Beta/UserData.cs | 1 + ScadaWeb/ScadaWebCommon5Beta/UserMonitor.cs | 10 ++ ScadaWeb/ScadaWebShell5Beta/MasterMain.Master | 4 +- .../js/controls/splitter.js | 2 +- ScadaWeb/ScadaWebShell5Beta/js/mastermain.js | 1 + .../lang/ScadaWeb.en-GB.xml | 4 +- 26 files changed, 307 insertions(+), 82 deletions(-) create mode 100644 ScadaWeb/OpenPlugins/PlgScheme/lib/font-awesome/css/font-awesome.min.css create mode 100644 ScadaWeb/OpenPlugins/PlgScheme/lib/font-awesome/fonts/fontawesome-webfont.ttf create mode 100644 ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json create mode 100644 ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json.defaults create mode 100644 ScadaWeb/OpenPlugins/PlgTable/css/globalvar.less create mode 100644 ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css create mode 100644 ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.less create mode 100644 ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css diff --git a/ScadaWeb/OpenPlugins/PlgScheme/PlgScheme.csproj b/ScadaWeb/OpenPlugins/PlgScheme/PlgScheme.csproj index 62d72454a..65f2b22f1 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/PlgScheme.csproj +++ b/ScadaWeb/OpenPlugins/PlgScheme/PlgScheme.csproj @@ -79,6 +79,7 @@ + scheme.less @@ -124,6 +125,7 @@ + Web.config diff --git a/ScadaWeb/OpenPlugins/PlgScheme/css/globalvar.less b/ScadaWeb/OpenPlugins/PlgScheme/css/globalvar.less index 3cb9a0df6..ce6eb9cda 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/css/globalvar.less +++ b/ScadaWeb/OpenPlugins/PlgScheme/css/globalvar.less @@ -1,4 +1,5 @@ @content-back-color: #f1f1f1; -@panel-border-color: #eee; +@content-fore-color: #444; +@panel-border-color: #e5e5e5; @default-font-family: 'Open Sans', sans-serif; /*Verdana, Geneva, Tahoma, sans-serif;*/ @default-font-size: 12px; \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgScheme/lib/font-awesome/css/font-awesome.min.css b/ScadaWeb/OpenPlugins/PlgScheme/lib/font-awesome/css/font-awesome.min.css new file mode 100644 index 000000000..d0603cb4b --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgScheme/lib/font-awesome/css/font-awesome.min.css @@ -0,0 +1,4 @@ +/*! + * Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.5.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.5.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.5.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.5.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.5.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.5.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"} diff --git a/ScadaWeb/OpenPlugins/PlgScheme/lib/font-awesome/fonts/fontawesome-webfont.ttf b/ScadaWeb/OpenPlugins/PlgScheme/lib/font-awesome/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..26dea7951a73079223b50653c455c5adf46a4648 GIT binary patch literal 142072 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 +|+&Vdls@0SAya!8#E9iVniRwHu0GY;H*n zR85WCMp8<;snu)zXP=G#Xp%p5&d~RHxMxCJ%JB}XSeUWMFU9vZy3ei-xcz(F8k=rp zdyPM(m0MZZ60|zi?q$sAj;xPPN%hK%PyX-8mZZEy{;|=m@WRkFXXA z5nF70;)1&WoP37EU9F}3icj&lSaW?;#r|w_SUit?N9L1_cPc}*K5%Pkt1n=2nYaoV z5-=GAhF=RUdZ;btZBMs=_tMe1fL6m~K|7*rAS?BN=yO0|fNo_f%Xms&H32%tGnW7tmw`>^wOMdk3PM6+%w}g8kf6c?98ir#!ZcT z6o%=3F`@>TLafTh+!$%g~lJN`>1|lZ=iJwyN^0%@(IsRoHUw zXOYP(ZdllU&ZNn)iuxBGyy(%3XGgV=Sf4qC*5@Qi3JMh0*%4vsObbtU5^D;iN4f
2+6Pgs9+! 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�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%0D0!#Bo^>xe$Z4SmykflG_VnkLvLv4@e#4_y4Q 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 Scheme - Rapid SCADA + @@ -25,16 +26,21 @@
-
-
+
100%
Load SchemeCreate DOMStart UpdatingAdd Notification +
+
diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.designer.cs b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.designer.cs index cfceb72e8..f7bf3e520 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.designer.cs +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.designer.cs @@ -20,5 +20,41 @@ public partial class WFrmScheme { /// To modify move field declaration from designer file to code-behind file. /// protected global::System.Web.UI.HtmlControls.HtmlForm frmScheme; + + /// + /// lblFitScreenBtn control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblFitScreenBtn; + + /// + /// lblFitWidthBtn control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblFitWidthBtn; + + /// + /// lblZoomInBtn control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblZoomInBtn; + + /// + /// lblZoomOutBtn control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblZoomOutBtn; } } diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.css b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.css index 4b0392d1e..d65faacd3 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.css +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.css @@ -13,30 +13,17 @@ body { background-position: center; background-repeat: no-repeat; } -/* Debug tools */ -#divDebugTools { - position: fixed; - top: 0; - left: 0; - width: 100%; - padding: 10px; - background-color: #f1f1f1; - display: none; - white-space: nowrap; -} -#divDebugTools input { - margin-right: 10px; - padding: 3px 15px; -} /* Notifications */ #divNotif { position: fixed; - top: 50px; + top: 0; left: 0; max-height: 100px; padding: 10px; - border-bottom: 1px solid #eee; + background-color: white; + border-bottom: 1px solid #e5e5e5; display: none; + /*block*/ font-family: 'Open Sans', sans-serif; overflow: auto; white-space: nowrap; @@ -52,4 +39,48 @@ body { min-width: 300px; min-height: 200px; overflow: auto; +} +/* Toolbar (above the scheme) */ +#divToolbar { + position: fixed; + height: 30px; + left: 0; + top: 0; + color: #9ca1a6; + display: inline-block; + font-size: 14px; + opacity: 0.5; + white-space: nowrap; +} +#divToolbar:hover { + opacity: 1; +} +#divToolbar .tool-btn, +#spanCurScale { + height: 30px; + margin: 0 5px 0 0; + padding: 0 10px; + cursor: pointer; + background-color: white; + display: inline-block; + line-height: 30px; + vertical-align: top; +} +#divToolbar .tool-btn:hover { + color: #00b9eb; +} +#spanCurScale { + cursor: default; + font-size: 12px; +} +#spanCurScale:hover { + opacity: 1; +} +/* Debug tools */ +#divDebugTools { + padding: 0 10px; + display: none; + /*inline-block*/ + font-size: 12px; + vertical-align: top; } \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.less b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.less index dbe32486f..822d550fe 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.less +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.less @@ -1,7 +1,12 @@ @import "../../../css/globalvar.less"; @scheme-back-color: white; +@notif-back-color: white; +@toolbar-fore-color: #9ca1a6; +@tool-btn-back-color: white; +@tool-btn-hover-fore-color: #00b9eb; @scheme-font-family: Verdana, Geneva, Tahoma, sans-serif; +@toolbar-heigth: 30px; body { @@ -20,47 +25,27 @@ body { background-repeat: no-repeat; } - -/* Debug tools */ - -#divDebugTools { - position: fixed; - top: 0; - left: 0; - width: 100%; - padding: 10px; - background-color: @content-back-color; - display: none; - white-space: nowrap; -} - -#divDebugTools input { - margin-right: 10px; - padding: 3px 15px; -} - /* Notifications */ #divNotif { position: fixed; - top: 50px; + top: 0; left: 0; max-height: 100px; padding: 10px; + background-color: @notif-back-color; border-bottom: 1px solid @panel-border-color; - display: none; + display: none; /*block*/ font-family: @default-font-family; overflow: auto; white-space: nowrap; } -#divNotif .message -{ +#divNotif .message { color: #0073aa; } -#divNotif .message.error -{ +#divNotif .message.error { color: red; } @@ -71,3 +56,55 @@ body { min-height: 200px; overflow: auto; } + +/* Toolbar (above the scheme) */ + +#divToolbar { + position: fixed; + height: @toolbar-heigth; + left: 0; + top: 0; + color: @toolbar-fore-color; + display: inline-block; + font-size: 14px; + opacity: 0.5; + white-space: nowrap; +} + +#divToolbar:hover { + opacity: 1; +} + +#divToolbar .tool-btn, +#spanCurScale { + height: @toolbar-heigth; + margin: 0 5px 0 0; + padding: 0 10px; + cursor: pointer; + background-color: @tool-btn-back-color; + display: inline-block; + line-height: @toolbar-heigth; + vertical-align: top; +} + +#divToolbar .tool-btn:hover { + color: @tool-btn-hover-fore-color; +} + +#spanCurScale { + cursor: default; + font-size: 12px; +} + +#spanCurScale:hover { + opacity: 1; +} + +/* Debug tools */ + +#divDebugTools { + padding: 0 10px; + display: none; /*inline-block*/ + font-size: 12px; + vertical-align: top; +} diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.min.css b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.min.css index a897de828..b4cde1703 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.min.css +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.min.css @@ -1 +1 @@ -body{margin:0;padding:0;background-color:#fff;font-family:Verdana,Geneva,Tahoma,sans-serif;font-size:12px;overflow:hidden;}.loading{background-image:url('../images/loading.gif');background-attachment:fixed;background-position:center;background-repeat:no-repeat;}#divDebugTools{position:fixed;top:0;left:0;width:100%;padding:10px;background-color:#f1f1f1;display:none;white-space:nowrap;}#divDebugTools input{margin-right:10px;padding:3px 15px;}#divNotif{position:fixed;top:50px;left:0;max-height:100px;padding:10px;border-bottom:1px solid #eee;display:none;font-family:'Open Sans',sans-serif;overflow:auto;white-space:nowrap;}#divNotif .message{color:#0073aa;}#divNotif .message.error{color:#f00;}#divSchParent{min-width:300px;min-height:200px;overflow:auto;} \ No newline at end of file +body{margin:0;padding:0;background-color:#fff;font-family:Verdana,Geneva,Tahoma,sans-serif;font-size:12px;overflow:hidden;}.loading{background-image:url('../images/loading.gif');background-attachment:fixed;background-position:center;background-repeat:no-repeat;}#divNotif{position:fixed;top:0;left:0;max-height:100px;padding:10px;background-color:#fff;border-bottom:1px solid #e5e5e5;display:none;font-family:'Open Sans',sans-serif;overflow:auto;white-space:nowrap;}#divNotif .message{color:#0073aa;}#divNotif .message.error{color:#f00;}#divSchParent{min-width:300px;min-height:200px;overflow:auto;}#divToolbar{position:fixed;height:30px;left:0;top:0;color:#9ca1a6;display:inline-block;font-size:14px;opacity:.5;white-space:nowrap;}#divToolbar:hover{opacity:1;}#divToolbar .tool-btn,#spanCurScale{height:30px;margin:0 5px 0 0;padding:0 10px;cursor:pointer;background-color:#fff;display:inline-block;line-height:30px;vertical-align:top;}#divToolbar .tool-btn:hover{color:#00b9eb;}#spanCurScale{cursor:default;font-size:12px;}#spanCurScale:hover{opacity:1;}#divDebugTools{padding:0 10px;display:none;font-size:12px;vertical-align:top;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js index 9d951bcf9..0c9e06701 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js @@ -60,7 +60,15 @@ function startUpdatingScheme() { // Add notification to the notification panel function addNotification(messageHtml, error, lifetime) { + // remove the same previous message var divNotif = $("#divNotif"); + var divPrevMessage = divNotif.children(".message:last"); + + if (divPrevMessage.html() == messageHtml) { + divPrevMessage.remove(); + } + + // add the new message var divMessage = $("
").html(messageHtml); if (error) { @@ -68,7 +76,7 @@ function addNotification(messageHtml, error, lifetime) { } if (lifetime) { - $("").val(Date.now() + lifetime).appendTo(divMessage); + divMessage.attr("data-expires", Date.now() + lifetime); } divNotif @@ -87,8 +95,8 @@ function clearOutdatedNotifications() { var nowMs = Date.now(); $.each(messages, function () { - var expireMs = $(this).find("input:hidden").val(); - if (expireMs < nowMs) { + var expires = $(this).attr("data-expires"); + if (expires < nowMs) { $(this).remove(); } }); @@ -119,43 +127,37 @@ function reloadScheme() { // Initialize debug tools function initDebugTools() { - $("#divDebugTools").css("display", "block"); + $("#divDebugTools").css("display", "inline-block"); - $("#btnLoadScheme").click(function (event) { - event.preventDefault(); + $("#spanLoadSchemeBtn").click(function () { startLoadingScheme(viewID); }); - $("#btnCreateDom").click(function (event) { - event.preventDefault(); + $("#spanCreateDomBtn").click(function () { scheme.createDom(); }); - $("#btnStartUpd").click(function (event) { - event.preventDefault(); + $("#spanStartUpdBtn").click(function () { startUpdatingScheme(); $(this).prop("disabled", true); }); - $("#btnAddNotif").click(function (event) { - event.preventDefault(); + $("#spanAddNotifBtn").click(function () { addNotification(scada.utils.getCurTime() + " Test notification", false, DEF_NOTIF_LIFETIME); }); } // Update layout of the top level div elements function updateLayout() { - var divDebugTools = $("#divDebugTools"); var divNotif = $("#divNotif"); - var divSchParent = $("#divSchParent"); - - var debugToolsHeight = divDebugTools.css("display") == "block" ? divDebugTools.outerHeight() : 0; var notifHeight = divNotif.css("display") == "block" ? divNotif.outerHeight() : 0; + var divSchParent = $("#divSchParent"); + var divToolbar = $("#divToolbar"); - $("body").css("padding-top", debugToolsHeight + notifHeight); - divNotif.css("top", debugToolsHeight); + $("body").css("padding-top", notifHeight); divNotif.outerWidth($(window).width()); - divSchParent.height($(window).height() - debugToolsHeight - notifHeight); + divSchParent.height($(window).height() - notifHeight); + divToolbar.css("top", notifHeight); } diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js index c2a14c4f8..12bf0b59d 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js @@ -463,14 +463,14 @@ scada.scheme.StaticPictureRenderer.prototype.createDom = function (elem, renderC divElem.css("background-size", props.Size.Width + "px " + props.Size.Height + "px"); break; case ImageStretches.ZOOM: - divElem.css({ - "background-size": "contain", - "background-position": "center" - }); + divElem.css("background-size", "contain"); break; } - divElem.css("background-repeat", "no-repeat"); + divElem.css({ + "background-repeat": "no-repeat", + "background-position": "center" + }); var image = renderContext.getImage(props.Image); this.setBackgroundImage(divElem, image); diff --git a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj index c0da56414..84579c5a2 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj +++ b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj @@ -60,6 +60,12 @@ + + table.less + + + table.css + @@ -92,6 +98,12 @@ + + + + + compilerconfig.json + Web.config @@ -100,9 +112,7 @@ Web.config - - - + 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) diff --git a/ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json b/ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json new file mode 100644 index 000000000..c83c12d39 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json @@ -0,0 +1,6 @@ +[ + { + "outputFile": "plugins/Table/css/table.css", + "inputFile": "plugins/Table/css/table.less" + } +] \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json.defaults b/ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json.defaults new file mode 100644 index 000000000..c75eb7d51 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json.defaults @@ -0,0 +1,49 @@ +{ + "compilers": { + "less": { + "autoPrefix": "", + "cssComb": "none", + "ieCompat": true, + "strictMath": false, + "strictUnits": false, + "relativeUrls": true, + "rootPath": "", + "sourceMapRoot": "", + "sourceMapBasePath": "", + "sourceMap": false + }, + "sass": { + "includePath": "", + "indentType": "space", + "indentWidth": 2, + "outputStyle": "nested", + "Precision": 5, + "relativeUrls": true, + "sourceMapRoot": "", + "sourceMap": false + }, + "stylus": { + "sourceMap": false + }, + "babel": { + "sourceMap": false + }, + "coffeescript": { + "bare": false, + "runtimeMode": "node", + "sourceMap": false + } + }, + "minifiers": { + "css": { + "enabled": true, + "termSemicolons": true, + "gzip": false + }, + "javascript": { + "enabled": true, + "termSemicolons": true, + "gzip": false + } + } +} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/css/globalvar.less b/ScadaWeb/OpenPlugins/PlgTable/css/globalvar.less new file mode 100644 index 000000000..ce6eb9cda --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/css/globalvar.less @@ -0,0 +1,5 @@ +@content-back-color: #f1f1f1; +@content-fore-color: #444; +@panel-border-color: #e5e5e5; +@default-font-family: 'Open Sans', sans-serif; /*Verdana, Geneva, Tahoma, sans-serif;*/ +@default-font-size: 12px; \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx index 53adc6299..598670744 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx @@ -5,6 +5,8 @@ Events - Rapid SCADA + + diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx index e39d96bc7..287b6793f 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx @@ -5,6 +5,8 @@ Table - Rapid SCADA + + diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css new file mode 100644 index 000000000..cbdca7f1b --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css @@ -0,0 +1,9 @@ +/*Verdana, Geneva, Tahoma, sans-serif;*/ +body { + margin: 0; + padding: 10px; + background-color: #f1f1f1; + font-family: 'Open Sans', sans-serif; + font-size: 12px; + overflow: hidden; +} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.less b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.less new file mode 100644 index 000000000..3a98b9495 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.less @@ -0,0 +1,10 @@ +@import "../../../css/globalvar.less"; + +body { + margin: 0; + padding: 10px; + background-color: @content-back-color; + font-family: @default-font-family; + font-size: @default-font-size; + overflow: hidden; +} diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css new file mode 100644 index 000000000..67ba23a19 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css @@ -0,0 +1 @@ +body{margin:0;padding:10px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebCommon5Beta/UserData.cs b/ScadaWeb/ScadaWebCommon5Beta/UserData.cs index cca417fa1..fdb7d64e4 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/UserData.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/UserData.cs @@ -236,6 +236,7 @@ public bool Login(string username, string password, out string errMsg) { username = username == null ? "" : username.Trim(); int roleID; + AppData.Refresh(); if (AppData.CheckUser(username, password, password != null, out roleID, out errMsg)) { diff --git a/ScadaWeb/ScadaWebCommon5Beta/UserMonitor.cs b/ScadaWeb/ScadaWebCommon5Beta/UserMonitor.cs index 340b78f20..c5ff27bcd 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/UserMonitor.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/UserMonitor.cs @@ -136,7 +136,13 @@ public void AddUser(UserData userData) if (userData != null && !string.IsNullOrEmpty(userData.SessionID)) { lock (userDataDict) + { userDataDict[userData.SessionID] = userData; + log.WriteAction(string.Format(Localization.UseRussian ? + "Добавлена информация о пользователе. IP-адрес: {0}. Сессия: {1}" : + "User information has been added. IP address: {0}. Session: {1}", + userData.IpAddress, userData.SessionID)); + } } } catch (Exception ex) @@ -164,6 +170,10 @@ public void RemoveUser(string sessionID) if (userData.LoggedOn) userData.Logout(); userDataDict.Remove(sessionID); + log.WriteAction(string.Format(Localization.UseRussian ? + "Удалена информация о пользователе. IP-адрес: {0}. Сессия: {1}" : + "User information has been removed. IP address: {0}. Session: {1}", + userData.IpAddress, sessionID)); } } } diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master index decf8f4c7..62040f66c 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master @@ -42,10 +42,10 @@ AdminLogout + ID="lblMainFullscreenBtn" runat="server" CssClass="hdr-btn img-btn" ToolTip="Full Screen"> - +
"); + + if (opt_callback) { + modalElem + .data("modal-callback", opt_callback) + .data("dialog-result", false); + } + $("body").append(modalElem); // load the frame var modalFrame = modalElem.find(".modal-frame"); + var hideModalOnEscapeFunc = function (event) { + if (event.which == 27 /*Escape*/) { + modalElem.modal("hide"); + } + } modalFrame + .on("load", function () { + // remove the modal on press Escape key in the frame + var frameWnd = modalFrame[0].contentWindow; + if (frameWnd.$) { + var jqFrameDoc = frameWnd.$(frameWnd.document); + jqFrameDoc.ready(function () { + jqFrameDoc + .off("keydown", hideModalOnEscapeFunc) + .on("keydown", hideModalOnEscapeFunc); + }); + } + }) .one("load", function () { // set the frame size var frameBody = modalFrame.contents().find("body"); @@ -294,29 +307,39 @@ scada.Popup.prototype.showModal = function (url, opt_buttons, opt_callback) { modalElem.find(".modal-title").text(modalFrame[0].contentWindow.document.title); }) .on('hidden.bs.modal', function () { + var callback = $(this).data("modal-callback"); + if (callback) { + callback($(this).data("dialog-result"), $(this).data("extra-params")); + } + $(this).remove(); }) .modal("show"); }) .attr("src", url); - - // TODO: callback }; // Show the modal dialog scada.Popup.prototype.closeModal = function (modalWnd, dialogResult, extraParams) { + this.setModalResult(modalWnd, dialogResult, extraParams).modal("hide"); +} + +// Set dialog result for the whole modal dialog +scada.Popup.prototype.setModalResult = function (modalWnd, dialogResult, extraParams) { var frame = $(modalWnd.frameElement); var modalElem = frame.closest(".modal"); - modalElem.modal("hide"); - // TODO: callback + modalElem + .data("dialog-result", dialogResult) + .data("extra-params", extraParams); + return modalElem; } -// Show or hide the modal button +// Show or hide the button of the modal dialog scada.Popup.prototype.setButtonVisible = function (modalWnd, btn, val) { this._findModalButton(modalWnd, btn).css("display", val ? "" : "none"); } -// Enable or disable the modal button +// Enable or disable the button of the modal dialog scada.Popup.prototype.setButtonEnabled = function (modalWnd, btn, val) { var btnElem = this._findModalButton(modalWnd, btn); if (val) { From 200669d5b861a66abb6f71e6fdbcbb2551784309 Mon Sep 17 00:00:00 2001 From: 2mik Date: Tue, 19 Jul 2016 16:06:36 +0300 Subject: [PATCH 190/382] PlgTable: tune commands --- .../PlgTable/plugins/Table/Command.aspx | 5 +++-- .../PlgTable/plugins/Table/Command.aspx.cs | 2 ++ .../plugins/Table/Command.aspx.designer.cs | 9 ++++++++ .../PlgTable/plugins/Table/js/command.js | 17 +++++++------- .../plugins/Table/lang/PlgTable.en-GB.xml | 22 +++++++++++++++++++ .../plugins/Table/lang/PlgTable.ru-RU.xml | 22 +++++++++++++++++++ ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js | 5 ++--- 7 files changed, 68 insertions(+), 14 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx index af05978d8..6115f5a15 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx @@ -21,6 +21,7 @@ + @@ -58,7 +59,7 @@ - + @@ -67,7 +68,7 @@
+ CssClass="btn btn-danger" UseSubmitBehavior="False" Text='<%#: Eval("Text") %>' data-cmdval='<%# Eval("Val") %>' />
diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs index 49e091b81..46d3d9cba 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs @@ -295,6 +295,8 @@ protected void Page_Load(object sender, EventArgs e) repCommands.DataSource = GetDiscreteCmds(ctrlCnlProps.CmdValArr); repCommands.DataBind(); pnlDiscreteValue.Visible = true; + hidHideExecuteBtn.Value = "true"; + btnSubmit.Enabled = false; } break; case BaseValues.CmdTypes.Binary: diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs index 5136436c5..f84916138 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs @@ -21,6 +21,15 @@ public partial class WFrmCommand { /// protected global::System.Web.UI.HtmlControls.HtmlForm frmCommand; + /// + /// hidHideExecuteBtn control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.HiddenField hidHideExecuteBtn; + /// /// hidDisableExecuteBtn control. /// diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/command.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/command.js index 75c9b86cf..96731a9d8 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/command.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/command.js @@ -19,21 +19,20 @@ function startCountdown() { } } + if (popup) { + popup.setModalResult(window, true); + } + spanCountdown.text(closeTimeout); setTimeout(countdownFunc, 1000); } $(document).ready(function () { - // hide execute button for discrete command - if ($("#pnlDiscreteValue").length > 0) { - if (popup) { + // hide or disable execute button if it is specified by the server code + if (popup) { + if ($("#hidHideExecuteBtn").val() == "true") { popup.setButtonVisible(window, scada.ModalButtons.EXEC, false); - } - } - - // disable execute button if it is specified by the server code - if ($("#hidDisableExecuteBtn").val() == "true") { - if (popup) { + } else if ($("#hidDisableExecuteBtn").val() == "true") { popup.setButtonEnabled(window, scada.ModalButtons.EXEC, false); } } diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml index 08ca407ce..f1ba2391f 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml @@ -7,6 +7,28 @@ Error loading table view from file Error saving table view to file + + Command - Rapid SCADA + Incorrect password. + Insufficient rights. + Incorrect command value. + Incorrect command data. + Out channel: + Not found + Object: + Device: + Password + Value + Command + Data + String + Hexadecimal + The command is enqueued for execution. + The window will be closed after + sec. + Unable to send the command. Server is unavailable. + The command is rejected by the server. + All Events Events by View diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml index 4787d1d7c..d4869aaa5 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml @@ -7,6 +7,28 @@ Ошибка при загрузке табличного представления из файла Ошибка при сохранении табличного представления в файле + + Команда - Rapid SCADA + Неверный пароль. + Insufficient rights. + Некорректное значение команды. + Некорректные данные команды. + Канал упр.: + Не найден + Объект: + КП: + Пароль + Значение + Команда + Данные + Строка + 16-ричные данные + Команда поставлена в очередь на выполнение. + Окно будет закрыто через + с. + Невозможно отправить команду. Сервер недоступен. + Команда отклонена сервером. + Все события По представлению diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js b/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js index a47d69ed8..6419440e7 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js @@ -33,9 +33,8 @@ scada.dialogs = { }, // Show command dialog. - // opt_callback is a function (dialogResult, cmdVal), - // dialogResult is true or false, - // cmdVal is a command value which can be a number, array of bytes or string + // opt_callback is a function (dialogResult), + // dialogResult is true or false showCmd: function (viewID, ctrlCnlNum, opt_callback) { if (scada.cmd && scada.cmd.show) { scada.cmd.show(this.rootPath, viewID, ctrlCnlNum, opt_callback); From 23ad68c1510b520059f94961627cfdd0ab36f4b6 Mon Sep 17 00:00:00 2001 From: 2mik Date: Tue, 19 Jul 2016 17:11:12 +0300 Subject: [PATCH 191/382] PlgTable: fixes --- .../plugins/Scheme/js/schemerender.js | 8 ++++--- .../PlgTable/plugins/Table/Command.aspx | 10 ++++----- .../PlgTable/plugins/Table/Command.aspx.cs | 9 ++++---- .../plugins/Table/Command.aspx.designer.cs | 21 +++++++++++++------ .../plugins/Table/lang/PlgTable.en-GB.xml | 2 +- .../plugins/Table/lang/PlgTable.ru-RU.xml | 2 +- 6 files changed, 32 insertions(+), 20 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js index f4f34b284..6e84c916f 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js @@ -303,7 +303,8 @@ scada.scheme.ElementRenderer.prototype.bindAction = function (jqObj, elem) { var props = elem.props; if (props.Action) { - var dialogs = scada.scheme.viewHub ? scada.scheme.viewHub.dialogs : null; + var viewHub = scada.scheme.viewHub; + var dialogs = viewHub ? viewHub.dialogs : null; jqObj .css("cursor", "pointer") @@ -312,7 +313,8 @@ scada.scheme.ElementRenderer.prototype.bindAction = function (jqObj, elem) { case Actions.DRAW_DIAGRAM: if (props.InCnlNum > 0) { if (dialogs) { - dialogs.showChart(scada.scheme.viewHub.curViewID, new Date(), props.InCnlNum); + var date = viewHub.curViewDateMs ? new Date(viewHub.curViewDateMs) : new Date(); + dialogs.showChart(viewHub.curViewID, date, props.InCnlNum); } else { console.warn("Dialogs object is undefined"); } @@ -321,7 +323,7 @@ scada.scheme.ElementRenderer.prototype.bindAction = function (jqObj, elem) { case Actions.SEND_COMMAND: if (props.CtrlCnlNum > 0) { if (dialogs) { - dialogs.showCmd(scada.scheme.viewHub.curViewID, props.CtrlCnlNum); + dialogs.showCmd(viewHub.curViewID, props.CtrlCnlNum); } else { console.warn("Dialogs object is undefined"); } diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx index 6115f5a15..14db6a8bb 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx @@ -26,19 +26,19 @@ - -
+ - + @@ -49,7 +49,7 @@
-
+ diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs index 46d3d9cba..f271ea952 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs @@ -96,6 +96,7 @@ private bool TryParseCmdData(out byte[] cmdData) private void HideErrMsg() { pnlErrMsg.Visible = false; + lblCtrlCnlNotFound.Visible = false; lblWrongPwd.Visible = false; lblNoRights.Visible = false; lblIncorrectCmdVal.Visible = false; @@ -264,15 +265,15 @@ protected void Page_Load(object sender, EventArgs e) if (ctrlCnlProps == null) { - // вывести сообщение, что канал не найден, - // элементы управления ввода команды скрыты по умолчанию - lblCtrlCnl.Visible = false; - lblCtrlCnlNotFound.Visible = true; + // вывести сообщение, что канал управления не найден + lblCtrlCnlNotFound.Text = string.Format(lblCtrlCnlNotFound.Text, ctrlCnlNum); + ShowErrMsg(lblCtrlCnlNotFound); hidDisableExecuteBtn.Value = "true"; } else { // вывод информации по каналу управления + pnlInfo.Visible = true; lblCtrlCnl.Text = string.Format("[{0}] {1}", ctrlCnlProps.CtrlCnlNum, ctrlCnlProps.CtrlCnlName); lblObj.Text = ctrlCnlProps.ObjNum > 0 ? string.Format("[{0}] {1}", ctrlCnlProps.ObjNum, ctrlCnlProps.ObjName) : ""; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs index f84916138..a07ec013a 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs @@ -66,6 +66,15 @@ public partial class WFrmCommand { /// protected global::System.Web.UI.WebControls.Panel pnlErrMsg; + /// + /// lblCtrlCnlNotFound control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblCtrlCnlNotFound; + /// /// lblWrongPwd control. /// @@ -112,31 +121,31 @@ public partial class WFrmCommand { protected global::System.Web.UI.WebControls.Button btnSubmit; /// - /// lblCtrlCnlCaption control. + /// pnlInfo control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.WebControls.Label lblCtrlCnlCaption; + protected global::System.Web.UI.WebControls.Panel pnlInfo; /// - /// lblCtrlCnl control. + /// lblCtrlCnlCaption control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.WebControls.Label lblCtrlCnl; + protected global::System.Web.UI.WebControls.Label lblCtrlCnlCaption; /// - /// lblCtrlCnlNotFound control. + /// lblCtrlCnl control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.WebControls.Label lblCtrlCnlNotFound; + protected global::System.Web.UI.WebControls.Label lblCtrlCnl; /// /// lblObjCaption control. diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml index f1ba2391f..4886f6e2b 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml @@ -9,12 +9,12 @@ Command - Rapid SCADA + Output channel {0} not found. Incorrect password. Insufficient rights. Incorrect command value. Incorrect command data. Out channel: - Not found Object: Device: Password diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml index d4869aaa5..9b329eee0 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml @@ -9,12 +9,12 @@ Команда - Rapid SCADA + Канал управления {0} не найден. Неверный пароль. Insufficient rights. Некорректное значение команды. Некорректные данные команды. Канал упр.: - Не найден Объект: КП: Пароль From a99274d5251b402f649432048bc534b817e1ad48 Mon Sep 17 00:00:00 2001 From: 2mik Date: Wed, 20 Jul 2016 11:05:25 +0300 Subject: [PATCH 192/382] PlgTable: event duplicates fix --- .../OpenPlugins/PlgTable/plugins/Table/js/events.js | 12 +++++++++--- ScadaWeb/ScadaWebCommon5Beta/WebSettings.cs | 2 +- ScadaWeb/ScadaWebShell5Beta/config/WebSettings.xml | 3 ++- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js index 3df8c0118..3e0149ef9 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js @@ -289,9 +289,15 @@ function updateEvents(full, callback) { // clear the event table clearEvents(tableElem); } - } else { + } else if (eventArrLen > 0) { + // skip already loaded events + var startIndex = 0; + while (startIndex < eventArrLen && eventArr[startIndex].Num <= lastEvNum) { + startIndex++; + } + // append new events to the event table - appendEvents(tableElem, eventArr, 0); + appendEvents(tableElem, eventArr, startIndex); } partialDataAge = dataAge; @@ -396,7 +402,7 @@ $(document).ready(function () { }); // register the activity time - $("#divTblWrapper").on("scroll mousemove", function () { + $("#divTblWrapper").on("mousemove wheel touchstart", function () { activityTime = new Date(); }); diff --git a/ScadaWeb/ScadaWebCommon5Beta/WebSettings.cs b/ScadaWeb/ScadaWebCommon5Beta/WebSettings.cs index 1f27b7b6c..4797c61a7 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/WebSettings.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/WebSettings.cs @@ -130,7 +130,7 @@ protected void SetToDefault() DispEventCnt = 100; ChartGap = 90; CmdEnabled = true; - CmdPassword = true; // TODO: перенести в базу конфигурации для каждого канала управления + CmdPassword = true; RemEnabled = false; StartPage = ""; ViewsFromBase = true; diff --git a/ScadaWeb/ScadaWebShell5Beta/config/WebSettings.xml b/ScadaWeb/ScadaWebShell5Beta/config/WebSettings.xml index a6b1fc23f..70d5fd93e 100644 --- a/ScadaWeb/ScadaWebShell5Beta/config/WebSettings.xml +++ b/ScadaWeb/ScadaWebShell5Beta/config/WebSettings.xml @@ -2,7 +2,8 @@ - + + From c6e772c471bace1853aed294ab7601d2ed5b058e Mon Sep 17 00:00:00 2001 From: 2mik Date: Wed, 20 Jul 2016 16:50:33 +0300 Subject: [PATCH 193/382] PlgTable: start event ack dialog --- .../ScadaData/Data/Tables/EventTableLight.cs | 16 ++ ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj | 17 ++ .../OpenPlugins/PlgTable/compilerconfig.json | 4 + .../OpenPlugins/PlgTable/css/globalvar.less | 3 +- .../OpenPlugins/PlgTable/js/api/eventtypes.js | 4 + .../PlgTable/plugins/Table/Command.aspx | 9 +- .../PlgTable/plugins/Table/Command.aspx.cs | 19 +- .../plugins/Table/Command.aspx.designer.cs | 22 +- .../PlgTable/plugins/Table/EventAck.aspx | 72 +++++ .../PlgTable/plugins/Table/EventAck.aspx.cs | 121 +++++++++ .../plugins/Table/EventAck.aspx.designer.cs | 249 ++++++++++++++++++ .../PlgTable/plugins/Table/css/command.css | 2 + .../PlgTable/plugins/Table/css/command.less | 5 +- .../plugins/Table/css/command.min.css | 2 +- .../PlgTable/plugins/Table/css/eventack.css | 29 ++ .../PlgTable/plugins/Table/css/eventack.less | 36 +++ .../plugins/Table/css/eventack.min.css | 1 + .../PlgTable/plugins/Table/css/events.css | 4 + .../PlgTable/plugins/Table/css/events.less | 5 + .../PlgTable/plugins/Table/css/events.min.css | 2 +- .../PlgTable/plugins/Table/js/cmdobj.js | 2 +- .../PlgTable/plugins/Table/js/command.js | 21 +- .../PlgTable/plugins/Table/js/eventack.js | 21 ++ .../PlgTable/plugins/Table/js/eventackobj.js | 28 ++ ScadaWeb/ScadaWebShell5Beta/css/calendar.css | 2 +- ScadaWeb/ScadaWebShell5Beta/css/calendar.less | 3 +- .../ScadaWebShell5Beta/css/calendar.min.css | 2 +- .../ScadaWebShell5Beta/css/globalvar.less | 3 +- ScadaWeb/ScadaWebShell5Beta/css/login.css | 1 + ScadaWeb/ScadaWebShell5Beta/css/login.less | 1 + ScadaWeb/ScadaWebShell5Beta/css/login.min.css | 2 +- .../ScadaWebShell5Beta/css/mastermain.css | 1 + .../ScadaWebShell5Beta/css/mastermain.less | 1 + .../ScadaWebShell5Beta/css/mastermain.min.css | 2 +- ScadaWeb/ScadaWebShell5Beta/css/noview.css | 2 +- .../ScadaWebShell5Beta/css/noview.min.css | 2 +- ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js | 1 - 37 files changed, 656 insertions(+), 61 deletions(-) create mode 100644 ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventAck.aspx create mode 100644 ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventAck.aspx.cs create mode 100644 ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventAck.aspx.designer.cs create mode 100644 ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/eventack.css create mode 100644 ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/eventack.less create mode 100644 ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/eventack.min.css create mode 100644 ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/eventack.js create mode 100644 ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/eventackobj.js diff --git a/ScadaData/ScadaData/Data/Tables/EventTableLight.cs b/ScadaData/ScadaData/Data/Tables/EventTableLight.cs index 969e9d375..7a2457fe0 100644 --- a/ScadaData/ScadaData/Data/Tables/EventTableLight.cs +++ b/ScadaData/ScadaData/Data/Tables/EventTableLight.cs @@ -654,5 +654,21 @@ public List GetFilteredEvents(EventFilter filter, int lastCount, int star return filteredEvents; } + + /// + /// Получить событие по номеру + /// + public Event GetEventByNum(int evNum) + { + if (1 <= evNum && evNum <= allEvents.Count) + { + Event ev = allEvents[evNum - 1]; + return ev.Number == evNum ? ev : null; + } + else + { + return null; + } + } } } diff --git a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj index 63ad65bae..c8fbdcd1d 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj +++ b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj @@ -91,6 +91,12 @@ command.css + + eventack.less + + + eventack.css + events.less @@ -109,6 +115,7 @@ tablecommon.css + @@ -119,6 +126,8 @@ + + @@ -142,6 +151,13 @@ Command.aspx + + EventAck.aspx + ASPXCodeBehind + + + EventAck.aspx + Events.aspx ASPXCodeBehind @@ -175,6 +191,7 @@ + Web.config diff --git a/ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json b/ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json index b4f605eb9..2cf1d282f 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json +++ b/ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json @@ -22,5 +22,9 @@ { "outputFile": "plugins/Table/css/command.css", "inputFile": "plugins/Table/css/command.less" + }, + { + "outputFile": "plugins/Table/css/eventack.css", + "inputFile": "plugins/Table/css/eventack.less" } ] \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/css/globalvar.less b/ScadaWeb/OpenPlugins/PlgTable/css/globalvar.less index d85a2272b..a52ca261d 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/css/globalvar.less +++ b/ScadaWeb/OpenPlugins/PlgTable/css/globalvar.less @@ -1,6 +1,7 @@ @content-back-color: #f1f1f1; -@content-fore-color: #444; +@content-fore-color: #333; @panel-border-color: #e5e5e5; +@popup-back-color: white; @default-font-family: 'Open Sans', sans-serif; @secondary-font-family: Arial, Helvetica, sans-serif; @form-font-size: 13px; diff --git a/ScadaWeb/OpenPlugins/PlgTable/js/api/eventtypes.js b/ScadaWeb/OpenPlugins/PlgTable/js/api/eventtypes.js index 77882f744..d7b9d1354 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/js/api/eventtypes.js +++ b/ScadaWeb/OpenPlugins/PlgTable/js/api/eventtypes.js @@ -27,4 +27,8 @@ scada.EventTypes = { // Current view date has been changed // Event parameter: date VIEW_DATE_CHANGED: "scada:viewDateChanged", + + // Modal dialog button is clicked + // Event parameter: dialog result + MODAL_BTN_CLICK: "scada:modalBtnClick" }; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx index 14db6a8bb..91d2ba07e 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx @@ -13,7 +13,6 @@ - @@ -21,8 +20,7 @@ - - + @@ -32,10 +30,9 @@ ID="lblIncorrectCmdVal" runat="server" Text="Incorrect command value."> - - - + +
diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs index f271ea952..f29c4c3c4 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs @@ -30,6 +30,7 @@ using System; using System.Collections.Generic; using System.Text; +using System.Web; using System.Web.UI.WebControls; namespace Scada.Web.Plugins.Table @@ -200,7 +201,7 @@ private void SendRequestCmd(int kpNum) private void ShowCmdResult(bool sendOK, bool result) { mvCommand.SetActiveView(viewCmdResult); - hidDisableExecuteBtn.Value = "true"; + btnSubmit.Enabled = false; if (sendOK && result) { @@ -268,17 +269,18 @@ protected void Page_Load(object sender, EventArgs e) // вывести сообщение, что канал управления не найден lblCtrlCnlNotFound.Text = string.Format(lblCtrlCnlNotFound.Text, ctrlCnlNum); ShowErrMsg(lblCtrlCnlNotFound); - hidDisableExecuteBtn.Value = "true"; + btnSubmit.Enabled = false; } else { // вывод информации по каналу управления pnlInfo.Visible = true; - lblCtrlCnl.Text = string.Format("[{0}] {1}", ctrlCnlProps.CtrlCnlNum, ctrlCnlProps.CtrlCnlName); - lblObj.Text = ctrlCnlProps.ObjNum > 0 ? - string.Format("[{0}] {1}", ctrlCnlProps.ObjNum, ctrlCnlProps.ObjName) : ""; - lblDev.Text = ctrlCnlProps.KPNum > 0 ? - string.Format("[{0}] {1}", ctrlCnlProps.KPNum, ctrlCnlProps.KPName) : ""; + lblCtrlCnl.Text = HttpUtility.HtmlEncode( + string.Format("[{0}] {1}", ctrlCnlProps.CtrlCnlNum, ctrlCnlProps.CtrlCnlName)); + lblObj.Text = HttpUtility.HtmlEncode(ctrlCnlProps.ObjNum > 0 ? + string.Format("[{0}] {1}", ctrlCnlProps.ObjNum, ctrlCnlProps.ObjName) : ""); + lblDev.Text = HttpUtility.HtmlEncode(ctrlCnlProps.KPNum > 0 ? + string.Format("[{0}] {1}", ctrlCnlProps.KPNum, ctrlCnlProps.KPName) : ""); // установка видимости поля для ввода пароля pnlPassword.Visible = userData.WebSettings.CmdPassword; @@ -296,8 +298,7 @@ protected void Page_Load(object sender, EventArgs e) repCommands.DataSource = GetDiscreteCmds(ctrlCnlProps.CmdValArr); repCommands.DataBind(); pnlDiscreteValue.Visible = true; - hidHideExecuteBtn.Value = "true"; - btnSubmit.Enabled = false; + btnSubmit.Visible = false; } break; case BaseValues.CmdTypes.Binary: diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs index a07ec013a..b12fa9dbe 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs @@ -22,22 +22,13 @@ public partial class WFrmCommand { protected global::System.Web.UI.HtmlControls.HtmlForm frmCommand; /// - /// hidHideExecuteBtn control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.HiddenField hidHideExecuteBtn; - - /// - /// hidDisableExecuteBtn control. + /// btnSubmit control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.WebControls.HiddenField hidDisableExecuteBtn; + protected global::System.Web.UI.WebControls.Button btnSubmit; /// /// mvCommand control. @@ -111,15 +102,6 @@ public partial class WFrmCommand { /// protected global::System.Web.UI.WebControls.Label lblIncorrectCmdData; - /// - /// btnSubmit control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button btnSubmit; - /// /// pnlInfo control. /// diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventAck.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventAck.aspx new file mode 100644 index 000000000..0802c8143 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventAck.aspx @@ -0,0 +1,72 @@ +<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="EventAck.aspx.cs" Inherits="Scada.Web.plugins.Table.WFrmEventAck" %> + + + + + + + + + + Event Acknowledgement - Rapid SCADA + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventAck.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventAck.aspx.cs new file mode 100644 index 000000000..d7559d2de --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventAck.aspx.cs @@ -0,0 +1,121 @@ +/* + * Copyright 2016 Mikhail Shiryaev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Product : Rapid SCADA + * Module : PlgTable + * Summary : Event acknowledgement dialog web form + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + */ + +using Scada.Client; +using Scada.Data.Tables; +using Scada.UI; +using System; +using System.Web.UI.WebControls; + +namespace Scada.Web.plugins.Table +{ + /// + /// Event acknowledgement dialog web form + /// Веб-форма диалога квитирования события + /// + public partial class WFrmEventAck : System.Web.UI.Page + { + private DateTime evDate; // дата события + private int evNum; // номер события + + + /// + /// Скрыть сообщение об ошибке + /// + private void HideErrMsg() + { + pnlErrMsg.Visible = false; + lblEventNotFound.Visible = false; + } + + /// + /// Вывести сообщение об ошибке + /// + private void ShowErrMsg(Label lblMessage) + { + pnlErrMsg.Visible = true; + lblMessage.Visible = true; + } + + + protected void Page_Load(object sender, EventArgs e) + { + AppData appData = AppData.GetAppData(); + UserData userData = UserData.GetUserData(); + + // проверка входа в систему + if (!userData.LoggedOn) + throw new ScadaException(CommonPhrases.NoRights); + + // скрытие сообщения об ошибке + HideErrMsg(); + + if (!IsPostBack) + { + // перевод веб-страницы + Translator.TranslatePage(Page, "Scada.Web.Plugins.Table.WFrmEventAck"); + + // получение параметров запроса + int viewID; + int.TryParse(Request.QueryString["viewID"], out viewID); + evDate = WebUtils.GetDateFromQueryString(Request); + int.TryParse(Request.QueryString["evNum"], out evNum); + + // получение события + EventTableLight tblEvent = appData.DataAccess.DataCache.GetEventTable(evDate); + EventTableLight.Event ev = tblEvent.GetEventByNum(evNum); + + if (ev == null) + { + ShowErrMsg(lblEventNotFound); + btnSubmit.Enabled = false; + } + else + { + // проверка прав + /*if (!userData.UserRights.GetViewRights(viewID).ControlRight || + !userData.WebSettings.CmdEnabled) + throw new ScadaException(CommonPhrases.NoRights); + + Type viewType = userData.UserViews.GetViewType(viewID); + BaseView view = appData.ViewCache.GetView(viewType, viewID, true); + + if (!view.ContainsCtrlCnl(ev.CnlNum)) + throw new ScadaException(CommonPhrases.NoRights);*/ + + // вывод информации по событию + pnlInfo.Visible = true; + lblNum.Text = ev.Number.ToString(); + lblTime.Text = ev.DateTime.ToLocalizedString(); + } + } + } + + protected void btnSubmit_Click(object sender, EventArgs e) + { + + } + } +} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventAck.aspx.designer.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventAck.aspx.designer.cs new file mode 100644 index 000000000..49735ea97 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventAck.aspx.designer.cs @@ -0,0 +1,249 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Scada.Web.plugins.Table { + + + public partial class WFrmEventAck { + + /// + /// frmEventAck control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlForm frmEventAck; + + /// + /// btnSubmit control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button btnSubmit; + + /// + /// pnlErrMsg control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Panel pnlErrMsg; + + /// + /// lblEventNotFound control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblEventNotFound; + + /// + /// pnlInfo control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Panel pnlInfo; + + /// + /// lblNumCaption control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblNumCaption; + + /// + /// lblNum control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblNum; + + /// + /// lblTimeCaption control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblTimeCaption; + + /// + /// lblTime control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblTime; + + /// + /// lblObjCaption control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblObjCaption; + + /// + /// lblObj control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblObj; + + /// + /// lblDevCaption control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblDevCaption; + + /// + /// lblDev control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblDev; + + /// + /// lblCnlCaption control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblCnlCaption; + + /// + /// lblCnl control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblCnl; + + /// + /// lblTextCaption control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblTextCaption; + + /// + /// lblText control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblText; + + /// + /// lblAckCaption control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblAckCaption; + + /// + /// lblAck control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblAck; + + /// + /// lblNotAck control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblNotAck; + + /// + /// rowUser control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlTableRow rowUser; + + /// + /// lblUserCaption control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblUserCaption; + + /// + /// lblUser control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblUser; + + /// + /// lblUserNotFound control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblUserNotFound; + + /// + /// pnlTip control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Panel pnlTip; + + /// + /// lblTip control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblTip; + } +} diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.css index 4397e88e5..772e23c1e 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.css @@ -3,6 +3,7 @@ body { margin: 0; padding: 0; background-color: white; + color: #333; font-family: 'Open Sans', sans-serif; font-size: 13px; min-width: 300px; @@ -31,6 +32,7 @@ textarea { font-weight: normal; padding: 2px 0; vertical-align: top; + text-align: right; } #tblInfo td { color: #d9534f; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.less b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.less index d421f8dc7..2fe7f6e5d 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.less +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.less @@ -1,13 +1,13 @@ @import "../../../css/globalvar.less"; -@form-back-color: white; @info-fore-color: #d9534f; /* Bootstrap danger color */ @cmd-fore-color: @info-fore-color; body { margin: 0; padding: 0; - background-color: @form-back-color; + background-color: @popup-back-color; + color: @content-fore-color; font-family: @default-font-family; font-size: @form-font-size; min-width: @popup-min-width; @@ -43,6 +43,7 @@ textarea { font-weight: normal; padding: 2px 0; vertical-align: top; + text-align: right; } #tblInfo td { diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.min.css index b84da6303..7492568fc 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.min.css @@ -1 +1 @@ -body{margin:0;padding:0;background-color:#fff;font-family:'Open Sans',sans-serif;font-size:13px;min-width:300px;overflow:hidden;}table{border-collapse:collapse;}label{font-weight:600;}input[type=text],input[type=password]{width:calc(100% - 5px);}textarea{resize:none;}.form-control-feedback{color:transparent;}#btnSubmit{display:none;}#tblInfo th{font-weight:normal;padding:2px 0;vertical-align:top;}#tblInfo td{color:#d9534f;padding:2px 10px;vertical-align:top;}#txtCmdVal{color:#d9534f;}#divCommands{display:flex;flex-wrap:wrap;}#divCommands input{flex-grow:1;margin:0 5px 5px 0;}#divCmdDataFormat{position:relative;top:-5px;} \ No newline at end of file +body{margin:0;padding:0;background-color:#fff;color:#333;font-family:'Open Sans',sans-serif;font-size:13px;min-width:300px;overflow:hidden;}table{border-collapse:collapse;}label{font-weight:600;}input[type=text],input[type=password]{width:calc(100% - 5px);}textarea{resize:none;}.form-control-feedback{color:transparent;}#btnSubmit{display:none;}#tblInfo th{font-weight:normal;padding:2px 0;vertical-align:top;text-align:right;}#tblInfo td{color:#d9534f;padding:2px 10px;vertical-align:top;}#txtCmdVal{color:#d9534f;}#divCommands{display:flex;flex-wrap:wrap;}#divCommands input{flex-grow:1;margin:0 5px 5px 0;}#divCmdDataFormat{position:relative;top:-5px;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/eventack.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/eventack.css new file mode 100644 index 000000000..7e33ccc2b --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/eventack.css @@ -0,0 +1,29 @@ +body { + margin: 0; + padding: 0; + background-color: white; + color: #333; + font-family: 'Open Sans', sans-serif; + font-size: 13px; + min-width: 300px; + overflow: hidden; +} +table { + border-collapse: collapse; +} +#btnSubmit { + display: none; +} +#tblInfo th { + font-weight: normal; + padding: 2px 0; + vertical-align: top; + text-align: right; +} +#tblInfo td { + padding: 2px 10px; + vertical-align: top; +} +#tblInfo .label { + font-size: 100%; +} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/eventack.less b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/eventack.less new file mode 100644 index 000000000..f9fbdc4f0 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/eventack.less @@ -0,0 +1,36 @@ +@import "../../../css/globalvar.less"; + +body { + margin: 0; + padding: 0; + background-color: @popup-back-color; + color: @content-fore-color; + font-family: @default-font-family; + font-size: @form-font-size; + min-width: @popup-min-width; + overflow: hidden; +} + +table { + border-collapse: collapse; +} + +#btnSubmit { + display: none; +} + +#tblInfo th { + font-weight: normal; + padding: 2px 0; + vertical-align: top; + text-align: right; +} + +#tblInfo td { + padding: 2px 10px; + vertical-align: top; +} + +#tblInfo .label { + font-size: 100%; +} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/eventack.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/eventack.min.css new file mode 100644 index 000000000..94e562bf1 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/eventack.min.css @@ -0,0 +1 @@ +body{margin:0;padding:0;background-color:#fff;color:#333;font-family:'Open Sans',sans-serif;font-size:13px;min-width:300px;overflow:hidden;}table{border-collapse:collapse;}#btnSubmit{display:none;}#tblInfo th{font-weight:normal;padding:2px 0;vertical-align:top;text-align:right;}#tblInfo td{padding:2px 10px;vertical-align:top;}#tblInfo .label{font-size:100%;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css index d3cb6caa3..8827fc78c 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css @@ -167,6 +167,10 @@ #tblEvents td.text { width: 35%; } +#tblEvents td.ack a { + display: inline-block; + padding: 0 5px; +} #divLoading, #divNoEvents { padding: 5px 10px; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.less b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.less index e6863e48b..1ce9fb83b 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.less +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.less @@ -48,6 +48,11 @@ width: 35%; } +#tblEvents td.ack a { + display: inline-block; + padding: 0 5px; +} + #divLoading, #divNoEvents { padding: 5px 10px; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css index 3dad79d9e..31d019721 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:13px;overflow:hidden;}.hidden{display:none;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a{color:#0073aa;text-decoration:none;}#divTblWrapper td a:hover{color:#00a0d2;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divToolbar .tool-btn.selected{background-color:#0073aa;color:#fff;}#divToolbar .tool-btn.disabled{color:#ccc;cursor:default;}#tblEvents{width:100%;}#divTblWrapper tr.event:hover{background-color:#e0e0e0;color:#000;}#tblEvents td.num,#tblEvents td.time,#tblEvents td.ack{text-align:center;}#tblEvents td.num,#tblEvents td.ack{width:5%;}#tblEvents td.time{width:10%;}#tblEvents td.obj,#tblEvents td.dev,#tblEvents td.cnl{width:15%;}#tblEvents td.text{width:35%;}#divLoading,#divNoEvents{padding:5px 10px;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:13px;overflow:hidden;}.hidden{display:none;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a{color:#0073aa;text-decoration:none;}#divTblWrapper td a:hover{color:#00a0d2;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divToolbar .tool-btn.selected{background-color:#0073aa;color:#fff;}#divToolbar .tool-btn.disabled{color:#ccc;cursor:default;}#tblEvents{width:100%;}#divTblWrapper tr.event:hover{background-color:#e0e0e0;color:#000;}#tblEvents td.num,#tblEvents td.time,#tblEvents td.ack{text-align:center;}#tblEvents td.num,#tblEvents td.ack{width:5%;}#tblEvents td.time{width:10%;}#tblEvents td.obj,#tblEvents td.dev,#tblEvents td.cnl{width:15%;}#tblEvents td.text{width:35%;}#tblEvents td.ack a{display:inline-block;padding:0 5px;}#divLoading,#divNoEvents{padding:5px 10px;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/cmdobj.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/cmdobj.js index c3dee51c5..bbe4a22a1 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/cmdobj.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/cmdobj.js @@ -1,5 +1,5 @@ /* - * Command object that implements sending commands + * Command object that implements showing of the appropriate dialog * * Author : Mikhail Shiryaev * Created : 2016 diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/command.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/command.js index 96731a9d8..5e82394ad 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/command.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/command.js @@ -28,15 +28,23 @@ function startCountdown() { } $(document).ready(function () { - // hide or disable execute button if it is specified by the server code + // hide or disable execute button according to the submit button state if (popup) { - if ($("#hidHideExecuteBtn").val() == "true") { + var btnSubmit = $("#btnSubmit"); + if (btnSubmit.length == 0) { popup.setButtonVisible(window, scada.ModalButtons.EXEC, false); - } else if ($("#hidDisableExecuteBtn").val() == "true") { + } else if (btnSubmit.is(":disabled")) { popup.setButtonEnabled(window, scada.ModalButtons.EXEC, false); } } + // submit the form on execute button click + $(window).on(scada.EventTypes.MODAL_BTN_CLICK, function (event, result) { + if (result == scada.ModalButtons.EXEC) { + $("#btnSubmit").click(); + } + }); + // highlight password error if ($("#lblWrongPwd").length > 0) { $("#pnlPassword").addClass("has-error"); @@ -51,11 +59,4 @@ $(document).ready(function () { if ($("#lblIncorrectCmdData").length > 0) { $("#pnlData").addClass("has-error"); } - - // submit the form on execute button click - $(window).on(scada.EventTypes.MODAL_BTN_CLICK, function (event, result) { - if (result == scada.ModalButtons.EXEC) { - $("#btnSubmit").click(); - } - }); }); \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/eventack.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/eventack.js new file mode 100644 index 000000000..8f9697f3d --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/eventack.js @@ -0,0 +1,21 @@ +// Popup dialogs manipulation object +var popup = scada.popupLocator.getPopup(); + +$(document).ready(function () { + // hide or disable OK button according to the submit button state + if (popup) { + var btnSubmit = $("#btnSubmit"); + if (btnSubmit.length == 0) { + popup.setButtonVisible(window, scada.ModalButtons.OK, false); + } else if (btnSubmit.is(":disabled")) { + popup.setButtonEnabled(window, scada.ModalButtons.OK, false); + } + } + + // submit the form on OK button click + $(window).on(scada.EventTypes.MODAL_BTN_CLICK, function (event, result) { + if (result == scada.ModalButtons.OK) { + $("#btnSubmit").click(); + } + }); +}); \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/eventackobj.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/eventackobj.js new file mode 100644 index 000000000..cb69cdcc3 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/eventackobj.js @@ -0,0 +1,28 @@ +/* + * Event acknowledgement object that implements showing of the appropriate dialog + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + * + * Requires: + * - jquery + * - bootstrap + * - utils.js + * - popup.js + */ + +// Rapid SCADA namespace +var scada = scada || {}; + +scada.eventAck = { + // Show event acknowledgement dialog + show: function (rootPath, viewID, date, evNum, opt_callback) { + var popup = scada.popupLocator.getPopup(); + if (popup) { + popup.showModal(rootPath + "plugins/Table/EventAck.aspx?viewID=" + + viewID + "&" + scada.utils.dateToQueryString(date) + "&evNum=" + evNum, + [scada.ModalButtons.OK, scada.ModalButtons.CANCEL], opt_callback); + } + } +} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/calendar.css b/ScadaWeb/ScadaWebShell5Beta/css/calendar.css index d52fd79f6..2263d12f0 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/calendar.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/calendar.css @@ -2,7 +2,7 @@ margin: 0; padding: 0; background-color: white; - color: #444; + color: #333; font-family: 'Open Sans', sans-serif; font-size: 12px; overflow: hidden; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/calendar.less b/ScadaWeb/ScadaWebShell5Beta/css/calendar.less index 09e11ae1b..c84bedfa2 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/calendar.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/calendar.less @@ -1,6 +1,5 @@ @import "globalvar.less"; -@calendar-back-color: white; @link-hover-fore-color: @menu-item-hover-fore-color; @header-back-color: #ccc; @day-header-back-color: #f9f9f9; @@ -13,7 +12,7 @@ body { margin: 0; padding: 0; - background-color: @calendar-back-color; + background-color: @popup-back-color; color: @content-fore-color; font-family: @default-font-family; font-size: 12px; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/calendar.min.css b/ScadaWeb/ScadaWebShell5Beta/css/calendar.min.css index e1485f440..8162ddb48 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/calendar.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/calendar.min.css @@ -1 +1 @@ -body{margin:0;padding:0;background-color:#fff;color:#444;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;display:inline-block;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#frmCalendar .header{background-color:#ccc;font-weight:600;}#frmCalendar .header a{font-weight:600;width:30px;height:25px;line-height:25px;text-align:center;}#frmCalendar th.day-header{background-color:#f9f9f9;font-size:11px;font-weight:600;height:25px;}#frmCalendar td{margin:0;padding:0;vertical-align:middle;}#frmCalendar a{display:inline-block;}#frmCalendar a:hover{color:#00b9eb;}#frmCalendar td.day a{width:30px;height:30px;line-height:30px;text-align:center;}#frmCalendar td.day.other-month a{color:#9ca1a6;}#frmCalendar td.day.today a{background-color:#ccc;}#frmCalendar td.day.selected a{background-color:#0073aa;color:#fff;}#frmCalendar td.day a:hover{color:#00b9eb;}#frmCalendar td.day.regular a:hover,#frmCalendar td.day.other-month a:hover{background-color:#e0e0e0;} \ No newline at end of file +body{margin:0;padding:0;background-color:#fff;color:#333;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;display:inline-block;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#frmCalendar .header{background-color:#ccc;font-weight:600;}#frmCalendar .header a{font-weight:600;width:30px;height:25px;line-height:25px;text-align:center;}#frmCalendar th.day-header{background-color:#f9f9f9;font-size:11px;font-weight:600;height:25px;}#frmCalendar td{margin:0;padding:0;vertical-align:middle;}#frmCalendar a{display:inline-block;}#frmCalendar a:hover{color:#00b9eb;}#frmCalendar td.day a{width:30px;height:30px;line-height:30px;text-align:center;}#frmCalendar td.day.other-month a{color:#9ca1a6;}#frmCalendar td.day.today a{background-color:#ccc;}#frmCalendar td.day.selected a{background-color:#0073aa;color:#fff;}#frmCalendar td.day a:hover{color:#00b9eb;}#frmCalendar td.day.regular a:hover,#frmCalendar td.day.other-month a:hover{background-color:#e0e0e0;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less b/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less index d85a2272b..a52ca261d 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less @@ -1,6 +1,7 @@ @content-back-color: #f1f1f1; -@content-fore-color: #444; +@content-fore-color: #333; @panel-border-color: #e5e5e5; +@popup-back-color: white; @default-font-family: 'Open Sans', sans-serif; @secondary-font-family: Arial, Helvetica, sans-serif; @form-font-size: 13px; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/login.css b/ScadaWeb/ScadaWebShell5Beta/css/login.css index 57ca5becb..28b13edba 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/login.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/login.css @@ -2,6 +2,7 @@ margin: 0; padding: 0; background-color: #f1f1f1; + color: #333; font-family: 'Open Sans', sans-serif; font-size: 13px; } diff --git a/ScadaWeb/ScadaWebShell5Beta/css/login.less b/ScadaWeb/ScadaWebShell5Beta/css/login.less index 425026483..add78613c 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/login.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/login.less @@ -9,6 +9,7 @@ body { margin: 0; padding: 0; background-color: @content-back-color; + color: @content-fore-color; font-family: @default-font-family; font-size: @form-font-size; } diff --git a/ScadaWeb/ScadaWebShell5Beta/css/login.min.css b/ScadaWeb/ScadaWebShell5Beta/css/login.min.css index 7ef97afe2..ffd5e6306 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/login.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/login.min.css @@ -1 +1 @@ -body{margin:0;padding:0;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:13px;}#divLoginContainer{margin:40px 0 0 0;text-align:center;}#divTitle{margin:20px 0;font-size:30px;}#divAlertsOuter{text-align:center;}#divAlertsInner{display:inline-block;}div.alert{margin:0 0 10px 0;padding-left:25px;text-align:left;}#divLogin{padding:25px 25px 35px;background-color:#fff;border:1px solid #ddd;border-radius:1px;color:#777;display:inline-block;text-align:left;}#divLogin span,#divLogin label{font-size:14px;font-weight:normal;}#divLogin input[type=text],#divLogin input[type=password]{width:265px;margin:0 0 20px 0;color:#000;}#divLogin input[type=submit]{padding-left:20px;padding-right:20px;font-size:14px;} \ No newline at end of file +body{margin:0;padding:0;background-color:#f1f1f1;color:#333;font-family:'Open Sans',sans-serif;font-size:13px;}#divLoginContainer{margin:40px 0 0 0;text-align:center;}#divTitle{margin:20px 0;font-size:30px;}#divAlertsOuter{text-align:center;}#divAlertsInner{display:inline-block;}div.alert{margin:0 0 10px 0;padding-left:25px;text-align:left;}#divLogin{padding:25px 25px 35px;background-color:#fff;border:1px solid #ddd;border-radius:1px;color:#777;display:inline-block;text-align:left;}#divLogin span,#divLogin label{font-size:14px;font-weight:normal;}#divLogin input[type=text],#divLogin input[type=password]{width:265px;margin:0 0 20px 0;color:#000;}#divLogin input[type=submit]{padding-left:20px;padding-right:20px;font-size:14px;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css index dc7285b4f..894161f56 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css @@ -2,6 +2,7 @@ margin: 0; padding: 30px 0 0 250px; background-color: #f1f1f1; + color: #333; font-family: 'Open Sans', sans-serif; font-size: 13px; } diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less index bff9cb84e..1fc6dedb2 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less @@ -9,6 +9,7 @@ body { margin: 0; padding: @header-heigth 0 0 @left-pane-width; background-color: @content-back-color; + color: @content-fore-color; font-family: @default-font-family; font-size: @form-font-size; } diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css index 595529384..027cb4886 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 250px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:13px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;}#divMainHeader .hdr-btn{height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;display:inline-block;font-size:13px;text-decoration:none;white-space:nowrap;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenuBtn{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;white-space:nowrap;}#divMainUserMenu{float:right;height:30px;display:inline-block;}#lblMainNormalViewBtn{position:fixed;top:0;right:0;height:30px;padding:0 10px;background-color:#fff;display:none;line-height:30px;opacity:.5;cursor:pointer;color:#9ca1a6;font-size:13px;}#lblMainNormalViewBtn:hover{color:#00b9eb;opacity:1;}#divMainLeftPane{position:fixed;left:0;top:30px;width:250px;min-height:250px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 30px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainLeftPane .tree-view .node:hover{background-color:#191e23;color:#00b9eb;}#divMainLeftPane .tree-view .node.selected{background-color:#0073aa;color:#fff;}#divMainLeftPane .tree-view .node.disabled{color:#9ca1a6;}#divMainLeftPane .tree-view .expander::before{font-size:10px;}#divMainMenu .node[data-level="1"],#divMainMenu .node[data-level="2"],#divMainMenu .node[data-level="3"],#divMainMenu .node[data-level="4"],#divMainMenu .node[data-level="5"]{color:#9ca1a6;font-size:13px;}#divMainMenu .tree-view .node{padding:5px 10px;}#divMainMenu .tree-view .expander.left{display:none;width:0;}#divMainMenu .tree-view .expander.right{display:table-cell;width:20px;}#divMainExplorer{font-size:13px;line-height:13px;}#divMainExplorer .tree-view .node{padding:3px 5px 3px 10px;}#divMainExplorer .tree-view .expander{padding:4px 0 0 0;}#divMainExplorer .tree-view .icon{width:21px;min-width:21px;}#divMainExplorer .tree-view img{position:relative;top:-2px;}#divMainCollapseLeftPaneBtn{position:absolute;bottom:0;width:220px;height:30px;margin:0 0 0 30px;padding:0 10px;color:#9ca1a6;cursor:pointer;font-size:13px;line-height:30px;}#divMainCollapseLeftPaneBtn i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapseLeftPaneBtn:hover{color:#00b9eb;}#divMainContent{overflow:auto;} \ No newline at end of file +body{margin:0;padding:30px 0 0 250px;background-color:#f1f1f1;color:#333;font-family:'Open Sans',sans-serif;font-size:13px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;}#divMainHeader .hdr-btn{height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;display:inline-block;font-size:13px;text-decoration:none;white-space:nowrap;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenuBtn{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;white-space:nowrap;}#divMainUserMenu{float:right;height:30px;display:inline-block;}#lblMainNormalViewBtn{position:fixed;top:0;right:0;height:30px;padding:0 10px;background-color:#fff;display:none;line-height:30px;opacity:.5;cursor:pointer;color:#9ca1a6;font-size:13px;}#lblMainNormalViewBtn:hover{color:#00b9eb;opacity:1;}#divMainLeftPane{position:fixed;left:0;top:30px;width:250px;min-height:250px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 30px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainLeftPane .tree-view .node:hover{background-color:#191e23;color:#00b9eb;}#divMainLeftPane .tree-view .node.selected{background-color:#0073aa;color:#fff;}#divMainLeftPane .tree-view .node.disabled{color:#9ca1a6;}#divMainLeftPane .tree-view .expander::before{font-size:10px;}#divMainMenu .node[data-level="1"],#divMainMenu .node[data-level="2"],#divMainMenu .node[data-level="3"],#divMainMenu .node[data-level="4"],#divMainMenu .node[data-level="5"]{color:#9ca1a6;font-size:13px;}#divMainMenu .tree-view .node{padding:5px 10px;}#divMainMenu .tree-view .expander.left{display:none;width:0;}#divMainMenu .tree-view .expander.right{display:table-cell;width:20px;}#divMainExplorer{font-size:13px;line-height:13px;}#divMainExplorer .tree-view .node{padding:3px 5px 3px 10px;}#divMainExplorer .tree-view .expander{padding:4px 0 0 0;}#divMainExplorer .tree-view .icon{width:21px;min-width:21px;}#divMainExplorer .tree-view img{position:relative;top:-2px;}#divMainCollapseLeftPaneBtn{position:absolute;bottom:0;width:220px;height:30px;margin:0 0 0 30px;padding:0 10px;color:#9ca1a6;cursor:pointer;font-size:13px;line-height:30px;}#divMainCollapseLeftPaneBtn i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapseLeftPaneBtn:hover{color:#00b9eb;}#divMainContent{overflow:auto;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/noview.css b/ScadaWeb/ScadaWebShell5Beta/css/noview.css index 7c231ce11..c58069421 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/noview.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/noview.css @@ -2,7 +2,7 @@ margin: 0; padding: 0; background-color: #f1f1f1; - color: #444; + color: #333; font-family: 'Open Sans', sans-serif; font-size: 17px; } diff --git a/ScadaWeb/ScadaWebShell5Beta/css/noview.min.css b/ScadaWeb/ScadaWebShell5Beta/css/noview.min.css index be06413eb..b4b7e77be 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/noview.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/noview.min.css @@ -1 +1 @@ -body{margin:0;padding:0;background-color:#f1f1f1;color:#444;font-family:'Open Sans',sans-serif;font-size:17px;}#divViewNotExist{position:absolute;padding:10px 20px;width:100%;top:50%;transform:translateY(-50%);text-align:center;box-sizing:border-box;} \ No newline at end of file +body{margin:0;padding:0;background-color:#f1f1f1;color:#333;font-family:'Open Sans',sans-serif;font-size:17px;}#divViewNotExist{position:absolute;padding:10px 20px;width:100%;top:50%;transform:translateY(-50%);text-align:center;box-sizing:border-box;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js b/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js index 6419440e7..43c3fd4eb 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js @@ -53,7 +53,6 @@ scada.dialogs = { scada.eventAck.show(this.rootPath, viewID, date, evNum, opt_callback); } else { console.warn("Unable to show event acknowledgement dialog because scada.eventAck is undefined"); - alert("Acknowledgement is not implemented yet, evNum=" + evNum); } }, From f8715b0d71a507af79048f969415f23f56c16de9 Mon Sep 17 00:00:00 2001 From: 2mik Date: Thu, 21 Jul 2016 16:13:38 +0300 Subject: [PATCH 194/382] PlgTable: event ack ready --- ScadaData/ScadaData/Client/DataAccess.cs | 76 ++++++++++- ScadaData/ScadaData/Client/DataFormatter.cs | 28 ++++ ScadaData/ScadaData/CommonPhrases.cs | 8 +- .../ScadaData/Data/Models/DispEventProps.cs | 97 ++++++++++++++ ScadaData/ScadaData/Lang/ScadaData.en-GB.xml | 2 + ScadaData/ScadaData/Lang/ScadaData.ru-RU.xml | 72 ++++++++++ ScadaData/ScadaData/ScadaData.csproj | 4 + .../PlgTable/plugins/Table/Command.aspx.cs | 7 +- .../PlgTable/plugins/Table/EventAck.aspx | 17 ++- .../PlgTable/plugins/Table/EventAck.aspx.cs | 85 ++++++++++-- .../plugins/Table/EventAck.aspx.designer.cs | 58 +++----- .../PlgTable/plugins/Table/Events.aspx | 4 +- .../PlgTable/plugins/Table/Events.aspx.cs | 8 +- .../PlgTable/plugins/Table/Table.aspx | 2 +- .../PlgTable/plugins/Table/Table.aspx.cs | 4 +- .../PlgTable/plugins/Table/css/eventack.css | 22 ++- .../PlgTable/plugins/Table/css/eventack.less | 25 +++- .../plugins/Table/css/eventack.min.css | 2 +- .../PlgTable/plugins/Table/css/events.css | 3 + .../PlgTable/plugins/Table/css/events.min.css | 2 +- .../PlgTable/plugins/Table/css/table.css | 5 +- .../PlgTable/plugins/Table/css/table.min.css | 2 +- .../plugins/Table/css/tablecommon.css | 3 + .../plugins/Table/css/tablecommon.less | 5 + .../plugins/Table/css/tablecommon.min.css | 2 +- .../PlgTable/plugins/Table/js/eventack.js | 7 + .../plugins/Table/lang/PlgTable.en-GB.xml | 15 +++ .../plugins/Table/lang/PlgTable.ru-RU.xml | 15 +++ ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs | 125 ++---------------- ScadaWeb/ScadaWebCommon5Beta/WebPhrases.cs | 13 -- ScadaWeb/ScadaWebShell5Beta/About.aspx | 2 +- .../ScadaWebShell5Beta.csproj | 3 + .../lang/ScadaWeb.en-GB.xml | 4 - .../lang/ScadaWeb.ru-RU.xml | 4 - 34 files changed, 502 insertions(+), 229 deletions(-) create mode 100644 ScadaData/ScadaData/Data/Models/DispEventProps.cs create mode 100644 ScadaData/ScadaData/Lang/ScadaData.ru-RU.xml diff --git a/ScadaData/ScadaData/Client/DataAccess.cs b/ScadaData/ScadaData/Client/DataAccess.cs index de1814df2..2c5cd7b52 100644 --- a/ScadaData/ScadaData/Client/DataAccess.cs +++ b/ScadaData/ScadaData/Client/DataAccess.cs @@ -337,6 +337,34 @@ public Dictionary GetContentRights(int roleID) return contentRightsDict; } + /// + /// Получить имя пользователя по идентификатору + /// + public string GetUserName(int userID) + { + try + { + dataCache.RefreshBaseTables(); + BaseTables baseTables = dataCache.BaseTables; + + lock (baseTables.SyncRoot) + { + BaseTables.CheckColumnsExist(baseTables.UserTable, true); + DataView viewUser = baseTables.UserTable.DefaultView; + viewUser.Sort = "UserID"; + int rowInd = viewUser.Find(userID); + return rowInd >= 0 ? (string)viewUser[rowInd]["Name"] : ""; + } + } + catch (Exception ex) + { + log.WriteException(ex, Localization.UseRussian ? + "Ошибка при получении имени пользователя по ид.={0}" : + "Error getting user name by ID={0}", userID); + return null; + } + } + /// /// Получить свойства пользователя по идентификатору /// @@ -345,8 +373,6 @@ public UserProps GetUserProps(int userID) try { dataCache.RefreshBaseTables(); - - // необходимо сохранить ссылку, т.к. объект может быть пересоздан другим потоком BaseTables baseTables = dataCache.BaseTables; lock (baseTables.SyncRoot) @@ -494,7 +520,7 @@ public SrezTableLight.CnlData GetCurCnlData(int cnlNum, out DateTime dataAge) { SrezTableLight.Srez snapshot = dataCache.GetCurSnapshot(out dataAge); SrezTableLight.CnlData cnlData; - return snapshot != null && snapshot.GetCnlData(cnlNum, out cnlData) ? + return snapshot != null && snapshot.GetCnlData(cnlNum, out cnlData) ? cnlData : SrezTableLight.CnlData.Empty; } catch (Exception ex) @@ -507,5 +533,49 @@ public SrezTableLight.CnlData GetCurCnlData(int cnlNum, out DateTime dataAge) return SrezTableLight.CnlData.Empty; } } + + /// + /// Получить отображаемые свойства события + /// + /// Метод всегда возвращает объект, не равный null + public DispEventProps GetDispEventProps(EventTableLight.Event ev, DataFormatter dataFormatter) + { + DispEventProps dispEventProps = new DispEventProps(); + + try + { + dispEventProps.Num = ev.Number; + dispEventProps.Time = ev.DateTime.ToLocalizedString(); + dispEventProps.Ack = ev.Checked ? CommonPhrases.EventAck : CommonPhrases.EventNotAck; + + InCnlProps cnlProps = GetCnlProps(ev.CnlNum); + CnlStatProps cnlStatProps = GetCnlStatProps(ev.NewCnlStat); + + if (cnlProps == null) + { + dispEventProps.Obj = GetObjName(ev.ObjNum); + dispEventProps.KP = GetKPName(ev.KPNum); + } + else + { + dispEventProps.Obj = cnlProps.ObjName; + dispEventProps.KP = cnlProps.KPName; + dispEventProps.Cnl = cnlProps.CnlName; + dispEventProps.Color = dataFormatter.GetCnlValColor( + ev.NewCnlVal, ev.NewCnlStat, cnlProps, cnlStatProps); + dispEventProps.Sound = cnlProps.EvSound; + } + + dispEventProps.Text = dataFormatter.GetEventText(ev, cnlProps, cnlStatProps); + } + catch (Exception ex) + { + log.WriteException(ex, Localization.UseRussian ? + "Ошибка при получении отображаемых свойств события" : + "Error getting displayed event properties"); + } + + return dispEventProps; + } } } diff --git a/ScadaData/ScadaData/Client/DataFormatter.cs b/ScadaData/ScadaData/Client/DataFormatter.cs index e8095410c..330c4d905 100644 --- a/ScadaData/ScadaData/Client/DataFormatter.cs +++ b/ScadaData/ScadaData/Client/DataFormatter.cs @@ -27,6 +27,7 @@ using Scada.Data.Tables; using System; using System.Globalization; +using System.Text; namespace Scada.Client { @@ -190,6 +191,33 @@ public void FormatCnlVal(double val, int stat, InCnlProps cnlProps, string decSe } } + /// + /// Получить текст события + /// + public string GetEventText(EventTableLight.Event ev, InCnlProps cnlProps, CnlStatProps cnlStatProps) + { + if (string.IsNullOrEmpty(ev.Descr)) + { + // текст в формате "<статус>: <значение>" + StringBuilder sbText = cnlStatProps == null ? + new StringBuilder() : new StringBuilder(cnlStatProps.Name); + + if (ev.NewCnlStat > BaseValues.CnlStatuses.Undefined) + { + if (sbText.Length > 0) + sbText.Append(": "); + sbText.Append(FormatCnlVal(ev.NewCnlVal, ev.NewCnlStat, cnlProps, true)); + } + + return sbText.ToString(); + } + else + { + // только описание события + return ev.Descr; + } + } + /// /// Получить цвет значения входного канала /// diff --git a/ScadaData/ScadaData/CommonPhrases.cs b/ScadaData/ScadaData/CommonPhrases.cs index 7625fa18f..890b83704 100644 --- a/ScadaData/ScadaData/CommonPhrases.cs +++ b/ScadaData/ScadaData/CommonPhrases.cs @@ -73,6 +73,8 @@ static CommonPhrases() public static string IncorrectXmlAttrVal { get; private set; } public static string IncorrectXmlParamVal { get; private set; } public static string XmlNodeNotFound { get; private set; } + public static string EventAck { get; private set; } + public static string EventNotAck { get; private set; } public static string CmdTypeTable { get; private set; } public static string CmdValTable { get; private set; } @@ -133,7 +135,7 @@ private static void SetToDefault() NonemptyRequired = "Требуется непустое значение."; DateTimeRequired = "Требуется дата и время."; LineLengthLimit = "Длина строки должна быть не более {0} символов."; - NotNumber = "\"{0}\" не является числом"; + NotNumber = "\"{0}\" не является числом."; LoadImageError = "Ошибка при загрузке изображения из файла:\n{0}"; LoadHyperlinkError = "Ошибка при загрузке гиперссылки из файла:\n{0}"; IncorrectFileFormat = "Некорректный формат файла."; @@ -143,6 +145,8 @@ private static void SetToDefault() IncorrectXmlAttrVal = "Некорректное значение XML-атрибута \"{0}\"."; IncorrectXmlParamVal = "Некорректное значение параметра \"{0}\"."; XmlNodeNotFound = "XML-узел \"{0}\" не найден внутри узла \"{1}\"."; + EventAck = "Да"; + EventNotAck = "Нет"; CmdTypeTable = "Типы команд"; CmdValTable = "Значения команд"; @@ -216,6 +220,8 @@ public static void Init() IncorrectXmlAttrVal = dict.GetPhrase("IncorrectXmlAttrVal", IncorrectXmlAttrVal); IncorrectXmlParamVal = dict.GetPhrase("IncorrectXmlParamVal", IncorrectXmlParamVal); XmlNodeNotFound = dict.GetPhrase("XmlNodeNotFound", XmlNodeNotFound); + EventAck = dict.GetPhrase("EventAck", EventAck); + EventNotAck = dict.GetPhrase("EventNotAck", EventNotAck); CmdTypeTable = dict.GetPhrase("CmdTypeTable", CmdTypeTable); CmdValTable = dict.GetPhrase("CmdValTable", CmdValTable); diff --git a/ScadaData/ScadaData/Data/Models/DispEventProps.cs b/ScadaData/ScadaData/Data/Models/DispEventProps.cs new file mode 100644 index 000000000..092c49447 --- /dev/null +++ b/ScadaData/ScadaData/Data/Models/DispEventProps.cs @@ -0,0 +1,97 @@ +/* + * Copyright 2016 Mikhail Shiryaev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Product : Rapid SCADA + * Module : ScadaData + * Summary : Displayed event properties + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + */ + +namespace Scada.Data.Models +{ + /// + /// Displayed event properties + /// Отображаемые свойства события + /// + /// Свойства имеют короткие имена для передачи в формате JSON + public class DispEventProps + { + /// + /// Конструктор + /// + public DispEventProps() + { + Num = 0; + Time = ""; + Obj = ""; + KP = ""; + Cnl = ""; + Text = ""; + Ack = ""; + Color = ""; + Sound = false; + } + + + /// + /// Получить или установить порядковый номер + /// + public int Num { get; set; } + + /// + /// Получить или установить отформатированную дату и время + /// + public string Time { get; set; } + + /// + /// Получить или установить наименование объекта + /// + public string Obj { get; set; } + + /// + /// Получить или установить наименование КП + /// + public string KP { get; set; } + + /// + /// Получить или установить наименование входного канала + /// + public string Cnl { get; set; } + + /// + /// Получить или установить текст события + /// + public string Text { get; set; } + + /// + /// Получить или установить информацию о квитировании + /// + public string Ack { get; set; } + + /// + /// Получить или установить цвет + /// + public string Color { get; set; } + + /// + /// Получить или установить признак воспроизведения звука + /// + public bool Sound { get; set; } + } +} diff --git a/ScadaData/ScadaData/Lang/ScadaData.en-GB.xml b/ScadaData/ScadaData/Lang/ScadaData.en-GB.xml index af5a8b627..adeb2609b 100644 --- a/ScadaData/ScadaData/Lang/ScadaData.en-GB.xml +++ b/ScadaData/ScadaData/Lang/ScadaData.en-GB.xml @@ -36,6 +36,8 @@ Incorrect value of XML attribute "{0}". Incorrect value of the parameter "{0}". XML node "{0}" not found within the node "{1}". + Yes + No Command types Command values Channel types diff --git a/ScadaData/ScadaData/Lang/ScadaData.ru-RU.xml b/ScadaData/ScadaData/Lang/ScadaData.ru-RU.xml new file mode 100644 index 000000000..c70765614 --- /dev/null +++ b/ScadaData/ScadaData/Lang/ScadaData.ru-RU.xml @@ -0,0 +1,72 @@ + + + + Информация + Вопрос + Ошибка + Предупреждение + Ошибка: + Необработанное исключение + Настройки были изменены. Сохранить изменения? + Не найден файл. + Директория не существует. + Файл {0} не найден. + Директория {0} не существует. + Директория базы конфигурации в формате DAT + Директория базы конфигурации в формате DAT не существует. + Выберите директорию базы конфигурации в формате DAT + Ошибка при загрузке настроек приложения + Ошибка при сохранении настроек приложения + Ошибка при загрузке настроек соединения с сервером + Ошибка при сохранении настроек соединения с сервером + Ошибка при работе с данными + Требуется целое число + Требуется целое число в диапазоне от {0} до {1}. + Требуется вещественное число. + Требуется непустое значение. + Требуется дата и время. + Длина строки должна быть не более {0} символов. + "{0}" не является числом. + Ошибка при загрузке изображения из файла: {0} + Ошибка при загрузке гиперссылки из файла: {0} + Некорректный формат файла. + Нет данных + Недостаточно прав. + Некорректное значение XML-узла "{0}". + Некорректное значение XML-атрибута "{0}". + Некорректное значение параметра "{0}". + XML-узел "{0}" не найден внутри узла "{1}". + Да + Нет + Типы команд + Значения команд + Типы каналов + Линии связи + Каналы управления + Типы событий + Форматы чисел + Формулы + Входные каналы + Интерфейс + КП + Типы КП + Объекты + Величины + Права + Роли + Размерности + Пользователи + ожидание продолжения + пауза + ожидание паузы + работает + ожидание запуска + остановлена + ожидание остановки + не установлена + Данные отправлены успешно. + Событие отправлено успешно. + Команда квитирования события отправлена успешно. + Команда отправлена успешно. + + \ No newline at end of file diff --git a/ScadaData/ScadaData/ScadaData.csproj b/ScadaData/ScadaData/ScadaData.csproj index fb779aeac..58da0d1e1 100644 --- a/ScadaData/ScadaData/ScadaData.csproj +++ b/ScadaData/ScadaData/ScadaData.csproj @@ -53,6 +53,7 @@ + @@ -91,6 +92,9 @@ + + Designer + Designer diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs index f29c4c3c4..3fb7c26fb 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs @@ -238,11 +238,13 @@ protected void Page_Load(object sender, EventArgs e) } else { - // получение параметров запроса + // получение параметров запроса и сохранение во ViewState int viewID; int.TryParse(Request.QueryString["viewID"], out viewID); int.TryParse(Request.QueryString["ctrlCnlNum"], out ctrlCnlNum); + ViewState["CtrlCnlNum"] = ctrlCnlNum; + // проверка прав if (!userData.UserRights.GetViewRights(viewID).ControlRight || !userData.WebSettings.CmdEnabled) @@ -254,9 +256,6 @@ protected void Page_Load(object sender, EventArgs e) if (!view.ContainsCtrlCnl(ctrlCnlNum)) throw new ScadaException(CommonPhrases.NoRights); - // сохранение номера канала управления во ViewState - ViewState["CtrlCnlNum"] = ctrlCnlNum; - // перевод веб-страницы Translator.TranslatePage(Page, "Scada.Web.Plugins.Table.WFrmCommand"); diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventAck.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventAck.aspx index 0802c8143..beffefde4 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventAck.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventAck.aspx @@ -16,13 +16,16 @@ +
- + @@ -52,14 +55,10 @@ - - - - - - + + + + diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventAck.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventAck.aspx.cs index d7559d2de..7ebcadf03 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventAck.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventAck.aspx.cs @@ -24,9 +24,11 @@ */ using Scada.Client; +using Scada.Data.Models; using Scada.Data.Tables; using Scada.UI; using System; +using System.Drawing; using System.Web.UI.WebControls; namespace Scada.Web.plugins.Table @@ -37,8 +39,10 @@ namespace Scada.Web.plugins.Table ///
public partial class WFrmEventAck : System.Web.UI.Page { - private DateTime evDate; // дата события - private int evNum; // номер события + private AppData appData; // общие данные веб-приложения + private UserData userData; // данные пользователя приложения + private DateTime evDate; // дата события + private int evNum; // номер события /// @@ -48,6 +52,8 @@ private void HideErrMsg() { pnlErrMsg.Visible = false; lblEventNotFound.Visible = false; + lblAckNotSent.Visible = false; + lblAckRejected.Visible = false; } /// @@ -62,8 +68,8 @@ private void ShowErrMsg(Label lblMessage) protected void Page_Load(object sender, EventArgs e) { - AppData appData = AppData.GetAppData(); - UserData userData = UserData.GetUserData(); + appData = AppData.GetAppData(); + userData = UserData.GetUserData(); // проверка входа в систему if (!userData.LoggedOn) @@ -72,17 +78,25 @@ protected void Page_Load(object sender, EventArgs e) // скрытие сообщения об ошибке HideErrMsg(); - if (!IsPostBack) + if (IsPostBack) + { + evDate = (DateTime)ViewState["EvDate"]; + evNum = (int)ViewState["EvNum"]; + } + else { // перевод веб-страницы Translator.TranslatePage(Page, "Scada.Web.Plugins.Table.WFrmEventAck"); - // получение параметров запроса + // получение параметров запроса и сохранение во ViewState int viewID; int.TryParse(Request.QueryString["viewID"], out viewID); evDate = WebUtils.GetDateFromQueryString(Request); int.TryParse(Request.QueryString["evNum"], out evNum); + ViewState["EvDate"] = evDate; + ViewState["EvNum"] = evNum; + // получение события EventTableLight tblEvent = appData.DataAccess.DataCache.GetEventTable(evDate); EventTableLight.Event ev = tblEvent.GetEventByNum(evNum); @@ -95,27 +109,74 @@ protected void Page_Load(object sender, EventArgs e) else { // проверка прав - /*if (!userData.UserRights.GetViewRights(viewID).ControlRight || - !userData.WebSettings.CmdEnabled) + EntityRights rights = userData.UserRights.GetViewRights(viewID); + if (!rights.ViewRight) throw new ScadaException(CommonPhrases.NoRights); Type viewType = userData.UserViews.GetViewType(viewID); BaseView view = appData.ViewCache.GetView(viewType, viewID, true); - if (!view.ContainsCtrlCnl(ev.CnlNum)) - throw new ScadaException(CommonPhrases.NoRights);*/ + if (!view.ContainsCnl(ev.CnlNum)) + throw new ScadaException(CommonPhrases.NoRights); + + btnSubmit.Visible = pnlTip.Visible = + rights.ControlRight && !ev.Checked; // вывод информации по событию pnlInfo.Visible = true; - lblNum.Text = ev.Number.ToString(); - lblTime.Text = ev.DateTime.ToLocalizedString(); + DispEventProps evProps = appData.DataAccess.GetDispEventProps(ev, new DataFormatter()); + lblNum.Text = evProps.Num.ToString(); + lblTime.Text = evProps.Time; + lblObj.Text = evProps.Obj; + lblDev.Text = evProps.KP; + lblCnl.Text = evProps.Cnl; + lblText.Text = evProps.Text; + lblAck.Text = evProps.Ack; + lblAck.CssClass = ev.Checked ? "ack-yes" : "ack-no"; + + if (ev.Checked && ev.UserID > 0) + { + string userName = appData.DataAccess.GetUserName(ev.UserID); + lblByUser.Text = string.Format(lblByUser.Text, userName); + lblByUser.Visible = userName != ""; + } + + if (evProps.Color != "") + { + try + { + lblNum.ForeColor = lblTime.ForeColor = lblObj.ForeColor = + lblDev.ForeColor = lblCnl.ForeColor = lblText.ForeColor = + ColorTranslator.FromHtml(evProps.Color); + } + catch { } + } } } } protected void btnSubmit_Click(object sender, EventArgs e) { + // отправка команды квитирования + bool result; + bool sendOK = appData.ServerComm.CheckEvent(userData.UserProps.UserID, evDate, evNum, out result); + + btnSubmit.Enabled = false; + pnlInfo.Visible = false; + pnlTip.Visible = false; + if (sendOK && result) + { + ClientScript.RegisterStartupScript(GetType(), "Startup", "closeModal();", true); + } + else if (sendOK) + { + ShowErrMsg(lblAckRejected); + } + else + { + ShowErrMsg(lblAckNotSent); + } } } } \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventAck.aspx.designer.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventAck.aspx.designer.cs index 49735ea97..4f505a9db 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventAck.aspx.designer.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventAck.aspx.designer.cs @@ -48,6 +48,24 @@ public partial class WFrmEventAck { /// protected global::System.Web.UI.WebControls.Label lblEventNotFound; + /// + /// lblAckNotSent control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblAckNotSent; + + /// + /// lblAckRejected control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblAckRejected; + /// /// pnlInfo control. /// @@ -184,49 +202,13 @@ public partial class WFrmEventAck { protected global::System.Web.UI.WebControls.Label lblAck; /// - /// lblNotAck control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label lblNotAck; - - /// - /// rowUser control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlTableRow rowUser; - - /// - /// lblUserCaption control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label lblUserCaption; - - /// - /// lblUser control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label lblUser; - - /// - /// lblUserNotFound control. + /// lblByUser control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.WebControls.Label lblUserNotFound; + protected global::System.Web.UI.WebControls.Label lblByUser; /// /// pnlTip control. diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx index dd55925f2..d2d85a3c4 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx @@ -27,8 +27,6 @@ var locale = "<%= Scada.Localization.Culture.Name %>"; var dispEventCnt = <%= dispEventCnt %>; var viewAllRight = <%= viewAllRight ? "true" : "false" %>; - var controlAllRight = <%= controlAllRight ? "true" : "false" %>; - var controlViewRight = <%= controlViewRight ? "true" : "false" %>; @@ -40,7 +38,7 @@
TitleChangedNavigateDateChanged diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs index ed82434c5..3c9e8746a 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs @@ -46,8 +46,6 @@ public partial class WFrmEvents : System.Web.UI.Page protected string today; // текущая дата protected int dispEventCnt; // количество отображаемых событий protected bool viewAllRight; // право на просмотр всех данных - protected bool controlAllRight; // право на любое управление - protected bool controlViewRight; // право на управление представлением protected void Page_Load(object sender, EventArgs e) { @@ -66,9 +64,7 @@ protected void Page_Load(object sender, EventArgs e) int.TryParse(Request.QueryString["viewID"], out viewID); // проверка прав на просмотр представления - EntityRights rights = userData.LoggedOn ? - userData.UserRights.GetViewRights(viewID) : EntityRights.NoRights; - if (!rights.ViewRight) + if (!userData.UserRights.GetViewRights(viewID).ViewRight) Response.Redirect(UrlTemplates.NoView); // загрузка представления в кеш для последующего получения событий через API @@ -83,8 +79,6 @@ protected void Page_Load(object sender, EventArgs e) arcRefrRate = userData.WebSettings.ArcRefrRate; dispEventCnt = userData.WebSettings.DispEventCnt; viewAllRight = userData.UserRights.ViewAllRight; - controlAllRight = userData.UserRights.ControlAllRight; - controlViewRight = rights.ControlRight; Localization.Dict dict; Localization.Dictionaries.TryGetValue("Scada.Web.Plugins.Table.WFrmEvents.Js", out dict); diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx index 805f685db..08bdcc7b4 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx @@ -36,7 +36,7 @@
<%= selTimeFromHtml %> - <%= selTimeToHtml %>
TitleChangedNavigateDateChanged diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs index 341d4d78a..9f519bb4a 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs @@ -158,7 +158,7 @@ private string GenerateTableViewHtml(TableView tableView, bool cmdEnabled, int t { InCnlProps cnlProps = item.CnlProps; int cnlNum = item.CnlNum; - int ctrlCnlNum = cmdEnabled ? item.CtrlCnlNum : 0; + int ctrlCnlNum = item.CtrlCnlNum; // тег начала строки sbHtml.Append(altRow ? "").Append(caption).Append(""); // команда - if (ctrlCnlNum > 0) + if (ctrlCnlNum > 0 && cmdEnabled) sbCapHtml.Append(""); // всплывающая подсказка diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/eventack.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/eventack.css index 7e33ccc2b..b63e40aa9 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/eventack.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/eventack.css @@ -1,4 +1,6 @@ -body { +/* Bootstrap primary color */ +/* Bootstrap default color */ +body { margin: 0; padding: 0; background-color: white; @@ -24,6 +26,20 @@ table { padding: 2px 10px; vertical-align: top; } -#tblInfo .label { - font-size: 100%; +#tblInfo td.ack span { + position: relative; + top: -2px; +} +#tblInfo .ack-yes, +#tblInfo .ack-no { + border-radius: 3px; + color: white; + display: inline-block; + padding: 2px 5px; +} +#tblInfo .ack-yes { + background-color: #337ab7; +} +#tblInfo .ack-no { + background-color: #777; } \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/eventack.less b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/eventack.less index f9fbdc4f0..c45a31fb5 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/eventack.less +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/eventack.less @@ -1,5 +1,9 @@ @import "../../../css/globalvar.less"; +@ack-fore-color: white; +@ack-yes-back-color: #337ab7; /* Bootstrap primary color */ +@ack-no-back-color: #777; /* Bootstrap default color */ + body { margin: 0; padding: 0; @@ -31,6 +35,23 @@ table { vertical-align: top; } -#tblInfo .label { - font-size: 100%; +#tblInfo td.ack span { + position: relative; + top: -2px; +} + +#tblInfo .ack-yes, +#tblInfo .ack-no { + border-radius: 3px; + color: @ack-fore-color; + display: inline-block; + padding: 2px 5px; +} + +#tblInfo .ack-yes { + background-color: @ack-yes-back-color; +} + +#tblInfo .ack-no { + background-color: @ack-no-back-color; } \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/eventack.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/eventack.min.css index 94e562bf1..7a9b76b6d 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/eventack.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/eventack.min.css @@ -1 +1 @@ -body{margin:0;padding:0;background-color:#fff;color:#333;font-family:'Open Sans',sans-serif;font-size:13px;min-width:300px;overflow:hidden;}table{border-collapse:collapse;}#btnSubmit{display:none;}#tblInfo th{font-weight:normal;padding:2px 0;vertical-align:top;text-align:right;}#tblInfo td{padding:2px 10px;vertical-align:top;}#tblInfo .label{font-size:100%;} \ No newline at end of file +body{margin:0;padding:0;background-color:#fff;color:#333;font-family:'Open Sans',sans-serif;font-size:13px;min-width:300px;overflow:hidden;}table{border-collapse:collapse;}#btnSubmit{display:none;}#tblInfo th{font-weight:normal;padding:2px 0;vertical-align:top;text-align:right;}#tblInfo td{padding:2px 10px;vertical-align:top;}#tblInfo td.ack span{position:relative;top:-2px;}#tblInfo .ack-yes,#tblInfo .ack-no{border-radius:3px;color:#fff;display:inline-block;padding:2px 5px;}#tblInfo .ack-yes{background-color:#337ab7;}#tblInfo .ack-no{background-color:#777;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css index 8827fc78c..1e777739f 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css @@ -10,6 +10,9 @@ .hidden { display: none; } +.not-implemented { + display: none !important; +} /* Toolbar */ #divToolbar { position: fixed; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css index 31d019721..1c72ab896 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:13px;overflow:hidden;}.hidden{display:none;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a{color:#0073aa;text-decoration:none;}#divTblWrapper td a:hover{color:#00a0d2;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divToolbar .tool-btn.selected{background-color:#0073aa;color:#fff;}#divToolbar .tool-btn.disabled{color:#ccc;cursor:default;}#tblEvents{width:100%;}#divTblWrapper tr.event:hover{background-color:#e0e0e0;color:#000;}#tblEvents td.num,#tblEvents td.time,#tblEvents td.ack{text-align:center;}#tblEvents td.num,#tblEvents td.ack{width:5%;}#tblEvents td.time{width:10%;}#tblEvents td.obj,#tblEvents td.dev,#tblEvents td.cnl{width:15%;}#tblEvents td.text{width:35%;}#tblEvents td.ack a{display:inline-block;padding:0 5px;}#divLoading,#divNoEvents{padding:5px 10px;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:13px;overflow:hidden;}.hidden{display:none;}.not-implemented{display:none !important;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a{color:#0073aa;text-decoration:none;}#divTblWrapper td a:hover{color:#00a0d2;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divToolbar .tool-btn.selected{background-color:#0073aa;color:#fff;}#divToolbar .tool-btn.disabled{color:#ccc;cursor:default;}#tblEvents{width:100%;}#divTblWrapper tr.event:hover{background-color:#e0e0e0;color:#000;}#tblEvents td.num,#tblEvents td.time,#tblEvents td.ack{text-align:center;}#tblEvents td.num,#tblEvents td.ack{width:5%;}#tblEvents td.time{width:10%;}#tblEvents td.obj,#tblEvents td.dev,#tblEvents td.cnl{width:15%;}#tblEvents td.text{width:35%;}#tblEvents td.ack a{display:inline-block;padding:0 5px;}#divLoading,#divNoEvents{padding:5px 10px;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css index 54dfca771..791446aec 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css @@ -10,6 +10,9 @@ .hidden { display: none; } +.not-implemented { + display: none !important; +} /* Toolbar */ #divToolbar { position: fixed; @@ -173,7 +176,7 @@ border: 1px solid #888; border-radius: 5px; box-shadow: 2px 2px 5px #aaa; - color: #444; + color: #333; display: none; line-height: normal; padding: 5px 10px; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css index c64921d84..e1cc29737 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:13px;overflow:hidden;}.hidden{display:none;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a{color:#0073aa;text-decoration:none;}#divTblWrapper td a:hover{color:#00a0d2;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td.cap{padding:2px 5px;}#divTblWrapper td.cap img{width:16px;height:16px;margin:0 5px 0 0;border:none;}#divTblWrapper td.cap a{height:18px;margin-right:5px;display:inline-block;line-height:18px;}#divTblWrapper td.cap span.cmd{display:inline-block;width:18px;height:18px;cursor:pointer;color:#a00;font-size:15px;line-height:18px;text-align:center;}#divTblWrapper td.cap span.cmd:hover{color:#f00;}#divTblWrapper td.cap span.cmd::before{content:"";font-family:FontAwesome;}#divTblWrapper td.cap span.hint{background-color:#fff;border:1px solid #888;border-radius:5px;box-shadow:2px 2px 5px #aaa;color:#444;display:none;line-height:normal;padding:5px 10px;position:fixed;z-index:10;}#divTblWrapper td.cap span.hint.show{display:inline-block;}#divTblWrapper td.cur,#divTblWrapper td.hour{text-align:center;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:13px;overflow:hidden;}.hidden{display:none;}.not-implemented{display:none !important;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a{color:#0073aa;text-decoration:none;}#divTblWrapper td a:hover{color:#00a0d2;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td.cap{padding:2px 5px;}#divTblWrapper td.cap img{width:16px;height:16px;margin:0 5px 0 0;border:none;}#divTblWrapper td.cap a{height:18px;margin-right:5px;display:inline-block;line-height:18px;}#divTblWrapper td.cap span.cmd{display:inline-block;width:18px;height:18px;cursor:pointer;color:#a00;font-size:15px;line-height:18px;text-align:center;}#divTblWrapper td.cap span.cmd:hover{color:#f00;}#divTblWrapper td.cap span.cmd::before{content:"";font-family:FontAwesome;}#divTblWrapper td.cap span.hint{background-color:#fff;border:1px solid #888;border-radius:5px;box-shadow:2px 2px 5px #aaa;color:#333;display:none;line-height:normal;padding:5px 10px;position:fixed;z-index:10;}#divTblWrapper td.cap span.hint.show{display:inline-block;}#divTblWrapper td.cur,#divTblWrapper td.hour{text-align:center;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css index f15c4bb27..1c9b976b6 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css @@ -10,6 +10,9 @@ .hidden { display: none; } +.not-implemented { + display: none !important; +} /* Toolbar */ #divToolbar { position: fixed; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less index 474c6f9ce..48c7f0183 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less @@ -41,6 +41,11 @@ body { display: none; } +.not-implemented { + display: none !important; +} + + /* Toolbar */ #divToolbar { diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css index 9f799f1d6..6f116bab9 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:13px;overflow:hidden;}.hidden{display:none;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a{color:#0073aa;text-decoration:none;}#divTblWrapper td a:hover{color:#00a0d2;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:13px;overflow:hidden;}.hidden{display:none;}.not-implemented{display:none !important;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a{color:#0073aa;text-decoration:none;}#divTblWrapper td a:hover{color:#00a0d2;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/eventack.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/eventack.js index 8f9697f3d..b7bf751d9 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/eventack.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/eventack.js @@ -1,6 +1,13 @@ // Popup dialogs manipulation object var popup = scada.popupLocator.getPopup(); +// Close the modal with successful result +function closeModal() { + if (popup) { + popup.closeModal(window, true); + } +} + $(document).ready(function () { // hide or disable OK button according to the submit button state if (popup) { diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml index 4886f6e2b..1aa201b5e 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml @@ -29,6 +29,21 @@ Unable to send the command. Server is unavailable. The command is rejected by the server. + + Event Acknowledgement - Rapid SCADA + Event not found. + Unable to send the acknowledgement. Server is unavailable. + The acknowledgement is rejected by the server. + Number: + Date and time: + Object: + Device: + Channel: + Description: + Acknowledged: + by {0} + Click OK button to acknowledge the event. + All Events Events by View diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml index 9b329eee0..9c055f5c8 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml @@ -29,6 +29,21 @@ Невозможно отправить команду. Сервер недоступен. Команда отклонена сервером. + + Квитирование события - Rapid SCADA + Событие не найдено. + Невозможно отправить команду квитирования. Сервер недоступен. + Команда квитирования отклонена сервером. + Номер: + Дата и время: + Объект: + КП: + Канал: + Описание: + Квитирование: + пользователем {0} + Нажмите кнопку OK, чтобы квитировать событие. + Все события По представлению diff --git a/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs b/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs index efc8190a2..7e49f0ec8 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs @@ -147,65 +147,6 @@ public HourCnlData(double hour) public CnlDataExt[] CnlDataExtArr { get; set; } } - /// - /// Данные события - /// - private class Event - { - /// - /// Конструктор - /// - public Event() - { - Num = 0; - Time = ""; - Obj = ""; - KP = ""; - Cnl = ""; - Text = ""; - Ack = ""; - Color = ""; - Sound = false; - } - - /// - /// Получить или установить порядковый номер - /// - public int Num { get; set; } - /// - /// Получить или установить отформатированную дату и время - /// - public string Time { get; set; } - /// - /// Получить или установить наименование объекта - /// - public string Obj { get; set; } - /// - /// Получить или установить наименование КП - /// - public string KP { get; set; } - /// - /// Получить или установить наименование входного канала - /// - public string Cnl { get; set; } - /// - /// Получить или установить текст события - /// - public string Text { get; set; } - /// - /// Получить или установить информацию о квитировании - /// - public string Ack { get; set; } - /// - /// Получить или установить цвет - /// - public string Color { get; set; } - /// - /// Получить или установить признак воспроизведения звука - /// - public bool Sound { get; set; } - } - /// /// Максимальное количество символов в строке данных формата JSON, 10 МБ @@ -473,57 +414,6 @@ private HourCnlData[] GetHourCnlDataArr(int year, int month, int day, return hourCnlDataList.ToArray(); } - /// - /// Преобразовать событие из таблицы в событие для передачи сервисом - /// - private Event ConvertEvent(EventTableLight.Event srcEvent) - { - Event destEvent = new Event(); - destEvent.Num = srcEvent.Number; - destEvent.Time = srcEvent.DateTime.ToLocalizedString(); - destEvent.Ack = srcEvent.Checked ? WebPhrases.EventAck : WebPhrases.EventNotAck; - - DataAccess dataAccess = AppData.DataAccess; - InCnlProps cnlProps = dataAccess.GetCnlProps(srcEvent.CnlNum); - - destEvent.Obj = cnlProps != null && cnlProps.ObjNum == srcEvent.ObjNum ? - cnlProps.ObjName : dataAccess.GetObjName(srcEvent.ObjNum); - destEvent.KP = cnlProps != null && cnlProps.KPNum == srcEvent.KPNum ? - cnlProps.KPName : dataAccess.GetKPName(srcEvent.KPNum); - - double cnlVal = srcEvent.NewCnlVal; - int cnlStat = srcEvent.NewCnlStat; - CnlStatProps cnlStatProps = dataAccess.GetCnlStatProps(cnlStat); - - if (cnlProps != null) - { - destEvent.Cnl = cnlProps.CnlName; - destEvent.Color = DataFormatter.GetCnlValColor(cnlVal, cnlStat, cnlProps, cnlStatProps); - destEvent.Sound = cnlProps.EvSound; - } - - // формирование текста события - if (string.IsNullOrEmpty(srcEvent.Descr)) - { - // текст в формате "<статус>: <значение>" - StringBuilder sbText = cnlStatProps == null ? - new StringBuilder() : new StringBuilder(cnlStatProps.Name); - if (cnlStat > BaseValues.CnlStatuses.Undefined) - { - if (sbText.Length > 0) - sbText.Append(": "); - sbText.Append(DataFormatter.FormatCnlVal(cnlVal, cnlStat, cnlProps, true)); - } - destEvent.Text = sbText.ToString(); - } - else - { - destEvent.Text = srcEvent.Descr; - } - - return destEvent; - } - /// /// Получить объект для передачи данных, содержащий информацию об ошибке, в формате JSON /// @@ -638,7 +528,7 @@ public string GetHourCnlData(int year, int month, int day, int startHour, int en /// /// Получить события по заданному фильтру /// - /// Возвращает Event[], упакованный в ArcDTO, в формате в JSON. + /// Возвращает DispEventProps[], упакованный в ArcDTO, в формате в JSON. /// Если задан фильтр по представлению, то оно должно быть уже загружено в кеш [OperationContract] [WebGet] @@ -658,10 +548,11 @@ public string GetEvents(int year, int month, int day, string cnlNums, int viewID eventFilter.CnlNums = cnlSet; // получение событий + DataAccess dataAccess = AppData.DataAccess; DateTime date = new DateTime(year, month, day); - EventTableLight tblEvent = AppData.DataAccess.DataCache.GetEventTable(date); + EventTableLight tblEvent = dataAccess.DataCache.GetEventTable(date); long newDataAge = WebUtils.DateTimeToJs(tblEvent.FileModTime); - Event[] eventsToSend; + DispEventProps[] eventsToSend; if (tblEvent.FileModTime > DateTime.MinValue && dataAge < newDataAge) { @@ -672,21 +563,21 @@ public string GetEvents(int year, int month, int day, string cnlNums, int viewID // преобразование событий для передачи int evCnt = events.Count; - eventsToSend = new Event[evCnt]; + eventsToSend = new DispEventProps[evCnt]; if (reversed) { for (int i = 0, j = evCnt - 1; i < evCnt; i++, j--) - eventsToSend[i] = ConvertEvent(events[j]); + eventsToSend[i] = dataAccess.GetDispEventProps(events[j], DataFormatter); } else { for (int i = 0; i < evCnt; i++) - eventsToSend[i] = ConvertEvent(events[i]); + eventsToSend[i] = dataAccess.GetDispEventProps(events[i], DataFormatter); } } else { - eventsToSend = new Event[0]; + eventsToSend = new DispEventProps[0]; } return JsSerializer.Serialize(new ArcDTO(eventsToSend, newDataAge)); diff --git a/ScadaWeb/ScadaWebCommon5Beta/WebPhrases.cs b/ScadaWeb/ScadaWebCommon5Beta/WebPhrases.cs index b1af6e051..b545998f2 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/WebPhrases.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/WebPhrases.cs @@ -48,10 +48,6 @@ static WebPhrases() public static string NoRights { get; private set; } public static string IllegalRole { get; private set; } - // Словарь Scada.Web.ClientApiSvc - public static string EventAck { get; private set; } - public static string EventNotAck { get; private set; } - // Словарь Scada.Web.ViewSettings public static string LoadViewSettingsError { get; private set; } public static string SaveViewSettingsError { get; private set; } @@ -84,9 +80,6 @@ private static void SetToDefault() NoRights = Localization.Dict.GetEmptyPhrase("NoRights"); IllegalRole = Localization.Dict.GetEmptyPhrase("IllegalRole"); - EventAck = Localization.Dict.GetEmptyPhrase("EventAck"); - EventNotAck = Localization.Dict.GetEmptyPhrase("EventNotAck"); - LoadViewSettingsError = Localization.Dict.GetEmptyPhrase("LoadViewSettingsError"); SaveViewSettingsError = Localization.Dict.GetEmptyPhrase("SaveViewSettingsError"); LoadViewSettingsBaseError = Localization.Dict.GetEmptyPhrase("LoadViewSettingsBaseError"); @@ -122,12 +115,6 @@ public static void Init() IllegalRole = dict.GetPhrase("IllegalRole", IllegalRole); } - if (Localization.Dictionaries.TryGetValue("Scada.Web.ClientApiSvc", out dict)) - { - EventAck = dict.GetPhrase("EventAck", EventAck); - EventNotAck = dict.GetPhrase("EventNotAck", EventNotAck); - } - if (Localization.Dictionaries.TryGetValue("Scada.Web.ViewSettings", out dict)) { LoadWebSettingsError = dict.GetPhrase("LoadViewSettingsError", LoadViewSettingsError); diff --git a/ScadaWeb/ScadaWebShell5Beta/About.aspx b/ScadaWeb/ScadaWebShell5Beta/About.aspx index 69ec7e466..288f3224c 100644 --- a/ScadaWeb/ScadaWebShell5Beta/About.aspx +++ b/ScadaWeb/ScadaWebShell5Beta/About.aspx @@ -5,6 +5,6 @@
Webstation - 4.6.0.1 Beta
+ 4.6.1.0 Beta
diff --git a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj index 2f85ff59a..3d107e31c 100644 --- a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj +++ b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj @@ -66,6 +66,9 @@ lang\ScadaData.en-GB.xml + + lang\ScadaData.ru-RU.xml + diff --git a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml index ece8a9ab0..4d1d60332 100644 --- a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml +++ b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml @@ -10,10 +10,6 @@ Insufficient rights. Illegal user role. - - Yes - No - Logout Full Screen diff --git a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml index aea979643..23d996276 100644 --- a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml +++ b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml @@ -10,10 +10,6 @@ Недостаточно прав. Недопустимая роль пользователя. - - Да - Нет - Выйти На весь экран From a9be37ba6054f9e64ccbb6988cf8e9fa1fc2ebf6 Mon Sep 17 00:00:00 2001 From: 2mik Date: Thu, 21 Jul 2016 16:25:54 +0300 Subject: [PATCH 195/382] ScadaWeb5: fix css --- ScadaWeb/ScadaWebShell5Beta/css/mastermain.css | 2 +- ScadaWeb/ScadaWebShell5Beta/css/mastermain.less | 2 +- ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css index 894161f56..df0c37237 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css @@ -96,7 +96,7 @@ a:active { left: 0; top: 30px; width: 250px; - min-height: 250px; + min-height: 200px; background-color: #23282d; color: #eee; font-size: 14px; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less index 1fc6dedb2..42b672f69 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less @@ -116,7 +116,7 @@ a:active { left: 0; top: @header-heigth; width: @left-pane-width; - min-height: 250px; + min-height: 200px; background-color: @menu-back-color; color: @menu-fore-color; font-size: 14px; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css index 027cb4886..60c4c53b7 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 250px;background-color:#f1f1f1;color:#333;font-family:'Open Sans',sans-serif;font-size:13px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;}#divMainHeader .hdr-btn{height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;display:inline-block;font-size:13px;text-decoration:none;white-space:nowrap;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenuBtn{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;white-space:nowrap;}#divMainUserMenu{float:right;height:30px;display:inline-block;}#lblMainNormalViewBtn{position:fixed;top:0;right:0;height:30px;padding:0 10px;background-color:#fff;display:none;line-height:30px;opacity:.5;cursor:pointer;color:#9ca1a6;font-size:13px;}#lblMainNormalViewBtn:hover{color:#00b9eb;opacity:1;}#divMainLeftPane{position:fixed;left:0;top:30px;width:250px;min-height:250px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 30px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainLeftPane .tree-view .node:hover{background-color:#191e23;color:#00b9eb;}#divMainLeftPane .tree-view .node.selected{background-color:#0073aa;color:#fff;}#divMainLeftPane .tree-view .node.disabled{color:#9ca1a6;}#divMainLeftPane .tree-view .expander::before{font-size:10px;}#divMainMenu .node[data-level="1"],#divMainMenu .node[data-level="2"],#divMainMenu .node[data-level="3"],#divMainMenu .node[data-level="4"],#divMainMenu .node[data-level="5"]{color:#9ca1a6;font-size:13px;}#divMainMenu .tree-view .node{padding:5px 10px;}#divMainMenu .tree-view .expander.left{display:none;width:0;}#divMainMenu .tree-view .expander.right{display:table-cell;width:20px;}#divMainExplorer{font-size:13px;line-height:13px;}#divMainExplorer .tree-view .node{padding:3px 5px 3px 10px;}#divMainExplorer .tree-view .expander{padding:4px 0 0 0;}#divMainExplorer .tree-view .icon{width:21px;min-width:21px;}#divMainExplorer .tree-view img{position:relative;top:-2px;}#divMainCollapseLeftPaneBtn{position:absolute;bottom:0;width:220px;height:30px;margin:0 0 0 30px;padding:0 10px;color:#9ca1a6;cursor:pointer;font-size:13px;line-height:30px;}#divMainCollapseLeftPaneBtn i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapseLeftPaneBtn:hover{color:#00b9eb;}#divMainContent{overflow:auto;} \ No newline at end of file +body{margin:0;padding:30px 0 0 250px;background-color:#f1f1f1;color:#333;font-family:'Open Sans',sans-serif;font-size:13px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;}#divMainHeader .hdr-btn{height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;display:inline-block;font-size:13px;text-decoration:none;white-space:nowrap;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenuBtn{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;white-space:nowrap;}#divMainUserMenu{float:right;height:30px;display:inline-block;}#lblMainNormalViewBtn{position:fixed;top:0;right:0;height:30px;padding:0 10px;background-color:#fff;display:none;line-height:30px;opacity:.5;cursor:pointer;color:#9ca1a6;font-size:13px;}#lblMainNormalViewBtn:hover{color:#00b9eb;opacity:1;}#divMainLeftPane{position:fixed;left:0;top:30px;width:250px;min-height:200px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 30px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainLeftPane .tree-view .node:hover{background-color:#191e23;color:#00b9eb;}#divMainLeftPane .tree-view .node.selected{background-color:#0073aa;color:#fff;}#divMainLeftPane .tree-view .node.disabled{color:#9ca1a6;}#divMainLeftPane .tree-view .expander::before{font-size:10px;}#divMainMenu .node[data-level="1"],#divMainMenu .node[data-level="2"],#divMainMenu .node[data-level="3"],#divMainMenu .node[data-level="4"],#divMainMenu .node[data-level="5"]{color:#9ca1a6;font-size:13px;}#divMainMenu .tree-view .node{padding:5px 10px;}#divMainMenu .tree-view .expander.left{display:none;width:0;}#divMainMenu .tree-view .expander.right{display:table-cell;width:20px;}#divMainExplorer{font-size:13px;line-height:13px;}#divMainExplorer .tree-view .node{padding:3px 5px 3px 10px;}#divMainExplorer .tree-view .expander{padding:4px 0 0 0;}#divMainExplorer .tree-view .icon{width:21px;min-width:21px;}#divMainExplorer .tree-view img{position:relative;top:-2px;}#divMainCollapseLeftPaneBtn{position:absolute;bottom:0;width:220px;height:30px;margin:0 0 0 30px;padding:0 10px;color:#9ca1a6;cursor:pointer;font-size:13px;line-height:30px;}#divMainCollapseLeftPaneBtn i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapseLeftPaneBtn:hover{color:#00b9eb;}#divMainContent{overflow:auto;} \ No newline at end of file From 8c33fc9f08e228d604983d76209ca9893e4771d6 Mon Sep 17 00:00:00 2001 From: 2mik Date: Thu, 21 Jul 2016 16:26:06 +0300 Subject: [PATCH 196/382] PlgTable: events --- ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js index 3e0149ef9..7195a2b4f 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js @@ -32,10 +32,6 @@ var partUpdateTimeoutID = null; var dispEventCnt = dispEventCnt || 0; // Right to view all data var viewAllRight = viewAllRight || false; -// Right to any control -var controlAllRight = controlAllRight || false; -// Right to control the view -var controlViewRight = controlViewRight || false; // Set current view date and process the consequent changes function changeViewDate(date, notify) { @@ -102,9 +98,7 @@ function showEventAck(evNum) { // Generate HTML of acknowledgement cell function generateAckHtml(evNum, ack) { - return controlAllRight || viewID > 0 && controlViewRight ? - "" + ack + "" : - ack; + return "" + ack + ""; } // Create detached jQuery object that represents an event row From 452410a75b8ce55598e38e0a388b6ddad6efebfb Mon Sep 17 00:00:00 2001 From: 2mik Date: Thu, 21 Jul 2016 18:41:58 +0300 Subject: [PATCH 197/382] ScadaWeb5: fixes --- .../PlgTable/plugins/Table/Command.aspx.cs | 3 +- .../PlgTable/plugins/Table/js/command.js | 2 +- .../ScadaWebShell5Beta/js/controls/popup.js | 39 +++++++++++++------ 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs index 3fb7c26fb..a05c979dc 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs @@ -297,7 +297,8 @@ protected void Page_Load(object sender, EventArgs e) repCommands.DataSource = GetDiscreteCmds(ctrlCnlProps.CmdValArr); repCommands.DataBind(); pnlDiscreteValue.Visible = true; - btnSubmit.Visible = false; + btnSubmit.Enabled = false; // disable postback on Enter + btnSubmit.CssClass = "hide-exec-btn"; // hide Execute button } break; case BaseValues.CmdTypes.Binary: diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/command.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/command.js index 5e82394ad..ed987018f 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/command.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/command.js @@ -31,7 +31,7 @@ $(document).ready(function () { // hide or disable execute button according to the submit button state if (popup) { var btnSubmit = $("#btnSubmit"); - if (btnSubmit.length == 0) { + if (btnSubmit.hasClass("hide-exec-btn")) { popup.setButtonVisible(window, scada.ModalButtons.EXEC, false); } else if (btnSubmit.is(":disabled")) { popup.setButtonEnabled(window, scada.ModalButtons.EXEC, false); diff --git a/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js b/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js index 5df13b2b6..da6577f4f 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js @@ -231,6 +231,7 @@ scada.Popup.prototype.closeDropdown = function (popupWnd, dialogResult, extraPar // opt_callback is a function (dialogResult, extraParams), // requires Bootstrap scada.Popup.prototype.showModal = function (url, opt_buttons, opt_callback) { + // create the modal var footerHtml = opt_buttons && opt_buttons.length ? "" : ""; @@ -241,7 +242,7 @@ scada.Popup.prototype.showModal = function (url, opt_buttons, opt_callback) { "" + - "" + + "" + footerHtml + "
"); @@ -251,16 +252,22 @@ scada.Popup.prototype.showModal = function (url, opt_buttons, opt_callback) { .data("dialog-result", false); } - $("body").append(modalElem); + // create the frame + var modalFrame = $(""); + modalFrame.css({ + "position": "fixed", + "opacity": 0.0 // hide the frame while it's loading + }); + $("body").append(modalFrame); - // load the frame - var modalFrame = modalElem.find(".modal-frame"); + // create a function that hides the modal on press Escape key var hideModalOnEscapeFunc = function (event) { if (event.which == 27 /*Escape*/) { modalElem.modal("hide"); } } + // load the frame modalFrame .on("load", function () { // remove the modal on press Escape key in the frame @@ -275,25 +282,34 @@ scada.Popup.prototype.showModal = function (url, opt_buttons, opt_callback) { } }) .one("load", function () { - // set the frame size + // get the frame size var frameBody = modalFrame.contents().find("body"); var frameWidth = frameBody.outerWidth(true); var frameHeight = frameBody.outerHeight(true); - modalFrame.css({ - "width": "100%", - "height": frameHeight - }); - // tune the modal + var frameWnd = modalFrame[0].contentWindow; var modalBody = modalElem.find(".modal-body"); var modalPaddings = parseInt(modalBody.css("padding-left")) + parseInt(modalBody.css("padding-right")); modalElem.find(".modal-content").css("min-width", frameWidth + modalPaddings) + modalElem.find(".modal-title").text(frameWnd.document.title); + + // move the frame into the modal + modalFrame.detach(); + modalBody.append(modalFrame); + $("body").append(modalElem); + + // set the frame style + modalFrame.css({ + "width": "100%", + "height": frameHeight, + "position": "", + "opacity": 1.0 + }); // raise event on modal button click modalElem.find(".modal-footer button").click(function () { var result = $(this).data("result"); - var frameWnd = modalFrame[0].contentWindow; var frameJq = frameWnd.$; if (result && frameJq) { frameJq(frameWnd).trigger(scada.EventTypes.MODAL_BTN_CLICK, result); @@ -304,7 +320,6 @@ scada.Popup.prototype.showModal = function (url, opt_buttons, opt_callback) { modalElem .on('shown.bs.modal', function () { modalFrame.focus(); - modalElem.find(".modal-title").text(modalFrame[0].contentWindow.document.title); }) .on('hidden.bs.modal', function () { var callback = $(this).data("modal-callback"); From ef5867cf1cb25d7a3f0b90a27bdcd98316b3edeb Mon Sep 17 00:00:00 2001 From: 2mik Date: Thu, 21 Jul 2016 20:30:49 +0300 Subject: [PATCH 198/382] ScadaWeb5: fix popup --- ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js b/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js index da6577f4f..892a646e6 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js @@ -288,11 +288,10 @@ scada.Popup.prototype.showModal = function (url, opt_buttons, opt_callback) { var frameHeight = frameBody.outerHeight(true); // tune the modal - var frameWnd = modalFrame[0].contentWindow; var modalBody = modalElem.find(".modal-body"); var modalPaddings = parseInt(modalBody.css("padding-left")) + parseInt(modalBody.css("padding-right")); modalElem.find(".modal-content").css("min-width", frameWidth + modalPaddings) - modalElem.find(".modal-title").text(frameWnd.document.title); + modalElem.find(".modal-title").text(modalFrame[0].contentWindow.document.title); // move the frame into the modal modalFrame.detach(); @@ -310,6 +309,7 @@ scada.Popup.prototype.showModal = function (url, opt_buttons, opt_callback) { // raise event on modal button click modalElem.find(".modal-footer button").click(function () { var result = $(this).data("result"); + var frameWnd = modalFrame[0].contentWindow; var frameJq = frameWnd.$; if (result && frameJq) { frameJq(frameWnd).trigger(scada.EventTypes.MODAL_BTN_CLICK, result); From 44bdaddb0c8a627f87940d1c811bcf5286b32923 Mon Sep 17 00:00:00 2001 From: 2mik Date: Thu, 21 Jul 2016 21:12:54 +0300 Subject: [PATCH 199/382] ScadaWeb5: update csproj --- ScadaData/ScadaData/ScadaData.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScadaData/ScadaData/ScadaData.csproj b/ScadaData/ScadaData/ScadaData.csproj index 58da0d1e1..936f85c33 100644 --- a/ScadaData/ScadaData/ScadaData.csproj +++ b/ScadaData/ScadaData/ScadaData.csproj @@ -92,7 +92,7 @@ - + Designer From f5ce754555ea5883a1f848bfea687e75524e607e Mon Sep 17 00:00:00 2001 From: 2mik Date: Fri, 22 Jul 2016 10:16:29 +0300 Subject: [PATCH 200/382] ScadaWeb5: fixes for Mono .NET --- ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx | 2 +- ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs | 4 ++-- ScadaWeb/ScadaWebShell5Beta/About.aspx | 2 +- ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj | 1 + ScadaWeb/ScadaWebShell5Beta/View.aspx | 2 +- ScadaWeb/ScadaWebShell5Beta/index.htm | 8 ++++++++ ScadaWeb/ScadaWebShell5Beta/js/api/utils.js | 4 +++- 7 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 ScadaWeb/ScadaWebShell5Beta/index.htm diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx index 91d2ba07e..f3a25c8e9 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx @@ -65,7 +65,7 @@
+ CssClass="btn btn-danger" UseSubmitBehavior="False" Text='<%# Server.HtmlEncode((string)Eval("Text")) %>' data-cmdval='<%# Eval("Val") %>' />
diff --git a/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs b/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs index 7e49f0ec8..a120212f3 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs @@ -192,7 +192,7 @@ private BaseView GetViewFromCache(int viewID, UserRights userRights) ///
private HashSet GetCnlSet(string cnlNums, int viewID, UserRights userRights) { - if (!string.IsNullOrEmpty(cnlNums)) + if (!string.IsNullOrWhiteSpace(cnlNums)) { if (!userRights.ViewAllRight) throw new ScadaException(WebPhrases.NoRights); @@ -214,7 +214,7 @@ private HashSet GetCnlSet(string cnlNums, int viewID, UserRights userRights ///
private IList GetCnlList(string cnlNums, int viewID, UserRights userRights) { - if (!string.IsNullOrEmpty(cnlNums)) + if (!string.IsNullOrWhiteSpace(cnlNums)) { if (!userRights.ViewAllRight) throw new ScadaException(WebPhrases.NoRights); diff --git a/ScadaWeb/ScadaWebShell5Beta/About.aspx b/ScadaWeb/ScadaWebShell5Beta/About.aspx index 288f3224c..c4c38429a 100644 --- a/ScadaWeb/ScadaWebShell5Beta/About.aspx +++ b/ScadaWeb/ScadaWebShell5Beta/About.aspx @@ -5,6 +5,6 @@
Webstation - 4.6.1.0 Beta
+ 4.6.1.1 Beta
diff --git a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj index 3d107e31c..7780bed87 100644 --- a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj +++ b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj @@ -164,6 +164,7 @@ + diff --git a/ScadaWeb/ScadaWebShell5Beta/View.aspx b/ScadaWeb/ScadaWebShell5Beta/View.aspx index 88ec13231..a2587c62c 100644 --- a/ScadaWeb/ScadaWebShell5Beta/View.aspx +++ b/ScadaWeb/ScadaWebShell5Beta/View.aspx @@ -1,7 +1,7 @@ <%@ Page Title="View - Rapid SCADA" Language="C#" MasterPageFile="~/MasterMain.Master" AutoEventWireup="true" CodeBehind="View.aspx.cs" Inherits="Scada.Web.WFrmView" %> - + diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.cs b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.cs index 39204f6c0..dac343ef8 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.cs @@ -23,6 +23,7 @@ * Modified : 2016 */ +using Scada.Data.Models; using Scada.Scheme; using Scada.UI; using Scada.Web.Shell; @@ -41,6 +42,7 @@ public partial class WFrmScheme : System.Web.UI.Page protected int viewID; // ид. представления protected int refrRate; // частота обновления данных protected string phrases; // локализованные фразы + protected bool controlRight; // право на управление представлением protected void Page_Load(object sender, EventArgs e) { @@ -59,7 +61,9 @@ protected void Page_Load(object sender, EventArgs e) int.TryParse(Request.QueryString["viewID"], out viewID); // проверка прав на просмотр представления - if (!(userData.LoggedOn && userData.UserRights.GetViewRights(viewID).ViewRight)) + EntityRights rights = userData.LoggedOn ? + userData.UserRights.GetViewRights(viewID) : EntityRights.NoRights; + if (!rights.ViewRight) Response.Redirect(UrlTemplates.NoView); // загрузка представления в кеш, чтобы проверить, что оно доступно, присвоить метку @@ -73,10 +77,8 @@ protected void Page_Load(object sender, EventArgs e) // подготовка данных для вывода на веб-страницу refrRate = userData.WebSettings.DataRefrRate; - - Localization.Dict dict; - Localization.Dictionaries.TryGetValue("Scada.Web.Plugins.Scheme.WFrmScheme.Js", out dict); - phrases = WebUtils.DictionaryToJs(dict); + phrases = WebUtils.DictionaryToJs("Scada.Web.Plugins.Scheme.WFrmScheme.Js"); + controlRight = rights.ControlRight; } } } \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js index d1e05f02e..76ae50aec 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js @@ -8,6 +8,8 @@ var viewID = viewID || 0; var refrRate = refrRate || 1000; // Localized phrases var phrases = phrases || {}; +// View control right +var controlRight = controlRight || false; // Possible scale values var scaleVals = [0.1, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 2.5, 3, 4, 5]; @@ -30,7 +32,7 @@ function continueLoadingScheme(viewID) { $("body").removeClass("loading"); if (!DEBUG_MODE) { - scheme.createDom(); + scheme.createDom(controlRight); loadScale(); displayScale(); startUpdatingScheme(); diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js index 5cf8eb04b..0a78cbba6 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js @@ -428,8 +428,10 @@ scada.scheme.Scheme.prototype.load = function (viewID, callback) { }; // Create DOM content of the scheme elements -scada.scheme.Scheme.prototype.createDom = function () { +scada.scheme.Scheme.prototype.createDom = function (opt_controlRight) { this.renderContext.imageMap = this.imageMap; + this.renderContext.controlRight = typeof opt_controlRight === "undefined" ? + true : opt_controlRight; try { diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js index 7cc5a790b..f8caef87d 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js @@ -298,11 +298,15 @@ scada.scheme.ElementRenderer.prototype.prepareElem = function (jqObj, elem, opt_ }; // Bind user action to the element -scada.scheme.ElementRenderer.prototype.bindAction = function (jqObj, elem) { +scada.scheme.ElementRenderer.prototype.bindAction = function (jqObj, elem, controlRight) { var Actions = scada.scheme.Actions; var props = elem.props; + var action = props.Action; + var actionIsBound = + action == Actions.DRAW_DIAGRAM && props.InCnlNum > 0 || + action == Actions.SEND_COMMAND && props.CtrlCnlNum > 0 && controlRight; - if (props.Action) { + if (actionIsBound) { var viewHub = scada.scheme.viewHub; var dialogs = viewHub ? viewHub.dialogs : null; @@ -311,22 +315,18 @@ scada.scheme.ElementRenderer.prototype.bindAction = function (jqObj, elem) { .click(function () { switch (props.Action) { case Actions.DRAW_DIAGRAM: - if (props.InCnlNum > 0) { - if (dialogs) { - var date = viewHub.curViewDateMs ? new Date(viewHub.curViewDateMs) : new Date(); - dialogs.showChart(viewHub.curViewID, date, props.InCnlNum); - } else { - console.warn("Dialogs object is undefined"); - } + if (dialogs) { + var date = viewHub.curViewDateMs ? new Date(viewHub.curViewDateMs) : new Date(); + dialogs.showChart(viewHub.curViewID, date, props.InCnlNum); + } else { + console.warn("Dialogs object is undefined"); } break; case Actions.SEND_COMMAND: - if (props.CtrlCnlNum > 0) { - if (dialogs) { - dialogs.showCmd(viewHub.curViewID, props.CtrlCnlNum); - } else { - console.warn("Dialogs object is undefined"); - } + if (dialogs) { + dialogs.showCmd(viewHub.curViewID, props.CtrlCnlNum); + } else { + console.warn("Dialogs object is undefined"); } break; } @@ -408,7 +408,7 @@ scada.scheme.DynamicTextRenderer.prototype.createDom = function (elem, renderCon var spanText = elem.dom.children(); this.setToolTip(spanElem, props.ToolTip); - this.bindAction(spanElem, elem); + this.bindAction(spanElem, elem, renderContext.controlRight); // apply properties on hover var thisRenderer = this; @@ -534,7 +534,7 @@ scada.scheme.DynamicPictureRenderer.prototype.createDom = function (elem, render var divElem = elem.dom; this.setToolTip(divElem, props.ToolTip); - this.bindAction(divElem, elem); + this.bindAction(divElem, elem, renderContext.controlRight); // apply properties on hover var thisRenderer = this; @@ -599,6 +599,7 @@ scada.scheme.DynamicPictureRenderer.prototype.update = function (elem, renderCon scada.scheme.RenderContext = function () { this.curCnlDataMap = null; this.imageMap = null; + this.controlRight = true; }; // Get scheme image object by image property of an element diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs index 3c9e8746a..03d7472a9 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs @@ -77,15 +77,13 @@ protected void Page_Load(object sender, EventArgs e) // подготовка данных для вывода на веб-страницу dataRefrRate = userData.WebSettings.DataRefrRate; arcRefrRate = userData.WebSettings.ArcRefrRate; - dispEventCnt = userData.WebSettings.DispEventCnt; - viewAllRight = userData.UserRights.ViewAllRight; - - Localization.Dict dict; - Localization.Dictionaries.TryGetValue("Scada.Web.Plugins.Table.WFrmEvents.Js", out dict); - phrases = WebUtils.DictionaryToJs(dict); + phrases = WebUtils.DictionaryToJs("Scada.Web.Plugins.Table.WFrmEvents.Js"); DateTime nowDT = DateTime.Now; today = string.Format("new Date({0}, {1}, {2})", nowDT.Year, nowDT.Month - 1, nowDT.Day); + + dispEventCnt = userData.WebSettings.DispEventCnt; + viewAllRight = userData.UserRights.ViewAllRight; } } } \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs index 9f519bb4a..efb64e605 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs @@ -271,10 +271,7 @@ protected void Page_Load(object sender, EventArgs e) viewTitle = HttpUtility.JavaScriptStringEncode(tableView.Title); dataRefrRate = userData.WebSettings.DataRefrRate; arcRefrRate = userData.WebSettings.ArcRefrRate; - - Localization.Dict dict; - Localization.Dictionaries.TryGetValue("Scada.Web.Plugins.Table.WFrmTable.Js", out dict); - phrases = WebUtils.DictionaryToJs(dict); + phrases = WebUtils.DictionaryToJs("Scada.Web.Plugins.Table.WFrmTable.Js"); DateTime nowDT = DateTime.Now; today = string.Format("new Date({0}, {1}, {2})", nowDT.Year, nowDT.Month - 1, nowDT.Day); diff --git a/ScadaWeb/ScadaWebCommon5Beta/WebUtils.cs b/ScadaWeb/ScadaWebCommon5Beta/WebUtils.cs index bbb4b78b3..a8654f07b 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/WebUtils.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/WebUtils.cs @@ -179,6 +179,16 @@ public static string DictionaryToJs(Localization.Dict dict) return sbJs.ToString(); } + /// + /// Получить словарь по ключу и преобразовать в объект JavaScript + /// + public static string DictionaryToJs(string dictKey) + { + Localization.Dict dict; + Localization.Dictionaries.TryGetValue(dictKey, out dict); + return DictionaryToJs(dict); + } + /// /// Преобразовать дату в время в число миллисекунд для создания даты в JavaScript /// From fedee731d998d7a56b2e49c0092cc33b958c48b1 Mon Sep 17 00:00:00 2001 From: 2mik Date: Mon, 15 Aug 2016 16:43:59 +0300 Subject: [PATCH 211/382] PlgTable: optimize performance (client) --- .../PlgTable/plugins/Table/Table.aspx.cs | 31 +++++++++------- .../PlgTable/plugins/Table/css/table.css | 9 +++-- .../PlgTable/plugins/Table/css/table.less | 10 ++++-- .../PlgTable/plugins/Table/css/table.min.css | 2 +- .../PlgTable/plugins/Table/js/table.js | 36 ++++++++++--------- 5 files changed, 53 insertions(+), 35 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs index efb64e605..cb96204a7 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs @@ -104,15 +104,15 @@ private string GenerateTimeSelectHtml(string elemID, bool addPrevDay, int select /// /// Добавить ячейку в табличное представление /// - private void AppendCell(StringBuilder sbHtml, string cssClass, int? hour, string innerHtml) + private void AppendCell(StringBuilder sbHtml, string cssClass, string innerHtml, string hour = null) { sbHtml.Append("").Append(innerHtml).Append(""); } @@ -138,17 +138,21 @@ private string GenerateTableViewHtml(TableView tableView, bool cmdEnabled, int t const int FirstHour = -24; const int LastHour = 23; + string[] hourStrings = new string[LastHour - FirstHour + 1]; + for (int hour = FirstHour, hourInd = 0; hour <= LastHour; hour++, hourInd++) + hourStrings[hourInd] = hour.ToString(); + StringBuilder sbHtml = new StringBuilder(); sbHtml.AppendLine(""); // заголовок таблицы sbHtml.AppendLine(""); - AppendCell(sbHtml, "cap", null, "" + PlgPhrases.ItemCol + ""); - AppendCell(sbHtml, "cur", null, "" + PlgPhrases.CurCol + ""); - for (int hour = FirstHour; hour <= LastHour; hour++) + AppendCell(sbHtml, "cap", "" + PlgPhrases.ItemCol + ""); + AppendCell(sbHtml, "cur", "" + PlgPhrases.CurCol + ""); + for (int hour = FirstHour, hourInd = 0; hour <= LastHour; hour++, hourInd++) { - AppendCell(sbHtml, timeFrom <= hour && hour <= timeTo ? "hour" : "hour hidden", hour, - "" + GetLocalizedHour(hour) + ""); + AppendCell(sbHtml, timeFrom <= hour && hour <= timeTo ? "hour" : "hour hidden", + "" + GetLocalizedHour(hour) + "", hourStrings[hourInd]); } sbHtml.AppendLine().AppendLine(""); @@ -205,17 +209,18 @@ private string GenerateTableViewHtml(TableView tableView, bool cmdEnabled, int t } sbCapHtml.Append(""); - AppendCell(sbHtml, "cap", null, sbCapHtml.ToString()); + AppendCell(sbHtml, "cap", sbCapHtml.ToString()); } else { - AppendCell(sbHtml, "cap", null, caption); + AppendCell(sbHtml, "cap", caption); } // ячейки текущих и часовых данных - AppendCell(sbHtml, "cur", null, ""); - for (int hour = FirstHour; hour <= LastHour; hour++) - AppendCell(sbHtml, timeFrom <= hour && hour <= timeTo ? "hour" : "hour hidden", hour, ""); + AppendCell(sbHtml, "cur", ""); + for (int hour = FirstHour, hourInd = 0; hour <= LastHour; hour++, hourInd++) + AppendCell(sbHtml, timeFrom <= hour && hour <= timeTo ? "hour" : "hour hidden", + "", hourStrings[hourInd]); // тег окончания строки sbHtml.AppendLine().AppendLine(""); diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css index 791446aec..f70e8f803 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css @@ -140,20 +140,25 @@ color: black; } #divTblWrapper td.cap { + overflow: hidden; padding: 2px 5px; } -#divTblWrapper td.cap img { +#divTblWrapper td.cap img.icon { width: 16px; height: 16px; margin: 0 5px 0 0; border: none; } -#divTblWrapper td.cap a { +#divTblWrapper td.cap a.lbl { + width: calc(100% - 21px); height: 18px; margin-right: 5px; display: inline-block; line-height: 18px; } +#divTblWrapper td.cap a.lbl.before-cmd { + width: calc(100% - 26px - 18px); +} #divTblWrapper td.cap span.cmd { display: inline-block; width: 18px; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.less b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.less index 2a85ef70e..949c52964 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.less +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.less @@ -14,23 +14,29 @@ } #divTblWrapper td.cap { + overflow: hidden; padding: 2px 5px; } -#divTblWrapper td.cap img { +#divTblWrapper td.cap img.icon { width: 16px; height: 16px; margin: 0 5px 0 0; border: none; } -#divTblWrapper td.cap a { +#divTblWrapper td.cap a.lbl { + width: calc(~"100% - 21px"); height: @cell-height; margin-right: 5px; display: inline-block; line-height: @cell-height; } +#divTblWrapper td.cap a.lbl.before-cmd { + width: calc(~"100% - 26px - "@cell-height); +} + #divTblWrapper td.cap span.cmd { display: inline-block; width: @cell-height; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css index e1cc29737..e48835219 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:13px;overflow:hidden;}.hidden{display:none;}.not-implemented{display:none !important;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a{color:#0073aa;text-decoration:none;}#divTblWrapper td a:hover{color:#00a0d2;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td.cap{padding:2px 5px;}#divTblWrapper td.cap img{width:16px;height:16px;margin:0 5px 0 0;border:none;}#divTblWrapper td.cap a{height:18px;margin-right:5px;display:inline-block;line-height:18px;}#divTblWrapper td.cap span.cmd{display:inline-block;width:18px;height:18px;cursor:pointer;color:#a00;font-size:15px;line-height:18px;text-align:center;}#divTblWrapper td.cap span.cmd:hover{color:#f00;}#divTblWrapper td.cap span.cmd::before{content:"";font-family:FontAwesome;}#divTblWrapper td.cap span.hint{background-color:#fff;border:1px solid #888;border-radius:5px;box-shadow:2px 2px 5px #aaa;color:#333;display:none;line-height:normal;padding:5px 10px;position:fixed;z-index:10;}#divTblWrapper td.cap span.hint.show{display:inline-block;}#divTblWrapper td.cur,#divTblWrapper td.hour{text-align:center;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:13px;overflow:hidden;}.hidden{display:none;}.not-implemented{display:none !important;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a{color:#0073aa;text-decoration:none;}#divTblWrapper td a:hover{color:#00a0d2;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td.cap{overflow:hidden;padding:2px 5px;}#divTblWrapper td.cap img.icon{width:16px;height:16px;margin:0 5px 0 0;border:none;}#divTblWrapper td.cap a.lbl{width:calc(100% - 21px);height:18px;margin-right:5px;display:inline-block;line-height:18px;}#divTblWrapper td.cap a.lbl.before-cmd{width:calc(100% - 26px - 18px);}#divTblWrapper td.cap span.cmd{display:inline-block;width:18px;height:18px;cursor:pointer;color:#a00;font-size:15px;line-height:18px;text-align:center;}#divTblWrapper td.cap span.cmd:hover{color:#f00;}#divTblWrapper td.cap span.cmd::before{content:"";font-family:FontAwesome;}#divTblWrapper td.cap span.hint{background-color:#fff;border:1px solid #888;border-radius:5px;box-shadow:2px 2px 5px #aaa;color:#333;display:none;line-height:normal;padding:5px 10px;position:fixed;z-index:10;}#divTblWrapper td.cap span.hint.show{display:inline-block;}#divTblWrapper td.cur,#divTblWrapper td.hour{text-align:center;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js index 6602befab..134e234d0 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js @@ -91,7 +91,7 @@ function initCurDataCells() { // copy data-cnl attributes from rows to the cells curDataCells.each(function () { - $(this).data("cnl", $(this).closest("tr.item").data("cnl")); + $(this).attr("data-cnl", $(this).closest("tr.item").data("cnl")); }); } @@ -104,13 +104,14 @@ function initHourDataCols() { } // get and arrange cells + // Note: cell.attr() significantly faster than cell.data() $("#divTblWrapper tr.item").each(function () { var row = $(this); var cnlNum = row.data("cnl"); row.find("td.hour").each(function () { var cell = $(this); - cell.data("cnl", cnlNum); - var hour = cell.data("hour"); + cell.attr("data-cnl", cnlNum); + var hour = cell.attr("data-hour"); // faster than cell.data("hour") hourDataCols[hour - firstHour].push(cell); }); }); @@ -165,15 +166,9 @@ function updateHourDataColVisibility() { }); } -// Set widths of the item links to fill cells -function setItemLinkWidths() { - var cellWidth = $("#divTblWrapper td.cap:first").width(); - $("#divTblWrapper td.cap").each(function () { - var cell = $(this); - cell.children("a.lbl").outerWidth(cellWidth - - cell.children("img.icon").outerWidth(true) - - cell.children("span.cmd").outerWidth(true)); - }); +// Tune style of the item links to fill cells +function styleItemLinks() { + $("#divTblWrapper td.cap span.cmd").prev("a.lbl").addClass("before-cmd"); } // Show hint associated with the icon @@ -233,13 +228,20 @@ function showCmd(clickedElem) { // Display the given data in the cell. // Returns true if the cell text has been changed function displayCellData(cell, cnlDataMap) { - var cnlNum = cell.data("cnl"); + var cnlNum = parseInt(cell.attr("data-cnl")); if (cnlNum) { var cnlData = cnlDataMap.get(cnlNum); - var text = cnlData ? cnlData.Text : ""; + var text = ""; + var color = ""; + + if (cnlData) { + text = cnlData.Text; + color = cnlData.Color; + } + var textChanged = cell.text() != text; - cell.text(text); // special characters will be encoded - cell.css("color", cnlData ? cnlData.Color : ""); + cell.text(text); // special characters are encoded + cell.css("color", color); return textChanged; } } @@ -358,7 +360,6 @@ $(document).ready(function () { setTitle(); styleIOS(); updateLayout(); - setItemLinkWidths(); initViewDate(); initHourLimits(); createHourPeriod(); @@ -366,6 +367,7 @@ $(document).ready(function () { initCurDataCells(); initHourDataCols(); updateHourDataColHdrText(); + styleItemLinks(); scada.tableHeader.create(); notifier = new scada.Notifier("#divNotif"); notifier.startClearingNotifications(); From 0e195285b7c1987f2357a6fad7ad0f159b15d22f Mon Sep 17 00:00:00 2001 From: 2mik Date: Tue, 16 Aug 2016 09:50:35 +0300 Subject: [PATCH 212/382] ScadaWeb5: hide panels on tablet by default --- ScadaWeb/ScadaWebShell5Beta/config/WebSettings.xml | 6 ++++-- ScadaWeb/ScadaWebShell5Beta/js/api/utils.js | 8 ++++++++ ScadaWeb/ScadaWebShell5Beta/js/mastermain.js | 2 +- ScadaWeb/ScadaWebShell5Beta/js/view.js | 2 +- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/ScadaWeb/ScadaWebShell5Beta/config/WebSettings.xml b/ScadaWeb/ScadaWebShell5Beta/config/WebSettings.xml index 6777ac9f8..c3860c4a1 100644 --- a/ScadaWeb/ScadaWebShell5Beta/config/WebSettings.xml +++ b/ScadaWeb/ScadaWebShell5Beta/config/WebSettings.xml @@ -14,12 +14,14 @@ <%= GenScriptPathsHtml() %> diff --git a/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js b/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js index 603b1ba7f..d86df3a94 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js @@ -12,7 +12,7 @@ * Requires for modal dialogs: * - bootstrap * - eventtypes.js - * - scada.modalButtonMap object + * - scada.modalButtonCaptions object */ // Rapid SCADA namespace @@ -84,23 +84,28 @@ scada.Popup.prototype._getOffset = function (elem) { return { left: left, top: top }; }; +// Get caption for the specified modal dialog button +scada.Popup.prototype._getModalButtonCaption = function (btn) { + var btnCaption = scada.modalButtonCaptions ? scada.modalButtonCaptions[btn] : null; + if (!btnCaption) { + btnCaption = btn; + } + return btnCaption; +} + // Get html markup of a modal dialog footer buttons scada.Popup.prototype._genModalButtonsHtml = function (buttons) { var html = ""; for (var btn of buttons) { - var btnText = scada.modalButtonMap ? scada.modalButtonMap.get(btn) : null; - if (!btnText) { - btnText = btn; - } - + var btnCaption = this._getModalButtonCaption(btn); var subclass = btn == scada.ModalButtons.OK || btn == scada.ModalButtons.YES ? "btn-primary" : (btn == scada.ModalButtons.EXEC ? "btn-danger" : "btn-default"); var dismiss = btn == scada.ModalButtons.CANCEL || btn == scada.ModalButtons.CLOSE ? " data-dismiss='modal'" : ""; html += ""; + "' data-result='" + btn + "'" + dismiss + ">" + btnCaption + ""; } return html; diff --git a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml index 82b8c8d5f..2da97c3a5 100644 --- a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml +++ b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml @@ -18,7 +18,7 @@ Views Collapse menu - + OK Yes No diff --git a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml index d698ee5db..0206e02d8 100644 --- a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml +++ b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml @@ -18,7 +18,7 @@ Представления Свернуть меню - + OK Да Нет From 21783e6241fc30f92f224a476615b880de8aa844 Mon Sep 17 00:00:00 2001 From: 2mik Date: Fri, 9 Sep 2016 11:38:45 +0300 Subject: [PATCH 255/382] ScadaWeb: use ToLocalizedDateString() --- ScadaData/ScadaData/Client/DataCache.cs | 6 +-- ScadaData/ScadaData/Localization.cs | 17 +++++++ .../PlgChart/plugins/Chart/Chart.aspx.cs | 3 +- .../PlgChartCommon/ChartDataBuilder.cs | 48 +++++++++---------- ScadaWeb/ScadaWebShell5Beta/Calendar.aspx.cs | 2 +- 5 files changed, 46 insertions(+), 30 deletions(-) diff --git a/ScadaData/ScadaData/Client/DataCache.cs b/ScadaData/ScadaData/Client/DataCache.cs index 602029119..bf5dcfc05 100644 --- a/ScadaData/ScadaData/Client/DataCache.cs +++ b/ScadaData/ScadaData/Client/DataCache.cs @@ -627,7 +627,7 @@ public SrezTableLight GetHourTable(DateTime date) log.WriteException(ex, Localization.UseRussian ? "Ошибка при получении таблицы часовых данных за {0} из кэша или от сервера" : "Error getting hourly data table for {0} from the cache or from the server", - date.ToString("d", Localization.Culture)); + date.ToLocalizedDateString()); return new SrezTableLight(); } } @@ -699,7 +699,7 @@ public EventTableLight GetEventTable(DateTime date) log.WriteException(ex, Localization.UseRussian ? "Ошибка при получении таблицы событий за {0} из кэша или от сервера" : "Error getting event table for {0} from the cache or from the server", - date.ToString("d", Localization.Culture)); + date.ToLocalizedDateString()); return new EventTableLight(); } } @@ -731,7 +731,7 @@ public Trend GetMinTrend(DateTime date, int cnlNum) { log.WriteException(ex, Localization.UseRussian ? "Ошибка при получении тренда минутных данных за {0}" : - "Error getting minute data trend for {0}", date.ToString("d", Localization.Culture)); + "Error getting minute data trend for {0}", date.ToLocalizedDateString()); } return trend; diff --git a/ScadaData/ScadaData/Localization.cs b/ScadaData/ScadaData/Localization.cs index 3982cdc8f..ba1037b6a 100644 --- a/ScadaData/ScadaData/Localization.cs +++ b/ScadaData/ScadaData/Localization.cs @@ -344,6 +344,7 @@ public static bool LoadingRequired(string directory, string fileNamePrefix) return !UseRussian || File.Exists(GetDictionaryFileName(directory, fileNamePrefix)); } + /// /// Преобразовать дату и время в строку в соответствии с культурой SCADA /// @@ -351,5 +352,21 @@ public static string ToLocalizedString(this DateTime dateTime) { return dateTime.ToString("d", Culture) + " " + dateTime.ToString("T", Culture); } + + /// + /// Преобразовать дату в строку в соответствии с культурой SCADA + /// + public static string ToLocalizedDateString(this DateTime dateTime) + { + return dateTime.ToString("d", Culture); + } + + /// + /// Преобразовать время в строку в соответствии с культурой SCADA + /// + public static string ToLocalizedTimeString(this DateTime dateTime) + { + return dateTime.ToString("T", Culture); + } } } \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/Chart.aspx.cs b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/Chart.aspx.cs index 92f113054..022b33e71 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/Chart.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/Chart.aspx.cs @@ -80,8 +80,7 @@ protected void Page_Load(object sender, EventArgs e) #endif // вывод дополнительной информации - lblStartDate.Text = (string.IsNullOrEmpty(lblTitle.Text) ? "" : ", ") + - startDate.ToString("d", Localization.Culture); + lblStartDate.Text = (string.IsNullOrEmpty(lblTitle.Text) ? "" : ", ") + startDate.ToLocalizedDateString(); lblGenDT.Text = DateTime.Now.ToLocalizedString(); // подготовка данных графика diff --git a/ScadaWeb/OpenPlugins/PlgChartCommon/ChartDataBuilder.cs b/ScadaWeb/OpenPlugins/PlgChartCommon/ChartDataBuilder.cs index 376d47525..de5cf522f 100644 --- a/ScadaWeb/OpenPlugins/PlgChartCommon/ChartDataBuilder.cs +++ b/ScadaWeb/OpenPlugins/PlgChartCommon/ChartDataBuilder.cs @@ -112,7 +112,7 @@ public ChartDataBuilder(int[] cnlNums, DateTime startDate, int period, int chart this.startDate = startDate; this.period = period; this.chartGap = chartGap; - NormalizeTimeRange(); + NormalizeTimeRange(ref this.startDate, ref this.period); cnlCnt = cnlNums.Length; cnlPropsArr = new InCnlProps[cnlCnt]; @@ -146,29 +146,6 @@ public int Period } - /// - /// Нормализовать интервал времени - /// - /// Чтобы начальная дата являлась левой границей интервала времени и период был положительным - protected void NormalizeTimeRange() - { - // Примеры: - // период равный -1, 0 или 1 - это одни сутки startDate, - // период 2 - двое суток, начиная от startDate включительно, - // период -2 - двое суток, заканчивая startDate включительно - if (period > -2) - { - startDate = startDate.Date; - if (period < 1) - period = 1; - } - else - { - startDate = startDate.AddDays(period + 1).Date; - period = -period; - } - } - /// /// Получить имя величины с указанием размерности /// @@ -424,5 +401,28 @@ public string ToJs() return sbJs.ToString(); } + + /// + /// Нормализовать интервал времени + /// + /// Чтобы начальная дата являлась левой границей интервала времени и период был положительным + public static void NormalizeTimeRange(ref DateTime startDate, ref int period) + { + // Примеры: + // период равный -1, 0 или 1 - это одни сутки startDate, + // период 2 - двое суток, начиная от startDate включительно, + // период -2 - двое суток, заканчивая startDate включительно + if (period > -2) + { + startDate = startDate.Date; + if (period < 1) + period = 1; + } + else + { + startDate = startDate.AddDays(period + 1).Date; + period = -period; + } + } } } diff --git a/ScadaWeb/ScadaWebShell5Beta/Calendar.aspx.cs b/ScadaWeb/ScadaWebShell5Beta/Calendar.aspx.cs index e76e0eec8..430739834 100644 --- a/ScadaWeb/ScadaWebShell5Beta/Calendar.aspx.cs +++ b/ScadaWeb/ScadaWebShell5Beta/Calendar.aspx.cs @@ -58,7 +58,7 @@ protected void Calendar_DayRender(object sender, DayRenderEventArgs e) { DateTime date = e.Day.Date; e.Cell.Text = string.Format("{2}", - date.Year, date.Month, date.Day, date.ToString("d", Localization.Culture)); + date.Year, date.Month, date.Day, date.ToLocalizedDateString()); } } } \ No newline at end of file From c4f73c26e4e05b265c3a3208c4c64fccc506ae76 Mon Sep 17 00:00:00 2001 From: 2mik Date: Fri, 9 Sep 2016 12:09:11 +0300 Subject: [PATCH 256/382] ScadaWeb: refactor date parsing --- ScadaData/ScadaData/ScadaUtils.cs | 8 ++++++++ ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs | 2 +- ScadaWeb/ScadaWebShell5Beta/Calendar.aspx.cs | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/ScadaData/ScadaData/ScadaUtils.cs b/ScadaData/ScadaData/ScadaUtils.cs index 5d6f9d3ca..95c5b89c4 100644 --- a/ScadaData/ScadaData/ScadaUtils.cs +++ b/ScadaData/ScadaData/ScadaUtils.cs @@ -150,6 +150,14 @@ public static DateTime StrToDate(string s) return DateTime.MinValue; } + /// + /// Попытаться преобразовать строку в дату и время, используя Localization.Culture + /// + public static bool TryParseDateTime(string s, out DateTime result) + { + return DateTime.TryParse(s, Localization.Culture, DateTimeStyles.None, out result); + } + /// /// Преобразовать строку в вещественное число /// diff --git a/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs b/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs index e1fd2b7e3..eb60c3715 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs @@ -642,7 +642,7 @@ public string ParseDateTime(string s) { AppData.CheckLoggedOn(); DateTime dateTime; - object data = DateTime.TryParse(s, Localization.Culture, DateTimeStyles.None, out dateTime) ? + object data = ScadaUtils.TryParseDateTime(s, out dateTime) ? (object)WebUtils.DateTimeToJs(dateTime) : null; return JsSerializer.Serialize(new DataTransferObject(data)); } diff --git a/ScadaWeb/ScadaWebShell5Beta/Calendar.aspx.cs b/ScadaWeb/ScadaWebShell5Beta/Calendar.aspx.cs index 430739834..a7abb4559 100644 --- a/ScadaWeb/ScadaWebShell5Beta/Calendar.aspx.cs +++ b/ScadaWeb/ScadaWebShell5Beta/Calendar.aspx.cs @@ -47,7 +47,7 @@ protected void Page_Load(object sender, EventArgs e) { // установка выбранной даты, если она задана в параметрах запроса DateTime date; - if (DateTime.TryParse(Request.QueryString["date"], Localization.Culture, DateTimeStyles.None, out date)) + if (ScadaUtils.TryParseDateTime(Request.QueryString["date"], out date)) Calendar.VisibleDate = Calendar.SelectedDate = date; // убрать всплывающую подсказку по умолчанию From 01deff006b9b046e194f19f53c26eb68734ffdb3 Mon Sep 17 00:00:00 2001 From: 2mik Date: Sat, 10 Sep 2016 13:27:11 +0300 Subject: [PATCH 257/382] ScadaWeb: minor fix --- ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js b/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js index d86df3a94..13f3422d1 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js @@ -344,7 +344,7 @@ scada.Popup.prototype.showModal = function (url, opt_buttons, opt_callback) { .attr("src", url); }; -// Show the modal dialog +// Close the modal dialog scada.Popup.prototype.closeModal = function (modalWnd, dialogResult, extraParams) { this.setModalResult(modalWnd, dialogResult, extraParams).modal("hide"); } From 0a5eb011cf6b40e9f25a189fa1223cb977421105 Mon Sep 17 00:00:00 2001 From: 2mik Date: Sat, 10 Sep 2016 20:45:38 +0300 Subject: [PATCH 258/382] ScadaWeb: refactor dialog objects --- .../PlgChart/AppCode/PlgChartSpec.cs | 2 +- ScadaWeb/OpenPlugins/PlgChart/PlgChart.csproj | 2 +- .../Chart/js/{chartobj.js => chartdialog.js} | 4 ++- ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj | 4 +-- .../Table/js/{cmdobj.js => cmddialog.js} | 4 ++- .../js/{eventackobj.js => eventackdialog.js} | 4 ++- .../ScadaWebShell5Beta/MasterMain.Master.cs | 24 ------------------ .../ScadaWebShell5Beta/config/WebSettings.xml | 6 ++--- .../ScadaWebShell5Beta/js/api/clientapi.js | 1 - ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js | 25 +++++++++---------- 10 files changed, 28 insertions(+), 48 deletions(-) rename ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/js/{chartobj.js => chartdialog.js} (88%) rename ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/{cmdobj.js => cmddialog.js} (90%) rename ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/{eventackobj.js => eventackdialog.js} (88%) diff --git a/ScadaWeb/OpenPlugins/PlgChart/AppCode/PlgChartSpec.cs b/ScadaWeb/OpenPlugins/PlgChart/AppCode/PlgChartSpec.cs index 8b33400e3..bc552ad36 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/AppCode/PlgChartSpec.cs +++ b/ScadaWeb/OpenPlugins/PlgChart/AppCode/PlgChartSpec.cs @@ -91,7 +91,7 @@ public override ScriptPaths ScriptPaths { get { - return new ScriptPaths() { ChartScriptPath = "~/plugins/Chart/js/chartobj.js" }; + return new ScriptPaths() { ChartScriptPath = "~/plugins/Chart/js/chartdialog.js" }; } } diff --git a/ScadaWeb/OpenPlugins/PlgChart/PlgChart.csproj b/ScadaWeb/OpenPlugins/PlgChart/PlgChart.csproj index cb2348d1a..2c4d7c0f8 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/PlgChart.csproj +++ b/ScadaWeb/OpenPlugins/PlgChart/PlgChart.csproj @@ -75,7 +75,7 @@ - + diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/js/chartobj.js b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/js/chartdialog.js similarity index 88% rename from ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/js/chartobj.js rename to ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/js/chartdialog.js index 91796bdaf..296095d32 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/js/chartobj.js +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/js/chartdialog.js @@ -11,8 +11,10 @@ // Rapid SCADA namespace var scada = scada || {}; +// Chart namespace +scada.chart = scada.chart || {}; -scada.chart = { +scada.chart.dialog = { // Get chart URL getChartUrl: function (cnlNums, viewIDs, date) { return "plugins/Chart/Chart.aspx?cnlNum=" + cnlNums + "&viewID=" + viewIDs + diff --git a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj index 454b88fbe..e805af9b2 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj +++ b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj @@ -124,10 +124,10 @@ - + - + diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/cmdobj.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/cmddialog.js similarity index 90% rename from ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/cmdobj.js rename to ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/cmddialog.js index bd452e664..af64a3e05 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/cmdobj.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/cmddialog.js @@ -14,8 +14,10 @@ // Rapid SCADA namespace var scada = scada || {}; +// Command namespace +scada.cmd = scada.cmd || {}; -scada.cmd = { +scada.cmd.dialog = { // Show command dialog show: function (rootPath, ctrlCnlNum, viewID, opt_callback) { var popup = scada.popupLocator.getPopup(); diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/eventackobj.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/eventackdialog.js similarity index 88% rename from ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/eventackobj.js rename to ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/eventackdialog.js index 420558420..3828d577f 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/eventackobj.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/eventackdialog.js @@ -14,8 +14,10 @@ // Rapid SCADA namespace var scada = scada || {}; +// Event acknowledgement namespace +scada.eventAck = scada.eventAck || {}; -scada.eventAck = { +scada.eventAck.dialog = { // Show event acknowledgement dialog show: function (rootPath, date, evNum, viewID, opt_callback) { var popup = scada.popupLocator.getPopup(); diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs index ee9836a40..434794149 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs @@ -53,30 +53,6 @@ public partial class MasterMain : System.Web.UI.MasterPage public int SelectedViewID = 0; - /// - /// Генерировать JavaScript-код карты кнопок для модальных диалогов - /// - protected string GenModalButtonMapJs() - { - StringBuilder sbJs = new StringBuilder(); - sbJs.AppendLine("new Map(["); - - Localization.Dict dict; - Localization.Dictionaries.TryGetValue("Scada.Web.MasterMain.Js.ModalButtons", out dict); - - if (dict != null) - { - foreach (KeyValuePair pair in dict.Phrases) - { - sbJs.Append("[\"").Append(pair.Key).Append("\", \"") - .Append(HttpUtility.JavaScriptStringEncode(pair.Value)).AppendLine("\"],"); - } - } - - sbJs.Append("])"); - return sbJs.ToString(); - } - /// /// Генерировать HTML-код дополнительных скриптов /// diff --git a/ScadaWeb/ScadaWebShell5Beta/config/WebSettings.xml b/ScadaWeb/ScadaWebShell5Beta/config/WebSettings.xml index c3860c4a1..81534254b 100644 --- a/ScadaWeb/ScadaWebShell5Beta/config/WebSettings.xml +++ b/ScadaWeb/ScadaWebShell5Beta/config/WebSettings.xml @@ -13,9 +13,9 @@ - + + + + + + + + + + + + + + diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/SelectCnls.aspx.cs b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/SelectCnls.aspx.cs new file mode 100644 index 000000000..de8988235 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/SelectCnls.aspx.cs @@ -0,0 +1,21 @@ +using System; + +namespace Scada.Web.Plugins.Chart +{ + public partial class WFrmSelectCnls : System.Web.UI.Page + { + protected void Page_Load(object sender, EventArgs e) + { + + } + + protected void btnSubmit_Click(object sender, EventArgs e) + { + // завершить выбор каналов + string cnlNums = txtCnls.Text; + string viewIDs = txtViewIDs.Text; + ClientScript.RegisterStartupScript(GetType(), "CloseModalScript", + string.Format("closeModal('{0}', '{1}');", cnlNums, viewIDs), true); + } + } +} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/SelectCnls.aspx.designer.cs b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/SelectCnls.aspx.designer.cs new file mode 100644 index 000000000..14a9342e3 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/SelectCnls.aspx.designer.cs @@ -0,0 +1,51 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Scada.Web.Plugins.Chart { + + + public partial class WFrmSelectCnls { + + /// + /// frmSelectCnls control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlForm frmSelectCnls; + + /// + /// btnSubmit control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button btnSubmit; + + /// + /// txtCnls control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox txtCnls; + + /// + /// txtViewIDs control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox txtViewIDs; + } +} diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/mindatarep.css b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/mindatarep.css index d3f9ff837..9aca581a5 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/mindatarep.css +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/mindatarep.css @@ -1,3 +1,47 @@ -.form-group.period { +.main-content { + padding: 20px; + font-size: 14px; +} +h1 { + font-size: 23px; +} +h2 { + font-size: 17px; +} +h3 { + font-size: 14px; +} +h1, +h2, +h3 { + font-weight: 500; + margin: 20px 0 10px; +} +h1:first-of-type { + margin-top: 0; +} +a, +a:active, +a:hover, +a:focus, +a:visited { + color: #0073aa; + outline: 0; + text-decoration: none; +} +a:hover { + color: #00a0d2; +} +label { + font-weight: 600; +} +.form-group.period { max-width: 300px; +} +.popovers-container { + position: relative; +} +#lblGenStarted { + display: block; + margin-top: 5px; } \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/mindatarep.less b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/mindatarep.less index c54883398..f665f98bc 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/mindatarep.less +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/mindatarep.less @@ -1,3 +1,14 @@ -.form-group.period { +@import "../../../css/common/contentform.less"; + +.form-group.period { max-width: 300px; +} + +.popovers-container { + position: relative; +} + +#lblGenStarted { + display: block; + margin-top: 5px; } \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/mindatarep.min.css b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/mindatarep.min.css index b6cffd286..65e46d0b6 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/mindatarep.min.css +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/mindatarep.min.css @@ -1 +1 @@ -.form-group.period{max-width:300px;} \ No newline at end of file +.main-content{padding:20px;font-size:14px;}h1{font-size:23px;}h2{font-size:17px;}h3{font-size:14px;}h1,h2,h3{font-weight:500;margin:20px 0 10px;}h1:first-of-type{margin-top:0;}a,a:active,a:hover,a:focus,a:visited{color:#0073aa;outline:0;text-decoration:none;}a:hover{color:#00a0d2;}label{font-weight:600;}.form-group.period{max-width:300px;}.popovers-container{position:relative;}#lblGenStarted{display:block;margin-top:5px;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/js/mindatarep.js b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/js/mindatarep.js index 5f282702b..b2a6a2b9d 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/js/mindatarep.js +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/js/mindatarep.js @@ -1 +1,80 @@ - \ No newline at end of file +// Duration of locking the generate report button, ms +var GenBtnLockDuration = 3000; + +// Select begin or end date using a calendar popup +function selectDate(inputElem, buttonElem) { + scada.dialogs.showCalendar(buttonElem, inputElem.val(), function (dialogResult, extraParams) { + if (dialogResult) { + inputElem.val(extraParams.dateStr); + } + }); +} + +// Show modal dialog to select the channels +function showSelectCnlsModal() { + popup.showModal("SelectCnls.aspx", + new scada.ModalOptions([scada.ModalButtons.OK, scada.ModalButtons.CANCEL]), + function (dialogResult, extraParams) { + if (dialogResult) { + // perform adding channels + if (extraParams.cnlNums && extraParams.viewIDs) { + $("#hidAddedCnlNums").val(extraParams.cnlNums); + $("#hidAddedViewIDs").val(extraParams.viewIDs); + $("#btnApplyAddedCnls").click(); + } + } + }); +} + +// Start report generation +function generateReport(cnlNums, viewIDs, year, month, day, period) { + lockGenerateButton(); + window.location = "MinDataRepOut.aspx?cnlNums=" + cnlNums + "&viewIDs=" + viewIDs + + "&year=" + year + "&month=" + month + "&day=" + day + "&period=" + period; +} + +// Lock the generate report button after click +function lockGenerateButton() { + $("#btnGenReport").prop("disabled", true); + $("#lblGenStarted").removeClass("hidden"); + + setTimeout(function () { + $("#btnGenReport").prop("disabled", false); + $("#lblGenStarted").addClass("hidden"); + }, GenBtnLockDuration) +} + +// Initialize Bootstrap popovers +function initPopovers() { + $('[data-toggle="popover"]').popover({ html: true }); +} + +// Bind the form events +function bindEvents() { + // open a calendar popup on a calendar button click + $("button.calendar") + .off("click") + .on("click", function () { + selectDate($(this).parent().siblings("input"), $(this)); + }); + + // open a select channels popup on the appropriate button click + $("#btnAddCnls") + .off("click") + .on("click", function () { + showSelectCnlsModal(); + return false; + }); +} + +// Do actions after asynchronous request +function asyncEndRequest() { + initPopovers(); + bindEvents(); + scada.utils.scrollTo($(".main-content:first"), $(".alert")); +} + +$(document).ready(function () { + initPopovers(); + bindEvents(); +}); \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/js/selectcnls.js b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/js/selectcnls.js new file mode 100644 index 000000000..918e30eef --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/js/selectcnls.js @@ -0,0 +1,26 @@ +// Close the modal with successful result +function closeModal(cnlNums, viewIDs) { + var popup = scada.popupLocator.getPopup(); + if (popup) { + popup.closeModal(window, true, { cnlNums: cnlNums, viewIDs: viewIDs }); + } +} + +$(document).ready(function () { + // initialize Bootstrap popovers + $('[data-toggle="popover"]').popover({ html: true }); + + // submit the form on OK button click + $(window).on(scada.EventTypes.MODAL_BTN_CLICK, function (event, result) { + if (result == scada.ModalButtons.OK) { + $("#btnSubmit").click(); + } + }); + + // show "loading" message on change a view + /*$("#ddlView").change(function () { + $("#pnlCnlsByView").addClass("hidden"); + $(".list-msg").addClass("hidden"); + $("#lblLoading").removeClass("hidden"); + });*/ +}); \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/lang/PlgChart.en-GB.xml b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/lang/PlgChart.en-GB.xml index 72dad8070..aa073cf91 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/lang/PlgChart.en-GB.xml +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/lang/PlgChart.en-GB.xml @@ -1,5 +1,19 @@  + + Incorrect start date. + Incorrect end date. + Start date must be less or equal than end date. + The maximum period length is {0} days. + Channel numbers are not specified. + Mismatch in number of channels and view IDs. + Add + Remove + Info + Object: + Device: + View: + Minute data Generated: diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/lang/PlgChart.ru-RU.xml b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/lang/PlgChart.ru-RU.xml index 600c2d7c3..68a38b92a 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/lang/PlgChart.ru-RU.xml +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/lang/PlgChart.ru-RU.xml @@ -1,5 +1,19 @@  + + Некорректная дата начала. + Некорректная дата окончания. + Дата начала должна быть меньше или равна дате окончания. + Максимальная длина периода {0} день. + Номера каналов не заданы. + Несоответствие количества каналов и ид. представлений. + Добавить + Удалить + Инфо + Объект: + КП: + Представление: + Минутные данные Получено: diff --git a/ScadaWeb/OpenPlugins/PlgChartCommon/ChartPhrases.cs b/ScadaWeb/OpenPlugins/PlgChartCommon/ChartPhrases.cs index dbd7e14b2..e9d643e78 100644 --- a/ScadaWeb/OpenPlugins/PlgChartCommon/ChartPhrases.cs +++ b/ScadaWeb/OpenPlugins/PlgChartCommon/ChartPhrases.cs @@ -39,6 +39,12 @@ static ChartPhrases() } // Словарь Scada.Web.Plugins.Chart + public static string IncorrectStartDate { get; private set; } + public static string IncorrectEndDate { get; private set; } + public static string IncorrectPeriod { get; private set; } + public static string PeriodTooLong { get; private set; } + public static string CnlNumsEmptyError { get; private set; } + public static string CountMismatchError { get; private set; } public static string AddCnlBtn { get; private set; } public static string RemoveCnlBtn { get; private set; } public static string CnlInfoBtn { get; private set; } @@ -48,6 +54,12 @@ static ChartPhrases() private static void SetToDefault() { + IncorrectStartDate = Localization.Dict.GetEmptyPhrase("IncorrectStartDate"); + IncorrectEndDate = Localization.Dict.GetEmptyPhrase("IncorrectEndDate"); + IncorrectPeriod = Localization.Dict.GetEmptyPhrase("IncorrectPeriod"); + PeriodTooLong = Localization.Dict.GetEmptyPhrase("PeriodTooLong"); + CnlNumsEmptyError = Localization.Dict.GetEmptyPhrase("CnlNumsEmptyError"); + CountMismatchError = Localization.Dict.GetEmptyPhrase("CountMismatchError"); AddCnlBtn = Localization.Dict.GetEmptyPhrase("AddCnlBtn"); RemoveCnlBtn = Localization.Dict.GetEmptyPhrase("RemoveCnlBtn"); CnlInfoBtn = Localization.Dict.GetEmptyPhrase("CnlInfoBtn"); @@ -61,6 +73,12 @@ public static void Init() Localization.Dict dict; if (Localization.Dictionaries.TryGetValue("Scada.Web.Plugins.Chart", out dict)) { + IncorrectStartDate = dict.GetPhrase("IncorrectStartDate", IncorrectStartDate); + IncorrectEndDate = dict.GetPhrase("IncorrectEndDate", IncorrectEndDate); + IncorrectPeriod = dict.GetPhrase("IncorrectPeriod", IncorrectPeriod); + PeriodTooLong = dict.GetPhrase("PeriodTooLong", PeriodTooLong); + CnlNumsEmptyError = dict.GetPhrase("CnlNumsEmptyError", CnlNumsEmptyError); + CountMismatchError = dict.GetPhrase("CountMismatchError", CountMismatchError); AddCnlBtn = dict.GetPhrase("AddCnlBtn", AddCnlBtn); RemoveCnlBtn = dict.GetPhrase("RemoveCnlBtn", RemoveCnlBtn); CnlInfoBtn = dict.GetPhrase("CnlInfoBtn", CnlInfoBtn); diff --git a/ScadaWeb/OpenPlugins/PlgChartCommon/ChartUtils.cs b/ScadaWeb/OpenPlugins/PlgChartCommon/ChartUtils.cs index 262f3e2ff..f374b3eda 100644 --- a/ScadaWeb/OpenPlugins/PlgChartCommon/ChartUtils.cs +++ b/ScadaWeb/OpenPlugins/PlgChartCommon/ChartUtils.cs @@ -25,8 +25,8 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Text; +using System.Web.UI.WebControls; namespace Scada.Web.Plugins.Chart { @@ -37,25 +37,64 @@ namespace Scada.Web.Plugins.Chart public static class ChartUtils { /// - /// Проверить корректность заданных массивов + /// Макс. длина периода, дн. /// - public static void CheckArrays(int[] cnlNums, int[] viewIDs) + public const int MaxPeriodLength = 31; + + /// + /// Количество графиков, выше которого могут быть проблемы с производительностью + /// + public const int NormalChartCnt = 10; + + + /// + /// Распознать даты, введённые пользователем, и вернуть сообщение в случае ошибки + /// + public static bool ParseDates(TextBox txtDateFrom, TextBox txtDateTo, + out DateTime dateFrom, out DateTime dateTo, out string errMsg) { - if (cnlNums == null) - throw new ArgumentNullException("cnlNums"); + dateFrom = DateTime.MinValue; + dateTo = DateTime.MinValue; - if (viewIDs == null) - throw new ArgumentNullException("viewIDs"); + if (!ScadaUtils.TryParseDateTime(txtDateFrom.Text, out dateFrom)) + { + errMsg = ChartPhrases.IncorrectStartDate; + return false; + } + else if (!ScadaUtils.TryParseDateTime(txtDateTo.Text, out dateTo)) + { + errMsg = ChartPhrases.IncorrectEndDate; + return false; + } + else + { + errMsg = ""; + return true; + } + } - if (cnlNums.Length == 0) - throw new ArgumentException(Localization.UseRussian ? - "Номера каналов не заданы." : - "Channel numbers are not specified."); + /// + /// Проверить даты, введённые пользователем, и вернуть сообщение в случае ошибки + /// + public static bool CheckDates(DateTime dateFrom, DateTime dateTo, out int period, out string errMsg) + { + period = (int)(dateTo - dateFrom).TotalDays + 1; - if (cnlNums.Length != viewIDs.Length) - throw new ScadaException(Localization.UseRussian ? - "Несоответствие количества каналов и ид. представлений." : - "Mismatch in number of channels and view IDs."); + if (dateFrom > dateTo) + { + errMsg = ChartPhrases.IncorrectPeriod; + return false; + } + else if (period > MaxPeriodLength) + { + errMsg = string.Format(ChartPhrases.PeriodTooLong, MaxPeriodLength); + return false; + } + else + { + errMsg = ""; + return true; + } } /// @@ -80,5 +119,48 @@ public static void NormalizeTimeRange(ref DateTime startDate, ref int period) period = -period; } } + + /// + /// Проверить корректность заданных массивов + /// + public static void CheckArrays(int[] cnlNums, int[] viewIDs) + { + if (cnlNums == null) + throw new ArgumentNullException("cnlNums"); + + if (viewIDs == null) + throw new ArgumentNullException("viewIDs"); + + if (cnlNums.Length == 0) + throw new ArgumentException(ChartPhrases.CnlNumsEmptyError); + + if (cnlNums.Length != viewIDs.Length) + throw new ScadaException(ChartPhrases.CountMismatchError); + } + + /// + /// Получить выбранные каналы и соответствующие им представления + /// + public static void GetSelection(List selectedCnls, out string cnlNums, out string viewIDs) + { + StringBuilder sbCnlNums = new StringBuilder(); + StringBuilder sbViewIDs = new StringBuilder(); + + for (int i = 0, lastInd = selectedCnls.Count - 1; i <= lastInd; i++) + { + CnlViewPair pair = selectedCnls[i]; + sbCnlNums.Append(pair.CnlNum); + sbViewIDs.Append(pair.ViewID); + + if (i < lastInd) + { + sbCnlNums.Append(","); + sbViewIDs.Append(","); + } + } + + cnlNums = sbCnlNums.ToString(); + viewIDs = sbViewIDs.ToString(); + } } } diff --git a/ScadaWeb/OpenPlugins/PlgChartCommon/CnlViewPair.cs b/ScadaWeb/OpenPlugins/PlgChartCommon/CnlViewPair.cs new file mode 100644 index 000000000..f966e97e4 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgChartCommon/CnlViewPair.cs @@ -0,0 +1,114 @@ +/* + * Copyright 2016 Mikhail Shiryaev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Product : Rapid SCADA + * Module : PlgChartCommon + * Summary : Defines a input channel/view pair + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + */ + +using Scada.Data.Models; +using Scada.Web.Shell; +using System; +using System.Text; + +namespace Scada.Web.Plugins.Chart +{ + /// + /// Defines a input channel/view pair + /// Определяет пару входной канал/представление + /// + [Serializable] + public class CnlViewPair + { + /// + /// Конструктор + /// + public CnlViewPair() + : this(0, 0) + { + } + + /// + /// Конструктор + /// + public CnlViewPair(int cnlNum, int viewID) + { + CnlNum = cnlNum; + ViewID = viewID; + CnlName = ""; + Info = ""; + } + + + /// + /// Получить или установить номер входного канала + /// + public int CnlNum { get; set; } + + /// + /// Получить или установить ид. представления + /// + public int ViewID { get; set; } + + + /// + /// Получить или установить наименование входного канала + /// + public string CnlName { get; set; } + + /// + /// Получить или установить информацию о канале и представлении + /// + public string Info { get; set; } + + + /// + /// Заполнить наименование канала и информацию + /// + public void FillInfo(InCnlProps cnlProps, UserViews userViews) + { + StringBuilder sbInfo = new StringBuilder(); + + if (cnlProps == null) + { + CnlName = ""; + } + else + { + CnlName = cnlProps.CnlName; + if (cnlProps.ObjNum > 0) + sbInfo.Append(ChartPhrases.ObjectHint).Append("[").Append(cnlProps.ObjNum).Append("] ") + .AppendLine(cnlProps.ObjName); + if (cnlProps.KPNum > 0) + sbInfo.Append(ChartPhrases.DeviceHint).Append("[").Append(cnlProps.KPNum).Append("] ") + .AppendLine(cnlProps.KPName); + } + + if (ViewID > 0) + { + ViewNode viewNode = userViews.GetViewNode(ViewID); + if (viewNode != null) + sbInfo.Append(ChartPhrases.ViewHint).Append(viewNode.Text); + } + + Info = sbInfo.ToString().TrimEnd(); + } + } +} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgChartCommon/PlgChartCommon.csproj b/ScadaWeb/OpenPlugins/PlgChartCommon/PlgChartCommon.csproj index 5d8672cb4..04c1e6623 100644 --- a/ScadaWeb/OpenPlugins/PlgChartCommon/PlgChartCommon.csproj +++ b/ScadaWeb/OpenPlugins/PlgChartCommon/PlgChartCommon.csproj @@ -49,6 +49,7 @@ + diff --git a/ScadaWeb/ScadaWebCommon5Beta/WebUtils.cs b/ScadaWeb/ScadaWebCommon5Beta/WebUtils.cs index 43b682291..f3f4a9637 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/WebUtils.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/WebUtils.cs @@ -67,6 +67,14 @@ public static void CheckHttpContext(HttpContext httpContext, bool checkCookies = } } + /// + /// Проверить, что выполняется AJAX-запрос + /// + public static bool IsAjaxRequest(HttpRequest request) + { + return request.Headers["X-Requested-With"] == "XMLHttpRequest"; + } + /// /// Отключить кэширование страницы /// diff --git a/ScadaWeb/ScadaWebShell5Beta/Global.asax.cs b/ScadaWeb/ScadaWebShell5Beta/Global.asax.cs index ca08b8728..5143ffd60 100644 --- a/ScadaWeb/ScadaWebShell5Beta/Global.asax.cs +++ b/ScadaWeb/ScadaWebShell5Beta/Global.asax.cs @@ -24,6 +24,7 @@ */ using System; +using System.Web; using Utils; namespace Scada.Web @@ -58,7 +59,8 @@ protected void Application_AuthenticateRequest(object sender, EventArgs e) protected void Application_Error(object sender, EventArgs e) { - Server.Transfer("~/Error.aspx"); + if (!WebUtils.IsAjaxRequest(HttpContext.Current.Request)) + Server.Transfer("~/Error.aspx"); } protected void Session_End(object sender, EventArgs e) diff --git a/ScadaWeb/ScadaWebShell5Beta/Reports.aspx b/ScadaWeb/ScadaWebShell5Beta/Reports.aspx index ed8004f6d..d6b5f39c2 100644 --- a/ScadaWeb/ScadaWebShell5Beta/Reports.aspx +++ b/ScadaWeb/ScadaWebShell5Beta/Reports.aspx @@ -1,6 +1,5 @@ <%@ Page Title="Reports - Rapid SCADA" Language="C#" MasterPageFile="~/MasterMain.Master" AutoEventWireup="true" CodeBehind="Reports.aspx.cs" Inherits="Scada.Web.WFrmReports" %> - diff --git a/ScadaWeb/ScadaWebShell5Beta/css/common/contentform.css b/ScadaWeb/ScadaWebShell5Beta/css/common/contentform.css index 84d4cc3ec..429f59d6d 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/common/contentform.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/common/contentform.css @@ -17,7 +17,7 @@ h3 { font-weight: 500; margin: 20px 0 10px; } -h1:first-child { +h1:first-of-type { margin-top: 0; } a, diff --git a/ScadaWeb/ScadaWebShell5Beta/css/common/contentform.less b/ScadaWeb/ScadaWebShell5Beta/css/common/contentform.less index 1d5bc6398..dc87082a5 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/common/contentform.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/common/contentform.less @@ -22,7 +22,7 @@ h1, h2, h3 { margin: 20px 0 10px; } -h1:first-child { +h1:first-of-type { margin-top: 0; } diff --git a/ScadaWeb/ScadaWebShell5Beta/css/common/contentform.min.css b/ScadaWeb/ScadaWebShell5Beta/css/common/contentform.min.css index 8d7a80714..a574996f9 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/common/contentform.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/common/contentform.min.css @@ -1 +1 @@ -.main-content{padding:20px;font-size:14px;}h1{font-size:23px;}h2{font-size:17px;}h3{font-size:14px;}h1,h2,h3{font-weight:500;margin:20px 0 10px;}h1:first-child{margin-top:0;}a,a:active,a:hover,a:focus,a:visited{color:#0073aa;outline:0;text-decoration:none;}a:hover{color:#00a0d2;}label{font-weight:600;} \ No newline at end of file +.main-content{padding:20px;font-size:14px;}h1{font-size:23px;}h2{font-size:17px;}h3{font-size:14px;}h1,h2,h3{font-weight:500;margin:20px 0 10px;}h1:first-of-type{margin-top:0;}a,a:active,a:hover,a:focus,a:visited{color:#0073aa;outline:0;text-decoration:none;}a:hover{color:#00a0d2;}label{font-weight:600;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/reports.css b/ScadaWeb/ScadaWebShell5Beta/css/reports.css index b9614230e..554a638b3 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/reports.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/reports.css @@ -1,4 +1,41 @@ -#divMainContent { +.main-content { + padding: 20px; + font-size: 14px; +} +h1 { + font-size: 23px; +} +h2 { + font-size: 17px; +} +h3 { + font-size: 14px; +} +h1, +h2, +h3 { + font-weight: 500; + margin: 20px 0 10px; +} +h1:first-of-type { + margin-top: 0; +} +a, +a:active, +a:hover, +a:focus, +a:visited { + color: #0073aa; + outline: 0; + text-decoration: none; +} +a:hover { + color: #00a0d2; +} +label { + font-weight: 600; +} +#divMainContent { font-size: 15px; } .report-item { diff --git a/ScadaWeb/ScadaWebShell5Beta/css/reports.less b/ScadaWeb/ScadaWebShell5Beta/css/reports.less index 9c6a4997a..19e3edb32 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/reports.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/reports.less @@ -1,4 +1,6 @@ -#divMainContent { +@import "common/contentform.less"; + +#divMainContent { font-size: 15px; } diff --git a/ScadaWeb/ScadaWebShell5Beta/css/reports.min.css b/ScadaWeb/ScadaWebShell5Beta/css/reports.min.css index f65ea2d00..a50453e07 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/reports.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/reports.min.css @@ -1 +1 @@ -#divMainContent{font-size:15px;}.report-item{margin:5px 0;} \ No newline at end of file +.main-content{padding:20px;font-size:14px;}h1{font-size:23px;}h2{font-size:17px;}h3{font-size:14px;}h1,h2,h3{font-weight:500;margin:20px 0 10px;}h1:first-of-type{margin-top:0;}a,a:active,a:hover,a:focus,a:visited{color:#0073aa;outline:0;text-decoration:none;}a:hover{color:#00a0d2;}label{font-weight:600;}#divMainContent{font-size:15px;}.report-item{margin:5px 0;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/utils.js b/ScadaWeb/ScadaWebShell5Beta/js/api/utils.js index 7ce6e1d7e..c51af7b9e 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/utils.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/utils.js @@ -218,6 +218,17 @@ scada.utils = { } }, + // Scroll the first specified element to make the second element visible if it exists + scrollTo: function (jqScrolledElem, jqTargetElem) { + if (jqTargetElem.length > 0) { + var targetTop = jqTargetElem.offset().top; + + if (jqScrolledElem.scrollTop() > targetTop) { + jqScrolledElem.scrollTop(targetTop); + } + } + }, + // Detect if iOS is used iOS: function () { return /iPad|iPhone|iPod/.test(navigator.platform); diff --git a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js index 6ad0e0546..e01cbf20c 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js @@ -188,9 +188,9 @@ scada.masterMain = { $(document).ready(function () { // unbind events to avoid doubling in case of using ASP.NET AJAX - $(window).off(); + /*$(window).off(); $(document).off(); - $("body").off(); + $("body").off();*/ // page setup scada.clientAPI.rootPath = scada.env.rootPath; From e8b336c1bca24d8bca4ac657594e297ccdc41bea Mon Sep 17 00:00:00 2001 From: 2mik Date: Mon, 26 Sep 2016 10:58:07 +0300 Subject: [PATCH 292/382] PlgChart: min data report done! --- ScadaWeb/OpenPlugins/PlgChart/PlgChart.csproj | 8 ++ .../OpenPlugins/PlgChart/compilerconfig.json | 4 + .../PlgChart/css/common/modalform.less | 20 +++ .../PlgChart/plugins/Chart/MinDataRep.aspx | 7 +- .../PlgChart/plugins/Chart/MinDataRep.aspx.cs | 32 ++++- .../plugins/Chart/MinDataRep.aspx.designer.cs | 18 +++ .../PlgChart/plugins/Chart/SelectCnls.aspx | 30 +++- .../PlgChart/plugins/Chart/SelectCnls.aspx.cs | 128 +++++++++++++++++- .../plugins/Chart/SelectCnls.aspx.designer.cs | 62 ++++++++- .../PlgChart/plugins/Chart/css/mindatarep.css | 30 ++++ .../plugins/Chart/css/mindatarep.less | 1 + .../plugins/Chart/css/mindatarep.min.css | 2 +- .../PlgChart/plugins/Chart/css/selectcnls.css | 46 +++++++ .../plugins/Chart/css/selectcnls.less | 2 + .../plugins/Chart/css/selectcnls.min.css | 1 + .../PlgChart/plugins/Chart/js/selectcnls.js | 29 +++- .../plugins/Chart/lang/PlgChart.en-GB.xml | 18 +++ .../plugins/Chart/lang/PlgChart.ru-RU.xml | 18 +++ .../PlgChartCommon/ChartPhrases.cs | 3 + .../OpenPlugins/PlgChartCommon/ChartUtils.cs | 98 +++++++++++++- .../OpenPlugins/PlgChartCommon/CnlViewPair.cs | 2 +- 21 files changed, 527 insertions(+), 32 deletions(-) create mode 100644 ScadaWeb/OpenPlugins/PlgChart/css/common/modalform.less create mode 100644 ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/selectcnls.css create mode 100644 ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/selectcnls.less create mode 100644 ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/selectcnls.min.css diff --git a/ScadaWeb/OpenPlugins/PlgChart/PlgChart.csproj b/ScadaWeb/OpenPlugins/PlgChart/PlgChart.csproj index e00bec1e9..4b904c665 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/PlgChart.csproj +++ b/ScadaWeb/OpenPlugins/PlgChart/PlgChart.csproj @@ -91,6 +91,12 @@ mindatarep.css + + selectcnls.less + + + selectcnls.css + @@ -152,6 +158,8 @@ + + Web.config diff --git a/ScadaWeb/OpenPlugins/PlgChart/compilerconfig.json b/ScadaWeb/OpenPlugins/PlgChart/compilerconfig.json index f5b18dd52..85df34043 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/compilerconfig.json +++ b/ScadaWeb/OpenPlugins/PlgChart/compilerconfig.json @@ -14,5 +14,9 @@ { "outputFile": "plugins/Chart/css/cnllist.css", "inputFile": "plugins/Chart/css/cnllist.less" + }, + { + "outputFile": "plugins/Chart/css/selectcnls.css", + "inputFile": "plugins/Chart/css/selectcnls.less" } ] \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgChart/css/common/modalform.less b/ScadaWeb/OpenPlugins/PlgChart/css/common/modalform.less new file mode 100644 index 000000000..c34091cc0 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgChart/css/common/modalform.less @@ -0,0 +1,20 @@ +@import "globalvar.less"; + +body { + margin: 0; + padding: 0; + background-color: @popup-back-color; + color: @content-fore-color; + font-family: @default-font-family; + font-size: @form-font-size; + min-width: @popup-min-width; + overflow: hidden; +} + +table { + border-collapse: collapse; +} + +label { + font-weight: 600; +} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/MinDataRep.aspx b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/MinDataRep.aspx index 73c0fd988..2140e5c1b 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/MinDataRep.aspx +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/MinDataRep.aspx @@ -1,7 +1,6 @@ -<%@ Page Title="" Language="C#" MasterPageFile="~/MasterMain.Master" AutoEventWireup="true" CodeBehind="MinDataRep.aspx.cs" Inherits="Scada.Web.Plugins.Chart.WFrmMinDataRep" %> +<%@ Page Title="Minute Data Report" Language="C#" MasterPageFile="~/MasterMain.Master" AutoEventWireup="true" CodeBehind="MinDataRep.aspx.cs" Inherits="Scada.Web.Plugins.Chart.WFrmMinDataRep" %> <%@ Import Namespace="Scada.Web.Plugins.Chart" %> - @@ -16,6 +15,10 @@ + + + +

diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/MinDataRep.aspx.cs b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/MinDataRep.aspx.cs index c9a612665..1c52d3125 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/MinDataRep.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/MinDataRep.aspx.cs @@ -43,11 +43,12 @@ public partial class WFrmMinDataRep : System.Web.UI.Page /// - /// Скрыть сообщение об ошибке + /// Скрыть все сообщения /// - private void HideErrMsg() + private void HideAllMsgs() { pnlErrMsg.Visible = false; + pnlWarnMsg.Visible = false; } /// @@ -59,6 +60,15 @@ private void ShowErrMsg(string errMsg) pnlErrMsg.Visible = true; } + /// + /// Вывести предупреждение + /// + private void ShowWarnMsg(string warnMsg) + { + lblWarnMsg.Text = warnMsg; + pnlWarnMsg.Visible = true; + } + /// /// Отобразить выбранные каналы /// @@ -68,6 +78,9 @@ private void ShowSelCnls() repSelCnls.DataBind(); btnGenReport.Enabled = selCnls.Count > 0; lblNoSelCnls.Visible = !btnGenReport.Enabled; + + if (selCnls.Count > ChartUtils.NormalChartCnt) + ShowWarnMsg(ChartPhrases.PerfWarning); } @@ -76,11 +89,12 @@ protected void Page_Load(object sender, EventArgs e) appData = AppData.GetAppData(); userData = UserData.GetUserData(); - // скрытие сообщения об ошибке - HideErrMsg(); + // скрытие всех сообщений + HideAllMsgs(); if (IsPostBack) { + // получение выбранных каналов selCnls = (List)ViewState["SelCnls"]; } else @@ -108,13 +122,17 @@ protected void btnApplyAddedCnls_Click(object sender, EventArgs e) int[] addedCnls = WebUtils.QueryParamToIntArray(hidAddedCnlNums.Value); int[] addedViewIDs = WebUtils.QueryParamToIntArray(hidAddedViewIDs.Value); ChartUtils.CheckArrays(addedCnls, addedViewIDs); + HashSet selCnlSet = ChartUtils.GetCnlSet(selCnls); for (int i = 0, cnt = addedCnls.Length; i < cnt; i++) { int cnlNum = addedCnls[i]; - CnlViewPair pair = new CnlViewPair(cnlNum, addedViewIDs[i]); - pair.FillInfo(appData.DataAccess.GetCnlProps(cnlNum), userData.UserViews); - selCnls.Add(pair); + if (!selCnlSet.Contains(cnlNum)) + { + CnlViewPair pair = new CnlViewPair(cnlNum, addedViewIDs[i]); + pair.FillInfo(appData.DataAccess.GetCnlProps(cnlNum), userData.UserViews); + selCnls.Add(pair); + } } ViewState.Add("SelCnls", selCnls); diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/MinDataRep.aspx.designer.cs b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/MinDataRep.aspx.designer.cs index d97dad68c..e578e52b7 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/MinDataRep.aspx.designer.cs +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/MinDataRep.aspx.designer.cs @@ -48,6 +48,24 @@ public partial class WFrmMinDataRep { /// protected global::System.Web.UI.WebControls.Label lblErrMsg; + /// + /// pnlWarnMsg control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Panel pnlWarnMsg; + + /// + /// lblWarnMsg control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblWarnMsg; + /// /// lblTitle control. /// diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/SelectCnls.aspx b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/SelectCnls.aspx index 638b843e8..b4740faf3 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/SelectCnls.aspx +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/SelectCnls.aspx @@ -1,4 +1,5 @@ <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="SelectCnls.aspx.cs" Inherits="Scada.Web.Plugins.Chart.WFrmSelectCnls" %> +<%@ Import Namespace="Scada.Web.Plugins.Chart" %> @@ -10,6 +11,7 @@ Select Channels - Rapid SCADA + @@ -20,8 +22,32 @@
- - + + + +
+ +
+
+ + + +
+
[<%# Eval("CnlNum") %>] <%# HttpUtility.HtmlEncode(Eval("CnlName")) %>
+ +
+
+
+
+ + + +
diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/SelectCnls.aspx.cs b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/SelectCnls.aspx.cs index de8988235..26ee69175 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/SelectCnls.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/SelectCnls.aspx.cs @@ -1,21 +1,143 @@ -using System; +/* + * Copyright 2016 Mikhail Shiryaev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Product : Rapid SCADA + * Module : PlgChart + * Summary : Select input channels web form + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + */ + +using Scada.Client; +using Scada.UI; +using Scada.Web.Shell; +using System; +using System.Collections.Generic; +using System.Web.UI.WebControls; namespace Scada.Web.Plugins.Chart { + /// + /// Select input channels web form + /// Веб-форма выбора входных каналов + /// public partial class WFrmSelectCnls : System.Web.UI.Page { + private AppData appData; // общие данные веб-приложения + private UserData userData; // данные пользователя приложения + private List selCnls; // выбранные каналы + protected HashSet selCnlSet; // множество выбранных каналов + + + /// + /// Отобразить каналы выбранного представления + /// + private void ShowCnlsByView() + { + int viewID; + int.TryParse(ddlView.SelectedValue, out viewID); + List cnlsByView = ChartUtils.GetCnlViewPairsByView( + viewID, appData.DataAccess, appData.ViewCache, userData.UserViews); + + repCnlsByView.DataSource = cnlsByView; + repCnlsByView.DataBind(); + lblUnableLoadView.Visible = cnlsByView == null; + lblNoCnlsByView.Visible = cnlsByView != null && cnlsByView.Count == 0; + } + + protected void Page_Load(object sender, EventArgs e) { + appData = AppData.GetAppData(); + userData = UserData.GetUserData(); + + // проверка входа в систему + if (!userData.LoggedOn) + throw new ScadaException(CommonPhrases.NoRights); + + if (IsPostBack) + { + // получение выбранных каналов + selCnls = (List)ViewState["SelCnls"]; + selCnlSet = ChartUtils.GetCnlSet(selCnls); + } + else + { + // перевод веб-страницы + Translator.TranslatePage(Page, "Scada.Web.Plugins.Chart.WFrmSelectCnls"); + lblPerfWarn.Text = ChartPhrases.PerfWarning; + + // настройка элементов управления + btnSubmit.Enabled = false; + pnlPerfWarn.Visible = false; + // создание списка выбранных каналов + selCnls = new List(); + ViewState.Add("SelCnls", selCnls); + selCnlSet = ChartUtils.GetCnlSet(selCnls); + + // заполнение выпадающего списка представлений и отображение каналов по представлению + ChartUtils.FillViewList(ddlView, 0, userData.UserViews); + ShowCnlsByView(); + ChartUtils.AddUpdateModalHeightScript(this); + } } protected void btnSubmit_Click(object sender, EventArgs e) { // завершить выбор каналов - string cnlNums = txtCnls.Text; - string viewIDs = txtViewIDs.Text; + string cnlNums; + string viewIDs; + selCnls.GetSelection(out cnlNums, out viewIDs); ClientScript.RegisterStartupScript(GetType(), "CloseModalScript", string.Format("closeModal('{0}', '{1}');", cnlNums, viewIDs), true); } + + protected void ddlView_SelectedIndexChanged(object sender, EventArgs e) + { + // выбор каналов по представлению + ShowCnlsByView(); + ChartUtils.AddUpdateModalHeightScript(this); + } + + protected void repCnlsByView_ItemCommand(object source, RepeaterCommandEventArgs e) + { + // добавление выбранного канала в список + if (e.CommandName == "AddCnl") + { + int cnlNum = int.Parse((string)e.CommandArgument); + + if (!selCnlSet.Contains(cnlNum)) + { + int viewID = int.Parse(ddlView.SelectedValue); + CnlViewPair pair = new CnlViewPair(cnlNum, viewID); + pair.FillInfo(appData.DataAccess.GetCnlProps(cnlNum), null); + + selCnls.Add(pair); + ViewState.Add("SelCnls", selCnls); + + btnSubmit.Enabled = true; + pnlPerfWarn.Visible = selCnls.Count > ChartUtils.NormalChartCnt; + } + + Label lblCnlAdded = (Label)e.Item.FindControl("lblCnlAdded"); + lblCnlAdded.Visible = true; + } + } } } \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/SelectCnls.aspx.designer.cs b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/SelectCnls.aspx.designer.cs index 14a9342e3..3a51c8bbf 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/SelectCnls.aspx.designer.cs +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/SelectCnls.aspx.designer.cs @@ -31,21 +31,75 @@ public partial class WFrmSelectCnls { protected global::System.Web.UI.WebControls.Button btnSubmit; /// - /// txtCnls control. + /// pnlPerfWarn control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.WebControls.TextBox txtCnls; + protected global::System.Web.UI.WebControls.Panel pnlPerfWarn; /// - /// txtViewIDs control. + /// lblPerfWarn control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.WebControls.TextBox txtViewIDs; + protected global::System.Web.UI.WebControls.Label lblPerfWarn; + + /// + /// ddlView control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.DropDownList ddlView; + + /// + /// pnlCnlsByView control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Panel pnlCnlsByView; + + /// + /// repCnlsByView control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Repeater repCnlsByView; + + /// + /// lblLoading control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblLoading; + + /// + /// lblUnableLoadView control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblUnableLoadView; + + /// + /// lblNoCnlsByView control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblNoCnlsByView; } } diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/mindatarep.css b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/mindatarep.css index 9aca581a5..8df2ed021 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/mindatarep.css +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/mindatarep.css @@ -35,6 +35,36 @@ a:hover { label { font-weight: 600; } +.cnl-list { + display: table; +} +.cnl-list .cnl-item { + display: table-row; +} +.cnl-list .cnl-field { + display: table-cell; + padding: 2px 0; +} +.cnl-list .cnl-name { + padding-left: 15px; + vertical-align: middle; +} +.cnl-list .cnl-btns { + padding-left: 10px; + vertical-align: top; + white-space: nowrap; +} +.cnl-list .cnl-btns input, +.cnl-list .cnl-btns a { + margin-right: 5px; +} +.cnl-list .popover { + font-size: 12px; + line-height: normal; +} +.cnl-list-msg { + margin-left: 15px; +} .form-group.period { max-width: 300px; } diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/mindatarep.less b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/mindatarep.less index f665f98bc..5cbd3421c 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/mindatarep.less +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/mindatarep.less @@ -1,4 +1,5 @@ @import "../../../css/common/contentform.less"; +@import "cnllist.less"; .form-group.period { max-width: 300px; diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/mindatarep.min.css b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/mindatarep.min.css index 65e46d0b6..78c16b2c8 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/mindatarep.min.css +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/mindatarep.min.css @@ -1 +1 @@ -.main-content{padding:20px;font-size:14px;}h1{font-size:23px;}h2{font-size:17px;}h3{font-size:14px;}h1,h2,h3{font-weight:500;margin:20px 0 10px;}h1:first-of-type{margin-top:0;}a,a:active,a:hover,a:focus,a:visited{color:#0073aa;outline:0;text-decoration:none;}a:hover{color:#00a0d2;}label{font-weight:600;}.form-group.period{max-width:300px;}.popovers-container{position:relative;}#lblGenStarted{display:block;margin-top:5px;} \ No newline at end of file +.main-content{padding:20px;font-size:14px;}h1{font-size:23px;}h2{font-size:17px;}h3{font-size:14px;}h1,h2,h3{font-weight:500;margin:20px 0 10px;}h1:first-of-type{margin-top:0;}a,a:active,a:hover,a:focus,a:visited{color:#0073aa;outline:0;text-decoration:none;}a:hover{color:#00a0d2;}label{font-weight:600;}.cnl-list{display:table;}.cnl-list .cnl-item{display:table-row;}.cnl-list .cnl-field{display:table-cell;padding:2px 0;}.cnl-list .cnl-name{padding-left:15px;vertical-align:middle;}.cnl-list .cnl-btns{padding-left:10px;vertical-align:top;white-space:nowrap;}.cnl-list .cnl-btns input,.cnl-list .cnl-btns a{margin-right:5px;}.cnl-list .popover{font-size:12px;line-height:normal;}.cnl-list-msg{margin-left:15px;}.form-group.period{max-width:300px;}.popovers-container{position:relative;}#lblGenStarted{display:block;margin-top:5px;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/selectcnls.css b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/selectcnls.css new file mode 100644 index 000000000..b4e54621c --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/selectcnls.css @@ -0,0 +1,46 @@ +body { + margin: 0; + padding: 0; + background-color: white; + color: #333; + font-family: 'Open Sans', sans-serif; + font-size: 13px; + min-width: 250px; + overflow: hidden; +} +table { + border-collapse: collapse; +} +label { + font-weight: 600; +} +.cnl-list { + display: table; +} +.cnl-list .cnl-item { + display: table-row; +} +.cnl-list .cnl-field { + display: table-cell; + padding: 2px 0; +} +.cnl-list .cnl-name { + padding-left: 15px; + vertical-align: middle; +} +.cnl-list .cnl-btns { + padding-left: 10px; + vertical-align: top; + white-space: nowrap; +} +.cnl-list .cnl-btns input, +.cnl-list .cnl-btns a { + margin-right: 5px; +} +.cnl-list .popover { + font-size: 12px; + line-height: normal; +} +.cnl-list-msg { + margin-left: 15px; +} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/selectcnls.less b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/selectcnls.less new file mode 100644 index 000000000..917970f58 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/selectcnls.less @@ -0,0 +1,2 @@ +@import "../../../css/common/modalform.less"; +@import "cnllist.less"; diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/selectcnls.min.css b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/selectcnls.min.css new file mode 100644 index 000000000..9744bf085 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/css/selectcnls.min.css @@ -0,0 +1 @@ +body{margin:0;padding:0;background-color:#fff;color:#333;font-family:'Open Sans',sans-serif;font-size:13px;min-width:250px;overflow:hidden;}table{border-collapse:collapse;}label{font-weight:600;}.cnl-list{display:table;}.cnl-list .cnl-item{display:table-row;}.cnl-list .cnl-field{display:table-cell;padding:2px 0;}.cnl-list .cnl-name{padding-left:15px;vertical-align:middle;}.cnl-list .cnl-btns{padding-left:10px;vertical-align:top;white-space:nowrap;}.cnl-list .cnl-btns input,.cnl-list .cnl-btns a{margin-right:5px;}.cnl-list .popover{font-size:12px;line-height:normal;}.cnl-list-msg{margin-left:15px;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/js/selectcnls.js b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/js/selectcnls.js index 918e30eef..d9eb988ff 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/js/selectcnls.js +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/js/selectcnls.js @@ -1,6 +1,15 @@ -// Close the modal with successful result +// Popup dialogs manipulation object +var popup = scada.popupLocator.getPopup(); + +// Update the modal dialog height according to a frame height +function updateModalHeight() { + if (popup) { + popup.updateModalHeight(window); + } +} + +// Close the modal with successful result function closeModal(cnlNums, viewIDs) { - var popup = scada.popupLocator.getPopup(); if (popup) { popup.closeModal(window, true, { cnlNums: cnlNums, viewIDs: viewIDs }); } @@ -10,6 +19,12 @@ $(document).ready(function () { // initialize Bootstrap popovers $('[data-toggle="popover"]').popover({ html: true }); + // disable OK button according to the submit button state + if (popup) { + var enabled = !$("#btnSubmit").is(":disabled"); + popup.setButtonEnabled(window, scada.ModalButtons.OK, enabled); + } + // submit the form on OK button click $(window).on(scada.EventTypes.MODAL_BTN_CLICK, function (event, result) { if (result == scada.ModalButtons.OK) { @@ -18,9 +33,9 @@ $(document).ready(function () { }); // show "loading" message on change a view - /*$("#ddlView").change(function () { - $("#pnlCnlsByView").addClass("hidden"); - $(".list-msg").addClass("hidden"); - $("#lblLoading").removeClass("hidden"); - });*/ + $("#ddlView").change(function () { + $(".cnl-list").addClass("hidden"); + $(".cnl-list-msg").addClass("hidden"); + $(".cnl-list-msg.loading").removeClass("hidden"); + }); }); \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/lang/PlgChart.en-GB.xml b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/lang/PlgChart.en-GB.xml index aa073cf91..aed11e52d 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/lang/PlgChart.en-GB.xml +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/lang/PlgChart.en-GB.xml @@ -7,6 +7,7 @@ The maximum period length is {0} days. Channel numbers are not specified. Mismatch in number of channels and view IDs. + Warning! Too many channels may affect performance. Add Remove Info @@ -25,4 +26,21 @@ Chart - Rapid SCADA Generated + + Minute Data Report - Rapid SCADA + Minute Data Report + From + To + Add Channels + Selected Channels: + Input channels are not selected + Download Report + Generating the report. Please wait... + + + Select Channels - Rapid SCADA + Loading... + Unable to load view + Input channels not found + diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/lang/PlgChart.ru-RU.xml b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/lang/PlgChart.ru-RU.xml index 68a38b92a..76d22aa04 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/lang/PlgChart.ru-RU.xml +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/lang/PlgChart.ru-RU.xml @@ -7,6 +7,7 @@ Максимальная длина периода {0} день. Номера каналов не заданы. Несоответствие количества каналов и ид. представлений. + Предупреждение! Слишком много каналов могут снизить производительность. Добавить Удалить Инфо @@ -25,4 +26,21 @@ График - Rapid SCADA Получено + + Отчёт по минутным данным - Rapid SCADA + Отчёт по минутным данным + Начало + Окончание + Добавить каналы + Выбранные каналы: + Входные каналы не выбраны + Скачать отчёт + Отчёт формируется. Пожалуйста, подождите... + + + Выбор каналов - Rapid SCADA + Загрузка... + Не удалось загрузить представление + Входные каналы не найдены + diff --git a/ScadaWeb/OpenPlugins/PlgChartCommon/ChartPhrases.cs b/ScadaWeb/OpenPlugins/PlgChartCommon/ChartPhrases.cs index e9d643e78..372fc05a0 100644 --- a/ScadaWeb/OpenPlugins/PlgChartCommon/ChartPhrases.cs +++ b/ScadaWeb/OpenPlugins/PlgChartCommon/ChartPhrases.cs @@ -45,6 +45,7 @@ static ChartPhrases() public static string PeriodTooLong { get; private set; } public static string CnlNumsEmptyError { get; private set; } public static string CountMismatchError { get; private set; } + public static string PerfWarning { get; private set; } public static string AddCnlBtn { get; private set; } public static string RemoveCnlBtn { get; private set; } public static string CnlInfoBtn { get; private set; } @@ -60,6 +61,7 @@ private static void SetToDefault() PeriodTooLong = Localization.Dict.GetEmptyPhrase("PeriodTooLong"); CnlNumsEmptyError = Localization.Dict.GetEmptyPhrase("CnlNumsEmptyError"); CountMismatchError = Localization.Dict.GetEmptyPhrase("CountMismatchError"); + PerfWarning = Localization.Dict.GetEmptyPhrase("PerfWarning"); AddCnlBtn = Localization.Dict.GetEmptyPhrase("AddCnlBtn"); RemoveCnlBtn = Localization.Dict.GetEmptyPhrase("RemoveCnlBtn"); CnlInfoBtn = Localization.Dict.GetEmptyPhrase("CnlInfoBtn"); @@ -79,6 +81,7 @@ public static void Init() PeriodTooLong = dict.GetPhrase("PeriodTooLong", PeriodTooLong); CnlNumsEmptyError = dict.GetPhrase("CnlNumsEmptyError", CnlNumsEmptyError); CountMismatchError = dict.GetPhrase("CountMismatchError", CountMismatchError); + PerfWarning = dict.GetPhrase("PerfWarning", PerfWarning); AddCnlBtn = dict.GetPhrase("AddCnlBtn", AddCnlBtn); RemoveCnlBtn = dict.GetPhrase("RemoveCnlBtn", RemoveCnlBtn); CnlInfoBtn = dict.GetPhrase("CnlInfoBtn", CnlInfoBtn); diff --git a/ScadaWeb/OpenPlugins/PlgChartCommon/ChartUtils.cs b/ScadaWeb/OpenPlugins/PlgChartCommon/ChartUtils.cs index f374b3eda..3e2ab6c0f 100644 --- a/ScadaWeb/OpenPlugins/PlgChartCommon/ChartUtils.cs +++ b/ScadaWeb/OpenPlugins/PlgChartCommon/ChartUtils.cs @@ -23,15 +23,18 @@ * Modified : 2016 */ +using Scada.Client; +using Scada.Web.Shell; using System; using System.Collections.Generic; using System.Text; +using System.Web.UI; using System.Web.UI.WebControls; namespace Scada.Web.Plugins.Chart { /// - /// The class contains utility methods for web charts + /// The class contains utility methods for charts /// Класс, содержащий вспомогательные методы для графиков /// public static class ChartUtils @@ -120,6 +123,7 @@ public static void NormalizeTimeRange(ref DateTime startDate, ref int period) } } + /// /// Проверить корректность заданных массивов /// @@ -139,16 +143,27 @@ public static void CheckArrays(int[] cnlNums, int[] viewIDs) } /// - /// Получить выбранные каналы и соответствующие им представления + /// Получить можество номеров канала из списка пар канал/представление + /// + public static HashSet GetCnlSet(List cnlViewPairs) + { + HashSet cnlSet = new HashSet(); + foreach (CnlViewPair pair in cnlViewPairs) + cnlSet.Add(pair.CnlNum); + return cnlSet; + } + + /// + /// Получить выбранные каналы и соответствующие им представления из списка /// - public static void GetSelection(List selectedCnls, out string cnlNums, out string viewIDs) + public static void GetSelection(this List cnlViewPairs, out string cnlNums, out string viewIDs) { StringBuilder sbCnlNums = new StringBuilder(); StringBuilder sbViewIDs = new StringBuilder(); - for (int i = 0, lastInd = selectedCnls.Count - 1; i <= lastInd; i++) + for (int i = 0, lastInd = cnlViewPairs.Count - 1; i <= lastInd; i++) { - CnlViewPair pair = selectedCnls[i]; + CnlViewPair pair = cnlViewPairs[i]; sbCnlNums.Append(pair.CnlNum); sbViewIDs.Append(pair.ViewID); @@ -162,5 +177,78 @@ public static void GetSelection(List selectedCnls, out string cnlNu cnlNums = sbCnlNums.ToString(); viewIDs = sbViewIDs.ToString(); } + + /// + /// Получить список пар канал/представление по ид. представления + /// + public static List GetCnlViewPairsByView( + int viewID, DataAccess dataAccess, ViewCache viewCache, UserViews userViews) + { + BaseView view = null; + + if (viewID > 0) + { + Type viewType = userViews.GetViewType(viewID); + view = viewCache.GetView(viewType, viewID); + } + + if (view == null) + { + return null; + } + else + { + List cnlsByView = new List(); + foreach (int cnlNum in view.CnlList) + { + CnlViewPair pair = new CnlViewPair(cnlNum, 0); + pair.FillInfo(dataAccess.GetCnlProps(cnlNum), null); + cnlsByView.Add(pair); + } + return cnlsByView; + } + } + + /// + /// Заполнить выпадающий список представлений + /// + public static void FillViewList(DropDownList ddlView, int preferableViewID, UserViews userViews) + { + int selInd1 = -1; // индекс выбранного элемента, соответствующего непустому представлению + int selInd2 = -1; // индекс выбранного элемента, соответствующего предпочтительному представлению + List viewNodes = userViews.GetLinearViewNodes(); + int viewNodesCnt = viewNodes.Count; + + // заполнение списка представлений и определение индексов выбранного элемента + ddlView.Items.Clear(); + for (int i = 0; i < viewNodesCnt; i++) + { + ViewNode viewNode = viewNodes[i]; + + if (selInd1 <= 0 && !viewNode.IsEmpty) + selInd1 = i; + if (selInd2 <= 0 && preferableViewID > 0 && viewNode.ViewID == preferableViewID) + selInd2 = i; + + string text = new string('-', viewNode.Level) + " " + viewNode.Text; + ddlView.Items.Add(new ListItem(text, viewNode.ViewID.ToString())); + } + + // установка выбранного элемента + if (selInd2 >= 0) + ddlView.SelectedIndex = selInd2; + else if (selInd1 >= 0) + ddlView.SelectedIndex = selInd1; + } + + /// + /// Добавить на страницу скрипт обновления высоты диалогового окна + /// + public static void AddUpdateModalHeightScript(Page page) + { + if (!page.ClientScript.IsStartupScriptRegistered(page.GetType(), "UpdateModalHeightScript")) + page.ClientScript.RegisterStartupScript( + page.GetType(), "UpdateModalHeightScript", "updateModalHeight();", true); + } } } diff --git a/ScadaWeb/OpenPlugins/PlgChartCommon/CnlViewPair.cs b/ScadaWeb/OpenPlugins/PlgChartCommon/CnlViewPair.cs index f966e97e4..0b62ed12b 100644 --- a/ScadaWeb/OpenPlugins/PlgChartCommon/CnlViewPair.cs +++ b/ScadaWeb/OpenPlugins/PlgChartCommon/CnlViewPair.cs @@ -101,7 +101,7 @@ public void FillInfo(InCnlProps cnlProps, UserViews userViews) .AppendLine(cnlProps.KPName); } - if (ViewID > 0) + if (ViewID > 0 && userViews != null) { ViewNode viewNode = userViews.GetViewNode(ViewID); if (viewNode != null) From c999affed5243374bc8089efaa00772446cce6a8 Mon Sep 17 00:00:00 2001 From: 2mik Date: Mon, 26 Sep 2016 11:50:22 +0300 Subject: [PATCH 293/382] PlgChart: refactor --- .../PlgChart/AppCode/Chart/MinDataRepBuilder.cs | 14 +++++++------- .../Chart/{PlgPhrases.cs => MinDataRepPhrases.cs} | 10 +++++----- .../PlgChart/AppCode/Chart/MinDataRepSpec.cs | 6 +++--- .../OpenPlugins/PlgChart/AppCode/PlgChartSpec.cs | 2 +- ScadaWeb/OpenPlugins/PlgChart/PlgChart.csproj | 2 +- .../PlgChart/plugins/Chart/MinDataRep.aspx | 4 ++-- 6 files changed, 19 insertions(+), 19 deletions(-) rename ScadaWeb/OpenPlugins/PlgChart/AppCode/Chart/{PlgPhrases.cs => MinDataRepPhrases.cs} (89%) diff --git a/ScadaWeb/OpenPlugins/PlgChart/AppCode/Chart/MinDataRepBuilder.cs b/ScadaWeb/OpenPlugins/PlgChart/AppCode/Chart/MinDataRepBuilder.cs index d01fe455b..a87c896c2 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/AppCode/Chart/MinDataRepBuilder.cs +++ b/ScadaWeb/OpenPlugins/PlgChart/AppCode/Chart/MinDataRepBuilder.cs @@ -37,7 +37,7 @@ namespace Scada.Web.Plugins.Chart /// Minute data report builder /// Формирует отчёт по минутным данным ///
- public class MinDataRepBuilder : ExcelRepBuilder + internal class MinDataRepBuilder : ExcelRepBuilder { private DataAccess dataAccess; // объект для доступа к данным @@ -164,7 +164,7 @@ private void AddMinTableColumns(Table table) colIndex++; Cell valColCell = valColCellTemplate.Clone(); - valColCell.DataNode.InnerText = string.Format(PlgPhrases.ValColTitle, cnlNums[i]); + valColCell.DataNode.InnerText = string.Format(MinDataRepPhrases.ValColTitle, cnlNums[i]); dataHdrRow.AppendCell(valColCell); Cell emptyCell = valCellTemplate.Clone(); @@ -320,7 +320,7 @@ protected override void FinalXmlDocProc() } // перевод наименования листа - workbook.Worksheets[0].Name = PlgPhrases.MinDataWorksheet; + workbook.Worksheets[0].Name = MinDataRepPhrases.MinDataWorksheet; // удаление лишних атрибутов таблицы table.RemoveTableNodeAttrs(); @@ -357,11 +357,11 @@ protected override void ProcVal(Cell cell, string valName) } else if (valName == "Gen") { - nodeText = PlgPhrases.MinDataGen + DateTime.Now.ToLocalizedString(); + nodeText = MinDataRepPhrases.MinDataGen + DateTime.Now.ToLocalizedString(); } else if (valName == "CnlsCaption") { - nodeText = PlgPhrases.CnlsCaption; + nodeText = MinDataRepPhrases.CnlsCaption; } else if (procCnlRow) { @@ -379,13 +379,13 @@ protected override void ProcVal(Cell cell, string valName) } else if (valName == "TimeCol") { - nodeText = PlgPhrases.TimeColTitle; + nodeText = MinDataRepPhrases.TimeColTitle; dataHdrRow = cell.ParentRow; } else if (valName == "ValCol") { valColCellTemplate = cell; - nodeText = string.Format(PlgPhrases.ValColTitle, cnlNums[0]); + nodeText = string.Format(MinDataRepPhrases.ValColTitle, cnlNums[0]); } else if (valName == "Date") { diff --git a/ScadaWeb/OpenPlugins/PlgChart/AppCode/Chart/PlgPhrases.cs b/ScadaWeb/OpenPlugins/PlgChart/AppCode/Chart/MinDataRepPhrases.cs similarity index 89% rename from ScadaWeb/OpenPlugins/PlgChart/AppCode/Chart/PlgPhrases.cs rename to ScadaWeb/OpenPlugins/PlgChart/AppCode/Chart/MinDataRepPhrases.cs index 0f14abc52..1cb7599a1 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/AppCode/Chart/PlgPhrases.cs +++ b/ScadaWeb/OpenPlugins/PlgChart/AppCode/Chart/MinDataRepPhrases.cs @@ -16,7 +16,7 @@ * * Product : Rapid SCADA * Module : PlgChart - * Summary : The phrases used by the plugin + * Summary : The phrases used by the minute data report * * Author : Mikhail Shiryaev * Created : 2016 @@ -28,12 +28,12 @@ namespace Scada.Web.Plugins.Chart { /// - /// The phrases used by the plugin - /// Фразы, используемые плагином + /// The phrases used by the minute data report + /// Фразы, используемые отчётом по минутным данным /// - internal static class PlgPhrases + internal static class MinDataRepPhrases { - static PlgPhrases() + static MinDataRepPhrases() { SetToDefault(); } diff --git a/ScadaWeb/OpenPlugins/PlgChart/AppCode/Chart/MinDataRepSpec.cs b/ScadaWeb/OpenPlugins/PlgChart/AppCode/Chart/MinDataRepSpec.cs index a4c1e3f28..5446995b8 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/AppCode/Chart/MinDataRepSpec.cs +++ b/ScadaWeb/OpenPlugins/PlgChart/AppCode/Chart/MinDataRepSpec.cs @@ -27,7 +27,7 @@ namespace Scada.Web.Plugins.Chart { /// /// Minute data report specification - /// Спецификация отчёта минутных данных + /// Спецификация отчёта по минутным данным /// public class MinDataRepSpec : ReportSpec { @@ -50,8 +50,8 @@ public override string Name get { return Localization.UseRussian ? - "Минутные данные" : - "Minute data"; + "Отчёт по минутным данным" : + "Minute data report"; } } diff --git a/ScadaWeb/OpenPlugins/PlgChart/AppCode/PlgChartSpec.cs b/ScadaWeb/OpenPlugins/PlgChart/AppCode/PlgChartSpec.cs index 0a876866c..cab82dcda 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/AppCode/PlgChartSpec.cs +++ b/ScadaWeb/OpenPlugins/PlgChart/AppCode/PlgChartSpec.cs @@ -116,7 +116,7 @@ public override void Init() { dictUpdater = new DictUpdater( string.Format("{0}Chart{1}lang{1}", AppDirs.PluginsDir, Path.DirectorySeparatorChar), - "PlgChart", () => { PlgPhrases.Init(); ChartPhrases.Init(); }, Log); + "PlgChart", () => { ChartPhrases.Init(); MinDataRepPhrases.Init(); }, Log); } /// diff --git a/ScadaWeb/OpenPlugins/PlgChart/PlgChart.csproj b/ScadaWeb/OpenPlugins/PlgChart/PlgChart.csproj index 4b904c665..dde17bc0c 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/PlgChart.csproj +++ b/ScadaWeb/OpenPlugins/PlgChart/PlgChart.csproj @@ -114,7 +114,7 @@ - + Chart.aspx diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/MinDataRep.aspx b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/MinDataRep.aspx index 2140e5c1b..152f311ce 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/MinDataRep.aspx +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/MinDataRep.aspx @@ -13,11 +13,11 @@ - + - + From b20cedc3c04fb16a1b60ba4752ac690f248b0d36 Mon Sep 17 00:00:00 2001 From: 2mik Date: Mon, 26 Sep 2016 16:54:33 +0300 Subject: [PATCH 294/382] ScadaWeb: about form css --- ScadaWeb/ScadaWebShell5Beta/css/about.css | 39 ++++++++++++++++++- ScadaWeb/ScadaWebShell5Beta/css/about.less | 4 +- ScadaWeb/ScadaWebShell5Beta/css/about.min.css | 2 +- 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/ScadaWeb/ScadaWebShell5Beta/css/about.css b/ScadaWeb/ScadaWebShell5Beta/css/about.css index 7c83234f3..4e8d63333 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/about.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/about.css @@ -1,4 +1,41 @@ -#divContainer { +.main-content { + padding: 20px; + font-size: 14px; +} +h1 { + font-size: 23px; +} +h2 { + font-size: 17px; +} +h3 { + font-size: 14px; +} +h1, +h2, +h3 { + font-weight: 500; + margin: 20px 0 10px; +} +h1:first-of-type { + margin-top: 0; +} +a, +a:active, +a:hover, +a:focus, +a:visited { + color: #0073aa; + outline: 0; + text-decoration: none; +} +a:hover { + color: #00a0d2; +} +label { + font-weight: 600; +} +#divContainer { display: flex; height: 100%; } diff --git a/ScadaWeb/ScadaWebShell5Beta/css/about.less b/ScadaWeb/ScadaWebShell5Beta/css/about.less index e0515a21d..03144edbb 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/about.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/about.less @@ -1,4 +1,6 @@ -#divContainer { +@import "common/contentform.less"; + +#divContainer { display: flex; height: 100%; } diff --git a/ScadaWeb/ScadaWebShell5Beta/css/about.min.css b/ScadaWeb/ScadaWebShell5Beta/css/about.min.css index 75cb31439..71ddb91be 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/about.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/about.min.css @@ -1 +1 @@ -#divContainer{display:flex;height:100%;}#divAppName{margin:auto;font-size:35px;white-space:nowrap;}#spanVersion{font-size:15px;position:relative;top:-20px;} \ No newline at end of file +.main-content{padding:20px;font-size:14px;}h1{font-size:23px;}h2{font-size:17px;}h3{font-size:14px;}h1,h2,h3{font-weight:500;margin:20px 0 10px;}h1:first-of-type{margin-top:0;}a,a:active,a:hover,a:focus,a:visited{color:#0073aa;outline:0;text-decoration:none;}a:hover{color:#00a0d2;}label{font-weight:600;}#divContainer{display:flex;height:100%;}#divAppName{margin:auto;font-size:35px;white-space:nowrap;}#spanVersion{font-size:15px;position:relative;top:-20px;} \ No newline at end of file From f760b51bc748643dbcfbaf389b43e532167b7fe4 Mon Sep 17 00:00:00 2001 From: 2mik Date: Wed, 28 Sep 2016 10:28:45 +0300 Subject: [PATCH 295/382] ScadaWeb: minor fixes --- ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/MinDataRep.aspx | 2 +- ScadaWeb/ScadaWebCommon5Beta/WebSettings.cs | 2 +- ScadaWeb/ScadaWebCommon5Beta/WebUtils.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/MinDataRep.aspx b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/MinDataRep.aspx index 152f311ce..a75f298bd 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/MinDataRep.aspx +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/MinDataRep.aspx @@ -1,4 +1,4 @@ -<%@ Page Title="Minute Data Report" Language="C#" MasterPageFile="~/MasterMain.Master" AutoEventWireup="true" CodeBehind="MinDataRep.aspx.cs" Inherits="Scada.Web.Plugins.Chart.WFrmMinDataRep" %> +<%@ Page Title="Minute Data Report - Rapid SCADA" Language="C#" MasterPageFile="~/MasterMain.Master" AutoEventWireup="true" CodeBehind="MinDataRep.aspx.cs" Inherits="Scada.Web.Plugins.Chart.WFrmMinDataRep" %> <%@ Import Namespace="Scada.Web.Plugins.Chart" %> diff --git a/ScadaWeb/ScadaWebCommon5Beta/WebSettings.cs b/ScadaWeb/ScadaWebCommon5Beta/WebSettings.cs index 841dfeac2..d49f2487a 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/WebSettings.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/WebSettings.cs @@ -217,7 +217,7 @@ public bool LoadFromFile(string fileName, out string errMsg) } catch { - throw new Exception(string.Format(CommonPhrases.IncorrectXmlParamVal, name)); + throw new ScadaException(string.Format(CommonPhrases.IncorrectXmlParamVal, name)); } } } diff --git a/ScadaWeb/ScadaWebCommon5Beta/WebUtils.cs b/ScadaWeb/ScadaWebCommon5Beta/WebUtils.cs index f3f4a9637..92c5b0a33 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/WebUtils.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/WebUtils.cs @@ -116,7 +116,7 @@ public static DateTime GetDateFromQueryString(HttpRequest request, else { try { return new DateTime(year, month, day); } - catch { throw new Exception(WebPhrases.IncorrectDate); } + catch { throw new ScadaException(WebPhrases.IncorrectDate); } } } From 8a5660f4832fa637389adb533a2417fabdaf0a9f Mon Sep 17 00:00:00 2001 From: 2mik Date: Wed, 28 Sep 2016 17:49:32 +0300 Subject: [PATCH 296/382] ScadaWeb5: refactor query string access --- Report/RepBuilder/ExcelRepBuilder.cs | 8 +- ScadaData/ScadaData/ScadaUtils.Xml.cs | 2 +- .../PlgChart/plugins/Chart/Chart.aspx.cs | 8 +- .../plugins/Chart/MinDataRepOut.aspx.cs | 9 +- .../PlgScheme/plugins/Scheme/Scheme.aspx.cs | 2 +- .../PlgTable/plugins/Table/Command.aspx.cs | 5 +- .../PlgTable/plugins/Table/EventAck.aspx.cs | 7 +- .../PlgTable/plugins/Table/Events.aspx.cs | 2 +- .../plugins/Table/EventsRepOut.aspx.cs | 5 +- .../plugins/Table/HourDataRepOut.aspx.cs | 10 +- .../PlgTable/plugins/Table/Table.aspx.cs | 2 +- .../plugins/WebPage/Landing.aspx.cs | 3 +- .../ScadaWebCommon5Beta.csproj | 1 + .../ScadaWebCommon5Beta/Shell/RememberMe.cs | 2 +- .../WebUtils.QueryString.cs | 155 ++++++++++++++++++ ScadaWeb/ScadaWebCommon5Beta/WebUtils.cs | 81 +-------- ScadaWeb/ScadaWebShell5Beta/View.aspx.cs | 2 +- 17 files changed, 194 insertions(+), 110 deletions(-) create mode 100644 ScadaWeb/ScadaWebCommon5Beta/WebUtils.QueryString.cs diff --git a/Report/RepBuilder/ExcelRepBuilder.cs b/Report/RepBuilder/ExcelRepBuilder.cs index 806a26a42..86859a3af 100644 --- a/Report/RepBuilder/ExcelRepBuilder.cs +++ b/Report/RepBuilder/ExcelRepBuilder.cs @@ -1272,7 +1272,7 @@ public void SetNumberType() /// /// XML-узлы могут иметь текст, содержащий переносы строк /// - protected bool textBreaked; + protected bool textBroken; /// @@ -1333,7 +1333,7 @@ protected void SetNodeTextWithBreak(XmlNode xmlNode, string text, string textBre { if (text == null) text = ""; xmlNode.InnerText = text.Replace(textBreak, Break); - textBreaked = true; + textBroken = true; } /// @@ -1534,7 +1534,7 @@ public override void Make(Stream outStream, string templateDir) table = null; row = null; cell = null; - textBreaked = false; + textBroken = false; // создание отчёта - модификация xmlDoc StartXmlDocProc(); @@ -1542,7 +1542,7 @@ public override void Make(Stream outStream, string templateDir) FinalXmlDocProc(); // запись в выходной поток - if (textBreaked) + if (textBroken) { StringWriter stringWriter = new StringWriter(); xmlDoc.Save(stringWriter); diff --git a/ScadaData/ScadaData/ScadaUtils.Xml.cs b/ScadaData/ScadaData/ScadaUtils.Xml.cs index 28290bcf7..b00976a8d 100644 --- a/ScadaData/ScadaData/ScadaUtils.Xml.cs +++ b/ScadaData/ScadaData/ScadaUtils.Xml.cs @@ -1,5 +1,5 @@ /* - * Copyright 2014 Mikhail Shiryaev + * Copyright 2016 Mikhail Shiryaev * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/Chart.aspx.cs b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/Chart.aspx.cs index f8aa8731f..8b21bbe01 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/Chart.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/Chart.aspx.cs @@ -55,11 +55,9 @@ protected void Page_Load(object sender, EventArgs e) Translator.TranslatePage(Page, "Scada.Web.Plugins.Chart.WFrmChart"); // получение параметров запроса - int cnlNum; - int.TryParse(Request.QueryString["cnlNum"], out cnlNum); - int viewID; - int.TryParse(Request.QueryString["viewID"], out viewID); - DateTime startDate = WebUtils.GetDateFromQueryString(Request); + int cnlNum = Request.QueryString.GetParamAsInt("cnlNum"); + int viewID = Request.QueryString.GetParamAsInt("viewID"); + DateTime startDate = Request.QueryString.GetParamAsDate(); // проверка прав if (!userData.LoggedOn || diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/MinDataRepOut.aspx.cs b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/MinDataRepOut.aspx.cs index 5620a0bc6..a44ebb0e3 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/MinDataRepOut.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/MinDataRepOut.aspx.cs @@ -49,11 +49,10 @@ protected void Page_Load(object sender, EventArgs e) throw new ScadaException(CommonPhrases.NoRights); // получение параметров запроса - int[] cnlNums = WebUtils.QueryParamToIntArray(Request.QueryString["cnlNums"]); - int[] viewIDs = WebUtils.QueryParamToIntArray(Request.QueryString["viewIDs"]); - DateTime startDate = WebUtils.GetDateFromQueryString(Request); - int period; - int.TryParse(Request.QueryString["period"], out period); + int[] cnlNums = Request.QueryString.GetParamAsIntArray("cnlNums"); + int[] viewIDs = Request.QueryString.GetParamAsIntArray("viewIDs"); + DateTime startDate = Request.QueryString.GetParamAsDate(); + int period = Request.QueryString.GetParamAsInt("period"); // проверка прав и получение представления, если оно единственное RightsChecker rightsChecker = new RightsChecker(appData.ViewCache); diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.cs b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.cs index bb6d38754..1037f5808 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.cs @@ -58,7 +58,7 @@ protected void Page_Load(object sender, EventArgs e) Translator.TranslatePage(Page, "Scada.Web.Plugins.Scheme.WFrmScheme"); // получение ид. представления из параметров запроса - int.TryParse(Request.QueryString["viewID"], out viewID); + viewID = Request.QueryString.GetParamAsInt("viewID"); // проверка прав на просмотр представления EntityRights rights = userData.LoggedOn ? diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs index 31b64cf99..ec6e96f82 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs @@ -239,11 +239,10 @@ protected void Page_Load(object sender, EventArgs e) else { // получение параметров запроса и сохранение во ViewState - int.TryParse(Request.QueryString["ctrlCnlNum"], out ctrlCnlNum); + ctrlCnlNum = Request.QueryString.GetParamAsInt("ctrlCnlNum"); ViewState["CtrlCnlNum"] = ctrlCnlNum; - int viewID; - int.TryParse(Request.QueryString["viewID"], out viewID); + int viewID = Request.QueryString.GetParamAsInt("viewID"); // проверка прав if (!userData.UserRights.GetUiObjRights(viewID).ControlRight || diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventAck.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventAck.aspx.cs index a9ec3ad12..a23b82a96 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventAck.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventAck.aspx.cs @@ -89,13 +89,12 @@ protected void Page_Load(object sender, EventArgs e) Translator.TranslatePage(Page, "Scada.Web.Plugins.Table.WFrmEventAck"); // получение параметров запроса и сохранение во ViewState - evDate = WebUtils.GetDateFromQueryString(Request); - int.TryParse(Request.QueryString["evNum"], out evNum); + evDate = Request.QueryString.GetParamAsDate(); + evNum = Request.QueryString.GetParamAsInt("evNum"); ViewState["EvDate"] = evDate; ViewState["EvNum"] = evNum; - int viewID; - int.TryParse(Request.QueryString["viewID"], out viewID); + int viewID = Request.QueryString.GetParamAsInt("viewID"); // получение события EventTableLight tblEvent = appData.DataAccess.DataCache.GetEventTable(evDate); diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs index 6e024d3dd..ac60c8fc6 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs @@ -61,7 +61,7 @@ protected void Page_Load(object sender, EventArgs e) Translator.TranslatePage(Page, "Scada.Web.Plugins.Table.WFrmEvents"); // получение ид. представления из параметров запроса - int.TryParse(Request.QueryString["viewID"], out viewID); + viewID = Request.QueryString.GetParamAsInt("viewID"); // проверка прав на просмотр представления if (!(userData.LoggedOn && userData.UserRights.GetUiObjRights(viewID).ViewRight)) diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventsRepOut.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventsRepOut.aspx.cs index 6cb98ad0e..7ffb52bb1 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventsRepOut.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/EventsRepOut.aspx.cs @@ -45,8 +45,7 @@ protected void Page_Load(object sender, EventArgs e) UserData userData = UserData.GetUserData(); // получение ид. представления из параметров запроса - int viewID; - int.TryParse(Request.QueryString["viewID"], out viewID); + int viewID = Request.QueryString.GetParamAsInt("viewID"); bool eventsByView = viewID > 0; // проверка прав @@ -67,7 +66,7 @@ protected void Page_Load(object sender, EventArgs e) } // получение даты запрашиваемых событий - DateTime reqDate = WebUtils.GetDateFromQueryString(Request); + DateTime reqDate = Request.QueryString.GetParamAsDate(); // генерация отчёта RepBuilder repBuilder = new EventsRepBuilder(appData.DataAccess); diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/HourDataRepOut.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/HourDataRepOut.aspx.cs index e887d9ff1..ba5d3f5b6 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/HourDataRepOut.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/HourDataRepOut.aspx.cs @@ -44,8 +44,7 @@ protected void Page_Load(object sender, EventArgs e) UserData userData = UserData.GetUserData(); // получение ид. представления из параметров запроса - int viewID; - int.TryParse(Request.QueryString["viewID"], out viewID); + int viewID = Request.QueryString.GetParamAsInt("viewID"); // проверка прав if (!(userData.LoggedOn && userData.UserRights.GetUiObjRights(viewID).ViewRight)) @@ -55,10 +54,9 @@ protected void Page_Load(object sender, EventArgs e) TableView tableView = appData.ViewCache.GetView(viewID, true); // получение оставшихся параметров запроса - DateTime reqDate = WebUtils.GetDateFromQueryString(Request); - int startHour, endHour; - int.TryParse(Request.QueryString["startHour"], out startHour); - int.TryParse(Request.QueryString["endHour"], out endHour); + DateTime reqDate = Request.QueryString.GetParamAsDate(); + int startHour = Request.QueryString.GetParamAsInt("startHour"); + int endHour = Request.QueryString.GetParamAsInt("endHour"); // генерация отчёта RepBuilder repBuilder = new HourDataRepBuilder(appData.DataAccess); diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs index 4e387c064..a51734ff4 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs @@ -246,7 +246,7 @@ protected void Page_Load(object sender, EventArgs e) Translator.TranslatePage(Page, "Scada.Web.Plugins.Table.WFrmTable"); // получение ид. представления из параметров запроса - int.TryParse(Request.QueryString["viewID"], out viewID); + viewID = Request.QueryString.GetParamAsInt("viewID"); // проверка прав на просмотр представления EntityRights rights = userData.LoggedOn ? diff --git a/ScadaWeb/OpenPlugins/PlgWebPage/plugins/WebPage/Landing.aspx.cs b/ScadaWeb/OpenPlugins/PlgWebPage/plugins/WebPage/Landing.aspx.cs index 66f721608..2062768fa 100644 --- a/ScadaWeb/OpenPlugins/PlgWebPage/plugins/WebPage/Landing.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgWebPage/plugins/WebPage/Landing.aspx.cs @@ -41,8 +41,7 @@ protected void Page_Load(object sender, EventArgs e) UserData userData = UserData.GetUserData(); // получение ид. представления из параметров запроса - int viewID; - int.TryParse(Request.QueryString["viewID"], out viewID); + int viewID = Request.QueryString.GetParamAsInt("viewID"); // проверка прав на просмотр представления EntityRights rights = userData.LoggedOn ? diff --git a/ScadaWeb/ScadaWebCommon5Beta/ScadaWebCommon5Beta.csproj b/ScadaWeb/ScadaWebCommon5Beta/ScadaWebCommon5Beta.csproj index 47425de45..ac9a6dc50 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/ScadaWebCommon5Beta.csproj +++ b/ScadaWeb/ScadaWebCommon5Beta/ScadaWebCommon5Beta.csproj @@ -88,6 +88,7 @@ + diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/RememberMe.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/RememberMe.cs index 24d18465d..8bad73815 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/RememberMe.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/RememberMe.cs @@ -95,7 +95,7 @@ public static string GenerateID() /// /// Длительность хранения информации о входе пользователя в систему /// - protected static readonly TimeSpan ExpireSpan = TimeSpan.FromDays(7); + protected static readonly TimeSpan ExpireSpan = WebUtils.CookieExpiration; /// /// Объект для работы с хранилищем приложения diff --git a/ScadaWeb/ScadaWebCommon5Beta/WebUtils.QueryString.cs b/ScadaWeb/ScadaWebCommon5Beta/WebUtils.QueryString.cs new file mode 100644 index 000000000..8ae5abd86 --- /dev/null +++ b/ScadaWeb/ScadaWebCommon5Beta/WebUtils.QueryString.cs @@ -0,0 +1,155 @@ +/* + * Copyright 2016 Mikhail Shiryaev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Product : Rapid SCADA + * Module : ScadaWebCommon + * Summary : The class contains utility methods for web applications. Query string utilities + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + */ + +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Web; + +namespace Scada.Web +{ + partial class WebUtils + { + /// + /// Получить значение параметра из строки запроса как целое число + /// + public static int GetParamAsInt(this NameValueCollection queryString, string paramName) + { + int paramVal; + return int.TryParse(queryString[paramName], out paramVal) ? paramVal : 0; + } + + /// + /// Получить значение параметра из строки запроса как дату + /// + public static DateTime GetParamAsDate(this NameValueCollection queryString, + string yearParamName = "year", string monthParamName = "month", string dayParamName = "day") + { + int year, month, day; + int.TryParse(queryString[yearParamName], out year); + int.TryParse(queryString[monthParamName], out month); + int.TryParse(queryString[dayParamName], out day); + + if (year == 0 && month == 0 && day == 0) + { + return DateTime.Today; + } + else + { + try { return new DateTime(year, month, day); } + catch { throw new ScadaException(WebPhrases.IncorrectDate); } + } + } + + /// + /// Получить значение параметра из строки запроса как дату + /// + public static DateTime GetParamAsDate(this NameValueCollection queryString, string paramName) + { + string dateStr = queryString[paramName]; + DateTime dateTime; + + if (string.IsNullOrEmpty(dateStr)) + return DateTime.Today; + else if (DateTime.TryParse(dateStr, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None, out dateTime)) + return dateTime.Date; + else + throw new ScadaException(WebPhrases.IncorrectDate); + } + + /// + /// Получить значение параметра из строки запроса как массив целых чисел + /// + public static int[] GetParamAsIntArray(this NameValueCollection queryString, string paramName) + { + return QueryParamToIntArray(queryString[paramName]); + } + + /// + /// Получить значение параметра из строки запроса как множество целых чисел + /// + public static HashSet GetParamAsIntSet(this NameValueCollection queryString, string paramName) + { + return QueryParamToIntSet(queryString[paramName]); + } + + /// + /// Преобразовать значение параметра запроса в массив целых чисел + /// + public static int[] QueryParamToIntArray(string paramVal) + { + try + { + string[] elems = (paramVal ?? "").Split(new char[] { ' ', ',' }, + StringSplitOptions.RemoveEmptyEntries); + int len = elems.Length; + int[] arr = new int[len]; + + for (int i = 0; i < len; i++) + arr[i] = int.Parse(elems[i]); + + return arr; + } + catch (FormatException ex) + { + throw new FormatException("Query parameter is not array of integers.", ex); + } + } + + /// + /// Преобразовать значение параметра запроса в множество целых чисел + /// + public static HashSet QueryParamToIntSet(string paramVal) + { + try + { + string[] elems = (paramVal ?? "").Split(new char[] { ' ', ',' }, + StringSplitOptions.RemoveEmptyEntries); + int len = elems.Length; + HashSet hashSet = new HashSet(); + + for (int i = 0; i < len; i++) + hashSet.Add(int.Parse(elems[i])); + + return hashSet; + } + catch (FormatException ex) + { + throw new FormatException("Query parameter is not set of integers.", ex); + } + } + + /// + /// Преобразовать дату в значение параметра запроса + /// + public static string DateToQueryParam(DateTime date) + { + return date.ToString("yyyy-MM-dd"); + } + } +} diff --git a/ScadaWeb/ScadaWebCommon5Beta/WebUtils.cs b/ScadaWeb/ScadaWebCommon5Beta/WebUtils.cs index 92c5b0a33..b4e700733 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/WebUtils.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/WebUtils.cs @@ -34,12 +34,20 @@ namespace Scada.Web /// The class contains utility methods for web applications /// Класс, содержащий вспомогательные методы для веб-приложений /// - public static class WebUtils + public static partial class WebUtils { /// /// Начало отчёта времени в Unix, которое используется в Javascript реализации даты /// public static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + /// + /// Длительность хранения данных в кэше приложения + /// + public static readonly TimeSpan CacheExpiration = TimeSpan.FromMinutes(1); + /// + /// Длительность хранения данных в cookies + /// + public static readonly TimeSpan CookieExpiration = TimeSpan.FromDays(7); /// @@ -95,77 +103,6 @@ public static string HtmlEncodeWithBreak(object val) return HttpUtility.HtmlEncode(val).Replace("\n", "
"); } - /// - /// Получить дату из параметров запроса - /// - public static DateTime GetDateFromQueryString(HttpRequest request, - string yearParamName = "year", string monthParamName = "month", string dayParamName = "day") - { - if (request == null) - throw new ArgumentNullException("request"); - - int year, month, day; - int.TryParse(request.QueryString[yearParamName], out year); - int.TryParse(request.QueryString[monthParamName], out month); - int.TryParse(request.QueryString[dayParamName], out day); - - if (year == 0 && month == 0 && day == 0) - { - return DateTime.Today; - } - else - { - try { return new DateTime(year, month, day); } - catch { throw new ScadaException(WebPhrases.IncorrectDate); } - } - } - - /// - /// Преобразовать параметр запроса в массив целых чисел - /// - public static int[] QueryParamToIntArray(string param) - { - try - { - string[] elems = (param ?? "").Split(new char[] { ' ', ',' }, - StringSplitOptions.RemoveEmptyEntries); - int len = elems.Length; - int[] arr = new int[len]; - - for (int i = 0; i < len; i++) - arr[i] = int.Parse(elems[i]); - - return arr; - } - catch (FormatException ex) - { - throw new FormatException("Query parameter is not array of integers.", ex); - } - } - - /// - /// Преобразовать параметр запроса в множество целых чисел - /// - public static HashSet QueryParamToIntSet(string param) - { - try - { - string[] elems = (param ?? "").Split(new char[] { ' ', ',' }, - StringSplitOptions.RemoveEmptyEntries); - int len = elems.Length; - HashSet hashSet = new HashSet(); - - for (int i = 0; i < len; i++) - hashSet.Add(int.Parse(elems[i])); - - return hashSet; - } - catch (FormatException ex) - { - throw new FormatException("Query parameter is not set of integers.", ex); - } - } - /// /// Преобразовать словарь в объект JavaScript /// diff --git a/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs b/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs index eeb70db5c..f3a8fa7de 100644 --- a/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs +++ b/ScadaWeb/ScadaWebShell5Beta/View.aspx.cs @@ -79,7 +79,7 @@ protected void Page_Load(object sender, EventArgs e) phrases = WebUtils.DictionaryToJs(dict); // получение ид. и ссылки представления для загрузки - int.TryParse(Request.QueryString["viewID"], out initialViewID); + initialViewID = Request.QueryString.GetParamAsInt("viewID"); ViewNode viewNode; if (initialViewID > 0) From 64ab751638e5d5451d2cbce36355a1e3cba99df7 Mon Sep 17 00:00:00 2001 From: 2mik Date: Wed, 28 Sep 2016 23:40:51 +0300 Subject: [PATCH 297/382] ScadaWeb: dev --- .../ScadaWebCommon5Beta/Shell/UserContent.cs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/UserContent.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/UserContent.cs index 45e5d5d5b..5aad444d4 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/UserContent.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/UserContent.cs @@ -227,6 +227,33 @@ public ReportItem GetReportItem(int reportID) throw new NotImplementedException(); } + /// + /// Получить элемент отчёта, имеющий спецификацию заданного типа, по идентификатору + /// + public ReportItem GetReportItem(int reportID, Type specType, bool throwOnFail = false) + { + ReportItem reportItem = GetReportItem(reportID); + + if (reportItem == null) + { + if (throwOnFail) + throw new ScadaException("!!!"); + else + return null; + } + else if (reportItem.ReportSpec == null || reportItem.ReportSpec.GetType() != specType) + { + if (throwOnFail) + throw new ScadaException("!!!"); + else + return null; + } + else + { + return reportItem; + } + } + /// /// Получить элемент окна данных по идентификатору /// @@ -234,5 +261,13 @@ public DataWndItem GetDataWndItem(int dataWndID) { throw new NotImplementedException(); } + + /// + /// Получить элемент окна данных, имеющий спецификацию заданного типа, по идентификатору + /// + public DataWndItem GetDataWndItem(int dataWndID, bool throwOnFail = false) + { + throw new NotImplementedException(); + } } } From 73eceb22b182528d14d6ac2ae148974213c3e5c5 Mon Sep 17 00:00:00 2001 From: 2mik Date: Thu, 29 Sep 2016 14:09:30 +0300 Subject: [PATCH 298/382] ScadaWeb: dev --- ScadaData/ScadaData/Client/BaseView.cs | 2 +- ScadaData/ScadaData/Client/ISupportLoading.cs | 46 ++++ ScadaData/ScadaData/Client/ServerComm.cs | 246 +++++++++--------- ScadaData/ScadaData/ScadaData.csproj | 1 + .../AppCode/Chart/MinDataRepBuilder.cs | 2 - ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs | 2 +- ScadaWeb/ScadaWebCommon5Beta/RepUtils.cs | 8 + .../ScadaWebCommon5Beta/Shell/UserContent.cs | 52 +++- .../ScadaWebCommon5Beta/Shell/UserViews.cs | 1 + ScadaWeb/ScadaWebCommon5Beta/UserRights.cs | 9 - ScadaWeb/ScadaWebCommon5Beta/WebPhrases.cs | 19 ++ .../lang/ScadaWeb.en-GB.xml | 6 + .../lang/ScadaWeb.ru-RU.xml | 6 + 13 files changed, 260 insertions(+), 140 deletions(-) create mode 100644 ScadaData/ScadaData/Client/ISupportLoading.cs diff --git a/ScadaData/ScadaData/Client/BaseView.cs b/ScadaData/ScadaData/Client/BaseView.cs index 24f02ac72..c16c45d9b 100644 --- a/ScadaData/ScadaData/Client/BaseView.cs +++ b/ScadaData/ScadaData/Client/BaseView.cs @@ -39,7 +39,7 @@ namespace Scada.Client /// Write operations must be synchronized /// Дочерние представления должны обеспечивать потокобезопасный доступ на чтение при условии, /// что объект не изменяется. Операции записи должны синхронизироваться - public abstract class BaseView + public abstract class BaseView : ISupportLoading { /// /// Конструктор diff --git a/ScadaData/ScadaData/Client/ISupportLoading.cs b/ScadaData/ScadaData/Client/ISupportLoading.cs new file mode 100644 index 000000000..02efb6a29 --- /dev/null +++ b/ScadaData/ScadaData/Client/ISupportLoading.cs @@ -0,0 +1,46 @@ +/* + * Copyright 2016 Mikhail Shiryaev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Product : Rapid SCADA + * Module : ScadaData + * Summary : Specifies objects that support loading + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + */ + +using System.IO; + +namespace Scada.Client +{ + /// + /// Specifies objects that support loading + /// Определяет объекты, поддерживающие загрузку + /// + public interface ISupportLoading + { + /// + /// Загрузить объект из потока + /// + void LoadFromStream(Stream stream); + + /// + /// Очистить объект + /// + void Clear(); + } +} diff --git a/ScadaData/ScadaData/Client/ServerComm.cs b/ScadaData/ScadaData/Client/ServerComm.cs index 4fa7edf45..b774fd773 100644 --- a/ScadaData/ScadaData/Client/ServerComm.cs +++ b/ScadaData/ScadaData/Client/ServerComm.cs @@ -77,6 +77,7 @@ public enum CommStates /// /// Роли пользователей /// + [Obsolete("Use BaseValues.Roles")] public enum Roles { /// @@ -465,13 +466,13 @@ protected bool Connect() // обработка считанных данных if (bytesRead == buf.Length && CheckDataFormat(buf, 0x01)) { - Roles role = (Roles)buf[3]; + int roleID = buf[3]; - if (role == Roles.App) + if (roleID == BaseValues.Roles.App) { commState = CommStates.Authorized; } - else if (role < Roles.Err) + else if (roleID < BaseValues.Roles.Err) { errMsg = Localization.UseRussian ? "Недостаточно прав для соединения со SCADA-Сервером" : @@ -479,7 +480,7 @@ protected bool Connect() WriteAction(errMsg, Log.ActTypes.Error); commState = CommStates.Error; } - else // role == Roles.Err + else // roleID == BaseValues.Roles.Err { errMsg = Localization.UseRussian ? "Неверное имя пользователя или пароль" : @@ -611,94 +612,107 @@ protected void ClearNetStream() /// protected bool RestoreConnection() { - bool connectNeeded = false; // требуется повторное соединение - DateTime now = DateTime.Now; - - if (commState >= CommStates.Authorized) + try { - if (now - restConnSuccDT > PingSpan) + bool connectNeeded = false; // требуется повторное соединение + DateTime now = DateTime.Now; + + if (commState >= CommStates.Authorized) { - // проверка соединения - try + if (now - restConnSuccDT > PingSpan) { - WriteAction(Localization.UseRussian ? - "Запрос состояния SCADA-Сервера" : - "Request SCADA-Server state", Log.ActTypes.Action); - commState = CommStates.WaitResponse; - - // запрос состояния SCADA-Сервера (ping) - byte[] buf = new byte[3]; - buf[0] = 0x03; - buf[1] = 0x00; - buf[2] = 0x02; - netStream.Write(buf, 0, 3); - - // приём результата - buf = new byte[4]; - netStream.Read(buf, 0, 4); - - // обработка результата - if (CheckDataFormat(buf, 0x02)) + // проверка соединения + try { - commState = buf[3] > 0 ? CommStates.Authorized : CommStates.NotReady; + WriteAction(Localization.UseRussian ? + "Запрос состояния SCADA-Сервера" : + "Request SCADA-Server state", Log.ActTypes.Action); + commState = CommStates.WaitResponse; + + // запрос состояния SCADA-Сервера (ping) + byte[] buf = new byte[3]; + buf[0] = 0x03; + buf[1] = 0x00; + buf[2] = 0x02; + netStream.Write(buf, 0, 3); + + // приём результата + buf = new byte[4]; + netStream.Read(buf, 0, 4); + + // обработка результата + if (CheckDataFormat(buf, 0x02)) + { + commState = buf[3] > 0 ? CommStates.Authorized : CommStates.NotReady; + } + else + { + errMsg = Localization.UseRussian ? + "Неверный формат ответа SCADA-Сервера на запрос состояния" : + "Incorrect SCADA-Server response to state request"; + WriteAction(errMsg, Log.ActTypes.Error); + commState = CommStates.Error; + connectNeeded = true; + } } - else + catch { - errMsg = Localization.UseRussian ? - "Неверный формат ответа SCADA-Сервера на запрос состояния" : - "Incorrect SCADA-Server response to state request"; - WriteAction(errMsg, Log.ActTypes.Error); - commState = CommStates.Error; connectNeeded = true; } } - catch - { - connectNeeded = true; - } } - } - else if (now - restConnErrDT > ConnectSpan) - { - connectNeeded = true; - } - - // соединение при необходимости - if (connectNeeded) - { - if (tcpClient != null) - Disconnect(); - - if (Connect()) + else if (now - restConnErrDT > ConnectSpan) { - restConnSuccDT = now; - restConnErrDT = DateTime.MinValue; - return true; + connectNeeded = true; } - else - { - restConnSuccDT = DateTime.MinValue; - restConnErrDT = now; - return false; - } - } - else - { - ClearNetStream(); // очистка потока данных TCP-клиента - if (commState >= CommStates.Authorized) + // соединение при необходимости + if (connectNeeded) { - restConnSuccDT = now; - return true; + if (tcpClient != null) + Disconnect(); + + if (Connect()) + { + restConnSuccDT = now; + restConnErrDT = DateTime.MinValue; + return true; + } + else + { + restConnSuccDT = DateTime.MinValue; + restConnErrDT = now; + return false; + } } else { - errMsg = Localization.UseRussian ? - "Невозможно соединиться со SCADA-Сервером. Повторите попытку." : - "Unable to connect to SCADA-Server. Try again."; - return false; + ClearNetStream(); // очистка потока данных TCP-клиента + + if (commState >= CommStates.Authorized) + { + restConnSuccDT = now; + return true; + } + else + { + errMsg = Localization.UseRussian ? + "Невозможно соединиться со SCADA-Сервером. Повторите попытку." : + "Unable to connect to SCADA-Server. Try again."; + WriteAction(errMsg, Log.ActTypes.Error); + return false; + } } } + catch (Exception ex) + { + errMsg = (Localization.UseRussian ? + "Ошибка при восстановлении соединения со SCADA-Сервером: " : + "Error restoring connection with SCADA-Server: ") + ex.Message; + WriteAction(errMsg, Log.ActTypes.Exception); + commState = CommStates.Error; + return false; + } } /// @@ -717,7 +731,7 @@ protected void RestoreReceiveTimeout() /// /// Принять файл от SCADA-Сервера /// - protected bool ReceiveFile(Dirs dir, string fileName, Stream inStream) + protected bool ReceiveFileToStream(Dirs dir, string fileName, Stream inStream) { bool result = false; string filePath = DirToString(dir) + fileName; @@ -733,11 +747,11 @@ protected bool ReceiveFile(Dirs dir, string fileName, Stream inStream) commState = CommStates.WaitResponse; tcpClient.ReceiveTimeout = commSettings.ServerTimeout; - const int dataSize = 10240; // размер запрашиваемых данных 10 кБ - const byte dataSizeL = dataSize % 256; - const byte dataSizeH = dataSize / 256; + const int DataSize = 50 * 1024; // размер запрашиваемых данных 50 КБ + const byte DataSizeL = DataSize % 256; + const byte DataSizeH = DataSize / 256; - byte[] buf = new byte[6 + dataSize]; // буфер отправляемых и получаемых данных + byte[] buf = new byte[6 + DataSize]; // буфер отправляемых и получаемых данных bool open = true; // выполняется открытие файла bool stop = false; // признак завершения приёма данных @@ -754,8 +768,8 @@ protected bool ReceiveFile(Dirs dir, string fileName, Stream inStream) buf[3] = (byte)dir; buf[4] = fileNameLen; Array.Copy(Encoding.Default.GetBytes(fileName), 0, buf, 5, fileNameLen); - buf[cmdLen - 2] = dataSizeL; - buf[cmdLen - 1] = dataSizeH; + buf[cmdLen - 2] = DataSizeL; + buf[cmdLen - 1] = DataSizeH; netStream.Write(buf, 0, cmdLen); } else @@ -764,8 +778,8 @@ protected bool ReceiveFile(Dirs dir, string fileName, Stream inStream) buf[0] = 0x05; buf[1] = 0x00; buf[2] = 0x0A; - buf[3] = dataSizeL; - buf[4] = dataSizeH; + buf[3] = DataSizeL; + buf[4] = DataSizeH; netStream.Write(buf, 0, 5); } @@ -778,7 +792,7 @@ protected bool ReceiveFile(Dirs dir, string fileName, Stream inStream) if (bytesRead == headerLen) { dataSizeRead = buf[headerLen - 2] + 256 * buf[headerLen - 1]; - if (0 < dataSizeRead && dataSizeRead <= dataSize) + if (0 < dataSizeRead && dataSizeRead <= DataSize) bytesRead += ReadNetStream(buf, headerLen, dataSizeRead); } @@ -792,7 +806,7 @@ protected bool ReceiveFile(Dirs dir, string fileName, Stream inStream) { inStream.Write(buf, 6, dataSizeRead); commState = CommStates.Authorized; - stop = dataSizeRead < dataSize; + stop = dataSizeRead < DataSize; } else { @@ -808,7 +822,7 @@ protected bool ReceiveFile(Dirs dir, string fileName, Stream inStream) { inStream.Write(buf, 5, dataSizeRead); commState = CommStates.Authorized; - stop = dataSizeRead < dataSize; + stop = dataSizeRead < DataSize; } } else @@ -952,7 +966,7 @@ public bool CheckUser(string username, string password, out int roleID) { Monitor.Enter(tcpLock); bool result = false; - roleID = (int)Roles.Disabled; + roleID = BaseValues.Roles.Disabled; errMsg = ""; try @@ -1034,7 +1048,7 @@ public bool ReceiveBaseTable(string tableName, DataTable dataTable) { using (MemoryStream memStream = new MemoryStream()) { - if (ReceiveFile(Dirs.BaseDAT, tableName, memStream)) + if (ReceiveFileToStream(Dirs.BaseDAT, tableName, memStream)) { BaseAdapter adapter = new BaseAdapter(); adapter.Stream = memStream; @@ -1095,7 +1109,7 @@ public bool ReceiveSrezTable(string tableName, SrezTableLight srezTableLight) // приём данных using (MemoryStream memStream = new MemoryStream()) { - if (ReceiveFile(dir, tableName, memStream)) + if (ReceiveFileToStream(dir, tableName, memStream)) { SrezAdapter adapter = new SrezAdapter(); adapter.Stream = memStream; @@ -1273,7 +1287,7 @@ public bool ReceiveEventTable(string tableName, EventTableLight eventTableLight) { using (MemoryStream memStream = new MemoryStream()) { - if (ReceiveFile(Dirs.Events, tableName, memStream)) + if (ReceiveFileToStream(Dirs.Events, tableName, memStream)) { EventAdapter adapter = new EventAdapter(); adapter.Stream = memStream; @@ -1313,6 +1327,17 @@ public bool ReceiveEventTable(string tableName, EventTableLight eventTableLight) /// Принять представление от SCADA-Сервера /// public bool ReceiveView(string fileName, BaseView view) + { + bool result = ReceiveUiObj(fileName, view); + if (result && view != null) + view.Path = fileName; + return result; + } + + /// + /// Принять объект пользовательского интерфейса от SCADA-Сервера + /// + public bool ReceiveUiObj(string fileName, ISupportLoading uiObj) { Monitor.Enter(tcpLock); bool result = false; @@ -1326,9 +1351,9 @@ public bool ReceiveView(string fileName, BaseView view) { using (MemoryStream memStream = new MemoryStream()) { - if (ReceiveFile(Dirs.Itf, fileName, memStream)) + if (ReceiveFileToStream(Dirs.Itf, fileName, memStream)) { - view.LoadFromStream(memStream); + uiObj.LoadFromStream(memStream); result = true; } } @@ -1336,18 +1361,15 @@ public bool ReceiveView(string fileName, BaseView view) } finally { - // очистка представления, если не удалось получить новые данные if (!result) - view.Clear(); - // установка пути файла представления - view.Path = fileName; + uiObj.Clear(); } } catch (Exception ex) { - errMsg = (Localization.UseRussian ? - "Ошибка при приёме представления от SCADA-Сервера: " : - "Error receiving view from SCADA-Server: ") + ex.Message; + errMsg = (Localization.UseRussian ? + "Ошибка при приёме объекта пользовательского интерфейса от SCADA-Сервера: " : + "Error receiving user interface object from SCADA-Server: ") + ex.Message; WriteAction(errMsg, Log.ActTypes.Exception); } finally @@ -1359,32 +1381,14 @@ public bool ReceiveView(string fileName, BaseView view) } /// - /// Принять объект интерфейса от SCADA-Сервера + /// Принять файл от SCADA-Сервера /// - public bool ReceiveItfObj(string fileName, Stream stream) + public bool ReceiveFile(Dirs dir, string fileName, Stream stream) { - Monitor.Enter(tcpLock); - bool result = false; - errMsg = ""; - - try + lock (tcpLock) { - if (RestoreConnection() && ReceiveFile(Dirs.Itf, fileName, stream)) - result = true; + return RestoreConnection() && ReceiveFileToStream(dir, fileName, stream); } - catch (Exception ex) - { - errMsg = (Localization.UseRussian ? - "Ошибка при приёме объекта интерфейса от SCADA-Сервера: " : - "Error receiving interface object from SCADA-Server: ") + ex.Message; - WriteAction(errMsg, Log.ActTypes.Exception); - } - finally - { - Monitor.Exit(tcpLock); - } - - return result; } /// @@ -1940,7 +1944,7 @@ public void Close() /// /// Получить роль пользователя по её идентификатору /// - [Obsolete("Get rid of using ServerComm.Roles. Use BaseValues.Roles instead")] + [Obsolete("Use BaseValues.Roles")] public static Roles GetRole(int roleID) { if ((int)Roles.Admin <= roleID && roleID <= (int)Roles.App) diff --git a/ScadaData/ScadaData/ScadaData.csproj b/ScadaData/ScadaData/ScadaData.csproj index df29941aa..7b3f75cc2 100644 --- a/ScadaData/ScadaData/ScadaData.csproj +++ b/ScadaData/ScadaData/ScadaData.csproj @@ -51,6 +51,7 @@ + diff --git a/ScadaWeb/OpenPlugins/PlgChart/AppCode/Chart/MinDataRepBuilder.cs b/ScadaWeb/OpenPlugins/PlgChart/AppCode/Chart/MinDataRepBuilder.cs index a87c896c2..fc7c2358e 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/AppCode/Chart/MinDataRepBuilder.cs +++ b/ScadaWeb/OpenPlugins/PlgChart/AppCode/Chart/MinDataRepBuilder.cs @@ -27,8 +27,6 @@ using Scada.Data.Models; using Scada.Data.Tables; using System; -using System.Collections.Generic; -using System.Xml; using Utils.Report; namespace Scada.Web.Plugins.Chart diff --git a/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs b/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs index 9223cd3ce..4e8ddd641 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs @@ -624,7 +624,7 @@ public string GetViewStamp(int viewID) { AppData.Log.WriteException(ex, Localization.UseRussian ? "Ошибка при получении метки предсталения с ид.={0} из кеша" : - "Error getting stamp of the view with id={0} from the cache", viewID); + "Error getting stamp of the view with ID={0} from the cache", viewID); return GetErrorDtoJs(ex); } } diff --git a/ScadaWeb/ScadaWebCommon5Beta/RepUtils.cs b/ScadaWeb/ScadaWebCommon5Beta/RepUtils.cs index ff509bced..5a4d5caf4 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/RepUtils.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/RepUtils.cs @@ -83,5 +83,13 @@ public static void WriteGenerationAction(Log log, RepBuilder repBuilder, UserDat { log.WriteAction(string.Format(WebPhrases.GenReport, repBuilder.RepName, userData.UserProps.UserName)); } + + /// + /// Записать сообщение о генерации отчёта в журнал + /// + public static void WriteGenerationAction(Log log, string repName, UserData userData) + { + log.WriteAction(string.Format(WebPhrases.GenReport, repName, userData.UserProps.UserName)); + } } } diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/UserContent.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/UserContent.cs index 5aad444d4..9c0e9eea5 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/UserContent.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/UserContent.cs @@ -42,6 +42,14 @@ public class UserContent /// Журнал /// protected readonly Log log; + /// + /// Словарь элементов отчётов, ключ - ид. отчёта + /// + protected readonly Dictionary reportItemDict; + /// + /// Словарь элементов окон данных, ключ - ид. окна данных + /// + protected readonly Dictionary dataWndItemDict; /// @@ -60,6 +68,9 @@ public UserContent(Log log) throw new ArgumentNullException("log"); this.log = log; + reportItemDict = new Dictionary(); + dataWndItemDict = new Dictionary(); + ReportItems = new List(); DataWndItems = new List(); } @@ -116,7 +127,10 @@ protected void AddContentFromBase(UserRights userRights, Dictionary public ReportItem GetReportItem(int reportID) { - throw new NotImplementedException(); + ReportItem reportItem; + return reportItemDict.TryGetValue(reportID, out reportItem) ? reportItem : null; } /// @@ -237,14 +257,14 @@ public ReportItem GetReportItem(int reportID, Type specType, bool throwOnFail = if (reportItem == null) { if (throwOnFail) - throw new ScadaException("!!!"); + throw new ScadaException(string.Format(WebPhrases.ReportNotFound, reportID)); else return null; } else if (reportItem.ReportSpec == null || reportItem.ReportSpec.GetType() != specType) { if (throwOnFail) - throw new ScadaException("!!!"); + throw new ScadaException(string.Format(WebPhrases.UnexpectedReportSpec, reportID)); else return null; } @@ -259,15 +279,35 @@ public ReportItem GetReportItem(int reportID, Type specType, bool throwOnFail = /// public DataWndItem GetDataWndItem(int dataWndID) { - throw new NotImplementedException(); + DataWndItem dataWndItem; + return dataWndItemDict.TryGetValue(dataWndID, out dataWndItem) ? dataWndItem : null; } /// /// Получить элемент окна данных, имеющий спецификацию заданного типа, по идентификатору /// - public DataWndItem GetDataWndItem(int dataWndID, bool throwOnFail = false) + public DataWndItem GetDataWndItem(int dataWndID, Type specType, bool throwOnFail = false) { - throw new NotImplementedException(); + DataWndItem dataWndItem = GetDataWndItem(dataWndID); + + if (dataWndItem == null) + { + if (throwOnFail) + throw new ScadaException(string.Format(WebPhrases.DataWndNotFound, dataWndID)); + else + return null; + } + else if (dataWndItem.DataWndSpec == null || dataWndItem.DataWndSpec.GetType() != specType) + { + if (throwOnFail) + throw new ScadaException(string.Format(WebPhrases.UnexpectedDataWndSpec, dataWndID)); + else + return null; + } + else + { + return dataWndItem; + } } } } diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/UserViews.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/UserViews.cs index 361443f11..0b4d06683 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/UserViews.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/UserViews.cs @@ -187,6 +187,7 @@ public void Init(UserData userData, DataAccess dataAccess) try { viewNodeDict.Clear(); + ViewNodes.Clear(); if (userData.UserRights != null && userData.UiObjSpecs != null) { diff --git a/ScadaWeb/ScadaWebCommon5Beta/UserRights.cs b/ScadaWeb/ScadaWebCommon5Beta/UserRights.cs index ab7747244..fe87ed37f 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/UserRights.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/UserRights.cs @@ -146,14 +146,5 @@ public EntityRights GetUiObjRights(int uiObjID) rights : EntityRights.NoRights; } } - - /// - /// Получить права на контент - /// - [Obsolete] - public EntityRights GetContentRights(string contentTypeCode) - { - return EntityRights.NoRights; - } } } \ No newline at end of file diff --git a/ScadaWeb/ScadaWebCommon5Beta/WebPhrases.cs b/ScadaWeb/ScadaWebCommon5Beta/WebPhrases.cs index abe5be36e..7c16759ad 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/WebPhrases.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/WebPhrases.cs @@ -73,6 +73,12 @@ static WebPhrases() // Словарь Scada.Web.Shell.RememberMe public static string SecurityViolation { get; private set; } + // Словарь Scada.Web.Shell.UserContent + public static string ReportNotFound { get; private set; } + public static string UnexpectedReportSpec { get; private set; } + public static string DataWndNotFound { get; private set; } + public static string UnexpectedDataWndSpec { get; private set; } + private static void SetToDefault() { NotLoggedOn = Localization.Dict.GetEmptyPhrase("NotLoggedOn"); @@ -101,6 +107,11 @@ private static void SetToDefault() AboutMenuItem = Localization.Dict.GetEmptyPhrase("AboutMenuItem"); SecurityViolation = Localization.Dict.GetEmptyPhrase("SecurityViolation"); + + ReportNotFound = Localization.Dict.GetEmptyPhrase("ReportNotFound"); + UnexpectedReportSpec = Localization.Dict.GetEmptyPhrase("UnexpectedReportSpec"); + DataWndNotFound = Localization.Dict.GetEmptyPhrase("DataWndNotFound"); + UnexpectedDataWndSpec = Localization.Dict.GetEmptyPhrase("UnexpectedDataWndSpec"); } public static void Init() @@ -156,6 +167,14 @@ public static void Init() { SecurityViolation = dict.GetPhrase("SecurityViolation", SecurityViolation); } + + if (Localization.Dictionaries.TryGetValue("Scada.Web.Shell.UserContent", out dict)) + { + ReportNotFound = dict.GetPhrase("ReportNotFound", ReportNotFound); + UnexpectedReportSpec = dict.GetPhrase("UnexpectedReportSpec", UnexpectedReportSpec); + DataWndNotFound = dict.GetPhrase("DataWndNotFound", DataWndNotFound); + UnexpectedDataWndSpec = dict.GetPhrase("UnexpectedDataWndSpec", UnexpectedDataWndSpec); + } } } } diff --git a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml index 20e3df40e..0d7d02095 100644 --- a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml +++ b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml @@ -97,4 +97,10 @@ Security violation is detected. Immediately contact your system administrator. + + Report with ID={0} not found. + Report with ID={0} has the unexpected specification type. + Data window with ID={0} not found. + Data window with ID={0} has the unexpected specification type. + diff --git a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml index 333038b29..0a067a494 100644 --- a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml +++ b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml @@ -97,4 +97,10 @@ Обнаружено нарушение безопасности. Незамедлительно обратитесь к системному администратору. + + Отчёт с ид.={0} не найден. + Отчёт с ид.={0} имеет несоответствующий тип спецификации. + Окно данных с ид.={0} не найдено. + Окно данных с ид.={0} имеет несоответствующий тип спецификации. + From 0c3e539fb9be248f59bd9433cd6c961453334789 Mon Sep 17 00:00:00 2001 From: 2mik Date: Fri, 30 Sep 2016 23:34:57 +0300 Subject: [PATCH 299/382] ScadaData: add parse enum methods --- ScadaData/ScadaData/ScadaUtils.Xml.cs | 40 +++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/ScadaData/ScadaData/ScadaUtils.Xml.cs b/ScadaData/ScadaData/ScadaUtils.Xml.cs index b00976a8d..abab1808e 100644 --- a/ScadaData/ScadaData/ScadaUtils.Xml.cs +++ b/ScadaData/ScadaData/ScadaUtils.Xml.cs @@ -99,6 +99,14 @@ public static TimeSpan XmlParseTimeSpan(string s) return TimeSpan.Parse(s, DateTimeFormatInfo.InvariantInfo); } + /// + /// Преобразовать строку, считанную из XML-документа, в перечислимое значение + /// + public static T XmlParseEnum(string s) where T : struct + { + return (T)Enum.Parse(typeof(T), s); + } + /// /// Создать и добавить XML-элемент @@ -225,6 +233,22 @@ public static TimeSpan GetChildAsTimeSpan(this XmlNode parentXmlNode, string chi } } + /// + /// Получить перечислимое значение дочернего XML-узла + /// + public static T GetChildAsEnum(this XmlNode parentXmlNode, string childNodeName) where T : struct + { + try + { + XmlNode node = parentXmlNode.SelectSingleNode(childNodeName); + return node == null ? default(T) : XmlParseEnum(node.InnerText); + } + catch (FormatException) + { + throw NewXmlNodeFormatException(childNodeName); + } + } + /// /// Установить значение атрибута XML-элемента @@ -313,5 +337,21 @@ public static TimeSpan GetAttrAsTimeSpan(this XmlElement xmlElem, string attrNam throw NewXmlAttrFormatException(attrName); } } + + /// + /// Получить перечислимое значение атрибута XML-элемента + /// + public static T GetAttrAsEnum(this XmlElement xmlElem, string attrName) where T : struct + { + try + { + return xmlElem.HasAttribute(attrName) ? + XmlParseEnum(xmlElem.GetAttribute(attrName)) : default(T); + } + catch (FormatException) + { + throw NewXmlAttrFormatException(attrName); + } + } } } From d2532c5b244a8f654d10995ec86b1670e4d6b202 Mon Sep 17 00:00:00 2001 From: 2mik Date: Sat, 1 Oct 2016 21:20:40 +0300 Subject: [PATCH 300/382] ScadaData: useful addings --- ScadaData/ScadaData/Client/DataFormatter.cs | 10 +++++----- ScadaData/ScadaData/Data/Tables/SrezTableLight.cs | 9 +++++++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/ScadaData/ScadaData/Client/DataFormatter.cs b/ScadaData/ScadaData/Client/DataFormatter.cs index 330c4d905..eb88b5020 100644 --- a/ScadaData/ScadaData/Client/DataFormatter.cs +++ b/ScadaData/ScadaData/Client/DataFormatter.cs @@ -42,14 +42,10 @@ public class DataFormatter /// public delegate CnlStatProps GetCnlStatPropsDelegate(int stat); - /// - /// Количество знаков дробной части по умолчанию - /// - protected const int DefDecDig = 3; /// /// Пустое значение входного канала /// - protected const string EmptyVal = "---"; + public const string EmptyVal = "---"; /// /// Отсутствующее значение входного канала /// @@ -59,6 +55,10 @@ public class DataFormatter /// protected const string NextHourVal = "*"; /// + /// Количество знаков дробной части по умолчанию + /// + protected const int DefDecDig = 3; + /// /// Цвет значения по умолчанию /// protected const string DefColor = "black"; diff --git a/ScadaData/ScadaData/Data/Tables/SrezTableLight.cs b/ScadaData/ScadaData/Data/Tables/SrezTableLight.cs index 1e3c29d4b..b72135338 100644 --- a/ScadaData/ScadaData/Data/Tables/SrezTableLight.cs +++ b/ScadaData/ScadaData/Data/Tables/SrezTableLight.cs @@ -131,6 +131,15 @@ public bool GetCnlData(int cnlNum, out CnlData cnlData) } } /// + /// Получить данные входного канала по номеру + /// + public CnlData GetCnlData(int cnlNum) + { + CnlData cnlData; + GetCnlData(cnlNum, out cnlData); + return cnlData; + } + /// /// Получить значение и статус входного канала по номеру /// public bool GetCnlData(int cnlNum, out double val, out int stat) From 634608261a6597d0c8bbdde533f653a2a92b0550 Mon Sep 17 00:00:00 2001 From: 2mik Date: Mon, 3 Oct 2016 11:11:52 +0300 Subject: [PATCH 301/382] Report fixes in plugins --- .../PlgChart/AppCode/Chart/MinDataRepBuilder.cs | 8 +++++++- .../PlgChart/plugins/Chart/templates/MinDataRep.xml | 13 +++++++------ .../PlgTable/plugins/Table/templates/EventsRep.xml | 11 ++++++----- .../plugins/Table/templates/HourDataRep.xml | 11 ++++++----- 4 files changed, 26 insertions(+), 17 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgChart/AppCode/Chart/MinDataRepBuilder.cs b/ScadaWeb/OpenPlugins/PlgChart/AppCode/Chart/MinDataRepBuilder.cs index fc7c2358e..2e5fe8f97 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/AppCode/Chart/MinDataRepBuilder.cs +++ b/ScadaWeb/OpenPlugins/PlgChart/AppCode/Chart/MinDataRepBuilder.cs @@ -37,7 +37,7 @@ namespace Scada.Web.Plugins.Chart /// internal class MinDataRepBuilder : ExcelRepBuilder { - private DataAccess dataAccess; // объект для доступа к данным + private readonly DataAccess dataAccess; // объект для доступа к данным private int[] cnlNums; // номера каналов, по которым строится отчёт private DateTime startDate; // начальная дата данных отчёта @@ -55,6 +55,12 @@ internal class MinDataRepBuilder : ExcelRepBuilder private InCnlProps[] cnlPropsArr; // массив свойств входных каналов отчёта private InCnlProps curCnlProps; // свойства текущего обрабатываемого входного канала + /// + /// Конструктор, ограничивающий создание объекта без параметров + /// + protected MinDataRepBuilder() + { + } /// /// Конструктор diff --git a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/templates/MinDataRep.xml b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/templates/MinDataRep.xml index 11b0ba4f3..1be8ae5fb 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/templates/MinDataRep.xml +++ b/ScadaWeb/OpenPlugins/PlgChart/plugins/Chart/templates/MinDataRep.xml @@ -6,8 +6,9 @@ xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:html="http://www.w3.org/TR/REC-html40"> - 2016-02-19T09:00:44Z - 2016-02-19T09:00:44Z + Rapid SCADA + Rapid SCADA + 2016-02-19T09:00:44Z 14.00 @@ -25,14 +26,14 @@
diff --git a/ScadaWeb/ScadaWebShell5Beta/js/controls/splitter.js b/ScadaWeb/ScadaWebShell5Beta/js/controls/splitter.js index aabcacaf6..8af2db77f 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/controls/splitter.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/controls/splitter.js @@ -74,7 +74,7 @@ scada.SplitterBulk.prototype._addOverlay = function () { // Remove overlay div scada.SplitterBulk.prototype._removeOverlay = function () { - $("div.splitter-overlay").detach(); + $("div.splitter-overlay").remove(); }; // Enter resizing mode diff --git a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js index 522b12bea..d57ca67cd 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js @@ -189,6 +189,7 @@ $(document).ready(function () { $("body").off(); // page setup + scada.clientAPI.rootPath = scada.env.rootPath; scada.masterMain.updateLayout(); scada.masterMain.chooseToolWindow(); scada.masterMain.loadVisualState(); diff --git a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml index afb9c5cc5..280dce4cc 100644 --- a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml +++ b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml @@ -11,8 +11,8 @@ Logout - Full screen - Normal view + Full Screen + Normal View Main Menu Views Collapse menu From 2f7bf6272b22cac5914b085844371c8ebfa7058d Mon Sep 17 00:00:00 2001 From: 2mik Date: Tue, 24 May 2016 15:41:36 +0300 Subject: [PATCH 063/382] ScadaComm: add common methods --- .../ScadaCommCommon/Devices/KPLogic.Types.cs | 8 ++++---- ScadaComm/ScadaCommCommon/Devices/KPLogic.cs | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/ScadaComm/ScadaCommCommon/Devices/KPLogic.Types.cs b/ScadaComm/ScadaCommCommon/Devices/KPLogic.Types.cs index 415517dd2..b49b073e7 100644 --- a/ScadaComm/ScadaCommCommon/Devices/KPLogic.Types.cs +++ b/ScadaComm/ScadaCommCommon/Devices/KPLogic.Types.cs @@ -1,5 +1,5 @@ /* - * Copyright 2015 Mikhail Shiryaev + * Copyright 2016 Mikhail Shiryaev * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ * * Author : Mikhail Shiryaev * Created : 2006 - * Modified : 2015 + * Modified : 2016 */ using Scada.Data; @@ -215,14 +215,14 @@ public List GetBoundTagIndexes() /// /// Установить данные тега среза /// - protected void SetTagData(int tagIndex, double newVal, int newStat) + public void SetTagData(int tagIndex, double newVal, int newStat) { SetTagData(tagIndex, new SrezTableLight.CnlData(newVal, newStat)); } /// /// Установить данные тега среза /// - protected void SetTagData(int tagIndex, SrezTableLight.CnlData newData) + public void SetTagData(int tagIndex, SrezTableLight.CnlData newData) { if (0 <= tagIndex && tagIndex < TagData.Length) TagData[tagIndex] = newData; diff --git a/ScadaComm/ScadaCommCommon/Devices/KPLogic.cs b/ScadaComm/ScadaCommCommon/Devices/KPLogic.cs index ef474b6df..fed04a1f9 100644 --- a/ScadaComm/ScadaCommCommon/Devices/KPLogic.cs +++ b/ScadaComm/ScadaCommCommon/Devices/KPLogic.cs @@ -914,6 +914,23 @@ protected void SetCurData(int tagIndex, SrezTableLight.CnlData newData) } } + /// + /// Потокобезопасно увеличить текущее значение тега КП без изменения статуса + /// + protected void IncCurData(int tagIndex, double number) + { + lock (curData) + { + if (0 <= tagIndex && tagIndex < curData.Length) + { + SrezTableLight.CnlData curTagData = curData[tagIndex]; + double newVal = curTagData.Val + number; + curDataModified[tagIndex] |= number != 0; + curData[tagIndex] = new SrezTableLight.CnlData(newVal, curTagData.Stat); + } + } + } + /// /// Потокобезопасно установить текущие данные как недостоверные /// From c5286a9f8f2c940e17bccef2b220bb8b82cfbc9a Mon Sep 17 00:00:00 2001 From: 2mik Date: Tue, 24 May 2016 17:30:38 +0300 Subject: [PATCH 064/382] PlgTable: css fix --- ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css | 1 - ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.less | 1 - ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css index cbdca7f1b..66e6c2516 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css @@ -5,5 +5,4 @@ body { background-color: #f1f1f1; font-family: 'Open Sans', sans-serif; font-size: 12px; - overflow: hidden; } \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.less b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.less index 3a98b9495..09e763551 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.less +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.less @@ -6,5 +6,4 @@ body { background-color: @content-back-color; font-family: @default-font-family; font-size: @default-font-size; - overflow: hidden; } diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css index 67ba23a19..10a78c255 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css @@ -1 +1 @@ -body{margin:0;padding:10px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;} \ No newline at end of file +body{margin:0;padding:10px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;} \ No newline at end of file From 39c4553b4d73028d60055fba266d69974fc11691 Mon Sep 17 00:00:00 2001 From: 2mik Date: Thu, 26 May 2016 00:15:25 +0300 Subject: [PATCH 065/382] PlgScheme: scaling --- .../OpenPlugins/PlgScheme/PlgScheme.csproj | 1 + .../OpenPlugins/PlgScheme/css/shellvar.less | 11 ++++ .../PlgScheme/plugins/Scheme/Scheme.aspx | 4 +- .../plugins/Scheme/Scheme.aspx.designer.cs | 8 +-- .../PlgScheme/plugins/Scheme/css/scheme.less | 5 +- .../PlgScheme/plugins/Scheme/js/scheme.js | 55 +++++++++++++++++++ .../plugins/Scheme/js/schemecommon.js | 6 ++ .../plugins/Scheme/js/schememodel.js | 17 +++++- .../plugins/Scheme/js/schemerender.js | 32 ++++++++++- 9 files changed, 129 insertions(+), 10 deletions(-) create mode 100644 ScadaWeb/OpenPlugins/PlgScheme/css/shellvar.less diff --git a/ScadaWeb/OpenPlugins/PlgScheme/PlgScheme.csproj b/ScadaWeb/OpenPlugins/PlgScheme/PlgScheme.csproj index 65f2b22f1..a684399e2 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/PlgScheme.csproj +++ b/ScadaWeb/OpenPlugins/PlgScheme/PlgScheme.csproj @@ -126,6 +126,7 @@ + Web.config diff --git a/ScadaWeb/OpenPlugins/PlgScheme/css/shellvar.less b/ScadaWeb/OpenPlugins/PlgScheme/css/shellvar.less new file mode 100644 index 000000000..133207b1c --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgScheme/css/shellvar.less @@ -0,0 +1,11 @@ +@menu-back-color: #23282d; +@menu-back-color-air: white; +@menu-fore-color: #eee; +@menu-fore-color-dark: #9ca1a6; +@menu-fore-color-air: #444; +@menu-fore-color-air-light: #8f8f8f; +@menu-item-hover-back-color: #32373c; +@menu-item-hover-back-color-dark: #191e23; +@menu-item-hover-fore-color: #00b9eb; +@menu-item-selected-back-color: #0073aa; +@menu-item-selected-fore-color: white; diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx index 8d8fe341c..fb98eb9b8 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx @@ -33,8 +33,8 @@
100%
100%
Load SchemeCreate DOMStart Updating - /// lblZoomInBtn control. + /// lblZoomOutBtn control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.WebControls.Label lblZoomInBtn; + protected global::System.Web.UI.WebControls.Label lblZoomOutBtn; /// - /// lblZoomOutBtn control. + /// lblZoomInBtn control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.WebControls.Label lblZoomOutBtn; + protected global::System.Web.UI.WebControls.Label lblZoomInBtn; } } diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.less b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.less index 822d550fe..e71c626b3 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.less +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.less @@ -1,10 +1,11 @@ @import "../../../css/globalvar.less"; +@import "../../../css/shellvar.less"; @scheme-back-color: white; @notif-back-color: white; -@toolbar-fore-color: #9ca1a6; +@toolbar-fore-color: @menu-fore-color-dark; @tool-btn-back-color: white; -@tool-btn-hover-fore-color: #00b9eb; +@tool-btn-hover-fore-color: @menu-item-hover-fore-color; @scheme-font-family: Verdana, Geneva, Tahoma, sans-serif; @toolbar-heigth: 30px; diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js index 0c9e06701..c5a024f0f 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js @@ -6,6 +6,8 @@ var viewID = viewID || 0; var refrRate = refrRate || 1000; // Localized phrases var phrases = phrases || {}; +// Possible scale values +var scaleVals = [0.1, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 2.5, 3, 4, 5]; // Default notification message lifetime, ms var DEF_NOTIF_LIFETIME = 10000; @@ -125,6 +127,58 @@ function reloadScheme() { location = location; } +// Bind handlers of the toolbar buttons +function initToolbar() { + var Scales = scada.scheme.Scales; + + $("#lblFitScreenBtn").click(function () { + scheme.setScale(Scales.FIT_SCREEN); + displayScale(); + }); + + $("#lblFitWidthBtn").click(function () { + scheme.setScale(Scales.FIT_WIDTH); + displayScale(); + }); + + $("#lblZoomInBtn").click(function (event) { + scheme.setScale(getNextScale()); + displayScale(); + }); + + $("#lblZoomOutBtn").click(function (event) { + scheme.setScale(getPrevScale()); + displayScale(); + }); +} + +// Get the previous scale value from the possible values array +function getPrevScale() { + var curScale = scheme.scale; + for (var i = scaleVals.length - 1; i >= 0; i--) { + var prevScale = scaleVals[i]; + if (curScale > prevScale) + return prevScale; + } + return curScale; +} + +// Get the next scale value from the possible values array +function getNextScale() { + var curScale = scheme.scale; + for (var i = 0, len = scaleVals.length; i < len; i++) { + var nextScale = scaleVals[i]; + if (curScale < nextScale) + return nextScale; + } + return curScale; +} + +// Display the scheme scale +function displayScale() { + $("#spanCurScale").text(Math.round(scheme.scale * 100) + "%"); +} + // Initialize debug tools function initDebugTools() { $("#divDebugTools").css("display", "inline-block"); @@ -164,6 +218,7 @@ function updateLayout() { $(document).ready(function () { scada.clientAPI.rootPath = "../../"; scheme.parentDomElem = $("#divSchParent"); + initToolbar(); if (DEBUG_MODE) { initDebugTools(); diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemecommon.js b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemecommon.js index 2c295662a..5436a161c 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemecommon.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemecommon.js @@ -67,6 +67,12 @@ scada.scheme.ShowValueKinds = { SHOW_WITHOUT_UNIT: 2 }; +// Predefined scales enumeration +scada.scheme.Scales = { + FIT_SCREEN: "FitScreen", + FIT_WIDTH: "FitWidth" +}; + // Scheme calculations scada.scheme.calc = { // Compare two values using the specified operator diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js index 5f78a3b93..97fdb63de 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js @@ -82,6 +82,8 @@ scada.scheme.Scheme = function () { this.renderContext = new scada.scheme.RenderContext(); // Parent jQuery object this.parentDomElem = null; + // Current scale + this.scale = 1.0; }; scada.scheme.Scheme.prototype = Object.create(scada.scheme.BaseElement.prototype); @@ -391,7 +393,7 @@ scada.scheme.Scheme.prototype._updateElement = function (elem, renderContext) { } }; -// Clear scheme +// Clear the scheme scada.scheme.Scheme.prototype.clear = function () { this.props = null; @@ -508,6 +510,19 @@ scada.scheme.Scheme.prototype.update = function (clientAPI, callback) { }); }; +// Set the scheme scale. +// scale is a predefined string or floating point number +scada.scheme.Scheme.prototype.setScale = function (scale) { + try { + var scaleVal = $.isNumeric(scale) ? scale : this.renderer.calcScale(this, scale); + this.renderer.setScale(this, scaleVal); + this.scale = scaleVal; + } + catch (ex) { + console.error("Error scaling the scheme:", ex.message); + } +} + /********** Element **********/ // Element type diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js index 12bf0b59d..1aaa499be 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js @@ -118,7 +118,8 @@ scada.scheme.SchemeRenderer.prototype.createDom = function (elem, renderContext) .css({ "position": "relative", // to position scheme elements "width": props.Size.Width, - "height": props.Size.Height + "height": props.Size.Height, + "transform-origin": "left top" // for scaling }); this.setBackColor($("body"), props.BackColor); @@ -147,6 +148,35 @@ scada.scheme.SchemeRenderer.prototype.createDom = function (elem, renderContext) elem.dom = divScheme; }; +// Calculate numeric scale based on the predefined string value +scada.scheme.SchemeRenderer.prototype.calcScale = function (elem, scaleStr) { + var Scales = scada.scheme.Scales; + var areaWidth = elem.parentDomElem.innerWidth(); + var schemeWidth = elem.props.Size.Width; + + if (scaleStr == Scales.FIT_SCREEN) { + var schemeHeight = elem.props.Size.Height; + var areaHeight = elem.parentDomElem.innerHeight(); + return Math.min(areaWidth / schemeWidth, areaHeight / schemeHeight); + } else if (scaleStr == Scales.FIT_WIDTH) { + return areaWidth / schemeWidth; + } else { + return 1.0; + } +} + +// Set the scheme scale. +// scaleVal is a floating point number +scada.scheme.SchemeRenderer.prototype.setScale = function (elem, scaleVal) { + // set style of #divScheme + var sizeCoef = Math.min(scaleVal, 1); + elem.dom.css({ + "transform": "scale(" + scaleVal + ", " + scaleVal + ")", + "width": elem.props.Size.Width * sizeCoef, + "height": elem.props.Size.Height * sizeCoef + }); +}; + /********** Element Renderer **********/ // Abstract element renderer type extends scada.scheme.Renderer From e3f971e69b9aa085117e65633136ca1c16e3a9ea Mon Sep 17 00:00:00 2001 From: 2mik Date: Thu, 26 May 2016 09:36:23 +0300 Subject: [PATCH 066/382] PlgScheme: scaling works on PC --- .../PlgScheme/plugins/Scheme/js/scheme.js | 19 +++++++++++++++ .../plugins/Scheme/js/schemerender.js | 23 ++++++++++++------- .../PlgSchemeCommon/PlgSchemeCommon.csproj | 3 --- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js index c5a024f0f..35d877f19 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js @@ -34,6 +34,8 @@ function continueLoadingScheme(viewID) { if (!DEBUG_MODE) { scheme.createDom(); + loadScale(); + displayScale(); startUpdatingScheme(); } } else { @@ -134,21 +136,25 @@ function initToolbar() { $("#lblFitScreenBtn").click(function () { scheme.setScale(Scales.FIT_SCREEN); displayScale(); + saveScale(Scales.FIT_SCREEN); }); $("#lblFitWidthBtn").click(function () { scheme.setScale(Scales.FIT_WIDTH); displayScale(); + saveScale(Scales.FIT_WIDTH); }); $("#lblZoomInBtn").click(function (event) { scheme.setScale(getNextScale()); displayScale(); + saveScale(); }); $("#lblZoomOutBtn").click(function (event) { scheme.setScale(getPrevScale()); displayScale(); + saveScale(); }); } @@ -179,6 +185,19 @@ function displayScale() { $("#spanCurScale").text(Math.round(scheme.scale * 100) + "%"); } +// Load the scheme scale from the cookies +function loadScale() { + var scale = scada.utils.getCookie("SchemeScale"); + if (scale) { + scheme.setScale(scale); + } +} + +// Save the scheme scale in the cookies +function saveScale(opt_scale) { + scada.utils.setCookie("SchemeScale", opt_scale ? opt_scale : scheme.scale); +} + // Initialize debug tools function initDebugTools() { $("#divDebugTools").css("display", "inline-block"); diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js index 1aaa499be..ff9cc7453 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js @@ -113,12 +113,14 @@ scada.scheme.SchemeRenderer.constructor = scada.scheme.SchemeRenderer; scada.scheme.SchemeRenderer.prototype.createDom = function (elem, renderContext) { var props = elem.props; // scheme properties + var schemeWidth = props.Size.Width; + var schemeHeight = props.Size.Height; var divScheme = $("
") .css({ "position": "relative", // to position scheme elements - "width": props.Size.Width, - "height": props.Size.Height, + "width": schemeWidth, + "height": schemeHeight, "transform-origin": "left top" // for scaling }); @@ -127,14 +129,17 @@ scada.scheme.SchemeRenderer.prototype.createDom = function (elem, renderContext) this.setFont(divScheme, props.Font); this.setForeColor(divScheme, props.ForeColor); - // set background image if presents + // set background image if presents, + // the additional div is required for correct scaling var backImage = renderContext.getImage(elem.props.BackImage); if (backImage) { - divScheme.css({ + $("
").css({ + "width": schemeWidth, + "height": schemeHeight, "background-image": this.imageToDataUrlCss(backImage), - "background-size": props.Size.Width + "px " + props.Size.Height + "px", + "background-size": schemeWidth + "px " + schemeHeight + "px", "background-repeat": "no-repeat" - }); + }).appendTo(divScheme); } // set title @@ -153,13 +158,15 @@ scada.scheme.SchemeRenderer.prototype.calcScale = function (elem, scaleStr) { var Scales = scada.scheme.Scales; var areaWidth = elem.parentDomElem.innerWidth(); var schemeWidth = elem.props.Size.Width; + var horScale = areaWidth / schemeWidth; if (scaleStr == Scales.FIT_SCREEN) { var schemeHeight = elem.props.Size.Height; var areaHeight = elem.parentDomElem.innerHeight(); - return Math.min(areaWidth / schemeWidth, areaHeight / schemeHeight); + var vertScale = areaHeight / schemeHeight; + return Math.min(horScale, vertScale); } else if (scaleStr == Scales.FIT_WIDTH) { - return areaWidth / schemeWidth; + return horScale; } else { return 1.0; } diff --git a/ScadaWeb/OpenPlugins/PlgSchemeCommon/PlgSchemeCommon.csproj b/ScadaWeb/OpenPlugins/PlgSchemeCommon/PlgSchemeCommon.csproj index fbdd9e99c..17b01f4f8 100644 --- a/ScadaWeb/OpenPlugins/PlgSchemeCommon/PlgSchemeCommon.csproj +++ b/ScadaWeb/OpenPlugins/PlgSchemeCommon/PlgSchemeCommon.csproj @@ -33,9 +33,6 @@ ..\..\..\ScadaData\ScadaData\bin\Release\ScadaData.dll - - ..\..\..\ScadaScheme\ScadaSchemeCommon\bin\Release\ScadaSchemeCommon.dll - From 24102eb0d013e9df46ef4422071fac5865e894e5 Mon Sep 17 00:00:00 2001 From: 2mik Date: Thu, 26 May 2016 11:58:54 +0300 Subject: [PATCH 067/382] PlgScheme: localization --- .../OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.cs | 4 ++++ .../PlgScheme/plugins/Scheme/lang/PlgScheme.en-GB.xml | 7 +++++++ .../PlgScheme/plugins/Scheme/lang/PlgScheme.ru-RU.xml | 7 +++++++ 3 files changed, 18 insertions(+) diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.cs b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.cs index 4aeccf85d..e8b0695be 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.cs @@ -26,6 +26,7 @@ //#define DEBUG_MODE using Scada.Scheme; +using Scada.UI; using Scada.Web.Shell; using System; @@ -74,6 +75,9 @@ protected void Page_Load(object sender, EventArgs e) userData = UserData.GetUserData(); SetupDebugMode(); + // перевод веб-страницы + Translator.TranslatePage(Page, "Scada.Web.Plugins.Scheme.WFrmScheme"); + // получение ид. представления из параметров запроса int.TryParse(Request.QueryString["viewID"], out viewID); diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/lang/PlgScheme.en-GB.xml b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/lang/PlgScheme.en-GB.xml index 0148f197f..1fb4e2646 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/lang/PlgScheme.en-GB.xml +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/lang/PlgScheme.en-GB.xml @@ -1,5 +1,12 @@  + + Scheme - Rapid SCADA + Fit to Screen + Fit to Width + Zoom Out + Zoom In + Scheme loading failed. Try to reload scheme Error updating scheme data diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/lang/PlgScheme.ru-RU.xml b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/lang/PlgScheme.ru-RU.xml index 0d0afa947..34fafe94a 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/lang/PlgScheme.ru-RU.xml +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/lang/PlgScheme.ru-RU.xml @@ -1,5 +1,12 @@  + + Схема - Rapid SCADA + По размеру экрана + По ширине + Уменьшить + Увеличить + Ошибка при загрузке схемы. Попробуйте перезагрузить схему Ошибка при обновлении данных схемы From a7a0204e4ac2daf2439198ef68e36bb7b901a006 Mon Sep 17 00:00:00 2001 From: 2mik Date: Thu, 26 May 2016 16:21:11 +0300 Subject: [PATCH 068/382] PlgTable: development --- .../PlgScheme/plugins/Scheme/Scheme.aspx | 2 +- .../PlgScheme/plugins/Scheme/Scheme.aspx.cs | 49 +-- .../PlgScheme/plugins/Scheme/SchemeSvc.svc.cs | 2 +- .../PlgScheme/plugins/Scheme/css/scheme.css | 4 +- .../PlgScheme/plugins/Scheme/css/scheme.less | 4 +- .../plugins/Scheme/css/scheme.min.css | 2 +- .../PlgScheme/plugins/Scheme/js/scheme.js | 6 +- .../PlgTable/AppCode/PlgTableSpec.cs | 2 +- .../PlgTable/AppCode/Table/EventsWndSpec.cs | 4 +- .../PlgTable/AppCode/Table/PlgPhrases.cs | 13 +- .../PlgTable/AppCode/Table/TableView.cs | 340 ++++++++++++++++++ ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj | 9 + .../OpenPlugins/PlgTable/compilerconfig.json | 4 + .../PlgTable/css/controls/notifier.css | 21 ++ .../PlgTable/css/controls/notifier.less | 27 ++ .../PlgTable/css/controls/notifier.min.css | 1 + .../PlgTable/js/controls/notifier.js | 85 +++++ .../PlgTable/plugins/Table/Table.aspx | 15 + .../PlgTable/plugins/Table/Table.aspx.cs | 66 +++- .../PlgTable/plugins/Table/js/table.js | 7 +- .../plugins/Table/lang/PlgTable.en-GB.xml | 6 +- .../plugins/Table/lang/PlgTable.ru-RU.xml | 6 +- ScadaWeb/ScadaWeb/lang/ScadaWeb.en-GB.xml | 4 +- ScadaWeb/ScadaWebCommon5Beta/UserData.cs | 12 + .../ScadaWebShell5Beta.csproj | 2 + .../css/controls/notifier.less | 27 ++ ScadaWeb/ScadaWebShell5Beta/css/globalvar.css | 2 +- .../ScadaWebShell5Beta/css/globalvar.less | 2 +- ScadaWeb/ScadaWebShell5Beta/css/login.css | 3 +- .../ScadaWebShell5Beta/css/mastermain.css | 3 +- ScadaWeb/ScadaWebShell5Beta/css/noview.css | 3 +- ScadaWeb/ScadaWebShell5Beta/css/view.css | 3 +- .../js/controls/notifier.js | 91 +++++ .../js/controls/splitter.js | 4 +- .../js/controls/treeview.js | 4 +- 35 files changed, 758 insertions(+), 77 deletions(-) create mode 100644 ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/TableView.cs create mode 100644 ScadaWeb/OpenPlugins/PlgTable/css/controls/notifier.css create mode 100644 ScadaWeb/OpenPlugins/PlgTable/css/controls/notifier.less create mode 100644 ScadaWeb/OpenPlugins/PlgTable/css/controls/notifier.min.css create mode 100644 ScadaWeb/OpenPlugins/PlgTable/js/controls/notifier.js create mode 100644 ScadaWeb/ScadaWebShell5Beta/css/controls/notifier.less create mode 100644 ScadaWeb/ScadaWebShell5Beta/js/controls/notifier.js diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx index fb98eb9b8..fa5c0693e 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx @@ -28,7 +28,7 @@
-
+
public partial class WFrmScheme : System.Web.UI.Page { - /// - /// Имя словаря с фразами для формирования JavaScript - /// - private const string DictName = "Scada.Web.Plugins.Scheme.WFrmScheme.Js"; - // Переменные для вывода на веб-страницу - protected bool debugMode; // режим отладки - protected int viewID; // ид. представления - protected int refrRate; // частота обновления данных - protected string phrases; // локализованные фразы - - private AppData appData; // общие данные веб-приложения - private UserData userData; // данные пользователя приложения + protected bool debugMode = false; // режим отладки + protected int viewID; // ид. представления + protected int refrRate; // частота обновления данных + protected string phrases; // локализованные фразы - - /// - /// Установить или отключить режим отладки - /// - private void SetupDebugMode() + protected void Page_Load(object sender, EventArgs e) { -#if DEBUG_MODE + AppData appData = AppData.GetAppData(); + UserData userData = UserData.GetUserData(); + +#if DEBUG debugMode = true; - appData.Init(Server.MapPath("~")); - appData.UserMonitor.AddUser(userData); - string errMsg; - userData.Login("admin", out errMsg); -#else - debugMode = false; + userData.LoginForDebug(); #endif - } - - protected void Page_Load(object sender, EventArgs e) - { - appData = AppData.GetAppData(); - userData = UserData.GetUserData(); - SetupDebugMode(); // перевод веб-страницы Translator.TranslatePage(Page, "Scada.Web.Plugins.Scheme.WFrmScheme"); @@ -85,11 +62,11 @@ protected void Page_Load(object sender, EventArgs e) if (!(userData.LoggedOn && userData.UserRights.GetViewRights(viewID).ViewRight)) Response.Redirect(UrlTemplates.NoView); - // подготовка данных для веб-страницы + // подготовка данных для вывода на веб-страницу refrRate = userData.WebSettings.DataRefrRate; Localization.Dict dict; - Localization.Dictionaries.TryGetValue(DictName, out dict); + Localization.Dictionaries.TryGetValue("Scada.Web.Plugins.Scheme.WFrmScheme.Js", out dict); phrases = WebUtils.DictionaryToJs(dict); // загрузка представления в кеш, чтобы проверить, что оно доступно, присвоить метку diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/SchemeSvc.svc.cs b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/SchemeSvc.svc.cs index 5950b6503..6bf8b1ecf 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/SchemeSvc.svc.cs +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/SchemeSvc.svc.cs @@ -15,7 +15,7 @@ * * * Product : Rapid SCADA - * Module : PlgSchemeCommon + * Module : PlgScheme * Summary : WCF service for interacting with the scheme JavaScript code * * Author : Mikhail Shiryaev diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.css b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.css index d65faacd3..12c29063d 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.css +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.css @@ -35,9 +35,7 @@ body { color: red; } /* Scheme */ -#divSchParent { - min-width: 300px; - min-height: 200px; +#divSchWrapper { overflow: auto; } /* Toolbar (above the scheme) */ diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.less b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.less index e71c626b3..2e2cf3721 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.less +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.less @@ -52,9 +52,7 @@ body { /* Scheme */ -#divSchParent { - min-width: 300px; - min-height: 200px; +#divSchWrapper { overflow: auto; } diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.min.css b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.min.css index b4cde1703..5b23d9ddd 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.min.css +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.min.css @@ -1 +1 @@ -body{margin:0;padding:0;background-color:#fff;font-family:Verdana,Geneva,Tahoma,sans-serif;font-size:12px;overflow:hidden;}.loading{background-image:url('../images/loading.gif');background-attachment:fixed;background-position:center;background-repeat:no-repeat;}#divNotif{position:fixed;top:0;left:0;max-height:100px;padding:10px;background-color:#fff;border-bottom:1px solid #e5e5e5;display:none;font-family:'Open Sans',sans-serif;overflow:auto;white-space:nowrap;}#divNotif .message{color:#0073aa;}#divNotif .message.error{color:#f00;}#divSchParent{min-width:300px;min-height:200px;overflow:auto;}#divToolbar{position:fixed;height:30px;left:0;top:0;color:#9ca1a6;display:inline-block;font-size:14px;opacity:.5;white-space:nowrap;}#divToolbar:hover{opacity:1;}#divToolbar .tool-btn,#spanCurScale{height:30px;margin:0 5px 0 0;padding:0 10px;cursor:pointer;background-color:#fff;display:inline-block;line-height:30px;vertical-align:top;}#divToolbar .tool-btn:hover{color:#00b9eb;}#spanCurScale{cursor:default;font-size:12px;}#spanCurScale:hover{opacity:1;}#divDebugTools{padding:0 10px;display:none;font-size:12px;vertical-align:top;} \ No newline at end of file +body{margin:0;padding:0;background-color:#fff;font-family:Verdana,Geneva,Tahoma,sans-serif;font-size:12px;overflow:hidden;}.loading{background-image:url('../images/loading.gif');background-attachment:fixed;background-position:center;background-repeat:no-repeat;}#divNotif{position:fixed;top:0;left:0;max-height:100px;padding:10px;background-color:#fff;border-bottom:1px solid #e5e5e5;display:none;font-family:'Open Sans',sans-serif;overflow:auto;white-space:nowrap;}#divNotif .message{color:#0073aa;}#divNotif .message.error{color:#f00;}#divSchWrapper{overflow:auto;}#divToolbar{position:fixed;height:30px;left:0;top:0;color:#9ca1a6;display:inline-block;font-size:14px;opacity:.5;white-space:nowrap;}#divToolbar:hover{opacity:1;}#divToolbar .tool-btn,#spanCurScale{height:30px;margin:0 5px 0 0;padding:0 10px;cursor:pointer;background-color:#fff;display:inline-block;line-height:30px;vertical-align:top;}#divToolbar .tool-btn:hover{color:#00b9eb;}#spanCurScale{cursor:default;font-size:12px;}#spanCurScale:hover{opacity:1;}#divDebugTools{padding:0 10px;display:none;font-size:12px;vertical-align:top;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js index 35d877f19..971cf9ced 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js @@ -224,19 +224,19 @@ function initDebugTools() { function updateLayout() { var divNotif = $("#divNotif"); var notifHeight = divNotif.css("display") == "block" ? divNotif.outerHeight() : 0; - var divSchParent = $("#divSchParent"); + var divSchWrapper = $("#divSchWrapper"); var divToolbar = $("#divToolbar"); $("body").css("padding-top", notifHeight); divNotif.outerWidth($(window).width()); - divSchParent.height($(window).height() - notifHeight); + divSchWrapper.height($(window).height() - notifHeight); divToolbar.css("top", notifHeight); } $(document).ready(function () { scada.clientAPI.rootPath = "../../"; - scheme.parentDomElem = $("#divSchParent"); + scheme.parentDomElem = $("#divSchWrapper"); initToolbar(); if (DEBUG_MODE) { diff --git a/ScadaWeb/OpenPlugins/PlgTable/AppCode/PlgTableSpec.cs b/ScadaWeb/OpenPlugins/PlgTable/AppCode/PlgTableSpec.cs index dbc2019b0..8d07c3d0a 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/AppCode/PlgTableSpec.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/AppCode/PlgTableSpec.cs @@ -115,7 +115,7 @@ public override void Init() { dictUpdater = new DictUpdater( string.Format("{0}Table{1}lang{1}", AppDirs.PluginsDir, Path.DirectorySeparatorChar), - "PlgTable", PlgPhrases.Init, Log); + "PlgTable", PlgPhrases.Init, Log); } /// diff --git a/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/EventsWndSpec.cs b/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/EventsWndSpec.cs index 68b302df9..d6349f58e 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/EventsWndSpec.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/EventsWndSpec.cs @@ -49,9 +49,7 @@ public override string Name { get { - return "Events"; - // TODO: вернуть обратно - // return PlgPhrases.EventsTitle; + return PlgPhrases.EventsTitle; } } diff --git a/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/PlgPhrases.cs b/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/PlgPhrases.cs index 98e20bbd9..a0467f256 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/PlgPhrases.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/PlgPhrases.cs @@ -38,8 +38,13 @@ static PlgPhrases() SetToDefault(); } + // Словарь Scada.Web.Plugins.Table.EventsWndSpec public static string EventsTitle { get; private set; } + // Словарь Scada.Web.Plugins.Table.TableView + public static string LoadTableViewError { get; private set; } + public static string SaveTableViewError { get; private set; } + private static void SetToDefault() { EventsTitle = Localization.Dict.GetEmptyPhrase("EventsTitle"); @@ -48,10 +53,16 @@ private static void SetToDefault() public static void Init() { Localization.Dict dict; - if (Localization.Dictionaries.TryGetValue("Scada.Web.Plugins.PlgTable", out dict)) + if (Localization.Dictionaries.TryGetValue("Scada.Web.Plugins.Table.EventsWndSpec", out dict)) { EventsTitle = dict.GetPhrase("EventsTitle", EventsTitle); } + + if (Localization.Dictionaries.TryGetValue("Scada.Web.Plugins.Table.TableView", out dict)) + { + LoadTableViewError = dict.GetPhrase("LoadTableViewError", LoadTableViewError); + SaveTableViewError = dict.GetPhrase("SaveTableViewError", SaveTableViewError); + } } } } diff --git a/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/TableView.cs b/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/TableView.cs new file mode 100644 index 000000000..8b7415f82 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/TableView.cs @@ -0,0 +1,340 @@ +/* + * Copyright 2016 Mikhail Shiryaev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Product : Rapid SCADA + * Module : ScadaWebCommon + * Summary : Table view + * + * Author : Mikhail Shiryaev + * Created : 2011 + * Modified : 2016 + */ + +using Scada.Client; +using Scada.Data; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Xml; + +namespace Scada.Web.Plugins.Table +{ + /// + /// Table view + /// Табличное представление + /// + public class TableView : BaseView + { + /// + /// Элемент представления + /// + public class Item + { + /// + /// Конструктор + /// + public Item() + : this(0, 0, false, "") + { + } + /// + /// Конструктор + /// + public Item(int cnlNum, int ctrlCnlNum, bool hidden, string caption) + { + CnlNum = cnlNum; + CtrlCnlNum = ctrlCnlNum; + Hidden = hidden; + Caption = caption; + CnlProps = null; + } + + /// + /// Получить или установить номер входного канала + /// + public int CnlNum { get; set; } + /// + /// Получить или установить номер канала управления + /// + public int CtrlCnlNum { get; set; } + /// + /// Получить или установить признак, что элемент является скрытым + /// + public bool Hidden { get; set; } + /// + /// Получить или установить обозначение элемента + /// + public string Caption { get; set; } + /// + /// Получить или установить свойства канала элемента + /// + public InCnlProps CnlProps { get; set; } + } + + + /// + /// Конструктор + /// + public TableView() + { + Items = new List(); + VisibleItems = new List(); + } + + + /// + /// Получить список элементов представления + /// + public List Items { get; protected set; } + + /// + /// Получить список видимых элементов представления + /// + /// Список заполняется при загрузке таблицы + public List VisibleItems { get; protected set; } + + /// + /// Получить количество элементов представления + /// + public int Count + { + get + { + return Items.Count; + } + } + + /// + /// Получить количество видимых элементов представления + /// + public int VisibleCount + { + get + { + return VisibleItems.Count; + } + } + + + /// + /// Добавить элемент в списки элементов + /// + private void AddItem(Item item) + { + Items.Add(item); + if (!item.Hidden) + VisibleItems.Add(item); + } + + /// + /// Загрузить представление из потока + /// + public override void LoadFromStream(Stream stream) + { + // очистка представления + Clear(); + + // загрузка представления в новом формате + XmlDocument xmlDoc; + bool xmlDocLoaded; // XML-документ успешно загружен + + try + { + xmlDoc = new XmlDocument(); + xmlDoc.Load(stream); + xmlDocLoaded = true; + } + catch + { + xmlDoc = null; + xmlDocLoaded = false; + stream.Seek(0, SeekOrigin.Begin); + } + + if (xmlDocLoaded) + { + Title = xmlDoc.DocumentElement.GetAttribute("title"); + XmlNodeList paramNodeList = xmlDoc.DocumentElement.SelectNodes("Param"); + + foreach (XmlElement paramElem in paramNodeList) + { + int cnlNum; + int ctrlCnlNum; + bool hidden; + + int.TryParse(paramElem.GetAttribute("cnlNum"), out cnlNum); + int.TryParse(paramElem.GetAttribute("ctrlCnlNum"), out ctrlCnlNum); + bool.TryParse(paramElem.GetAttribute("hidden"), out hidden); + + Item item = new Item(cnlNum, ctrlCnlNum, hidden, paramElem.InnerText); + AddItem(item); + AddCnlNum(cnlNum); + AddCtrlCnlNum(ctrlCnlNum); + } + } + else + { + // загружка представления в старом формате + const int IdentLen = 10; // длина идентификатора в файле таблицы + StreamReader reader = null; + + try + { + reader = new StreamReader(stream, Encoding.Default); + + // считывание заголовка таблицы + string titLn = reader.ReadLine(); + if (titLn != null && titLn.Length > IdentLen) + Title = titLn.Substring(IdentLen); + + // считывание элементов + string cnlLn; + string capLn; + do + { + cnlLn = reader.ReadLine(); + capLn = reader.ReadLine(); + if (cnlLn != null && capLn != null) + { + int cnlNum = 0; + if (cnlLn.Length > IdentLen) + { + try { cnlNum = int.Parse(cnlLn.Substring(IdentLen)); } + catch { } + } + + string caption = capLn.Length > IdentLen ? capLn.Substring(IdentLen) : ""; + + AddItem(new Item(cnlNum, 0, false, caption)); + AddCnlNum(cnlNum); + } + } while (cnlLn != null && capLn != null); + } + finally + { + if (reader != null) + reader.Close(); + } + } + } + + /// + /// Загрузить представление из файла + /// + public bool LoadFromFile(string fileName, out string errMsg) + { + try + { + using (FileStream fileStream = + new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + LoadFromStream(fileStream); + } + + errMsg = ""; + return true; + } + catch (Exception ex) + { + errMsg = PlgPhrases.LoadTableViewError + ": " + ex.Message; + return false; + } + } + + /// + /// Сохранить представление в файле + /// + public bool SaveToFile(string fileName, out string errMsg) + { + try + { + XmlDocument xmlDoc = new XmlDocument(); + XmlDeclaration xmlDecl = xmlDoc.CreateXmlDeclaration("1.0", "utf-8", null); + xmlDoc.AppendChild(xmlDecl); + + XmlElement rootElem = xmlDoc.CreateElement("TableView"); + rootElem.SetAttribute("title", Title); + xmlDoc.AppendChild(rootElem); + + foreach (Item item in Items) + { + XmlElement paramElem = xmlDoc.CreateElement("Param"); + paramElem.InnerText = item.Caption; + if (item.CnlNum > 0) + paramElem.SetAttribute("cnlNum", item.CnlNum.ToString()); + if (item.CtrlCnlNum > 0) + paramElem.SetAttribute("ctrlCnlNum", item.CtrlCnlNum.ToString()); + if (item.Hidden) + paramElem.SetAttribute("hidden", "true"); + paramElem.IsEmpty = string.IsNullOrEmpty(item.Caption) && + item.CnlNum <= 0 && item.CtrlCnlNum <= 0 && !item.Hidden; + rootElem.AppendChild(paramElem); + } + + xmlDoc.Save(fileName); + errMsg = ""; + return true; + } + catch (Exception ex) + { + errMsg = PlgPhrases.SaveTableViewError + ": " + ex.Message; + return false; + } + } + + /// + /// Привязать свойства входных каналов к элементам представления + /// + public override void BindCnlProps(InCnlProps[] cnlPropsArr) + { + base.BindCnlProps(cnlPropsArr); + + if (cnlPropsArr != null) + { + foreach (Item item in Items) + { + int ind = Array.BinarySearch(cnlPropsArr, item.CnlNum, InCnlProps.IntComp); + if (ind >= 0) + { + InCnlProps cnlProps = cnlPropsArr[ind]; + item.CnlProps = cnlProps; + if (cnlProps.CtrlCnlNum > 0) + { + item.CtrlCnlNum = cnlProps.CtrlCnlNum; + AddCtrlCnlNum(cnlProps.CtrlCnlNum); + } + } + else + { + item.CnlProps = null; + } + } + } + } + + /// + /// Очистить представление + /// + public override void Clear() + { + base.Clear(); + Title = ""; + Items.Clear(); + VisibleItems.Clear(); + } + } +} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj index 84579c5a2..565ccf7c2 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj +++ b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj @@ -57,8 +57,15 @@ + + notifier.less + + + notifier.css + + table.less @@ -80,6 +87,7 @@ + Events.aspx @@ -104,6 +112,7 @@ compilerconfig.json + Web.config diff --git a/ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json b/ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json index c83c12d39..c11147c3c 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json +++ b/ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json @@ -2,5 +2,9 @@ { "outputFile": "plugins/Table/css/table.css", "inputFile": "plugins/Table/css/table.less" + }, + { + "outputFile": "css/controls/notifier.css", + "inputFile": "css/controls/notifier.less" } ] \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/css/controls/notifier.css b/ScadaWeb/OpenPlugins/PlgTable/css/controls/notifier.css new file mode 100644 index 000000000..dd65b3250 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/css/controls/notifier.css @@ -0,0 +1,21 @@ +/*Verdana, Geneva, Tahoma, sans-serif;*/ +.notifier { + position: fixed; + top: 0; + left: 0; + max-height: 100px; + padding: 10px; + background-color: white; + border-bottom: 1px solid #e5e5e5; + display: none; + /*block*/ + font-family: 'Open Sans', sans-serif; + overflow: auto; + white-space: nowrap; +} +.notifier .message { + color: #0073aa; +} +.notifier .message.error { + color: red; +} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/css/controls/notifier.less b/ScadaWeb/OpenPlugins/PlgTable/css/controls/notifier.less new file mode 100644 index 000000000..47d933887 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/css/controls/notifier.less @@ -0,0 +1,27 @@ +@import "../globalvar.less"; + +@notifier-back-color: white; +@notifier-msg-fore-color: #0073aa; +@notifier-err-fore-color: red; + +.notifier { + position: fixed; + top: 0; + left: 0; + max-height: 100px; + padding: 10px; + background-color: @notifier-back-color; + border-bottom: 1px solid @panel-border-color; + display: none; /*block*/ + font-family: @default-font-family; + overflow: auto; + white-space: nowrap; +} + +.notifier .message { + color: @notifier-msg-fore-color; +} + +.notifier .message.error { + color: @notifier-err-fore-color; +} diff --git a/ScadaWeb/OpenPlugins/PlgTable/css/controls/notifier.min.css b/ScadaWeb/OpenPlugins/PlgTable/css/controls/notifier.min.css new file mode 100644 index 000000000..424c08c79 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/css/controls/notifier.min.css @@ -0,0 +1 @@ +.notifier{position:fixed;top:0;left:0;max-height:100px;padding:10px;background-color:#fff;border-bottom:1px solid #e5e5e5;display:none;font-family:'Open Sans',sans-serif;overflow:auto;white-space:nowrap;}.notifier .message{color:#0073aa;}.notifier .message.error{color:#f00;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/js/controls/notifier.js b/ScadaWeb/OpenPlugins/PlgTable/js/controls/notifier.js new file mode 100644 index 000000000..3a9fc9523 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/js/controls/notifier.js @@ -0,0 +1,85 @@ +/* + * Notifier control + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + * + * Requires: + * - jquery + * - eventtypes.js + */ + +// Rapid SCADA namespace +var scada = scada || {}; + + +// Notifier type +scada.Notifier = function (id) { + // jQuery object of the notification area + this._notifier = $("#" + id); +}; + +// Add notification to the notification area +scada.Notifier.prototype.addNotification = function (messageHtml, error, lifetime) { + // remove the previous message if it is equal the new + var divPrevMessage = this._notifier.children(".message:last"); + + if (divPrevMessage.html() == messageHtml) { + divPrevMessage.remove(); + } + + // add the new message + var divMessage = $("
").html(messageHtml); + + if (error) { + divMessage.addClass("error"); + } + + if (lifetime) { + divMessage.attr("data-expires", Date.now() + lifetime); + } + + this._notifier + .css("display", "block") + .append(divMessage) + .scrollTop(divNotif.prop("scrollHeight")); + + $(window).trigger(scada.EventTypes.UPDATE_LAYOUT); +}; + +// Clear the notifications which lifetime is expired +scada.Notifier.prototype.clearOutdatedNotifications = function () { + var messages = this._notifier.find(".message"); + + if (messages.length > 0) { + var nowMs = Date.now(); + var removed = false; + + $.each(messages, function () { + var expires = $(this).attr("data-expires"); + if (expires < nowMs) { + $(this).remove(); + removed = true; + } + }); + + if (removed) { + if (this._notifier.find(".message").length == 0) { + this._notifier.css("display", "none"); + } + + $(window).trigger(scada.EventTypes.UPDATE_LAYOUT); + } + } +}; + +// Clear all the notifications +scada.Notifier.prototype.clearAllNotifications = function () { + var messages = this._notifier.find(".message"); + + if (messages.length > 0) { + messages.remove(); + $(window).trigger(scada.EventTypes.UPDATE_LAYOUT); + } +}; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx index 287b6793f..c88a02aff 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx @@ -6,14 +6,29 @@ Table - Rapid SCADA + + + +
+
+
+
+
+
+
diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs index 2a9740e29..1d5bd4cc8 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs @@ -1,4 +1,31 @@ -using System; +/* + * Copyright 2016 Mikhail Shiryaev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Product : Rapid SCADA + * Module : PlgSchemeCommon + * Summary : Table view web form + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + */ + +using Scada.UI; +using Scada.Web.Shell; +using System; using System.Collections.Generic; using System.Linq; using System.Web; @@ -7,11 +34,48 @@ namespace Scada.Web.Plugins.Table { + /// + /// Table view web form + /// Веб-форма табличного представления + /// public partial class WFrmTable : System.Web.UI.Page { + // Переменные для вывода на веб-страницу + protected bool debugMode = false; // режим отладки + protected int viewID; // ид. представления + protected int refrRate; // частота обновления данных + protected string phrases; // локализованные фразы + protected void Page_Load(object sender, EventArgs e) { + AppData appData = AppData.GetAppData(); + UserData userData = UserData.GetUserData(); + +#if DEBUG + debugMode = true; + userData.LoginForDebug(); +#endif + + // перевод веб-страницы + Translator.TranslatePage(Page, "Scada.Web.Plugins.Table.WFrmTable"); + + // получение ид. представления из параметров запроса + int.TryParse(Request.QueryString["viewID"], out viewID); + + // проверка прав на просмотр представления + if (!(userData.LoggedOn && userData.UserRights.GetViewRights(viewID).ViewRight)) + Response.Redirect(UrlTemplates.NoView); + + // подготовка данных для вывода на веб-страницу + refrRate = userData.WebSettings.DataRefrRate; + + Localization.Dict dict; + Localization.Dictionaries.TryGetValue("Scada.Web.Plugins.Table.WFrmTable.Js", out dict); + phrases = WebUtils.DictionaryToJs(dict); + // загрузка представления в кеш + TableView tableView = appData.ViewCache.GetView(viewID, true); + appData.AssignStamp(tableView); } } } \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js index 07c12f9ab..3d1de953d 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js @@ -1,4 +1,7 @@ -$(document).ready(function () { +// View hub. Must be defined in the parent window +var viewHub = scada.viewHubLocator.getViewHub(); + +$(document).ready(function () { /*$(window).on(scada.EventTypes.VIEW_NAVIGATE, function (event) { console.log(event.type); });*/ @@ -11,8 +14,6 @@ divLog.html(divLog.html() + event.type + " - " + extraParams + "
") }); - var viewHub = scada.viewHubLocator.getViewHub(); - if (viewHub) { $("#btn1").click(function () { viewHub.notify(window, scada.EventTypes.VIEW_TITLE_CHANGED, "new title " + (new Date())); diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml index 65b0509a2..be233e012 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml @@ -1,6 +1,10 @@  - + Events + + Error loading table view from file + Error saving table view to file + diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml index 3eea0959b..7b0720dd8 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml @@ -1,6 +1,10 @@  - + События + + Ошибка при загрузке табличного представления из файла + Ошибка при сохранении табличного представления в файле + diff --git a/ScadaWeb/ScadaWeb/lang/ScadaWeb.en-GB.xml b/ScadaWeb/ScadaWeb/lang/ScadaWeb.en-GB.xml index b4e4c57d5..5e837e1dd 100644 --- a/ScadaWeb/ScadaWeb/lang/ScadaWeb.en-GB.xml +++ b/ScadaWeb/ScadaWeb/lang/ScadaWeb.en-GB.xml @@ -76,8 +76,8 @@ {0} - Events by "{1}" view. Generated {2} - Error loading table of parameters from file - Error saving table of parameters to file + Error loading table view from file + Error saving table view to file Error loading view settings from file diff --git a/ScadaWeb/ScadaWebCommon5Beta/UserData.cs b/ScadaWeb/ScadaWebCommon5Beta/UserData.cs index fdb7d64e4..23880e4bc 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/UserData.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/UserData.cs @@ -30,6 +30,7 @@ using System.Web.SessionState; using Scada.Web.Plugins; using Scada.Web.Shell; +using System.Web.Hosting; namespace Scada.Web { @@ -294,6 +295,17 @@ public bool Login(string username, out string errMsg) return Login(username, null, out errMsg); } + /// + /// Выполнить вход пользователя в систему для последующей отладки + /// + public void LoginForDebug() + { + AppData.Init(HostingEnvironment.MapPath("~")); + AppData.UserMonitor.AddUser(this); + string errMsg; + Login("admin", out errMsg); + } + /// /// Выполнить выход пользователя из системы /// diff --git a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj index 26456d58a..01beb124e 100644 --- a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj +++ b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj @@ -114,6 +114,7 @@ + @@ -198,6 +199,7 @@ + diff --git a/ScadaWeb/ScadaWebShell5Beta/css/controls/notifier.less b/ScadaWeb/ScadaWebShell5Beta/css/controls/notifier.less new file mode 100644 index 000000000..47d933887 --- /dev/null +++ b/ScadaWeb/ScadaWebShell5Beta/css/controls/notifier.less @@ -0,0 +1,27 @@ +@import "../globalvar.less"; + +@notifier-back-color: white; +@notifier-msg-fore-color: #0073aa; +@notifier-err-fore-color: red; + +.notifier { + position: fixed; + top: 0; + left: 0; + max-height: 100px; + padding: 10px; + background-color: @notifier-back-color; + border-bottom: 1px solid @panel-border-color; + display: none; /*block*/ + font-family: @default-font-family; + overflow: auto; + white-space: nowrap; +} + +.notifier .message { + color: @notifier-msg-fore-color; +} + +.notifier .message.error { + color: @notifier-err-fore-color; +} diff --git a/ScadaWeb/ScadaWebShell5Beta/css/globalvar.css b/ScadaWeb/ScadaWebShell5Beta/css/globalvar.css index df3f5c993..5f282702b 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/globalvar.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/globalvar.css @@ -1 +1 @@ -/*Verdana, Geneva, Tahoma, sans-serif;*/ \ No newline at end of file + \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less b/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less index ce6eb9cda..2212f0a9f 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less @@ -1,5 +1,5 @@ @content-back-color: #f1f1f1; @content-fore-color: #444; @panel-border-color: #e5e5e5; -@default-font-family: 'Open Sans', sans-serif; /*Verdana, Geneva, Tahoma, sans-serif;*/ +@default-font-family: 'Open Sans', sans-serif; @default-font-size: 12px; \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/login.css b/ScadaWeb/ScadaWebShell5Beta/css/login.css index f671c51cc..3260252bd 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/login.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/login.css @@ -1,5 +1,4 @@ -/*Verdana, Geneva, Tahoma, sans-serif;*/ -body { +body { margin: 0; padding: 0; background-color: #f1f1f1; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css index 7c53755a8..138bd583d 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css @@ -1,5 +1,4 @@ -/*Verdana, Geneva, Tahoma, sans-serif;*/ -body { +body { margin: 0; padding: 30px 0 0 250px; background-color: #f1f1f1; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/noview.css b/ScadaWeb/ScadaWebShell5Beta/css/noview.css index ca2ab08c3..7c231ce11 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/noview.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/noview.css @@ -1,5 +1,4 @@ -/*Verdana, Geneva, Tahoma, sans-serif;*/ -body { +body { margin: 0; padding: 0; background-color: #f1f1f1; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/view.css b/ScadaWeb/ScadaWebShell5Beta/css/view.css index 06d3dd243..d9166418a 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/view.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/view.css @@ -1,5 +1,4 @@ -/*Verdana, Geneva, Tahoma, sans-serif;*/ -#divViewContent { +#divViewContent { height: 100%; overflow: hidden; } diff --git a/ScadaWeb/ScadaWebShell5Beta/js/controls/notifier.js b/ScadaWeb/ScadaWebShell5Beta/js/controls/notifier.js new file mode 100644 index 000000000..b5e5964d4 --- /dev/null +++ b/ScadaWeb/ScadaWebShell5Beta/js/controls/notifier.js @@ -0,0 +1,91 @@ +/* + * Notifier control + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + * + * Requires: + * - jquery + * - eventtypes.js + */ + +// Rapid SCADA namespace +var scada = scada || {}; + + +// Notifier type +scada.Notifier = function (id) { + // jQuery object of the notification area + this._notifier = $("#" + id); + + // Default notification message lifetime, ms + this.DEF_NOTIF_LIFETIME = 10000; + + // Infinite notification message lifetime + this.INFINITE_NOTIF_LIFETIME = 0; +}; + +// Add notification to the notification area +scada.Notifier.prototype.addNotification = function (messageHtml, error, lifetime) { + // remove the previous message if it is equal the new + var divPrevMessage = this._notifier.children(".message:last"); + + if (divPrevMessage.html() == messageHtml) { + divPrevMessage.remove(); + } + + // add the new message + var divMessage = $("
").html(messageHtml); + + if (error) { + divMessage.addClass("error"); + } + + if (lifetime) { + divMessage.attr("data-expires", Date.now() + lifetime); + } + + this._notifier + .css("display", "block") + .append(divMessage) + .scrollTop(divNotif.prop("scrollHeight")); + + $(window).trigger(scada.EventTypes.UPDATE_LAYOUT); +}; + +// Clear the notifications which lifetime is expired +scada.Notifier.prototype.clearOutdatedNotifications = function () { + var messages = this._notifier.find(".message"); + + if (messages.length > 0) { + var nowMs = Date.now(); + var removed = false; + + $.each(messages, function () { + var expires = $(this).attr("data-expires"); + if (expires < nowMs) { + $(this).remove(); + removed = true; + } + }); + + if (removed) { + if (this._notifier.find(".message").length == 0) { + this._notifier.css("display", "none"); + } + + $(window).trigger(scada.EventTypes.UPDATE_LAYOUT); + } + } +}; + +// Clear all the notifications +scada.Notifier.prototype.clearAllNotifications = function () { + var messages = this._notifier.find(".message"); + + if (messages.length > 0) { + messages.remove(); + $(window).trigger(scada.EventTypes.UPDATE_LAYOUT); + } +}; diff --git a/ScadaWeb/ScadaWebShell5Beta/js/controls/splitter.js b/ScadaWeb/ScadaWebShell5Beta/js/controls/splitter.js index 8af2db77f..19e4a73cb 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/controls/splitter.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/controls/splitter.js @@ -4,9 +4,7 @@ * Author : Mikhail Shiryaev * Created : 2016 * Modified : 2016 - */ - -/* + * * Requires: * - jquery */ diff --git a/ScadaWeb/ScadaWebShell5Beta/js/controls/treeview.js b/ScadaWeb/ScadaWebShell5Beta/js/controls/treeview.js index ac3d7ab42..5acf4b46e 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/controls/treeview.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/controls/treeview.js @@ -4,9 +4,7 @@ * Author : Mikhail Shiryaev * Created : 2016 * Modified : 2016 - */ - -/* + * * Requires: * - jquery * - scadautils.js From 4ad060e434942f7d79ae422d4495f60a9fd83abe Mon Sep 17 00:00:00 2001 From: 2mik Date: Fri, 27 May 2016 15:21:11 +0300 Subject: [PATCH 069/382] ScadaWeb5: optimize css&js --- .../OpenPlugins/PlgScheme/PlgScheme.csproj | 8 ++ .../OpenPlugins/PlgScheme/compilerconfig.json | 4 + .../PlgScheme/css/controls/notifier.css | 20 ++++ .../PlgScheme/css/controls/notifier.less | 27 +++++ .../PlgScheme/css/controls/notifier.min.css | 1 + .../OpenPlugins/PlgScheme/css/globalvar.css | 2 +- .../OpenPlugins/PlgScheme/css/globalvar.less | 2 +- .../OpenPlugins/PlgScheme/js/api/clientapi.js | 3 +- .../OpenPlugins/PlgScheme/js/api/utils.js | 7 +- .../PlgScheme/js/controls/notifier.js | 99 +++++++++++++++++++ .../PlgScheme/plugins/Scheme/Scheme.aspx | 4 +- .../PlgScheme/plugins/Scheme/css/scheme.css | 24 +---- .../PlgScheme/plugins/Scheme/css/scheme.less | 24 ----- .../plugins/Scheme/css/scheme.min.css | 2 +- .../plugins/Scheme/js/schememodel.js | 9 +- .../plugins/Scheme/js/schemerender.js | 2 +- .../PlgTable/plugins/Table/js/table.js | 10 +- ScadaWeb/ScadaWebShell5Beta/MasterMain.Master | 2 +- .../MasterMain.Master.designer.cs | 18 ++-- .../ScadaWebShell5Beta.csproj | 6 ++ .../ScadaWebShell5Beta/compilerconfig.json | 4 + .../css/controls/notifier.css | 20 ++++ .../css/controls/notifier.min.css | 1 + .../ScadaWebShell5Beta/js/api/clientapi.js | 3 +- ScadaWeb/ScadaWebShell5Beta/js/api/utils.js | 2 +- ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js | 20 ++-- .../js/controls/notifier.js | 16 ++- ScadaWeb/ScadaWebShell5Beta/js/mastermain.js | 9 +- ScadaWeb/ScadaWebShell5Beta/js/view.js | 32 +++--- 29 files changed, 268 insertions(+), 113 deletions(-) create mode 100644 ScadaWeb/OpenPlugins/PlgScheme/css/controls/notifier.css create mode 100644 ScadaWeb/OpenPlugins/PlgScheme/css/controls/notifier.less create mode 100644 ScadaWeb/OpenPlugins/PlgScheme/css/controls/notifier.min.css create mode 100644 ScadaWeb/OpenPlugins/PlgScheme/js/controls/notifier.js create mode 100644 ScadaWeb/ScadaWebShell5Beta/css/controls/notifier.css create mode 100644 ScadaWeb/ScadaWebShell5Beta/css/controls/notifier.min.css diff --git a/ScadaWeb/OpenPlugins/PlgScheme/PlgScheme.csproj b/ScadaWeb/OpenPlugins/PlgScheme/PlgScheme.csproj index a684399e2..232fd89f6 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/PlgScheme.csproj +++ b/ScadaWeb/OpenPlugins/PlgScheme/PlgScheme.csproj @@ -73,10 +73,17 @@ + + notifier.less + + + notifier.css + + @@ -127,6 +134,7 @@ + Web.config diff --git a/ScadaWeb/OpenPlugins/PlgScheme/compilerconfig.json b/ScadaWeb/OpenPlugins/PlgScheme/compilerconfig.json index 8d095b539..bb9a9d3e0 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/compilerconfig.json +++ b/ScadaWeb/OpenPlugins/PlgScheme/compilerconfig.json @@ -6,5 +6,9 @@ { "outputFile": "css/globalvar.css", "inputFile": "css/globalvar.less" + }, + { + "outputFile": "css/controls/notifier.css", + "inputFile": "css/controls/notifier.less" } ] \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgScheme/css/controls/notifier.css b/ScadaWeb/OpenPlugins/PlgScheme/css/controls/notifier.css new file mode 100644 index 000000000..c435faa98 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgScheme/css/controls/notifier.css @@ -0,0 +1,20 @@ +.notifier { + position: fixed; + top: 0; + left: 0; + max-height: 100px; + padding: 10px; + background-color: white; + border-bottom: 1px solid #e5e5e5; + display: none; + /*block*/ + font-family: 'Open Sans', sans-serif; + overflow: auto; + white-space: nowrap; +} +.notifier .message { + color: #0073aa; +} +.notifier .message.error { + color: red; +} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgScheme/css/controls/notifier.less b/ScadaWeb/OpenPlugins/PlgScheme/css/controls/notifier.less new file mode 100644 index 000000000..47d933887 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgScheme/css/controls/notifier.less @@ -0,0 +1,27 @@ +@import "../globalvar.less"; + +@notifier-back-color: white; +@notifier-msg-fore-color: #0073aa; +@notifier-err-fore-color: red; + +.notifier { + position: fixed; + top: 0; + left: 0; + max-height: 100px; + padding: 10px; + background-color: @notifier-back-color; + border-bottom: 1px solid @panel-border-color; + display: none; /*block*/ + font-family: @default-font-family; + overflow: auto; + white-space: nowrap; +} + +.notifier .message { + color: @notifier-msg-fore-color; +} + +.notifier .message.error { + color: @notifier-err-fore-color; +} diff --git a/ScadaWeb/OpenPlugins/PlgScheme/css/controls/notifier.min.css b/ScadaWeb/OpenPlugins/PlgScheme/css/controls/notifier.min.css new file mode 100644 index 000000000..424c08c79 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgScheme/css/controls/notifier.min.css @@ -0,0 +1 @@ +.notifier{position:fixed;top:0;left:0;max-height:100px;padding:10px;background-color:#fff;border-bottom:1px solid #e5e5e5;display:none;font-family:'Open Sans',sans-serif;overflow:auto;white-space:nowrap;}.notifier .message{color:#0073aa;}.notifier .message.error{color:#f00;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgScheme/css/globalvar.css b/ScadaWeb/OpenPlugins/PlgScheme/css/globalvar.css index df3f5c993..5f282702b 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/css/globalvar.css +++ b/ScadaWeb/OpenPlugins/PlgScheme/css/globalvar.css @@ -1 +1 @@ -/*Verdana, Geneva, Tahoma, sans-serif;*/ \ No newline at end of file + \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgScheme/css/globalvar.less b/ScadaWeb/OpenPlugins/PlgScheme/css/globalvar.less index ce6eb9cda..2212f0a9f 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/css/globalvar.less +++ b/ScadaWeb/OpenPlugins/PlgScheme/css/globalvar.less @@ -1,5 +1,5 @@ @content-back-color: #f1f1f1; @content-fore-color: #444; @panel-border-color: #e5e5e5; -@default-font-family: 'Open Sans', sans-serif; /*Verdana, Geneva, Tahoma, sans-serif;*/ +@default-font-family: 'Open Sans', sans-serif; @default-font-size: 12px; \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgScheme/js/api/clientapi.js b/ScadaWeb/OpenPlugins/PlgScheme/js/api/clientapi.js index 22405bac2..14cc654ff 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/js/api/clientapi.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/js/api/clientapi.js @@ -49,7 +49,8 @@ scada.clientAPI = { $.ajax({ url: this.rootPath + operation + queryString, method: "GET", - dataType: "json" + dataType: "json", + cache: false }) .done(function (data, textStatus, jqXHR) { try { diff --git a/ScadaWeb/OpenPlugins/PlgScheme/js/api/utils.js b/ScadaWeb/OpenPlugins/PlgScheme/js/api/utils.js index d23485855..5dc457d9c 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/js/api/utils.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/js/api/utils.js @@ -179,8 +179,13 @@ scada.utils = { eval(script); } else { // open web page - location = href; + location.href = href; } } + }, + + // Detect if iOS is used + iOS: function () { + return /iPad|iPhone|iPod/.test(navigator.platform); } }; diff --git a/ScadaWeb/OpenPlugins/PlgScheme/js/controls/notifier.js b/ScadaWeb/OpenPlugins/PlgScheme/js/controls/notifier.js new file mode 100644 index 000000000..f1f9fb66a --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgScheme/js/controls/notifier.js @@ -0,0 +1,99 @@ +/* + * Notifier control + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + * + * Requires: + * - jquery + * - eventtypes.js + */ + +// Rapid SCADA namespace +var scada = scada || {}; + +// Notifier type +scada.Notifier = function (selector) { + // jQuery object of the notification area + this._notifier = $(selector); + + // Default notification message lifetime, ms + this.DEF_NOTIF_LIFETIME = 10000; + + // Infinite notification message lifetime + this.INFINITE_NOTIF_LIFETIME = 0; + + // Clearing outdated notifications rate + this.CLEAR_RATE = 1000; +}; + +// Add notification to the notification area +scada.Notifier.prototype.addNotification = function (messageHtml, error, lifetime) { + // remove the previous message if it is equal the new + var divPrevMessage = this._notifier.children(".message:last"); + + if (divPrevMessage.html() == messageHtml) { + divPrevMessage.remove(); + } + + // add the new message + var divMessage = $("
").html(messageHtml); + + if (error) { + divMessage.addClass("error"); + } + + if (lifetime) { + divMessage.attr("data-expires", Date.now() + lifetime); + } + + this._notifier + .css("display", "block") + .append(divMessage) + .scrollTop(this._notifier.prop("scrollHeight")); + + $(window).trigger(scada.EventTypes.UPDATE_LAYOUT); +}; + +// Clear the notifications which lifetime is expired +scada.Notifier.prototype.clearOutdatedNotifications = function () { + var messages = this._notifier.find(".message"); + + if (messages.length > 0) { + var nowMs = Date.now(); + var removed = false; + + $.each(messages, function () { + var expires = $(this).attr("data-expires"); + if (expires < nowMs) { + $(this).remove(); + removed = true; + } + }); + + if (removed) { + if (this._notifier.find(".message").length == 0) { + this._notifier.css("display", "none"); + } + + $(window).trigger(scada.EventTypes.UPDATE_LAYOUT); + } + } +}; + +// Clear all the notifications +scada.Notifier.prototype.clearAllNotifications = function () { + var messages = this._notifier.find(".message"); + + if (messages.length > 0) { + messages.remove(); + $(window).trigger(scada.EventTypes.UPDATE_LAYOUT); + } +}; + +// Start outdated notifications clearing process +scada.Notifier.prototype.startClearingNotifications = function () { + var thisNotifier = this; + setInterval(function () { thisNotifier.clearOutdatedNotifications(); }, this.CLEAR_RATE); +} diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx index fa5c0693e..cab601656 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx @@ -7,12 +7,14 @@ Scheme - Rapid SCADA + + @@ -26,7 +28,7 @@ -
+
diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.css b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.css index 12c29063d..3aef57a14 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.css +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.css @@ -1,5 +1,4 @@ -/*Verdana, Geneva, Tahoma, sans-serif;*/ -body { +body { margin: 0; padding: 0; background-color: white; @@ -13,27 +12,6 @@ body { background-position: center; background-repeat: no-repeat; } -/* Notifications */ -#divNotif { - position: fixed; - top: 0; - left: 0; - max-height: 100px; - padding: 10px; - background-color: white; - border-bottom: 1px solid #e5e5e5; - display: none; - /*block*/ - font-family: 'Open Sans', sans-serif; - overflow: auto; - white-space: nowrap; -} -#divNotif .message { - color: #0073aa; -} -#divNotif .message.error { - color: red; -} /* Scheme */ #divSchWrapper { overflow: auto; diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.less b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.less index 2e2cf3721..da50bdacf 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.less +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.less @@ -26,30 +26,6 @@ body { background-repeat: no-repeat; } -/* Notifications */ - -#divNotif { - position: fixed; - top: 0; - left: 0; - max-height: 100px; - padding: 10px; - background-color: @notif-back-color; - border-bottom: 1px solid @panel-border-color; - display: none; /*block*/ - font-family: @default-font-family; - overflow: auto; - white-space: nowrap; -} - -#divNotif .message { - color: #0073aa; -} - -#divNotif .message.error { - color: red; -} - /* Scheme */ #divSchWrapper { diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.min.css b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.min.css index 5b23d9ddd..ea88c34a4 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.min.css +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.min.css @@ -1 +1 @@ -body{margin:0;padding:0;background-color:#fff;font-family:Verdana,Geneva,Tahoma,sans-serif;font-size:12px;overflow:hidden;}.loading{background-image:url('../images/loading.gif');background-attachment:fixed;background-position:center;background-repeat:no-repeat;}#divNotif{position:fixed;top:0;left:0;max-height:100px;padding:10px;background-color:#fff;border-bottom:1px solid #e5e5e5;display:none;font-family:'Open Sans',sans-serif;overflow:auto;white-space:nowrap;}#divNotif .message{color:#0073aa;}#divNotif .message.error{color:#f00;}#divSchWrapper{overflow:auto;}#divToolbar{position:fixed;height:30px;left:0;top:0;color:#9ca1a6;display:inline-block;font-size:14px;opacity:.5;white-space:nowrap;}#divToolbar:hover{opacity:1;}#divToolbar .tool-btn,#spanCurScale{height:30px;margin:0 5px 0 0;padding:0 10px;cursor:pointer;background-color:#fff;display:inline-block;line-height:30px;vertical-align:top;}#divToolbar .tool-btn:hover{color:#00b9eb;}#spanCurScale{cursor:default;font-size:12px;}#spanCurScale:hover{opacity:1;}#divDebugTools{padding:0 10px;display:none;font-size:12px;vertical-align:top;} \ No newline at end of file +body{margin:0;padding:0;background-color:#fff;font-family:Verdana,Geneva,Tahoma,sans-serif;font-size:12px;overflow:hidden;}.loading{background-image:url('../images/loading.gif');background-attachment:fixed;background-position:center;background-repeat:no-repeat;}#divSchWrapper{overflow:auto;}#divToolbar{position:fixed;height:30px;left:0;top:0;color:#9ca1a6;display:inline-block;font-size:14px;opacity:.5;white-space:nowrap;}#divToolbar:hover{opacity:1;}#divToolbar .tool-btn,#spanCurScale{height:30px;margin:0 5px 0 0;padding:0 10px;cursor:pointer;background-color:#fff;display:inline-block;line-height:30px;vertical-align:top;}#divToolbar .tool-btn:hover{color:#00b9eb;}#spanCurScale{cursor:default;font-size:12px;}#spanCurScale:hover{opacity:1;}#divDebugTools{padding:0 10px;display:none;font-size:12px;vertical-align:top;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js index 97fdb63de..15b6bf92e 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js @@ -100,7 +100,8 @@ scada.scheme.Scheme.prototype._loadSchemeProps = function (viewID, callback) { "?viewID=" + viewID + "&viewStamp=" + this.viewStamp, method: "GET", - dataType: "json" + dataType: "json", + cache: false }) .done(function (data, textStatus, jqXHR) { try { @@ -188,7 +189,8 @@ scada.scheme.Scheme.prototype._loadElements = function (viewID, callback) { "&startIndex=" + this.elements.length + "&count=" + this.LOAD_ELEM_CNT, method: "GET", - dataType: "json" + dataType: "json", + cache: false }) .done(function (data, textStatus, jqXHR) { try { @@ -287,7 +289,8 @@ scada.scheme.Scheme.prototype._loadImages = function (viewID, callback) { "&startIndex=" + this.images.length + "&totalDataSize=" + this.LOAD_IMG_SIZE, method: "GET", - dataType: "json" + dataType: "json", + cache: false }) .done(function (data, textStatus, jqXHR) { try { diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js index ff9cc7453..82a847f87 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js @@ -146,7 +146,7 @@ scada.scheme.SchemeRenderer.prototype.createDom = function (elem, renderContext) if (props.Title) { document.title = props.Title + " - Rapid SCADA"; if (scada.scheme.viewHub) { - scada.scheme.viewHub.notify(window, scada.EventTypes.VIEW_TITLE_CHANGED, document.title); + scada.scheme.viewHub.notify(scada.EventTypes.VIEW_TITLE_CHANGED, window, document.title); } } diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js index 3d1de953d..f854c21e5 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js @@ -9,22 +9,22 @@ $(document).ready(function () { $(window).on( scada.EventTypes.VIEW_TITLE_CHANGED + " " + scada.EventTypes.VIEW_NAVIGATE + " " + - scada.EventTypes.VIEW_DATE_CHANGED, function (event, extraParams) { + scada.EventTypes.VIEW_DATE_CHANGED, function (event, sender, extraParams) { var divLog = $("#divLog"); - divLog.html(divLog.html() + event.type + " - " + extraParams + "
") + divLog.html(divLog.html() + event.type + " - " + sender.document.title + " - " + extraParams + "
") }); if (viewHub) { $("#btn1").click(function () { - viewHub.notify(window, scada.EventTypes.VIEW_TITLE_CHANGED, "new title " + (new Date())); + viewHub.notify(scada.EventTypes.VIEW_TITLE_CHANGED, window, "new title " + (new Date())); }); $("#btn2").click(function () { - viewHub.notify(window, scada.EventTypes.VIEW_NAVIGATE, 100); + viewHub.notify(scada.EventTypes.VIEW_NAVIGATE, window, 100); }); $("#btn3").click(function () { - viewHub.notify(window, scada.EventTypes.VIEW_DATE_CHANGED, new Date()); + viewHub.notify(scada.EventTypes.VIEW_DATE_CHANGED, window, new Date()); }); } }); diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master index 62040f66c..b254ae40a 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master @@ -45,7 +45,6 @@ ID="lblMainFullscreenBtn" runat="server" CssClass="hdr-btn img-btn" ToolTip="Full Screen">
-
@@ -65,6 +64,7 @@
+ diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.designer.cs b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.designer.cs index cc770f860..ea0bf97ff 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.designer.cs +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.designer.cs @@ -48,15 +48,6 @@ public partial class MasterMain { /// protected global::System.Web.UI.WebControls.Label lblMainFullscreenBtn; - /// - /// lblMainNormalViewBtn control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label lblMainNormalViewBtn; - /// /// lblMainMenuTab control. /// @@ -92,5 +83,14 @@ public partial class MasterMain { /// To modify move field declaration from designer file to code-behind file. /// protected global::System.Web.UI.WebControls.ContentPlaceHolder cphMainContent; + + /// + /// lblMainNormalViewBtn control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblMainNormalViewBtn; } } diff --git a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj index 01beb124e..a126d4928 100644 --- a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj +++ b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj @@ -70,6 +70,12 @@ + + notifier.less + + + notifier.css + globalvar.less diff --git a/ScadaWeb/ScadaWebShell5Beta/compilerconfig.json b/ScadaWeb/ScadaWebShell5Beta/compilerconfig.json index e2453d633..d65ed6ba3 100644 --- a/ScadaWeb/ScadaWebShell5Beta/compilerconfig.json +++ b/ScadaWeb/ScadaWebShell5Beta/compilerconfig.json @@ -22,5 +22,9 @@ { "outputFile": "css/noview.css", "inputFile": "css/noview.less" + }, + { + "outputFile": "css/controls/notifier.css", + "inputFile": "css/controls/notifier.less" } ] \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/controls/notifier.css b/ScadaWeb/ScadaWebShell5Beta/css/controls/notifier.css new file mode 100644 index 000000000..c435faa98 --- /dev/null +++ b/ScadaWeb/ScadaWebShell5Beta/css/controls/notifier.css @@ -0,0 +1,20 @@ +.notifier { + position: fixed; + top: 0; + left: 0; + max-height: 100px; + padding: 10px; + background-color: white; + border-bottom: 1px solid #e5e5e5; + display: none; + /*block*/ + font-family: 'Open Sans', sans-serif; + overflow: auto; + white-space: nowrap; +} +.notifier .message { + color: #0073aa; +} +.notifier .message.error { + color: red; +} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/controls/notifier.min.css b/ScadaWeb/ScadaWebShell5Beta/css/controls/notifier.min.css new file mode 100644 index 000000000..424c08c79 --- /dev/null +++ b/ScadaWeb/ScadaWebShell5Beta/css/controls/notifier.min.css @@ -0,0 +1 @@ +.notifier{position:fixed;top:0;left:0;max-height:100px;padding:10px;background-color:#fff;border-bottom:1px solid #e5e5e5;display:none;font-family:'Open Sans',sans-serif;overflow:auto;white-space:nowrap;}.notifier .message{color:#0073aa;}.notifier .message.error{color:#f00;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js b/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js index 22405bac2..14cc654ff 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js @@ -49,7 +49,8 @@ scada.clientAPI = { $.ajax({ url: this.rootPath + operation + queryString, method: "GET", - dataType: "json" + dataType: "json", + cache: false }) .done(function (data, textStatus, jqXHR) { try { diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/utils.js b/ScadaWeb/ScadaWebShell5Beta/js/api/utils.js index 3d86765dd..5dc457d9c 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/utils.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/utils.js @@ -179,7 +179,7 @@ scada.utils = { eval(script); } else { // open web page - location = href; + location.href = href; } } }, diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js b/ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js index 120de6d88..4b434ff53 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js @@ -53,18 +53,10 @@ scada.ViewHub.prototype.removeDataWindow = function () { // Send notification to a view or data window. // The method is called by a child window -scada.ViewHub.prototype.notify = function (senderWnd, eventType, opt_extraParams) { +scada.ViewHub.prototype.notify = function (eventType, senderWnd, opt_extraParams) { var handled = false; var senderIsView = senderWnd == this.viewWindow; - // set main window title - if (eventType == scada.EventTypes.VIEW_TITLE_CHANGED) { - if (senderIsView && this.mainWindow) { - this.mainWindow.document.title = opt_extraParams; - } - handled = true; - } - // preprocess navigation if (eventType == scada.EventTypes.VIEW_NAVIGATE) { if (senderIsView) { @@ -74,16 +66,22 @@ scada.ViewHub.prototype.notify = function (senderWnd, eventType, opt_extraParams } } + // pass the notification to the main window + if (!handled && this.mainWindow && this.mainWindow != senderWnd) { + var jq = this.mainWindow.$; + jq(this.mainWindow).trigger(eventType, [senderWnd, opt_extraParams]); + } + // pass the notification to the view window if (!handled && this.viewWindow && this.viewWindow != senderWnd) { var jq = this.viewWindow.$; - jq(this.viewWindow).trigger(eventType, opt_extraParams); + jq(this.viewWindow).trigger(eventType, [senderWnd, opt_extraParams]); } // pass the notification to the data window if (!handled && this.dataWindow && this.dataWindow != senderWnd) { var jq = this.dataWindow.$; - jq(this.dataWindow).trigger(eventType, opt_extraParams); + jq(this.dataWindow).trigger(eventType, [senderWnd, opt_extraParams]); } }; diff --git a/ScadaWeb/ScadaWebShell5Beta/js/controls/notifier.js b/ScadaWeb/ScadaWebShell5Beta/js/controls/notifier.js index b5e5964d4..f1f9fb66a 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/controls/notifier.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/controls/notifier.js @@ -13,17 +13,19 @@ // Rapid SCADA namespace var scada = scada || {}; - // Notifier type -scada.Notifier = function (id) { +scada.Notifier = function (selector) { // jQuery object of the notification area - this._notifier = $("#" + id); + this._notifier = $(selector); // Default notification message lifetime, ms this.DEF_NOTIF_LIFETIME = 10000; // Infinite notification message lifetime this.INFINITE_NOTIF_LIFETIME = 0; + + // Clearing outdated notifications rate + this.CLEAR_RATE = 1000; }; // Add notification to the notification area @@ -49,7 +51,7 @@ scada.Notifier.prototype.addNotification = function (messageHtml, error, lifetim this._notifier .css("display", "block") .append(divMessage) - .scrollTop(divNotif.prop("scrollHeight")); + .scrollTop(this._notifier.prop("scrollHeight")); $(window).trigger(scada.EventTypes.UPDATE_LAYOUT); }; @@ -89,3 +91,9 @@ scada.Notifier.prototype.clearAllNotifications = function () { $(window).trigger(scada.EventTypes.UPDATE_LAYOUT); } }; + +// Start outdated notifications clearing process +scada.Notifier.prototype.startClearingNotifications = function () { + var thisNotifier = this; + setInterval(function () { thisNotifier.clearOutdatedNotifications(); }, this.CLEAR_RATE); +} diff --git a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js index d57ca67cd..3be5abd51 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js @@ -44,11 +44,11 @@ scada.masterMain = { // Check that a user is logged on _checkLoggedOn: function () { - var thisObj = scada.masterMain; + var thisObj = this; scada.clientAPI.checkLoggedOn(function (success, loggedOn) { if (loggedOn) { // enqueue the next check - setTimeout(thisObj._checkLoggedOn, thisObj.CHECK_LOGGEDON_RATE); + setTimeout(function () { thisObj._checkLoggedOn(); }, thisObj.CHECK_LOGGEDON_RATE); } else { // redirect to login page setTimeout(function() { location.href = scada.env.rootPath + "Login.aspx" }, thisObj.LOGIN_DELAY); @@ -172,13 +172,14 @@ scada.masterMain = { if (scada.view.loadView) { scada.view.loadView(viewID, viewUrl); } else { - location = scada.env.rootPath + "View.aspx?viewID=" + viewID; + location.href = scada.env.rootPath + "View.aspx?viewID=" + viewID; } }, // Start cyclic checking user logged on startCheckingLoggedOn: function () { - setTimeout(this._checkLoggedOn, this.CHECK_LOGGEDON_RATE); + var thisObj = this; + setTimeout(function () { thisObj._checkLoggedOn(); }, this.CHECK_LOGGEDON_RATE); } }; diff --git a/ScadaWeb/ScadaWebShell5Beta/js/view.js b/ScadaWeb/ScadaWebShell5Beta/js/view.js index 3b4864e7b..1e33bdc59 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/view.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/view.js @@ -90,16 +90,6 @@ scada.view = { scada.utils.setCookie("ActiveDataWindow", this._dataWindow.url); }, - // Apply additional css styles in case of using iOS - styleIOS: function () { - if (scada.utils.iOS()) { - $("#divView, #divDataWindow").css({ - "overflow": "scroll", - "-webkit-overflow-scrolling": "touch" - }); - } - }, - // Hide bottom pane if no data windows exist hideBottomTabsIfEmpty: function () { if ($("#divBottomTabsContainer .tab").length == 0) { @@ -192,21 +182,11 @@ scada.view = { $(document).ready(function () { // the order of the calls below is important scada.view.initialPageTitle = document.title; - scada.view.styleIOS(); scada.view.hideBottomTabsIfEmpty(); scada.view.loadView(initialViewID, initialViewUrl); // arguments are defined in View.aspx scada.view.loadVisualState(); scada.view.enqueueUpdateLayout(); - // update layout on window resize and master page layout changes - $(window) - .resize(function () { - scada.view.updateLayout(); - }) - .on(scada.EventTypes.UPDATE_LAYOUT, function () { - scada.view.updateLayout(); - }); - // operate the splitter scada.splitter.prepare(function (splitterBulk) { if (splitterBulk.splitter.attr("id") == "divViewSplitter") { @@ -214,6 +194,18 @@ $(document).ready(function () { } }); + // update layout on window resize and master page layout changes + $(window).on("resize " + scada.EventTypes.UPDATE_LAYOUT, function () { + scada.view.updateLayout(); + }); + + // set window title on view title changed + $(window).on(scada.EventTypes.VIEW_TITLE_CHANGED, function (event, sender, extraParams) { + if (sender == viewHub.viewWindow) { + document.title = extraParams; + } + }); + // activate a data window if the tab is clicked $("#divBottomTabsContainer .tab").click(function () { scada.view.activateDataWindow($(this)); From aafa77184d84eb9c92b80ab19728da2a91605a93 Mon Sep 17 00:00:00 2001 From: 2mik Date: Fri, 27 May 2016 16:41:27 +0300 Subject: [PATCH 070/382] ScadaWeb5: fixes --- ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js | 12 +++++++++--- ScadaWeb/ScadaWebShell5Beta/js/view.js | 16 ++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js b/ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js index 4b434ff53..9ea3b83e9 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js @@ -69,19 +69,25 @@ scada.ViewHub.prototype.notify = function (eventType, senderWnd, opt_extraParams // pass the notification to the main window if (!handled && this.mainWindow && this.mainWindow != senderWnd) { var jq = this.mainWindow.$; - jq(this.mainWindow).trigger(eventType, [senderWnd, opt_extraParams]); + if (jq) { + jq(this.mainWindow).trigger(eventType, [senderWnd, opt_extraParams]); + } } // pass the notification to the view window if (!handled && this.viewWindow && this.viewWindow != senderWnd) { var jq = this.viewWindow.$; - jq(this.viewWindow).trigger(eventType, [senderWnd, opt_extraParams]); + if (jq) { + jq(this.viewWindow).trigger(eventType, [senderWnd, opt_extraParams]); + } } // pass the notification to the data window if (!handled && this.dataWindow && this.dataWindow != senderWnd) { var jq = this.dataWindow.$; - jq(this.dataWindow).trigger(eventType, [senderWnd, opt_extraParams]); + if (jq) { + jq(this.dataWindow).trigger(eventType, [senderWnd, opt_extraParams]); + } } }; diff --git a/ScadaWeb/ScadaWebShell5Beta/js/view.js b/ScadaWeb/ScadaWebShell5Beta/js/view.js index 1e33bdc59..035befc70 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/view.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/view.js @@ -176,6 +176,17 @@ scada.view = { saveSplitterPosition: function () { var dataWindowHeight = $("#divDataWindow").outerHeight(); scada.utils.setCookie("DataWindowHeight", dataWindowHeight); + }, + + // Reload view and data windows + reloadWindows: function (){ + if (viewHub.viewWindow) { + viewHub.viewWindow.location.reload(); + } + + if (viewHub.dataWindow) { + viewHub.dataWindow.location.reload(); + } } }; @@ -191,6 +202,11 @@ $(document).ready(function () { scada.splitter.prepare(function (splitterBulk) { if (splitterBulk.splitter.attr("id") == "divViewSplitter") { scada.view.saveSplitterPosition(); + + if (scada.utils.iOS()) { + // fix frame size + scada.view.reloadWindows(); + } } }); From d3291611b8065714857289f5f5975b8c1a80666a Mon Sep 17 00:00:00 2001 From: 2mik Date: Fri, 27 May 2016 16:58:30 +0300 Subject: [PATCH 071/382] ScadaWeb5: append to the prev commit --- .../PlgScheme/plugins/Scheme/js/scheme.js | 98 +++++-------------- 1 file changed, 26 insertions(+), 72 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js index 971cf9ced..c77378bb0 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js @@ -1,5 +1,7 @@ // Scheme object var scheme = new scada.scheme.Scheme(); +// Notifier control +var notifier = null; // View ID. Must be defined in Scheme.aspx var viewID = viewID || 0; // Scheme refresh rate @@ -44,7 +46,7 @@ function continueLoadingScheme(viewID) { } else { console.error(getCurTime() + " Scheme loading failed"); $("body").removeClass("loading"); - addNotification(phrases.LoadSchemeError + + notifier.addNotification(phrases.LoadSchemeError + " ", true, INFINITE_NOTIF_LIFETIME); } @@ -55,78 +57,16 @@ function continueLoadingScheme(viewID) { function startUpdatingScheme() { scheme.update(scada.clientAPI, function (success) { if (!success) { - addNotification(phrases.UpdateError, true, DEF_NOTIF_LIFETIME); + notifier.addNotification(phrases.UpdateError, true, DEF_NOTIF_LIFETIME); } setTimeout(startUpdatingScheme, refrRate); }); } -// Add notification to the notification panel -function addNotification(messageHtml, error, lifetime) { - // remove the same previous message - var divNotif = $("#divNotif"); - var divPrevMessage = divNotif.children(".message:last"); - - if (divPrevMessage.html() == messageHtml) { - divPrevMessage.remove(); - } - - // add the new message - var divMessage = $("
").html(messageHtml); - - if (error) { - divMessage.addClass("error"); - } - - if (lifetime) { - divMessage.attr("data-expires", Date.now() + lifetime); - } - - divNotif - .css("display", "block") - .append(divMessage) - .scrollTop(divNotif.prop("scrollHeight")); - - updateLayout(); -} - -// Clear the notifications which lifetime is expired -function clearOutdatedNotifications() { - var messages = $("#divNotif .message"); - - if (messages.length > 0) { - var nowMs = Date.now(); - - $.each(messages, function () { - var expires = $(this).attr("data-expires"); - if (expires < nowMs) { - $(this).remove(); - } - }); - - if ($("#divNotif .message").length == 0) { - $("#divNotif").css("display", "none"); - } - - updateLayout(); - } -} - -// Clear all the notifications -function clearAllNotifications() { - $("#divNotif .message").remove(); - updateLayout(); -} - -// Start outdated notifications clearing process -function startClearingNotifications() { - setInterval(clearOutdatedNotifications, 1000); -} - // Reload scheme function reloadScheme() { - location = location; + location.reload(true); } // Bind handlers of the toolbar buttons @@ -216,24 +156,36 @@ function initDebugTools() { }); $("#spanAddNotifBtn").click(function () { - addNotification(scada.utils.getCurTime() + " Test notification", false, DEF_NOTIF_LIFETIME); + notifier.addNotification(scada.utils.getCurTime() + " Test notification", false, DEF_NOTIF_LIFETIME); }); } +// Apply additional css styles in case of using iOS +function styleIOS() { + if (scada.utils.iOS()) { + $("#divSchWrapper").css({ + "overflow": "scroll", + "-webkit-overflow-scrolling": "touch" + }); + } +} + // Update layout of the top level div elements function updateLayout() { var divNotif = $("#divNotif"); var notifHeight = divNotif.css("display") == "block" ? divNotif.outerHeight() : 0; + var windowWidth = $(window).width(); var divSchWrapper = $("#divSchWrapper"); var divToolbar = $("#divToolbar"); $("body").css("padding-top", notifHeight); - divNotif.outerWidth($(window).width()); - divSchWrapper.height($(window).height() - notifHeight); + divNotif.outerWidth(windowWidth); + divSchWrapper + .outerWidth(windowWidth) + .outerHeight($(window).height() - notifHeight); divToolbar.css("top", notifHeight); } - $(document).ready(function () { scada.clientAPI.rootPath = "../../"; scheme.parentDomElem = $("#divSchWrapper"); @@ -245,10 +197,12 @@ $(document).ready(function () { startLoadingScheme(viewID); } + styleIOS(); + updateLayout(); + notifier = new scada.Notifier("#divNotif"); + notifier.startClearingNotifications(); + $(window).resize(function () { updateLayout(); }); - - updateLayout(); - startClearingNotifications(); }); \ No newline at end of file From 4dda7647f8057ddfbc781d6168855b475f553768 Mon Sep 17 00:00:00 2001 From: 2mik Date: Mon, 30 May 2016 17:12:52 +0300 Subject: [PATCH 072/382] PlgTable: layout --- .../PlgScheme/plugins/Scheme/js/scheme.js | 68 +++---- ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj | 25 ++- .../OpenPlugins/PlgTable/compilerconfig.json | 8 + .../PlgTable/config/CommSettings.xml | 8 + .../OpenPlugins/PlgTable/css/globalvar.less | 2 +- .../OpenPlugins/PlgTable/css/shellvar.less | 11 + .../OpenPlugins/PlgTable/js/api/clientapi.js | 129 ++++++++++++ ScadaWeb/OpenPlugins/PlgTable/js/api/utils.js | 191 ++++++++++++++++++ .../OpenPlugins/PlgTable/js/api/viewhub.js | 26 ++- .../PlgTable/js/controls/notifier.js | 22 +- .../PlgTable/plugins/Table/Events.aspx | 24 ++- .../plugins/Table/Events.aspx.designer.cs | 9 + .../PlgTable/plugins/Table/Table.aspx | 24 +-- .../plugins/Table/Table.aspx.designer.cs | 27 +++ .../PlgTable/plugins/Table/css/events.css | 1 + .../PlgTable/plugins/Table/css/events.less | 1 + .../PlgTable/plugins/Table/css/events.min.css | 1 + .../PlgTable/plugins/Table/css/layout.css | 39 ++++ .../PlgTable/plugins/Table/css/layout.less | 51 +++++ .../PlgTable/plugins/Table/css/layout.min.css | 1 + .../PlgTable/plugins/Table/css/table.css | 9 +- .../PlgTable/plugins/Table/css/table.less | 10 +- .../PlgTable/plugins/Table/css/table.min.css | 2 +- .../PlgTable/plugins/Table/js/events.js | 14 ++ .../PlgTable/plugins/Table/js/layout.js | 60 ++++++ .../PlgTable/plugins/Table/js/table.js | 46 ++--- ScadaWeb/ScadaWebCommon5Beta/WebUtils.cs | 2 +- 27 files changed, 692 insertions(+), 119 deletions(-) create mode 100644 ScadaWeb/OpenPlugins/PlgTable/config/CommSettings.xml create mode 100644 ScadaWeb/OpenPlugins/PlgTable/css/shellvar.less create mode 100644 ScadaWeb/OpenPlugins/PlgTable/js/api/clientapi.js create mode 100644 ScadaWeb/OpenPlugins/PlgTable/js/api/utils.js create mode 100644 ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css create mode 100644 ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.less create mode 100644 ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css create mode 100644 ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/layout.css create mode 100644 ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/layout.less create mode 100644 ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/layout.min.css create mode 100644 ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/layout.js diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js index c77378bb0..6ae4e8938 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js @@ -11,11 +11,6 @@ var phrases = phrases || {}; // Possible scale values var scaleVals = [0.1, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 2.5, 3, 4, 5]; -// Default notification message lifetime, ms -var DEF_NOTIF_LIFETIME = 10000; -// Infinite notification message lifetime -var INFINITE_NOTIF_LIFETIME = 0; - // Start scheme loading process function startLoadingScheme(viewID) { console.info(scada.utils.getCurTime() + " Start loading scheme"); @@ -48,7 +43,7 @@ function continueLoadingScheme(viewID) { $("body").removeClass("loading"); notifier.addNotification(phrases.LoadSchemeError + " ", - true, INFINITE_NOTIF_LIFETIME); + true, notifier.INFINITE_NOTIF_LIFETIME); } }); } @@ -57,7 +52,7 @@ function continueLoadingScheme(viewID) { function startUpdatingScheme() { scheme.update(scada.clientAPI, function (success) { if (!success) { - notifier.addNotification(phrases.UpdateError, true, DEF_NOTIF_LIFETIME); + notifier.addNotification(phrases.UpdateError, true, notifier.DEF_NOTIF_LIFETIME); } setTimeout(startUpdatingScheme, refrRate); @@ -138,28 +133,6 @@ function saveScale(opt_scale) { scada.utils.setCookie("SchemeScale", opt_scale ? opt_scale : scheme.scale); } -// Initialize debug tools -function initDebugTools() { - $("#divDebugTools").css("display", "inline-block"); - - $("#spanLoadSchemeBtn").click(function () { - startLoadingScheme(viewID); - }); - - $("#spanCreateDomBtn").click(function () { - scheme.createDom(); - }); - - $("#spanStartUpdBtn").click(function () { - startUpdatingScheme(); - $(this).prop("disabled", true); - }); - - $("#spanAddNotifBtn").click(function () { - notifier.addNotification(scada.utils.getCurTime() + " Test notification", false, DEF_NOTIF_LIFETIME); - }); -} - // Apply additional css styles in case of using iOS function styleIOS() { if (scada.utils.iOS()) { @@ -173,10 +146,10 @@ function styleIOS() { // Update layout of the top level div elements function updateLayout() { var divNotif = $("#divNotif"); - var notifHeight = divNotif.css("display") == "block" ? divNotif.outerHeight() : 0; - var windowWidth = $(window).width(); var divSchWrapper = $("#divSchWrapper"); var divToolbar = $("#divToolbar"); + var notifHeight = divNotif.css("display") == "block" ? divNotif.outerHeight() : 0; + var windowWidth = $(window).width(); $("body").css("padding-top", notifHeight); divNotif.outerWidth(windowWidth); @@ -186,10 +159,36 @@ function updateLayout() { divToolbar.css("top", notifHeight); } +// Initialize debug tools +function initDebugTools() { + $("#divDebugTools").css("display", "inline-block"); + + $("#spanLoadSchemeBtn").click(function () { + startLoadingScheme(viewID); + }); + + $("#spanCreateDomBtn").click(function () { + scheme.createDom(); + }); + + $("#spanStartUpdBtn").click(function () { + startUpdatingScheme(); + $(this).prop("disabled", true); + }); + + $("#spanAddNotifBtn").click(function () { + notifier.addNotification(scada.utils.getCurTime() + " Test notification", false, notifier.DEF_NOTIF_LIFETIME); + }); +} + $(document).ready(function () { scada.clientAPI.rootPath = "../../"; scheme.parentDomElem = $("#divSchWrapper"); initToolbar(); + styleIOS(); + updateLayout(); + notifier = new scada.Notifier("#divNotif"); + notifier.startClearingNotifications(); if (DEBUG_MODE) { initDebugTools(); @@ -197,12 +196,7 @@ $(document).ready(function () { startLoadingScheme(viewID); } - styleIOS(); - updateLayout(); - notifier = new scada.Notifier("#divNotif"); - notifier.startClearingNotifications(); - - $(window).resize(function () { + $(window).on("resize " + scada.EventTypes.UPDATE_LAYOUT, function () { updateLayout(); }); }); \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj index 565ccf7c2..5bdecebd6 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj +++ b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj @@ -57,16 +57,31 @@ + notifier.less notifier.css + + + + events.less + + + events.css + + + layout.less + + + layout.css + table.less @@ -76,6 +91,7 @@ + @@ -106,13 +122,16 @@ - compilerconfig.json + + + + Web.config @@ -121,7 +140,9 @@ Web.config - + + + 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) diff --git a/ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json b/ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json index c11147c3c..25eb782bf 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json +++ b/ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json @@ -6,5 +6,13 @@ { "outputFile": "css/controls/notifier.css", "inputFile": "css/controls/notifier.less" + }, + { + "outputFile": "plugins/Table/css/layout.css", + "inputFile": "plugins/Table/css/layout.less" + }, + { + "outputFile": "plugins/Table/css/events.css", + "inputFile": "plugins/Table/css/events.less" } ] \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/config/CommSettings.xml b/ScadaWeb/OpenPlugins/PlgTable/config/CommSettings.xml new file mode 100644 index 000000000..85ee9c1a5 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/config/CommSettings.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/css/globalvar.less b/ScadaWeb/OpenPlugins/PlgTable/css/globalvar.less index ce6eb9cda..2212f0a9f 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/css/globalvar.less +++ b/ScadaWeb/OpenPlugins/PlgTable/css/globalvar.less @@ -1,5 +1,5 @@ @content-back-color: #f1f1f1; @content-fore-color: #444; @panel-border-color: #e5e5e5; -@default-font-family: 'Open Sans', sans-serif; /*Verdana, Geneva, Tahoma, sans-serif;*/ +@default-font-family: 'Open Sans', sans-serif; @default-font-size: 12px; \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/css/shellvar.less b/ScadaWeb/OpenPlugins/PlgTable/css/shellvar.less new file mode 100644 index 000000000..133207b1c --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/css/shellvar.less @@ -0,0 +1,11 @@ +@menu-back-color: #23282d; +@menu-back-color-air: white; +@menu-fore-color: #eee; +@menu-fore-color-dark: #9ca1a6; +@menu-fore-color-air: #444; +@menu-fore-color-air-light: #8f8f8f; +@menu-item-hover-back-color: #32373c; +@menu-item-hover-back-color-dark: #191e23; +@menu-item-hover-fore-color: #00b9eb; +@menu-item-selected-back-color: #0073aa; +@menu-item-selected-fore-color: white; diff --git a/ScadaWeb/OpenPlugins/PlgTable/js/api/clientapi.js b/ScadaWeb/OpenPlugins/PlgTable/js/api/clientapi.js new file mode 100644 index 000000000..14cc654ff --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/js/api/clientapi.js @@ -0,0 +1,129 @@ +/* + * Rapid SCADA client API for access data and sending commands + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + * + * Requires: + * - jquery + * - utils.js + */ + +// Rapid SCADA namespace +var scada = scada || {}; + +// Input channel data type. +// Note: Casing is caused by C# naming rules +scada.CnlData = function () { + this.Val = 0.0; + this.Stat = 0; +}; + +// Extended input channel data type +scada.CnlDataExt = function () { + scada.CnlData.call(this); + + this.CnlNum = 0; + this.Text = ""; + this.TextWithUnit = ""; + this.Color = ""; +}; + +scada.CnlDataExt.prototype = Object.create(scada.CnlData.prototype); +scada.CnlDataExt.constructor = scada.CnlDataExt; + +// Client API object +scada.clientAPI = { + // Empty input channel data + _emptyCnlData: Object.freeze(new scada.CnlData()), + + // Empty extended input channel data + _emptyCnlDataExt: Object.freeze(new scada.CnlDataExt()), + + // Web service root path + rootPath: "", + + // Execute an AJAX request + _request: function (operation, queryString, callback, errorResult) { + $.ajax({ + url: this.rootPath + operation + queryString, + method: "GET", + dataType: "json", + cache: false + }) + .done(function (data, textStatus, jqXHR) { + try { + var parsedData = $.parseJSON(data.d); + if (parsedData.Success) { + scada.utils.logSuccessfulRequest(operation/*, data*/); + callback(true, parsedData.Data == null ? parsedData : parsedData.Data); + } else { + scada.utils.logServiceError(operation, parsedData.ErrorMessage); + callback(false, errorResult); + } + } + catch (ex) { + scada.utils.logServiceFormatError(operation); + callback(false, errorResult); + } + }) + .fail(function (jqXHR, textStatus, errorThrown) { + scada.utils.logFailedRequest(operation, jqXHR); + callback(false, errorResult); + }); + }, + + // Check that a user is logged on. + // callback is function (success, loggedOn) + checkLoggedOn: function (callback) { + this._request( + "ClientApiSvc.svc/CheckLoggedOn", "", + callback, false); + }, + + // Get current value and status of the input channel. + // callback is function (success, cnlData) + getCurCnlData: function (cnlNum, callback) { + this._request( + "ClientApiSvc.svc/GetCurCnlData", + "?cnlNum=" + cnlNum, + callback, this._emptyCnlData); + }, + + // Get extended current data of the input channel. + // callback is function (success, cnlDataExt) + getCurCnlDataExt: function (cnlNum, callback) { + this._request( + "ClientApiSvc.svc/GetCurCnlDataExt", + "?cnlNum=" + cnlNum, + callback, this._emptyCnlDataExt); + }, + + // Get extended current data of the specified input channels. + // callback is function (success, cnlDataExtArr) + getCurCnlDataExtByCnlNums: function (cnlNums, callback) { + this._request( + "ClientApiSvc.svc/GetCurCnlDataExtByCnlNums", + "?cnlNums=" + cnlNums, + callback, []); + }, + + // Get extended current input channel data of the view. + // callback is function (success, cnlDataExtArr) + getCurCnlDataExtByView: function (viewID, callback) { + this._request( + "ClientApiSvc.svc/GetCurCnlDataExtByView", + "?viewID=" + viewID, + callback, []); + }, + + // Get the stamp of the view from the cache. + // callback is function (success, stamp) + getViewStamp: function (viewID, callback) { + this._request( + "ClientApiSvc.svc/GetViewStamp", + "?viewID=" + viewID, + callback, 0); + } +}; diff --git a/ScadaWeb/OpenPlugins/PlgTable/js/api/utils.js b/ScadaWeb/OpenPlugins/PlgTable/js/api/utils.js new file mode 100644 index 000000000..5dc457d9c --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/js/api/utils.js @@ -0,0 +1,191 @@ +/* + * JavaScript utilities + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + * + * No dependencies + */ + +// Rapid SCADA namespace +var scada = scada || {}; + +// JavaScript utilities object +scada.utils = { + // Default cookie expiration period in days + cookieExpiration: 7, + + // Get cookie + getCookie: function (name) { + var cookie = " " + document.cookie; + var search = " " + name + "="; + var offset = cookie.indexOf(search); + + if (offset >= 0) { + offset += search.length; + var end = cookie.indexOf(";", offset) + + if (end < 0) + end = cookie.length; + + return decodeURIComponent(cookie.substring(offset, end)); + } else { + return null; + } + }, + + // Set cookie + setCookie: function (name, value, opt_expDays) { + var expDays = opt_expDays ? opt_expDays : this.cookieExpiration; + var expires = new Date(); + expires.setDate(expires.getDate() + expDays); + document.cookie = name + "=" + encodeURIComponent(value) + "; expires=" + expires.toUTCString(); + }, + + // Get the query string parameter value + getQueryStringParam: function (paramName, opt_url) { + if (paramName) { + var url = opt_url ? opt_url : unescape(window.location); + var begInd = queryString.indexOf("?"); + + if (begInd > 0) { + url = "&" + url.substring(begInd + 1); + } + + paramName = "&" + paramName + "="; + begInd = url.indexOf(paramName); + + if (begInd >= 0) { + begInd += paramName.length; + var endInd = url.indexOf("&", begInd); + return endInd >= 0 ? url.substring(begInd, endInd) : url.substring(begInd); + } + } + + return ""; + }, + + // Set or add the query string parameter value. + // The method returns a new string + setQueryStringParam: function (paramName, paramVal, opt_url) { + if (paramName) { + var url = opt_url ? opt_url : unescape(window.location); + var searchName = "?" + paramName + "="; + var nameBegInd = url.indexOf(searchName); + + if (nameBegInd < 0) { + searchName = "&" + paramName + "="; + nameBegInd = url.indexOf(searchName); + } + + if (nameBegInd >= 0) { + // replace parameter value + var valBegInd = nameBegInd + searchName.length; + var valEndInd = url.indexOf("&", valBegInd); + var newUrl = url.substring(0, valBegInd) + paramVal; + return valEndInd > 0 ? + newUrl + url.substring(valEndInd) : + newUrl; + } else { + // add parameter + var mark = url.indexOf("?") >= 0 ? "&" : "?"; + return url + mark + paramName + "=" + paramVal; + } + } else { + return ""; + } + }, + + // Returns the current time string + getCurTime: function () { + return new Date().toLocaleTimeString("en-GB"); + }, + + // Write information about the successful request to console + logSuccessfulRequest: function (operation, opt_data) { + console.log(this.getCurTime() + " Request '" + operation + "' successful"); + if (opt_data) { + console.log(opt_data.d); + } + }, + + // Write information about the internal service error to console + logServiceError: function (operation, opt_message) { + console.error(this.getCurTime() + " Request '" + operation + "' reports internal service error" + + (opt_message ? ": " + opt_message : "")); + }, + + // Write information about the internal service error to console + logServiceFormatError: function (operation) { + console.error(this.getCurTime() + " Request '" + operation + "' returns data in incorrect format"); + }, + + // Write information about the failed request to console + logFailedRequest: function (operation, jqXHR) { + console.error(this.getCurTime() + " Request '" + operation + "' failed: " + + jqXHR.status + " (" + jqXHR.statusText + ")"); + }, + + // Check if browser is in fullscreen mode + // See https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API + isFullscreen: function() { + return document.fullscreenElement || document.mozFullScreenElement || + document.webkitFullscreenElement || document.msFullscreenElement; + }, + + // Switch browser to fullscreen mode + requestFullscreen: function () { + if (document.documentElement.requestFullscreen) { + document.documentElement.requestFullscreen(); + } else if (document.documentElement.msRequestFullscreen) { + document.documentElement.msRequestFullscreen(); + } else if (document.documentElement.mozRequestFullScreen) { + document.documentElement.mozRequestFullScreen(); + } else if (document.documentElement.webkitRequestFullscreen) { + document.documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); + } + }, + + // Exit browser fullscreen mode + exitFullscreen: function () { + if (document.exitFullscreen) { + document.exitFullscreen(); + } else if (document.msExitFullscreen) { + document.msExitFullscreen(); + } else if (document.mozCancelFullScreen) { + document.mozCancelFullScreen(); + } else if (document.webkitExitFullscreen) { + document.webkitExitFullscreen(); + } + }, + + // Switch browser to full screen mode and back to normal view + toggleFullscreen: function () { + if (this.isFullscreen()) { + this.exitFullscreen(); + } else { + this.requestFullscreen(); + } + }, + + // Click hyperlink programmatically + clickLink: function (jqLink) { + var href = jqLink.attr("href"); + if (href) { + if (href.startsWith("javascript:")) { + // execute script + var script = href.substr(11); + eval(script); + } else { + // open web page + location.href = href; + } + } + }, + + // Detect if iOS is used + iOS: function () { + return /iPad|iPhone|iPod/.test(navigator.platform); + } +}; diff --git a/ScadaWeb/OpenPlugins/PlgTable/js/api/viewhub.js b/ScadaWeb/OpenPlugins/PlgTable/js/api/viewhub.js index 120de6d88..9ea3b83e9 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/js/api/viewhub.js +++ b/ScadaWeb/OpenPlugins/PlgTable/js/api/viewhub.js @@ -53,18 +53,10 @@ scada.ViewHub.prototype.removeDataWindow = function () { // Send notification to a view or data window. // The method is called by a child window -scada.ViewHub.prototype.notify = function (senderWnd, eventType, opt_extraParams) { +scada.ViewHub.prototype.notify = function (eventType, senderWnd, opt_extraParams) { var handled = false; var senderIsView = senderWnd == this.viewWindow; - // set main window title - if (eventType == scada.EventTypes.VIEW_TITLE_CHANGED) { - if (senderIsView && this.mainWindow) { - this.mainWindow.document.title = opt_extraParams; - } - handled = true; - } - // preprocess navigation if (eventType == scada.EventTypes.VIEW_NAVIGATE) { if (senderIsView) { @@ -74,16 +66,28 @@ scada.ViewHub.prototype.notify = function (senderWnd, eventType, opt_extraParams } } + // pass the notification to the main window + if (!handled && this.mainWindow && this.mainWindow != senderWnd) { + var jq = this.mainWindow.$; + if (jq) { + jq(this.mainWindow).trigger(eventType, [senderWnd, opt_extraParams]); + } + } + // pass the notification to the view window if (!handled && this.viewWindow && this.viewWindow != senderWnd) { var jq = this.viewWindow.$; - jq(this.viewWindow).trigger(eventType, opt_extraParams); + if (jq) { + jq(this.viewWindow).trigger(eventType, [senderWnd, opt_extraParams]); + } } // pass the notification to the data window if (!handled && this.dataWindow && this.dataWindow != senderWnd) { var jq = this.dataWindow.$; - jq(this.dataWindow).trigger(eventType, opt_extraParams); + if (jq) { + jq(this.dataWindow).trigger(eventType, [senderWnd, opt_extraParams]); + } } }; diff --git a/ScadaWeb/OpenPlugins/PlgTable/js/controls/notifier.js b/ScadaWeb/OpenPlugins/PlgTable/js/controls/notifier.js index 3a9fc9523..f1f9fb66a 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/js/controls/notifier.js +++ b/ScadaWeb/OpenPlugins/PlgTable/js/controls/notifier.js @@ -13,11 +13,19 @@ // Rapid SCADA namespace var scada = scada || {}; - // Notifier type -scada.Notifier = function (id) { +scada.Notifier = function (selector) { // jQuery object of the notification area - this._notifier = $("#" + id); + this._notifier = $(selector); + + // Default notification message lifetime, ms + this.DEF_NOTIF_LIFETIME = 10000; + + // Infinite notification message lifetime + this.INFINITE_NOTIF_LIFETIME = 0; + + // Clearing outdated notifications rate + this.CLEAR_RATE = 1000; }; // Add notification to the notification area @@ -43,7 +51,7 @@ scada.Notifier.prototype.addNotification = function (messageHtml, error, lifetim this._notifier .css("display", "block") .append(divMessage) - .scrollTop(divNotif.prop("scrollHeight")); + .scrollTop(this._notifier.prop("scrollHeight")); $(window).trigger(scada.EventTypes.UPDATE_LAYOUT); }; @@ -83,3 +91,9 @@ scada.Notifier.prototype.clearAllNotifications = function () { $(window).trigger(scada.EventTypes.UPDATE_LAYOUT); } }; + +// Start outdated notifications clearing process +scada.Notifier.prototype.startClearingNotifications = function () { + var thisNotifier = this; + setInterval(function () { thisNotifier.clearOutdatedNotifications(); }, this.CLEAR_RATE); +} diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx index 598670744..9c4e01e84 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx @@ -6,20 +6,30 @@ Events - Rapid SCADA - + + + + + - + + +
-
- - - +
+
+
TitleChangedNavigateDateChanged +
+
+
-
diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.designer.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.designer.cs index 0fbb4fe57..4ce8a6163 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.designer.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.designer.cs @@ -20,5 +20,14 @@ public partial class WFrmEvents { /// To modify move field declaration from designer file to code-behind file. /// protected global::System.Web.UI.HtmlControls.HtmlForm frmEvents; + + /// + /// txtDate control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox txtDate; } } diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx index c88a02aff..65edd740b 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx @@ -7,11 +7,15 @@ Table - Rapid SCADA + + + + @@ -21,6 +22,7 @@ +
diff --git a/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js b/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js index 44aa866cc..4edc37add 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js @@ -18,6 +18,8 @@ var scada = scada || {}; // Popup dialogs manipulation type scada.Popup = function () { + // Window that holds popups + this._holderWindow = window; }; // Event handler that removes the popup on press Escape key @@ -26,7 +28,43 @@ scada.Popup.prototype._removePopupOnEscape = function (event) { var popupElem = event.data; popupElem.remove(); } -} +}; + +// Get coodinates of the specified element relative to the holder window +scada.Popup.prototype._getOffset = function (elem) { + // validate the element + var defaultOffset = { left: 0, top: 0 }; + if (!(elem && elem.length)) { + return defaultOffset; + } + + // get coodinates within a window that contains the element + var wnd = elem[0].ownerDocument.defaultView; + var offset = elem.offset(); + var left = offset.left + $(wnd).scrollLeft(); + var top = offset.top + $(wnd).scrollTop(); + + // add coordinates of the parent frames + do { + var parentWnd = wnd.parent; + if (wnd != parentWnd) { + if (parentWnd.$) { + var frame = parentWnd.$(wnd.frameElement); + if (frame.length > 0) { + offset = frame.offset(); + left += offset.left + $(parentWnd).scrollLeft(); + top += offset.top + $(parentWnd).scrollTop(); + } + } else { + console.warn("Unable to get offset, because jQuery is not found"); + return defaultOffset; + } + wnd = parentWnd; + } + } while (wnd != this._holderWindow && wnd != window.top); + + return { left: left, top: top }; +}; // Show popup with the specified url as a dropdown menu below the anchorElem. // callback is function (success, dialogResult, extraParams) @@ -47,32 +85,30 @@ scada.Popup.prototype.showDropdown = function (url, anchorElem, callback) { }); // setup wrapper - wrapper - .focus() - .css({ + wrapper.css({ "z-index": scada.utils.FRONT_ZINDEX + 1, // above the overlay "opacity": 0.0 // hide the popup while it's loading }); // remove the popup on press Escape key in the parent window - /*$(document) - .off(this._removePopupOnEscape) - .on("keydown", null, popupElem, this._removePopupOnEscape);*/ + $(document) + .off("keydown", null, this._removePopupOnEscape) + .on("keydown", null, popupElem, this._removePopupOnEscape); // load the frame var thisObj = this; frame .on("load", function () { // remove the popup on press Escape key in the frame - /*var frameWnd = frame[0].contentWindow; + var frameWnd = frame[0].contentWindow; if (frameWnd.$) { var jqFrameDoc = frameWnd.$(frameWnd.document); jqFrameDoc.ready(function () { jqFrameDoc - .off(thisObj._removePopupOnEscape) + .off("keydown", null, thisObj._removePopupOnEscape) .on("keydown", null, popupElem, thisObj._removePopupOnEscape); }); - }*/ + } }) .one("load", function () { // set the popup position @@ -83,8 +119,9 @@ scada.Popup.prototype.showDropdown = function (url, anchorElem, callback) { var top = 0; if (anchorElem.length > 0) { - left = anchorElem.offset().left; - top = anchorElem.offset().top + anchorElem.outerHeight(); + var offset = thisObj._getOffset(anchorElem); + left = offset.left; + top = offset.top + anchorElem.outerHeight(); var borderWidthX2 = parseInt(wrapper.css("border-width"), 10) * 2; if (left + width + borderWidthX2 > $(document).width()) @@ -104,10 +141,12 @@ scada.Popup.prototype.showDropdown = function (url, anchorElem, callback) { }); // set the popup size and display the popup - frame.css({ + frame + .css({ "width": width, "height": height - }); + }) + .focus(); wrapper.css({ "width": width, From 79cd39bb00f82ad3e55b218cf28ef4b3de0a947d Mon Sep 17 00:00:00 2001 From: 2mik Date: Sat, 4 Jun 2016 16:05:31 +0300 Subject: [PATCH 089/382] ScadaWeb5: woff fonts support --- ScadaWeb/ScadaWebShell5Beta/Web.config | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ScadaWeb/ScadaWebShell5Beta/Web.config b/ScadaWeb/ScadaWebShell5Beta/Web.config index 8ef1b35fa..d42573c62 100644 --- a/ScadaWeb/ScadaWebShell5Beta/Web.config +++ b/ScadaWeb/ScadaWebShell5Beta/Web.config @@ -17,6 +17,12 @@ + + + + + + From 59a7d3b33b47d019fd6d5d20912196924563c5f7 Mon Sep 17 00:00:00 2001 From: 2mik Date: Sat, 4 Jun 2016 16:48:40 +0300 Subject: [PATCH 090/382] ScadaWeb5: calendar --- ScadaWeb/ScadaWebShell5Beta/Calendar.aspx | 6 ++++++ ScadaWeb/ScadaWebShell5Beta/css/calendar.css | 10 +++++++++- ScadaWeb/ScadaWebShell5Beta/css/calendar.less | 18 ++++++++++++++++-- .../ScadaWebShell5Beta/css/calendar.min.css | 2 +- 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/ScadaWeb/ScadaWebShell5Beta/Calendar.aspx b/ScadaWeb/ScadaWebShell5Beta/Calendar.aspx index 2fa5a8880..51eaf1236 100644 --- a/ScadaWeb/ScadaWebShell5Beta/Calendar.aspx +++ b/ScadaWeb/ScadaWebShell5Beta/Calendar.aspx @@ -11,7 +11,13 @@ + + + + + + diff --git a/ScadaWeb/ScadaWebShell5Beta/css/calendar.css b/ScadaWeb/ScadaWebShell5Beta/css/calendar.css index 0739eef33..bd1550751 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/calendar.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/calendar.css @@ -2,11 +2,19 @@ margin: 0; padding: 0; background-color: white; - font-family: Arial, Helvetica, sans-serif; + font-family: 'Open Sans', sans-serif; font-size: 12px; overflow: hidden; display: inline-block; } +#frmCalendar .header { + background-color: #ccc; + height: 25px; +} +#frmCalendar th.day-header { + background-color: #f9f9f9; + height: 25px; +} #frmCalendar td { margin: 0; padding: 0; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/calendar.less b/ScadaWeb/ScadaWebShell5Beta/css/calendar.less index 8479962fc..84229d9e6 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/calendar.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/calendar.less @@ -1,6 +1,10 @@ -@calendar-back-color: white; +@import "globalvar.less"; + +@calendar-back-color: white; +@header-back-color: #ccc; +@day-hdr-back-color: #f9f9f9; @day-hover-back-color: #e0e0e0; -@calendar-font-family: Arial, Helvetica, sans-serif; +@calendar-font-family: @default-font-family;// Arial, Helvetica, sans-serif; body { margin: 0; @@ -15,6 +19,16 @@ body { #frmCalendar table { } +#frmCalendar .header { + background-color: @header-back-color; + height: 25px; +} + +#frmCalendar th.day-header { + background-color: @day-hdr-back-color; + height: 25px; +} + #frmCalendar td { margin: 0; padding: 0; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/calendar.min.css b/ScadaWeb/ScadaWebShell5Beta/css/calendar.min.css index 4470d8e55..dd80747cd 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/calendar.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/calendar.min.css @@ -1 +1 @@ -body{margin:0;padding:0;background-color:#fff;font-family:Arial,Helvetica,sans-serif;font-size:12px;overflow:hidden;display:inline-block;}#frmCalendar td{margin:0;padding:0;vertical-align:middle;}#frmCalendar a{display:inline-block;text-decoration:none;}#frmCalendar td.day a{width:30px;height:30px;line-height:30px;text-align:center;}#frmCalendar a:hover{background-color:#e0e0e0;} \ No newline at end of file +body{margin:0;padding:0;background-color:#fff;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;display:inline-block;}#frmCalendar .header{background-color:#ccc;height:25px;}#frmCalendar th.day-header{background-color:#f9f9f9;height:25px;}#frmCalendar td{margin:0;padding:0;vertical-align:middle;}#frmCalendar a{display:inline-block;text-decoration:none;}#frmCalendar td.day a{width:30px;height:30px;line-height:30px;text-align:center;}#frmCalendar a:hover{background-color:#e0e0e0;} \ No newline at end of file From 5cbb4f9560293fa60d880fade2b47af7da116116 Mon Sep 17 00:00:00 2001 From: 2mik Date: Sat, 4 Jun 2016 23:36:05 +0300 Subject: [PATCH 091/382] ScadaWeb5: calendar style --- ScadaWeb/ScadaWebShell5Beta/Calendar.aspx | 6 +- ScadaWeb/ScadaWebShell5Beta/Calendar.aspx.cs | 5 ++ .../ScadaWebShell5Beta.csproj | 1 + ScadaWeb/ScadaWebShell5Beta/css/calendar.css | 41 ++++++++++++- ScadaWeb/ScadaWebShell5Beta/css/calendar.less | 57 ++++++++++++++++--- .../ScadaWebShell5Beta/css/calendar.min.css | 2 +- .../ScadaWebShell5Beta/css/controls/popup.css | 1 + .../css/controls/popup.less | 7 ++- .../css/controls/popup.min.css | 2 +- ScadaWeb/ScadaWebShell5Beta/js/calendar.js | 7 +++ 10 files changed, 112 insertions(+), 17 deletions(-) create mode 100644 ScadaWeb/ScadaWebShell5Beta/js/calendar.js diff --git a/ScadaWeb/ScadaWebShell5Beta/Calendar.aspx b/ScadaWeb/ScadaWebShell5Beta/Calendar.aspx index 51eaf1236..d673ea150 100644 --- a/ScadaWeb/ScadaWebShell5Beta/Calendar.aspx +++ b/ScadaWeb/ScadaWebShell5Beta/Calendar.aspx @@ -7,17 +7,17 @@ Calendar - Rapid SCADA +
- + - + - diff --git a/ScadaWeb/ScadaWebShell5Beta/Calendar.aspx.cs b/ScadaWeb/ScadaWebShell5Beta/Calendar.aspx.cs index 92b3db11d..bf0066b26 100644 --- a/ScadaWeb/ScadaWebShell5Beta/Calendar.aspx.cs +++ b/ScadaWeb/ScadaWebShell5Beta/Calendar.aspx.cs @@ -13,5 +13,10 @@ protected void Page_Load(object sender, EventArgs e) { } + + protected void Calendar_DayRender(object sender, DayRenderEventArgs e) + { + + } } } \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj index 004a9495d..d6a73199a 100644 --- a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj +++ b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj @@ -133,6 +133,7 @@ + diff --git a/ScadaWeb/ScadaWebShell5Beta/css/calendar.css b/ScadaWeb/ScadaWebShell5Beta/css/calendar.css index bd1550751..cc2fc609a 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/calendar.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/calendar.css @@ -1,18 +1,37 @@ -body { +/*to pretend splitter thinner*/ +body { margin: 0; padding: 0; background-color: white; + color: #444; font-family: 'Open Sans', sans-serif; font-size: 12px; overflow: hidden; display: inline-block; } +a, +a:visited, +a:hover, +a:focus, +a:active { + outline: 0; + color: inherit; + text-decoration: none; +} #frmCalendar .header { background-color: #ccc; + font-weight: 600; height: 25px; } +#frmCalendar .header a { + font-weight: 600; + width: 30px; + text-align: center; +} #frmCalendar th.day-header { background-color: #f9f9f9; + font-size: 11px; + font-weight: 600; height: 25px; } #frmCalendar td { @@ -22,7 +41,9 @@ } #frmCalendar a { display: inline-block; - text-decoration: none; +} +#frmCalendar a:hover { + color: #00b9eb; } #frmCalendar td.day a { width: 30px; @@ -30,6 +51,20 @@ line-height: 30px; text-align: center; } -#frmCalendar a:hover { +#frmCalendar td.day.other-month a { + color: #9ca1a6; +} +#frmCalendar td.day.today a { + background-color: #ccc; +} +#frmCalendar td.day.selected a { + background-color: #0073aa; + color: white; +} +#frmCalendar td.day a:hover { + color: #00b9eb; +} +#frmCalendar td.day.regular a:hover, +#frmCalendar td.day.other-month a:hover { background-color: #e0e0e0; } \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/calendar.less b/ScadaWeb/ScadaWebShell5Beta/css/calendar.less index 84229d9e6..ec01ba4a1 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/calendar.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/calendar.less @@ -1,31 +1,53 @@ @import "globalvar.less"; +@import "shellvar.less"; @calendar-back-color: white; +@link-hover-fore-color: @menu-item-hover-fore-color; @header-back-color: #ccc; -@day-hdr-back-color: #f9f9f9; +@day-header-back-color: #f9f9f9; @day-hover-back-color: #e0e0e0; -@calendar-font-family: @default-font-family;// Arial, Helvetica, sans-serif; +@day-other-month-fore-color: @menu-fore-color-dark; +@day-today-back-color: @header-back-color; +@day-selected-back-color: @menu-item-selected-back-color; +@day-selected-fore-color: @menu-item-selected-fore-color; body { margin: 0; padding: 0; background-color: @calendar-back-color; - font-family: @calendar-font-family; + color: @content-fore-color; + font-family: @default-font-family; font-size: 12px; overflow: hidden; display: inline-block; } -#frmCalendar table { +a, +a:visited, +a:hover, +a:focus, +a:active { + outline: 0; + color: inherit; + text-decoration: none; } #frmCalendar .header { background-color: @header-back-color; + font-weight: 600; height: 25px; } +#frmCalendar .header a { + font-weight: 600; + width: 30px; + text-align: center; +} + #frmCalendar th.day-header { - background-color: @day-hdr-back-color; + background-color: @day-header-back-color; + font-size: 11px; + font-weight: 600; height: 25px; } @@ -37,7 +59,10 @@ body { #frmCalendar a { display: inline-block; - text-decoration: none; +} + +#frmCalendar a:hover { + color: @link-hover-fore-color; } #frmCalendar td.day a { @@ -47,6 +72,24 @@ body { text-align: center; } -#frmCalendar a:hover { +#frmCalendar td.day.other-month a { + color: @day-other-month-fore-color; +} + +#frmCalendar td.day.today a { + background-color: @day-today-back-color; +} + +#frmCalendar td.day.selected a { + background-color: @day-selected-back-color; + color: @day-selected-fore-color; +} + +#frmCalendar td.day a:hover { + color: @link-hover-fore-color; +} + +#frmCalendar td.day.regular a:hover, +#frmCalendar td.day.other-month a:hover { background-color: @day-hover-back-color; } diff --git a/ScadaWeb/ScadaWebShell5Beta/css/calendar.min.css b/ScadaWeb/ScadaWebShell5Beta/css/calendar.min.css index dd80747cd..b8a7c96cb 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/calendar.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/calendar.min.css @@ -1 +1 @@ -body{margin:0;padding:0;background-color:#fff;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;display:inline-block;}#frmCalendar .header{background-color:#ccc;height:25px;}#frmCalendar th.day-header{background-color:#f9f9f9;height:25px;}#frmCalendar td{margin:0;padding:0;vertical-align:middle;}#frmCalendar a{display:inline-block;text-decoration:none;}#frmCalendar td.day a{width:30px;height:30px;line-height:30px;text-align:center;}#frmCalendar a:hover{background-color:#e0e0e0;} \ No newline at end of file +body{margin:0;padding:0;background-color:#fff;color:#444;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;display:inline-block;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#frmCalendar .header{background-color:#ccc;font-weight:600;height:25px;}#frmCalendar .header a{font-weight:600;width:30px;text-align:center;}#frmCalendar th.day-header{background-color:#f9f9f9;font-size:11px;font-weight:600;height:25px;}#frmCalendar td{margin:0;padding:0;vertical-align:middle;}#frmCalendar a{display:inline-block;}#frmCalendar a:hover{color:#00b9eb;}#frmCalendar td.day a{width:30px;height:30px;line-height:30px;text-align:center;}#frmCalendar td.day.other-month a{color:#9ca1a6;}#frmCalendar td.day.today a{background-color:#ccc;}#frmCalendar td.day.selected a{background-color:#0073aa;color:#fff;}#frmCalendar td.day a:hover{color:#00b9eb;}#frmCalendar td.day.regular a:hover,#frmCalendar td.day.other-month a:hover{background-color:#e0e0e0;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/controls/popup.css b/ScadaWeb/ScadaWebShell5Beta/css/controls/popup.css index a5741902b..a46da67d1 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/controls/popup.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/controls/popup.css @@ -13,6 +13,7 @@ top: 0px; width: 0px; height: 0px; + background-color: white; border: 1px solid #888; border-radius: 5px; box-sizing: content-box; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/controls/popup.less b/ScadaWeb/ScadaWebShell5Beta/css/controls/popup.less index c140b2a84..842fa8456 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/controls/popup.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/controls/popup.less @@ -1,5 +1,7 @@ -@popup-border-color: #888; +@popup-overlay-back-color: gray; +@popup-border-color: #888; @popup-shadow-color: #aaa; +@popup-wrapper-back-color: white; .popup-overlay { position: fixed; @@ -7,7 +9,7 @@ top: 0; width: 100%; height: 100%; - background-color: gray; + background-color: @popup-overlay-back-color; opacity: 0; } @@ -17,6 +19,7 @@ top: 0px; width: 0px; height: 0px; + background-color: @popup-wrapper-back-color; border: 1px solid @popup-border-color; border-radius: 5px; box-sizing: content-box; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/controls/popup.min.css b/ScadaWeb/ScadaWebShell5Beta/css/controls/popup.min.css index 24f24f0bc..b39313533 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/controls/popup.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/controls/popup.min.css @@ -1 +1 @@ -.popup-overlay{position:fixed;left:0;top:0;width:100%;height:100%;background-color:#808080;opacity:0;}.popup-wrapper{position:fixed;left:0;top:0;width:0;height:0;border:1px solid #888;border-radius:5px;box-sizing:content-box;box-shadow:2px 2px 5px #aaa;display:inline-block;overflow:hidden;}.popup-frame{margin:0;padding:0;border:none;display:inline-block;overflow:hidden;} \ No newline at end of file +.popup-overlay{position:fixed;left:0;top:0;width:100%;height:100%;background-color:#808080;opacity:0;}.popup-wrapper{position:fixed;left:0;top:0;width:0;height:0;background-color:#fff;border:1px solid #888;border-radius:5px;box-sizing:content-box;box-shadow:2px 2px 5px #aaa;display:inline-block;overflow:hidden;}.popup-frame{margin:0;padding:0;border:none;display:inline-block;overflow:hidden;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/js/calendar.js b/ScadaWeb/ScadaWebShell5Beta/js/calendar.js new file mode 100644 index 000000000..9a3c5ddfa --- /dev/null +++ b/ScadaWeb/ScadaWebShell5Beta/js/calendar.js @@ -0,0 +1,7 @@ +$(document).ready(function () { + // remove default calendar colors generated by ASP.NET + $("#frmCalendar *").css({ + "background-color": "", + "color": "" + }); +}); \ No newline at end of file From a4117640fc3c6b706284e9fff433bb1600f83fe7 Mon Sep 17 00:00:00 2001 From: 2mik Date: Mon, 6 Jun 2016 12:15:15 +0300 Subject: [PATCH 092/382] ScadaWeb5: popup callback --- .../PlgTable/plugins/Table/js/table.js | 4 +- .../ScadaWebShell5Beta/js/controls/popup.js | 66 ++++++++++++++----- 2 files changed, 54 insertions(+), 16 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js index 89b4382f5..88f01eb50 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js @@ -81,7 +81,9 @@ $(document).ready(function () { var dialogs = viewHub ? viewHub.dialogs : null; if (dialogs) { $("#spanDate *").click(function (event) { - dialogs.showCalendar($("#txtDate"), null); + dialogs.showCalendar($("#txtDate"), function (dialogResult) { + alert("dialogResult = " + dialogResult); + }); }); } }); diff --git a/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js b/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js index 4edc37add..586c1ada2 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js @@ -20,12 +20,31 @@ var scada = scada || {}; scada.Popup = function () { // Window that holds popups this._holderWindow = window; + + // Callback functions of the existing popups + this._popupCallbacks = new Map(); }; // Event handler that removes the popup on press Escape key scada.Popup.prototype._removePopupOnEscape = function (event) { if (event.which == 27 /*Escape*/) { - var popupElem = event.data; + this._cancelDropdown(event.data); + } +}; + +// Close the dropdown popup and execute a callback with a cancel result +scada.Popup.prototype._cancelDropdown = function (popupElem) { + var frame = popupElem.find(".popup-frame"); + if (frame.length > 0) { + var frameWnd = frame[0].contentWindow; + var callback = this._popupCallbacks.get(frameWnd); + this._popupCallbacks.delete(frameWnd); + popupElem.remove(); + + if (callback) { + callback(false); + } + } else { popupElem.remove(); } }; @@ -61,27 +80,28 @@ scada.Popup.prototype._getOffset = function (elem) { } wnd = parentWnd; } - } while (wnd != this._holderWindow && wnd != window.top); + } while (wnd != this._holderWindow && wnd != wnd.parent); return { left: left, top: top }; }; // Show popup with the specified url as a dropdown menu below the anchorElem. -// callback is function (success, dialogResult, extraParams) +// callback is function (dialogResult, extraParams) scada.Popup.prototype.showDropdown = function (url, anchorElem, callback) { - var popupElem = $("" + - ""); + var thisObj = this; + var popupElem = $(""); $("body").append(popupElem); - var overlay = popupElem.filter(".popup-overlay"); - var wrapper = popupElem.filter(".popup-wrapper"); + var overlay = popupElem.find(".popup-overlay"); + var wrapper = popupElem.find(".popup-wrapper"); var frame = popupElem.find(".popup-frame"); // setup overlay overlay .css("z-index", scada.utils.FRONT_ZINDEX) .click(function () { - popupElem.remove(); + thisObj._cancelDropdown(popupElem); }); // setup wrapper @@ -91,22 +111,25 @@ scada.Popup.prototype.showDropdown = function (url, anchorElem, callback) { }); // remove the popup on press Escape key in the parent window + var removePopupOnEscape = this._removePopupOnEscape.bind(this); $(document) - .off("keydown", null, this._removePopupOnEscape) - .on("keydown", null, popupElem, this._removePopupOnEscape); + .off("keydown", null, removePopupOnEscape) + .on("keydown", null, popupElem, removePopupOnEscape); // load the frame - var thisObj = this; frame .on("load", function () { - // remove the popup on press Escape key in the frame + // store callback function var frameWnd = frame[0].contentWindow; + thisObj._popupCallbacks.set(frameWnd, callback); + + // remove the popup on press Escape key in the frame if (frameWnd.$) { var jqFrameDoc = frameWnd.$(frameWnd.document); jqFrameDoc.ready(function () { jqFrameDoc - .off("keydown", null, thisObj._removePopupOnEscape) - .on("keydown", null, popupElem, thisObj._removePopupOnEscape); + .off("keydown", null, removePopupOnEscape) + .on("keydown", null, popupElem, removePopupOnEscape); }); } }) @@ -157,8 +180,21 @@ scada.Popup.prototype.showDropdown = function (url, anchorElem, callback) { .attr("src", url); }; +// Close the dropdown popup and execute a callback with the specified result +scada.Popup.prototype.closeDropdown = function (popupWnd, dialogResult, extraParams) { + var frame = $(popupWnd.frameElement); + var popupElem = frame.closest(".popup-dropdown"); + var callback = this._popupCallbacks.get(popupWnd); + this._popupCallbacks.delete(popupWnd); + popupElem.remove(); + + if (callback) { + callback(dialogResult, extraParams); + } +}; + // Show modal dialog with the specified url. -// callback is function (success, dialogResult, extraParams), +// callback is function (dialogResult, extraParams), // requires Bootstrap scada.Popup.prototype.showModal = function (url, callback) { From e0ce2dc1fcba4b13e7f1f3e8aa12b24ce8b1ff91 Mon Sep 17 00:00:00 2001 From: 2mik Date: Mon, 6 Jun 2016 14:11:02 +0300 Subject: [PATCH 093/382] ScadaWeb5: calendar works --- .../PlgTable/plugins/Table/js/table.js | 4 +- ScadaWeb/ScadaWebShell5Beta/Calendar.aspx | 2 + ScadaWeb/ScadaWebShell5Beta/Calendar.aspx.cs | 46 ++++++++++++++++--- ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js | 10 ++-- ScadaWeb/ScadaWebShell5Beta/js/api/utils.js | 8 ++-- ScadaWeb/ScadaWebShell5Beta/js/calendar.js | 10 +++- 6 files changed, 64 insertions(+), 16 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js index 88f01eb50..37005c918 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js @@ -81,8 +81,8 @@ $(document).ready(function () { var dialogs = viewHub ? viewHub.dialogs : null; if (dialogs) { $("#spanDate *").click(function (event) { - dialogs.showCalendar($("#txtDate"), function (dialogResult) { - alert("dialogResult = " + dialogResult); + dialogs.showCalendar($("#txtDate"), function (dialogResult, extraParams) { + alert("dialogResult = " + dialogResult + ", extraParams = " + extraParams); }); }); } diff --git a/ScadaWeb/ScadaWebShell5Beta/Calendar.aspx b/ScadaWeb/ScadaWebShell5Beta/Calendar.aspx index d673ea150..5e97cafdd 100644 --- a/ScadaWeb/ScadaWebShell5Beta/Calendar.aspx +++ b/ScadaWeb/ScadaWebShell5Beta/Calendar.aspx @@ -7,6 +7,8 @@ Calendar - Rapid SCADA + + diff --git a/ScadaWeb/ScadaWebShell5Beta/Calendar.aspx.cs b/ScadaWeb/ScadaWebShell5Beta/Calendar.aspx.cs index bf0066b26..73d23cd6d 100644 --- a/ScadaWeb/ScadaWebShell5Beta/Calendar.aspx.cs +++ b/ScadaWeb/ScadaWebShell5Beta/Calendar.aspx.cs @@ -1,22 +1,56 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using System.Web.UI; +/* + * Copyright 2016 Mikhail Shiryaev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Product : Rapid SCADA + * Module : SCADA-Web + * Summary : Calendar popup web form + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + */ + +using System; +using System.Globalization; using System.Web.UI.WebControls; namespace Scada.Web { + /// + /// Calendar popup web form + /// Всплывающая веб-форма календаря + /// public partial class WFrmCalendar : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { + // установка выбранной даты, если она задана в параметрах запроса + DateTime date; + if (DateTime.TryParse(Request.QueryString["date"], Localization.Culture, DateTimeStyles.None, out date)) + Calendar.VisibleDate = Calendar.SelectedDate = date; + // убрать всплывающую подсказку по умолчанию + Calendar.Attributes["title"] = ""; } protected void Calendar_DayRender(object sender, DayRenderEventArgs e) { - + DateTime date = e.Day.Date; + e.Cell.Text = string.Format("{2}", + date.Year, date.Month, date.Day, date.ToString("d", Localization.Culture)); } } } \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js b/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js index 7fedecb81..6903b72af 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js @@ -50,11 +50,15 @@ scada.dialogs = { } }, - // Show calendar dropdown form - showCalendar: function (anchorElem, callback) { + // Show calendar dropdown form. + // callback is function (dialogResult, extraParams), + // dialogResult is true or false, + // extraParams is object { date, dateStr } + showCalendar: function (anchorElem, selectedDate, callback) { var popup = scada.popupLocator.getPopup(); if (popup) { - popup.showDropdown(this.rootPath + "Calendar.aspx", anchorElem, callback); + var queryString = selectedDate ? "?date=" + encodeURIComponent(selectedDate) : ""; + popup.showDropdown(this.rootPath + "Calendar.aspx" + queryString, anchorElem, callback); } } }; \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/utils.js b/ScadaWeb/ScadaWebShell5Beta/js/api/utils.js index 5851fa202..407fafa6f 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/utils.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/utils.js @@ -49,7 +49,7 @@ scada.utils = { // Get the query string parameter value getQueryStringParam: function (paramName, opt_url) { if (paramName) { - var url = opt_url ? opt_url : unescape(window.location); + var url = opt_url ? opt_url : decodeURIComponent(window.location); var begInd = queryString.indexOf("?"); if (begInd > 0) { @@ -73,7 +73,7 @@ scada.utils = { // The method returns a new string setQueryStringParam: function (paramName, paramVal, opt_url) { if (paramName) { - var url = opt_url ? opt_url : unescape(window.location); + var url = opt_url ? opt_url : decodeURIComponent(window.location); var searchName = "?" + paramName + "="; var nameBegInd = url.indexOf(searchName); @@ -86,14 +86,14 @@ scada.utils = { // replace parameter value var valBegInd = nameBegInd + searchName.length; var valEndInd = url.indexOf("&", valBegInd); - var newUrl = url.substring(0, valBegInd) + paramVal; + var newUrl = url.substring(0, valBegInd) + encodeURIComponent(paramVal); return valEndInd > 0 ? newUrl + url.substring(valEndInd) : newUrl; } else { // add parameter var mark = url.indexOf("?") >= 0 ? "&" : "?"; - return url + mark + paramName + "=" + paramVal; + return url + mark + paramName + "=" + encodeURIComponent(paramVal); } } else { return ""; diff --git a/ScadaWeb/ScadaWebShell5Beta/js/calendar.js b/ScadaWeb/ScadaWebShell5Beta/js/calendar.js index 9a3c5ddfa..61b14e4de 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/calendar.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/calendar.js @@ -1,4 +1,12 @@ -$(document).ready(function () { +// Return the selected date and close the popup +function selectDate(year, month, day, dateStr) { + var popup = scada.popupLocator.getPopup(); + if (popup) { + popup.closeDropdown(window, true, { date: new Date(year, month - 1, day), dateStr: dateStr }); + } +} + +$(document).ready(function () { // remove default calendar colors generated by ASP.NET $("#frmCalendar *").css({ "background-color": "", From b507e8176f618e2c5aabc96c6b699ce6ef23bbab Mon Sep 17 00:00:00 2001 From: 2mik Date: Mon, 6 Jun 2016 18:44:20 +0300 Subject: [PATCH 094/382] PlgTable: time period --- .../PlgScheme/plugins/Scheme/js/scheme.js | 12 +- .../PlgTable/plugins/Table/Table.aspx | 2 +- .../PlgTable/plugins/Table/Table.aspx.cs | 123 +++++++++--------- .../PlgTable/plugins/Table/css/layout.css | 7 +- .../PlgTable/plugins/Table/css/layout.less | 8 +- .../PlgTable/plugins/Table/css/layout.min.css | 2 +- .../PlgTable/plugins/Table/css/table.css | 14 +- .../PlgTable/plugins/Table/css/table.less | 10 +- .../PlgTable/plugins/Table/css/table.min.css | 2 +- .../PlgTable/plugins/Table/js/layout.js | 3 +- .../PlgTable/plugins/Table/js/table.js | 106 +++++++++++++-- ScadaWeb/ScadaWebShell5Beta/js/mastermain.js | 4 +- ScadaWeb/ScadaWebShell5Beta/js/view.js | 11 +- 13 files changed, 195 insertions(+), 109 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js index 6ae4e8938..ff7a0e73f 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/scheme.js @@ -122,7 +122,7 @@ function displayScale() { // Load the scheme scale from the cookies function loadScale() { - var scale = scada.utils.getCookie("SchemeScale"); + var scale = scada.utils.getCookie("Scheme.SchemeScale"); if (scale) { scheme.setScale(scale); } @@ -130,7 +130,7 @@ function loadScale() { // Save the scheme scale in the cookies function saveScale(opt_scale) { - scada.utils.setCookie("SchemeScale", opt_scale ? opt_scale : scheme.scale); + scada.utils.setCookie("Scheme.SchemeScale", opt_scale ? opt_scale : scheme.scale); } // Apply additional css styles in case of using iOS @@ -190,13 +190,13 @@ $(document).ready(function () { notifier = new scada.Notifier("#divNotif"); notifier.startClearingNotifications(); + $(window).on("resize " + scada.EventTypes.UPDATE_LAYOUT, function () { + updateLayout(); + }); + if (DEBUG_MODE) { initDebugTools(); } else { startLoadingScheme(viewID); } - - $(window).on("resize " + scada.EventTypes.UPDATE_LAYOUT, function () { - updateLayout(); - }); }); \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx index 6b227b31a..6ed34063e 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx @@ -31,7 +31,7 @@
<%= GenerateTimeSelectHtml("selTimeFrom", true, 0) %> - <%= GenerateTimeSelectHtml("selTimeTo", false, 23) %><%= selTimeFromHtml %> - <%= selTimeToHtml %>
TitleChangedNavigate private string GetLocalizedHour(int hour) { - return DateTime.MinValue.AddHours(hour).ToString("t", Localization.Culture); + return + (hour >= 0 ? "" : PlgPhrases.PrevDayItem) + + DateTime.MinValue.AddHours(hour > 0 ? hour : hour + 24).ToString("t", Localization.Culture); } /// - /// Добавить ячейку заголовка + /// Генерировать HTML-код выпадающего списка выбора времени /// - private void AppendHeaderCell(StringBuilder sbHtml, string cssClass, string innerHtml) + private string GenerateTimeSelectHtml(string elemID, bool addPrevDay, int selectedHour) { - if (string.IsNullOrEmpty(cssClass)) - sbHtml.Append(""); - else - sbHtml.Append(""); + StringBuilder sbHtml = new StringBuilder(); + sbHtml.Append(""); + return sbHtml.ToString(); } /// @@ -109,17 +135,23 @@ private void AppendHint(StringBuilder sbHtml, bool addBreak, string label, int n /// /// Генерировать HTML-код табличного представления /// - private string GenerateTableViewHtml(TableView tableView, bool cmdEnabled) + private string GenerateTableViewHtml(TableView tableView, bool cmdEnabled, int timeFrom, int timeTo) { + const int FirstHour = -24; + const int LastHour = 23; + StringBuilder sbHtml = new StringBuilder(); sbHtml.AppendLine(""); // заголовок таблицы sbHtml.AppendLine(""); - AppendHeaderCell(sbHtml, "", "Item"); - AppendHeaderCell(sbHtml, "cur", "Current"); - for (int hour = 0; hour < 24; hour++) - AppendHeaderCell(sbHtml, "", GetLocalizedHour(hour)); + AppendCell(sbHtml, "cap", null, "Item"); + AppendCell(sbHtml, "cur", null, "Current"); + for (int hour = FirstHour; hour <= LastHour; hour++) + { + AppendCell(sbHtml, timeFrom <= hour && hour <= timeTo ? "hour" : "hour hidden", hour, + GetLocalizedHour(hour)); + } sbHtml.AppendLine().AppendLine(""); // строки таблицы @@ -183,18 +215,9 @@ private string GenerateTableViewHtml(TableView tableView, bool cmdEnabled) } // ячейки текущих и часовых данных - if (cnlNum > 0) - { - AppendCell(sbHtml, "cur", null, "---"); - for (int hour = 0; hour < 24; hour++) - AppendCell(sbHtml, "hour", hour, "---"); - } - else - { - AppendCell(sbHtml, "cur", null, ""); - for (int hour = 0; hour < 24; hour++) - AppendCell(sbHtml, "hour", hour, ""); - } + AppendCell(sbHtml, "cur", null, ""); + for (int hour = FirstHour; hour <= LastHour; hour++) + AppendCell(sbHtml, timeFrom <= hour && hour <= timeTo ? "hour" : "hour hidden", hour, ""); // тег окончания строки sbHtml.AppendLine().AppendLine(""); @@ -205,41 +228,6 @@ private string GenerateTableViewHtml(TableView tableView, bool cmdEnabled) return sbHtml.ToString(); } - /// - /// Генерировать HTML-код выпадающего списка выбора времени - /// - protected string GenerateTimeSelectHtml(string elemID, bool addPrevDay, int selectedHour) - { - StringBuilder sbHtml = new StringBuilder(); - sbHtml.Append(""); - return sbHtml.ToString(); - } - protected void Page_Load(object sender, EventArgs e) { @@ -277,6 +265,17 @@ protected void Page_Load(object sender, EventArgs e) Response.Redirect(UrlTemplates.NoView); } + // получение периода времени из cookies + HttpCookie cookie = Request.Cookies["Table.TimeFrom"]; + int timeFrom; + if (cookie == null || !int.TryParse(cookie.Value, out timeFrom)) + timeFrom = 0; + + cookie = Request.Cookies["Table.TimeTo"]; + int timeTo; + if (cookie == null || !int.TryParse(cookie.Value, out timeTo)) + timeTo = 23; + // подготовка данных для вывода на веб-страницу refrRate = userData.WebSettings.DataRefrRate; @@ -284,8 +283,10 @@ protected void Page_Load(object sender, EventArgs e) Localization.Dictionaries.TryGetValue("Scada.Web.Plugins.Table.WFrmTable.Js", out dict); phrases = WebUtils.DictionaryToJs(dict); + selTimeFromHtml = GenerateTimeSelectHtml("selTimeFrom", true, timeFrom); + selTimeToHtml = GenerateTimeSelectHtml("selTimeTo", false, timeTo); bool cmdEnabled = userData.WebSettings.CmdEnabled && rights.ControlRight; - tableViewHtml = GenerateTableViewHtml(tableView, cmdEnabled); + tableViewHtml = GenerateTableViewHtml(tableView, cmdEnabled, timeFrom, timeTo); } } } \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/layout.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/layout.css index 7c290bb1e..d0a64a6f2 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/layout.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/layout.css @@ -91,23 +91,20 @@ body { background-color: #e0e0e0; color: black; } -#divTblWrapper th, #divTblWrapper td { border: 1px solid #999; font-size: 12px; line-height: 18px; + padding: 2px 3px; vertical-align: middle; white-space: nowrap; } -#divTblWrapper th { +#divTblWrapper tr.hdr td { border: 1px solid #888; font-weight: 600; padding: 2px 7px; text-align: center; } -#divTblWrapper td { - padding: 2px 3px; -} #divTblWrapper td a, #divTblWrapper td span, #divTblWrapper td img { diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/layout.less b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/layout.less index ac09f7e79..4d927246d 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/layout.less +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/layout.less @@ -133,26 +133,22 @@ body { color: @row-hover-fore-color; } -#divTblWrapper th, #divTblWrapper td { border: 1px solid @cell-border-color; font-size: @cell-font-size; line-height: @cell-height; + padding: 2px 3px; vertical-align: middle; white-space: nowrap; } -#divTblWrapper th { +#divTblWrapper tr.hdr td { border: 1px solid @cell-border-color-hdr; font-weight: 600; padding: 2px 7px; text-align: center; } -#divTblWrapper td { - padding: 2px 3px; -} - #divTblWrapper td a, #divTblWrapper td span, #divTblWrapper td img { diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/layout.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/layout.min.css index fe33dadaa..a59d7db2d 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/layout.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/layout.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#9ca1a6;font-size:14px;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;line-height:30px;vertical-align:top;}#divToolbar .tool-btn:hover{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;}#spanDate:hover i{color:#00b9eb;}#divDebugTools{padding:0 10px;display:none;font-size:12px;vertical-align:top;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper th,#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;vertical-align:middle;white-space:nowrap;}#divTblWrapper th{border:1px solid #888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td{padding:2px 3px;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#9ca1a6;font-size:14px;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;line-height:30px;vertical-align:top;}#divToolbar .tool-btn:hover{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;}#spanDate:hover i{color:#00b9eb;}#divDebugTools{padding:0 10px;display:none;font-size:12px;vertical-align:top;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border:1px solid #888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css index eefd3c4a9..436d81de0 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css @@ -91,23 +91,20 @@ body { background-color: #e0e0e0; color: black; } -#divTblWrapper th, #divTblWrapper td { border: 1px solid #999; font-size: 12px; line-height: 18px; + padding: 2px 3px; vertical-align: middle; white-space: nowrap; } -#divTblWrapper th { +#divTblWrapper tr.hdr td { border: 1px solid #888; font-weight: 600; padding: 2px 7px; text-align: center; } -#divTblWrapper td { - padding: 2px 3px; -} #divTblWrapper td a, #divTblWrapper td span, #divTblWrapper td img { @@ -158,12 +155,17 @@ body { box-shadow: 2px 2px 5px #aaa; color: #444; display: none; - /*inline-block*/ line-height: normal; padding: 5px; position: fixed; } +#divTblWrapper td.cap span.hint.visible { + display: inline-block; +} #divTblWrapper td.cur, #divTblWrapper td.hour { text-align: center; +} +#divTblWrapper td.hidden { + display: none; } \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.less b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.less index 1e9c1dc08..52219e8f1 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.less +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.less @@ -59,13 +59,21 @@ border-radius: 5px; box-shadow: 2px 2px 5px @hint-shadow-color; color: @hint-fore-color; - display: none; /*inline-block*/ + display: none; line-height: normal; padding: 5px; position: fixed; } +#divTblWrapper td.cap span.hint.visible { + display: inline-block; +} + #divTblWrapper td.cur, #divTblWrapper td.hour { text-align: center; } + +#divTblWrapper td.hidden { + display: none; +} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css index a4725eec2..ba18b3246 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#9ca1a6;font-size:14px;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;line-height:30px;vertical-align:top;}#divToolbar .tool-btn:hover{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;}#spanDate:hover i{color:#00b9eb;}#divDebugTools{padding:0 10px;display:none;font-size:12px;vertical-align:top;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper th,#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;vertical-align:middle;white-space:nowrap;}#divTblWrapper th{border:1px solid #888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td{padding:2px 3px;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divTblWrapper td.cap{padding:2px 5px;}#divTblWrapper td.cap img{width:16px;height:16px;margin:0 5px 0 0;border:none;cursor:pointer;}#divTblWrapper td.cap a{height:18px;padding-right:5px;color:#0073aa;display:inline-block;line-height:18px;text-decoration:none;}#divTblWrapper td.cap a:hover{color:#00a0d2;}#divTblWrapper td.cap span.cmd{display:inline-block;width:18px;height:18px;cursor:pointer;color:#a00;font-size:15px;line-height:18px;text-align:center;}#divTblWrapper td.cap span.cmd:hover{color:#f00;}#divTblWrapper td.cap span.cmd::before{content:"";font-family:FontAwesome;}#divTblWrapper td.cap span.hint{background-color:#fff;border:1px solid #888;border-radius:5px;box-shadow:2px 2px 5px #aaa;color:#444;display:none;line-height:normal;padding:5px;position:fixed;}#divTblWrapper td.cur,#divTblWrapper td.hour{text-align:center;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#9ca1a6;font-size:14px;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;line-height:30px;vertical-align:top;}#divToolbar .tool-btn:hover{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;}#spanDate:hover i{color:#00b9eb;}#divDebugTools{padding:0 10px;display:none;font-size:12px;vertical-align:top;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border:1px solid #888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divTblWrapper td.cap{padding:2px 5px;}#divTblWrapper td.cap img{width:16px;height:16px;margin:0 5px 0 0;border:none;cursor:pointer;}#divTblWrapper td.cap a{height:18px;padding-right:5px;color:#0073aa;display:inline-block;line-height:18px;text-decoration:none;}#divTblWrapper td.cap a:hover{color:#00a0d2;}#divTblWrapper td.cap span.cmd{display:inline-block;width:18px;height:18px;cursor:pointer;color:#a00;font-size:15px;line-height:18px;text-align:center;}#divTblWrapper td.cap span.cmd:hover{color:#f00;}#divTblWrapper td.cap span.cmd::before{content:"";font-family:FontAwesome;}#divTblWrapper td.cap span.hint{background-color:#fff;border:1px solid #888;border-radius:5px;box-shadow:2px 2px 5px #aaa;color:#444;display:none;line-height:normal;padding:5px;position:fixed;}#divTblWrapper td.cap span.hint.visible{display:inline-block;}#divTblWrapper td.cur,#divTblWrapper td.hour{text-align:center;}#divTblWrapper td.hidden{display:none;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/layout.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/layout.js index 23e50f0fb..e648f1d41 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/layout.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/layout.js @@ -1,5 +1,4 @@ -// View hub. Must be defined in the parent window -// TODO: move to another *.js +// The view hub object var viewHub = scada.viewHubLocator.getViewHub(); // Apply additional css styles in case of using iOS diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js index 37005c918..a834b2e66 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js @@ -6,6 +6,10 @@ var viewID = viewID || 0; var refrRate = refrRate || 1000; // Localized phrases var phrases = phrases || {}; +// Beginning of the time period +var timeFrom = timeFrom || 0; +// End of the time period +var timeTo = timeTo || 23; // Set widths of the item links to fill cells function setItemLinkWidths() { @@ -18,6 +22,69 @@ function setItemLinkWidths() { }); } +// Retrieve the time period from the control values +function retrieveTimePeriod() { + timeFrom = parseInt($("#selTimeFrom").val()); + timeTo = parseInt($("#selTimeTo").val()); +} + +// Correct the beginning of the time period +function correctTimeFrom() { + if (timeFrom > timeTo) { + timeFrom = timeTo; + $("#selTimeFrom").val(timeFrom); + } +} + +// Correct the end of the time period +function correctTimeTo() +{ + if (timeTo < timeFrom) { + timeTo = timeFrom; + $("#selTimeTo").val(timeFrom); + } +} + +// Save the time period in the cookies +function saveTimePeriod() { + scada.utils.setCookie("Table.TimeFrom", $("#selTimeFrom").val()); + scada.utils.setCookie("Table.TimeTo", $("#selTimeTo").val()); +} + +// Set visibility of the table view columns according to the time period +function updateTableViewHours() { + var firstHour = $("#divTblWrapper tr:first td.hour:first").data("hour"); + var lastHour = $("#divTblWrapper tr:first td.hour:last").data("hour"); + + $("#divTblWrapper tr").each(function () { + var row = $(this); + var hourCells = row.find("td.hour"); + + // show all the cells of the row + hourCells.removeClass("hidden"); + + // hide cells from the left + var cellsToHide = timeFrom - firstHour; + if (cellsToHide > 0) { + var cells = hourCells.slice(0, cellsToHide); + cells.addClass("hidden"); + if (row.hasClass("item")) { + cells.text(""); // clear cell text + } + } + + // hide cells from the right + cellsToHide = lastHour - timeTo; + if (cellsToHide > 0) { + cells = hourCells.slice(-cellsToHide); + cells.addClass("hidden"); + if (row.hasClass("item")) { + cells.text(""); // clear cell text + } + } + }); +} + // Show hint associated with the icon function showHint(imgIcon) { var iconOffset = imgIcon.offset(); @@ -32,16 +99,17 @@ function showHint(imgIcon) { } } - hint.css({ - "display": "inline-block", + hint + .css({ "left": iconOffset.left + imgIcon.outerWidth(true), "top": hintTop - }); + }) + .addClass("visible"); } // Hide hint associated with the icon function hideHint(imgIcon) { - imgIcon.siblings("span.hint").css("display", "none"); + imgIcon.siblings("span.hint").removeClass("visible"); } @@ -53,17 +121,25 @@ $(document).ready(function () { notifier = new scada.Notifier("#divNotif"); notifier.startClearingNotifications(); - if (DEBUG_MODE) { - initDebugTools(); - } else { - // TODO: start updating - } - // update layout on window and table area resize $(window).on("resize " + scada.EventTypes.UPDATE_LAYOUT, function () { updateLayout(); }); + // process the time period changing + $("#selTimeFrom, #selTimeTo").change(function () { + retrieveTimePeriod(); + + if ($(this).attr("id") == "selTimeFrom") { + correctTimeTo(); + } else { + correctTimeFrom(); + } + + saveTimePeriod(); + updateTableViewHours(); + }); + // show and hide hint on hover and click $("#divTblWrapper img.icon").hover( function () { @@ -81,9 +157,15 @@ $(document).ready(function () { var dialogs = viewHub ? viewHub.dialogs : null; if (dialogs) { $("#spanDate *").click(function (event) { - dialogs.showCalendar($("#txtDate"), function (dialogResult, extraParams) { - alert("dialogResult = " + dialogResult + ", extraParams = " + extraParams); + dialogs.showCalendar($("#txtDate"), "09/06/2016", function (dialogResult, extraParams) { + //alert("dialogResult = " + dialogResult + ", extraParams = " + extraParams); }); }); } + + if (DEBUG_MODE) { + initDebugTools(); + } else { + // TODO: start updating + } }); diff --git a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js index c60c8a037..035266ad2 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js @@ -40,7 +40,7 @@ scada.masterMain = { // Save the left pane visibility in the cookies _saveLeftPaneVisible: function () { - scada.utils.setCookie("LeftPaneVisible", this.leftPaneExpanded); + scada.utils.setCookie("Shell.LeftPaneVisible", this.leftPaneExpanded); }, // Check that a user is logged on @@ -162,7 +162,7 @@ scada.masterMain = { // Load page visual state from the cookies loadVisualState: function () { - var leftPaneVisible = scada.utils.getCookie("LeftPaneVisible"); + var leftPaneVisible = scada.utils.getCookie("Shell.LeftPaneVisible"); if (leftPaneVisible == "false") { this.collapseLeftPane(); } diff --git a/ScadaWeb/ScadaWebShell5Beta/js/view.js b/ScadaWeb/ScadaWebShell5Beta/js/view.js index edb01c0a7..9dc3f7991 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/view.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/view.js @@ -49,9 +49,10 @@ scada.view = { return jqObj.css("display") == "none" ? 0 : jqObj.outerHeight(); }, - // Load the splitter position from cookies + // Load the splitter position from the cookies _loadSplitterPosition: function () { - var dataWindowHeight = scada.utils.getCookie("DataWindowHeight") || $("#divDataWindow").outerHeight(); + var dataWindowHeight = parseInt(scada.utils.getCookie("Shell.DataWindowHeight")) || + $("#divDataWindow").outerHeight(); var minHeight = parseInt($("#divDataWindow").css("min-height"), 10); var maxHeight = $("#divViewContent").innerHeight() - parseInt($("#divView").css("min-height"), 10) - @@ -71,7 +72,7 @@ scada.view = { // Load active data window URL from the cookies _loadActiveDataWindow: function () { - var activeDataWindow = scada.utils.getCookie("ActiveDataWindow"); + var activeDataWindow = scada.utils.getCookie("Shell.ActiveDataWindow"); var thisView = this; if (activeDataWindow) { @@ -87,7 +88,7 @@ scada.view = { // Save active data window URL in the cookies _saveActiveDataWindow: function () { - scada.utils.setCookie("ActiveDataWindow", this._dataWindow.url); + scada.utils.setCookie("Shell.ActiveDataWindow", this._dataWindow.url); }, // Hide bottom pane if no data windows exist @@ -175,7 +176,7 @@ scada.view = { // Save the splitter position in the cookies saveSplitterPosition: function () { var dataWindowHeight = $("#divDataWindow").outerHeight(); - scada.utils.setCookie("DataWindowHeight", dataWindowHeight); + scada.utils.setCookie("Shell.DataWindowHeight", dataWindowHeight); }, // Reload view and data windows on iOS to fix frame size, From 96cbbff67f6861fff3edbe50a8f49a24791abe32 Mon Sep 17 00:00:00 2001 From: 2mik Date: Tue, 7 Jun 2016 12:43:43 +0300 Subject: [PATCH 095/382] PlgTable: view date --- .../PlgScheme/plugins/Scheme/Scheme.aspx | 30 +++-- .../plugins/Scheme/Scheme.aspx.designer.cs | 9 -- .../OpenPlugins/PlgTable/js/api/viewhub.js | 9 +- .../PlgTable/plugins/Table/Table.aspx | 30 ++--- .../PlgTable/plugins/Table/Table.aspx.cs | 4 + .../plugins/Table/Table.aspx.designer.cs | 18 --- .../PlgTable/plugins/Table/css/layout.css | 3 +- .../PlgTable/plugins/Table/css/layout.less | 3 +- .../PlgTable/plugins/Table/css/layout.min.css | 2 +- .../PlgTable/plugins/Table/css/table.css | 3 +- .../PlgTable/plugins/Table/css/table.min.css | 2 +- .../PlgTable/plugins/Table/js/layout.js | 5 +- .../PlgTable/plugins/Table/js/table.js | 108 ++++++++++++++---- ScadaWeb/ScadaWebShell5Beta/css/calendar.css | 3 +- ScadaWeb/ScadaWebShell5Beta/css/calendar.less | 3 +- .../ScadaWebShell5Beta/css/calendar.min.css | 2 +- ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js | 9 +- 17 files changed, 150 insertions(+), 93 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx index bf7e577a0..53d98f4fc 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx @@ -27,22 +27,20 @@ -
-
+
+
+
+
+
100%
Load SchemeCreate DOMStart UpdatingAdd Notification
-
-
-
100%
Load SchemeCreate DOMStart UpdatingAdd Notification -
-
- +
diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.designer.cs b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.designer.cs index 19804a4ad..0ab55c339 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.designer.cs +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/Scheme.aspx.designer.cs @@ -12,15 +12,6 @@ namespace Scada.Web.Plugins.Scheme { public partial class WFrmScheme { - /// - /// frmScheme control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlForm frmScheme; - /// /// lblFitScreenBtn control. /// diff --git a/ScadaWeb/OpenPlugins/PlgTable/js/api/viewhub.js b/ScadaWeb/OpenPlugins/PlgTable/js/api/viewhub.js index 9ea3b83e9..576301a50 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/js/api/viewhub.js +++ b/ScadaWeb/OpenPlugins/PlgTable/js/api/viewhub.js @@ -20,6 +20,9 @@ scada.ViewHub = function (mainWindow) { // Current view ID this.currentViewID = 0; + // Current view date for displaying data + this.currentViewDate = null; + // Main window object that manages a view and data windows this.mainWindow = mainWindow; @@ -57,7 +60,7 @@ scada.ViewHub.prototype.notify = function (eventType, senderWnd, opt_extraParams var handled = false; var senderIsView = senderWnd == this.viewWindow; - // preprocess navigation + // preprocess events if (eventType == scada.EventTypes.VIEW_NAVIGATE) { if (senderIsView) { this.currentViewID = opt_extraParams; @@ -66,6 +69,10 @@ scada.ViewHub.prototype.notify = function (eventType, senderWnd, opt_extraParams } } + if (eventType == scada.EventTypes.VIEW_DATE_CHANGED) { + this.currentViewDate = opt_extraParams; + } + // pass the notification to the main window if (!handled && this.mainWindow && this.mainWindow != senderWnd) { var jq = this.mainWindow.$; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx index 6ed34063e..b162b61d1 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx @@ -22,25 +22,25 @@ var viewID = <%= viewID %>; var refrRate = <%= refrRate %>; var phrases = <%= phrases %>; + var today = <%= today %>; + var locale = "<%= Scada.Localization.Culture.Name %>"; -
-
+
+
+
<%= selTimeFromHtml %> - <%= selTimeToHtml %>
TitleChangedNavigateDateChanged
-
<%= selTimeFromHtml %> - <%= selTimeToHtml %>
TitleChangedNavigateDateChanged -
-
-
- <%= tableViewHtml %> -
- +
+
+ <%= tableViewHtml %> +
diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs index 11acff4fa..a0a40adb0 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs @@ -53,6 +53,7 @@ public partial class WFrmTable : System.Web.UI.Page protected int viewID; // ид. представления protected int refrRate; // частота обновления данных protected string phrases; // локализованные фразы + protected string today; // текущая дата protected string selTimeFromHtml; // HTML-код выбора начального времени protected string selTimeToHtml; // HTML-код выбора конечного времени protected string tableViewHtml; // HTML-код табличного представления @@ -283,6 +284,9 @@ protected void Page_Load(object sender, EventArgs e) Localization.Dictionaries.TryGetValue("Scada.Web.Plugins.Table.WFrmTable.Js", out dict); phrases = WebUtils.DictionaryToJs(dict); + DateTime nowDT = DateTime.Now; + today = string.Format("new Date({0}, {1}, {2})", nowDT.Year, nowDT.Month - 1, nowDT.Day); + selTimeFromHtml = GenerateTimeSelectHtml("selTimeFrom", true, timeFrom); selTimeToHtml = GenerateTimeSelectHtml("selTimeTo", false, timeTo); bool cmdEnabled = userData.WebSettings.CmdEnabled && rights.ControlRight; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.designer.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.designer.cs index 88f7e0b31..71901c7a0 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.designer.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.designer.cs @@ -11,23 +11,5 @@ namespace Scada.Web.Plugins.Table { public partial class WFrmTable { - - /// - /// frmTable control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlForm frmTable; - - /// - /// txtDate control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox txtDate; } } diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/layout.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/layout.css index d0a64a6f2..0d05ad635 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/layout.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/layout.css @@ -59,8 +59,9 @@ body { right: 5px; height: 30px; line-height: 30px; + cursor: pointer; } -#spanDate:hover i { +#spanDate i:hover { color: #00b9eb; } #divDebugTools { diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/layout.less b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/layout.less index 4d927246d..f41aebe69 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/layout.less +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/layout.less @@ -92,9 +92,10 @@ body { right: 5px; height: @toolbar-height; line-height: @toolbar-height; + cursor: pointer; } -#spanDate:hover i { +#spanDate i:hover { color: @tool-btn-hover-fore-color; } diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/layout.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/layout.min.css index a59d7db2d..826dc397a 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/layout.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/layout.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#9ca1a6;font-size:14px;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;line-height:30px;vertical-align:top;}#divToolbar .tool-btn:hover{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;}#spanDate:hover i{color:#00b9eb;}#divDebugTools{padding:0 10px;display:none;font-size:12px;vertical-align:top;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border:1px solid #888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#9ca1a6;font-size:14px;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;line-height:30px;vertical-align:top;}#divToolbar .tool-btn:hover{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;cursor:pointer;}#spanDate i:hover{color:#00b9eb;}#divDebugTools{padding:0 10px;display:none;font-size:12px;vertical-align:top;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border:1px solid #888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css index 436d81de0..00d6d6804 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css @@ -59,8 +59,9 @@ body { right: 5px; height: 30px; line-height: 30px; + cursor: pointer; } -#spanDate:hover i { +#spanDate i:hover { color: #00b9eb; } #divDebugTools { diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css index ba18b3246..606260b88 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#9ca1a6;font-size:14px;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;line-height:30px;vertical-align:top;}#divToolbar .tool-btn:hover{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;}#spanDate:hover i{color:#00b9eb;}#divDebugTools{padding:0 10px;display:none;font-size:12px;vertical-align:top;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border:1px solid #888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divTblWrapper td.cap{padding:2px 5px;}#divTblWrapper td.cap img{width:16px;height:16px;margin:0 5px 0 0;border:none;cursor:pointer;}#divTblWrapper td.cap a{height:18px;padding-right:5px;color:#0073aa;display:inline-block;line-height:18px;text-decoration:none;}#divTblWrapper td.cap a:hover{color:#00a0d2;}#divTblWrapper td.cap span.cmd{display:inline-block;width:18px;height:18px;cursor:pointer;color:#a00;font-size:15px;line-height:18px;text-align:center;}#divTblWrapper td.cap span.cmd:hover{color:#f00;}#divTblWrapper td.cap span.cmd::before{content:"";font-family:FontAwesome;}#divTblWrapper td.cap span.hint{background-color:#fff;border:1px solid #888;border-radius:5px;box-shadow:2px 2px 5px #aaa;color:#444;display:none;line-height:normal;padding:5px;position:fixed;}#divTblWrapper td.cap span.hint.visible{display:inline-block;}#divTblWrapper td.cur,#divTblWrapper td.hour{text-align:center;}#divTblWrapper td.hidden{display:none;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#9ca1a6;font-size:14px;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;line-height:30px;vertical-align:top;}#divToolbar .tool-btn:hover{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;cursor:pointer;}#spanDate i:hover{color:#00b9eb;}#divDebugTools{padding:0 10px;display:none;font-size:12px;vertical-align:top;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border:1px solid #888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divTblWrapper td.cap{padding:2px 5px;}#divTblWrapper td.cap img{width:16px;height:16px;margin:0 5px 0 0;border:none;cursor:pointer;}#divTblWrapper td.cap a{height:18px;padding-right:5px;color:#0073aa;display:inline-block;line-height:18px;text-decoration:none;}#divTblWrapper td.cap a:hover{color:#00a0d2;}#divTblWrapper td.cap span.cmd{display:inline-block;width:18px;height:18px;cursor:pointer;color:#a00;font-size:15px;line-height:18px;text-align:center;}#divTblWrapper td.cap span.cmd:hover{color:#f00;}#divTblWrapper td.cap span.cmd::before{content:"";font-family:FontAwesome;}#divTblWrapper td.cap span.hint{background-color:#fff;border:1px solid #888;border-radius:5px;box-shadow:2px 2px 5px #aaa;color:#444;display:none;line-height:normal;padding:5px;position:fixed;}#divTblWrapper td.cap span.hint.visible{display:inline-block;}#divTblWrapper td.cur,#divTblWrapper td.hour{text-align:center;}#divTblWrapper td.hidden{display:none;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/layout.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/layout.js index e648f1d41..488c2e3a5 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/layout.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/layout.js @@ -1,4 +1,7 @@ -// The view hub object +// View date format options +var VIEW_DATE_OPTIONS = { year: "numeric", month: "long", day: "2-digit" }; + +// The view hub object var viewHub = scada.viewHubLocator.getViewHub(); // Apply additional css styles in case of using iOS diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js index a834b2e66..45fc064f8 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js @@ -1,25 +1,56 @@ -// Notifier control -var notifier = null; -// View ID. Must be defined in Table.aspx +// The variables below must be defined in Table.aspx +// View ID var viewID = viewID || 0; // Current data refresh rate var refrRate = refrRate || 1000; // Localized phrases var phrases = phrases || {}; +// Current server date +var today = today || new Date(); +// Application culture name +var locale = locale || "en-GB"; + +// Notifier control +var notifier = null; +// Current view date +var viewDate = null; // Beginning of the time period -var timeFrom = timeFrom || 0; +var timeFrom = null; // End of the time period -var timeTo = timeTo || 23; +var timeTo = null; -// Set widths of the item links to fill cells -function setItemLinkWidths() { - var cellWidth = $("#divTblWrapper td.cap:first").width(); - $("#divTblWrapper td.cap").each(function () { - var cell = $(this); - cell.children("a.lbl").outerWidth(cellWidth - - cell.children("img.icon").outerWidth(true) - - cell.children("span.cmd").outerWidth(true)); - }); +// Set current view date to the initial value +function initViewDate() { + if (viewHub) { + if (viewHub.currentViewDate) { + setViewDate(viewHub.currentViewDate); + } else { + viewHub.currentViewDate = today; + setViewDate(today); + } + } else { + setViewDate(today); + } +} + +// Parse manually entered view date and apply it +function parseViewDate(dateStr) { + // TODO: ajax request here + var d = new Date(dateStr); + alert(d); +} + +// Set current view date +function setViewDate(date) { + viewDate = date; + $("#txtDate").val(date.toLocaleDateString(locale, VIEW_DATE_OPTIONS)); +} + +// Send view date changed notification to data windows +function sendViewDateNotification(date) { + if (viewHub) { + viewHub.notify(scada.EventTypes.VIEW_DATE_CHANGED, window, date); + } } // Retrieve the time period from the control values @@ -85,6 +116,17 @@ function updateTableViewHours() { }); } +// Set widths of the item links to fill cells +function setItemLinkWidths() { + var cellWidth = $("#divTblWrapper td.cap:first").width(); + $("#divTblWrapper td.cap").each(function () { + var cell = $(this); + cell.children("a.lbl").outerWidth(cellWidth - + cell.children("img.icon").outerWidth(true) - + cell.children("span.cmd").outerWidth(true)); + }); +} + // Show hint associated with the icon function showHint(imgIcon) { var iconOffset = imgIcon.offset(); @@ -118,6 +160,8 @@ $(document).ready(function () { styleIOS(); updateLayout(); setItemLinkWidths(); + initViewDate(); + retrieveTimePeriod(); notifier = new scada.Notifier("#divNotif"); notifier.startClearingNotifications(); @@ -126,6 +170,32 @@ $(document).ready(function () { updateLayout(); }); + // process the view date changing + $(window).on(scada.EventTypes.VIEW_DATE_CHANGED, function (event, sender, extraParams) { + setViewDate(extraParams); + }); + + // show calendar popup on click the calendar icon + $("#spanDate i").click(function (event) { + var dialogs = viewHub ? viewHub.dialogs : null; + if (dialogs) { + var txtDate = $("#txtDate"); + dialogs.showCalendar(txtDate, txtDate.val(), function (dialogResult, extraParams) { + if (dialogResult) { + setViewDate(extraParams.date); + sendViewDateNotification(extraParams.date); + } + }); + } else { + console.warn("Unable to show calendar because dialogs object is undefined"); + } + }); + + // parse manually entered view date + $("#txtDate").change(function () { + parseViewDate($(this).val()); + }); + // process the time period changing $("#selTimeFrom, #selTimeTo").change(function () { retrieveTimePeriod(); @@ -153,16 +223,6 @@ $(document).ready(function () { $(this).css("display", "none"); }); - // TODO - var dialogs = viewHub ? viewHub.dialogs : null; - if (dialogs) { - $("#spanDate *").click(function (event) { - dialogs.showCalendar($("#txtDate"), "09/06/2016", function (dialogResult, extraParams) { - //alert("dialogResult = " + dialogResult + ", extraParams = " + extraParams); - }); - }); - } - if (DEBUG_MODE) { initDebugTools(); } else { diff --git a/ScadaWeb/ScadaWebShell5Beta/css/calendar.css b/ScadaWeb/ScadaWebShell5Beta/css/calendar.css index cc2fc609a..d0ca16763 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/calendar.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/calendar.css @@ -21,11 +21,12 @@ a:active { #frmCalendar .header { background-color: #ccc; font-weight: 600; - height: 25px; } #frmCalendar .header a { font-weight: 600; width: 30px; + height: 25px; + line-height: 25px; text-align: center; } #frmCalendar th.day-header { diff --git a/ScadaWeb/ScadaWebShell5Beta/css/calendar.less b/ScadaWeb/ScadaWebShell5Beta/css/calendar.less index ec01ba4a1..3c608f63f 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/calendar.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/calendar.less @@ -35,12 +35,13 @@ a:active { #frmCalendar .header { background-color: @header-back-color; font-weight: 600; - height: 25px; } #frmCalendar .header a { font-weight: 600; width: 30px; + height: 25px; + line-height: 25px; text-align: center; } diff --git a/ScadaWeb/ScadaWebShell5Beta/css/calendar.min.css b/ScadaWeb/ScadaWebShell5Beta/css/calendar.min.css index b8a7c96cb..e1485f440 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/calendar.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/calendar.min.css @@ -1 +1 @@ -body{margin:0;padding:0;background-color:#fff;color:#444;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;display:inline-block;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#frmCalendar .header{background-color:#ccc;font-weight:600;height:25px;}#frmCalendar .header a{font-weight:600;width:30px;text-align:center;}#frmCalendar th.day-header{background-color:#f9f9f9;font-size:11px;font-weight:600;height:25px;}#frmCalendar td{margin:0;padding:0;vertical-align:middle;}#frmCalendar a{display:inline-block;}#frmCalendar a:hover{color:#00b9eb;}#frmCalendar td.day a{width:30px;height:30px;line-height:30px;text-align:center;}#frmCalendar td.day.other-month a{color:#9ca1a6;}#frmCalendar td.day.today a{background-color:#ccc;}#frmCalendar td.day.selected a{background-color:#0073aa;color:#fff;}#frmCalendar td.day a:hover{color:#00b9eb;}#frmCalendar td.day.regular a:hover,#frmCalendar td.day.other-month a:hover{background-color:#e0e0e0;} \ No newline at end of file +body{margin:0;padding:0;background-color:#fff;color:#444;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;display:inline-block;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#frmCalendar .header{background-color:#ccc;font-weight:600;}#frmCalendar .header a{font-weight:600;width:30px;height:25px;line-height:25px;text-align:center;}#frmCalendar th.day-header{background-color:#f9f9f9;font-size:11px;font-weight:600;height:25px;}#frmCalendar td{margin:0;padding:0;vertical-align:middle;}#frmCalendar a{display:inline-block;}#frmCalendar a:hover{color:#00b9eb;}#frmCalendar td.day a{width:30px;height:30px;line-height:30px;text-align:center;}#frmCalendar td.day.other-month a{color:#9ca1a6;}#frmCalendar td.day.today a{background-color:#ccc;}#frmCalendar td.day.selected a{background-color:#0073aa;color:#fff;}#frmCalendar td.day a:hover{color:#00b9eb;}#frmCalendar td.day.regular a:hover,#frmCalendar td.day.other-month a:hover{background-color:#e0e0e0;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js b/ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js index 9ea3b83e9..576301a50 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js @@ -20,6 +20,9 @@ scada.ViewHub = function (mainWindow) { // Current view ID this.currentViewID = 0; + // Current view date for displaying data + this.currentViewDate = null; + // Main window object that manages a view and data windows this.mainWindow = mainWindow; @@ -57,7 +60,7 @@ scada.ViewHub.prototype.notify = function (eventType, senderWnd, opt_extraParams var handled = false; var senderIsView = senderWnd == this.viewWindow; - // preprocess navigation + // preprocess events if (eventType == scada.EventTypes.VIEW_NAVIGATE) { if (senderIsView) { this.currentViewID = opt_extraParams; @@ -66,6 +69,10 @@ scada.ViewHub.prototype.notify = function (eventType, senderWnd, opt_extraParams } } + if (eventType == scada.EventTypes.VIEW_DATE_CHANGED) { + this.currentViewDate = opt_extraParams; + } + // pass the notification to the main window if (!handled && this.mainWindow && this.mainWindow != senderWnd) { var jq = this.mainWindow.$; From 960bdafa55b8f9969509d5cf60b27b7a01769d92 Mon Sep 17 00:00:00 2001 From: 2mik Date: Tue, 7 Jun 2016 12:52:35 +0300 Subject: [PATCH 096/382] PlgTable: rename few files --- ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj | 16 ++++++++-------- .../OpenPlugins/PlgTable/compilerconfig.json | 8 ++++---- .../PlgTable/plugins/Table/Events.aspx | 4 ++-- .../PlgTable/plugins/Table/Table.aspx | 4 ++-- .../Table/css/{layout.css => tablecommon.css} | 0 .../Table/css/{layout.less => tablecommon.less} | 0 .../css/{layout.min.css => tablecommon.min.css} | 0 .../PlgTable/plugins/Table/js/table.js | 14 +------------- .../Table/js/{layout.js => tablecommon.js} | 12 ++++++++++++ 9 files changed, 29 insertions(+), 29 deletions(-) rename ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/{layout.css => tablecommon.css} (100%) rename ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/{layout.less => tablecommon.less} (100%) rename ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/{layout.min.css => tablecommon.min.css} (100%) rename ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/{layout.js => tablecommon.js} (88%) diff --git a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj index f51a07621..542ee92b6 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj +++ b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj @@ -81,18 +81,18 @@ events.css - - layout.less - - - layout.css - table.less table.css + + tablecommon.less + + + tablecommon.css + @@ -101,7 +101,7 @@ - + @@ -140,7 +140,7 @@ - + diff --git a/ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json b/ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json index 73308e5ce..a646d212e 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json +++ b/ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json @@ -7,12 +7,12 @@ "outputFile": "css/controls/notifier.css", "inputFile": "css/controls/notifier.less" }, - { - "outputFile": "plugins/table/css/layout.css", - "inputFile": "plugins/table/css/layout.less" - }, { "outputFile": "plugins/table/css/events.css", "inputFile": "plugins/table/css/events.less" + }, + { + "outputFile": "plugins/Table/css/tablecommon.css", + "inputFile": "plugins/Table/css/tablecommon.less" } ] \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx index 8f244aa04..699c85be4 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx @@ -8,7 +8,7 @@ - + @@ -16,7 +16,7 @@ - + diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx index b162b61d1..6f4608611 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx @@ -8,7 +8,7 @@ - + @@ -16,7 +16,7 @@ - + diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx index ad98462e6..9e0626d60 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx @@ -8,7 +8,6 @@ - @@ -32,7 +31,7 @@
<%= selTimeFromHtml %> - <%= selTimeToHtml %>
TitleChanged private static readonly string UnableGetViewMsg = Localization.UseRussian ? "Не удалось получить представление из кеша" : "Unable to get view from the cache"; + /// + /// Начало отчёта времени в Unix, которое используется в Javascript реализации даты + /// + private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); /// @@ -223,18 +228,18 @@ private HourCnlDataExt[] GetHourCnlDataExtArr(int year, int month, int day, int cnlCnt = cnlList.Count; List hourCnlDataList = new List(); DateTime nowDT = DateTime.Now; - DateTime reqDate = startDate; + DateTime curDate = startDate; - while (reqDate <= endDate) + while (curDate <= endDate) { - SrezTableLight tblHour = dataCache.GetHourTable(reqDate); - DateTime nextReqDate = reqDate.AddDays(1.0); + SrezTableLight tblHour = dataCache.GetHourTable(curDate); + DateTime nextDate = curDate.AddDays(1.0); if (existing) { // получение всех существующих часовых срезов - DateTime dayStartDT = reqDate > startDate ? reqDate : startDT; - DateTime dayEndDT = reqDate < endDate ? nextReqDate : endDT; + DateTime dayStartDT = curDate > startDate ? curDate : startDT; + DateTime dayEndDT = curDate < endDate ? nextDate : endDT; foreach (SrezTableLight.Srez snapshot in tblHour.SrezList.Values) { @@ -249,19 +254,20 @@ private HourCnlDataExt[] GetHourCnlDataExtArr(int year, int month, int day, else { // заполнение данных по целым часам - int dayStartHour = reqDate > startDate ? 0 : startHour; - int dayEndHour = reqDate < endDate ? 23 : endHour; + int dayStartHour = curDate > startDate ? 0 : startDT.Hour; + int dayEndHour = curDate < endDate ? 23 : endDT.Hour; - for (int hour = dayStartHour; hour <= dayEndHour; hour++) + for (int dayHour = dayStartHour; dayHour <= dayEndHour; dayHour++) { - DateTime snapshotDT = reqDate.AddHours(hour); + DateTime snapshotDT = curDate.AddHours(dayHour); SrezTableLight.Srez snapshot; tblHour.SrezList.TryGetValue(snapshotDT, out snapshot); + double hour = (snapshotDT - date).TotalHours; AppendCnlDataExtArr(hourCnlDataList, hour, cnlList, snapshot, snapshotDT, nowDT); } } - reqDate = nextReqDate; + curDate = nextDate; } return hourCnlDataList.ToArray(); @@ -491,6 +497,8 @@ public string GetHourCnlDataExtByView(int year, int month, int day, /// Получить метку представления из кеша /// /// Возвращает long, упакованный в DataTransferObject, в формате в JSON + [OperationContract] + [WebGet] public string GetViewStamp(int viewID) { try @@ -508,5 +516,37 @@ public string GetViewStamp(int viewID) return GetErrorDtoJs(ex); } } + + /// + /// Преобразовать строку в дату + /// + /// Возвращает long или null упакованный в DataTransferObject, в формате в JSON. + /// Число означает количество миллисекунд для создания даты в Javascript или 0 в случае ошибки + [OperationContract] + [WebGet] + public string ParseDateTime(string s) + { + try + { + AppData.CheckLoggedOn(); + DateTime dateTime; + if (DateTime.TryParse(s, Localization.Culture, DateTimeStyles.None, out dateTime)) + { + long ms = (long)(dateTime - UnixEpoch).TotalMilliseconds; + return JsSerializer.Serialize(new DataTransferObject(ms)); + } + else + { + return JsSerializer.Serialize(new DataTransferObject(null)); + } + } + catch (Exception ex) + { + AppData.Log.WriteException(ex, Localization.UseRussian ? + "Ошибка при преобразовани строки в число" : + "Error parsing date and time"); + return GetErrorDtoJs(ex); + } + } } } \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js b/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js index a6f7042e3..ef3508e91 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js @@ -166,10 +166,14 @@ scada.clientAPI = { // Get the stamp of the view from the cache. // callback is function (success, stamp) getViewStamp: function (viewID, callback) { - this._request( - "ClientApiSvc.svc/GetViewStamp", - "?viewID=" + viewID, - callback, 0); + this._request("ClientApiSvc.svc/GetViewStamp", "?viewID=" + viewID, callback, 0); + }, + + // Parse date and time using the application culture + // callback is function (success, value), + // value is the number of milliseconds or null in case of any error + parseDateTime: function (s, callback) { + this._request("ClientApiSvc.svc/ParseDateTime", "?s=" + s, callback, null); }, // Create map of extended input channel data to access by channel number From 700236ae1b1a4f24718f8e0a9850c2919401b783 Mon Sep 17 00:00:00 2001 From: 2mik Date: Fri, 10 Jun 2016 10:30:06 +0300 Subject: [PATCH 103/382] PlgTable: column headers --- .../OpenPlugins/PlgTable/js/api/clientapi.js | 55 +++++++++++++------ .../PlgTable/plugins/Table/Table.aspx.cs | 42 +++++++------- .../PlgTable/plugins/Table/js/table.js | 39 +++++++++++-- 3 files changed, 91 insertions(+), 45 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgTable/js/api/clientapi.js b/ScadaWeb/OpenPlugins/PlgTable/js/api/clientapi.js index d621f50b7..ef3508e91 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/js/api/clientapi.js +++ b/ScadaWeb/OpenPlugins/PlgTable/js/api/clientapi.js @@ -79,23 +79,26 @@ scada.clientAPI = { } catch (ex) { scada.utils.logServiceFormatError(operation); - callback(false, errorResult); + if (typeof callback === "function") { + callback(false, errorResult); + } } }) .fail(function (jqXHR, textStatus, errorThrown) { scada.utils.logFailedRequest(operation, jqXHR); - callback(false, errorResult); + if (typeof callback === "function") { + callback(false, errorResult); + } }); }, - // Extract year, month and day from the date and join them into query string - _getDateQueryString: function (date, opt_startHour, opt_endHour) { - return - "year=" + date.getFullYear() + + // Extract year, month and day from the date, and join them with the hours into a query string + _getDateTimeQueryString: function (date, startHour, endHour) { + return "year=" + date.getFullYear() + "&month=" + (date.getMonth() + 1) + - "&date=" + date.getDay() + - (opt_startHour ? "&startHour=" + opt_startHour : "") + - (opt_endHour ? "&endHour=" + opt_endHour : ""); + "&day=" + date.getDate() + + "&startHour=" + startHour + + "&endHour=" + endHour; }, // Check that a user is logged on. @@ -147,7 +150,7 @@ scada.clientAPI = { getHourCnlDataExtByCnlNums: function (date, startHour, endHour, cnlNums, mode, callback) { this._request( "ClientApiSvc.svc/GetHourCnlDataExtByCnlNums", - "?" + this._getDateQueryString(date, startHour, endHour) + "&cnlNums=" + cnlNums + "&existing=" + mode, + "?" + this._getDateTimeQueryString(date, startHour, endHour) + "&cnlNums=" + cnlNums + "&existing=" + mode, callback, []); }, @@ -156,17 +159,21 @@ scada.clientAPI = { getHourCnlDataExtByView: function (date, startHour, endHour, viewID, mode, callback) { this._request( "ClientApiSvc.svc/GetHourCnlDataExtByView", - "?" + this._getDateQueryString(date, startHour, endHour) + "&viewID=" + viewID + "&existing=" + mode, + "?" + this._getDateTimeQueryString(date, startHour, endHour) + "&viewID=" + viewID + "&existing=" + mode, callback, []); }, // Get the stamp of the view from the cache. // callback is function (success, stamp) getViewStamp: function (viewID, callback) { - this._request( - "ClientApiSvc.svc/GetViewStamp", - "?viewID=" + viewID, - callback, 0); + this._request("ClientApiSvc.svc/GetViewStamp", "?viewID=" + viewID, callback, 0); + }, + + // Parse date and time using the application culture + // callback is function (success, value), + // value is the number of milliseconds or null in case of any error + parseDateTime: function (s, callback) { + this._request("ClientApiSvc.svc/ParseDateTime", "?s=" + s, callback, null); }, // Create map of extended input channel data to access by channel number @@ -181,7 +188,23 @@ scada.clientAPI = { catch (ex) { console.error(scada.utils.getCurTime() + " Error creating map of extended input channel data:", ex.message); - return null; + return new Map(); + } + }, + + // Create map of extended hourly input channel data to access by hour + createHourCnlDataExtMap: function (hourCnlDataExtArr) { + try { + var map = new Map(); + for (var hourCnlDataExt of hourCnlDataExtArr) { + map.set(hourCnlDataExt.Hour, hourCnlDataExt); + } + return map; + } + catch (ex) { + console.error(scada.utils.getCurTime() + " Error creating map of extended hourly input channel data:", + ex.message); + return new Map(); } } }; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs index 2c0fd195e..6efad60f2 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs @@ -71,42 +71,38 @@ private string GetLocalizedHour(int hour) } /// - /// Генерировать HTML-код выпадающего списка выбора времени + /// Добавить группу элементов в выпадающий список выбора времени /// - private string GenerateTimeSelectHtml(string elemID, bool addPrevDay, int selectedHour) + private void AppendOptGroup(StringBuilder sbHtml, string label, int startHour, int endHour, int selectedHour) { - StringBuilder sbHtml = new StringBuilder(); - sbHtml.Append(""); + + if (addPrevDay) + AppendOptGroup(sbHtml, PlgPhrases.PreviousDay, -24, -1, selectedHour); + AppendOptGroup(sbHtml, PlgPhrases.SelectedDay, 0, 23, selectedHour); sbHtml.AppendLine(""); return sbHtml.ToString(); } /// - /// Добавить ячейку + /// Добавить ячейку в табличное представление /// private void AppendCell(StringBuilder sbHtml, string cssClass, int? hour, string innerHtml) { @@ -122,7 +118,7 @@ private void AppendCell(StringBuilder sbHtml, string cssClass, int? hour, string } /// - /// Добавить текст подсказки + /// Добавить текст подсказки в табличное представление /// private void AppendHint(StringBuilder sbHtml, bool addBreak, string label, int num, string name) { diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js index 36fadae45..3161168cd 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js @@ -1,4 +1,9 @@ -// Notifier control +// Column header date format options +var HEADER_DATE_OPTIONS = { month: "short", day: "2-digit" }; +// Column header time format options +var HEADER_TIME_OPTIONS = { hour: "2-digit", minute: "2-digit" }; + +// Notifier control var notifier = null; // Current view date var viewDate = null; @@ -53,6 +58,7 @@ function setViewDate(date) { viewDate = date; $("#txtDate").val(date.toLocaleDateString(locale, VIEW_DATE_OPTIONS)); $("#spanDate i").removeClass("error"); + updateHourDataColHdrText(); } // Send view date changed notification to data windows @@ -93,7 +99,7 @@ function saveTimePeriod() { scada.utils.setCookie("Table.TimeTo", $("#selTimeTo").val()); } -// Select and prepare current data cells +// Select and prepare the current data cells function initCurDataCells() { // select cells curDataCells = $("#divTblWrapper td.cur"); @@ -104,7 +110,7 @@ function initCurDataCells() { }); } -// Select and prepare hourly data columns +// Select and prepare the hourly data columns function initHourDataCols() { // init columns hourDataCols.length = lastHour - firstHour + 1; @@ -125,8 +131,27 @@ function initHourDataCols() { }); } -// Set visibility of the table view columns according to the time period -function updateTableViewHours() { +// Update header text of the hourly data columns +function updateHourDataColHdrText() { + $("#divTblWrapper tr.hdr td.hour").each(function () { + var cell = $(this); + var hour = cell.data("hour"); + var colDT = new Date(viewDate.getTime()); + colDT.setHours(hour); + + if (timeFrom >= 0) { + // display time only + cell.text(colDT.toLocaleTimeString(locale, HEADER_TIME_OPTIONS)); + } else { + // display date and time + cell.text(colDT.toLocaleDateString(locale, HEADER_DATE_OPTIONS) + " " + + colDT.toLocaleTimeString(locale, HEADER_TIME_OPTIONS)); + } + }); +} + +// Update visibility of the table view columns according to the time period +function updateHourDataColVisibility() { $("#divTblWrapper tr").each(function () { var row = $(this); var hourCells = row.find("td.hour"); @@ -299,6 +324,7 @@ $(document).ready(function () { retrieveTimePeriod(); initCurDataCells(); initHourDataCols(); + updateHourDataColHdrText(); notifier = new scada.Notifier("#divNotif"); notifier.startClearingNotifications(); @@ -346,7 +372,8 @@ $(document).ready(function () { } saveTimePeriod(); - updateTableViewHours(); + updateHourDataColHdrText(); + updateHourDataColVisibility(); restartUpdatingHourData(); }); From 1a4d926cfa9c02988c26d818b4cfbdddc757a2b6 Mon Sep 17 00:00:00 2001 From: 2mik Date: Fri, 10 Jun 2016 11:21:42 +0300 Subject: [PATCH 104/382] PlgTable: format date and time --- ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js index 3161168cd..e5c24f474 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js @@ -1,7 +1,7 @@ -// Column header date format options -var HEADER_DATE_OPTIONS = { month: "short", day: "2-digit" }; -// Column header time format options +// Column header time format options var HEADER_TIME_OPTIONS = { hour: "2-digit", minute: "2-digit" }; +// Column header date and time format options +var HEADER_DATETIME_OPTIONS = { month: "short", day: "2-digit", hour: "2-digit", minute: "2-digit" }; // Notifier control var notifier = null; @@ -144,8 +144,7 @@ function updateHourDataColHdrText() { cell.text(colDT.toLocaleTimeString(locale, HEADER_TIME_OPTIONS)); } else { // display date and time - cell.text(colDT.toLocaleDateString(locale, HEADER_DATE_OPTIONS) + " " + - colDT.toLocaleTimeString(locale, HEADER_TIME_OPTIONS)); + cell.text(colDT.toLocaleString(locale, HEADER_DATETIME_OPTIONS)); } }); } From d59bd6cbb5c68e9560a5730077b18b9a136ad0c2 Mon Sep 17 00:00:00 2001 From: 2mik Date: Fri, 10 Jun 2016 15:15:43 +0300 Subject: [PATCH 105/382] PlgTable: fixed table header --- .../OpenPlugins/PlgScheme/js/api/clientapi.js | 95 +++++++++++++++++-- .../OpenPlugins/PlgTable/ClientApiSvc.svc | 1 + ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj | 9 ++ ScadaWeb/OpenPlugins/PlgTable/Web.config | 65 +++++++++++-- .../OpenPlugins/PlgTable/compilerconfig.json | 4 + .../PlgTable/css/controls/tableheader.css | 12 +++ .../PlgTable/css/controls/tableheader.less | 14 +++ .../PlgTable/css/controls/tableheader.min.css | 1 + .../PlgTable/js/controls/tableheader.js | 69 ++++++++++++++ .../PlgTable/plugins/Table/Table.aspx | 4 +- .../PlgTable/plugins/Table/Table.aspx.cs | 6 +- .../PlgTable/plugins/Table/css/events.css | 3 + .../PlgTable/plugins/Table/css/events.min.css | 2 +- .../PlgTable/plugins/Table/css/table.css | 3 + .../PlgTable/plugins/Table/css/table.min.css | 2 +- .../plugins/Table/css/tablecommon.css | 3 + .../plugins/Table/css/tablecommon.less | 6 +- .../plugins/Table/css/tablecommon.min.css | 2 +- .../PlgTable/plugins/Table/js/table.js | 24 +++-- .../ScadaWebShell5Beta.csproj | 8 ++ .../ScadaWebShell5Beta/compilerconfig.json | 4 + .../css/controls/tableheader.css | 12 +++ .../css/controls/tableheader.less | 14 +++ .../css/controls/tableheader.min.css | 1 + .../js/controls/tableheader.js | 69 ++++++++++++++ 25 files changed, 403 insertions(+), 30 deletions(-) create mode 100644 ScadaWeb/OpenPlugins/PlgTable/ClientApiSvc.svc create mode 100644 ScadaWeb/OpenPlugins/PlgTable/css/controls/tableheader.css create mode 100644 ScadaWeb/OpenPlugins/PlgTable/css/controls/tableheader.less create mode 100644 ScadaWeb/OpenPlugins/PlgTable/css/controls/tableheader.min.css create mode 100644 ScadaWeb/OpenPlugins/PlgTable/js/controls/tableheader.js create mode 100644 ScadaWeb/ScadaWebShell5Beta/css/controls/tableheader.css create mode 100644 ScadaWeb/ScadaWebShell5Beta/css/controls/tableheader.less create mode 100644 ScadaWeb/ScadaWebShell5Beta/css/controls/tableheader.min.css create mode 100644 ScadaWeb/ScadaWebShell5Beta/js/controls/tableheader.js diff --git a/ScadaWeb/OpenPlugins/PlgScheme/js/api/clientapi.js b/ScadaWeb/OpenPlugins/PlgScheme/js/api/clientapi.js index 14cc654ff..ef3508e91 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/js/api/clientapi.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/js/api/clientapi.js @@ -33,6 +33,20 @@ scada.CnlDataExt = function () { scada.CnlDataExt.prototype = Object.create(scada.CnlData.prototype); scada.CnlDataExt.constructor = scada.CnlDataExt; +// Extended hourly input channel data type +scada.HourCnlDataExt = function () { + this.Hour = NaN; + this.CnlDataExtArr = []; +} + +// Getting hour data modes enumeration +scada.HourDataModes = { + // Get data for integer hours even if a snapshot doesn't exist + INTEGER_HOURS: false, + // Get existing snapshots + EXISTING: true +}; + // Client API object scada.clientAPI = { // Empty input channel data @@ -65,15 +79,28 @@ scada.clientAPI = { } catch (ex) { scada.utils.logServiceFormatError(operation); - callback(false, errorResult); + if (typeof callback === "function") { + callback(false, errorResult); + } } }) .fail(function (jqXHR, textStatus, errorThrown) { scada.utils.logFailedRequest(operation, jqXHR); - callback(false, errorResult); + if (typeof callback === "function") { + callback(false, errorResult); + } }); }, + // Extract year, month and day from the date, and join them with the hours into a query string + _getDateTimeQueryString: function (date, startHour, endHour) { + return "year=" + date.getFullYear() + + "&month=" + (date.getMonth() + 1) + + "&day=" + date.getDate() + + "&startHour=" + startHour + + "&endHour=" + endHour; + }, + // Check that a user is logged on. // callback is function (success, loggedOn) checkLoggedOn: function (callback) { @@ -109,7 +136,7 @@ scada.clientAPI = { callback, []); }, - // Get extended current input channel data of the view. + // Get extended current data of the input channels of the specified view. // callback is function (success, cnlDataExtArr) getCurCnlDataExtByView: function (viewID, callback) { this._request( @@ -118,12 +145,66 @@ scada.clientAPI = { callback, []); }, + // Get extended hourly data of the specified input channels. + // callback is function (success, hourCnlDataExtArr) + getHourCnlDataExtByCnlNums: function (date, startHour, endHour, cnlNums, mode, callback) { + this._request( + "ClientApiSvc.svc/GetHourCnlDataExtByCnlNums", + "?" + this._getDateTimeQueryString(date, startHour, endHour) + "&cnlNums=" + cnlNums + "&existing=" + mode, + callback, []); + }, + + // Get extended hourly data of the input channels of the specified view. + // callback is function (success, hourCnlDataExtArr) + getHourCnlDataExtByView: function (date, startHour, endHour, viewID, mode, callback) { + this._request( + "ClientApiSvc.svc/GetHourCnlDataExtByView", + "?" + this._getDateTimeQueryString(date, startHour, endHour) + "&viewID=" + viewID + "&existing=" + mode, + callback, []); + }, + // Get the stamp of the view from the cache. // callback is function (success, stamp) getViewStamp: function (viewID, callback) { - this._request( - "ClientApiSvc.svc/GetViewStamp", - "?viewID=" + viewID, - callback, 0); + this._request("ClientApiSvc.svc/GetViewStamp", "?viewID=" + viewID, callback, 0); + }, + + // Parse date and time using the application culture + // callback is function (success, value), + // value is the number of milliseconds or null in case of any error + parseDateTime: function (s, callback) { + this._request("ClientApiSvc.svc/ParseDateTime", "?s=" + s, callback, null); + }, + + // Create map of extended input channel data to access by channel number + createCnlDataExtMap: function (cnlDataExtArr) { + try { + var map = new Map(); + for (var cnlDataExt of cnlDataExtArr) { + map.set(cnlDataExt.CnlNum, cnlDataExt); + } + return map; + } + catch (ex) { + console.error(scada.utils.getCurTime() + " Error creating map of extended input channel data:", + ex.message); + return new Map(); + } + }, + + // Create map of extended hourly input channel data to access by hour + createHourCnlDataExtMap: function (hourCnlDataExtArr) { + try { + var map = new Map(); + for (var hourCnlDataExt of hourCnlDataExtArr) { + map.set(hourCnlDataExt.Hour, hourCnlDataExt); + } + return map; + } + catch (ex) { + console.error(scada.utils.getCurTime() + " Error creating map of extended hourly input channel data:", + ex.message); + return new Map(); + } } }; diff --git a/ScadaWeb/OpenPlugins/PlgTable/ClientApiSvc.svc b/ScadaWeb/OpenPlugins/PlgTable/ClientApiSvc.svc new file mode 100644 index 000000000..c42acd373 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/ClientApiSvc.svc @@ -0,0 +1 @@ +<%@ ServiceHost Language="C#" Debug="true" Service="Scada.Web.ClientApiSvc" %> diff --git a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj index 542ee92b6..755076b5d 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj +++ b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj @@ -58,6 +58,7 @@ + @@ -66,11 +67,18 @@ notifier.css + + tableheader.less + + + tableheader.css + + @@ -147,6 +155,7 @@ + Web.config diff --git a/ScadaWeb/OpenPlugins/PlgTable/Web.config b/ScadaWeb/OpenPlugins/PlgTable/Web.config index c72873f34..d42573c62 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/Web.config +++ b/ScadaWeb/OpenPlugins/PlgTable/Web.config @@ -1,13 +1,64 @@ - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json b/ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json index a646d212e..8394540d4 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json +++ b/ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json @@ -14,5 +14,9 @@ { "outputFile": "plugins/Table/css/tablecommon.css", "inputFile": "plugins/Table/css/tablecommon.less" + }, + { + "outputFile": "css/controls/tableheader.css", + "inputFile": "css/controls/tableheader.less" } ] \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/css/controls/tableheader.css b/ScadaWeb/OpenPlugins/PlgTable/css/controls/tableheader.css new file mode 100644 index 000000000..c4d5bf55c --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/css/controls/tableheader.css @@ -0,0 +1,12 @@ +.table-wrapper { + position: relative; +} +.table-wrapper tr.fixed-table-header { + position: absolute; + top: 0; + left: 0; +} +.table-wrapper tr:first-child td > span, +.table-wrapper tr.fixed-table-header td > span { + display: block; +} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/css/controls/tableheader.less b/ScadaWeb/OpenPlugins/PlgTable/css/controls/tableheader.less new file mode 100644 index 000000000..0d8794265 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/css/controls/tableheader.less @@ -0,0 +1,14 @@ +.table-wrapper { + position: relative; +} + +.table-wrapper tr.fixed-table-header { + position: absolute; + top: 0; + left: 0; +} + +.table-wrapper tr:first-child td > span, +.table-wrapper tr.fixed-table-header td > span { + display: block; +} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/css/controls/tableheader.min.css b/ScadaWeb/OpenPlugins/PlgTable/css/controls/tableheader.min.css new file mode 100644 index 000000000..ba72ab399 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/css/controls/tableheader.min.css @@ -0,0 +1 @@ +.table-wrapper{position:relative;}.table-wrapper tr.fixed-table-header{position:absolute;top:0;left:0;}.table-wrapper tr:first-child td>span,.table-wrapper tr.fixed-table-header td>span{display:block;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/js/controls/tableheader.js b/ScadaWeb/OpenPlugins/PlgTable/js/controls/tableheader.js new file mode 100644 index 000000000..230042f41 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/js/controls/tableheader.js @@ -0,0 +1,69 @@ +/* + * Fixed table header + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + * + * Requires: + * - jquery + */ + +// Rapid SCADA namespace +var scada = scada || {}; + +// Fixed table headers processing object +scada.tableHeader = { + // Set the fixed header cell widths equal to the original header cell widths + _updateHeaderCellWidths: function (origHeader, fixedHeader) { + var origCells = origHeader.find("td"); + + fixedHeader.find("td").each(function (index) { + // we need spans to set width exactly + var origCell = origCells.eq(index); + + if (origCell.css("display") != "none") { + var origCellSpan = origCell.children("span"); + var cellWidth = origCellSpan.width(); // jQuery may round fractional part + origCellSpan.width(cellWidth); + $(this).find("span").width(cellWidth); + } + }); + }, + + // Create fixed table headers and bind their events + create: function () { + var thisObj = this; + + $(".table-wrapper").each(function () { + var wrapper = $(this); + var table = wrapper.children("table"); + var origHeader = table.find("tr:first"); + var fixedHeader = origHeader.clone(false); + + origHeader.addClass("orig-table-header"); + fixedHeader.addClass("fixed-table-header"); + table.append(fixedHeader); + thisObj._updateHeaderCellWidths(origHeader, fixedHeader); + + wrapper + .off("scroll") + .scroll(function () { + var fixedHeaderTop = -table.position().top; + fixedHeader.css("top", fixedHeaderTop); + }); + }); + }, + + // Update the fixed header cell widths + update: function () { + var thisObj = this; + + $(".table-wrapper").each(function () { + var wrapper = $(this); + var origHeader = $(this).find("tr.orig-table-header:first"); + var fixedHeader = $(this).find("tr.fixed-table-header:first"); + thisObj._updateHeaderCellWidths(origHeader, fixedHeader); + }); + } +} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx index 9e0626d60..2dcc0ecf5 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx @@ -8,6 +8,7 @@ + @@ -15,6 +16,7 @@ + @@ -15,20 +16,53 @@ + +
-
TitleChangedNavigateDateChanged +
All EventsEvents by View
TitleChangedNavigateDateChanged
-
+
+
+ + + + + + + + + + + + + + + + + + +
NumberDate and TimeObjectDeviceChannelDescriptionAck
114/06/2016 10:45:23EnterpriseADAM-6015 Server roomt4_delayedNormal: 21.0 °CNo
diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs index 523a110ae..92e8d49b1 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs @@ -1,4 +1,7 @@ -using System; +using Scada.Data; +using Scada.UI; +using Scada.Web.Shell; +using System; using System.Collections.Generic; using System.Linq; using System.Web; @@ -9,8 +12,47 @@ namespace Scada.Web.Plugins.Table { public partial class WFrmEvents : System.Web.UI.Page { + // Переменные для вывода на веб-страницу + protected bool debugMode = false; // режим отладки + protected int viewID; // ид. представления + protected int dataRefrRate; // частота обновления текущих данных + protected int arcRefrRate; // частота обновления архивных данных + protected string phrases; // локализованные фразы + protected string today; // текущая дата + protected string eventsTableHtml; // HTML-код таблицы событий + protected void Page_Load(object sender, EventArgs e) { + AppData appData = AppData.GetAppData(); + UserData userData = UserData.GetUserData(); + +#if DEBUG + debugMode = true; + userData.LoginForDebug(); +#endif + + // перевод веб-страницы + Translator.TranslatePage(Page, "Scada.Web.Plugins.Table.WFrmEvents"); + + // получение ид. представления из параметров запроса + int.TryParse(Request.QueryString["viewID"], out viewID); + + // проверка прав на просмотр представления + EntityRights rights = userData.LoggedOn ? + userData.UserRights.GetViewRights(viewID) : EntityRights.NoRights; + if (!rights.ViewRight) + Response.Redirect(UrlTemplates.NoView); + + // подготовка данных для вывода на веб-страницу + dataRefrRate = userData.WebSettings.DataRefrRate; + arcRefrRate = userData.WebSettings.ArcRefrRate; + + Localization.Dict dict; + Localization.Dictionaries.TryGetValue("Scada.Web.Plugins.Table.WFrmEvents.Js", out dict); + phrases = WebUtils.DictionaryToJs(dict); + + DateTime nowDT = DateTime.Now; + today = string.Format("new Date({0}, {1}, {2})", nowDT.Year, nowDT.Month - 1, nowDT.Day); } } diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css index 74887cb33..e46afc83a 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css @@ -1,5 +1,4 @@ -/*to pretend splitter thinner*/ -body { +body { margin: 0; padding: 30px 0 0 0; background-color: #f1f1f1; @@ -17,7 +16,7 @@ body { height: 30px; padding: 0 10px; background-color: transparent; - color: #9ca1a6; + color: #444; font-size: 14px; white-space: nowrap; } @@ -44,7 +43,11 @@ body { line-height: 30px; vertical-align: top; } -#divToolbar .tool-btn:hover { +#divToolbar .tool-btn i { + color: #8f8f8f; +} +#divToolbar .tool-btn:hover, +#divToolbar .tool-btn:hover i { color: #00b9eb; } #spanDate { @@ -59,6 +62,7 @@ body { right: 5px; height: 30px; line-height: 30px; + color: #8f8f8f; cursor: pointer; } #spanDate i:hover { @@ -72,10 +76,11 @@ body { #divDebugTools { padding: 0 10px; display: none; - /*inline-block*/ - font-size: 12px; vertical-align: top; } +#divDebugTools.visible { + display: inline-block; +} /* Table */ #divTblWrapper { padding: 0 10px; @@ -119,6 +124,30 @@ body { #divTblWrapper td img { vertical-align: middle; } -#divToolbar { - margin-top: -3px; +#divToolbar .tool-btn.selected { + background-color: #0073aa; + color: white; +} +#tblEvents { + width: 100%; +} +#tblEvents td.num, +#tblEvents td.time, +#tblEvents td.ack { + text-align: center; +} +#tblEvents td.num, +#tblEvents td.ack { + width: 5%; +} +#tblEvents td.time { + width: 10%; +} +#tblEvents td.obj, +#tblEvents td.dev, +#tblEvents td.cnl { + width: 15%; +} +#tblEvents td.descr { + width: 35%; } \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.less b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.less index 39541c88e..1ba5b1b1f 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.less +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.less @@ -1,5 +1,38 @@ @import "tablecommon.less"; -#divToolbar { - margin-top: -@splitter-border-width; -} \ No newline at end of file +@tool-btn-selected-back-color: @menu-item-selected-back-color; +@tool-btn-selected-fore-color: @menu-item-selected-fore-color; + +#divToolbar .tool-btn.selected { + background-color: @tool-btn-selected-back-color; + color: @tool-btn-selected-fore-color; +} + +#tblEvents { + width :100%; +} + +#tblEvents td.num, +#tblEvents td.time, +#tblEvents td.ack { + text-align: center; +} + +#tblEvents td.num, +#tblEvents td.ack { + width: 5%; +} + +#tblEvents td.time { + width: 10%; +} + +#tblEvents td.obj, +#tblEvents td.dev, +#tblEvents td.cnl { + width: 15%; +} + +#tblEvents td.descr { + width: 35%; +} diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css index 1ae97ded7..067d9dd40 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#9ca1a6;font-size:14px;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;line-height:30px;vertical-align:top;}#divToolbar .tool-btn:hover{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;cursor:pointer;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;font-size:12px;vertical-align:top;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border:1px solid #888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divToolbar{margin-top:-3px;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;font-size:14px;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.visible{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border:1px solid #888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divToolbar .tool-btn.selected{background-color:#0073aa;color:#fff;}#tblEvents{width:100%;}#tblEvents td.num,#tblEvents td.time,#tblEvents td.ack{text-align:center;}#tblEvents td.num,#tblEvents td.ack{width:5%;}#tblEvents td.time{width:10%;}#tblEvents td.obj,#tblEvents td.dev,#tblEvents td.cnl{width:15%;}#tblEvents td.descr{width:35%;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css index 9f7476669..ef531e3f6 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css @@ -1,5 +1,4 @@ -/*to pretend splitter thinner*/ -body { +body { margin: 0; padding: 30px 0 0 0; background-color: #f1f1f1; @@ -17,7 +16,7 @@ body { height: 30px; padding: 0 10px; background-color: transparent; - color: #9ca1a6; + color: #444; font-size: 14px; white-space: nowrap; } @@ -44,7 +43,11 @@ body { line-height: 30px; vertical-align: top; } -#divToolbar .tool-btn:hover { +#divToolbar .tool-btn i { + color: #8f8f8f; +} +#divToolbar .tool-btn:hover, +#divToolbar .tool-btn:hover i { color: #00b9eb; } #spanDate { @@ -59,6 +62,7 @@ body { right: 5px; height: 30px; line-height: 30px; + color: #8f8f8f; cursor: pointer; } #spanDate i:hover { @@ -72,10 +76,11 @@ body { #divDebugTools { padding: 0 10px; display: none; - /*inline-block*/ - font-size: 12px; vertical-align: top; } +#divDebugTools.visible { + display: inline-block; +} /* Table */ #divTblWrapper { padding: 0 10px; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css index e0edd76a1..f10bf416f 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#9ca1a6;font-size:14px;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;line-height:30px;vertical-align:top;}#divToolbar .tool-btn:hover{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;cursor:pointer;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;font-size:12px;vertical-align:top;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border:1px solid #888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divTblWrapper td.cap{padding:2px 5px;}#divTblWrapper td.cap img{width:16px;height:16px;margin:0 5px 0 0;border:none;}#divTblWrapper td.cap a{height:18px;padding-right:5px;color:#0073aa;display:inline-block;line-height:18px;text-decoration:none;}#divTblWrapper td.cap a:hover{color:#00a0d2;}#divTblWrapper td.cap span.cmd{display:inline-block;width:18px;height:18px;cursor:pointer;color:#a00;font-size:15px;line-height:18px;text-align:center;}#divTblWrapper td.cap span.cmd:hover{color:#f00;}#divTblWrapper td.cap span.cmd::before{content:"";font-family:FontAwesome;}#divTblWrapper td.cap span.hint{background-color:#fff;border:1px solid #888;border-radius:5px;box-shadow:2px 2px 5px #aaa;color:#444;display:none;line-height:normal;padding:5px;position:fixed;z-index:10;}#divTblWrapper td.cap span.hint.visible{display:inline-block;}#divTblWrapper td.cur,#divTblWrapper td.hour{text-align:center;}#divTblWrapper td.hidden{display:none;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;font-size:14px;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.visible{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border:1px solid #888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divTblWrapper td.cap{padding:2px 5px;}#divTblWrapper td.cap img{width:16px;height:16px;margin:0 5px 0 0;border:none;}#divTblWrapper td.cap a{height:18px;padding-right:5px;color:#0073aa;display:inline-block;line-height:18px;text-decoration:none;}#divTblWrapper td.cap a:hover{color:#00a0d2;}#divTblWrapper td.cap span.cmd{display:inline-block;width:18px;height:18px;cursor:pointer;color:#a00;font-size:15px;line-height:18px;text-align:center;}#divTblWrapper td.cap span.cmd:hover{color:#f00;}#divTblWrapper td.cap span.cmd::before{content:"";font-family:FontAwesome;}#divTblWrapper td.cap span.hint{background-color:#fff;border:1px solid #888;border-radius:5px;box-shadow:2px 2px 5px #aaa;color:#444;display:none;line-height:normal;padding:5px;position:fixed;z-index:10;}#divTblWrapper td.cap span.hint.visible{display:inline-block;}#divTblWrapper td.cur,#divTblWrapper td.hour{text-align:center;}#divTblWrapper td.hidden{display:none;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css index 3425cf0cd..a2dc70894 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css @@ -1,5 +1,4 @@ -/*to pretend splitter thinner*/ -body { +body { margin: 0; padding: 30px 0 0 0; background-color: #f1f1f1; @@ -17,7 +16,7 @@ body { height: 30px; padding: 0 10px; background-color: transparent; - color: #9ca1a6; + color: #444; font-size: 14px; white-space: nowrap; } @@ -44,7 +43,11 @@ body { line-height: 30px; vertical-align: top; } -#divToolbar .tool-btn:hover { +#divToolbar .tool-btn i { + color: #8f8f8f; +} +#divToolbar .tool-btn:hover, +#divToolbar .tool-btn:hover i { color: #00b9eb; } #spanDate { @@ -59,6 +62,7 @@ body { right: 5px; height: 30px; line-height: 30px; + color: #8f8f8f; cursor: pointer; } #spanDate i:hover { @@ -72,10 +76,11 @@ body { #divDebugTools { padding: 0 10px; display: none; - /*inline-block*/ - font-size: 12px; vertical-align: top; } +#divDebugTools.visible { + display: inline-block; +} /* Table */ #divTblWrapper { padding: 0 10px; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less index 2a67dc9c5..7e31f3eb1 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less @@ -1,10 +1,10 @@ @import "../../../css/globalvar.less"; -@import "../../../css/shellvar.less"; @table-fore-color: black; @notif-back-color: white; @toolbar-back-color: transparent; -@toolbar-fore-color: @menu-fore-color-dark; +@toolbar-fore-color: @menu-fore-color-air; +@toolbar-fore-color-light: @menu-fore-color-air-light; @tool-btn-back-color: white; @tool-btn-fore-color-err: #a00; @tool-btn-hover-fore-color: @menu-item-hover-fore-color; @@ -75,7 +75,12 @@ body { vertical-align: top; } -#divToolbar .tool-btn:hover { +#divToolbar .tool-btn i { + color: @toolbar-fore-color-light; +} + +#divToolbar .tool-btn:hover, +#divToolbar .tool-btn:hover i { color: @tool-btn-hover-fore-color; } @@ -93,6 +98,7 @@ body { right: 5px; height: @toolbar-height; line-height: @toolbar-height; + color: @toolbar-fore-color-light; cursor: pointer; } @@ -107,11 +113,14 @@ body { #divDebugTools { padding: 0 10px; - display: none; /*inline-block*/ - font-size: 12px; + display: none; vertical-align: top; } +#divDebugTools.visible { + display: inline-block; +} + /* Table */ #divTblWrapper { diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css index cd636d98d..1dbc1db7e 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#9ca1a6;font-size:14px;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;line-height:30px;vertical-align:top;}#divToolbar .tool-btn:hover{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;cursor:pointer;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;font-size:12px;vertical-align:top;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border:1px solid #888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;font-size:14px;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.visible{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border:1px solid #888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js index faf95b76c..255325620 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js @@ -1,16 +1,45 @@ -$(document).ready(function () { +// Set current view date and process the consequent changes +function changeViewDate(date, notify) { + setViewDate(date); + //updateHourDataColHdrText(); + //restartUpdatingHourData(); + + if (notify) { + sendViewDateNotification(date); + } +} + +$(document).ready(function () { + scada.clientAPI.rootPath = "../../"; styleIOS(); updateLayout(); + initViewDate(); notifier = new scada.Notifier("#divNotif"); notifier.startClearingNotifications(); if (DEBUG_MODE) { initDebugTools(); - } else { - // TODO: start updating } $(window).on("resize " + scada.EventTypes.UPDATE_LAYOUT, function () { updateLayout(); }); + + // process the view date changing + $(window).on(scada.EventTypes.VIEW_DATE_CHANGED, function (event, sender, extraParams) { + changeViewDate(extraParams, false); + }); + + // select view date on click the calendar icon + $("#spanDate i").click(function (event) { + selectViewDate(changeViewDate); + }); + + // parse manually entered view date + $("#txtDate").change(function () { + parseViewDate($(this).val(), changeViewDate); + }); + + // temp + $("#spanEventsByViewBtn").addClass("selected"); }); diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js index 566adaf91..13b87d527 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js @@ -25,46 +25,14 @@ var hourDataCols = []; // Timeout ID of the hourly data updating timer var updateHourDataTimeoutID = null; -// Set current view date to the initial value -function initViewDate() { - if (viewHub) { - if (viewHub.currentViewDate) { - setViewDate(viewHub.currentViewDate); - } else { - viewHub.currentViewDate = today; - setViewDate(today); - } - } else { - setViewDate(today); - } -} - -// Parse manually entered view date and apply it -function parseViewDate(dateStr) { - scada.clientAPI.parseDateTime(dateStr, function (success, value) { - if (Number.isInteger(value)) { - var parsedDate = new Date(value); - setViewDate(parsedDate); - sendViewDateNotification(parsedDate); - restartUpdatingHourData(); - } else { - $("#spanDate i").addClass("error"); - } - }); -} - -// Set current view date -function setViewDate(date) { - viewDate = date; - $("#txtDate").val(date.toLocaleDateString(locale, VIEW_DATE_OPTIONS)); - $("#spanDate i").removeClass("error"); +// Set current view date and process the consequent changes +function changeViewDate(date, notify) { + setViewDate(date); updateHourDataColHdrText(); -} + restartUpdatingHourData(); -// Send view date changed notification to data windows -function sendViewDateNotification(date) { - if (viewHub) { - viewHub.notify(scada.EventTypes.VIEW_DATE_CHANGED, window, date); + if (notify) { + sendViewDateNotification(date); } } @@ -348,30 +316,17 @@ $(document).ready(function () { // process the view date changing $(window).on(scada.EventTypes.VIEW_DATE_CHANGED, function (event, sender, extraParams) { - setViewDate(extraParams); - restartUpdatingHourData(); + changeViewDate(extraParams, false); }); - // show calendar popup on click the calendar icon + // select view date on click the calendar icon $("#spanDate i").click(function (event) { - var dialogs = viewHub ? viewHub.dialogs : null; - if (dialogs) { - var txtDate = $("#txtDate"); - dialogs.showCalendar(txtDate, txtDate.val(), function (dialogResult, extraParams) { - if (dialogResult) { - setViewDate(extraParams.date); - sendViewDateNotification(extraParams.date); - restartUpdatingHourData(); - } - }); - } else { - console.warn("Unable to show calendar because dialogs object is undefined"); - } + selectViewDate(changeViewDate); }); // parse manually entered view date $("#txtDate").change(function () { - parseViewDate($(this).val()); + parseViewDate($(this).val(), changeViewDate); }); // process the time period changing diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/tablecommon.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/tablecommon.js index 00a4122d7..6d13d2177 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/tablecommon.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/tablecommon.js @@ -47,9 +47,67 @@ function updateLayout() { divToolbar.css("top", notifHeight); } +// Set current view date to the initial value +function initViewDate() { + if (viewHub) { + if (viewHub.currentViewDate) { + setViewDate(viewHub.currentViewDate); + } else { + viewHub.currentViewDate = today; + setViewDate(today); + } + } else { + setViewDate(today); + } +} + +// Set current view date +function setViewDate(date) { + viewDate = date; + $("#txtDate").val(date.toLocaleDateString(locale, VIEW_DATE_OPTIONS)); + $("#spanDate i").removeClass("error"); +} + +// Send view date changed notification to data windows +function sendViewDateNotification(date) { + if (viewHub) { + viewHub.notify(scada.EventTypes.VIEW_DATE_CHANGED, window, date); + } +} + +// Select view date using a calendar popup. +// changeViewDateFunc is function (date, notify) +function selectViewDate(changeViewDateFunc) { + var dialogs = viewHub ? viewHub.dialogs : null; + if (dialogs) { + var txtDate = $("#txtDate"); + dialogs.showCalendar(txtDate, txtDate.val(), function (dialogResult, extraParams) { + if (dialogResult) { + // copy the date to avoid toLocaleDateString() bug in Edge + var date = new Date(extraParams.date.getTime()); + changeViewDateFunc(date, true); + } + }); + } else { + console.warn("Unable to show calendar because dialogs object is undefined"); + } +} + +// Parse manually entered view date and apply it +function parseViewDate(dateStr, changeViewDateFunc) { + scada.clientAPI.parseDateTime(dateStr, function (success, value) { + if (Number.isInteger(value)) { + var parsedDate = new Date(value); + changeViewDateFunc(parsedDate, true); + } else { + $("#spanDate i").addClass("error"); + } + }); +} + // Initialize debug tools function initDebugTools() { - $("#divDebugTools").css("display", "inline-block"); + $("#divDebugTools").addClass("visible"); $(window).on( scada.EventTypes.VIEW_TITLE_CHANGED + " " + diff --git a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj index f8fb9f7f8..75457badb 100644 --- a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj +++ b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj @@ -258,7 +258,6 @@ - diff --git a/ScadaWeb/ScadaWebShell5Beta/css/calendar.css b/ScadaWeb/ScadaWebShell5Beta/css/calendar.css index d0ca16763..d52fd79f6 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/calendar.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/calendar.css @@ -1,5 +1,4 @@ -/*to pretend splitter thinner*/ -body { +body { margin: 0; padding: 0; background-color: white; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/calendar.less b/ScadaWeb/ScadaWebShell5Beta/css/calendar.less index 3c608f63f..09e11ae1b 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/calendar.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/calendar.less @@ -1,5 +1,4 @@ @import "globalvar.less"; -@import "shellvar.less"; @calendar-back-color: white; @link-hover-fore-color: @menu-item-hover-fore-color; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less b/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less index 2212f0a9f..335f490e4 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less @@ -2,4 +2,16 @@ @content-fore-color: #444; @panel-border-color: #e5e5e5; @default-font-family: 'Open Sans', sans-serif; -@default-font-size: 12px; \ No newline at end of file +@default-font-size: 12px; + +@menu-back-color: #23282d; +@menu-back-color-air: white; +@menu-fore-color: #eee; +@menu-fore-color-dark: #9ca1a6; +@menu-fore-color-air: #444; +@menu-fore-color-air-light: #8f8f8f; +@menu-item-hover-back-color: #32373c; +@menu-item-hover-back-color-dark: #191e23; +@menu-item-hover-fore-color: #00b9eb; +@menu-item-selected-back-color: #0073aa; +@menu-item-selected-fore-color: white; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css index e412a5e7c..138bd583d 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css @@ -1,5 +1,4 @@ -/*to pretend splitter thinner*/ -body { +body { margin: 0; padding: 30px 0 0 250px; background-color: #f1f1f1; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less index 387745e56..a53b72a19 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less @@ -1,5 +1,4 @@ @import "globalvar.less"; -@import "shellvar.less"; @header-heigth: 30px; @left-tabs-heigth: 30px; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/shellvar.less b/ScadaWeb/ScadaWebShell5Beta/css/shellvar.less deleted file mode 100644 index 519987a22..000000000 --- a/ScadaWeb/ScadaWebShell5Beta/css/shellvar.less +++ /dev/null @@ -1,12 +0,0 @@ -@menu-back-color: #23282d; -@menu-back-color-air: white; -@menu-fore-color: #eee; -@menu-fore-color-dark: #9ca1a6; -@menu-fore-color-air: #444; -@menu-fore-color-air-light: #8f8f8f; -@menu-item-hover-back-color: #32373c; -@menu-item-hover-back-color-dark: #191e23; -@menu-item-hover-fore-color: #00b9eb; -@menu-item-selected-back-color: #0073aa; -@menu-item-selected-fore-color: white; -@splitter-border-width: 3px; /*to pretend splitter thinner*/ \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/view.css b/ScadaWeb/ScadaWebShell5Beta/css/view.css index 9233cc85f..157090b31 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/view.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/view.css @@ -1,5 +1,4 @@ -/*to pretend splitter thinner*/ -#divViewContent { +#divViewContent { height: 100%; overflow: hidden; } @@ -16,9 +15,8 @@ display: block; } #divViewSplitter { - height: 10px; + height: 9px; background-color: #ddd; - border-bottom: 3px solid #f1f1f1; cursor: ns-resize; display: none; /*block*/ diff --git a/ScadaWeb/ScadaWebShell5Beta/css/view.less b/ScadaWeb/ScadaWebShell5Beta/css/view.less index 1809b6d79..d253de19d 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/view.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/view.less @@ -1,5 +1,4 @@ @import "globalvar.less"; -@import "shellvar.less"; @bottom-tabs-heigth: 30px; @collapse-btn-width: 40px; @@ -25,9 +24,8 @@ } #divViewSplitter { - height: 10px; + height: 9px; background-color: @splitter-color; - border-bottom: @splitter-border-width solid @content-back-color; cursor: ns-resize; display: none; /*block*/ } diff --git a/ScadaWeb/ScadaWebShell5Beta/css/view.min.css b/ScadaWeb/ScadaWebShell5Beta/css/view.min.css index ecd5d147e..e8a6234dc 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/view.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/view.min.css @@ -1 +1 @@ -#divViewContent{height:100%;overflow:hidden;}#divView{min-height:50px;overflow:hidden;}#divViewContent iframe{width:100%;height:100%;margin:0;padding:0;border:none;display:block;}#divViewSplitter{height:10px;background-color:#ddd;border-bottom:3px solid #f1f1f1;cursor:ns-resize;display:none;}#divDataWindow{height:200px;min-height:50px;background-color:#fff;border-bottom:1px solid #e5e5e5;display:none;overflow:hidden;}#divBottomTabs{background-color:#fff;color:#444;}#divBottomTabsContainer{max-width:calc(100% - 40px);padding:0 5px;display:inline-block;}#divBottomTabsContainer .tab{height:30px;margin:0 5px 0 0;padding:0 10px;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;white-space:nowrap;}#divBottomTabsContainer .tab.selected{font-weight:600;}#divCollapseDataWindowBtn{width:40px;height:100%;color:#8f8f8f;cursor:pointer;display:none;font-size:19px;text-align:center;vertical-align:top;}#divCollapseDataWindowBtn i{position:relative;top:2px;}#divBottomTabsContainer .tab:hover,#divCollapseDataWindowBtn:hover{color:#00b9eb;} \ No newline at end of file +#divViewContent{height:100%;overflow:hidden;}#divView{min-height:50px;overflow:hidden;}#divViewContent iframe{width:100%;height:100%;margin:0;padding:0;border:none;display:block;}#divViewSplitter{height:9px;background-color:#ddd;cursor:ns-resize;display:none;}#divDataWindow{height:200px;min-height:50px;background-color:#fff;border-bottom:1px solid #e5e5e5;display:none;overflow:hidden;}#divBottomTabs{background-color:#fff;color:#444;}#divBottomTabsContainer{max-width:calc(100% - 40px);padding:0 5px;display:inline-block;}#divBottomTabsContainer .tab{height:30px;margin:0 5px 0 0;padding:0 10px;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;white-space:nowrap;}#divBottomTabsContainer .tab.selected{font-weight:600;}#divCollapseDataWindowBtn{width:40px;height:100%;color:#8f8f8f;cursor:pointer;display:none;font-size:19px;text-align:center;vertical-align:top;}#divCollapseDataWindowBtn i{position:relative;top:2px;}#divBottomTabsContainer .tab:hover,#divCollapseDataWindowBtn:hover{color:#00b9eb;} \ No newline at end of file From 0140ec19962a24428ad13da0540ec9d5a0c6f050 Mon Sep 17 00:00:00 2001 From: 2mik Date: Tue, 14 Jun 2016 14:39:47 +0300 Subject: [PATCH 111/382] PlgTable: dev --- .../OpenPlugins/PlgTable/css/globalvar.less | 2 + .../PlgTable/plugins/Table/Events.aspx | 2 +- .../PlgTable/plugins/Table/Events.aspx.cs | 5 ++- .../plugins/Table/Events.aspx.designer.cs | 9 ++++ .../PlgTable/plugins/Table/css/events.css | 8 +++- .../PlgTable/plugins/Table/css/events.less | 6 +++ .../PlgTable/plugins/Table/css/events.min.css | 2 +- .../PlgTable/plugins/Table/css/table.css | 4 +- .../PlgTable/plugins/Table/css/table.min.css | 2 +- .../plugins/Table/css/tablecommon.css | 4 +- .../plugins/Table/css/tablecommon.less | 13 ++++-- .../plugins/Table/css/tablecommon.min.css | 2 +- .../PlgTable/plugins/Table/js/events.js | 17 +++++++- .../PlgTable/plugins/Table/js/table.js | 6 +-- ScadaWeb/ScadaWebCommon5Beta/UserRights.cs | 42 ++++++++++--------- .../ScadaWebShell5Beta/css/globalvar.less | 2 + 16 files changed, 89 insertions(+), 37 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgTable/css/globalvar.less b/ScadaWeb/OpenPlugins/PlgTable/css/globalvar.less index 335f490e4..1be849052 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/css/globalvar.less +++ b/ScadaWeb/OpenPlugins/PlgTable/css/globalvar.less @@ -15,3 +15,5 @@ @menu-item-hover-fore-color: #00b9eb; @menu-item-selected-back-color: #0073aa; @menu-item-selected-fore-color: white; +@menu-item-disabled-fore-color: @menu-fore-color-dark; +@menu-item-disabled-fore-color-air: #ccc; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx index a6b242329..251ddaeb7 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx @@ -34,7 +34,7 @@
All EventsAll EventsEvents by View
TitleChanged protected global::System.Web.UI.WebControls.TextBox txtDate; + + /// + /// spanAllEventsBtn control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlGenericControl spanAllEventsBtn; } } diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css index e46afc83a..e8afcb0e8 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css @@ -17,7 +17,6 @@ padding: 0 10px; background-color: transparent; color: #444; - font-size: 14px; white-space: nowrap; } #divToolbar .tool-ctrl { @@ -40,11 +39,13 @@ background-color: white; cursor: pointer; display: inline-block; + font-size: 13px; line-height: 30px; vertical-align: top; } #divToolbar .tool-btn i { color: #8f8f8f; + font-size: 14px; } #divToolbar .tool-btn:hover, #divToolbar .tool-btn:hover i { @@ -64,6 +65,7 @@ line-height: 30px; color: #8f8f8f; cursor: pointer; + font-size: 14px; } #spanDate i:hover { color: #00b9eb; @@ -128,6 +130,10 @@ background-color: #0073aa; color: white; } +#divToolbar .tool-btn.disabled { + color: #e5e5e5; + cursor: default; +} #tblEvents { width: 100%; } diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.less b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.less index 1ba5b1b1f..be866aa24 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.less +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.less @@ -2,12 +2,18 @@ @tool-btn-selected-back-color: @menu-item-selected-back-color; @tool-btn-selected-fore-color: @menu-item-selected-fore-color; +@tool-btn-disabled-fore-color: @menu-item-disabled-fore-color-air; #divToolbar .tool-btn.selected { background-color: @tool-btn-selected-back-color; color: @tool-btn-selected-fore-color; } +#divToolbar .tool-btn.disabled { + color: @tool-btn-disabled-fore-color; + cursor: default; +} + #tblEvents { width :100%; } diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css index 067d9dd40..c57bebdc9 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;font-size:14px;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.visible{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border:1px solid #888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divToolbar .tool-btn.selected{background-color:#0073aa;color:#fff;}#tblEvents{width:100%;}#tblEvents td.num,#tblEvents td.time,#tblEvents td.ack{text-align:center;}#tblEvents td.num,#tblEvents td.ack{width:5%;}#tblEvents td.time{width:10%;}#tblEvents td.obj,#tblEvents td.dev,#tblEvents td.cnl{width:15%;}#tblEvents td.descr{width:35%;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.visible{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border:1px solid #888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divToolbar .tool-btn.selected{background-color:#0073aa;color:#fff;}#divToolbar .tool-btn.disabled{color:#e5e5e5;cursor:default;}#tblEvents{width:100%;}#tblEvents td.num,#tblEvents td.time,#tblEvents td.ack{text-align:center;}#tblEvents td.num,#tblEvents td.ack{width:5%;}#tblEvents td.time{width:10%;}#tblEvents td.obj,#tblEvents td.dev,#tblEvents td.cnl{width:15%;}#tblEvents td.descr{width:35%;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css index ef531e3f6..41302a4af 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css @@ -17,7 +17,6 @@ padding: 0 10px; background-color: transparent; color: #444; - font-size: 14px; white-space: nowrap; } #divToolbar .tool-ctrl { @@ -40,11 +39,13 @@ background-color: white; cursor: pointer; display: inline-block; + font-size: 13px; line-height: 30px; vertical-align: top; } #divToolbar .tool-btn i { color: #8f8f8f; + font-size: 14px; } #divToolbar .tool-btn:hover, #divToolbar .tool-btn:hover i { @@ -64,6 +65,7 @@ line-height: 30px; color: #8f8f8f; cursor: pointer; + font-size: 14px; } #spanDate i:hover { color: #00b9eb; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css index f10bf416f..0221276d4 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;font-size:14px;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.visible{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border:1px solid #888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divTblWrapper td.cap{padding:2px 5px;}#divTblWrapper td.cap img{width:16px;height:16px;margin:0 5px 0 0;border:none;}#divTblWrapper td.cap a{height:18px;padding-right:5px;color:#0073aa;display:inline-block;line-height:18px;text-decoration:none;}#divTblWrapper td.cap a:hover{color:#00a0d2;}#divTblWrapper td.cap span.cmd{display:inline-block;width:18px;height:18px;cursor:pointer;color:#a00;font-size:15px;line-height:18px;text-align:center;}#divTblWrapper td.cap span.cmd:hover{color:#f00;}#divTblWrapper td.cap span.cmd::before{content:"";font-family:FontAwesome;}#divTblWrapper td.cap span.hint{background-color:#fff;border:1px solid #888;border-radius:5px;box-shadow:2px 2px 5px #aaa;color:#444;display:none;line-height:normal;padding:5px;position:fixed;z-index:10;}#divTblWrapper td.cap span.hint.visible{display:inline-block;}#divTblWrapper td.cur,#divTblWrapper td.hour{text-align:center;}#divTblWrapper td.hidden{display:none;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.visible{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border:1px solid #888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divTblWrapper td.cap{padding:2px 5px;}#divTblWrapper td.cap img{width:16px;height:16px;margin:0 5px 0 0;border:none;}#divTblWrapper td.cap a{height:18px;padding-right:5px;color:#0073aa;display:inline-block;line-height:18px;text-decoration:none;}#divTblWrapper td.cap a:hover{color:#00a0d2;}#divTblWrapper td.cap span.cmd{display:inline-block;width:18px;height:18px;cursor:pointer;color:#a00;font-size:15px;line-height:18px;text-align:center;}#divTblWrapper td.cap span.cmd:hover{color:#f00;}#divTblWrapper td.cap span.cmd::before{content:"";font-family:FontAwesome;}#divTblWrapper td.cap span.hint{background-color:#fff;border:1px solid #888;border-radius:5px;box-shadow:2px 2px 5px #aaa;color:#444;display:none;line-height:normal;padding:5px;position:fixed;z-index:10;}#divTblWrapper td.cap span.hint.visible{display:inline-block;}#divTblWrapper td.cur,#divTblWrapper td.hour{text-align:center;}#divTblWrapper td.hidden{display:none;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css index a2dc70894..ba035858a 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css @@ -17,7 +17,6 @@ padding: 0 10px; background-color: transparent; color: #444; - font-size: 14px; white-space: nowrap; } #divToolbar .tool-ctrl { @@ -40,11 +39,13 @@ background-color: white; cursor: pointer; display: inline-block; + font-size: 13px; line-height: 30px; vertical-align: top; } #divToolbar .tool-btn i { color: #8f8f8f; + font-size: 14px; } #divToolbar .tool-btn:hover, #divToolbar .tool-btn:hover i { @@ -64,6 +65,7 @@ line-height: 30px; color: #8f8f8f; cursor: pointer; + font-size: 14px; } #spanDate i:hover { color: #00b9eb; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less index 7e31f3eb1..77d072581 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less @@ -8,9 +8,12 @@ @tool-btn-back-color: white; @tool-btn-fore-color-err: #a00; @tool-btn-hover-fore-color: @menu-item-hover-fore-color; -@toolbar-ctrl-font-family: Arial, Helvetica, sans-serif; @toolbar-height: 30px; @toolbar-input-height: 22px; +@tool-ctrl-font-family: Arial, Helvetica, sans-serif; +@tool-btn-font-size: 13px; +@tool-ctrl-font-size: 14px; +@tool-img-btn-font-size: 14px; @row-back-color: white; @row-back-color-alt: #f9f9f9; @@ -45,7 +48,6 @@ body { padding: 0 10px; background-color: @toolbar-back-color; color: @toolbar-fore-color; - font-size: 14px; white-space: nowrap; } @@ -59,8 +61,8 @@ body { #divToolbar .tool-ctrl select { margin-top: (@toolbar-height - @toolbar-input-height) / 2; box-sizing: border-box; - font-family: @toolbar-ctrl-font-family; - font-size: 14px; + font-family: @tool-ctrl-font-family; + font-size: @tool-ctrl-font-size; height: @toolbar-input-height; } @@ -71,12 +73,14 @@ body { background-color: @tool-btn-back-color; cursor: pointer; display: inline-block; + font-size: @tool-btn-font-size; line-height: @toolbar-height; vertical-align: top; } #divToolbar .tool-btn i { color: @toolbar-fore-color-light; + font-size: @tool-img-btn-font-size; } #divToolbar .tool-btn:hover, @@ -100,6 +104,7 @@ body { line-height: @toolbar-height; color: @toolbar-fore-color-light; cursor: pointer; + font-size: @tool-img-btn-font-size; } #spanDate i:hover { diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css index 1dbc1db7e..6396501e6 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;font-size:14px;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.visible{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border:1px solid #888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.visible{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border:1px solid #888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js index 255325620..c3c4a0543 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js @@ -40,6 +40,19 @@ $(document).ready(function () { parseViewDate($(this).val(), changeViewDate); }); - // temp - $("#spanEventsByViewBtn").addClass("selected"); + // switch event filter + $("#spanAllEventsBtn").click(function () { + $("#spanAllEventsBtn").addClass("selected"); + $("#spanEventsByViewBtn").removeClass("selected"); + }); + + $("#spanEventsByViewBtn").click(function () { + $("#spanAllEventsBtn").removeClass("selected"); + $("#spanEventsByViewBtn").addClass("selected"); + }); + + // export events on the button click + $("#spanExportBtn").click(function () { + alert("Export is not implemented yet."); + }); }); diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js index 13b87d527..c96a838a1 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js @@ -346,7 +346,7 @@ $(document).ready(function () { restartUpdatingHourData(); }); - // export the table view on button click + // export the table view on the button click $("#spanExportBtn").click(function () { alert("Export is not implemented yet."); }); @@ -371,12 +371,12 @@ $(document).ready(function () { setTimeout(function () { hideHint(hint); }, 100); }); - // show chart on label click + // show chart on a label click $("#divTblWrapper a.lbl").click(function () { alert("Charts are not implemented yet."); }); - // send command on command icon click + // send command on a command icon click $("#divTblWrapper span.cmd").click(function () { alert("Commands are not implemented yet."); }); diff --git a/ScadaWeb/ScadaWebCommon5Beta/UserRights.cs b/ScadaWeb/ScadaWebCommon5Beta/UserRights.cs index 38a373172..a76c42d0e 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/UserRights.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/UserRights.cs @@ -36,14 +36,6 @@ namespace Scada.Web ///
public class UserRights { - /// - /// Права на все представления - /// - protected EntityRights allViewsRights; - /// - /// Права на весь контент - /// - protected EntityRights allContentRights; /// /// Права на предсталения /// @@ -63,6 +55,16 @@ public UserRights() } + /// + /// Получить права на все представления + /// + public EntityRights AllViewsRights { get; protected set; } + + /// + /// Получить права на весь контент + /// + public EntityRights AllContentRights { get; protected set; } + /// /// Получить право конфигурирования системы /// @@ -74,11 +76,11 @@ public UserRights() /// protected void SetToDefault() { - allViewsRights = EntityRights.NoRights; - allContentRights = EntityRights.NoRights; viewRightsDict = null; contentRightsDict = null; + AllViewsRights = EntityRights.NoRights; + AllContentRights = EntityRights.NoRights; ConfigRight = false; } @@ -94,19 +96,19 @@ public void Init(int roleID, DataAccess dataAccess) if (roleID == BaseValues.Roles.Admin) { - allViewsRights = new EntityRights(true, true); - allContentRights = new EntityRights(true, true); + AllViewsRights = new EntityRights(true, true); + AllContentRights = new EntityRights(true, true); ConfigRight = true; } else if (roleID == BaseValues.Roles.Dispatcher) { - allViewsRights = new EntityRights(true, true); - allContentRights = new EntityRights(true, true); + AllViewsRights = new EntityRights(true, true); + AllContentRights = new EntityRights(true, true); } else if (roleID == BaseValues.Roles.Guest) { - allViewsRights = new EntityRights(true, false); - allContentRights = new EntityRights(true, false); + AllViewsRights = new EntityRights(true, false); + AllContentRights = new EntityRights(true, false); } else if (BaseValues.Roles.Custom <= roleID && roleID < BaseValues.Roles.Err) { @@ -120,9 +122,9 @@ public void Init(int roleID, DataAccess dataAccess) /// public EntityRights GetViewRights(int viewID) { - if (allViewsRights.ViewRight) + if (AllViewsRights.ViewRight) { - return allViewsRights; + return AllViewsRights; } else { @@ -137,9 +139,9 @@ public EntityRights GetViewRights(int viewID) /// public EntityRights GetContentRights(string contentTypeCode) { - if (allContentRights.ViewRight) + if (AllContentRights.ViewRight) { - return allContentRights; + return AllContentRights; } else { diff --git a/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less b/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less index 335f490e4..1be849052 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less @@ -15,3 +15,5 @@ @menu-item-hover-fore-color: #00b9eb; @menu-item-selected-back-color: #0073aa; @menu-item-selected-fore-color: white; +@menu-item-disabled-fore-color: @menu-fore-color-dark; +@menu-item-disabled-fore-color-air: #ccc; From fbd5076752ef996dd8ec203b4c35a167a9d476f6 Mon Sep 17 00:00:00 2001 From: 2mik Date: Wed, 15 Jun 2016 22:30:26 +0300 Subject: [PATCH 112/382] ScadaWeb5: REST API --- .../plugins/Scheme/js/schememodel.js | 10 +-- .../PlgTable/plugins/Table/js/events.js | 39 +++++++-- .../PlgTable/plugins/Table/js/table.js | 4 +- .../ScadaWebShell5Beta/js/api/clientapi.js | 87 ++++++++++++++++--- ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js | 2 +- ScadaWeb/ScadaWebShell5Beta/js/api/utils.js | 9 +- .../ScadaWebShell5Beta/js/controls/popup.js | 4 +- ScadaWeb/ScadaWebShell5Beta/js/view.js | 2 +- 8 files changed, 126 insertions(+), 31 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js index cd13d68ef..d35c1a15b 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js @@ -90,7 +90,7 @@ scada.scheme.Scheme.prototype = Object.create(scada.scheme.BaseElement.prototype scada.scheme.Scheme.constructor = scada.scheme.Scheme; // Load scheme properties -// callback is function (success, complete) +// callback is a function (success, complete) scada.scheme.Scheme.prototype._loadSchemeProps = function (viewID, callback) { var operation = "SchemeSvc.svc/GetSchemeProps"; var thisScheme = this; @@ -177,7 +177,7 @@ scada.scheme.Scheme.prototype._obtainSchemeProps = function (parsedProps) { }; // Load scheme elements -// callback is function (success, complete) +// callback is a function (success, complete) scada.scheme.Scheme.prototype._loadElements = function (viewID, callback) { var operation = "SchemeSvc.svc/GetElements"; var thisScheme = this; @@ -277,7 +277,7 @@ scada.scheme.Scheme.prototype._appendElements = function (parsedElems) { }; // Load scheme images -// callback is function (success, complete) +// callback is a function (success, complete) scada.scheme.Scheme.prototype._loadImages = function (viewID, callback) { var operation = "SchemeSvc.svc/GetImages"; var thisScheme = this; @@ -400,7 +400,7 @@ scada.scheme.Scheme.prototype.clear = function () { }; // Load scheme. -// callback is function (success, complete) +// callback is a function (success, complete) scada.scheme.Scheme.prototype.load = function (viewID, callback) { var LoadStates = scada.scheme.LoadStates; @@ -474,7 +474,7 @@ scada.scheme.Scheme.prototype.createDom = function () { }; // Update the scheme elements -// callback is function (success) +// callback is a function (success) scada.scheme.Scheme.prototype.update = function (clientAPI, callback) { var thisScheme = this; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js index c3c4a0543..16961d816 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js @@ -1,4 +1,7 @@ -// Set current view date and process the consequent changes +// Filter events by view +var eventsByView = true; + +// Set current view date and process the consequent changes function changeViewDate(date, notify) { setViewDate(date); //updateHourDataColHdrText(); @@ -9,11 +12,37 @@ function changeViewDate(date, notify) { } } +// Enable or disable events by view filter +function setEventsByVeiw(val) { + eventsByView = val; + saveEventFilter(); + + if (val) { + $("#spanAllEventsBtn").removeClass("selected"); + $("#spanEventsByViewBtn").addClass("selected"); + } else { + $("#spanAllEventsBtn").addClass("selected"); + $("#spanEventsByViewBtn").removeClass("selected"); + } +} + +// Load the event filter from the cookies +function loadEventFilter() { + var val = scada.utils.getCookie("Table.EventsByView"); + setEventsByVeiw(val != "false"); +} + +// Save the event filter in the cookies +function saveEventFilter() { + scada.utils.setCookie("Table.EventsByView", eventsByView); +} + $(document).ready(function () { scada.clientAPI.rootPath = "../../"; styleIOS(); updateLayout(); initViewDate(); + loadEventFilter(); notifier = new scada.Notifier("#divNotif"); notifier.startClearingNotifications(); @@ -42,13 +71,13 @@ $(document).ready(function () { // switch event filter $("#spanAllEventsBtn").click(function () { - $("#spanAllEventsBtn").addClass("selected"); - $("#spanEventsByViewBtn").removeClass("selected"); + if (!$(this).hasClass("disabled")) { + setEventsByVeiw(false); + } }); $("#spanEventsByViewBtn").click(function () { - $("#spanAllEventsBtn").removeClass("selected"); - $("#spanEventsByViewBtn").addClass("selected"); + setEventsByVeiw(true); }); // export events on the button click diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js index c96a838a1..847ba7c83 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js @@ -211,7 +211,7 @@ function displayCellData(cell, cnlDataMap) { } // Request and display current data. -// callback is function (success) +// callback is a function (success) function updateCurData(callback) { scada.clientAPI.getCurCnlDataExtByView(viewID, function (success, cnlDataExtArr) { if (success) { @@ -230,7 +230,7 @@ function updateCurData(callback) { } // Request and display hourly data. -// callback is function (success) +// callback is a function (success) function updateHourData(callback) { scada.clientAPI.getHourCnlDataExtByView(viewDate, timeFrom, timeTo, viewID, scada.HourDataModes.INTEGER_HOURS, function (success, hourCnlDataExtArr) { diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js b/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js index ef3508e91..9c1c25054 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js @@ -13,6 +13,8 @@ // Rapid SCADA namespace var scada = scada || {}; +/********** Input Channel Data **********/ + // Input channel data type. // Note: Casing is caused by C# naming rules scada.CnlData = function () { @@ -36,17 +38,57 @@ scada.CnlDataExt.constructor = scada.CnlDataExt; // Extended hourly input channel data type scada.HourCnlDataExt = function () { this.Hour = NaN; + this.Modified = false; this.CnlDataExtArr = []; } -// Getting hour data modes enumeration +/********** Auxiliary Request Parameters **********/ + +// Input channel filter type. +// Warning: role access rights are validated only for the view +scada.CnlFilter = function () { + // Filter by the explicitly specified input channel numbers. No other filtering is applied + this.cnlNums = []; + // Filter by input channels included in the view + this.viewID = 0; +}; + +// Convert the input channel filter to a query string +scada.CnlFilter.prototype.toQueryString = function () { + return "cnlNums=" + scada.utils.arrayToQueryParam(this.cnlNums) + + "&viewID=" + (this.viewID ? this.viewID : 0); +}; + +// Time period in hours type +scada.HourPeriod = function () { + // Date is a reference point of the period + this.date = 0; + // Start hour relative to the date. May be negative + this.startHour = 0; + // End hour relative to the date + this.endHour = 0; +}; + +// Convert the time period to a query string +scada.HourPeriod.prototype.toQueryString = function () { + return "year=" + this.date.getFullYear() + + "&month=" + (this.date.getMonth() + 1) + + "&day=" + this.date.getDate() + + "&startHour=" + this.startHour + + "&endHour=" + this.endHour; +}; + +// Hourly data selection modes enumeration +// TODO: rename HourDataModes -> HourDataSelectModes scada.HourDataModes = { - // Get data for integer hours even if a snapshot doesn't exist + // Select data for integer hours even if a snapshot doesn't exist INTEGER_HOURS: false, - // Get existing snapshots + // Select existing hourly snapshots EXISTING: true }; +/********** Client API **********/ + // Client API object scada.clientAPI = { // Empty input channel data @@ -71,7 +113,7 @@ scada.clientAPI = { var parsedData = $.parseJSON(data.d); if (parsedData.Success) { scada.utils.logSuccessfulRequest(operation/*, data*/); - callback(true, parsedData.Data == null ? parsedData : parsedData.Data); + callback(true, parsedData.Data); } else { scada.utils.logServiceError(operation, parsedData.ErrorMessage); callback(false, errorResult); @@ -92,6 +134,13 @@ scada.clientAPI = { }); }, + // Extract year, month and day from the date, and join them into a query string + _dateToQueryString: function (date) { + return "year=" + date.getFullYear() + + "&month=" + (date.getMonth() + 1) + + "&day=" + date.getDate(); + }, + // Extract year, month and day from the date, and join them with the hours into a query string _getDateTimeQueryString: function (date, startHour, endHour) { return "year=" + date.getFullYear() + @@ -102,7 +151,7 @@ scada.clientAPI = { }, // Check that a user is logged on. - // callback is function (success, loggedOn) + // callback is a function (success, loggedOn) checkLoggedOn: function (callback) { this._request( "ClientApiSvc.svc/CheckLoggedOn", "", @@ -110,7 +159,7 @@ scada.clientAPI = { }, // Get current value and status of the input channel. - // callback is function (success, cnlData) + // callback is a function (success, cnlData) getCurCnlData: function (cnlNum, callback) { this._request( "ClientApiSvc.svc/GetCurCnlData", @@ -119,7 +168,7 @@ scada.clientAPI = { }, // Get extended current data of the input channel. - // callback is function (success, cnlDataExt) + // callback is a function (success, cnlDataExt) getCurCnlDataExt: function (cnlNum, callback) { this._request( "ClientApiSvc.svc/GetCurCnlDataExt", @@ -128,7 +177,7 @@ scada.clientAPI = { }, // Get extended current data of the specified input channels. - // callback is function (success, cnlDataExtArr) + // callback is a function (success, cnlDataExtArr) getCurCnlDataExtByCnlNums: function (cnlNums, callback) { this._request( "ClientApiSvc.svc/GetCurCnlDataExtByCnlNums", @@ -137,7 +186,8 @@ scada.clientAPI = { }, // Get extended current data of the input channels of the specified view. - // callback is function (success, cnlDataExtArr) + // callback is a function (success, cnlDataExtArr) + // TODO: getCurCnlDataExt: function (cnlFilter, callback) getCurCnlDataExtByView: function (viewID, callback) { this._request( "ClientApiSvc.svc/GetCurCnlDataExtByView", @@ -146,7 +196,7 @@ scada.clientAPI = { }, // Get extended hourly data of the specified input channels. - // callback is function (success, hourCnlDataExtArr) + // callback is a function (success, hourCnlDataExtArr) getHourCnlDataExtByCnlNums: function (date, startHour, endHour, cnlNums, mode, callback) { this._request( "ClientApiSvc.svc/GetHourCnlDataExtByCnlNums", @@ -155,7 +205,8 @@ scada.clientAPI = { }, // Get extended hourly data of the input channels of the specified view. - // callback is function (success, hourCnlDataExtArr) + // callback is a function (success, hourCnlDataExtArr) + // TODO: getHourCnlData: function (hourPeriod, cnlFilter, selectMode, dataAge /*array*/, callback) getHourCnlDataExtByView: function (date, startHour, endHour, viewID, mode, callback) { this._request( "ClientApiSvc.svc/GetHourCnlDataExtByView", @@ -163,14 +214,24 @@ scada.clientAPI = { callback, []); }, + // Get events for the specified date and channel filter. + // callback is a function (success, eventExtArr, dataAge) + getEvents: function (date, cnlFilter, lastCount, startEvNum, dataAge, callback) { + this._request( + "ClientApiSvc.svc/GetEvents", + "?" + this._dateToQueryString(date) + "&" + cnlFilter.toQueryString() + + "&lastCount=" + lastCount + "&startEvNum=" + startEvNum + "&dataAge=" + dataAge, + callback, []); + }, + // Get the stamp of the view from the cache. - // callback is function (success, stamp) + // callback is a function (success, stamp) getViewStamp: function (viewID, callback) { this._request("ClientApiSvc.svc/GetViewStamp", "?viewID=" + viewID, callback, 0); }, // Parse date and time using the application culture - // callback is function (success, value), + // callback is a function (success, value), // value is the number of milliseconds or null in case of any error parseDateTime: function (s, callback) { this._request("ClientApiSvc.svc/ParseDateTime", "?s=" + s, callback, null); diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js b/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js index 6903b72af..7e6b75999 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js @@ -51,7 +51,7 @@ scada.dialogs = { }, // Show calendar dropdown form. - // callback is function (dialogResult, extraParams), + // callback is a function (dialogResult, extraParams), // dialogResult is true or false, // extraParams is object { date, dateStr } showCalendar: function (anchorElem, selectedDate, callback) { diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/utils.js b/ScadaWeb/ScadaWebShell5Beta/js/api/utils.js index 407fafa6f..c95275588 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/utils.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/utils.js @@ -47,7 +47,7 @@ scada.utils = { }, // Get the query string parameter value - getQueryStringParam: function (paramName, opt_url) { + getQueryParam: function (paramName, opt_url) { if (paramName) { var url = opt_url ? opt_url : decodeURIComponent(window.location); var begInd = queryString.indexOf("?"); @@ -71,7 +71,7 @@ scada.utils = { // Set or add the query string parameter value. // The method returns a new string - setQueryStringParam: function (paramName, paramVal, opt_url) { + setQueryParam: function (paramName, paramVal, opt_url) { if (paramName) { var url = opt_url ? opt_url : decodeURIComponent(window.location); var searchName = "?" + paramName + "="; @@ -100,6 +100,11 @@ scada.utils = { } }, + // Convert array to a query string parameter by joining array elements with a comma + arrayToQueryParam: function (arr) { + return arr ? (Array.isArray(arr) ? arr.join(",") : arr) : ""; + }, + // Returns the current time string getCurTime: function () { return new Date().toLocaleTimeString("en-GB"); diff --git a/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js b/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js index 586c1ada2..45217e38e 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js @@ -86,7 +86,7 @@ scada.Popup.prototype._getOffset = function (elem) { }; // Show popup with the specified url as a dropdown menu below the anchorElem. -// callback is function (dialogResult, extraParams) +// callback is a function (dialogResult, extraParams) scada.Popup.prototype.showDropdown = function (url, anchorElem, callback) { var thisObj = this; var popupElem = $(" diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs index 9fc5ef9c5..f36df0b0f 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs @@ -1,16 +1,39 @@ -using Scada.Data; +/* + * Copyright 2016 Mikhail Shiryaev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Product : Rapid SCADA + * Module : PlgTable + * Summary : Events web form + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + */ + using Scada.Data.Models; using Scada.UI; using Scada.Web.Shell; using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using System.Web.UI; -using System.Web.UI.WebControls; namespace Scada.Web.Plugins.Table { + /// + /// Events web form + /// Веб-форма событий + /// public partial class WFrmEvents : System.Web.UI.Page { // Переменные для вывода на веб-страницу @@ -20,7 +43,7 @@ public partial class WFrmEvents : System.Web.UI.Page protected int arcRefrRate; // частота обновления архивных данных protected string phrases; // локализованные фразы protected string today; // текущая дата - protected string eventsTableHtml; // HTML-код таблицы событий + protected int dispEventCnt; // количество отображаемых событий protected void Page_Load(object sender, EventArgs e) { @@ -51,6 +74,7 @@ protected void Page_Load(object sender, EventArgs e) // подготовка данных для вывода на веб-страницу dataRefrRate = userData.WebSettings.DataRefrRate; arcRefrRate = userData.WebSettings.ArcRefrRate; + dispEventCnt = userData.WebSettings.DispEventCnt; Localization.Dict dict; Localization.Dictionaries.TryGetValue("Scada.Web.Plugins.Table.WFrmEvents.Js", out dict); diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs index 51edaf94a..45d7f86cf 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs @@ -15,7 +15,7 @@ * * * Product : Rapid SCADA - * Module : PlgSchemeCommon + * Module : PlgTable * Summary : Table view web form * * Author : Mikhail Shiryaev diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css index e8afcb0e8..ba887f548 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css @@ -131,7 +131,7 @@ color: white; } #divToolbar .tool-btn.disabled { - color: #e5e5e5; + color: #ccc; cursor: default; } #tblEvents { @@ -154,6 +154,6 @@ #tblEvents td.cnl { width: 15%; } -#tblEvents td.descr { +#tblEvents td.text { width: 35%; } \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.less b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.less index be866aa24..a8e886ed6 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.less +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.less @@ -39,6 +39,6 @@ width: 15%; } -#tblEvents td.descr { +#tblEvents td.text { width: 35%; } diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css index c57bebdc9..3499eaeaf 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.visible{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border:1px solid #888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divToolbar .tool-btn.selected{background-color:#0073aa;color:#fff;}#divToolbar .tool-btn.disabled{color:#e5e5e5;cursor:default;}#tblEvents{width:100%;}#tblEvents td.num,#tblEvents td.time,#tblEvents td.ack{text-align:center;}#tblEvents td.num,#tblEvents td.ack{width:5%;}#tblEvents td.time{width:10%;}#tblEvents td.obj,#tblEvents td.dev,#tblEvents td.cnl{width:15%;}#tblEvents td.descr{width:35%;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.visible{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border:1px solid #888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divToolbar .tool-btn.selected{background-color:#0073aa;color:#fff;}#divToolbar .tool-btn.disabled{color:#ccc;cursor:default;}#tblEvents{width:100%;}#tblEvents td.num,#tblEvents td.time,#tblEvents td.ack{text-align:center;}#tblEvents td.num,#tblEvents td.ack{width:5%;}#tblEvents td.time{width:10%;}#tblEvents td.obj,#tblEvents td.dev,#tblEvents td.cnl{width:15%;}#tblEvents td.text{width:35%;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js index 16961d816..6e3b84a68 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js @@ -1,5 +1,16 @@ // Filter events by view var eventsByView = true; +// Input channel filter for event requests +var cnlFilter = new scada.CnlFilter(); +// Events data age after full update +var fullDataAge = 0; +// Events data age after partial update +var partialDataAge = 0; +// Number of the last received event +var lastEvNum = 0; + +// Displayed event count. Must be defined in Events.aspx +var dispEventCnt = dispEventCnt || 0; // Set current view date and process the consequent changes function changeViewDate(date, notify) { @@ -15,6 +26,7 @@ function changeViewDate(date, notify) { // Enable or disable events by view filter function setEventsByVeiw(val) { eventsByView = val; + cnlFilter.viewID = val ? viewID : 0; saveEventFilter(); if (val) { @@ -37,6 +49,68 @@ function saveEventFilter() { scada.utils.setCookie("Table.EventsByView", eventsByView); } +// Append new event to the event table +function appendEvent(tableElem, event) { + var eventElem = $("" + + "" + event.Num + "" + + "" + event.Time + "" + + "" + event.Obj + "" + + "" + event.KP + "" + + "" + event.Cnl + "" + + "" + event.Text + "" + + "" + event.Ack + "" + + ""); + + if (event.Color) { + eventElem.css("color", event.Color); + } + + tableElem.append(eventElem); +} + +// Request and display events. +// callback is a function (success) +function updateEvents(full, callback) { + var startEvNum = full ? 0 : lastEvNum + 1; + var reqDataAge = full ? fullDataAge : partialDataAge; + + scada.clientAPI.getEvents(viewDate, cnlFilter, dispEventCnt, startEvNum, reqDataAge, + function (success, eventArr, dataAge) { + if (success) { + partialDataAge = dataAge; + + if (eventArr.length) { + lastEvNum = eventArr[eventArr.length - 1].Num; + } + + var tableElem = $("#tblEvents"); + + if (full) { + fullDataAge = dataAge; + } else { + for (var event of eventArr) { + appendEvent(tableElem, event); + } + } + + callback(true); + } else { + callback(false); + } + }); +} + +// Start cyclic updating of all displayed or newly added events +function startUpdatingEvents(full) { + updateEvents(full, function (success) { + if (!success) { + notifier.addNotification(phrases.UpdateEventsError, true, notifier.DEF_NOTIF_LIFETIME); + } + + setTimeout(startUpdatingEvents, full ? arcRefrRate : dataRefrRate, full); + }); +} + $(document).ready(function () { scada.clientAPI.rootPath = "../../"; styleIOS(); @@ -84,4 +158,8 @@ $(document).ready(function () { $("#spanExportBtn").click(function () { alert("Export is not implemented yet."); }); + + // start updating events + setTimeout(startUpdatingEvents, arcRefrRate, true); + setTimeout(startUpdatingEvents, dataRefrRate, false); }); diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js index 847ba7c83..2ff302aa2 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js @@ -3,11 +3,6 @@ var HEADER_TIME_OPTIONS = { hour: "2-digit", minute: "2-digit" }; // Column header date and time format options var HEADER_DATETIME_OPTIONS = { month: "short", day: "2-digit", hour: "2-digit", minute: "2-digit" }; -// Notifier control -var notifier = null; -// Current view date -var viewDate = null; - // Time period: firstHour <= timeFrom <= timeTo <= lastHour // Beginning of the displayed time period var timeFrom = null; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/tablecommon.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/tablecommon.js index 6d13d2177..8cd989f3c 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/tablecommon.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/tablecommon.js @@ -3,6 +3,10 @@ var VIEW_DATE_OPTIONS = { year: "numeric", month: "long", day: "2-digit" }; // The view hub object var viewHub = scada.viewHubLocator.getViewHub(); +// Notifier control +var notifier = null; +// Current view date +var viewDate = null; // The variables below must be defined in *.aspx // View ID diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml index 6ab72d7e3..215fcbaca 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml @@ -7,6 +7,11 @@ Error loading table view from file Error saving table view to file + + + + Error updating events + Selected day: Previous day: diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml index c74d686e4..3b0dfde0f 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml @@ -7,6 +7,11 @@ Ошибка при загрузке табличного представления из файла Ошибка при сохранении табличного представления в файле + + + + Ошибка при обновлении событий + Выбранные сутки: Предыдущие сутки: diff --git a/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs b/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs index 40a20c596..b64466d00 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs @@ -674,7 +674,7 @@ public string GetHourCnlDataExtByView(int year, int month, int day, [OperationContract] [WebGet] public string GetEvents(int year, int month, int day, string cnlNums, int viewID, - int lastCount, int startEvNum, int dataAge) + int lastCount, int startEvNum, long dataAge) { try { @@ -691,17 +691,37 @@ public string GetEvents(int year, int month, int day, string cnlNums, int viewID // получение событий DateTime date = new DateTime(year, month, day); EventTableLight tblEvent = AppData.DataAccess.DataCache.GetEventTable(date); - bool reversed; - List events = - tblEvent.GetFilteredEvents(eventFilter, lastCount, startEvNum, out reversed); + long newDataAge = WebUtils.DateTimeToJs(tblEvent.FileModTime); + Event[] eventsToSend; - // преобразование событий для передачи - int evCnt = events.Count; - Event[] eventsToSend = new Event[evCnt]; - for (int i = 0; i < evCnt; i++) - eventsToSend[i] = ConvertEvent(events[i]); + if (tblEvent.FileModTime > DateTime.MinValue && dataAge < newDataAge) + { + // применение фильтра событий + bool reversed; + List events = + tblEvent.GetFilteredEvents(eventFilter, lastCount, startEvNum, out reversed); + + // преобразование событий для передачи + int evCnt = events.Count; + eventsToSend = new Event[evCnt]; + if (reversed) + { + for (int i = 0, j = evCnt - 1; i < evCnt; i++, j--) + eventsToSend[i] = ConvertEvent(events[j]); + } + else + { + for (int i = 0; i < evCnt; i++) + eventsToSend[i] = ConvertEvent(events[i]); + } + } + else + { + eventsToSend = new Event[0]; + newDataAge = 0; + } - return JsSerializer.Serialize(new ArcDTO(eventsToSend, WebUtils.DateTimeToJs(tblEvent.FileModTime))); + return JsSerializer.Serialize(new ArcDTO(eventsToSend, newDataAge)); } catch (Exception ex) { diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js b/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js index fa994eb13..9d62f419d 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js @@ -42,6 +42,21 @@ scada.HourCnlDataExt = function () { this.CnlDataExtArr = []; } +/********** Event **********/ + +// Event type +scada.Event = function () { + this.Num = 0; + this.Time = ""; + this.Obj = ""; + this.KP = ""; + this.Cnl = ""; + this.Text = ""; + this.Ack = ""; + this.Color = ""; + this.Sound = false; +}; + /********** Auxiliary Request Parameters **********/ // Input channel filter type @@ -112,7 +127,11 @@ scada.clientAPI = { var parsedData = $.parseJSON(data.d); if (parsedData.Success) { scada.utils.logSuccessfulRequest(operation/*, data*/); - callback(true, parsedData.Data); + if (typeof parsedData.DataAge === "undefined") { + callback(true, parsedData.Data); + } else { + callback(true, parsedData.Data, parsedData.DataAge); + } } else { scada.utils.logServiceError(operation, parsedData.ErrorMessage); callback(false, errorResult); From fce72aa2122e21bd8068040704c0d646f1447509 Mon Sep 17 00:00:00 2001 From: 2mik Date: Wed, 22 Jun 2016 18:24:01 +0300 Subject: [PATCH 129/382] PlgTable: load events --- ScadaData/ScadaData/Client/DataCache.cs | 2 +- .../OpenPlugins/PlgScheme/js/api/clientapi.js | 144 +++++++++---- .../OpenPlugins/PlgScheme/js/api/utils.js | 43 ++-- .../plugins/Scheme/js/schememodel.js | 6 +- .../OpenPlugins/PlgTable/js/api/clientapi.js | 17 +- ScadaWeb/OpenPlugins/PlgTable/js/api/utils.js | 19 +- .../PlgTable/plugins/Table/Events.aspx | 23 +-- .../PlgTable/plugins/Table/js/events.js | 191 +++++++++++++++--- .../PlgTable/plugins/Table/js/table.js | 8 +- ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs | 44 ++-- ScadaWeb/ScadaWebCommon5Beta/WebUtils.cs | 2 +- .../ScadaWebShell5Beta/js/api/clientapi.js | 2 +- ScadaWeb/ScadaWebShell5Beta/js/api/utils.js | 19 +- 13 files changed, 374 insertions(+), 146 deletions(-) diff --git a/ScadaData/ScadaData/Client/DataCache.cs b/ScadaData/ScadaData/Client/DataCache.cs index 85d605886..f5271189b 100644 --- a/ScadaData/ScadaData/Client/DataCache.cs +++ b/ScadaData/ScadaData/Client/DataCache.cs @@ -405,7 +405,7 @@ protected void FillCnlStatProps() { DataRow row = tblEvType.Rows[i]; CnlStatProps cnlStatProps = new CnlStatProps((int)row["CnlStatus"]) { Color = (string)row["Color"] }; - CnlStatProps.Add(cnlStatProps.Status, cnlStatProps); + newCnlStatProps.Add(cnlStatProps.Status, cnlStatProps); } CnlStatProps = newCnlStatProps; diff --git a/ScadaWeb/OpenPlugins/PlgScheme/js/api/clientapi.js b/ScadaWeb/OpenPlugins/PlgScheme/js/api/clientapi.js index ef3508e91..111090f74 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/js/api/clientapi.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/js/api/clientapi.js @@ -13,6 +13,8 @@ // Rapid SCADA namespace var scada = scada || {}; +/********** Input Channel Data **********/ + // Input channel data type. // Note: Casing is caused by C# naming rules scada.CnlData = function () { @@ -36,24 +38,78 @@ scada.CnlDataExt.constructor = scada.CnlDataExt; // Extended hourly input channel data type scada.HourCnlDataExt = function () { this.Hour = NaN; + this.Modified = false; this.CnlDataExtArr = []; } -// Getting hour data modes enumeration +/********** Event **********/ + +// Event type +scada.Event = function () { + this.Num = 0; + this.Time = ""; + this.Obj = ""; + this.KP = ""; + this.Cnl = ""; + this.Text = ""; + this.Ack = ""; + this.Color = ""; + this.Sound = false; +}; + +/********** Auxiliary Request Parameters **********/ + +// Input channel filter type +scada.CnlFilter = function () { + // Filter by the explicitly specified input channel numbers. No other filtering is applied + this.cnlNums = []; + // Filter by input channels included in the view + this.viewID = 0; +}; + +// Convert the input channel filter to a query string +scada.CnlFilter.prototype.toQueryString = function () { + return "cnlNums=" + scada.utils.arrayToQueryParam(this.cnlNums) + + "&viewID=" + (this.viewID ? this.viewID : 0); +}; + +// Time period in hours type +scada.HourPeriod = function () { + // Date is a reference point of the period + this.date = 0; + // Start hour relative to the date. May be negative + this.startHour = 0; + // End hour relative to the date + this.endHour = 0; +}; + +// Convert the time period to a query string +scada.HourPeriod.prototype.toQueryString = function () { + return "year=" + this.date.getFullYear() + + "&month=" + (this.date.getMonth() + 1) + + "&day=" + this.date.getDate() + + "&startHour=" + this.startHour + + "&endHour=" + this.endHour; +}; + +// Hourly data selection modes enumeration +// TODO: rename HourDataModes -> HourDataSelectModes scada.HourDataModes = { - // Get data for integer hours even if a snapshot doesn't exist + // Select data for integer hours even if a snapshot doesn't exist INTEGER_HOURS: false, - // Get existing snapshots + // Select existing hourly snapshots EXISTING: true }; +/********** Client API **********/ + // Client API object scada.clientAPI = { // Empty input channel data - _emptyCnlData: Object.freeze(new scada.CnlData()), + _EMPTY_CNL_DATA: Object.freeze(new scada.CnlData()), // Empty extended input channel data - _emptyCnlDataExt: Object.freeze(new scada.CnlDataExt()), + _EMPTY_CNL_DATA_EXT: Object.freeze(new scada.CnlDataExt()), // Web service root path rootPath: "", @@ -71,14 +127,18 @@ scada.clientAPI = { var parsedData = $.parseJSON(data.d); if (parsedData.Success) { scada.utils.logSuccessfulRequest(operation/*, data*/); - callback(true, parsedData.Data == null ? parsedData : parsedData.Data); + if (typeof parsedData.DataAge === "undefined") { + callback(true, parsedData.Data); + } else { + callback(true, parsedData.Data, parsedData.DataAge); + } } else { scada.utils.logServiceError(operation, parsedData.ErrorMessage); callback(false, errorResult); } } catch (ex) { - scada.utils.logServiceFormatError(operation); + scada.utils.logProcessingError(operation, ex.message); if (typeof callback === "function") { callback(false, errorResult); } @@ -92,7 +152,15 @@ scada.clientAPI = { }); }, + // Extract year, month and day from the date, and join them into a query string + _dateToQueryString: function (date) { + return "year=" + date.getFullYear() + + "&month=" + (date.getMonth() + 1) + + "&day=" + date.getDate(); + }, + // Extract year, month and day from the date, and join them with the hours into a query string + // TODO: obsolete _getDateTimeQueryString: function (date, startHour, endHour) { return "year=" + date.getFullYear() + "&month=" + (date.getMonth() + 1) + @@ -102,75 +170,73 @@ scada.clientAPI = { }, // Check that a user is logged on. - // callback is function (success, loggedOn) + // callback is a function (success, loggedOn) checkLoggedOn: function (callback) { - this._request( - "ClientApiSvc.svc/CheckLoggedOn", "", - callback, false); + this._request("ClientApiSvc.svc/CheckLoggedOn", "", callback, false); }, // Get current value and status of the input channel. - // callback is function (success, cnlData) + // callback is a function (success, cnlData) getCurCnlData: function (cnlNum, callback) { - this._request( - "ClientApiSvc.svc/GetCurCnlData", - "?cnlNum=" + cnlNum, - callback, this._emptyCnlData); + this._request("ClientApiSvc.svc/GetCurCnlData", "?cnlNum=" + cnlNum, callback, this._EMPTY_CNL_DATA); }, // Get extended current data of the input channel. - // callback is function (success, cnlDataExt) + // callback is a function (success, cnlDataExt) + // TODO: obsolete getCurCnlDataExt: function (cnlNum, callback) { - this._request( - "ClientApiSvc.svc/GetCurCnlDataExt", - "?cnlNum=" + cnlNum, - callback, this._emptyCnlDataExt); + this._request("ClientApiSvc.svc/GetCurCnlDataExt", "?cnlNum=" + cnlNum, callback, this._EMPTY_CNL_DATA_EXT); }, // Get extended current data of the specified input channels. - // callback is function (success, cnlDataExtArr) + // callback is a function (success, cnlDataExtArr) + // TODO: obsolete getCurCnlDataExtByCnlNums: function (cnlNums, callback) { - this._request( - "ClientApiSvc.svc/GetCurCnlDataExtByCnlNums", - "?cnlNums=" + cnlNums, - callback, []); + this._request("ClientApiSvc.svc/GetCurCnlDataExtByCnlNums", "?cnlNums=" + cnlNums, callback, []); }, // Get extended current data of the input channels of the specified view. - // callback is function (success, cnlDataExtArr) + // callback is a function (success, cnlDataExtArr) + // TODO: getCurCnlDataExt: function (cnlFilter, callback) getCurCnlDataExtByView: function (viewID, callback) { - this._request( - "ClientApiSvc.svc/GetCurCnlDataExtByView", - "?viewID=" + viewID, - callback, []); + this._request("ClientApiSvc.svc/GetCurCnlDataExtByView", "?viewID=" + viewID, callback, []); }, // Get extended hourly data of the specified input channels. - // callback is function (success, hourCnlDataExtArr) + // callback is a function (success, hourCnlDataExtArr) + // TODO: obsolete getHourCnlDataExtByCnlNums: function (date, startHour, endHour, cnlNums, mode, callback) { - this._request( - "ClientApiSvc.svc/GetHourCnlDataExtByCnlNums", + this._request("ClientApiSvc.svc/GetHourCnlDataExtByCnlNums", "?" + this._getDateTimeQueryString(date, startHour, endHour) + "&cnlNums=" + cnlNums + "&existing=" + mode, callback, []); }, // Get extended hourly data of the input channels of the specified view. - // callback is function (success, hourCnlDataExtArr) + // callback is a function (success, hourCnlDataExtArr) + // TODO: getHourCnlData: function (hourPeriod, cnlFilter, selectMode, dataAge /*array*/, callback) getHourCnlDataExtByView: function (date, startHour, endHour, viewID, mode, callback) { - this._request( - "ClientApiSvc.svc/GetHourCnlDataExtByView", + this._request("ClientApiSvc.svc/GetHourCnlDataExtByView", "?" + this._getDateTimeQueryString(date, startHour, endHour) + "&viewID=" + viewID + "&existing=" + mode, callback, []); }, + // Get events by the specified filter. + // callback is a function (success, eventArr, dataAge) + getEvents: function (date, cnlFilter, lastCount, startEvNum, dataAge, callback) { + this._request("ClientApiSvc.svc/GetEvents", + "?" + this._dateToQueryString(date) + "&" + cnlFilter.toQueryString() + + "&lastCount=" + lastCount + "&startEvNum=" + startEvNum + "&dataAge=" + dataAge, + callback, []); + }, + // Get the stamp of the view from the cache. - // callback is function (success, stamp) + // callback is a function (success, stamp) getViewStamp: function (viewID, callback) { this._request("ClientApiSvc.svc/GetViewStamp", "?viewID=" + viewID, callback, 0); }, // Parse date and time using the application culture - // callback is function (success, value), + // callback is a function (success, value), // value is the number of milliseconds or null in case of any error parseDateTime: function (s, callback) { this._request("ClientApiSvc.svc/ParseDateTime", "?s=" + s, callback, null); diff --git a/ScadaWeb/OpenPlugins/PlgScheme/js/api/utils.js b/ScadaWeb/OpenPlugins/PlgScheme/js/api/utils.js index 5dc457d9c..5d6bedc2d 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/js/api/utils.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/js/api/utils.js @@ -13,8 +13,11 @@ var scada = scada || {}; // JavaScript utilities object scada.utils = { + // z-index that moves element to the front + FRONT_ZINDEX: 10000, + // Default cookie expiration period in days - cookieExpiration: 7, + COOKIE_EXPIRATION: 7, // Get cookie getCookie: function (name) { @@ -37,16 +40,16 @@ scada.utils = { // Set cookie setCookie: function (name, value, opt_expDays) { - var expDays = opt_expDays ? opt_expDays : this.cookieExpiration; + var expDays = opt_expDays ? opt_expDays : this.COOKIE_EXPIRATION; var expires = new Date(); expires.setDate(expires.getDate() + expDays); document.cookie = name + "=" + encodeURIComponent(value) + "; expires=" + expires.toUTCString(); }, // Get the query string parameter value - getQueryStringParam: function (paramName, opt_url) { + getQueryParam: function (paramName, opt_url) { if (paramName) { - var url = opt_url ? opt_url : unescape(window.location); + var url = opt_url ? opt_url : decodeURIComponent(window.location); var begInd = queryString.indexOf("?"); if (begInd > 0) { @@ -68,9 +71,9 @@ scada.utils = { // Set or add the query string parameter value. // The method returns a new string - setQueryStringParam: function (paramName, paramVal, opt_url) { + setQueryParam: function (paramName, paramVal, opt_url) { if (paramName) { - var url = opt_url ? opt_url : unescape(window.location); + var url = opt_url ? opt_url : decodeURIComponent(window.location); var searchName = "?" + paramName + "="; var nameBegInd = url.indexOf(searchName); @@ -83,20 +86,25 @@ scada.utils = { // replace parameter value var valBegInd = nameBegInd + searchName.length; var valEndInd = url.indexOf("&", valBegInd); - var newUrl = url.substring(0, valBegInd) + paramVal; + var newUrl = url.substring(0, valBegInd) + encodeURIComponent(paramVal); return valEndInd > 0 ? newUrl + url.substring(valEndInd) : newUrl; } else { // add parameter var mark = url.indexOf("?") >= 0 ? "&" : "?"; - return url + mark + paramName + "=" + paramVal; + return url + mark + paramName + "=" + encodeURIComponent(paramVal); } } else { return ""; } }, + // Convert array to a query string parameter by joining array elements with a comma + arrayToQueryParam: function (arr) { + return arr ? (Array.isArray(arr) ? arr.join(",") : arr) : ""; + }, + // Returns the current time string getCurTime: function () { return new Date().toLocaleTimeString("en-GB"); @@ -110,21 +118,22 @@ scada.utils = { } }, + // Write information about the failed request to console + logFailedRequest: function (operation, jqXHR) { + console.error(this.getCurTime() + " Request '" + operation + "' failed: " + + jqXHR.status + " (" + jqXHR.statusText + ")"); + }, + // Write information about the internal service error to console logServiceError: function (operation, opt_message) { console.error(this.getCurTime() + " Request '" + operation + "' reports internal service error" + (opt_message ? ": " + opt_message : "")); }, - // Write information about the internal service error to console - logServiceFormatError: function (operation) { - console.error(this.getCurTime() + " Request '" + operation + "' returns data in incorrect format"); - }, - - // Write information about the failed request to console - logFailedRequest: function (operation, jqXHR) { - console.error(this.getCurTime() + " Request '" + operation + "' failed: " + - jqXHR.status + " (" + jqXHR.statusText + ")"); + // Write information about the request processing error to console + logProcessingError: function (operation, opt_message) { + console.error(this.getCurTime() + " Error processing request '" + operation + "'" + + (opt_message ? ": " + opt_message : "")); }, // Check if browser is in fullscreen mode diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js index d35c1a15b..2478af715 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js @@ -120,7 +120,7 @@ scada.scheme.Scheme.prototype._loadSchemeProps = function (viewID, callback) { } } catch (ex) { - scada.utils.logServiceFormatError(operation); + scada.utils.logProcessingError(operation, ex.message); callback(false, false); } }) @@ -211,7 +211,7 @@ scada.scheme.Scheme.prototype._loadElements = function (viewID, callback) { } } catch (ex) { - scada.utils.logServiceFormatError(operation); + scada.utils.logProcessingError(operation, ex.message); callback(false, false); } }) @@ -313,7 +313,7 @@ scada.scheme.Scheme.prototype._loadImages = function (viewID, callback) { } } catch (ex) { - scada.utils.logServiceFormatError(operation); + scada.utils.logProcessingError(operation, ex.message); callback(false, false); } }) diff --git a/ScadaWeb/OpenPlugins/PlgTable/js/api/clientapi.js b/ScadaWeb/OpenPlugins/PlgTable/js/api/clientapi.js index 33322e0cd..111090f74 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/js/api/clientapi.js +++ b/ScadaWeb/OpenPlugins/PlgTable/js/api/clientapi.js @@ -42,6 +42,21 @@ scada.HourCnlDataExt = function () { this.CnlDataExtArr = []; } +/********** Event **********/ + +// Event type +scada.Event = function () { + this.Num = 0; + this.Time = ""; + this.Obj = ""; + this.KP = ""; + this.Cnl = ""; + this.Text = ""; + this.Ack = ""; + this.Color = ""; + this.Sound = false; +}; + /********** Auxiliary Request Parameters **********/ // Input channel filter type @@ -123,7 +138,7 @@ scada.clientAPI = { } } catch (ex) { - scada.utils.logServiceFormatError(operation); + scada.utils.logProcessingError(operation, ex.message); if (typeof callback === "function") { callback(false, errorResult); } diff --git a/ScadaWeb/OpenPlugins/PlgTable/js/api/utils.js b/ScadaWeb/OpenPlugins/PlgTable/js/api/utils.js index c95275588..5d6bedc2d 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/js/api/utils.js +++ b/ScadaWeb/OpenPlugins/PlgTable/js/api/utils.js @@ -118,21 +118,22 @@ scada.utils = { } }, + // Write information about the failed request to console + logFailedRequest: function (operation, jqXHR) { + console.error(this.getCurTime() + " Request '" + operation + "' failed: " + + jqXHR.status + " (" + jqXHR.statusText + ")"); + }, + // Write information about the internal service error to console logServiceError: function (operation, opt_message) { console.error(this.getCurTime() + " Request '" + operation + "' reports internal service error" + (opt_message ? ": " + opt_message : "")); }, - // Write information about the internal service error to console - logServiceFormatError: function (operation) { - console.error(this.getCurTime() + " Request '" + operation + "' returns data in incorrect format"); - }, - - // Write information about the failed request to console - logFailedRequest: function (operation, jqXHR) { - console.error(this.getCurTime() + " Request '" + operation + "' failed: " + - jqXHR.status + " (" + jqXHR.statusText + ")"); + // Write information about the request processing error to console + logProcessingError: function (operation, opt_message) { + console.error(this.getCurTime() + " Error processing request '" + operation + "'" + + (opt_message ? ": " + opt_message : "")); }, // Check if browser is in fullscreen mode diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx index 1b4f397e0..dcd517707 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx @@ -46,23 +46,14 @@
- - - - - - - + + + + + + + -
NumberDate and TimeObjectDeviceChannelDescriptionAckNumberDate and TimeObjectDeviceChannelDescriptionAck
diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js index 6e3b84a68..3c573e537 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js @@ -1,13 +1,22 @@ // Filter events by view var eventsByView = true; // Input channel filter for event requests -var cnlFilter = new scada.CnlFilter(); +var cnlFilter = null; + +// Array of jQuery objects, where each element represents an event row +var eventRows = []; // Events data age after full update var fullDataAge = 0; // Events data age after partial update var partialDataAge = 0; // Number of the last received event var lastEvNum = 0; +// The last received event has the alternate style +var lastEvAlt = true; +// Timeout ID of the full events updating timer +var fullUpdateTimeoutID = null; +// Timeout ID of the partial events updating timer +var partUpdateTimeoutID = null; // Displayed event count. Must be defined in Events.aspx var dispEventCnt = dispEventCnt || 0; @@ -15,8 +24,7 @@ var dispEventCnt = dispEventCnt || 0; // Set current view date and process the consequent changes function changeViewDate(date, notify) { setViewDate(date); - //updateHourDataColHdrText(); - //restartUpdatingHourData(); + resetEvents(); if (notify) { sendViewDateNotification(date); @@ -26,6 +34,7 @@ function changeViewDate(date, notify) { // Enable or disable events by view filter function setEventsByVeiw(val) { eventsByView = val; + cnlFilter = new scada.CnlFilter(); cnlFilter.viewID = val ? viewID : 0; saveEventFilter(); @@ -49,9 +58,9 @@ function saveEventFilter() { scada.utils.setCookie("Table.EventsByView", eventsByView); } -// Append new event to the event table -function appendEvent(tableElem, event) { - var eventElem = $("" + +// Create detached jQuery object that represents an event row +function createEventRow(event) { + var eventRow = $("" + "" + event.Num + "" + "" + event.Time + "" + "" + event.Obj + "" + @@ -62,37 +71,146 @@ function appendEvent(tableElem, event) { ""); if (event.Color) { - eventElem.css("color", event.Color); + eventRow.css("color", event.Color); + } + + eventRow.data("num", event.Num); + return eventRow; +} + +// Append new event to the event table +function appendEvent(tableElem, event) { + var eventRow = createEventRow(event); + + lastEvAlt = !lastEvAlt; + if (lastEvAlt) { + eventRow.addClass("alt"); } - tableElem.append(eventElem); + eventRows.push(eventRow); + tableElem.append(eventRow); +} + +// Rewrite event HTML +function rewriteEvent(eventRow, event) { + if (eventRow.data("num") == event.Num) { + eventRow.children("td.time").text(event.Time); + eventRow.children("td.obj").text(event.Obj); + eventRow.children("td.dev").text(event.KP); + eventRow.children("td.cnl").text(event.Cnl); + eventRow.children("td.text").text(event.Text); + eventRow.children("td.ack").text(event.Ack); + } else { + console.error(scada.utils.getCurTime() + " Event number mismatch"); + } +} + +// Append new events to the event table starting from the specified index +function appendEvents(tableElem, eventArr, startIndex) { + var len = eventArr.length ? eventArr.length : 0; + for (var i = startIndex; i < len; i++) { + appendEvent(tableElem, eventArr[i]); + } +} + +// Rewrite HTML of the events from the specified range not including the end index +function rewriteEvents(tableElem, eventArr, startIndex, endIndex) { + for (var i = startIndex; i < endIndex; i++) { + rewriteEvent(eventRows[i], eventArr[i]); + } +} + +// Remove events from the specified range not including the end index +function removeEvents(tableElem, startIndex, endIndex) { + for (var i = startIndex; i < endIndex; i++) { + eventRows[i].remove(); + } + eventRows.splice(startIndex, endIndex - startIndex); +} + +// Clear the event table +function clearEvents(tableElem) { + tableElem.find("tr.event").remove(); + eventRows = []; + lastEvAlt = true; +} + +// Reset the event table to the default state and restart updating +function resetEvents() { + clearEvents($("#tblEvents")); + fullDataAge = 0; + partialDataAge = 0; + lastEvNum = 0; + + restartUpdatingEvents(); } // Request and display events. // callback is a function (success) function updateEvents(full, callback) { + var reqViewDate = viewDate; + var reqCnlFilter = cnlFilter; var startEvNum = full ? 0 : lastEvNum + 1; var reqDataAge = full ? fullDataAge : partialDataAge; - scada.clientAPI.getEvents(viewDate, cnlFilter, dispEventCnt, startEvNum, reqDataAge, + scada.clientAPI.getEvents(reqViewDate, reqCnlFilter, dispEventCnt, startEvNum, reqDataAge, function (success, eventArr, dataAge) { - if (success) { - partialDataAge = dataAge; + if (reqViewDate != viewDate || reqCnlFilter != cnlFilter) { + // do nothing + } + else if (success) { + var tableElem = $("#tblEvents"); + var eventArrLen = eventArr.length ? eventArr.length : 0; + + if (full) { + if (eventArrLen > 0) { + var firstEvNum = eventArr[0].Num; + var firstEvInd = 0; + var eventRowsCnt = eventRows.length; + while (firstEvInd < eventRowsCnt && eventRows[firstEvInd].data("num") < firstEvNum) { + firstEvInd++; + } + + var eventsToMerge = eventRowsCnt - firstEvInd; + var evNumsMatched = eventsToMerge <= eventArrLen; + var eventRowInd = firstEvInd; + var eventArrInd = 0; + while (eventRowInd < eventRowsCnt && eventArrInd < eventArrLen && evNumsMatched) { + evNumsMatched = eventRows[eventRowInd].data("num") == eventArr[eventArrInd].Num; + eventRowInd++; + eventArrInd++; + } - if (eventArr.length) { - lastEvNum = eventArr[eventArr.length - 1].Num; + if (evNumsMatched) { + // merge received events with the existing + removeEvents(tableElem, 0, firstEvInd); + rewriteEvents(tableElem, eventArr, 0, eventsToMerge); + appendEvents(tableElem, eventArr, eventsToMerge); + } else { + // clear and fill again the event table + clearEvents(tableElem); + appendEvents(tableElem, eventArr, 0); + } + } else if (fullDataAge != dataAge) { + // clear the event table + clearEvents(tableElem); + } + } else { + // append new events to the event table + appendEvents(tableElem, eventArr, 0); } - var tableElem = $("#tblEvents"); + partialDataAge = dataAge; - if (full) { + if (full || startEvNum <= 1) { fullDataAge = dataAge; - } else { - for (var event of eventArr) { - appendEvent(tableElem, event); - } } + if (eventArrLen > 0) { + lastEvNum = eventArr[eventArrLen - 1].Num; + } + + scada.tableHeader.update(); callback(true); } else { callback(false); @@ -100,23 +218,44 @@ function updateEvents(full, callback) { }); } -// Start cyclic updating of all displayed or newly added events -function startUpdatingEvents(full) { - updateEvents(full, function (success) { +// Start cyclic updating all displayed events +function startFullUpdatingEvents() { + updateEvents(true, function (success) { if (!success) { notifier.addNotification(phrases.UpdateEventsError, true, notifier.DEF_NOTIF_LIFETIME); } - setTimeout(startUpdatingEvents, full ? arcRefrRate : dataRefrRate, full); + fullUpdateTimeoutID = setTimeout(startFullUpdatingEvents, arcRefrRate); }); } +// Start cyclic updating newly added events +function startPartialUpdatingEvents() { + updateEvents(false, function (success) { + if (!success) { + notifier.addNotification(phrases.UpdateEventsError, true, notifier.DEF_NOTIF_LIFETIME); + } + + partUpdateTimeoutID = setTimeout(startPartialUpdatingEvents, dataRefrRate); + }); +} + +// Restart updating events immediately +function restartUpdatingEvents() { + clearTimeout(fullUpdateTimeoutID); + clearTimeout(partUpdateTimeoutID); + + startFullUpdatingEvents(); + partUpdateTimeoutID = setTimeout(startPartialUpdatingEvents, dataRefrRate); +} + $(document).ready(function () { scada.clientAPI.rootPath = "../../"; styleIOS(); updateLayout(); initViewDate(); loadEventFilter(); + scada.tableHeader.create(); notifier = new scada.Notifier("#divNotif"); notifier.startClearingNotifications(); @@ -147,11 +286,13 @@ $(document).ready(function () { $("#spanAllEventsBtn").click(function () { if (!$(this).hasClass("disabled")) { setEventsByVeiw(false); + resetEvents(); } }); $("#spanEventsByViewBtn").click(function () { setEventsByVeiw(true); + resetEvents(); }); // export events on the button click @@ -160,6 +301,6 @@ $(document).ready(function () { }); // start updating events - setTimeout(startUpdatingEvents, arcRefrRate, true); - setTimeout(startUpdatingEvents, dataRefrRate, false); + startFullUpdatingEvents(); + partUpdateTimeoutID = setTimeout(startPartialUpdatingEvents, dataRefrRate); }); diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js index 2ff302aa2..5efd53dc4 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js @@ -229,6 +229,8 @@ function updateCurData(callback) { function updateHourData(callback) { scada.clientAPI.getHourCnlDataExtByView(viewDate, timeFrom, timeTo, viewID, scada.HourDataModes.INTEGER_HOURS, function (success, hourCnlDataExtArr) { + // TODO: check that hourPeriod and cnlFilter were not changed + if (success) { var hourDataMap = scada.clientAPI.createHourCnlDataExtMap(hourCnlDataExtArr); @@ -258,7 +260,7 @@ function updateHourData(callback) { }); } -// Start cyclic updating of current data +// Start cyclic updating current data function startUpdatingCurData() { updateCurData(function (success) { if (!success) { @@ -269,7 +271,7 @@ function startUpdatingCurData() { }); } -// Start cyclic updating of hourly data +// Start cyclic updating hourly data function startUpdatingHourData() { updateHourData(function (success) { if (!success) { @@ -280,7 +282,7 @@ function startUpdatingHourData() { }); } -// Restart updating of hourly data immediately +// Restart updating hourly data immediately function restartUpdatingHourData() { clearTimeout(updateHourDataTimeoutID); startUpdatingHourData(); diff --git a/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs b/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs index b64466d00..42094fb0e 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs @@ -406,41 +406,44 @@ private Event ConvertEvent(EventTableLight.Event srcEvent) Event destEvent = new Event(); destEvent.Num = srcEvent.Number; destEvent.Time = srcEvent.DateTime.ToLocalizedString(); - destEvent.Text = srcEvent.Descr; destEvent.Ack = srcEvent.Checked ? WebPhrases.EventAck : WebPhrases.EventNotAck; DataAccess dataAccess = AppData.DataAccess; InCnlProps cnlProps = dataAccess.GetCnlProps(srcEvent.CnlNum); destEvent.Obj = cnlProps != null && cnlProps.ObjNum == srcEvent.ObjNum ? - dataAccess.GetObjName(srcEvent.ObjNum) : cnlProps.ObjName; + cnlProps.ObjName : dataAccess.GetObjName(srcEvent.ObjNum); destEvent.KP = cnlProps != null && cnlProps.KPNum == srcEvent.KPNum ? - dataAccess.GetKPName(srcEvent.KPNum) : cnlProps.KPName; + cnlProps.KPName : dataAccess.GetKPName(srcEvent.KPNum); + + double cnlVal = srcEvent.NewCnlVal; + int cnlStat = srcEvent.NewCnlStat; + CnlStatProps cnlStatProps = dataAccess.GetCnlStatProps(cnlStat); if (cnlProps != null) { destEvent.Cnl = cnlProps.CnlName; - destEvent.Sound = cnlProps.EvSound; - - // определение цвета - double cnlVal = srcEvent.NewCnlVal; - int cnlStat = srcEvent.NewCnlStat; - CnlStatProps cnlStatProps = dataAccess.GetCnlStatProps(cnlStat); destEvent.Color = DataFormatter.GetCnlValColor(cnlVal, cnlStat, cnlProps, cnlStatProps); + destEvent.Sound = cnlProps.EvSound; + } - // формирование текста в формате "<статус>: <значение>" - if (destEvent.Text == "") + // формирование текста события + if (string.IsNullOrEmpty(srcEvent.Descr)) + { + // текст в формате "<статус>: <значение>" + StringBuilder sbText = cnlStatProps == null ? + new StringBuilder() : new StringBuilder(cnlStatProps.Name); + if (cnlVal > BaseValues.CnlStatuses.Undefined) { - StringBuilder sbText = cnlStatProps == null ? - new StringBuilder() : new StringBuilder(cnlStatProps.Name); - if (cnlVal > BaseValues.CnlStatuses.Undefined) - { - if (sbText.Length > 0) - sbText.Append(": "); - sbText.Append(DataFormatter.FormatCnlVal(cnlVal, cnlStat, cnlProps, true)); - } - destEvent.Text = sbText.ToString(); + if (sbText.Length > 0) + sbText.Append(": "); + sbText.Append(DataFormatter.FormatCnlVal(cnlVal, cnlStat, cnlProps, true)); } + destEvent.Text = sbText.ToString(); + } + else + { + destEvent.Text = srcEvent.Descr; } return destEvent; @@ -718,7 +721,6 @@ public string GetEvents(int year, int month, int day, string cnlNums, int viewID else { eventsToSend = new Event[0]; - newDataAge = 0; } return JsSerializer.Serialize(new ArcDTO(eventsToSend, newDataAge)); diff --git a/ScadaWeb/ScadaWebCommon5Beta/WebUtils.cs b/ScadaWeb/ScadaWebCommon5Beta/WebUtils.cs index 7e1d0408f..d6ca1bde8 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/WebUtils.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/WebUtils.cs @@ -158,7 +158,7 @@ public static string DictionaryToJs(Localization.Dict dict) /// public static long DateTimeToJs(DateTime dateTime) { - return (long)(dateTime - UnixEpoch).TotalMilliseconds; + return dateTime > UnixEpoch ? (long)(dateTime - UnixEpoch).TotalMilliseconds : 0; } } } \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js b/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js index 9d62f419d..111090f74 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js @@ -138,7 +138,7 @@ scada.clientAPI = { } } catch (ex) { - scada.utils.logServiceFormatError(operation); + scada.utils.logProcessingError(operation, ex.message); if (typeof callback === "function") { callback(false, errorResult); } diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/utils.js b/ScadaWeb/ScadaWebShell5Beta/js/api/utils.js index c95275588..5d6bedc2d 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/utils.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/utils.js @@ -118,21 +118,22 @@ scada.utils = { } }, + // Write information about the failed request to console + logFailedRequest: function (operation, jqXHR) { + console.error(this.getCurTime() + " Request '" + operation + "' failed: " + + jqXHR.status + " (" + jqXHR.statusText + ")"); + }, + // Write information about the internal service error to console logServiceError: function (operation, opt_message) { console.error(this.getCurTime() + " Request '" + operation + "' reports internal service error" + (opt_message ? ": " + opt_message : "")); }, - // Write information about the internal service error to console - logServiceFormatError: function (operation) { - console.error(this.getCurTime() + " Request '" + operation + "' returns data in incorrect format"); - }, - - // Write information about the failed request to console - logFailedRequest: function (operation, jqXHR) { - console.error(this.getCurTime() + " Request '" + operation + "' failed: " + - jqXHR.status + " (" + jqXHR.statusText + ")"); + // Write information about the request processing error to console + logProcessingError: function (operation, opt_message) { + console.error(this.getCurTime() + " Error processing request '" + operation + "'" + + (opt_message ? ": " + opt_message : "")); }, // Check if browser is in fullscreen mode From 3eed373030da482a499dcc0d257de3a744cab3d9 Mon Sep 17 00:00:00 2001 From: 2mik Date: Thu, 23 Jun 2016 13:31:37 +0300 Subject: [PATCH 130/382] PlgTable: dev --- ScadaData/ScadaData/Client/ViewCache.cs | 23 +++++++++++++++---- .../AppCode/Scheme/SchemeViewSpec.cs | 14 +++++++++++ .../PlgTable/AppCode/Table/TableViewSpec.cs | 13 +++++++++++ .../PlgTable/plugins/Table/Events.aspx.cs | 4 ++++ .../PlgTable/plugins/Table/css/events.css | 2 +- .../PlgTable/plugins/Table/css/events.min.css | 2 +- .../PlgTable/plugins/Table/css/table.css | 2 +- .../PlgTable/plugins/Table/css/table.min.css | 2 +- .../plugins/Table/css/tablecommon.css | 2 +- .../plugins/Table/css/tablecommon.less | 2 +- .../plugins/Table/css/tablecommon.min.css | 2 +- .../PlgTable/plugins/Table/js/tablecommon.js | 2 ++ ScadaWeb/ScadaWebCommon5Beta/AppData.cs | 2 +- .../ScadaWebCommon5Beta/Plugins/ViewSpec.cs | 7 ++++++ .../ScadaWebCommon5Beta/Shell/UserContent.cs | 4 ++-- .../ScadaWebCommon5Beta/Shell/UserViews.cs | 20 +++++++++++++++- ScadaWeb/ScadaWebCommon5Beta/UserData.cs | 2 +- .../js/controls/tableheader.js | 15 ++++++++---- 18 files changed, 98 insertions(+), 22 deletions(-) diff --git a/ScadaData/ScadaData/Client/ViewCache.cs b/ScadaData/ScadaData/Client/ViewCache.cs index 70d3c33f6..04125160e 100644 --- a/ScadaData/ScadaData/Client/ViewCache.cs +++ b/ScadaData/ScadaData/Client/ViewCache.cs @@ -99,10 +99,14 @@ public ViewCache(ServerComm serverComm, DataAccess dataAccess, Log log) /// /// Получить представление из кэша или от сервера /// - public T GetView(int viewID, bool throwOnError = false) where T : BaseView + /// Метод используется, если тип предсталения неизвестен на момент компиляции + public BaseView GetView(Type viewType, int viewID, bool throwOnError = false) { try { + if (viewType == null) + throw new ArgumentNullException("viewType"); + // получение представления из кеша DateTime utcNowDT = DateTime.UtcNow; Cache.CacheItem cacheItem = Cache.GetOrCreateItem(viewID, utcNowDT); @@ -110,7 +114,7 @@ public T GetView(int viewID, bool throwOnError = false) where T : BaseView // блокировка доступа только к одному представлению lock (cacheItem) { - T view = null; // представление, которое необходимо получить + BaseView view = null; // представление, которое необходимо получить BaseView viewFromCache = cacheItem.Value; // представление из кеша DateTime viewAge = cacheItem.ValueAge; // время изменения файла представления bool viewIsNotValid = utcNowDT - cacheItem.ValueRefrDT > ViewValidSpan; // представление могло устареть @@ -139,7 +143,7 @@ public T GetView(int viewID, bool throwOnError = false) where T : BaseView else if (newViewAge != viewAge) // файл представления изменён { // создание и загрузка нового представления - view = (T)Activator.CreateInstance(typeof(T)); + view = (BaseView)Activator.CreateInstance(viewType); if (serverComm.ReceiveView(viewProps.FileName, view)) { // обновление представления в кеше @@ -158,8 +162,9 @@ public T GetView(int viewID, bool throwOnError = false) where T : BaseView // использование представления из кеша if (view == null && viewFromCache != null) { - view = viewFromCache as T; - if (view == null) + if (viewFromCache.GetType().Equals(viewType)) + view = viewFromCache; + else throw new ScadaException(Localization.UseRussian ? "Несоответствие типа представления." : "View type mismatch."); @@ -186,6 +191,14 @@ public T GetView(int viewID, bool throwOnError = false) where T : BaseView } } + /// + /// Получить представление из кэша или от сервера + /// + public T GetView(int viewID, bool throwOnError = false) where T : BaseView + { + return GetView(typeof(T), viewID, throwOnError) as T; + } + /// /// Получить уже загруженное представление только из кэша /// diff --git a/ScadaWeb/OpenPlugins/PlgScheme/AppCode/Scheme/SchemeViewSpec.cs b/ScadaWeb/OpenPlugins/PlgScheme/AppCode/Scheme/SchemeViewSpec.cs index 4c8e8e028..6db539422 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/AppCode/Scheme/SchemeViewSpec.cs +++ b/ScadaWeb/OpenPlugins/PlgScheme/AppCode/Scheme/SchemeViewSpec.cs @@ -23,6 +23,9 @@ * Modified : 2016 */ +using Scada.Scheme; +using System; + namespace Scada.Web.Plugins.Scheme { /// @@ -54,6 +57,17 @@ public override string IconUrl } } + /// + /// Получить тип представления + /// + public override Type ViewType + { + get + { + return typeof(SchemeView); + } + } + /// /// Получить ссылку на представление с заданным идентификатором diff --git a/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/TableViewSpec.cs b/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/TableViewSpec.cs index 1ff08ad9b..ebc3b83c0 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/TableViewSpec.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/TableViewSpec.cs @@ -23,6 +23,8 @@ * Modified : 2016 */ +using System; + namespace Scada.Web.Plugins.Table { /// @@ -53,6 +55,17 @@ public override string IconUrl return "~/plugins/Table/images/tableicon.png"; } } + + /// + /// Получить тип представления + /// + public override Type ViewType + { + get + { + return typeof(TableView); + } + } /// diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs index f36df0b0f..f7f45c8d5 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs @@ -67,6 +67,10 @@ protected void Page_Load(object sender, EventArgs e) if (!rights.ViewRight) Response.Redirect(UrlTemplates.NoView); + // загрузка представления в кеш для последующего получения событий + Type viewType = userData.UserViews.GetViewType(viewID); + appData.ViewCache.GetView(viewType, viewID, false); + // запретить отображение всех событий, если нет соответствующих прав if (!userData.UserRights.ViewAllRight) spanAllEventsBtn.Attributes["class"] += " disabled"; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css index ba887f548..75f14eaf8 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css @@ -116,7 +116,7 @@ white-space: nowrap; } #divTblWrapper tr.hdr td { - border: 1px solid #888; + border-color: #888; font-weight: 600; padding: 2px 7px; text-align: center; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css index 3499eaeaf..289104eba 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.visible{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border:1px solid #888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divToolbar .tool-btn.selected{background-color:#0073aa;color:#fff;}#divToolbar .tool-btn.disabled{color:#ccc;cursor:default;}#tblEvents{width:100%;}#tblEvents td.num,#tblEvents td.time,#tblEvents td.ack{text-align:center;}#tblEvents td.num,#tblEvents td.ack{width:5%;}#tblEvents td.time{width:10%;}#tblEvents td.obj,#tblEvents td.dev,#tblEvents td.cnl{width:15%;}#tblEvents td.text{width:35%;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.visible{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divToolbar .tool-btn.selected{background-color:#0073aa;color:#fff;}#divToolbar .tool-btn.disabled{color:#ccc;cursor:default;}#tblEvents{width:100%;}#tblEvents td.num,#tblEvents td.time,#tblEvents td.ack{text-align:center;}#tblEvents td.num,#tblEvents td.ack{width:5%;}#tblEvents td.time{width:10%;}#tblEvents td.obj,#tblEvents td.dev,#tblEvents td.cnl{width:15%;}#tblEvents td.text{width:35%;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css index 41302a4af..709502482 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css @@ -116,7 +116,7 @@ white-space: nowrap; } #divTblWrapper tr.hdr td { - border: 1px solid #888; + border-color: #888; font-weight: 600; padding: 2px 7px; text-align: center; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css index 0221276d4..b67d0c116 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.visible{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border:1px solid #888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divTblWrapper td.cap{padding:2px 5px;}#divTblWrapper td.cap img{width:16px;height:16px;margin:0 5px 0 0;border:none;}#divTblWrapper td.cap a{height:18px;padding-right:5px;color:#0073aa;display:inline-block;line-height:18px;text-decoration:none;}#divTblWrapper td.cap a:hover{color:#00a0d2;}#divTblWrapper td.cap span.cmd{display:inline-block;width:18px;height:18px;cursor:pointer;color:#a00;font-size:15px;line-height:18px;text-align:center;}#divTblWrapper td.cap span.cmd:hover{color:#f00;}#divTblWrapper td.cap span.cmd::before{content:"";font-family:FontAwesome;}#divTblWrapper td.cap span.hint{background-color:#fff;border:1px solid #888;border-radius:5px;box-shadow:2px 2px 5px #aaa;color:#444;display:none;line-height:normal;padding:5px;position:fixed;z-index:10;}#divTblWrapper td.cap span.hint.visible{display:inline-block;}#divTblWrapper td.cur,#divTblWrapper td.hour{text-align:center;}#divTblWrapper td.hidden{display:none;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.visible{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divTblWrapper td.cap{padding:2px 5px;}#divTblWrapper td.cap img{width:16px;height:16px;margin:0 5px 0 0;border:none;}#divTblWrapper td.cap a{height:18px;padding-right:5px;color:#0073aa;display:inline-block;line-height:18px;text-decoration:none;}#divTblWrapper td.cap a:hover{color:#00a0d2;}#divTblWrapper td.cap span.cmd{display:inline-block;width:18px;height:18px;cursor:pointer;color:#a00;font-size:15px;line-height:18px;text-align:center;}#divTblWrapper td.cap span.cmd:hover{color:#f00;}#divTblWrapper td.cap span.cmd::before{content:"";font-family:FontAwesome;}#divTblWrapper td.cap span.hint{background-color:#fff;border:1px solid #888;border-radius:5px;box-shadow:2px 2px 5px #aaa;color:#444;display:none;line-height:normal;padding:5px;position:fixed;z-index:10;}#divTblWrapper td.cap span.hint.visible{display:inline-block;}#divTblWrapper td.cur,#divTblWrapper td.hour{text-align:center;}#divTblWrapper td.hidden{display:none;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css index ba035858a..a753eb301 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css @@ -116,7 +116,7 @@ white-space: nowrap; } #divTblWrapper tr.hdr td { - border: 1px solid #888; + border-color: #888; font-weight: 600; padding: 2px 7px; text-align: center; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less index 77d072581..bdf364ac8 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less @@ -168,7 +168,7 @@ body { } #divTblWrapper tr.hdr td { - border: 1px solid @cell-border-color-hdr; + border-color: @cell-border-color-hdr; font-weight: 600; padding: 2px 7px; text-align: center; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css index 6396501e6..0fc230b15 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.visible{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border:1px solid #888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.visible{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/tablecommon.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/tablecommon.js index 8cd989f3c..f1884b0c0 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/tablecommon.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/tablecommon.js @@ -49,6 +49,8 @@ function updateLayout() { .outerWidth(windowWidth) .outerHeight($(window).height() - notifHeight - toolbarHeight); divToolbar.css("top", notifHeight); + + scada.tableHeader.update(); } // Set current view date to the initial value diff --git a/ScadaWeb/ScadaWebCommon5Beta/AppData.cs b/ScadaWeb/ScadaWebCommon5Beta/AppData.cs index b1962de33..c4830358e 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/AppData.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/AppData.cs @@ -108,7 +108,7 @@ private AppData() internal ViewSettings ViewSettings { get; private set; } /// - /// Получить список плагинов + /// Получить список спецификаций плагинов /// /// Объект создаётся заново при изменении файла настроек веб-приложения internal List PluginSpecs { get; private set; } diff --git a/ScadaWeb/ScadaWebCommon5Beta/Plugins/ViewSpec.cs b/ScadaWeb/ScadaWebCommon5Beta/Plugins/ViewSpec.cs index aedb6a091..2582590d4 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Plugins/ViewSpec.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Plugins/ViewSpec.cs @@ -23,6 +23,8 @@ * Modified : 2016 */ +using System; + namespace Scada.Web.Plugins { /// @@ -41,6 +43,11 @@ public abstract class ViewSpec /// public abstract string IconUrl { get; } + /// + /// Получить тип представления + /// + public abstract Type ViewType { get; } + /// /// Получить ссылку на представление с заданным идентификатором diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/UserContent.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/UserContent.cs index c02ad1b01..7db5c7c94 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/UserContent.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/UserContent.cs @@ -61,7 +61,7 @@ public UserContent(Log log) this.log = log; Reports = new List(); - DataWindows = new List(); + DataWindows = new List(); } @@ -73,7 +73,7 @@ public UserContent(Log log) /// /// Получить окна данных, доступные пользователю /// - public List DataWindows { get; protected set; } + public List DataWindows { get; protected set; } /// diff --git a/ScadaWeb/ScadaWebCommon5Beta/Shell/UserViews.cs b/ScadaWeb/ScadaWebCommon5Beta/Shell/UserViews.cs index 7242627ac..601e747d0 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/Shell/UserViews.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/Shell/UserViews.cs @@ -46,6 +46,10 @@ public class UserViews /// Ссылки на представления, ключ - ид. представления /// protected readonly Dictionary viewUrls; + /// + /// Типы представлений, ключ - ид. представления + /// + protected readonly Dictionary viewTypes; /// /// Права пользователя @@ -78,12 +82,13 @@ public UserViews(Log log) this.log = log; viewUrls = new Dictionary(); + viewTypes = new Dictionary(); userRights = null; viewSpecs = null; dataAccess = null; - ViewNodes = new List(); + ViewNodes = new List(); } @@ -125,6 +130,10 @@ protected void CreateViewNodes(List destViewNodes, List 0 && viewSpec != null) + viewTypes[viewID] = viewSpec.ViewType; } } @@ -216,5 +225,14 @@ public bool GetFirstView(out int viewID, out string viewUrl) return false; } } + + /// + /// Получить ссылку на тип представления с заданным идентификатором + /// + public Type GetViewType(int viewID) + { + Type viewType; + return viewTypes.TryGetValue(viewID, out viewType) ? viewType : null; + } } } \ No newline at end of file diff --git a/ScadaWeb/ScadaWebCommon5Beta/UserData.cs b/ScadaWeb/ScadaWebCommon5Beta/UserData.cs index 2b32395b7..778e780df 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/UserData.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/UserData.cs @@ -138,7 +138,7 @@ private UserData() public ViewSettings ViewSettings { get; private set; } /// - /// Получить ссылку на список плагинов + /// Получить ссылку на список спецификаций плагинов /// public List PluginSpecs { get; private set; } diff --git a/ScadaWeb/ScadaWebShell5Beta/js/controls/tableheader.js b/ScadaWeb/ScadaWebShell5Beta/js/controls/tableheader.js index 5c0a70687..bb9515eb9 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/controls/tableheader.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/controls/tableheader.js @@ -25,9 +25,11 @@ scada.tableHeader = { if (origCell.css("display") != "none") { var origCellSpan = origCell.children("span"); - var cellWidth = origCellSpan.width(); // jQuery may round fractional part - origCellSpan.width(cellWidth); - $(this).find("span").width(cellWidth); + if (origCellSpan.length > 0) { + var cellWidth = origCellSpan[0].getBoundingClientRect().width; // fractional value is required + $(this).find("span").width(cellWidth); + $(this).css("width", cellWidth); + } } }); }, @@ -74,9 +76,12 @@ scada.tableHeader = { $(".table-wrapper").each(function () { var wrapper = $(this); - var origHeader = $(this).find("tr.orig-table-header:first"); - var fixedHeader = $(this).find("tr.fixed-table-header:first"); + var table = wrapper.children("table"); + var origHeader = table.find("tr.orig-table-header:first"); + var fixedHeader = table.find("tr.fixed-table-header:first"); + fixedHeader.detach(); thisObj._updateHeaderCellWidths(origHeader, fixedHeader); + table.append(fixedHeader); // make sure that the fixed header is the last row }); } } \ No newline at end of file From 00643e27fdb84b8a049cb2dd23f4b805c863513f Mon Sep 17 00:00:00 2001 From: 2mik Date: Fri, 24 Jun 2016 14:48:10 +0300 Subject: [PATCH 131/382] PlgTable: events --- ScadaData/ScadaData/Client/DataCache.cs | 3 +- .../PlgTable/plugins/Table/Events.aspx | 18 +++-- .../plugins/Table/Events.aspx.designer.cs | 81 +++++++++++++++++++ .../PlgTable/plugins/Table/css/events.css | 9 ++- .../PlgTable/plugins/Table/css/events.less | 5 ++ .../PlgTable/plugins/Table/css/events.min.css | 2 +- .../PlgTable/plugins/Table/css/table.css | 10 +-- .../PlgTable/plugins/Table/css/table.less | 6 +- .../PlgTable/plugins/Table/css/table.min.css | 2 +- .../plugins/Table/css/tablecommon.css | 5 +- .../plugins/Table/css/tablecommon.less | 6 +- .../plugins/Table/css/tablecommon.min.css | 2 +- .../PlgTable/plugins/Table/js/events.js | 19 ++++- .../PlgTable/plugins/Table/js/table.js | 4 +- .../PlgTable/plugins/Table/js/tablecommon.js | 2 +- .../js/controls/notifier.js | 4 +- .../js/controls/splitter.js | 2 +- .../js/controls/treeview.js | 4 +- ScadaWeb/ScadaWebShell5Beta/js/view.js | 15 +++- 19 files changed, 163 insertions(+), 36 deletions(-) diff --git a/ScadaData/ScadaData/Client/DataCache.cs b/ScadaData/ScadaData/Client/DataCache.cs index f5271189b..b13122f3a 100644 --- a/ScadaData/ScadaData/Client/DataCache.cs +++ b/ScadaData/ScadaData/Client/DataCache.cs @@ -404,7 +404,8 @@ protected void FillCnlStatProps() for (int i = 0; i < statusCnt; i++) { DataRow row = tblEvType.Rows[i]; - CnlStatProps cnlStatProps = new CnlStatProps((int)row["CnlStatus"]) { Color = (string)row["Color"] }; + CnlStatProps cnlStatProps = new CnlStatProps((int)row["CnlStatus"]) { + Color = (string)row["Color"], Name = (string)row["Name"] }; newCnlStatProps.Add(cnlStatProps.Status, cnlStatProps); } diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx index dcd517707..c7519ed88 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx @@ -43,19 +43,21 @@ id="spanDateChangedBtn" class="tool-btn">DateChanged
-
+ + +
diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.designer.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.designer.cs index 8b06d270a..b375e9dfa 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.designer.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.designer.cs @@ -38,5 +38,86 @@ public partial class WFrmEvents { /// To modify move field declaration from designer file to code-behind file. /// protected global::System.Web.UI.HtmlControls.HtmlGenericControl spanAllEventsBtn; + + /// + /// lblNumCol control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblNumCol; + + /// + /// lblTimeCol control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblTimeCol; + + /// + /// lblObjCol control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblObjCol; + + /// + /// lblDevCol control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblDevCol; + + /// + /// lblCnlCol control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblCnlCol; + + /// + /// lblTextCol control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblTextCol; + + /// + /// lblAckCol control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblAckCol; + + /// + /// lblNoEvents control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblNoEvents; + + /// + /// lblLoading control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblLoading; } } diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css index 75f14eaf8..3297f2de9 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css @@ -7,6 +7,9 @@ font-size: 12px; overflow: hidden; } +.hidden { + display: none; +} /* Toolbar */ #divToolbar { position: fixed; @@ -80,7 +83,7 @@ display: none; vertical-align: top; } -#divDebugTools.visible { +#divDebugTools.show { display: inline-block; } /* Table */ @@ -156,4 +159,8 @@ } #tblEvents td.text { width: 35%; +} +#divLoading, +#divNoEvents { + padding: 5px 10px; } \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.less b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.less index a8e886ed6..964cab197 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.less +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.less @@ -42,3 +42,8 @@ #tblEvents td.text { width: 35%; } + +#divLoading, +#divNoEvents { + padding: 5px 10px; +} diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css index 289104eba..1b01fe8e4 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.visible{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divToolbar .tool-btn.selected{background-color:#0073aa;color:#fff;}#divToolbar .tool-btn.disabled{color:#ccc;cursor:default;}#tblEvents{width:100%;}#tblEvents td.num,#tblEvents td.time,#tblEvents td.ack{text-align:center;}#tblEvents td.num,#tblEvents td.ack{width:5%;}#tblEvents td.time{width:10%;}#tblEvents td.obj,#tblEvents td.dev,#tblEvents td.cnl{width:15%;}#tblEvents td.text{width:35%;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}.hidden{display:none;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divToolbar .tool-btn.selected{background-color:#0073aa;color:#fff;}#divToolbar .tool-btn.disabled{color:#ccc;cursor:default;}#tblEvents{width:100%;}#tblEvents td.num,#tblEvents td.time,#tblEvents td.ack{text-align:center;}#tblEvents td.num,#tblEvents td.ack{width:5%;}#tblEvents td.time{width:10%;}#tblEvents td.obj,#tblEvents td.dev,#tblEvents td.cnl{width:15%;}#tblEvents td.text{width:35%;}#divLoading,#divNoEvents{padding:5px 10px;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css index 709502482..6c588a4df 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css @@ -7,6 +7,9 @@ font-size: 12px; overflow: hidden; } +.hidden { + display: none; +} /* Toolbar */ #divToolbar { position: fixed; @@ -80,7 +83,7 @@ display: none; vertical-align: top; } -#divDebugTools.visible { +#divDebugTools.show { display: inline-block; } /* Table */ @@ -176,13 +179,10 @@ z-index: 10; /*above the fixed table header*/ } -#divTblWrapper td.cap span.hint.visible { +#divTblWrapper td.cap span.hint.show { display: inline-block; } #divTblWrapper td.cur, #divTblWrapper td.hour { text-align: center; -} -#divTblWrapper td.hidden { - display: none; } \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.less b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.less index e49a27c94..b2a665b78 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.less +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.less @@ -65,15 +65,11 @@ z-index: 10; /*above the fixed table header*/ } -#divTblWrapper td.cap span.hint.visible { +#divTblWrapper td.cap span.hint.show { display: inline-block; } #divTblWrapper td.cur, #divTblWrapper td.hour { text-align: center; -} - -#divTblWrapper td.hidden { - display: none; } \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css index b67d0c116..ce8b25996 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.visible{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divTblWrapper td.cap{padding:2px 5px;}#divTblWrapper td.cap img{width:16px;height:16px;margin:0 5px 0 0;border:none;}#divTblWrapper td.cap a{height:18px;padding-right:5px;color:#0073aa;display:inline-block;line-height:18px;text-decoration:none;}#divTblWrapper td.cap a:hover{color:#00a0d2;}#divTblWrapper td.cap span.cmd{display:inline-block;width:18px;height:18px;cursor:pointer;color:#a00;font-size:15px;line-height:18px;text-align:center;}#divTblWrapper td.cap span.cmd:hover{color:#f00;}#divTblWrapper td.cap span.cmd::before{content:"";font-family:FontAwesome;}#divTblWrapper td.cap span.hint{background-color:#fff;border:1px solid #888;border-radius:5px;box-shadow:2px 2px 5px #aaa;color:#444;display:none;line-height:normal;padding:5px;position:fixed;z-index:10;}#divTblWrapper td.cap span.hint.visible{display:inline-block;}#divTblWrapper td.cur,#divTblWrapper td.hour{text-align:center;}#divTblWrapper td.hidden{display:none;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}.hidden{display:none;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divTblWrapper td.cap{padding:2px 5px;}#divTblWrapper td.cap img{width:16px;height:16px;margin:0 5px 0 0;border:none;}#divTblWrapper td.cap a{height:18px;padding-right:5px;color:#0073aa;display:inline-block;line-height:18px;text-decoration:none;}#divTblWrapper td.cap a:hover{color:#00a0d2;}#divTblWrapper td.cap span.cmd{display:inline-block;width:18px;height:18px;cursor:pointer;color:#a00;font-size:15px;line-height:18px;text-align:center;}#divTblWrapper td.cap span.cmd:hover{color:#f00;}#divTblWrapper td.cap span.cmd::before{content:"";font-family:FontAwesome;}#divTblWrapper td.cap span.hint{background-color:#fff;border:1px solid #888;border-radius:5px;box-shadow:2px 2px 5px #aaa;color:#444;display:none;line-height:normal;padding:5px;position:fixed;z-index:10;}#divTblWrapper td.cap span.hint.show{display:inline-block;}#divTblWrapper td.cur,#divTblWrapper td.hour{text-align:center;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css index a753eb301..4073485e0 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css @@ -7,6 +7,9 @@ font-size: 12px; overflow: hidden; } +.hidden { + display: none; +} /* Toolbar */ #divToolbar { position: fixed; @@ -80,7 +83,7 @@ display: none; vertical-align: top; } -#divDebugTools.visible { +#divDebugTools.show { display: inline-block; } /* Table */ diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less index bdf364ac8..f46c851f8 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less @@ -37,6 +37,10 @@ body { overflow: hidden; } +.hidden { + display: none; +} + /* Toolbar */ #divToolbar { @@ -122,7 +126,7 @@ body { vertical-align: top; } -#divDebugTools.visible { +#divDebugTools.show { display: inline-block; } diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css index 0fc230b15..4ded0db47 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.visible{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}.hidden{display:none;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js index 3c573e537..2a1bfae1f 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js @@ -137,6 +137,10 @@ function clearEvents(tableElem) { // Reset the event table to the default state and restart updating function resetEvents() { + $("#divTblWrapper").addClass("hidden"); + $("#divNoEvents").addClass("hidden"); + $("#divLoading").removeClass("hidden"); + clearEvents($("#tblEvents")); fullDataAge = 0; partialDataAge = 0; @@ -145,6 +149,19 @@ function resetEvents() { restartUpdatingEvents(); } +// Set elements visibility after loading events +function afterLoading() { + $("#divLoading").addClass("hidden"); + + if ($("#tblEvents tr.event:first").length > 0) { + $("#divTblWrapper").removeClass("hidden"); + setTimeout(scada.tableHeader.update.bind(scada.tableHeader), 0); + } else { + $("#divTblWrapper").addClass("hidden"); + $("#divNoEvents").removeClass("hidden"); + } +} + // Request and display events. // callback is a function (success) function updateEvents(full, callback) { @@ -210,7 +227,7 @@ function updateEvents(full, callback) { lastEvNum = eventArr[eventArrLen - 1].Num; } - scada.tableHeader.update(); + afterLoading(); callback(true); } else { callback(false); diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js index 5efd53dc4..88f89778c 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js @@ -177,7 +177,7 @@ function showHintByIcon(imgIcon) { "left": iconOffset.left + imgIcon.outerWidth(true), "top": hintTop }) - .addClass("visible"); + .addClass("show"); } // Hide hint associated with the icon @@ -187,7 +187,7 @@ function hideHintByIcon(imgIcon) { // Hide hint associated with the icon function hideHint(spanHint) { - spanHint.removeClass("visible"); + spanHint.removeClass("show"); } // Display the given data in the cell diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/tablecommon.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/tablecommon.js index f1884b0c0..d9dc49379 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/tablecommon.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/tablecommon.js @@ -113,7 +113,7 @@ function parseViewDate(dateStr, changeViewDateFunc) { // Initialize debug tools function initDebugTools() { - $("#divDebugTools").addClass("visible"); + $("#divDebugTools").addClass("show"); $(window).on( scada.EventTypes.VIEW_TITLE_CHANGED + " " + diff --git a/ScadaWeb/ScadaWebShell5Beta/js/controls/notifier.js b/ScadaWeb/ScadaWebShell5Beta/js/controls/notifier.js index f1f9fb66a..313097eb0 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/controls/notifier.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/controls/notifier.js @@ -45,7 +45,7 @@ scada.Notifier.prototype.addNotification = function (messageHtml, error, lifetim } if (lifetime) { - divMessage.attr("data-expires", Date.now() + lifetime); + divMessage.data("expires", Date.now() + lifetime); } this._notifier @@ -65,7 +65,7 @@ scada.Notifier.prototype.clearOutdatedNotifications = function () { var removed = false; $.each(messages, function () { - var expires = $(this).attr("data-expires"); + var expires = $(this).data("expires"); if (expires < nowMs) { $(this).remove(); removed = true; diff --git a/ScadaWeb/ScadaWebShell5Beta/js/controls/splitter.js b/ScadaWeb/ScadaWebShell5Beta/js/controls/splitter.js index f751e9972..cc6479b5e 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/controls/splitter.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/controls/splitter.js @@ -143,7 +143,7 @@ scada.SplitterBulk.prototype.bindEvents = function () { $("body").on("mousemove touchmove", function (event) { if (event.type == "touchmove") { - $(this).off("mousedown"); + $(this).off("mousemove"); event = event.originalEvent.touches[0]; } diff --git a/ScadaWeb/ScadaWebShell5Beta/js/controls/treeview.js b/ScadaWeb/ScadaWebShell5Beta/js/controls/treeview.js index 02c5f9fab..6dd775b66 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/controls/treeview.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/controls/treeview.js @@ -46,7 +46,7 @@ scada.treeView = { // set width of indents according to their level allNodes.each(function () { - var level = $(this).attr("data-level"); + var level = $(this).data("level"); $(this).find(".indent").width(oneIndent * level); }); @@ -64,7 +64,7 @@ scada.treeView = { event.preventDefault(); var node = $(this); - var script = node.attr("data-script"); + var script = node.data("script"); var expander = node.find(".expander"); var href = node.attr("href"); diff --git a/ScadaWeb/ScadaWebShell5Beta/js/view.js b/ScadaWeb/ScadaWebShell5Beta/js/view.js index 5cea858da..8ea22079c 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/view.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/view.js @@ -40,6 +40,9 @@ scada.view = { }, + // Events data window code + _EVENTS_WND_CODE: "Events", + // Page title just after loading initialPageTitle: "", @@ -77,12 +80,21 @@ scada.view = { if (activeDataWindow) { $("#divBottomTabsContainer .tab").each(function () { - var tabUrl = $(this).attr("data-url"); + var tabUrl = $(this).data("url"); if (activeDataWindow == tabUrl) { thisView.activateDataWindow($(this)); return false; // break the loop } }); + } else { + // activate events window if presented + $("#divBottomTabsContainer .tab").each(function () { + var code = $(this).data("code"); + if (code == thisView._EVENTS_WND_CODE) { + thisView.activateDataWindow($(this)); + return false; + } + }); } }, @@ -127,7 +139,6 @@ scada.view = { $("#divDataWindow").css("display", "block"); $("#divCollapseDataWindowBtn").css("display", "inline-block"); - this._dataWindow.load(divClickedTab.attr("data-url"), divClickedTab.attr("data-depends") == "true"); this._saveActiveDataWindow(); this.updateLayout(); }, From d21f21921ceed8318bc2174615554e2c1ec4a617 Mon Sep 17 00:00:00 2001 From: 2mik Date: Fri, 24 Jun 2016 17:08:38 +0300 Subject: [PATCH 132/382] PlgTable: event ack --- .../plugins/Scheme/js/schemerender.js | 4 +- .../PlgTable/plugins/Table/Events.aspx | 3 ++ .../PlgTable/plugins/Table/Events.aspx.cs | 10 +++-- .../PlgTable/plugins/Table/Table.aspx.cs | 2 +- .../PlgTable/plugins/Table/css/events.css | 7 ++++ .../PlgTable/plugins/Table/css/events.min.css | 2 +- .../PlgTable/plugins/Table/css/table.css | 12 +++--- .../PlgTable/plugins/Table/css/table.less | 6 --- .../PlgTable/plugins/Table/css/table.min.css | 2 +- .../plugins/Table/css/tablecommon.css | 7 ++++ .../plugins/Table/css/tablecommon.less | 9 ++++ .../plugins/Table/css/tablecommon.min.css | 2 +- .../PlgTable/plugins/Table/js/events.js | 41 ++++++++++++++++--- .../PlgTable/plugins/Table/js/tablecommon.js | 4 +- .../plugins/Table/lang/PlgTable.en-GB.xml | 9 ++++ .../plugins/Table/lang/PlgTable.ru-RU.xml | 9 ++++ ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js | 1 + ScadaWeb/ScadaWebShell5Beta/js/view.js | 1 + 18 files changed, 103 insertions(+), 28 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js index 1e085c871..e2b338109 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js @@ -314,7 +314,7 @@ scada.scheme.ElementRenderer.prototype.bindAction = function (jqObj, elem) { if (dialogs) { dialogs.showChart(scada.scheme.viewHub.currentViewID, props.InCnlNum); } else { - console.warn("Unable to show chart because viewHub.dialogs is undefined"); + console.warn("Dialogs object is undefined"); } } break; @@ -323,7 +323,7 @@ scada.scheme.ElementRenderer.prototype.bindAction = function (jqObj, elem) { if (dialogs) { dialogs.showCmd(scada.scheme.viewHub.currentViewID, props.CtrlCnlNum); } else { - console.warn("Unable to show command dialog because viewHub.dialogs is undefined"); + console.warn("Dialogs object is undefined"); } } break; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx index c7519ed88..e64692f87 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx @@ -27,6 +27,9 @@ var today = <%= today %>; var locale = "<%= Scada.Localization.Culture.Name %>"; var dispEventCnt = <%= dispEventCnt %>; + var viewAllRight = <%= viewAllRight ? "true" : "false" %>; + var controlAllRight = <%= controlAllRight ? "true" : "false" %>; + var controlViewRight = <%= controlViewRight ? "true" : "false" %>; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs index f7f45c8d5..c2c055f66 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx.cs @@ -44,6 +44,9 @@ public partial class WFrmEvents : System.Web.UI.Page protected string phrases; // локализованные фразы protected string today; // текущая дата protected int dispEventCnt; // количество отображаемых событий + protected bool viewAllRight; // право на просмотр всех данных + protected bool controlAllRight; // право на любое управление + protected bool controlViewRight; // право на управление представлением protected void Page_Load(object sender, EventArgs e) { @@ -71,14 +74,13 @@ protected void Page_Load(object sender, EventArgs e) Type viewType = userData.UserViews.GetViewType(viewID); appData.ViewCache.GetView(viewType, viewID, false); - // запретить отображение всех событий, если нет соответствующих прав - if (!userData.UserRights.ViewAllRight) - spanAllEventsBtn.Attributes["class"] += " disabled"; - // подготовка данных для вывода на веб-страницу dataRefrRate = userData.WebSettings.DataRefrRate; arcRefrRate = userData.WebSettings.ArcRefrRate; dispEventCnt = userData.WebSettings.DispEventCnt; + viewAllRight = userData.UserRights.ViewAllRight; + controlAllRight = userData.UserRights.ControlAllRight; + controlViewRight = rights.ControlRight; Localization.Dict dict; Localization.Dictionaries.TryGetValue("Scada.Web.Plugins.Table.WFrmEvents.Js", out dict); diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs index 45d7f86cf..65aa500ef 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs @@ -177,7 +177,7 @@ private string GenerateTableViewHtml(TableView tableView, bool cmdEnabled, int t string iconFileName = cnlProps == null || cnlProps.IconFileName == "" ? DefQuantityIcon : cnlProps.IconFileName; sbCapHtml.Append("") - .Append("
").Append(caption).Append(""); + .Append("").Append(caption).Append(""); // команда if (ctrlCnlNum > 0) diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css index 3297f2de9..691585a69 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css @@ -124,6 +124,13 @@ padding: 2px 7px; text-align: center; } +#divTblWrapper td a { + color: #0073aa; + text-decoration: none; +} +#divTblWrapper td a:hover { + color: #00a0d2; +} #divTblWrapper td a, #divTblWrapper td span, #divTblWrapper td img { diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css index 1b01fe8e4..2404521ec 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}.hidden{display:none;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divToolbar .tool-btn.selected{background-color:#0073aa;color:#fff;}#divToolbar .tool-btn.disabled{color:#ccc;cursor:default;}#tblEvents{width:100%;}#tblEvents td.num,#tblEvents td.time,#tblEvents td.ack{text-align:center;}#tblEvents td.num,#tblEvents td.ack{width:5%;}#tblEvents td.time{width:10%;}#tblEvents td.obj,#tblEvents td.dev,#tblEvents td.cnl{width:15%;}#tblEvents td.text{width:35%;}#divLoading,#divNoEvents{padding:5px 10px;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}.hidden{display:none;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a{color:#0073aa;text-decoration:none;}#divTblWrapper td a:hover{color:#00a0d2;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divToolbar .tool-btn.selected{background-color:#0073aa;color:#fff;}#divToolbar .tool-btn.disabled{color:#ccc;cursor:default;}#tblEvents{width:100%;}#tblEvents td.num,#tblEvents td.time,#tblEvents td.ack{text-align:center;}#tblEvents td.num,#tblEvents td.ack{width:5%;}#tblEvents td.time{width:10%;}#tblEvents td.obj,#tblEvents td.dev,#tblEvents td.cnl{width:15%;}#tblEvents td.text{width:35%;}#divLoading,#divNoEvents{padding:5px 10px;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css index 6c588a4df..3bdebce17 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css @@ -124,6 +124,13 @@ padding: 2px 7px; text-align: center; } +#divTblWrapper td a { + color: #0073aa; + text-decoration: none; +} +#divTblWrapper td a:hover { + color: #00a0d2; +} #divTblWrapper td a, #divTblWrapper td span, #divTblWrapper td img { @@ -141,13 +148,8 @@ #divTblWrapper td.cap a { height: 18px; padding-right: 5px; - color: #0073aa; display: inline-block; line-height: 18px; - text-decoration: none; -} -#divTblWrapper td.cap a:hover { - color: #00a0d2; } #divTblWrapper td.cap span.cmd { display: inline-block; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.less b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.less index b2a665b78..6926a625c 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.less +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.less @@ -22,14 +22,8 @@ #divTblWrapper td.cap a { height: @cell-height; padding-right: 5px; - color: @link-fore-color; display: inline-block; line-height: @cell-height; - text-decoration: none; -} - -#divTblWrapper td.cap a:hover { - color: @link-hover-fore-color; } #divTblWrapper td.cap span.cmd { diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css index ce8b25996..1c871ff70 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}.hidden{display:none;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divTblWrapper td.cap{padding:2px 5px;}#divTblWrapper td.cap img{width:16px;height:16px;margin:0 5px 0 0;border:none;}#divTblWrapper td.cap a{height:18px;padding-right:5px;color:#0073aa;display:inline-block;line-height:18px;text-decoration:none;}#divTblWrapper td.cap a:hover{color:#00a0d2;}#divTblWrapper td.cap span.cmd{display:inline-block;width:18px;height:18px;cursor:pointer;color:#a00;font-size:15px;line-height:18px;text-align:center;}#divTblWrapper td.cap span.cmd:hover{color:#f00;}#divTblWrapper td.cap span.cmd::before{content:"";font-family:FontAwesome;}#divTblWrapper td.cap span.hint{background-color:#fff;border:1px solid #888;border-radius:5px;box-shadow:2px 2px 5px #aaa;color:#444;display:none;line-height:normal;padding:5px;position:fixed;z-index:10;}#divTblWrapper td.cap span.hint.show{display:inline-block;}#divTblWrapper td.cur,#divTblWrapper td.hour{text-align:center;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}.hidden{display:none;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a{color:#0073aa;text-decoration:none;}#divTblWrapper td a:hover{color:#00a0d2;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divTblWrapper td.cap{padding:2px 5px;}#divTblWrapper td.cap img{width:16px;height:16px;margin:0 5px 0 0;border:none;}#divTblWrapper td.cap a{height:18px;padding-right:5px;display:inline-block;line-height:18px;}#divTblWrapper td.cap span.cmd{display:inline-block;width:18px;height:18px;cursor:pointer;color:#a00;font-size:15px;line-height:18px;text-align:center;}#divTblWrapper td.cap span.cmd:hover{color:#f00;}#divTblWrapper td.cap span.cmd::before{content:"";font-family:FontAwesome;}#divTblWrapper td.cap span.hint{background-color:#fff;border:1px solid #888;border-radius:5px;box-shadow:2px 2px 5px #aaa;color:#444;display:none;line-height:normal;padding:5px;position:fixed;z-index:10;}#divTblWrapper td.cap span.hint.show{display:inline-block;}#divTblWrapper td.cur,#divTblWrapper td.hour{text-align:center;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css index 4073485e0..4a1fc4823 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css @@ -124,6 +124,13 @@ padding: 2px 7px; text-align: center; } +#divTblWrapper td a { + color: #0073aa; + text-decoration: none; +} +#divTblWrapper td a:hover { + color: #00a0d2; +} #divTblWrapper td a, #divTblWrapper td span, #divTblWrapper td img { diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less index f46c851f8..951607345 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less @@ -178,6 +178,15 @@ body { text-align: center; } +#divTblWrapper td a { + color: @link-fore-color; + text-decoration: none; +} + +#divTblWrapper td a:hover { + color: @link-hover-fore-color; +} + #divTblWrapper td a, #divTblWrapper td span, #divTblWrapper td img { diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css index 4ded0db47..db2fbe108 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}.hidden{display:none;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}.hidden{display:none;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a{color:#0073aa;text-decoration:none;}#divTblWrapper td a:hover{color:#00a0d2;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js index 2a1bfae1f..62b31c962 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js @@ -18,8 +18,15 @@ var fullUpdateTimeoutID = null; // Timeout ID of the partial events updating timer var partUpdateTimeoutID = null; -// Displayed event count. Must be defined in Events.aspx +// The variables below must be defined in Events.aspx +// Displayed event count var dispEventCnt = dispEventCnt || 0; +// Right to view all data +var viewAllRight = viewAllRight || false; +// Right to any control +var controlAllRight = controlAllRight || false; +// Right to control the view +var controlViewRight = controlViewRight || false; // Set current view date and process the consequent changes function changeViewDate(date, notify) { @@ -31,14 +38,21 @@ function changeViewDate(date, notify) { } } +// Init the page controls +function initControls() { + if (!viewAllRight) { + $("#spanAllEventsBtn").addClass("disabled"); + } +} + // Enable or disable events by view filter function setEventsByVeiw(val) { - eventsByView = val; + eventsByView = val || !viewAllRight; cnlFilter = new scada.CnlFilter(); - cnlFilter.viewID = val ? viewID : 0; + cnlFilter.viewID = eventsByView ? viewID : 0; saveEventFilter(); - if (val) { + if (eventsByView) { $("#spanAllEventsBtn").removeClass("selected"); $("#spanEventsByViewBtn").addClass("selected"); } else { @@ -58,8 +72,22 @@ function saveEventFilter() { scada.utils.setCookie("Table.EventsByView", eventsByView); } +// Show event acknowledgement dialog +function showEventAck(evNum) { + var dialogs = viewHub ? viewHub.dialogs : null; + if (dialogs) { + dialogs.showEventAck(viewID, viewDate.getFullYear(), viewDate.getMonth() + 1, viewDate.getDate(), evNum); + } else { + console.warn(DIALOGS_UNDEFINED); + } +} + // Create detached jQuery object that represents an event row function createEventRow(event) { + var ackHtml = controlAllRight || viewID > 0 && controlViewRight ? + "" + event.Ack + "" : + event.Ack; + var eventRow = $("" + "" + event.Num + "" + "" + event.Time + "" + @@ -67,7 +95,7 @@ function createEventRow(event) { "" + event.KP + "" + "" + event.Cnl + "" + "" + event.Text + "" + - "" + event.Ack + "" + + "" + ackHtml + "" + ""); if (event.Color) { @@ -271,6 +299,7 @@ $(document).ready(function () { styleIOS(); updateLayout(); initViewDate(); + initControls(); loadEventFilter(); scada.tableHeader.create(); notifier = new scada.Notifier("#divNotif"); @@ -301,7 +330,7 @@ $(document).ready(function () { // switch event filter $("#spanAllEventsBtn").click(function () { - if (!$(this).hasClass("disabled")) { + if (viewAllRight) { setEventsByVeiw(false); resetEvents(); } diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/tablecommon.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/tablecommon.js index d9dc49379..75af791ad 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/tablecommon.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/tablecommon.js @@ -1,5 +1,7 @@ // View date format options var VIEW_DATE_OPTIONS = { year: "numeric", month: "long", day: "2-digit" }; +// Error message that is shown if dialogs object is undefined +var DIALOGS_UNDEFINED = "Dialogs object is undefined"; // The view hub object var viewHub = scada.viewHubLocator.getViewHub(); @@ -95,7 +97,7 @@ function selectViewDate(changeViewDateFunc) { } }); } else { - console.warn("Unable to show calendar because dialogs object is undefined"); + console.warn(DIALOGS_UNDEFINED); } } diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml index 215fcbaca..dd59f7f89 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml @@ -8,6 +8,15 @@ Error saving table view to file + Number + Date and Time + Object + Device + Channel + Description + Ack + No events + Loading... Error updating events diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml index 3b0dfde0f..aa211428c 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml @@ -8,6 +8,15 @@ Ошибка при сохранении табличного представления в файле + Номер + Дата и время + Объект + КП + Канал + Описание + Квит + Нет событий + Загрузка... Ошибка при обновлении событий diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js b/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js index 7e6b75999..1d2b70236 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js @@ -51,6 +51,7 @@ scada.dialogs = { }, // Show calendar dropdown form. + // selectedDate is a string date representation, // callback is a function (dialogResult, extraParams), // dialogResult is true or false, // extraParams is object { date, dateStr } diff --git a/ScadaWeb/ScadaWebShell5Beta/js/view.js b/ScadaWeb/ScadaWebShell5Beta/js/view.js index 8ea22079c..e35f3c489 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/view.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/view.js @@ -139,6 +139,7 @@ scada.view = { $("#divDataWindow").css("display", "block"); $("#divCollapseDataWindowBtn").css("display", "inline-block"); + this._dataWindow.load(divClickedTab.data("url"), divClickedTab.data("depends")/*boolean*/); this._saveActiveDataWindow(); this.updateLayout(); }, From 1076dd7402a8f179ea87963c58276aa4fd234020 Mon Sep 17 00:00:00 2001 From: 2mik Date: Fri, 24 Jun 2016 20:16:36 +0300 Subject: [PATCH 133/382] PlgTable: events almost ready --- .../PlgTable/AppCode/PlgTableSpec.cs | 6 +- .../AppCode/Table/CustomDataWndSpec.cs | 32 --------- .../PlgTable/AppCode/Table/EventsWndSpec.cs | 4 +- .../PlgTable/AppCode/Table/SampleWndSpec.cs | 68 +++++++++++++++++++ ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj | 2 +- .../PlgTable/plugins/Table/js/events.js | 24 ++++--- ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js | 18 +++-- 7 files changed, 101 insertions(+), 53 deletions(-) delete mode 100644 ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/CustomDataWndSpec.cs create mode 100644 ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/SampleWndSpec.cs diff --git a/ScadaWeb/OpenPlugins/PlgTable/AppCode/PlgTableSpec.cs b/ScadaWeb/OpenPlugins/PlgTable/AppCode/PlgTableSpec.cs index 901d08986..9184be002 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/AppCode/PlgTableSpec.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/AppCode/PlgTableSpec.cs @@ -103,9 +103,9 @@ public override List DataWindowSpecs { get { - // TODO - //return base.DataWindowSpecs; - return new List() { new EventsWndSpec(), new CustomDataWndSpec(), new CustomDataWndSpec(), new CustomDataWndSpec(), new CustomDataWndSpec(), new CustomDataWndSpec(), new CustomDataWndSpec() }; + return new List() { new EventsWndSpec()/*, + new SampleWndSpec(), new SampleWndSpec(), new SampleWndSpec(), + new SampleWndSpec(), new SampleWndSpec(), new SampleWndSpec()*/ }; } } diff --git a/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/CustomDataWndSpec.cs b/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/CustomDataWndSpec.cs deleted file mode 100644 index d33c08ca6..000000000 --- a/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/CustomDataWndSpec.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; - -namespace Scada.Web.Plugins.Table -{ - [Obsolete("For test purposes")] - public class CustomDataWndSpec : DataWindowSpec - { - public override string ContentTypeCode - { - get - { - return "CustomData"; - } - } - - public override string Name - { - get - { - return "Custom data"; - } - } - - public override string Url - { - get - { - return "~/plugins/Table/CustomData.aspx"; - } - } - } -} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/EventsWndSpec.cs b/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/EventsWndSpec.cs index d6349f58e..2fc6e5d66 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/EventsWndSpec.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/EventsWndSpec.cs @@ -43,7 +43,7 @@ public override string ContentTypeCode } /// - /// Получить наименование контента + /// Получить наименование окна данных /// public override string Name { @@ -54,7 +54,7 @@ public override string Name } /// - /// Получить ссылку на страницу контента + /// Получить ссылку на страницу окна данных /// public override string Url { diff --git a/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/SampleWndSpec.cs b/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/SampleWndSpec.cs new file mode 100644 index 000000000..2fcef0d08 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/SampleWndSpec.cs @@ -0,0 +1,68 @@ +/* + * Copyright 2016 Mikhail Shiryaev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Product : Rapid SCADA + * Module : PlgTable + * Summary : Sample data window specification + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + */ + +namespace Scada.Web.Plugins.Table +{ + /// + /// Sample data window specification + /// Спецификация-пример окна данных + /// + public class SampleWndSpec : DataWindowSpec + { + /// + /// Получить код типа контента + /// + public override string ContentTypeCode + { + get + { + return "SampleWnd"; + } + } + + /// + /// Получить наименование окна данных + /// + public override string Name + { + get + { + return "Sample Window"; + } + } + + /// + /// Получить ссылку на страницу окна данных + /// + /// Страница не существует + public override string Url + { + get + { + return "~/plugins/Table/SampleData.aspx"; + } + } + } +} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj index 80be4b9dd..2abfd060f 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj +++ b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj @@ -118,7 +118,7 @@ - + diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js index 62b31c962..532db7bd6 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js @@ -76,18 +76,25 @@ function saveEventFilter() { function showEventAck(evNum) { var dialogs = viewHub ? viewHub.dialogs : null; if (dialogs) { - dialogs.showEventAck(viewID, viewDate.getFullYear(), viewDate.getMonth() + 1, viewDate.getDate(), evNum); + dialogs.showEventAck(viewID, viewDate, evNum, function (dialogResult) { + if (dialogResult) { + restartUpdatingEvents(); + } + }); } else { console.warn(DIALOGS_UNDEFINED); } } +// Generate HTML of acknowledgement cell +function generateAckHtml(evNum, ack) { + return controlAllRight || viewID > 0 && controlViewRight ? + "" + ack + "" : + ack; +} + // Create detached jQuery object that represents an event row function createEventRow(event) { - var ackHtml = controlAllRight || viewID > 0 && controlViewRight ? - "" + event.Ack + "" : - event.Ack; - var eventRow = $("" + "" + event.Num + "" + "" + event.Time + "" + @@ -95,7 +102,7 @@ function createEventRow(event) { "" + event.KP + "" + "" + event.Cnl + "" + "" + event.Text + "" + - "" + ackHtml + "" + + "" + generateAckHtml(event.Num, event.Ack) + "" + ""); if (event.Color) { @@ -127,7 +134,7 @@ function rewriteEvent(eventRow, event) { eventRow.children("td.dev").text(event.KP); eventRow.children("td.cnl").text(event.Cnl); eventRow.children("td.text").text(event.Text); - eventRow.children("td.ack").text(event.Ack); + eventRow.children("td.ack").html(generateAckHtml(event.Num, event.Ack)); } else { console.error(scada.utils.getCurTime() + " Event number mismatch"); } @@ -347,6 +354,5 @@ $(document).ready(function () { }); // start updating events - startFullUpdatingEvents(); - partUpdateTimeoutID = setTimeout(startPartialUpdatingEvents, dataRefrRate); + restartUpdatingEvents(); }); diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js b/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js index 1d2b70236..f53b8d319 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js @@ -32,19 +32,25 @@ scada.dialogs = { } }, - // Show command dialog - showCmd: function (viewID, ctrlCnlNum) { + // Show command dialog. + // opt_callback is a function (dialogResult, cmdVal), + // dialogResult is true or false, + // cmdVal is a command value which can be a number, array of bytes or string + showCmd: function (viewID, ctrlCnlNum, opt_callback) { if (scada.cmd && scada.cmd.show) { - scada.cmd.show(viewID, ctrlCnlNum); + scada.cmd.show(viewID, ctrlCnlNum, opt_callback); } else { console.warn("Unable to show command dialog because scada.cmd is undefined"); } }, - // Show event acknowledgement dialog - showEventAck: function (viewID, year, month, day, evNum) { + // Show event acknowledgement dialog. + // date is a JavaScript date object, + // opt_callback is a function (dialogResult), + // dialogResult is true or false + showEventAck: function (viewID, date, evNum, opt_callback) { if (scada.eventAck && scada.eventAck.show) { - scada.eventAck.show(viewID, year, month, day, evNum); + scada.eventAck.show(viewID, date, evNum, opt_callback); } else { console.warn("Unable to show event acknowledgement dialog because scada.eventAck is undefined"); } From f7a0c532a5852c4cad54f0f86716d582d2ac303a Mon Sep 17 00:00:00 2001 From: 2mik Date: Sat, 25 Jun 2016 00:48:38 +0300 Subject: [PATCH 134/382] PlgTable: scroll events --- .../PlgTable/plugins/Table/css/events.css | 8 +++--- .../PlgTable/plugins/Table/css/events.less | 7 ++++- .../PlgTable/plugins/Table/css/events.min.css | 2 +- .../PlgTable/plugins/Table/css/table.css | 8 +++--- .../PlgTable/plugins/Table/css/table.less | 5 ++++ .../PlgTable/plugins/Table/css/table.min.css | 2 +- .../plugins/Table/css/tablecommon.css | 4 --- .../plugins/Table/css/tablecommon.less | 5 ---- .../plugins/Table/css/tablecommon.min.css | 2 +- .../PlgTable/plugins/Table/js/events.js | 26 +++++++++++++++++-- .../PlgTable/plugins/Table/js/table.js | 5 +++- .../js/controls/tableheader.js | 16 +++++++++--- 12 files changed, 62 insertions(+), 28 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css index 691585a69..b036da1bf 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css @@ -106,10 +106,6 @@ #divTblWrapper tr.alt { background-color: #f9f9f9; } -#divTblWrapper tr.item:hover { - background-color: #e0e0e0; - color: black; -} #divTblWrapper td { border: 1px solid #999; font-size: 12px; @@ -147,6 +143,10 @@ #tblEvents { width: 100%; } +#divTblWrapper tr.event:hover { + background-color: #e0e0e0; + color: black; +} #tblEvents td.num, #tblEvents td.time, #tblEvents td.ack { diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.less b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.less index 964cab197..e6863e48b 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.less +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.less @@ -15,7 +15,12 @@ } #tblEvents { - width :100%; + width: 100%; +} + +#divTblWrapper tr.event:hover { + background-color: @row-hover-back-color; + color: @row-hover-fore-color; } #tblEvents td.num, diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css index 2404521ec..4c64608e8 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}.hidden{display:none;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a{color:#0073aa;text-decoration:none;}#divTblWrapper td a:hover{color:#00a0d2;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divToolbar .tool-btn.selected{background-color:#0073aa;color:#fff;}#divToolbar .tool-btn.disabled{color:#ccc;cursor:default;}#tblEvents{width:100%;}#tblEvents td.num,#tblEvents td.time,#tblEvents td.ack{text-align:center;}#tblEvents td.num,#tblEvents td.ack{width:5%;}#tblEvents td.time{width:10%;}#tblEvents td.obj,#tblEvents td.dev,#tblEvents td.cnl{width:15%;}#tblEvents td.text{width:35%;}#divLoading,#divNoEvents{padding:5px 10px;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}.hidden{display:none;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a{color:#0073aa;text-decoration:none;}#divTblWrapper td a:hover{color:#00a0d2;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divToolbar .tool-btn.selected{background-color:#0073aa;color:#fff;}#divToolbar .tool-btn.disabled{color:#ccc;cursor:default;}#tblEvents{width:100%;}#divTblWrapper tr.event:hover{background-color:#e0e0e0;color:#000;}#tblEvents td.num,#tblEvents td.time,#tblEvents td.ack{text-align:center;}#tblEvents td.num,#tblEvents td.ack{width:5%;}#tblEvents td.time{width:10%;}#tblEvents td.obj,#tblEvents td.dev,#tblEvents td.cnl{width:15%;}#tblEvents td.text{width:35%;}#divLoading,#divNoEvents{padding:5px 10px;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css index 3bdebce17..7d779beba 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css @@ -106,10 +106,6 @@ #divTblWrapper tr.alt { background-color: #f9f9f9; } -#divTblWrapper tr.item:hover { - background-color: #e0e0e0; - color: black; -} #divTblWrapper td { border: 1px solid #999; font-size: 12px; @@ -136,6 +132,10 @@ #divTblWrapper td img { vertical-align: middle; } +#divTblWrapper tr.item:hover { + background-color: #e0e0e0; + color: black; +} #divTblWrapper td.cap { padding: 2px 5px; } diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.less b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.less index 6926a625c..3d67a7816 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.less +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.less @@ -8,6 +8,11 @@ @hint-border-color: @cell-border-color-hdr; @hint-shadow-color: #aaa; +#divTblWrapper tr.item:hover { + background-color: @row-hover-back-color; + color: @row-hover-fore-color; +} + #divTblWrapper td.cap { padding: 2px 5px; } diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css index 1c871ff70..e23827c11 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}.hidden{display:none;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a{color:#0073aa;text-decoration:none;}#divTblWrapper td a:hover{color:#00a0d2;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divTblWrapper td.cap{padding:2px 5px;}#divTblWrapper td.cap img{width:16px;height:16px;margin:0 5px 0 0;border:none;}#divTblWrapper td.cap a{height:18px;padding-right:5px;display:inline-block;line-height:18px;}#divTblWrapper td.cap span.cmd{display:inline-block;width:18px;height:18px;cursor:pointer;color:#a00;font-size:15px;line-height:18px;text-align:center;}#divTblWrapper td.cap span.cmd:hover{color:#f00;}#divTblWrapper td.cap span.cmd::before{content:"";font-family:FontAwesome;}#divTblWrapper td.cap span.hint{background-color:#fff;border:1px solid #888;border-radius:5px;box-shadow:2px 2px 5px #aaa;color:#444;display:none;line-height:normal;padding:5px;position:fixed;z-index:10;}#divTblWrapper td.cap span.hint.show{display:inline-block;}#divTblWrapper td.cur,#divTblWrapper td.hour{text-align:center;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}.hidden{display:none;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a{color:#0073aa;text-decoration:none;}#divTblWrapper td a:hover{color:#00a0d2;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td.cap{padding:2px 5px;}#divTblWrapper td.cap img{width:16px;height:16px;margin:0 5px 0 0;border:none;}#divTblWrapper td.cap a{height:18px;padding-right:5px;display:inline-block;line-height:18px;}#divTblWrapper td.cap span.cmd{display:inline-block;width:18px;height:18px;cursor:pointer;color:#a00;font-size:15px;line-height:18px;text-align:center;}#divTblWrapper td.cap span.cmd:hover{color:#f00;}#divTblWrapper td.cap span.cmd::before{content:"";font-family:FontAwesome;}#divTblWrapper td.cap span.hint{background-color:#fff;border:1px solid #888;border-radius:5px;box-shadow:2px 2px 5px #aaa;color:#444;display:none;line-height:normal;padding:5px;position:fixed;z-index:10;}#divTblWrapper td.cap span.hint.show{display:inline-block;}#divTblWrapper td.cur,#divTblWrapper td.hour{text-align:center;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css index 4a1fc4823..e2a215af9 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css @@ -106,10 +106,6 @@ #divTblWrapper tr.alt { background-color: #f9f9f9; } -#divTblWrapper tr.item:hover { - background-color: #e0e0e0; - color: black; -} #divTblWrapper td { border: 1px solid #999; font-size: 12px; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less index 951607345..76c40eab5 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less @@ -157,11 +157,6 @@ body { background-color: @row-back-color-alt; } -#divTblWrapper tr.item:hover { - background-color: @row-hover-back-color; - color: @row-hover-fore-color; -} - #divTblWrapper td { border: 1px solid @cell-border-color; font-size: @cell-font-size; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css index db2fbe108..e2e8d2ce7 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}.hidden{display:none;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a{color:#0073aa;text-decoration:none;}#divTblWrapper td a:hover{color:#00a0d2;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}.hidden{display:none;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a{color:#0073aa;text-decoration:none;}#divTblWrapper td a:hover{color:#00a0d2;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js index 532db7bd6..fb44f2b2e 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js @@ -1,4 +1,7 @@ -// Filter events by view +// Period of preventing auto scrolling the event table after a user scrolling, ms +var STOP_SCROLL_PERIOD = 30000; + +// Filter events by view var eventsByView = true; // Input channel filter for event requests var cnlFilter = null; @@ -13,6 +16,8 @@ var partialDataAge = 0; var lastEvNum = 0; // The last received event has the alternate style var lastEvAlt = true; +// Date and time of recent user activity +var activityTime = 0; // Timeout ID of the full events updating timer var fullUpdateTimeoutID = null; // Timeout ID of the partial events updating timer @@ -189,8 +194,20 @@ function afterLoading() { $("#divLoading").addClass("hidden"); if ($("#tblEvents tr.event:first").length > 0) { - $("#divTblWrapper").removeClass("hidden"); + var divTblWrapper = $("#divTblWrapper"); + divTblWrapper.removeClass("hidden"); setTimeout(scada.tableHeader.update.bind(scada.tableHeader), 0); + + // scroll down the event table if a user has been idle for the time + if ((new Date()) - activityTime > STOP_SCROLL_PERIOD) { + var scrollHeight = divTblWrapper[0].scrollHeight; + var newScrollTop = scrollHeight - divTblWrapper.innerHeight(); + if (divTblWrapper.scrollTop() < newScrollTop) { + divTblWrapper.animate({ scrollTop: newScrollTop }, "slow", function () { + activityTime = 0; // allow auto scrolling again + }); + } + } } else { $("#divTblWrapper").addClass("hidden"); $("#divNoEvents").removeClass("hidden"); @@ -353,6 +370,11 @@ $(document).ready(function () { alert("Export is not implemented yet."); }); + // register the activity time + $("#divTblWrapper").on("scroll mousemove", function () { + activityTime = new Date(); + }); + // start updating events restartUpdatingEvents(); }); diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js index 88f89778c..9f3d07c44 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js @@ -217,6 +217,7 @@ function updateCurData(callback) { displayCellData(cell, cnlDataMap); }); + scada.tableHeader.update(); callback(true); } else { callback(false); @@ -252,7 +253,8 @@ function updateHourData(callback) { }); } } - + + scada.tableHeader.update(); // TODO: check that hour data changed callback(true); } else { callback(false); @@ -371,6 +373,7 @@ $(document).ready(function () { // show chart on a label click $("#divTblWrapper a.lbl").click(function () { alert("Charts are not implemented yet."); + return false; }); // send command on a command icon click diff --git a/ScadaWeb/ScadaWebShell5Beta/js/controls/tableheader.js b/ScadaWeb/ScadaWebShell5Beta/js/controls/tableheader.js index bb9515eb9..5b5888a03 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/controls/tableheader.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/controls/tableheader.js @@ -77,11 +77,19 @@ scada.tableHeader = { $(".table-wrapper").each(function () { var wrapper = $(this); var table = wrapper.children("table"); - var origHeader = table.find("tr.orig-table-header:first"); var fixedHeader = table.find("tr.fixed-table-header:first"); - fixedHeader.detach(); - thisObj._updateHeaderCellWidths(origHeader, fixedHeader); - table.append(fixedHeader); // make sure that the fixed header is the last row + + if (fixedHeader.length > 0 /*already created*/) { + var origHeader = table.find("tr.orig-table-header:first"); + thisObj._updateHeaderCellWidths(origHeader, fixedHeader); + + // make sure that the fixed header is the last row + var lastRow = table.find("tr:last"); + if (!fixedHeader.is(lastRow)) { + fixedHeader.detach(); + table.append(fixedHeader); + } + } }); } } \ No newline at end of file From c41079d6a2d2bf52e9d563e8693cf030eae88db6 Mon Sep 17 00:00:00 2001 From: 2mik Date: Mon, 27 Jun 2016 16:32:21 +0300 Subject: [PATCH 135/382] PlgTable: event sound --- ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj | 1 + .../PlgTable/plugins/Table/Events.aspx | 15 ++++++++----- .../PlgTable/plugins/Table/js/events.js | 21 +++++++++++++++++- .../PlgTable/plugins/Table/sounds/event.mp3 | Bin 0 -> 5015 bytes 4 files changed, 30 insertions(+), 7 deletions(-) create mode 100644 ScadaWeb/OpenPlugins/PlgTable/plugins/Table/sounds/event.mp3 diff --git a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj index 2abfd060f..5b93156e0 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj +++ b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj @@ -114,6 +114,7 @@ + diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx index e64692f87..9f663d8d7 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx @@ -38,12 +38,12 @@
All EventsEvents by View
TitleChangedNavigateDateChanged + id="spanAllEventsBtn" runat="server" class="tool-btn">All EventsEvents by View
TitleChangedNavigateDateChanged
+ diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js index fb44f2b2e..751607356 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js @@ -18,6 +18,8 @@ var lastEvNum = 0; var lastEvAlt = true; // Date and time of recent user activity var activityTime = 0; +// Event sound is enabled +var evSoundEnabled = false; // Timeout ID of the full events updating timer var fullUpdateTimeoutID = null; // Timeout ID of the partial events updating timer @@ -77,6 +79,11 @@ function saveEventFilter() { scada.utils.setCookie("Table.EventsByView", eventsByView); } +// Play a sound if a new event is received +function playEventBeep() { + $("#audEvent")[0].play(); +} + // Show event acknowledgement dialog function showEventAck(evNum) { var dialogs = viewHub ? viewHub.dialogs : null; @@ -148,8 +155,18 @@ function rewriteEvent(eventRow, event) { // Append new events to the event table starting from the specified index function appendEvents(tableElem, eventArr, startIndex) { var len = eventArr.length ? eventArr.length : 0; + var beep = false; + for (var i = startIndex; i < len; i++) { - appendEvent(tableElem, eventArr[i]); + var event = eventArr[i]; + if (event.Sound) { + beep = true; + } + appendEvent(tableElem, event); + } + + if (beep && evSoundEnabled) { + playEventBeep(); } } @@ -185,12 +202,14 @@ function resetEvents() { fullDataAge = 0; partialDataAge = 0; lastEvNum = 0; + evSoundEnabled = false; restartUpdatingEvents(); } // Set elements visibility after loading events function afterLoading() { + evSoundEnabled = true; // enable event sound starting from the second response $("#divLoading").addClass("hidden"); if ($("#tblEvents tr.event:first").length > 0) { diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/sounds/event.mp3 b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/sounds/event.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..0b0007f7863b366e8930392c811e448ade1db752 GIT binary patch literal 5015 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 literal 0 HcmV?d00001 From f471c97a78f36cc99c2f950de836f8da40bfac61 Mon Sep 17 00:00:00 2001 From: 2mik Date: Mon, 27 Jun 2016 23:25:31 +0300 Subject: [PATCH 136/382] ScadaWeb5: update API --- .../OpenPlugins/PlgScheme/js/api/clientapi.js | 69 ++--- .../OpenPlugins/PlgScheme/js/api/viewhub.js | 35 ++- .../PlgScheme/plugins/Scheme/SchemeSvc.svc.cs | 13 - .../plugins/Scheme/js/schememodel.js | 23 +- .../OpenPlugins/PlgTable/js/api/clientapi.js | 69 ++--- .../PlgTable/plugins/Table/js/events.js | 2 +- .../PlgTable/plugins/Table/js/table.js | 145 +++++---- ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs | 276 ++++++++---------- .../ScadaWebShell5Beta/js/api/clientapi.js | 69 ++--- 9 files changed, 292 insertions(+), 409 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgScheme/js/api/clientapi.js b/ScadaWeb/OpenPlugins/PlgScheme/js/api/clientapi.js index 111090f74..cfb545d6d 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/js/api/clientapi.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/js/api/clientapi.js @@ -35,8 +35,8 @@ scada.CnlDataExt = function () { scada.CnlDataExt.prototype = Object.create(scada.CnlData.prototype); scada.CnlDataExt.constructor = scada.CnlDataExt; -// Extended hourly input channel data type -scada.HourCnlDataExt = function () { +// Hourly input channel data type +scada.HourCnlData = function () { this.Hour = NaN; this.Modified = false; this.CnlDataExtArr = []; @@ -159,64 +159,31 @@ scada.clientAPI = { "&day=" + date.getDate(); }, - // Extract year, month and day from the date, and join them with the hours into a query string - // TODO: obsolete - _getDateTimeQueryString: function (date, startHour, endHour) { - return "year=" + date.getFullYear() + - "&month=" + (date.getMonth() + 1) + - "&day=" + date.getDate() + - "&startHour=" + startHour + - "&endHour=" + endHour; - }, - // Check that a user is logged on. // callback is a function (success, loggedOn) checkLoggedOn: function (callback) { this._request("ClientApiSvc.svc/CheckLoggedOn", "", callback, false); }, - // Get current value and status of the input channel. + // Get current data of the input channel. // callback is a function (success, cnlData) getCurCnlData: function (cnlNum, callback) { this._request("ClientApiSvc.svc/GetCurCnlData", "?cnlNum=" + cnlNum, callback, this._EMPTY_CNL_DATA); }, - // Get extended current data of the input channel. - // callback is a function (success, cnlDataExt) - // TODO: obsolete - getCurCnlDataExt: function (cnlNum, callback) { - this._request("ClientApiSvc.svc/GetCurCnlDataExt", "?cnlNum=" + cnlNum, callback, this._EMPTY_CNL_DATA_EXT); - }, - - // Get extended current data of the specified input channels. + // Get extended current data by the specified filter. // callback is a function (success, cnlDataExtArr) - // TODO: obsolete - getCurCnlDataExtByCnlNums: function (cnlNums, callback) { - this._request("ClientApiSvc.svc/GetCurCnlDataExtByCnlNums", "?cnlNums=" + cnlNums, callback, []); - }, - - // Get extended current data of the input channels of the specified view. - // callback is a function (success, cnlDataExtArr) - // TODO: getCurCnlDataExt: function (cnlFilter, callback) - getCurCnlDataExtByView: function (viewID, callback) { - this._request("ClientApiSvc.svc/GetCurCnlDataExtByView", "?viewID=" + viewID, callback, []); - }, - - // Get extended hourly data of the specified input channels. - // callback is a function (success, hourCnlDataExtArr) - // TODO: obsolete - getHourCnlDataExtByCnlNums: function (date, startHour, endHour, cnlNums, mode, callback) { - this._request("ClientApiSvc.svc/GetHourCnlDataExtByCnlNums", - "?" + this._getDateTimeQueryString(date, startHour, endHour) + "&cnlNums=" + cnlNums + "&existing=" + mode, - callback, []); + getCurCnlDataExt: function (cnlFilter, callback) { + this._request("ClientApiSvc.svc/GetCurCnlDataExt", "?" + cnlFilter.toQueryString(), callback, []); }, - // Get extended hourly data of the input channels of the specified view. - // callback is a function (success, hourCnlDataExtArr) - // TODO: getHourCnlData: function (hourPeriod, cnlFilter, selectMode, dataAge /*array*/, callback) - getHourCnlDataExtByView: function (date, startHour, endHour, viewID, mode, callback) { - this._request("ClientApiSvc.svc/GetHourCnlDataExtByView", - "?" + this._getDateTimeQueryString(date, startHour, endHour) + "&viewID=" + viewID + "&existing=" + mode, + // Get hourly data by the specified filter. + // dataAge is an array of dates in milliseconds, + // callback is a function (success, hourCnlDataArr) + getHourCnlData: function (hourPeriod, cnlFilter, selectMode, dataAge, callback) { + this._request("ClientApiSvc.svc/GetHourCnlData", + "?" + hourPeriod.toQueryString() + "&" + cnlFilter.toQueryString() + "&existing=" + selectMode + + "&dataAge=" + scada.utils.arrayToQueryParam(dataAge), callback, []); }, @@ -258,17 +225,17 @@ scada.clientAPI = { } }, - // Create map of extended hourly input channel data to access by hour - createHourCnlDataExtMap: function (hourCnlDataExtArr) { + // Create map of hourly input channel data to access by hour + createHourCnlDataMap: function (hourCnlDataArr) { try { var map = new Map(); - for (var hourCnlDataExt of hourCnlDataExtArr) { - map.set(hourCnlDataExt.Hour, hourCnlDataExt); + for (var hourCnlData of hourCnlDataArr) { + map.set(hourCnlData.Hour, hourCnlData); } return map; } catch (ex) { - console.error(scada.utils.getCurTime() + " Error creating map of extended hourly input channel data:", + console.error(scada.utils.getCurTime() + " Error creating map of hourly input channel data:", ex.message); return new Map(); } diff --git a/ScadaWeb/OpenPlugins/PlgScheme/js/api/viewhub.js b/ScadaWeb/OpenPlugins/PlgScheme/js/api/viewhub.js index 120de6d88..576301a50 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/js/api/viewhub.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/js/api/viewhub.js @@ -20,6 +20,9 @@ scada.ViewHub = function (mainWindow) { // Current view ID this.currentViewID = 0; + // Current view date for displaying data + this.currentViewDate = null; + // Main window object that manages a view and data windows this.mainWindow = mainWindow; @@ -53,19 +56,11 @@ scada.ViewHub.prototype.removeDataWindow = function () { // Send notification to a view or data window. // The method is called by a child window -scada.ViewHub.prototype.notify = function (senderWnd, eventType, opt_extraParams) { +scada.ViewHub.prototype.notify = function (eventType, senderWnd, opt_extraParams) { var handled = false; var senderIsView = senderWnd == this.viewWindow; - // set main window title - if (eventType == scada.EventTypes.VIEW_TITLE_CHANGED) { - if (senderIsView && this.mainWindow) { - this.mainWindow.document.title = opt_extraParams; - } - handled = true; - } - - // preprocess navigation + // preprocess events if (eventType == scada.EventTypes.VIEW_NAVIGATE) { if (senderIsView) { this.currentViewID = opt_extraParams; @@ -74,16 +69,32 @@ scada.ViewHub.prototype.notify = function (senderWnd, eventType, opt_extraParams } } + if (eventType == scada.EventTypes.VIEW_DATE_CHANGED) { + this.currentViewDate = opt_extraParams; + } + + // pass the notification to the main window + if (!handled && this.mainWindow && this.mainWindow != senderWnd) { + var jq = this.mainWindow.$; + if (jq) { + jq(this.mainWindow).trigger(eventType, [senderWnd, opt_extraParams]); + } + } + // pass the notification to the view window if (!handled && this.viewWindow && this.viewWindow != senderWnd) { var jq = this.viewWindow.$; - jq(this.viewWindow).trigger(eventType, opt_extraParams); + if (jq) { + jq(this.viewWindow).trigger(eventType, [senderWnd, opt_extraParams]); + } } // pass the notification to the data window if (!handled && this.dataWindow && this.dataWindow != senderWnd) { var jq = this.dataWindow.$; - jq(this.dataWindow).trigger(eventType, opt_extraParams); + if (jq) { + jq(this.dataWindow).trigger(eventType, [senderWnd, opt_extraParams]); + } } }; diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/SchemeSvc.svc.cs b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/SchemeSvc.svc.cs index 02b6069c6..bb59149b5 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/SchemeSvc.svc.cs +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/SchemeSvc.svc.cs @@ -74,22 +74,12 @@ public SchemePropsDTO() : base() { SchemeProps = null; - ElementCount = 0; - ImageCount = 0; } /// /// Получить или установить свойства схемы /// public SchemeView.Scheme SchemeProps { get; set; } - /// - /// Получить или установить количество элементов схемы - /// - public int ElementCount { get; set; } - /// - /// Получить или установить количество изображений схемы - /// - public int ImageCount { get; set; } } /// @@ -244,9 +234,6 @@ public string GetSchemeProps(int viewID, long viewStamp) Font = srcSchemeProps.Font, Title = srcSchemeProps.Title }; - - dto.ElementCount = schemeView.ElementList.Count; - dto.ImageCount = schemeView.ImageDict.Count; } return JsSerializer.Serialize(dto); diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js index 2478af715..5cf8eb04b 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schememodel.js @@ -56,15 +56,11 @@ scada.scheme.Scheme = function () { // Count of elements received by a one request this.LOAD_ELEM_CNT = 100; - // Maximum count of elements - this.MAX_ELEM_CNT = 10000; // Total data size of images received by a one request, 1 MB this.LOAD_IMG_SIZE = 1048576; - // Expected element count - this._expectedElemCnt = 0; - // Expected image count - this._expectedImageCnt = 0; + // Input channel filter for request current data + this._cnlFilter = null; // Scheme loading state this.loadState = scada.scheme.LoadStates.UNDEFINED; @@ -151,20 +147,10 @@ scada.scheme.Scheme.prototype._obtainSchemeProps = function (parsedProps) { throw { message: "SchemeProps property is missing." }; } - if (typeof parsedProps.ElementCount === "undefined") { - throw { message: "ElementCount property is missing." }; - } - - if (typeof parsedProps.ImageCount === "undefined") { - throw { message: "ImageCount property is missing." }; - } - if (this._viewStampsMatched(this.viewStamp, parsedProps.ViewStamp)) { this.type = parsedProps.Type; this.props = parsedProps.SchemeProps; this.viewStamp = parsedProps.ViewStamp; - this._expectedElemCnl = parsedProps.ElementCount; - this._expectedImageCnt = parsedProps.ImageCount; return true; } else { return false; @@ -390,6 +376,7 @@ scada.scheme.Scheme.prototype.clear = function () { this.dom = null; } + this._cnlFilter = null; this.loadState = scada.scheme.LoadStates.UNDEFINED; this.viewID = 0; this.viewStamp = 0; @@ -405,6 +392,8 @@ scada.scheme.Scheme.prototype.load = function (viewID, callback) { var LoadStates = scada.scheme.LoadStates; if (this.viewID == 0) { + this._cnlFilter = new scada.CnlFilter(); + this._cnlFilter.viewID = viewID; this.viewID = viewID; } else if (this.viewID != viewID) { console.warn(scada.utils.getCurTime() + @@ -478,7 +467,7 @@ scada.scheme.Scheme.prototype.createDom = function () { scada.scheme.Scheme.prototype.update = function (clientAPI, callback) { var thisScheme = this; - clientAPI.getCurCnlDataExtByView(this.viewID, function (success, cnlDataExtArr) { + clientAPI.getCurCnlDataExt(this._cnlFilter, function (success, cnlDataExtArr) { if (success) { var curCnlDataMap = clientAPI.createCnlDataExtMap(cnlDataExtArr); diff --git a/ScadaWeb/OpenPlugins/PlgTable/js/api/clientapi.js b/ScadaWeb/OpenPlugins/PlgTable/js/api/clientapi.js index 111090f74..cfb545d6d 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/js/api/clientapi.js +++ b/ScadaWeb/OpenPlugins/PlgTable/js/api/clientapi.js @@ -35,8 +35,8 @@ scada.CnlDataExt = function () { scada.CnlDataExt.prototype = Object.create(scada.CnlData.prototype); scada.CnlDataExt.constructor = scada.CnlDataExt; -// Extended hourly input channel data type -scada.HourCnlDataExt = function () { +// Hourly input channel data type +scada.HourCnlData = function () { this.Hour = NaN; this.Modified = false; this.CnlDataExtArr = []; @@ -159,64 +159,31 @@ scada.clientAPI = { "&day=" + date.getDate(); }, - // Extract year, month and day from the date, and join them with the hours into a query string - // TODO: obsolete - _getDateTimeQueryString: function (date, startHour, endHour) { - return "year=" + date.getFullYear() + - "&month=" + (date.getMonth() + 1) + - "&day=" + date.getDate() + - "&startHour=" + startHour + - "&endHour=" + endHour; - }, - // Check that a user is logged on. // callback is a function (success, loggedOn) checkLoggedOn: function (callback) { this._request("ClientApiSvc.svc/CheckLoggedOn", "", callback, false); }, - // Get current value and status of the input channel. + // Get current data of the input channel. // callback is a function (success, cnlData) getCurCnlData: function (cnlNum, callback) { this._request("ClientApiSvc.svc/GetCurCnlData", "?cnlNum=" + cnlNum, callback, this._EMPTY_CNL_DATA); }, - // Get extended current data of the input channel. - // callback is a function (success, cnlDataExt) - // TODO: obsolete - getCurCnlDataExt: function (cnlNum, callback) { - this._request("ClientApiSvc.svc/GetCurCnlDataExt", "?cnlNum=" + cnlNum, callback, this._EMPTY_CNL_DATA_EXT); - }, - - // Get extended current data of the specified input channels. + // Get extended current data by the specified filter. // callback is a function (success, cnlDataExtArr) - // TODO: obsolete - getCurCnlDataExtByCnlNums: function (cnlNums, callback) { - this._request("ClientApiSvc.svc/GetCurCnlDataExtByCnlNums", "?cnlNums=" + cnlNums, callback, []); - }, - - // Get extended current data of the input channels of the specified view. - // callback is a function (success, cnlDataExtArr) - // TODO: getCurCnlDataExt: function (cnlFilter, callback) - getCurCnlDataExtByView: function (viewID, callback) { - this._request("ClientApiSvc.svc/GetCurCnlDataExtByView", "?viewID=" + viewID, callback, []); - }, - - // Get extended hourly data of the specified input channels. - // callback is a function (success, hourCnlDataExtArr) - // TODO: obsolete - getHourCnlDataExtByCnlNums: function (date, startHour, endHour, cnlNums, mode, callback) { - this._request("ClientApiSvc.svc/GetHourCnlDataExtByCnlNums", - "?" + this._getDateTimeQueryString(date, startHour, endHour) + "&cnlNums=" + cnlNums + "&existing=" + mode, - callback, []); + getCurCnlDataExt: function (cnlFilter, callback) { + this._request("ClientApiSvc.svc/GetCurCnlDataExt", "?" + cnlFilter.toQueryString(), callback, []); }, - // Get extended hourly data of the input channels of the specified view. - // callback is a function (success, hourCnlDataExtArr) - // TODO: getHourCnlData: function (hourPeriod, cnlFilter, selectMode, dataAge /*array*/, callback) - getHourCnlDataExtByView: function (date, startHour, endHour, viewID, mode, callback) { - this._request("ClientApiSvc.svc/GetHourCnlDataExtByView", - "?" + this._getDateTimeQueryString(date, startHour, endHour) + "&viewID=" + viewID + "&existing=" + mode, + // Get hourly data by the specified filter. + // dataAge is an array of dates in milliseconds, + // callback is a function (success, hourCnlDataArr) + getHourCnlData: function (hourPeriod, cnlFilter, selectMode, dataAge, callback) { + this._request("ClientApiSvc.svc/GetHourCnlData", + "?" + hourPeriod.toQueryString() + "&" + cnlFilter.toQueryString() + "&existing=" + selectMode + + "&dataAge=" + scada.utils.arrayToQueryParam(dataAge), callback, []); }, @@ -258,17 +225,17 @@ scada.clientAPI = { } }, - // Create map of extended hourly input channel data to access by hour - createHourCnlDataExtMap: function (hourCnlDataExtArr) { + // Create map of hourly input channel data to access by hour + createHourCnlDataMap: function (hourCnlDataArr) { try { var map = new Map(); - for (var hourCnlDataExt of hourCnlDataExtArr) { - map.set(hourCnlDataExt.Hour, hourCnlDataExt); + for (var hourCnlData of hourCnlDataArr) { + map.set(hourCnlData.Hour, hourCnlData); } return map; } catch (ex) { - console.error(scada.utils.getCurTime() + " Error creating map of extended hourly input channel data:", + console.error(scada.utils.getCurTime() + " Error creating map of hourly input channel data:", ex.message); return new Map(); } diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js index 751607356..7a12d03ac 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js @@ -45,7 +45,7 @@ function changeViewDate(date, notify) { } } -// Init the page controls +// Initialize the page controls function initControls() { if (!viewAllRight) { $("#spanAllEventsBtn").addClass("disabled"); diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js index 9f3d07c44..d684553c5 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js @@ -3,20 +3,21 @@ var HEADER_TIME_OPTIONS = { hour: "2-digit", minute: "2-digit" }; // Column header date and time format options var HEADER_DATETIME_OPTIONS = { month: "short", day: "2-digit", hour: "2-digit", minute: "2-digit" }; -// Time period: firstHour <= timeFrom <= timeTo <= lastHour -// Beginning of the displayed time period -var timeFrom = null; -// End of the displayed time period -var timeTo = null; -// First possible hour of the time period +// Input channel filter for current and hourly data requests +var cnlFilter = null; +// Hour period for hourly data requests: firstHour <= hourPeriod.startHour <= hourPeriod.endHour <= lastHour +var hourPeriod = null; +// First possible hour of the hour period var firstHour = null; -// Last possible hour of the time period +// Last possible hour of the hour period var lastHour = null // jQuery cells that display current data var curDataCells = null; // Array of columns those display hourly data, and consist of jQuery cells var hourDataCols = []; +// Hourly data age +var dataAge = []; // Timeout ID of the hourly data updating timer var updateHourDataTimeoutID = null; @@ -31,33 +32,39 @@ function changeViewDate(date, notify) { } } -// Retrieve the time period from the control values -function retrieveTimePeriod() { - timeFrom = parseInt($("#selTimeFrom").val()); - timeTo = parseInt($("#selTimeTo").val()); +// Initialize first and last possible hours of the time period +function initHourLimits() { firstHour = $("#divTblWrapper tr:first td.hour:first").data("hour"); lastHour = $("#divTblWrapper tr:first td.hour:last").data("hour"); } -// Correct the beginning of the time period -function correctTimeFrom() { - if (timeFrom > timeTo) { - timeFrom = timeTo; - $("#selTimeFrom").val(timeFrom); +// Create the hour period according to the control values +function createHourPeriod() { + hourPeriod = new scada.HourPeriod(); + hourPeriod.date = viewDate; + hourPeriod.startHour = parseInt($("#selTimeFrom").val()); + hourPeriod.endHour = parseInt($("#selTimeTo").val()); +} + +// Correct the beginning of the hour period +function correctStartHour() { + if (hourPeriod.startHour > hourPeriod.endHour) { + hourPeriod.startHour = hourPeriod.endHour; + $("#selTimeFrom").val(hourPeriod.startHour); } } -// Correct the end of the time period -function correctTimeTo() +// Correct the end of the hour period +function correctEndHour() { - if (timeTo < timeFrom) { - timeTo = timeFrom; - $("#selTimeTo").val(timeFrom); + if (hourPeriod.endHour < hourPeriod.startHour) { + hourPeriod.endHour = hourPeriod.startHour; + $("#selTimeTo").val(hourPeriod.endHour); } } -// Save the time period in the cookies -function saveTimePeriod() { +// Save the hour period in the cookies +function saveHourPeriod() { scada.utils.setCookie("Table.TimeFrom", $("#selTimeFrom").val()); scada.utils.setCookie("Table.TimeTo", $("#selTimeTo").val()); } @@ -104,13 +111,12 @@ function updateHourDataColHdrText() { var colDT = new Date(viewDate.getTime()); colDT.setHours(hour); - // replacing span is required for fixed table header calculations - if (timeFrom >= 0) { + if (hourPeriod.startHour >= 0) { // display time only - cell.html("" + colDT.toLocaleTimeString(locale, HEADER_TIME_OPTIONS) + ""); + cell.children("span").text(colDT.toLocaleTimeString(locale, HEADER_TIME_OPTIONS)); } else { // display date and time - cell.html("" + colDT.toLocaleString(locale, HEADER_DATETIME_OPTIONS) + ""); + cell.children("span").text(colDT.toLocaleString(locale, HEADER_DATETIME_OPTIONS)); } }); } @@ -126,7 +132,7 @@ function updateHourDataColVisibility() { hourCells.removeClass("hidden"); // hide cells from the left - var cellsToHide = timeFrom - firstHour; + var cellsToHide = hourPeriod.startHour - firstHour; if (cellsToHide > 0) { var cells = hourCells.slice(0, cellsToHide); cells.addClass("hidden"); @@ -136,7 +142,7 @@ function updateHourDataColVisibility() { } // hide cells from the right - cellsToHide = lastHour - timeTo; + cellsToHide = lastHour - hourPeriod.endHour; if (cellsToHide > 0) { cells = hourCells.slice(-cellsToHide); cells.addClass("hidden"); @@ -190,34 +196,38 @@ function hideHint(spanHint) { spanHint.removeClass("show"); } -// Display the given data in the cell +// Display the given data in the cell and return true if the cell text has been changed function displayCellData(cell, cnlDataMap) { var cnlNum = cell.data("cnl"); if (cnlNum) { var cnlData = cnlDataMap.get(cnlNum); - if (cnlData) { - cell.text(cnlData.Text); - cell.css("color", cnlData.Color); - } else { - cell.text(""); - cell.css("color", ""); - } + var text = cnlData ? cnlData.Text : ""; + var textChanged = cell.text() != text; + cell.text(text); + cell.css("color", cnlData ? cnlData.Color : ""); + return textChanged; } } // Request and display current data. // callback is a function (success) function updateCurData(callback) { - scada.clientAPI.getCurCnlDataExtByView(viewID, function (success, cnlDataExtArr) { + scada.clientAPI.getCurCnlDataExt(cnlFilter, function (success, cnlDataExtArr) { if (success) { var cnlDataMap = scada.clientAPI.createCnlDataExtMap(cnlDataExtArr); + var updateHeader = false; curDataCells.each(function () { var cell = $(this); - displayCellData(cell, cnlDataMap); + if (displayCellData(cell, cnlDataMap)) { + updateHeader = true; + } }); - scada.tableHeader.update(); + if (updateHeader) { + scada.tableHeader.update(); + } + callback(true); } else { callback(false); @@ -228,33 +238,49 @@ function updateCurData(callback) { // Request and display hourly data. // callback is a function (success) function updateHourData(callback) { - scada.clientAPI.getHourCnlDataExtByView(viewDate, timeFrom, timeTo, viewID, scada.HourDataModes.INTEGER_HOURS, - function (success, hourCnlDataExtArr) { - // TODO: check that hourPeriod and cnlFilter were not changed + var reqHourPeriod = hourPeriod; + var reqDataAge = dataAge; + scada.clientAPI.getHourCnlData(hourPeriod, cnlFilter, scada.HourDataModes.INTEGER_HOURS, reqDataAge, + function (success, hourCnlDataArr, dataAge) { + if (reqHourPeriod != hourPeriod) { + // do nothing + } if (success) { - var hourDataMap = scada.clientAPI.createHourCnlDataExtMap(hourCnlDataExtArr); + var hourCnlDataMap = scada.clientAPI.createHourCnlDataMap(hourCnlDataArr); + var updateHeader = false; - for (var hour = timeFrom; hour <= timeTo; hour++) { - var hourData = hourDataMap.get(hour); + for (var hour = hourPeriod.startHour; hour <= hourPeriod.endHour; hour++) { + var hourData = hourCnlDataMap.get(hour); var hourCol = hourDataCols[hour - firstHour]; if (hourData) { - var cnlDataMap = scada.clientAPI.createCnlDataExtMap(hourData.CnlDataExtArr); - $.each(hourCol, function () { - var cell = $(this); - displayCellData(cell, cnlDataMap); - }); + if (hourData.Modified) { + var cnlDataMap = scada.clientAPI.createCnlDataExtMap(hourData.CnlDataExtArr); + $.each(hourCol, function () { + var cell = $(this); + if (displayCellData(cell, cnlDataMap)) { + updateHeader = true; + } + }); + } } else { $.each(hourCol, function () { var cell = $(this); + if (cell.text() != "") { + updateHeader = true; + } cell.text(""); cell.css("color", ""); }); + updateHeader = true; } } - - scada.tableHeader.update(); // TODO: check that hour data changed + + if (updateHeader) { + scada.tableHeader.update(); + } + callback(true); } else { callback(false); @@ -296,13 +322,16 @@ $(document).ready(function () { updateLayout(); setItemLinkWidths(); initViewDate(); - retrieveTimePeriod(); + initHourLimits(); + createHourPeriod(); initCurDataCells(); initHourDataCols(); updateHourDataColHdrText(); scada.tableHeader.create(); notifier = new scada.Notifier("#divNotif"); notifier.startClearingNotifications(); + cnlFilter = new scada.CnlFilter(); + cnlFilter.viewID = viewID; if (DEBUG_MODE) { initDebugTools(); @@ -330,15 +359,15 @@ $(document).ready(function () { // process the time period changing $("#selTimeFrom, #selTimeTo").change(function () { - retrieveTimePeriod(); + createHourPeriod(); if ($(this).attr("id") == "selTimeFrom") { - correctTimeTo(); + correctEndHour(); } else { - correctTimeFrom(); + correctStartHour(); } - saveTimePeriod(); + saveHourPeriod(); updateHourDataColHdrText(); updateHourDataColVisibility(); scada.tableHeader.update(); diff --git a/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs b/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs index 42094fb0e..de63ea1f9 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs @@ -119,16 +119,17 @@ public CnlDataExt(int cnlNum) } /// - /// Расширенные часовые данные входных каналов + /// Часовые данные входных каналов /// - private class HourCnlDataExt + private class HourCnlData { /// /// Конструктор /// - public HourCnlDataExt(double hour) + public HourCnlData(double hour) { Hour = hour; + Modified = false; CnlDataExtArr = null; } @@ -137,6 +138,10 @@ public HourCnlDataExt(double hour) /// public double Hour { get; private set; } /// + /// Получить или установить признак изменения данных относительно метки времени в запросе + /// + public bool Modified { get; set; } + /// /// Получить или установить массив расширенных данных входных каналов /// public CnlDataExt[] CnlDataExtArr { get; set; } @@ -263,6 +268,51 @@ private HashSet GetCnlSet(string cnlNums, int viewID, UserRights userRights } } + /// + /// Получить список номеров каналов из условий запроса с проверкой прав + /// + private IList GetCnlList(string cnlNums, int viewID, UserRights userRights) + { + if (!string.IsNullOrEmpty(cnlNums)) + { + if (!userRights.ViewAllRight) + throw new ScadaException(WebPhrases.NoRights); + return WebUtils.QueryParamToIntArray(cnlNums); + } + else if (viewID > 0) + { + BaseView view = GetViewFromCache(viewID, userRights); + return view.CnlList; + } + else + { + return null; + } + } + + /// + /// Преобразовать параметр запроса в массив 64-битных целых чисел + /// + private long[] QueryParamToLongArray(string param) + { + try + { + string[] elems = (param ?? "").Split(new char[] { ' ', ',' }, + StringSplitOptions.RemoveEmptyEntries); + int len = elems.Length; + long[] arr = new long[len]; + + for (int i = 0; i < len; i++) + arr[i] = int.Parse(elems[i]); + + return arr; + } + catch (FormatException ex) + { + throw new FormatException("Query parameter is not array of long integers.", ex); + } + } + /// /// Создать и заполнить массив расширенных данных входных каналов /// @@ -307,12 +357,27 @@ private CnlDataExt[] CreateCnlDataExtArr(IList cnlList, SrezTableLight.Srez } /// - /// Добавить расширенные часовые данные в список + /// Получить расширенные текущие данные входных каналов + /// + private CnlDataExt[] GetCurCnlDataExtArr(IList cnlList) + { + DateTime dataAge; + SrezTableLight.Srez snapshot = AppData.DataAccess.DataCache.GetCurSnapshot(out dataAge); + + string emptyVal; + bool dataVisible = DataFormatter.CurDataVisible(dataAge, DateTime.Now, out emptyVal); + + return CreateCnlDataExtArr(cnlList, snapshot, dataVisible, emptyVal); + } + + /// + /// Добавить часовые данные в список /// - private void AppendCnlDataExtArr(List hourCnlDataList, double hour, IList cnlList, + private void AppendHourCnlData(List hourCnlDataList, double hour, IList cnlList, SrezTableLight.Srez snapshot, DateTime snapshotDT, DateTime nowDT) { - HourCnlDataExt hourCnlData = new HourCnlDataExt(hour); + HourCnlData hourCnlData = new HourCnlData(hour); + hourCnlData.Modified = true; string emptyVal = ""; bool dataVisible = DataFormatter.HourDataVisible(snapshotDT, nowDT, snapshot != null, out emptyVal); @@ -322,25 +387,22 @@ private void AppendCnlDataExtArr(List hourCnlDataList, double ho } /// - /// Получить расширенные текущие данные входных каналов + /// Добавить пустые часовые данные в список /// - private CnlDataExt[] GetCurCnlDataExtArr(IList cnlList) + private void AppendEmptyHourCnlData(List hourCnlDataList, double hour) { - DateTime dataAge; - SrezTableLight.Srez snapshot = AppData.DataAccess.DataCache.GetCurSnapshot(out dataAge); - - string emptyVal; - bool dataVisible = DataFormatter.CurDataVisible(dataAge, DateTime.Now, out emptyVal); - - return CreateCnlDataExtArr(cnlList, snapshot, dataVisible, emptyVal); + hourCnlDataList.Add(new HourCnlData(hour)); } /// /// Получить часовые данные входных каналов /// - private HourCnlDataExt[] GetHourCnlDataExtArr(int year, int month, int day, - int startHour, int endHour, IList cnlList, bool existing) + private HourCnlData[] GetHourCnlDataArr(int year, int month, int day, + int startHour, int endHour, IList cnlList, bool existing, ref long[] dataAgeArr) { + if (startHour > endHour) + throw new ArgumentException("Start hour must be less or equal than end hour."); + DataAccess dataAccess = AppData.DataAccess; DataCache dataCache = dataAccess.DataCache; @@ -349,15 +411,21 @@ private HourCnlDataExt[] GetHourCnlDataExtArr(int year, int month, int day, DateTime startDate = startDT.Date; DateTime endDT = date.AddHours(endHour); DateTime endDate = endDT.Date; + int hourCnt = endHour - startHour + 1; + int dayCnt = (int)(endDate - startDate).TotalDays + 1; - int cnlCnt = cnlList.Count; - List hourCnlDataList = new List(); + List hourCnlDataList = new List(hourCnt); + long[] newDataAgeArr = new long[dayCnt]; DateTime nowDT = DateTime.Now; DateTime curDate = startDate; - while (curDate <= endDate) + for (int dayInd = 0; dayInd < dayCnt; dayInd++) { SrezTableLight tblHour = dataCache.GetHourTable(curDate); + long newDataAge = WebUtils.DateTimeToJs(tblHour.FileModTime); + long prevDataAge = dayInd < dataAgeArr.Length ? dataAgeArr[dayInd] : 0; + bool modified = tblHour.FileModTime > DateTime.MinValue && prevDataAge < newDataAge; + newDataAgeArr[dayInd] = newDataAge; DateTime nextDate = curDate.AddDays(1.0); if (existing) @@ -372,7 +440,10 @@ private HourCnlDataExt[] GetHourCnlDataExtArr(int year, int month, int day, if (dayStartDT <= snapshotDT && snapshotDT <= dayEndDT) { double hour = (snapshotDT - date).TotalHours; - AppendCnlDataExtArr(hourCnlDataList, hour, cnlList, snapshot, snapshotDT, nowDT); + if (modified) + AppendHourCnlData(hourCnlDataList, hour, cnlList, snapshot, snapshotDT, nowDT); + else + AppendEmptyHourCnlData(hourCnlDataList, hour); } } } @@ -388,13 +459,17 @@ private HourCnlDataExt[] GetHourCnlDataExtArr(int year, int month, int day, SrezTableLight.Srez snapshot; tblHour.SrezList.TryGetValue(snapshotDT, out snapshot); double hour = (snapshotDT - date).TotalHours; - AppendCnlDataExtArr(hourCnlDataList, hour, cnlList, snapshot, snapshotDT, nowDT); + if (modified) + AppendHourCnlData(hourCnlDataList, hour, cnlList, snapshot, snapshotDT, nowDT); + else + AppendEmptyHourCnlData(hourCnlDataList, hour); } } curDate = nextDate; } + dataAgeArr = newDataAgeArr; return hourCnlDataList.ToArray(); } @@ -504,167 +579,58 @@ public string GetCurCnlData(int cnlNum) } /// - /// Получить расширенные текущие данные входного канала - /// - /// Возвращает CnlDataExt, упакованный в DataTransferObject, в формате в JSON - [OperationContract] - [WebGet] - public string GetCurCnlDataExt(int cnlNum) - { - try - { - AppData.CheckLoggedOn(); - CnlDataExt cnlDataExt = new CnlDataExt(cnlNum); - DataAccess dataAccess = AppData.DataAccess; - DateTime dataAge; - SrezTableLight.CnlData cnlData = dataAccess.GetCurCnlData(cnlNum, out dataAge); - cnlDataExt.Val = cnlData.Val; - cnlDataExt.Stat = cnlData.Stat; - - string emptyVal; - if (DataFormatter.CurDataVisible(dataAge, DateTime.Now, out emptyVal)) - { - InCnlProps cnlProps = dataAccess.GetCnlProps(cnlNum); - string text; - string textWithUnit; - DataFormatter.FormatCnlVal(cnlData.Val, cnlData.Stat, cnlProps, out text, out textWithUnit); - - cnlDataExt.Text = text; - cnlDataExt.TextWithUnit = textWithUnit; - CnlStatProps cnlStatProps = dataAccess.GetCnlStatProps(cnlData.Stat); - cnlDataExt.Color = DataFormatter.GetCnlValColor(cnlData.Val, cnlData.Stat, cnlProps, cnlStatProps); - } - else - { - cnlDataExt.Text = emptyVal; - cnlDataExt.TextWithUnit = emptyVal; - } - - return JsSerializer.Serialize(new DataTransferObject(cnlDataExt)); - } - catch (Exception ex) - { - AppData.Log.WriteException(ex, Localization.UseRussian ? - "Ошибка при получении расширенных текущих данных входного канала {0}" : - "Error getting extended current data of the input channel {0}", cnlNum); - return GetErrorDtoJs(ex); - } - } - - /// - /// Получить расширенные текущие данные заданных входных каналов - /// - /// Возвращает CnlDataExt[], упакованный в DataTransferObject, в формате в JSON - [OperationContract] - [WebGet] - public string GetCurCnlDataExtByCnlNums(string cnlNums) - { - try - { - AppData.CheckLoggedOn(); - int[] cnlNumArr = WebUtils.QueryParamToIntArray(cnlNums); - CnlDataExt[] cnlDataExtArr = GetCurCnlDataExtArr(cnlNumArr); - return JsSerializer.Serialize(new DataTransferObject(cnlDataExtArr)); - } - catch (Exception ex) - { - AppData.Log.WriteException(ex, Localization.UseRussian ? - "Ошибка при получении расширенных текущих данных заданных входных каналов" : - "Error getting extended current data of the specified input channels"); - return GetErrorDtoJs(ex); - } - } - - /// - /// Получить расширенные текущие данные входных каналов заданного представления + /// Получить расширенные текущие данные по заданному фильтру /// /// Возвращает CnlDataExt[], упакованный в DataTransferObject, в формате в JSON. - /// Представление должно быть уже загружено в кеш (для ускорения работы метода) + /// Если задан фильтр по представлению, то оно должно быть уже загружено в кеш [OperationContract] [WebGet] - public string GetCurCnlDataExtByView(int viewID) + public string GetCurCnlDataExt(string cnlNums, int viewID) { try { - AppData.CheckLoggedOn(); - BaseView view = AppData.ViewCache.GetViewFromCache(viewID); - - if (view == null) - { - throw new ScadaException(UnableGetViewMsg); - } - else - { - CnlDataExt[] cnlDataExtArr = GetCurCnlDataExtArr(view.CnlList); - return JsSerializer.Serialize(new DataTransferObject(cnlDataExtArr)); - } - } - catch (Exception ex) - { - AppData.Log.WriteException(ex, Localization.UseRussian ? - "Ошибка при получении расширенных текущих данных входных каналов предсталения с ид.={0}" : - "Error getting extended current data of the input channels of the view with id={0}", viewID); - return GetErrorDtoJs(ex); - } - } + UserRights userRights; + AppData.CheckLoggedOn(out userRights); - /// - /// Получить расширенные часовые данные заданных входных каналов - /// - /// Возвращает HourCnlDataExt[], упакованный в DataTransferObject, в формате в JSON - [OperationContract] - [WebGet] - public string GetHourCnlDataExtByCnlNums(int year, int month, int day, - int startHour, int endHour, string cnlNums, bool existing) - { - try - { - AppData.CheckLoggedOn(); - int[] cnlNumArr = WebUtils.QueryParamToIntArray(cnlNums); - HourCnlDataExt[] hourCnlDataExtArr = - GetHourCnlDataExtArr(year, month, day, startHour, endHour, cnlNumArr, existing); - return JsSerializer.Serialize(new DataTransferObject(hourCnlDataExtArr)); + IList cnlList = GetCnlList(cnlNums, viewID, userRights); + CnlDataExt[] cnlDataExtArr = GetCurCnlDataExtArr(cnlList); + return JsSerializer.Serialize(new DataTransferObject(cnlDataExtArr)); } catch (Exception ex) { AppData.Log.WriteException(ex, Localization.UseRussian ? - "Ошибка при получении расширенных часовых данных заданных входных каналов" : - "Error getting extended hourly data of the specified input channels"); + "Ошибка при получении расширенных текущих данных по фильтру, где каналы={0}, ид. представления={1}" : + "Error getting extended current data by the filter where channels={0}, view id={1}", cnlNums, viewID); return GetErrorDtoJs(ex); } } /// - /// Получить расширенные часовые данные входных каналов заданного представления + /// Получить часовые данные по заданному фильтру /// - /// Возвращает HourCnlDataExt[], упакованный в DataTransferObject, в формате в JSON. - /// Представление должно быть уже загружено в кеш (для ускорения работы метода) + /// Возвращает HourCnlDataExt[], упакованный в ArcDTO, в формате в JSON. + /// Если задан фильтр по представлению, то оно должно быть уже загружено в кеш [OperationContract] [WebGet] - public string GetHourCnlDataExtByView(int year, int month, int day, - int startHour, int endHour, int viewID, bool existing) + public string GetHourCnlData(int year, int month, int day, int startHour, int endHour, + string cnlNums, int viewID, bool existing, string dataAge) { try { - AppData.CheckLoggedOn(); - BaseView view = AppData.ViewCache.GetViewFromCache(viewID); + UserRights userRights; + AppData.CheckLoggedOn(out userRights); - if (view == null) - { - throw new ScadaException(UnableGetViewMsg); - } - else - { - HourCnlDataExt[] hourCnlDataExtArr = - GetHourCnlDataExtArr(year, month, day, startHour, endHour, view.CnlList, existing); - return JsSerializer.Serialize(new DataTransferObject(hourCnlDataExtArr)); - } + IList cnlList = GetCnlList(cnlNums, viewID, userRights); + long[] dataAgeArr = QueryParamToLongArray(dataAge); + HourCnlData[] hourCnlDataArr = + GetHourCnlDataArr(year, month, day, startHour, endHour, cnlList, existing, ref dataAgeArr); + return JsSerializer.Serialize(new ArcDTO(hourCnlDataArr, dataAgeArr)); } catch (Exception ex) { AppData.Log.WriteException(ex, Localization.UseRussian ? - "Ошибка при получении расширенных часовых данных входных каналов представления с ид.={0}" : - "Error getting extended hourly data of the input channels of the view with id={0}", viewID); + "Ошибка при получении часовых данных по фильтру, где каналы={0}, ид. представления={1}" : + "Error getting hourly data by the filter where channels={0}, view id={1}", cnlNums, viewID); return GetErrorDtoJs(ex); } } diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js b/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js index 111090f74..f1b270095 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js @@ -35,8 +35,8 @@ scada.CnlDataExt = function () { scada.CnlDataExt.prototype = Object.create(scada.CnlData.prototype); scada.CnlDataExt.constructor = scada.CnlDataExt; -// Extended hourly input channel data type -scada.HourCnlDataExt = function () { +// Hourly input channel data type +scada.HourCnlData = function () { this.Hour = NaN; this.Modified = false; this.CnlDataExtArr = []; @@ -159,64 +159,31 @@ scada.clientAPI = { "&day=" + date.getDate(); }, - // Extract year, month and day from the date, and join them with the hours into a query string - // TODO: obsolete - _getDateTimeQueryString: function (date, startHour, endHour) { - return "year=" + date.getFullYear() + - "&month=" + (date.getMonth() + 1) + - "&day=" + date.getDate() + - "&startHour=" + startHour + - "&endHour=" + endHour; - }, - // Check that a user is logged on. // callback is a function (success, loggedOn) checkLoggedOn: function (callback) { this._request("ClientApiSvc.svc/CheckLoggedOn", "", callback, false); }, - // Get current value and status of the input channel. + // Get current data of the input channel. // callback is a function (success, cnlData) getCurCnlData: function (cnlNum, callback) { this._request("ClientApiSvc.svc/GetCurCnlData", "?cnlNum=" + cnlNum, callback, this._EMPTY_CNL_DATA); }, - // Get extended current data of the input channel. - // callback is a function (success, cnlDataExt) - // TODO: obsolete - getCurCnlDataExt: function (cnlNum, callback) { - this._request("ClientApiSvc.svc/GetCurCnlDataExt", "?cnlNum=" + cnlNum, callback, this._EMPTY_CNL_DATA_EXT); - }, - - // Get extended current data of the specified input channels. + // Get extended current data by the specified filter. // callback is a function (success, cnlDataExtArr) - // TODO: obsolete - getCurCnlDataExtByCnlNums: function (cnlNums, callback) { - this._request("ClientApiSvc.svc/GetCurCnlDataExtByCnlNums", "?cnlNums=" + cnlNums, callback, []); - }, - - // Get extended current data of the input channels of the specified view. - // callback is a function (success, cnlDataExtArr) - // TODO: getCurCnlDataExt: function (cnlFilter, callback) - getCurCnlDataExtByView: function (viewID, callback) { - this._request("ClientApiSvc.svc/GetCurCnlDataExtByView", "?viewID=" + viewID, callback, []); - }, - - // Get extended hourly data of the specified input channels. - // callback is a function (success, hourCnlDataExtArr) - // TODO: obsolete - getHourCnlDataExtByCnlNums: function (date, startHour, endHour, cnlNums, mode, callback) { - this._request("ClientApiSvc.svc/GetHourCnlDataExtByCnlNums", - "?" + this._getDateTimeQueryString(date, startHour, endHour) + "&cnlNums=" + cnlNums + "&existing=" + mode, - callback, []); + getCurCnlDataExt: function (cnlFilter, callback) { + this._request("ClientApiSvc.svc/GetCurCnlDataExt", "?" + cnlFilter.toQueryString(), callback, []); }, - // Get extended hourly data of the input channels of the specified view. - // callback is a function (success, hourCnlDataExtArr) - // TODO: getHourCnlData: function (hourPeriod, cnlFilter, selectMode, dataAge /*array*/, callback) - getHourCnlDataExtByView: function (date, startHour, endHour, viewID, mode, callback) { - this._request("ClientApiSvc.svc/GetHourCnlDataExtByView", - "?" + this._getDateTimeQueryString(date, startHour, endHour) + "&viewID=" + viewID + "&existing=" + mode, + // Get hourly data by the specified filter. + // dataAge is an array of dates in milliseconds, + // callback is a function (success, hourCnlDataArr, dataAge) + getHourCnlData: function (hourPeriod, cnlFilter, selectMode, dataAge, callback) { + this._request("ClientApiSvc.svc/GetHourCnlData", + "?" + hourPeriod.toQueryString() + "&" + cnlFilter.toQueryString() + "&existing=" + selectMode + + "&dataAge=" + scada.utils.arrayToQueryParam(dataAge), callback, []); }, @@ -258,17 +225,17 @@ scada.clientAPI = { } }, - // Create map of extended hourly input channel data to access by hour - createHourCnlDataExtMap: function (hourCnlDataExtArr) { + // Create map of hourly input channel data to access by hour + createHourCnlDataMap: function (hourCnlDataArr) { try { var map = new Map(); - for (var hourCnlDataExt of hourCnlDataExtArr) { - map.set(hourCnlDataExt.Hour, hourCnlDataExt); + for (var hourCnlData of hourCnlDataArr) { + map.set(hourCnlData.Hour, hourCnlData); } return map; } catch (ex) { - console.error(scada.utils.getCurTime() + " Error creating map of extended hourly input channel data:", + console.error(scada.utils.getCurTime() + " Error creating map of hourly input channel data:", ex.message); return new Map(); } From 0bbc317fa4c4d993bbcf6fca5b724d58fab0874f Mon Sep 17 00:00:00 2001 From: 2mik Date: Mon, 27 Jun 2016 23:38:21 +0300 Subject: [PATCH 137/382] PlgTable: cosmetic changes --- .../PlgTable/plugins/Table/js/table.js | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js index d684553c5..6f62f51ca 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/table.js @@ -5,16 +5,16 @@ var HEADER_DATETIME_OPTIONS = { month: "short", day: "2-digit", hour: "2-digit", // Input channel filter for current and hourly data requests var cnlFilter = null; -// Hour period for hourly data requests: firstHour <= hourPeriod.startHour <= hourPeriod.endHour <= lastHour +// Hour period: firstHour <= hourPeriod.startHour <= hourPeriod.endHour <= lastHour var hourPeriod = null; // First possible hour of the hour period var firstHour = null; // Last possible hour of the hour period var lastHour = null -// jQuery cells that display current data +// jQuery cells those display current data var curDataCells = null; -// Array of columns those display hourly data, and consist of jQuery cells +// Array of columns those display hourly data, and which consist of jQuery cells var hourDataCols = []; // Hourly data age var dataAge = []; @@ -38,7 +38,7 @@ function initHourLimits() { lastHour = $("#divTblWrapper tr:first td.hour:last").data("hour"); } -// Create the hour period according to the control values +// Create the hour period according to the view date and control values function createHourPeriod() { hourPeriod = new scada.HourPeriod(); hourPeriod.date = viewDate; @@ -111,18 +111,15 @@ function updateHourDataColHdrText() { var colDT = new Date(viewDate.getTime()); colDT.setHours(hour); - if (hourPeriod.startHour >= 0) { - // display time only - cell.children("span").text(colDT.toLocaleTimeString(locale, HEADER_TIME_OPTIONS)); - } else { - // display date and time - cell.children("span").text(colDT.toLocaleString(locale, HEADER_DATETIME_OPTIONS)); - } + var hdrText = hourPeriod.startHour >= 0 ? + colDT.toLocaleTimeString(locale, HEADER_TIME_OPTIONS) /*display time only*/ : + colDT.toLocaleString(locale, HEADER_DATETIME_OPTIONS) /*display date and time*/; + cell.children("span").text(hdrText); }); } } -// Update visibility of the table view columns according to the time period +// Update visibility of the table view columns according to the hour period function updateHourDataColVisibility() { $("#divTblWrapper tr").each(function () { var row = $(this); @@ -196,7 +193,8 @@ function hideHint(spanHint) { spanHint.removeClass("show"); } -// Display the given data in the cell and return true if the cell text has been changed +// Display the given data in the cell. +// Returns true if the cell text has been changed function displayCellData(cell, cnlDataMap) { var cnlNum = cell.data("cnl"); if (cnlNum) { @@ -246,7 +244,7 @@ function updateHourData(callback) { if (reqHourPeriod != hourPeriod) { // do nothing } - if (success) { + else if (success) { var hourCnlDataMap = scada.clientAPI.createHourCnlDataMap(hourCnlDataArr); var updateHeader = false; From 5de13f36a0d9809905fc2248c223fb8ef0bb6ccc Mon Sep 17 00:00:00 2001 From: 2mik Date: Tue, 28 Jun 2016 09:50:05 +0300 Subject: [PATCH 138/382] PlgTable: beta release --- .../PlgTable/plugins/Table/Events.aspx | 2 +- .../PlgTable/plugins/Table/css/table.css | 2 +- .../PlgTable/plugins/Table/css/table.less | 2 +- .../PlgTable/plugins/Table/css/table.min.css | 2 +- .../PlgTable/plugins/Table/js/table.js | 34 ++++++++++++++++--- ScadaWeb/ScadaWebCommon5Beta/ClientApiSvc.cs | 4 +-- ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js | 3 ++ 7 files changed, 39 insertions(+), 10 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx index 9f663d8d7..9d86622ee 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx @@ -34,7 +34,7 @@ -
+
DateTime.MinValue && prevDataAge < newDataAge; + bool modified = prevDataAge <= 0 || prevDataAge < newDataAge; newDataAgeArr[dayInd] = newDataAge; DateTime nextDate = curDate.AddDays(1.0); diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js b/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js index f53b8d319..e3c72abb6 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/dialogs.js @@ -30,6 +30,7 @@ scada.dialogs = { } else { console.warn("Unable to show chart because scada.chart is undefined"); } + alert("Charts are not implemented yet, cnlNums=" + cnlNums); }, // Show command dialog. @@ -42,6 +43,7 @@ scada.dialogs = { } else { console.warn("Unable to show command dialog because scada.cmd is undefined"); } + alert("Commands are not implemented yet, ctrlCnlNum=" + ctrlCnlNum); }, // Show event acknowledgement dialog. @@ -54,6 +56,7 @@ scada.dialogs = { } else { console.warn("Unable to show event acknowledgement dialog because scada.eventAck is undefined"); } + alert("Acknowledgement is not implemented yet, evNum=" + evNum); }, // Show calendar dropdown form. From 144c344735bdaa2284be174e42afdb2ead52635d Mon Sep 17 00:00:00 2001 From: 2mik Date: Tue, 28 Jun 2016 11:45:49 +0300 Subject: [PATCH 139/382] ScadaWeb5: change web app culture! --- ScadaData/ScadaData/Localization.cs | 38 +++++++++++++++---- .../PlgTable/AppCode/Table/PlgPhrases.cs | 6 +++ .../PlgTable/plugins/Table/Events.aspx | 6 +-- .../plugins/Table/Events.aspx.designer.cs | 13 ++++++- .../PlgTable/plugins/Table/Table.aspx.cs | 4 +- .../PlgTable/plugins/Table/js/events.js | 14 +++---- .../plugins/Table/lang/PlgTable.en-GB.xml | 4 ++ .../plugins/Table/lang/PlgTable.ru-RU.xml | 4 ++ ScadaWeb/ScadaWebCommon5Beta/AppData.cs | 37 ++++++++++++++++-- ScadaWeb/ScadaWebCommon5Beta/WebSettings.cs | 11 +++++- .../ScadaWebShell5Beta/config/WebSettings.xml | 1 + 11 files changed, 112 insertions(+), 26 deletions(-) diff --git a/ScadaData/ScadaData/Localization.cs b/ScadaData/ScadaData/Localization.cs index 2e27b94d0..e94ed7aa5 100644 --- a/ScadaData/ScadaData/Localization.cs +++ b/ScadaData/ScadaData/Localization.cs @@ -102,7 +102,7 @@ public static string GetEmptyPhrase(string key) static Localization() { InitDefaultCulture(); - ReadCulture(); + SetCulture(ReadCulture()); Dictionaries = new Dictionary(); } @@ -163,21 +163,35 @@ private static void InitDefaultCulture() } /// - /// Считать информацию о культуре из реестра + /// Считать наименование культуры из реестра /// - private static void ReadCulture() + private static string ReadCulture() { try { using (RegistryKey key = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64) .OpenSubKey("Software\\SCADA", false)) { - string cultureName = key.GetValue("Culture").ToString(); - Culture = string.IsNullOrEmpty(cultureName) ? - DefaultCulture : CultureInfo.GetCultureInfo(cultureName); + return key.GetValue("Culture").ToString(); } } catch + { + return ""; + } + } + + /// + /// Установить культуру + /// + public static void SetCulture(string cultureName) + { + try + { + Culture = string.IsNullOrEmpty(cultureName) ? + DefaultCulture : CultureInfo.GetCultureInfo(cultureName); + } + catch { Culture = DefaultCulture; } @@ -197,7 +211,17 @@ private static bool CultureIsRussian(CultureInfo cultureInfo) /// - /// Записать информацию о культуре в реестр + /// Изменить культуру + /// + public static void ChangeCulture(string cultureName) + { + if (string.IsNullOrEmpty(cultureName)) + cultureName = ReadCulture(); + SetCulture(cultureName); + } + + /// + /// Записать наименование культуры в реестр /// public static bool WriteCulture(string cultureName, out string errMsg) { diff --git a/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/PlgPhrases.cs b/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/PlgPhrases.cs index 59dadb96c..977bb4fea 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/PlgPhrases.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/AppCode/Table/PlgPhrases.cs @@ -49,6 +49,8 @@ static PlgPhrases() public static string SelectedDay { get; private set; } public static string PreviousDay { get; private set; } public static string PrevDayItem { get; private set; } + public static string ItemCol { get; private set; } + public static string CurCol { get; private set; } public static string InCnlHint { get; private set; } public static string CtrlCnlHint { get; private set; } public static string ObjectHint { get; private set; } @@ -66,6 +68,8 @@ private static void SetToDefault() SelectedDay = Localization.Dict.GetEmptyPhrase("SelectedDay"); PreviousDay = Localization.Dict.GetEmptyPhrase("PreviousDay"); PrevDayItem = Localization.Dict.GetEmptyPhrase("PrevDayItem"); + ItemCol = Localization.Dict.GetEmptyPhrase("ItemCol"); + CurCol = Localization.Dict.GetEmptyPhrase("CurCol"); InCnlHint = Localization.Dict.GetEmptyPhrase("InCnlHint"); CtrlCnlHint = Localization.Dict.GetEmptyPhrase("CtrlCnlHint"); ObjectHint = Localization.Dict.GetEmptyPhrase("ObjectHint"); @@ -93,6 +97,8 @@ public static void Init() SelectedDay = dict.GetPhrase("SelectedDay", SelectedDay); PreviousDay = dict.GetPhrase("PreviousDay", PreviousDay); PrevDayItem = dict.GetPhrase("PrevDayItem", PrevDayItem); + ItemCol = dict.GetPhrase("ItemCol", ItemCol); + CurCol = dict.GetPhrase("CurCol", CurCol); InCnlHint = dict.GetPhrase("InCnlHint", InCnlHint); CtrlCnlHint = dict.GetPhrase("CtrlCnlHint", CtrlCnlHint); ObjectHint = dict.GetPhrase("ObjectHint", ObjectHint); diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx index 9d86622ee..45d547dba 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx @@ -37,9 +37,9 @@
-
All EventsEvents by View
TitleChangedNavigate - /// spanAllEventsBtn control. + /// lblAllEventsBtn control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.HtmlControls.HtmlGenericControl spanAllEventsBtn; + protected global::System.Web.UI.WebControls.Label lblAllEventsBtn; + + /// + /// lblEventsByViewBtn control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblEventsByViewBtn; /// /// lblNumCol control. diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs index 65aa500ef..899006313 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx.cs @@ -142,8 +142,8 @@ private string GenerateTableViewHtml(TableView tableView, bool cmdEnabled, int t // заголовок таблицы sbHtml.AppendLine(""); - AppendCell(sbHtml, "cap", null, "Item"); - AppendCell(sbHtml, "cur", null, "Current"); + AppendCell(sbHtml, "cap", null, "" + PlgPhrases.ItemCol + ""); + AppendCell(sbHtml, "cur", null, "" + PlgPhrases.CurCol + ""); for (int hour = FirstHour; hour <= LastHour; hour++) { AppendCell(sbHtml, timeFrom <= hour && hour <= timeTo ? "hour" : "hour hidden", hour, diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js index 7a12d03ac..5e82f52b6 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/events.js @@ -48,7 +48,7 @@ function changeViewDate(date, notify) { // Initialize the page controls function initControls() { if (!viewAllRight) { - $("#spanAllEventsBtn").addClass("disabled"); + $("#lblAllEventsBtn").addClass("disabled"); } } @@ -60,11 +60,11 @@ function setEventsByVeiw(val) { saveEventFilter(); if (eventsByView) { - $("#spanAllEventsBtn").removeClass("selected"); - $("#spanEventsByViewBtn").addClass("selected"); + $("#lblAllEventsBtn").removeClass("selected"); + $("#lblEventsByViewBtn").addClass("selected"); } else { - $("#spanAllEventsBtn").addClass("selected"); - $("#spanEventsByViewBtn").removeClass("selected"); + $("#lblAllEventsBtn").addClass("selected"); + $("#lblEventsByViewBtn").removeClass("selected"); } } @@ -372,14 +372,14 @@ $(document).ready(function () { }); // switch event filter - $("#spanAllEventsBtn").click(function () { + $("#lblAllEventsBtn").click(function () { if (viewAllRight) { setEventsByVeiw(false); resetEvents(); } }); - $("#spanEventsByViewBtn").click(function () { + $("#lblEventsByViewBtn").click(function () { setEventsByVeiw(true); resetEvents(); }); diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml index dd59f7f89..08ca407ce 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.en-GB.xml @@ -8,6 +8,8 @@ Error saving table view to file + All Events + Events by View Number Date and Time Object @@ -25,6 +27,8 @@ Selected day: Previous day: Prev. + Item + Current In channel: Out channel: Object: diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml index aa211428c..4787d1d7c 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/lang/PlgTable.ru-RU.xml @@ -8,6 +8,8 @@ Ошибка при сохранении табличного представления в файле + Все события + По представлению Номер Дата и время Объект @@ -25,6 +27,8 @@ Выбранные сутки: Предыдущие сутки: Пред. + Элемент + Текущие Вх. канал: Канал упр.: Объект: diff --git a/ScadaWeb/ScadaWebCommon5Beta/AppData.cs b/ScadaWeb/ScadaWebCommon5Beta/AppData.cs index c4830358e..25c4f6544 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/AppData.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/AppData.cs @@ -50,6 +50,7 @@ public sealed class AppData private readonly object appDataLock; // объект для синхронизации доступа к данным приложения private bool inited; // признак инициализации данных приложения + private string cultureName; // имя используемой культуры private CommSettings commSettings; // настройки соединения с сервером private ServerComm serverComm; // объект для обмена данными с сервером private long viewStampCntr; // счётчик для генерации меток представлений @@ -77,9 +78,16 @@ private AppData() appDataLock = new object(); inited = false; + cultureName = Localization.Culture.Name; commSettings = new CommSettings(); viewStampCntr = 0; + scadaDataDictUpdater = null; + scadaWebDictUpdater = null; + commSettingsUpdater = null; + webSettingsUpdater = null; + viewSettingsUpdater = null; + WebSettings = new WebSettings(); ViewSettings = new ViewSettings(); PluginSpecs = new List(); @@ -90,7 +98,6 @@ private AppData() RememberMe = new RememberMe(Storage, Log); UserMonitor = new UserMonitor(Log); - InitUpdaters(); CreateDataObjects(); } @@ -160,13 +167,19 @@ private AppData() /// - /// Инициализировать объекты для обновления словарей и настроек + /// Инициализировать объекты для обновления словарей /// - private void InitUpdaters() + private void InitDictUpdaters() { scadaDataDictUpdater = new DictUpdater(AppDirs.LangDir, "ScadaData", CommonPhrases.Init, Log); scadaWebDictUpdater = new DictUpdater(AppDirs.LangDir, "ScadaWeb", WebPhrases.Init, Log); + } + /// + /// Инициализировать объекты для обновления настроек + /// + private void InitSettingsUpdaters() + { commSettingsUpdater = new SettingsUpdater(commSettings, AppDirs.ConfigDir + CommSettings.DefFileName, true, Log); webSettingsUpdater = new SettingsUpdater(WebSettings, @@ -227,6 +240,20 @@ private void RefreshViewSettings() ViewSettings = (ViewSettings)viewSettingsUpdater.Settings; } + /// + /// Обработать изменение культуры веб-приложения + /// + private void ProcCultureChange() + { + if (cultureName != WebSettings.Culture) + { + cultureName = WebSettings.Culture; + Localization.ChangeCulture(cultureName); + InitDictUpdaters(); + RefreshDictionaries(); + } + } + /// /// Загрузить спецификации плагинов /// @@ -333,7 +360,8 @@ public void Init(string webAppDir) Storage.StorageDir = AppDirs.StorageDir; // инициализация объектов для обновления настроек - InitUpdaters(); + InitDictUpdaters(); + InitSettingsUpdaters(); } // обновление данных веб-приложения @@ -357,6 +385,7 @@ public void Refresh() if (RefreshWebSettings()) { + ProcCultureChange(); LoadPlugins(); InitPlugins(); FillViewSpecs(); diff --git a/ScadaWeb/ScadaWebCommon5Beta/WebSettings.cs b/ScadaWeb/ScadaWebCommon5Beta/WebSettings.cs index 7a0b73684..5f2e0d0cf 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/WebSettings.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/WebSettings.cs @@ -56,6 +56,12 @@ public WebSettings() } + /// + /// Получить или установить культуру веб-приложения + /// + /// Переопределяет культуру, заданную в реестре для всех приложений SCADA + public string Culture { get; set; } + /// /// Получить или установить частоту обновления текущих данных и событий, мс /// @@ -102,6 +108,7 @@ public WebSettings() /// protected void SetToDefault() { + Culture = ""; DataRefrRate = 1000; ArcRefrRate = 10000; DispEventCnt = 100; @@ -160,7 +167,9 @@ public bool LoadFromFile(string fileName, out string errMsg) try { - if (nameL == "datarefrrate") + if (nameL == "culture") + Culture = val; + else if (nameL == "datarefrrate") DataRefrRate = Math.Max(MinDataRefrRate, int.Parse(val)); else if (nameL == "arcrefrrate") ArcRefrRate = Math.Max(MinDataRefrRate, int.Parse(val)); diff --git a/ScadaWeb/ScadaWebShell5Beta/config/WebSettings.xml b/ScadaWeb/ScadaWebShell5Beta/config/WebSettings.xml index 410fd8b73..559416171 100644 --- a/ScadaWeb/ScadaWebShell5Beta/config/WebSettings.xml +++ b/ScadaWeb/ScadaWebShell5Beta/config/WebSettings.xml @@ -1,6 +1,7 @@  + From c22e8583cbcb164749e38a24f6880b16b2a36650 Mon Sep 17 00:00:00 2001 From: 2mik Date: Tue, 28 Jun 2016 12:39:55 +0300 Subject: [PATCH 140/382] PlgChart: initial commit --- ScadaWeb/OpenPlugins/OpenPlugins.sln | 8 +- ScadaWeb/OpenPlugins/PlgChart/PlgChart.csproj | 118 ++++++++++++++++++ .../PlgChart/Properties/AssemblyInfo.cs | 35 ++++++ .../OpenPlugins/PlgChart/Web.Debug.config | 30 +++++ .../OpenPlugins/PlgChart/Web.Release.config | 31 +++++ ScadaWeb/OpenPlugins/PlgChart/Web.config | 13 ++ .../PlgChart/lib/jquery/jquery.min.js | 4 + 7 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 ScadaWeb/OpenPlugins/PlgChart/PlgChart.csproj create mode 100644 ScadaWeb/OpenPlugins/PlgChart/Properties/AssemblyInfo.cs create mode 100644 ScadaWeb/OpenPlugins/PlgChart/Web.Debug.config create mode 100644 ScadaWeb/OpenPlugins/PlgChart/Web.Release.config create mode 100644 ScadaWeb/OpenPlugins/PlgChart/Web.config create mode 100644 ScadaWeb/OpenPlugins/PlgChart/lib/jquery/jquery.min.js diff --git a/ScadaWeb/OpenPlugins/OpenPlugins.sln b/ScadaWeb/OpenPlugins/OpenPlugins.sln index b0fa7843c..4ad1145ec 100644 --- a/ScadaWeb/OpenPlugins/OpenPlugins.sln +++ b/ScadaWeb/OpenPlugins/OpenPlugins.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.24720.0 +VisualStudioVersion = 14.0.23107.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlgScheme", "PlgScheme\PlgScheme.csproj", "{36CBF606-7F85-4E46-BC7A-C86CACF675B2}" EndProject @@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlgConfig", "PlgConfig\PlgC EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlgTable", "PlgTable\PlgTable.csproj", "{7AE30E67-D10E-44B9-AC7A-81E21C8839E8}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PlgChart", "PlgChart\PlgChart.csproj", "{72AFAED2-953D-4939-A828-8EC8A63AB19F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -33,6 +35,10 @@ Global {7AE30E67-D10E-44B9-AC7A-81E21C8839E8}.Debug|Any CPU.Build.0 = Debug|Any CPU {7AE30E67-D10E-44B9-AC7A-81E21C8839E8}.Release|Any CPU.ActiveCfg = Release|Any CPU {7AE30E67-D10E-44B9-AC7A-81E21C8839E8}.Release|Any CPU.Build.0 = Release|Any CPU + {72AFAED2-953D-4939-A828-8EC8A63AB19F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {72AFAED2-953D-4939-A828-8EC8A63AB19F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {72AFAED2-953D-4939-A828-8EC8A63AB19F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {72AFAED2-953D-4939-A828-8EC8A63AB19F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ScadaWeb/OpenPlugins/PlgChart/PlgChart.csproj b/ScadaWeb/OpenPlugins/PlgChart/PlgChart.csproj new file mode 100644 index 000000000..c1966213d --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgChart/PlgChart.csproj @@ -0,0 +1,118 @@ + + + + + Debug + AnyCPU + + + 2.0 + {72AFAED2-953D-4939-A828-8EC8A63AB19F} + {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + PlgChart + PlgChart + v4.0 + true + + + + + + + + true + full + false + bin\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Web.config + + + Web.config + + + + + + + + + + + + + + + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + + + + + True + True + 54466 + / + http://localhost:54466/ + False + False + + + False + + + + + + \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgChart/Properties/AssemblyInfo.cs b/ScadaWeb/OpenPlugins/PlgChart/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..09b7d90a2 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgChart/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("PlgChart")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("PlgChart")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("72afaed2-953d-4939-a828-8ec8a63ab19f")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/ScadaWeb/OpenPlugins/PlgChart/Web.Debug.config b/ScadaWeb/OpenPlugins/PlgChart/Web.Debug.config new file mode 100644 index 000000000..2e302f9f9 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgChart/Web.Debug.config @@ -0,0 +1,30 @@ + + + + + + + + + + \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgChart/Web.Release.config b/ScadaWeb/OpenPlugins/PlgChart/Web.Release.config new file mode 100644 index 000000000..c35844462 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgChart/Web.Release.config @@ -0,0 +1,31 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgChart/Web.config b/ScadaWeb/OpenPlugins/PlgChart/Web.config new file mode 100644 index 000000000..c72873f34 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgChart/Web.config @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/ScadaWeb/OpenPlugins/PlgChart/lib/jquery/jquery.min.js b/ScadaWeb/OpenPlugins/PlgChart/lib/jquery/jquery.min.js new file mode 100644 index 000000000..b8c4187de --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgChart/lib/jquery/jquery.min.js @@ -0,0 +1,4 @@ +/*! jQuery v2.2.3 | (c) jQuery Foundation | jquery.org/license */ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=a.document,e=c.slice,f=c.concat,g=c.push,h=c.indexOf,i={},j=i.toString,k=i.hasOwnProperty,l={},m="2.2.3",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return e.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:e.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a){return n.each(this,a)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(e.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:g,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){var b=a&&a.toString();return!n.isArray(a)&&b-parseFloat(b)+1>=0},isPlainObject:function(a){var b;if("object"!==n.type(a)||a.nodeType||n.isWindow(a))return!1;if(a.constructor&&!k.call(a,"constructor")&&!k.call(a.constructor.prototype||{},"isPrototypeOf"))return!1;for(b in a);return void 0===b||k.call(a,b)},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?i[j.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=d.createElement("script"),b.text=a,d.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b){var c,d=0;if(s(a)){for(c=a.length;c>d;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):g.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:h.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,g=0,h=[];if(s(a))for(d=a.length;d>g;g++)e=b(a[g],g,c),null!=e&&h.push(e);else for(g in a)e=b(a[g],g,c),null!=e&&h.push(e);return f.apply([],h)},guid:1,proxy:function(a,b){var c,d,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(d=e.call(arguments,2),f=function(){return a.apply(b||this,d.concat(e.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:l}),"function"==typeof Symbol&&(n.fn[Symbol.iterator]=c[Symbol.iterator]),n.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){i["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=!!a&&"length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ga(),z=ga(),A=ga(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+M+"))|)"+L+"*\\]",O=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+N+")*)|.*)\\)|)",P=new RegExp(L+"+","g"),Q=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),R=new RegExp("^"+L+"*,"+L+"*"),S=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),T=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),U=new RegExp(O),V=new RegExp("^"+M+"$"),W={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M+"|[*])"),ATTR:new RegExp("^"+N),PSEUDO:new RegExp("^"+O),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},X=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Z=/^[^{]+\{\s*\[native \w/,$=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,_=/[+~]/,aa=/'|\\/g,ba=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),ca=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},da=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(ea){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fa(a,b,d,e){var f,h,j,k,l,o,r,s,w=b&&b.ownerDocument,x=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==x&&9!==x&&11!==x)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==x&&(o=$.exec(a)))if(f=o[1]){if(9===x){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(w&&(j=w.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(o[2])return H.apply(d,b.getElementsByTagName(a)),d;if((f=o[3])&&c.getElementsByClassName&&b.getElementsByClassName)return H.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==x)w=b,s=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(aa,"\\$&"):b.setAttribute("id",k=u),r=g(a),h=r.length,l=V.test(k)?"#"+k:"[id='"+k+"']";while(h--)r[h]=l+" "+qa(r[h]);s=r.join(","),w=_.test(a)&&oa(b.parentNode)||b}if(s)try{return H.apply(d,w.querySelectorAll(s)),d}catch(y){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(Q,"$1"),b,d,e)}function ga(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ha(a){return a[u]=!0,a}function ia(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ja(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function ka(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function la(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function na(a){return ha(function(b){return b=+b,ha(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function oa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=fa.support={},f=fa.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fa.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ia(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ia(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Z.test(n.getElementsByClassName),c.getById=ia(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return"undefined"!=typeof b.getElementsByClassName&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=Z.test(n.querySelectorAll))&&(ia(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ia(function(a){var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Z.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ia(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",O)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Z.test(o.compareDocumentPosition),t=b||Z.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return ka(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?ka(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},fa.matches=function(a,b){return fa(a,null,null,b)},fa.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(T,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fa(b,n,null,[a]).length>0},fa.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fa.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fa.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fa.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fa.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fa.selectors={cacheLength:50,createPseudo:ha,match:W,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ba,ca),a[3]=(a[3]||a[4]||a[5]||"").replace(ba,ca),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fa.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fa.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return W.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&U.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ba,ca).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fa.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(P," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fa.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ha(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ha(function(a){var b=[],c=[],d=h(a.replace(Q,"$1"));return d[u]?ha(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ha(function(a){return function(b){return fa(a,b).length>0}}),contains:ha(function(a){return a=a.replace(ba,ca),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ha(function(a){return V.test(a||"")||fa.error("unsupported lang: "+a),a=a.replace(ba,ca).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Y.test(a.nodeName)},input:function(a){return X.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:na(function(){return[0]}),last:na(function(a,b){return[b-1]}),eq:na(function(a,b,c){return[0>c?c+b:c]}),even:na(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:na(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:na(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:na(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function ra(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j,k=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(j=b[u]||(b[u]={}),i=j[b.uniqueID]||(j[b.uniqueID]={}),(h=i[d])&&h[0]===w&&h[1]===f)return k[2]=h[2];if(i[d]=k,k[2]=a(b,c,g))return!0}}}function sa(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ta(a,b,c){for(var d=0,e=b.length;e>d;d++)fa(a,b[d],c);return c}function ua(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function va(a,b,c,d,e,f){return d&&!d[u]&&(d=va(d)),e&&!e[u]&&(e=va(e,f)),ha(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ta(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ua(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ua(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ua(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function wa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ra(function(a){return a===b},h,!0),l=ra(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[ra(sa(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return va(i>1&&sa(m),i>1&&qa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(Q,"$1"),c,e>i&&wa(a.slice(i,e)),f>e&&wa(a=a.slice(e)),f>e&&qa(a))}m.push(c)}return sa(m)}function xa(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=F.call(i));u=ua(u)}H.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&fa.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ha(f):f}return h=fa.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xa(e,d)),f.selector=a}return f},i=fa.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ba,ca),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=W.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ba,ca),_.test(j[0].type)&&oa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qa(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,!b||_.test(a)&&oa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ia(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ia(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ja("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ia(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ja("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ia(function(a){return null==a.getAttribute("disabled")})||ja(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fa}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.uniqueSort=n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},v=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},w=n.expr.match.needsContext,x=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,y=/^.[^:#\[\.,]*$/;function z(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(y.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return h.call(b,a)>-1!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(z(this,a||[],!1))},not:function(a){return this.pushStack(z(this,a||[],!0))},is:function(a){return!!z(this,"string"==typeof a&&w.test(a)?n(a):a||[],!1).length}});var A,B=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=n.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||A,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:B.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),x.test(e[1])&&n.isPlainObject(b))for(e in b)n.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&f.parentNode&&(this.length=1,this[0]=f),this.context=d,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?void 0!==c.ready?c.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};C.prototype=n.fn,A=n(d);var D=/^(?:parents|prev(?:Until|All))/,E={children:!0,contents:!0,next:!0,prev:!0};n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=w.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?h.call(n(a),this[0]):h.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.uniqueSort(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function F(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return u(a,"parentNode")},parentsUntil:function(a,b,c){return u(a,"parentNode",c)},next:function(a){return F(a,"nextSibling")},prev:function(a){return F(a,"previousSibling")},nextAll:function(a){return u(a,"nextSibling")},prevAll:function(a){return u(a,"previousSibling")},nextUntil:function(a,b,c){return u(a,"nextSibling",c)},prevUntil:function(a,b,c){return u(a,"previousSibling",c)},siblings:function(a){return v((a.parentNode||{}).firstChild,a)},children:function(a){return v(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(E[a]||n.uniqueSort(e),D.test(a)&&e.reverse()),this.pushStack(e)}});var G=/\S+/g;function H(a){var b={};return n.each(a.match(G)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?H(a):n.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),h>=c&&h--}),this},has:function(a){return a?n.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().progress(c.notify).done(c.resolve).fail(c.reject):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=e.call(arguments),d=c.length,f=1!==d||a&&n.isFunction(a.promise)?d:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?e.call(arguments):d,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(d>1)for(i=new Array(d),j=new Array(d),k=new Array(d);d>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().progress(h(b,j,i)).done(h(b,k,c)).fail(g.reject):--f;return f||g.resolveWith(k,c),g.promise()}});var I;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(I.resolveWith(d,[n]),n.fn.triggerHandler&&(n(d).triggerHandler("ready"),n(d).off("ready"))))}});function J(){d.removeEventListener("DOMContentLoaded",J),a.removeEventListener("load",J),n.ready()}n.ready.promise=function(b){return I||(I=n.Deferred(),"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(n.ready):(d.addEventListener("DOMContentLoaded",J),a.addEventListener("load",J))),I.promise(b)},n.ready.promise();var K=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)K(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},L=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function M(){this.expando=n.expando+M.uid++}M.uid=1,M.prototype={register:function(a,b){var c=b||{};return a.nodeType?a[this.expando]=c:Object.defineProperty(a,this.expando,{value:c,writable:!0,configurable:!0}),a[this.expando]},cache:function(a){if(!L(a))return{};var b=a[this.expando];return b||(b={},L(a)&&(a.nodeType?a[this.expando]=b:Object.defineProperty(a,this.expando,{value:b,configurable:!0}))),b},set:function(a,b,c){var d,e=this.cache(a);if("string"==typeof b)e[b]=c;else for(d in b)e[d]=b[d];return e},get:function(a,b){return void 0===b?this.cache(a):a[this.expando]&&a[this.expando][b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=a[this.expando];if(void 0!==f){if(void 0===b)this.register(a);else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in f?d=[b,e]:(d=e,d=d in f?[d]:d.match(G)||[])),c=d.length;while(c--)delete f[d[c]]}(void 0===b||n.isEmptyObject(f))&&(a.nodeType?a[this.expando]=void 0:delete a[this.expando])}},hasData:function(a){var b=a[this.expando];return void 0!==b&&!n.isEmptyObject(b)}};var N=new M,O=new M,P=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Q=/[A-Z]/g;function R(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(Q,"-$&").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:P.test(c)?n.parseJSON(c):c; +}catch(e){}O.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return O.hasData(a)||N.hasData(a)},data:function(a,b,c){return O.access(a,b,c)},removeData:function(a,b){O.remove(a,b)},_data:function(a,b,c){return N.access(a,b,c)},_removeData:function(a,b){N.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=O.get(f),1===f.nodeType&&!N.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),R(f,d,e[d])));N.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){O.set(this,a)}):K(this,function(b){var c,d;if(f&&void 0===b){if(c=O.get(f,a)||O.get(f,a.replace(Q,"-$&").toLowerCase()),void 0!==c)return c;if(d=n.camelCase(a),c=O.get(f,d),void 0!==c)return c;if(c=R(f,d,void 0),void 0!==c)return c}else d=n.camelCase(a),this.each(function(){var c=O.get(this,d);O.set(this,d,b),a.indexOf("-")>-1&&void 0!==c&&O.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){O.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=N.get(a,b),c&&(!d||n.isArray(c)?d=N.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return N.get(a,c)||N.access(a,c,{empty:n.Callbacks("once memory").add(function(){N.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length",""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};$.optgroup=$.option,$.tbody=$.tfoot=$.colgroup=$.caption=$.thead,$.th=$.td;function _(a,b){var c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function aa(a,b){for(var c=0,d=a.length;d>c;c++)N.set(a[c],"globalEval",!b||N.get(b[c],"globalEval"))}var ba=/<|&#?\w+;/;function ca(a,b,c,d,e){for(var f,g,h,i,j,k,l=b.createDocumentFragment(),m=[],o=0,p=a.length;p>o;o++)if(f=a[o],f||0===f)if("object"===n.type(f))n.merge(m,f.nodeType?[f]:f);else if(ba.test(f)){g=g||l.appendChild(b.createElement("div")),h=(Y.exec(f)||["",""])[1].toLowerCase(),i=$[h]||$._default,g.innerHTML=i[1]+n.htmlPrefilter(f)+i[2],k=i[0];while(k--)g=g.lastChild;n.merge(m,g.childNodes),g=l.firstChild,g.textContent=""}else m.push(b.createTextNode(f));l.textContent="",o=0;while(f=m[o++])if(d&&n.inArray(f,d)>-1)e&&e.push(f);else if(j=n.contains(f.ownerDocument,f),g=_(l.appendChild(f),"script"),j&&aa(g),c){k=0;while(f=g[k++])Z.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),l.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",l.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var da=/^key/,ea=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,fa=/^([^.]*)(?:\.(.+)|)/;function ga(){return!0}function ha(){return!1}function ia(){try{return d.activeElement}catch(a){}}function ja(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)ja(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=ha;else if(!e)return a;return 1===f&&(g=e,e=function(a){return n().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=n.guid++)),a.each(function(){n.event.add(this,b,e,d,c)})}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=N.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return"undefined"!=typeof n&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(G)||[""],j=b.length;while(j--)h=fa.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=N.hasData(a)&&N.get(a);if(r&&(i=r.events)){b=(b||"").match(G)||[""],j=b.length;while(j--)if(h=fa.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&N.remove(a,"handle events")}},dispatch:function(a){a=n.event.fix(a);var b,c,d,f,g,h=[],i=e.call(arguments),j=(N.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())a.rnamespace&&!a.rnamespace.test(g.namespace)||(a.handleObj=g,a.data=g.data,d=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==d&&(a.result=d)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&("click"!==a.type||isNaN(a.button)||a.button<1))for(;i!==this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>-1:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]*)\/>/gi,la=/\s*$/g;function pa(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function qa(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function ra(a){var b=na.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function sa(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(N.hasData(a)&&(f=N.access(a),g=N.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}O.hasData(a)&&(h=O.access(a),i=n.extend({},h),O.set(b,i))}}function ta(a,b){var c=b.nodeName.toLowerCase();"input"===c&&X.test(a.type)?b.checked=a.checked:"input"!==c&&"textarea"!==c||(b.defaultValue=a.defaultValue)}function ua(a,b,c,d){b=f.apply([],b);var e,g,h,i,j,k,m=0,o=a.length,p=o-1,q=b[0],r=n.isFunction(q);if(r||o>1&&"string"==typeof q&&!l.checkClone&&ma.test(q))return a.each(function(e){var f=a.eq(e);r&&(b[0]=q.call(this,e,f.html())),ua(f,b,c,d)});if(o&&(e=ca(b,a[0].ownerDocument,!1,a,d),g=e.firstChild,1===e.childNodes.length&&(e=g),g||d)){for(h=n.map(_(e,"script"),qa),i=h.length;o>m;m++)j=e,m!==p&&(j=n.clone(j,!0,!0),i&&n.merge(h,_(j,"script"))),c.call(a[m],j,m);if(i)for(k=h[h.length-1].ownerDocument,n.map(h,ra),m=0;i>m;m++)j=h[m],Z.test(j.type||"")&&!N.access(j,"globalEval")&&n.contains(k,j)&&(j.src?n._evalUrl&&n._evalUrl(j.src):n.globalEval(j.textContent.replace(oa,"")))}return a}function va(a,b,c){for(var d,e=b?n.filter(b,a):a,f=0;null!=(d=e[f]);f++)c||1!==d.nodeType||n.cleanData(_(d)),d.parentNode&&(c&&n.contains(d.ownerDocument,d)&&aa(_(d,"script")),d.parentNode.removeChild(d));return a}n.extend({htmlPrefilter:function(a){return a.replace(ka,"<$1>")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(l.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=_(h),f=_(a),d=0,e=f.length;e>d;d++)ta(f[d],g[d]);if(b)if(c)for(f=f||_(a),g=g||_(h),d=0,e=f.length;e>d;d++)sa(f[d],g[d]);else sa(a,h);return g=_(h,"script"),g.length>0&&aa(g,!i&&_(a,"script")),h},cleanData:function(a){for(var b,c,d,e=n.event.special,f=0;void 0!==(c=a[f]);f++)if(L(c)){if(b=c[N.expando]){if(b.events)for(d in b.events)e[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);c[N.expando]=void 0}c[O.expando]&&(c[O.expando]=void 0)}}}),n.fn.extend({domManip:ua,detach:function(a){return va(this,a,!0)},remove:function(a){return va(this,a)},text:function(a){return K(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return ua(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=pa(this,a);b.appendChild(a)}})},prepend:function(){return ua(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=pa(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return ua(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return ua(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(_(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return K(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!la.test(a)&&!$[(Y.exec(a)||["",""])[1].toLowerCase()]){a=n.htmlPrefilter(a);try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(_(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=[];return ua(this,arguments,function(b){var c=this.parentNode;n.inArray(this,a)<0&&(n.cleanData(_(this)),c&&c.replaceChild(b,this))},a)}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),f=e.length-1,h=0;f>=h;h++)c=h===f?this:this.clone(!0),n(e[h])[b](c),g.apply(d,c.get());return this.pushStack(d)}});var wa,xa={HTML:"block",BODY:"block"};function ya(a,b){var c=n(b.createElement(a)).appendTo(b.body),d=n.css(c[0],"display");return c.detach(),d}function za(a){var b=d,c=xa[a];return c||(c=ya(a,b),"none"!==c&&c||(wa=(wa||n("
" + + "
"); + $("body").append(popupElem); + + // load the frame + var frame = popupElem.find(".modal-frame"); + frame + .one("load", function () { + // display the modal + popupElem + .on('hidden.bs.modal', function () { + $(this).remove(); + }) + .modal("show"); + }) + .attr("src", url); }; // Popup instance locator object From fd6e69059b4c2bc8d28ef8729a5764d9ad40f6bf Mon Sep 17 00:00:00 2001 From: 2mik Date: Wed, 13 Jul 2016 12:09:00 +0300 Subject: [PATCH 179/382] ScadaWeb5: fixes for Edge --- .../plugins/Scheme/js/schemerender.js | 4 ++-- .../PlgTable/plugins/Table/Events.aspx | 2 +- .../PlgTable/plugins/Table/Table.aspx | 2 +- .../PlgTable/plugins/Table/js/tablecommon.js | 19 ++++++++++--------- .../ScadaWebShell5Beta/js/api/clientapi.js | 2 +- ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js | 10 +++++----- ScadaWeb/ScadaWebShell5Beta/js/view.js | 6 +++--- 7 files changed, 23 insertions(+), 22 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js index 206026486..f4f34b284 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/js/schemerender.js @@ -312,7 +312,7 @@ scada.scheme.ElementRenderer.prototype.bindAction = function (jqObj, elem) { case Actions.DRAW_DIAGRAM: if (props.InCnlNum > 0) { if (dialogs) { - dialogs.showChart(scada.scheme.viewHub.currentViewID, new Date(), props.InCnlNum); + dialogs.showChart(scada.scheme.viewHub.curViewID, new Date(), props.InCnlNum); } else { console.warn("Dialogs object is undefined"); } @@ -321,7 +321,7 @@ scada.scheme.ElementRenderer.prototype.bindAction = function (jqObj, elem) { case Actions.SEND_COMMAND: if (props.CtrlCnlNum > 0) { if (dialogs) { - dialogs.showCmd(scada.scheme.viewHub.currentViewID, props.CtrlCnlNum); + dialogs.showCmd(scada.scheme.viewHub.curViewID, props.CtrlCnlNum); } else { console.warn("Dialogs object is undefined"); } diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx index 45d547dba..dd55925f2 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Events.aspx @@ -17,7 +17,6 @@ - + diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx index 94e732d2f..805f685db 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Table.aspx @@ -17,7 +17,6 @@ - + diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/tablecommon.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/tablecommon.js index 75af791ad..6ed9a0453 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/tablecommon.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/tablecommon.js @@ -58,21 +58,22 @@ function updateLayout() { // Set current view date to the initial value function initViewDate() { if (viewHub) { - if (viewHub.currentViewDate) { - setViewDate(viewHub.currentViewDate); - } else { - viewHub.currentViewDate = today; - setViewDate(today); + if (!viewHub.curViewDateMs) { + viewHub.curViewDateMs = today.getTime(); } + setViewDate(viewHub.curViewDateMs); } else { - setViewDate(today); + setViewDate(today.getTime()); } } // Set current view date -function setViewDate(date) { - viewDate = date; - $("#txtDate").val(date.toLocaleDateString(locale, VIEW_DATE_OPTIONS)); +function setViewDate(dateMs) { + viewDate = new Date(dateMs); + // convert date to string and remove invisible left-to-right marks in case of using Edge + var viewDateStr = viewDate.toLocaleDateString(locale, VIEW_DATE_OPTIONS).replace(/\u200E/g, ''); + // display date + $("#txtDate").val(viewDateStr); $("#spanDate i").removeClass("error"); } diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js b/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js index 82d74ca29..240241d65 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/clientapi.js @@ -199,7 +199,7 @@ scada.clientAPI = { // callback is a function (success, value), // value is the number of milliseconds or null in case of any error parseDateTime: function (s, callback) { - this._request("ClientApiSvc.svc/ParseDateTime", "?s=" + s, callback, null); + this._request("ClientApiSvc.svc/ParseDateTime", "?s=" + encodeURIComponent(s), callback, null); }, // Create map of extended input channel data to access by channel number diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js b/ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js index 576301a50..ab19d0fbe 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/viewhub.js @@ -18,10 +18,10 @@ var scada = scada || {}; // View hub type scada.ViewHub = function (mainWindow) { // Current view ID - this.currentViewID = 0; + this.curViewID = 0; - // Current view date for displaying data - this.currentViewDate = null; + // Current view date for displaying data (in milliseconds) + this.curViewDateMs = 0; // Main window object that manages a view and data windows this.mainWindow = mainWindow; @@ -63,14 +63,14 @@ scada.ViewHub.prototype.notify = function (eventType, senderWnd, opt_extraParams // preprocess events if (eventType == scada.EventTypes.VIEW_NAVIGATE) { if (senderIsView) { - this.currentViewID = opt_extraParams; + this.curViewID = opt_extraParams; } else { handled = true; // cancel notification } } if (eventType == scada.EventTypes.VIEW_DATE_CHANGED) { - this.currentViewDate = opt_extraParams; + this.curViewDateMs = opt_extraParams.getTime(); } // pass the notification to the main window diff --git a/ScadaWeb/ScadaWebShell5Beta/js/view.js b/ScadaWeb/ScadaWebShell5Beta/js/view.js index 98964228b..0ef864a44 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/view.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/view.js @@ -27,7 +27,7 @@ scada.view = { // Reload the data window using the current properties and the current view reload: function () { if (this.url) { - var viewID = viewHub.currentViewID; + var viewID = viewHub.curViewID; var newUrl = this.dependsOnView && viewID > 0 ? scada.utils.setQueryParam("viewID", viewID, this.url) : this.url; @@ -95,7 +95,7 @@ scada.view = { return false; // break the loop } }); - } else { + } else if (activeDataWindow == null /*no cookie*/) { // activate events window if presented $("#divBottomTabsContainer .tab").each(function () { var code = $(this).data("code"); @@ -169,7 +169,7 @@ scada.view = { loadView: function (viewID, viewUrl) { // load view document.title = this.initialPageTitle; - viewHub.currentViewID = viewID; + viewHub.curViewID = viewID; var frameView = $("#frameView"); frameView From 8e1cbb7602c1aeb4a21b260107d4886623881aa2 Mon Sep 17 00:00:00 2001 From: 2mik Date: Wed, 13 Jul 2016 13:58:26 +0300 Subject: [PATCH 180/382] OpenPlugins: update external files --- ScadaWeb/OpenPlugins/PlgScheme/js/api/clientapi.js | 13 +++---------- ScadaWeb/OpenPlugins/PlgScheme/js/api/utils.js | 7 +++++++ ScadaWeb/OpenPlugins/PlgScheme/js/api/viewhub.js | 10 +++++----- ScadaWeb/OpenPlugins/PlgTable/js/api/clientapi.js | 13 +++---------- ScadaWeb/OpenPlugins/PlgTable/js/api/utils.js | 7 +++++++ ScadaWeb/OpenPlugins/PlgTable/js/api/viewhub.js | 10 +++++----- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgScheme/js/api/clientapi.js b/ScadaWeb/OpenPlugins/PlgScheme/js/api/clientapi.js index cfb545d6d..240241d65 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/js/api/clientapi.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/js/api/clientapi.js @@ -152,13 +152,6 @@ scada.clientAPI = { }); }, - // Extract year, month and day from the date, and join them into a query string - _dateToQueryString: function (date) { - return "year=" + date.getFullYear() + - "&month=" + (date.getMonth() + 1) + - "&day=" + date.getDate(); - }, - // Check that a user is logged on. // callback is a function (success, loggedOn) checkLoggedOn: function (callback) { @@ -179,7 +172,7 @@ scada.clientAPI = { // Get hourly data by the specified filter. // dataAge is an array of dates in milliseconds, - // callback is a function (success, hourCnlDataArr) + // callback is a function (success, hourCnlDataArr, dataAge) getHourCnlData: function (hourPeriod, cnlFilter, selectMode, dataAge, callback) { this._request("ClientApiSvc.svc/GetHourCnlData", "?" + hourPeriod.toQueryString() + "&" + cnlFilter.toQueryString() + "&existing=" + selectMode + @@ -191,7 +184,7 @@ scada.clientAPI = { // callback is a function (success, eventArr, dataAge) getEvents: function (date, cnlFilter, lastCount, startEvNum, dataAge, callback) { this._request("ClientApiSvc.svc/GetEvents", - "?" + this._dateToQueryString(date) + "&" + cnlFilter.toQueryString() + + "?" + scada.utils.dateToQueryString(date) + "&" + cnlFilter.toQueryString() + "&lastCount=" + lastCount + "&startEvNum=" + startEvNum + "&dataAge=" + dataAge, callback, []); }, @@ -206,7 +199,7 @@ scada.clientAPI = { // callback is a function (success, value), // value is the number of milliseconds or null in case of any error parseDateTime: function (s, callback) { - this._request("ClientApiSvc.svc/ParseDateTime", "?s=" + s, callback, null); + this._request("ClientApiSvc.svc/ParseDateTime", "?s=" + encodeURIComponent(s), callback, null); }, // Create map of extended input channel data to access by channel number diff --git a/ScadaWeb/OpenPlugins/PlgScheme/js/api/utils.js b/ScadaWeb/OpenPlugins/PlgScheme/js/api/utils.js index 5d6bedc2d..c1a3659d1 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/js/api/utils.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/js/api/utils.js @@ -105,6 +105,13 @@ scada.utils = { return arr ? (Array.isArray(arr) ? arr.join(",") : arr) : ""; }, + // Extract year, month and day from the date, and join them into a query string + dateToQueryString: function (date) { + return "year=" + date.getFullYear() + + "&month=" + (date.getMonth() + 1) + + "&day=" + date.getDate(); + }, + // Returns the current time string getCurTime: function () { return new Date().toLocaleTimeString("en-GB"); diff --git a/ScadaWeb/OpenPlugins/PlgScheme/js/api/viewhub.js b/ScadaWeb/OpenPlugins/PlgScheme/js/api/viewhub.js index 576301a50..ab19d0fbe 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/js/api/viewhub.js +++ b/ScadaWeb/OpenPlugins/PlgScheme/js/api/viewhub.js @@ -18,10 +18,10 @@ var scada = scada || {}; // View hub type scada.ViewHub = function (mainWindow) { // Current view ID - this.currentViewID = 0; + this.curViewID = 0; - // Current view date for displaying data - this.currentViewDate = null; + // Current view date for displaying data (in milliseconds) + this.curViewDateMs = 0; // Main window object that manages a view and data windows this.mainWindow = mainWindow; @@ -63,14 +63,14 @@ scada.ViewHub.prototype.notify = function (eventType, senderWnd, opt_extraParams // preprocess events if (eventType == scada.EventTypes.VIEW_NAVIGATE) { if (senderIsView) { - this.currentViewID = opt_extraParams; + this.curViewID = opt_extraParams; } else { handled = true; // cancel notification } } if (eventType == scada.EventTypes.VIEW_DATE_CHANGED) { - this.currentViewDate = opt_extraParams; + this.curViewDateMs = opt_extraParams.getTime(); } // pass the notification to the main window diff --git a/ScadaWeb/OpenPlugins/PlgTable/js/api/clientapi.js b/ScadaWeb/OpenPlugins/PlgTable/js/api/clientapi.js index cfb545d6d..240241d65 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/js/api/clientapi.js +++ b/ScadaWeb/OpenPlugins/PlgTable/js/api/clientapi.js @@ -152,13 +152,6 @@ scada.clientAPI = { }); }, - // Extract year, month and day from the date, and join them into a query string - _dateToQueryString: function (date) { - return "year=" + date.getFullYear() + - "&month=" + (date.getMonth() + 1) + - "&day=" + date.getDate(); - }, - // Check that a user is logged on. // callback is a function (success, loggedOn) checkLoggedOn: function (callback) { @@ -179,7 +172,7 @@ scada.clientAPI = { // Get hourly data by the specified filter. // dataAge is an array of dates in milliseconds, - // callback is a function (success, hourCnlDataArr) + // callback is a function (success, hourCnlDataArr, dataAge) getHourCnlData: function (hourPeriod, cnlFilter, selectMode, dataAge, callback) { this._request("ClientApiSvc.svc/GetHourCnlData", "?" + hourPeriod.toQueryString() + "&" + cnlFilter.toQueryString() + "&existing=" + selectMode + @@ -191,7 +184,7 @@ scada.clientAPI = { // callback is a function (success, eventArr, dataAge) getEvents: function (date, cnlFilter, lastCount, startEvNum, dataAge, callback) { this._request("ClientApiSvc.svc/GetEvents", - "?" + this._dateToQueryString(date) + "&" + cnlFilter.toQueryString() + + "?" + scada.utils.dateToQueryString(date) + "&" + cnlFilter.toQueryString() + "&lastCount=" + lastCount + "&startEvNum=" + startEvNum + "&dataAge=" + dataAge, callback, []); }, @@ -206,7 +199,7 @@ scada.clientAPI = { // callback is a function (success, value), // value is the number of milliseconds or null in case of any error parseDateTime: function (s, callback) { - this._request("ClientApiSvc.svc/ParseDateTime", "?s=" + s, callback, null); + this._request("ClientApiSvc.svc/ParseDateTime", "?s=" + encodeURIComponent(s), callback, null); }, // Create map of extended input channel data to access by channel number diff --git a/ScadaWeb/OpenPlugins/PlgTable/js/api/utils.js b/ScadaWeb/OpenPlugins/PlgTable/js/api/utils.js index 5d6bedc2d..c1a3659d1 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/js/api/utils.js +++ b/ScadaWeb/OpenPlugins/PlgTable/js/api/utils.js @@ -105,6 +105,13 @@ scada.utils = { return arr ? (Array.isArray(arr) ? arr.join(",") : arr) : ""; }, + // Extract year, month and day from the date, and join them into a query string + dateToQueryString: function (date) { + return "year=" + date.getFullYear() + + "&month=" + (date.getMonth() + 1) + + "&day=" + date.getDate(); + }, + // Returns the current time string getCurTime: function () { return new Date().toLocaleTimeString("en-GB"); diff --git a/ScadaWeb/OpenPlugins/PlgTable/js/api/viewhub.js b/ScadaWeb/OpenPlugins/PlgTable/js/api/viewhub.js index 576301a50..ab19d0fbe 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/js/api/viewhub.js +++ b/ScadaWeb/OpenPlugins/PlgTable/js/api/viewhub.js @@ -18,10 +18,10 @@ var scada = scada || {}; // View hub type scada.ViewHub = function (mainWindow) { // Current view ID - this.currentViewID = 0; + this.curViewID = 0; - // Current view date for displaying data - this.currentViewDate = null; + // Current view date for displaying data (in milliseconds) + this.curViewDateMs = 0; // Main window object that manages a view and data windows this.mainWindow = mainWindow; @@ -63,14 +63,14 @@ scada.ViewHub.prototype.notify = function (eventType, senderWnd, opt_extraParams // preprocess events if (eventType == scada.EventTypes.VIEW_NAVIGATE) { if (senderIsView) { - this.currentViewID = opt_extraParams; + this.curViewID = opt_extraParams; } else { handled = true; // cancel notification } } if (eventType == scada.EventTypes.VIEW_DATE_CHANGED) { - this.currentViewDate = opt_extraParams; + this.curViewDateMs = opt_extraParams.getTime(); } // pass the notification to the main window From ee26bf768b291d3c10f6551e4c96d12c3fec6409 Mon Sep 17 00:00:00 2001 From: 2mik Date: Wed, 13 Jul 2016 18:55:05 +0300 Subject: [PATCH 181/382] ScadaWeb5: dev modals --- .../PlgTable/plugins/Table/js/cmdobj.js | 2 +- ScadaWeb/ScadaWebCommon5Beta/UserData.cs | 2 +- ScadaWeb/ScadaWebShell5Beta/MasterMain.Master | 1 + .../ScadaWebShell5Beta/MasterMain.Master.cs | 27 ++++++ .../ScadaWebShell5Beta/css/controls/popup.css | 3 +- .../css/controls/popup.less | 3 +- .../css/controls/popup.min.css | 2 +- .../ScadaWebShell5Beta/js/controls/popup.js | 85 +++++++++++++++---- .../lang/ScadaWeb.en-GB.xml | 8 ++ .../lang/ScadaWeb.ru-RU.xml | 8 ++ 10 files changed, 121 insertions(+), 20 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/cmdobj.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/cmdobj.js index 80ed6dbac..42c23ac65 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/cmdobj.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/cmdobj.js @@ -20,7 +20,7 @@ scada.cmd = { show: function (rootPath, viewID, ctrlCnlNum, opt_callback) { var popup = scada.popupLocator.getPopup(); if (popup) { - popup.showModal(rootPath + "plugins/Chart/Chart.aspx?viewID=" + viewID + "&cnlNum=101"); + popup.showModal(rootPath + "Calendar.aspx", ["execute", "close"], opt_callback); } } } \ No newline at end of file diff --git a/ScadaWeb/ScadaWebCommon5Beta/UserData.cs b/ScadaWeb/ScadaWebCommon5Beta/UserData.cs index c5cba503f..3368f017e 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/UserData.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/UserData.cs @@ -276,7 +276,7 @@ public bool Login(string username, string password, out string errMsg) AppData.Log.WriteError(string.Format(Localization.UseRussian ? "Неудачная попытка входа в систему: {0}{1}. IP-адрес: {2}" : "Unsuccessful login attempt: {0}{1}. IP address: {2}", - username == "" ? "" : username + " - ", errMsg, IpAddress)); + username == "" ? "" : username + " - ", errMsg.TrimEnd('.'), IpAddress)); return false; } } diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master index 9c61a2e66..40a489fb6 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master @@ -29,6 +29,7 @@ scada.env = { rootPath: "<%= VirtualPathUtility.AppendTrailingSlash(VirtualPathUtility.ToAbsolute("~")) %>" }; + scada.modalButtonMap = <%= GenModalButtonMapJs() %>; <%= GenScriptPathsHtml() %> diff --git a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs index 27f71d596..5fb2a3b17 100644 --- a/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs +++ b/ScadaWeb/ScadaWebShell5Beta/MasterMain.Master.cs @@ -26,6 +26,9 @@ using Scada.UI; using Scada.Web.Shell; using System; +using System.Collections.Generic; +using System.Text; +using System.Web; namespace Scada.Web { @@ -51,6 +54,30 @@ public partial class MasterMain : System.Web.UI.MasterPage public int SelectedViewID = 0; + /// + /// Генерировать JavaScript-код карты кнопок для модальных диалогов + /// + protected string GenModalButtonMapJs() + { + StringBuilder sbJs = new StringBuilder(); + sbJs.AppendLine("new Map(["); + + Localization.Dict dict; + Localization.Dictionaries.TryGetValue("Scada.Web.MasterMain.Js.ModalButtons", out dict); + + if (dict != null) + { + foreach (KeyValuePair pair in dict.Phrases) + { + sbJs.Append("[\"").Append(pair.Key).Append("\", \"") + .Append(HttpUtility.JavaScriptStringEncode(pair.Value)).AppendLine("\"],"); + } + } + + sbJs.Append("])"); + return sbJs.ToString(); + } + /// /// Генерировать HTML-код дополнительных скриптов /// diff --git a/ScadaWeb/ScadaWebShell5Beta/css/controls/popup.css b/ScadaWeb/ScadaWebShell5Beta/css/controls/popup.css index a46da67d1..a66fdb525 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/controls/popup.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/controls/popup.css @@ -21,7 +21,8 @@ display: inline-block; overflow: hidden; } -.popup-frame { +.popup-frame, +.modal-frame { margin: 0; padding: 0; border: none; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/controls/popup.less b/ScadaWeb/ScadaWebShell5Beta/css/controls/popup.less index 842fa8456..5fe068d6e 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/controls/popup.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/controls/popup.less @@ -28,7 +28,8 @@ overflow: hidden; } -.popup-frame { +.popup-frame, +.modal-frame { margin: 0; padding: 0; border: none; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/controls/popup.min.css b/ScadaWeb/ScadaWebShell5Beta/css/controls/popup.min.css index b39313533..193018ab1 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/controls/popup.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/controls/popup.min.css @@ -1 +1 @@ -.popup-overlay{position:fixed;left:0;top:0;width:100%;height:100%;background-color:#808080;opacity:0;}.popup-wrapper{position:fixed;left:0;top:0;width:0;height:0;background-color:#fff;border:1px solid #888;border-radius:5px;box-sizing:content-box;box-shadow:2px 2px 5px #aaa;display:inline-block;overflow:hidden;}.popup-frame{margin:0;padding:0;border:none;display:inline-block;overflow:hidden;} \ No newline at end of file +.popup-overlay{position:fixed;left:0;top:0;width:100%;height:100%;background-color:#808080;opacity:0;}.popup-wrapper{position:fixed;left:0;top:0;width:0;height:0;background-color:#fff;border:1px solid #888;border-radius:5px;box-sizing:content-box;box-shadow:2px 2px 5px #aaa;display:inline-block;overflow:hidden;}.popup-frame,.modal-frame{margin:0;padding:0;border:none;display:inline-block;overflow:hidden;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js b/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js index 0f106990b..3423f56ef 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js @@ -11,11 +11,22 @@ * * Optional: * - bootstrap + * - scada.modalButtonMap object */ // Rapid SCADA namespace var scada = scada || {}; +// Modal dialog buttons enumeration +scada.ModalButtons = { + OK: "ok", + YES: "yes", + NO: "no", + EXEC: "execute", + CANCEL: "cancel", + CLOSE: "close" +}; + // Popup dialogs manipulation type scada.Popup = function () { // Window that holds popups @@ -85,9 +96,33 @@ scada.Popup.prototype._getOffset = function (elem) { return { left: left, top: top }; }; +// Get html markup of a modal dialog footer buttons +scada.Popup.prototype._genModalButtonsHtml = function (buttons) { + var html = ""; + + for (var btn of buttons) { + var btnText = scada.modalButtonMap ? scada.modalButtonMap.get(btn) : null; + if (!btnText) { + btnText = btn; + } + + if (btn == scada.ModalButtons.CANCEL || btn == scada.ModalButtons.CLOSE) { + html += ""; + } else { + var subclass = btn == scada.ModalButtons.OK || btn == scada.ModalButtons.YES ? "btn-primary" : + (btn == scada.ModalButtons.EXEC ? "btn-danger" : "btn-default"); + + html += ""; + } + } + + return html; +} + // Show popup with the specified url as a dropdown menu below the anchorElem. -// callback is a function (dialogResult, extraParams) -scada.Popup.prototype.showDropdown = function (url, anchorElem, callback) { +// opt_callback is a function (dialogResult, extraParams) +scada.Popup.prototype.showDropdown = function (url, anchorElem, opt_callback) { var thisObj = this; var popupElem = $(""); @@ -121,7 +156,7 @@ scada.Popup.prototype.showDropdown = function (url, anchorElem, callback) { .on("load", function () { // store callback function var frameWnd = frame[0].contentWindow; - thisObj._popupCallbacks.set(frameWnd, callback); + thisObj._popupCallbacks.set(frameWnd, opt_callback); // remove the popup on press Escape key in the frame if (frameWnd.$) { @@ -194,32 +229,52 @@ scada.Popup.prototype.closeDropdown = function (popupWnd, dialogResult, extraPar }; // Show modal dialog with the specified url. -// callback is a function (dialogResult, extraParams), +// opt_callback is a function (dialogResult, extraParams), // requires Bootstrap -scada.Popup.prototype.showModal = function (url, callback) { - var popupElem = $( +scada.Popup.prototype.showModal = function (url, opt_buttons, opt_callback) { + var footerHtml = opt_buttons && opt_buttons.length ? + "" : ""; + + var modalElem = $( "
"); - $("body").append(popupElem); + footerHtml + + "
"); + $("body").append(modalElem); // load the frame - var frame = popupElem.find(".modal-frame"); + var modalFrame = modalElem.find(".modal-frame"); - frame + modalFrame .one("load", function () { + // set the frame size + var frameBody = modalFrame.contents().find("body"); + var frameWidth = frameBody.outerWidth(true); + var frameHeight = frameBody.outerHeight(true); + + modalFrame.css({ + "width": frameWidth, + "height": frameHeight + }); + // display the modal - popupElem + var modalBody = modalElem.find(".modal-body"); + var modalPaddings = parseInt(modalBody.css("padding-left")) + parseInt(modalBody.css("padding-right")); + modalElem.find(".modal-content").css("min-width", frameWidth + modalPaddings) + + modalElem + .on('shown.bs.modal', function () { + modalFrame.focus(); + modalElem.find(".modal-title").text(modalFrame[0].contentWindow.document.title); + }) .on('hidden.bs.modal', function () { $(this).remove(); - }) + }) .modal("show"); }) .attr("src", url); diff --git a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml index b655c21c4..ece8a9ab0 100644 --- a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml +++ b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.en-GB.xml @@ -22,6 +22,14 @@ Views Collapse menu + + OK + Yes + No + Execute + Cancel + Close + Error loading view settings Error saving view settings diff --git a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml index b1e79e903..aea979643 100644 --- a/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml +++ b/ScadaWeb/ScadaWebShell5Beta/lang/ScadaWeb.ru-RU.xml @@ -22,6 +22,14 @@ Представления Свернуть меню + + OK + Да + Нет + Выполнить + Отмена + Закрыть + Ошибка при загрузке настроек представлений Ошибка при сохранении настроек представлений From 2747ec012604d9dd540e51cdb27e7204f0221712 Mon Sep 17 00:00:00 2001 From: 2mik Date: Wed, 13 Jul 2016 18:58:47 +0300 Subject: [PATCH 182/382] ScadaWeb5: modals again --- ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj | 1 + .../OpenPlugins/PlgTable/js/controls/popup.js | 296 ++++++++++++++++++ .../PlgTable/plugins/Table/js/cmdobj.js | 3 +- 3 files changed, 299 insertions(+), 1 deletion(-) create mode 100644 ScadaWeb/OpenPlugins/PlgTable/js/controls/popup.js diff --git a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj index befc260e5..7a4ecb8a4 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj +++ b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj @@ -78,6 +78,7 @@ + diff --git a/ScadaWeb/OpenPlugins/PlgTable/js/controls/popup.js b/ScadaWeb/OpenPlugins/PlgTable/js/controls/popup.js new file mode 100644 index 000000000..3423f56ef --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/js/controls/popup.js @@ -0,0 +1,296 @@ +/* + * Popup dialogs manipulation + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + * + * Requires: + * - jquery + * - utils.js + * + * Optional: + * - bootstrap + * - scada.modalButtonMap object + */ + +// Rapid SCADA namespace +var scada = scada || {}; + +// Modal dialog buttons enumeration +scada.ModalButtons = { + OK: "ok", + YES: "yes", + NO: "no", + EXEC: "execute", + CANCEL: "cancel", + CLOSE: "close" +}; + +// Popup dialogs manipulation type +scada.Popup = function () { + // Window that holds popups + this._holderWindow = window; + + // Callback functions of the existing popups + this._popupCallbacks = new Map(); +}; + +// Event handler that removes the popup on press Escape key +scada.Popup.prototype._removePopupOnEscape = function (event) { + if (event.which == 27 /*Escape*/) { + this._cancelDropdown(event.data); + } +}; + +// Close the dropdown popup and execute a callback with a cancel result +scada.Popup.prototype._cancelDropdown = function (popupElem) { + var frame = popupElem.find(".popup-frame"); + if (frame.length > 0) { + var frameWnd = frame[0].contentWindow; + var callback = this._popupCallbacks.get(frameWnd); + this._popupCallbacks.delete(frameWnd); + popupElem.remove(); + + if (callback) { + callback(false); + } + } else { + popupElem.remove(); + } +}; + +// Get coodinates of the specified element relative to the holder window +scada.Popup.prototype._getOffset = function (elem) { + // validate the element + var defaultOffset = { left: 0, top: 0 }; + if (!(elem && elem.length)) { + return defaultOffset; + } + + // get coodinates within a window that contains the element + var wnd = elem[0].ownerDocument.defaultView; + var offset = elem.offset(); + var left = offset.left + $(wnd).scrollLeft(); + var top = offset.top + $(wnd).scrollTop(); + + // add coordinates of the parent frames + do { + var parentWnd = wnd.parent; + if (wnd != parentWnd) { + if (parentWnd.$) { + var frame = parentWnd.$(wnd.frameElement); + if (frame.length > 0) { + offset = frame.offset(); + left += offset.left + $(parentWnd).scrollLeft(); + top += offset.top + $(parentWnd).scrollTop(); + } + } else { + console.warn("Unable to get offset, because jQuery is not found"); + return defaultOffset; + } + wnd = parentWnd; + } + } while (wnd != this._holderWindow && wnd != wnd.parent); + + return { left: left, top: top }; +}; + +// Get html markup of a modal dialog footer buttons +scada.Popup.prototype._genModalButtonsHtml = function (buttons) { + var html = ""; + + for (var btn of buttons) { + var btnText = scada.modalButtonMap ? scada.modalButtonMap.get(btn) : null; + if (!btnText) { + btnText = btn; + } + + if (btn == scada.ModalButtons.CANCEL || btn == scada.ModalButtons.CLOSE) { + html += ""; + } else { + var subclass = btn == scada.ModalButtons.OK || btn == scada.ModalButtons.YES ? "btn-primary" : + (btn == scada.ModalButtons.EXEC ? "btn-danger" : "btn-default"); + + html += ""; + } + } + + return html; +} + +// Show popup with the specified url as a dropdown menu below the anchorElem. +// opt_callback is a function (dialogResult, extraParams) +scada.Popup.prototype.showDropdown = function (url, anchorElem, opt_callback) { + var thisObj = this; + var popupElem = $(""); + $("body").append(popupElem); + + var overlay = popupElem.find(".popup-overlay"); + var wrapper = popupElem.find(".popup-wrapper"); + var frame = popupElem.find(".popup-frame"); + + // setup overlay + overlay + .css("z-index", scada.utils.FRONT_ZINDEX) + .click(function () { + thisObj._cancelDropdown(popupElem); + }); + + // setup wrapper + wrapper.css({ + "z-index": scada.utils.FRONT_ZINDEX + 1, // above the overlay + "opacity": 0.0 // hide the popup while it's loading + }); + + // remove the popup on press Escape key in the parent window + var removePopupOnEscape = this._removePopupOnEscape.bind(this); + $(document) + .off("keydown", null, removePopupOnEscape) + .on("keydown", null, popupElem, removePopupOnEscape); + + // load the frame + frame + .on("load", function () { + // store callback function + var frameWnd = frame[0].contentWindow; + thisObj._popupCallbacks.set(frameWnd, opt_callback); + + // remove the popup on press Escape key in the frame + if (frameWnd.$) { + var jqFrameDoc = frameWnd.$(frameWnd.document); + jqFrameDoc.ready(function () { + jqFrameDoc + .off("keydown", null, removePopupOnEscape) + .on("keydown", null, popupElem, removePopupOnEscape); + }); + } + }) + .one("load", function () { + // set the popup position + var frameBody = frame.contents().find("body"); + var width = frameBody.outerWidth(true); + var height = frameBody.outerHeight(true); + var left = 0; + var top = 0; + + if (anchorElem.length > 0) { + var offset = thisObj._getOffset(anchorElem); + left = offset.left; + top = offset.top + anchorElem.outerHeight(); + var borderWidthX2 = parseInt(wrapper.css("border-width"), 10) * 2; + + if (left + width + borderWidthX2 > $(document).width()) + left = Math.max($(document).width() - width - borderWidthX2, 0); + + if (top + height + borderWidthX2 > $(document).height()) + top = Math.max($(document).height() - height - borderWidthX2, 0); + } + else { + left = Math.max(($(window).width() - width) / 2, 0); + top = Math.max(($(window).height() - height) / 2, 0); + } + + wrapper.css({ + "left": left, + "top": top + }); + + // set the popup size and display the popup + frame + .css({ + "width": width, + "height": height + }) + .focus(); + + wrapper.css({ + "width": width, + "height": height, + "opacity": 1.0 + }); + }) + .attr("src", url); +}; + +// Close the dropdown popup and execute a callback with the specified result +scada.Popup.prototype.closeDropdown = function (popupWnd, dialogResult, extraParams) { + var frame = $(popupWnd.frameElement); + var popupElem = frame.closest(".popup-dropdown"); + var callback = this._popupCallbacks.get(popupWnd); + this._popupCallbacks.delete(popupWnd); + popupElem.remove(); + + if (callback) { + callback(dialogResult, extraParams); + } +}; + +// Show modal dialog with the specified url. +// opt_callback is a function (dialogResult, extraParams), +// requires Bootstrap +scada.Popup.prototype.showModal = function (url, opt_buttons, opt_callback) { + var footerHtml = opt_buttons && opt_buttons.length ? + "" : ""; + + var modalElem = $( + ""); + $("body").append(modalElem); + + // load the frame + var modalFrame = modalElem.find(".modal-frame"); + + modalFrame + .one("load", function () { + // set the frame size + var frameBody = modalFrame.contents().find("body"); + var frameWidth = frameBody.outerWidth(true); + var frameHeight = frameBody.outerHeight(true); + + modalFrame.css({ + "width": frameWidth, + "height": frameHeight + }); + + // display the modal + var modalBody = modalElem.find(".modal-body"); + var modalPaddings = parseInt(modalBody.css("padding-left")) + parseInt(modalBody.css("padding-right")); + modalElem.find(".modal-content").css("min-width", frameWidth + modalPaddings) + + modalElem + .on('shown.bs.modal', function () { + modalFrame.focus(); + modalElem.find(".modal-title").text(modalFrame[0].contentWindow.document.title); + }) + .on('hidden.bs.modal', function () { + $(this).remove(); + }) + .modal("show"); + }) + .attr("src", url); +}; + +// Popup instance locator object +scada.popupLocator = { + // Find and return an existing popup object + getPopup: function () { + var wnd = window; + while (wnd) { + if (wnd.popup) { + return wnd.popup; + } + wnd = wnd == window.top ? null : window.parent; + } + return null; + } +}; \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/cmdobj.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/cmdobj.js index 42c23ac65..4cf879bef 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/cmdobj.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/cmdobj.js @@ -20,7 +20,8 @@ scada.cmd = { show: function (rootPath, viewID, ctrlCnlNum, opt_callback) { var popup = scada.popupLocator.getPopup(); if (popup) { - popup.showModal(rootPath + "Calendar.aspx", ["execute", "close"], opt_callback); + popup.showModal(rootPath + "Calendar.aspx", + [scada.ModalButtons.EXEC, scada.ModalButtons.CANCEL], opt_callback); } } } \ No newline at end of file From a92734bfbe253936944a7b8495d5b816c88e8eec Mon Sep 17 00:00:00 2001 From: 2mik Date: Wed, 13 Jul 2016 23:45:00 +0300 Subject: [PATCH 183/382] ScadaWeb5: command --- ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj | 8 ++++ .../PlgTable/plugins/Table/Command.aspx | 25 +++++++++++ .../PlgTable/plugins/Table/Command.aspx.cs | 41 ++++++++++++++++++ .../plugins/Table/Command.aspx.designer.cs | 42 +++++++++++++++++++ .../PlgTable/plugins/Table/js/cmdobj.js | 2 +- ScadaWeb/ScadaWebShell5Beta/Error.aspx | 2 + .../ScadaWebShell5Beta.csproj | 1 + ScadaWeb/ScadaWebShell5Beta/css/error.css | 6 +++ ScadaWeb/ScadaWebShell5Beta/css/error.less | 9 ++++ ScadaWeb/ScadaWebShell5Beta/css/error.min.css | 2 +- .../ScadaWebShell5Beta/js/controls/popup.js | 2 +- ScadaWeb/ScadaWebShell5Beta/js/error.js | 7 ++++ 12 files changed, 144 insertions(+), 3 deletions(-) create mode 100644 ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx create mode 100644 ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs create mode 100644 ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs create mode 100644 ScadaWeb/ScadaWebShell5Beta/js/error.js diff --git a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj index 7a4ecb8a4..2291bb4a0 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj +++ b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj @@ -84,6 +84,7 @@ + events.less @@ -127,6 +128,13 @@ + + Command.aspx + ASPXCodeBehind + + + Command.aspx + Events.aspx ASPXCodeBehind diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx new file mode 100644 index 000000000..628768c18 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx @@ -0,0 +1,25 @@ +<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Command.aspx.cs" Inherits="Scada.Web.Plugins.Table.WFrmCommand" %> + + + + + + + + + + Command - Rapid SCADA + + + + + + +
+ + +
+ + + diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs new file mode 100644 index 000000000..7654d2cf0 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs @@ -0,0 +1,41 @@ +/* + * Copyright 2016 Mikhail Shiryaev + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Product : Rapid SCADA + * Module : PlgTable + * Summary : Command dialog web form + * + * Author : Mikhail Shiryaev + * Created : 2016 + * Modified : 2016 + */ + +using System; + +namespace Scada.Web.Plugins.Table +{ + /// + /// Command dialog web form + /// Веб-форма диалога отправки команды ТУ + /// + public partial class WFrmCommand : System.Web.UI.Page + { + protected void Page_Load(object sender, EventArgs e) + { + + } + } +} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs new file mode 100644 index 000000000..09421ddad --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs @@ -0,0 +1,42 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Scada.Web.Plugins.Table { + + + public partial class WFrmCommand { + + /// + /// frmCommand control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlForm frmCommand; + + /// + /// TextBox1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox TextBox1; + + /// + /// Button1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button Button1; + } +} diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/cmdobj.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/cmdobj.js index 4cf879bef..7c95ae711 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/cmdobj.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/cmdobj.js @@ -20,7 +20,7 @@ scada.cmd = { show: function (rootPath, viewID, ctrlCnlNum, opt_callback) { var popup = scada.popupLocator.getPopup(); if (popup) { - popup.showModal(rootPath + "Calendar.aspx", + popup.showModal(rootPath + "plugins/Table/Command.aspx?viewID=" + viewID + "&ctrlCnlNum=" + ctrlCnlNum, [scada.ModalButtons.EXEC, scada.ModalButtons.CANCEL], opt_callback); } } diff --git a/ScadaWeb/ScadaWebShell5Beta/Error.aspx b/ScadaWeb/ScadaWebShell5Beta/Error.aspx index f3bbcab51..bc15ed959 100644 --- a/ScadaWeb/ScadaWebShell5Beta/Error.aspx +++ b/ScadaWeb/ScadaWebShell5Beta/Error.aspx @@ -9,6 +9,8 @@ + +
Rapid SCADA
diff --git a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj index 48ab3b54e..2f85ff59a 100644 --- a/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj +++ b/ScadaWeb/ScadaWebShell5Beta/ScadaWebShell5Beta.csproj @@ -168,6 +168,7 @@ + diff --git a/ScadaWeb/ScadaWebShell5Beta/css/error.css b/ScadaWeb/ScadaWebShell5Beta/css/error.css index 623ec5c0b..9af499aee 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/error.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/error.css @@ -5,6 +5,12 @@ font-family: 'Open Sans', sans-serif; font-size: 14px; } +body.popup { + background-color: white; +} +div { + box-sizing: border-box; +} #divHeader { width: 100%; height: 30px; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/error.less b/ScadaWeb/ScadaWebShell5Beta/css/error.less index 5fbacf81d..d3e77f372 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/error.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/error.less @@ -2,6 +2,7 @@ @header-heigth: 30px; @error-fore-color: red; +@popup-back-color: white; body { margin: 0; @@ -11,6 +12,14 @@ body { font-size: 14px; } +body.popup { + background-color: @popup-back-color; +} + +div { + box-sizing: border-box; +} + #divHeader { width: 100%; height: @header-heigth; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/error.min.css b/ScadaWeb/ScadaWebShell5Beta/css/error.min.css index 820995fed..1fd7fb0d4 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/error.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/error.min.css @@ -1 +1 @@ -body{margin:0;padding:0;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:14px;}#divHeader{width:100%;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;font-size:17px;line-height:30px;white-space:nowrap;}#divContent{padding:20px;}h1{font-size:30px;font-weight:500;margin:0 0 10px;color:#f00;}div.error{color:#f00;margin:0 0 10px;} \ No newline at end of file +body{margin:0;padding:0;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:14px;}body.popup{background-color:#fff;}div{box-sizing:border-box;}#divHeader{width:100%;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;font-size:17px;line-height:30px;white-space:nowrap;}#divContent{padding:20px;}h1{font-size:30px;font-weight:500;margin:0 0 10px;color:#f00;}div.error{color:#f00;margin:0 0 10px;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js b/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js index 3423f56ef..e7099bef2 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js @@ -258,7 +258,7 @@ scada.Popup.prototype.showModal = function (url, opt_buttons, opt_callback) { var frameHeight = frameBody.outerHeight(true); modalFrame.css({ - "width": frameWidth, + "width": frameWidth ? frameWidth : "100%", "height": frameHeight }); diff --git a/ScadaWeb/ScadaWebShell5Beta/js/error.js b/ScadaWeb/ScadaWebShell5Beta/js/error.js new file mode 100644 index 000000000..b2797361c --- /dev/null +++ b/ScadaWeb/ScadaWebShell5Beta/js/error.js @@ -0,0 +1,7 @@ +$(document).ready(function () { + // change the form style if it is shown as a popup + if (window != window.top) { + $("body").addClass("popup"); + $("#divHeader").css("display", "none"); + } +}); \ No newline at end of file From 03c76b53fe74003b142d6c3dc00ea5ba52d96c6d Mon Sep 17 00:00:00 2001 From: 2mik Date: Thu, 14 Jul 2016 17:26:28 +0300 Subject: [PATCH 184/382] PlgTable: commands --- .../OpenPlugins/PlgChart/css/globalvar.less | 4 +- .../OpenPlugins/PlgScheme/css/globalvar.less | 6 +- .../PlgScheme/plugins/Scheme/css/scheme.less | 2 +- ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj | 7 + .../OpenPlugins/PlgTable/compilerconfig.json | 4 + .../OpenPlugins/PlgTable/css/globalvar.less | 4 +- .../PlgTable/plugins/Table/Command.aspx | 40 ++++- .../PlgTable/plugins/Table/Command.aspx.cs | 99 ++++++++++++ .../plugins/Table/Command.aspx.designer.cs | 143 +++++++++++++++++- .../PlgTable/plugins/Table/css/command.css | 52 +++++++ .../PlgTable/plugins/Table/css/command.less | 70 +++++++++ .../plugins/Table/css/command.min.css | 1 + .../PlgTable/plugins/Table/css/events.css | 2 +- .../PlgTable/plugins/Table/css/events.min.css | 2 +- .../PlgTable/plugins/Table/css/table.css | 2 +- .../PlgTable/plugins/Table/css/table.min.css | 2 +- .../plugins/Table/css/tablecommon.css | 2 +- .../plugins/Table/css/tablecommon.less | 4 +- .../plugins/Table/css/tablecommon.min.css | 2 +- .../ScadaWebShell5Beta/css/globalvar.less | 4 +- ScadaWeb/ScadaWebShell5Beta/css/login.css | 3 +- ScadaWeb/ScadaWebShell5Beta/css/login.less | 3 +- ScadaWeb/ScadaWebShell5Beta/css/login.min.css | 2 +- .../ScadaWebShell5Beta/css/mastermain.css | 2 +- .../ScadaWebShell5Beta/css/mastermain.less | 2 +- .../ScadaWebShell5Beta/css/mastermain.min.css | 2 +- .../ScadaWebShell5Beta/js/controls/popup.js | 5 +- ScadaWeb/ScadaWebShell5Beta/js/mastermain.js | 4 +- 28 files changed, 443 insertions(+), 32 deletions(-) create mode 100644 ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.css create mode 100644 ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.less create mode 100644 ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.min.css diff --git a/ScadaWeb/OpenPlugins/PlgChart/css/globalvar.less b/ScadaWeb/OpenPlugins/PlgChart/css/globalvar.less index 1be849052..f9d3fa89a 100644 --- a/ScadaWeb/OpenPlugins/PlgChart/css/globalvar.less +++ b/ScadaWeb/OpenPlugins/PlgChart/css/globalvar.less @@ -2,7 +2,9 @@ @content-fore-color: #444; @panel-border-color: #e5e5e5; @default-font-family: 'Open Sans', sans-serif; -@default-font-size: 12px; +@secondary-font-family: Arial, Helvetica, sans-serif; +@form-font-size: 13px; +@data-font-size: 12px; @menu-back-color: #23282d; @menu-back-color-air: white; diff --git a/ScadaWeb/OpenPlugins/PlgScheme/css/globalvar.less b/ScadaWeb/OpenPlugins/PlgScheme/css/globalvar.less index 335f490e4..f9d3fa89a 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/css/globalvar.less +++ b/ScadaWeb/OpenPlugins/PlgScheme/css/globalvar.less @@ -2,7 +2,9 @@ @content-fore-color: #444; @panel-border-color: #e5e5e5; @default-font-family: 'Open Sans', sans-serif; -@default-font-size: 12px; +@secondary-font-family: Arial, Helvetica, sans-serif; +@form-font-size: 13px; +@data-font-size: 12px; @menu-back-color: #23282d; @menu-back-color-air: white; @@ -15,3 +17,5 @@ @menu-item-hover-fore-color: #00b9eb; @menu-item-selected-back-color: #0073aa; @menu-item-selected-fore-color: white; +@menu-item-disabled-fore-color: @menu-fore-color-dark; +@menu-item-disabled-fore-color-air: #ccc; diff --git a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.less b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.less index fcce35019..fc39f17e6 100644 --- a/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.less +++ b/ScadaWeb/OpenPlugins/PlgScheme/plugins/Scheme/css/scheme.less @@ -14,7 +14,7 @@ body { padding: 0; background-color: @scheme-back-color; font-family: @scheme-font-family; - font-size: @default-font-size; + font-size: @data-font-size; overflow: hidden; } diff --git a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj index 2291bb4a0..67ba2e03f 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj +++ b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj @@ -85,6 +85,12 @@ + + command.less + + + command.css + events.less @@ -167,6 +173,7 @@ + Web.config diff --git a/ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json b/ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json index 8394540d4..b4f605eb9 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json +++ b/ScadaWeb/OpenPlugins/PlgTable/compilerconfig.json @@ -18,5 +18,9 @@ { "outputFile": "css/controls/tableheader.css", "inputFile": "css/controls/tableheader.less" + }, + { + "outputFile": "plugins/Table/css/command.css", + "inputFile": "plugins/Table/css/command.less" } ] \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/css/globalvar.less b/ScadaWeb/OpenPlugins/PlgTable/css/globalvar.less index 1be849052..f9d3fa89a 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/css/globalvar.less +++ b/ScadaWeb/OpenPlugins/PlgTable/css/globalvar.less @@ -2,7 +2,9 @@ @content-fore-color: #444; @panel-border-color: #e5e5e5; @default-font-family: 'Open Sans', sans-serif; -@default-font-size: 12px; +@secondary-font-family: Arial, Helvetica, sans-serif; +@form-font-size: 13px; +@data-font-size: 12px; @menu-back-color: #23282d; @menu-back-color-air: white; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx index 628768c18..e240340c9 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx @@ -11,15 +11,47 @@ Command - Rapid SCADA +
-
- - -
+ +
+ + + + + + + + + + + + + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + +
+
diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs index 7654d2cf0..e893b5af7 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs @@ -23,7 +23,12 @@ * Modified : 2016 */ +using Scada.Client; +using Scada.Data.Models; +using Scada.Data.Tables; +using Scada.UI; using System; +using System.Collections.Generic; namespace Scada.Web.Plugins.Table { @@ -33,9 +38,103 @@ namespace Scada.Web.Plugins.Table /// public partial class WFrmCommand : System.Web.UI.Page { + /// + /// Дискретная команда + /// + private struct DiscreteCmd + { + public int Val { get; set; } + public string Text { get; set; } + } + + + /// + /// Получить список дискретных команд + /// + private List GetDiscreteCmds(string[] cmdValArr) + { + List discreteCmds = new List(); + for (int i = 0, len = cmdValArr.Length; i < len; i++) + { + discreteCmds.Add(new DiscreteCmd() { Val = i, Text = cmdValArr[i] }); + } + return discreteCmds; + } + + protected void Page_Load(object sender, EventArgs e) { + AppData appData = AppData.GetAppData(); + UserData userData = UserData.GetUserData(); + + // получение параметров запроса + int viewID; + int.TryParse(Request.QueryString["viewID"], out viewID); + int ctrlCnlNum; + int.TryParse(Request.QueryString["ctrlCnlNum"], out ctrlCnlNum); + + // проверка прав + if (!userData.LoggedOn || + !userData.UserRights.GetViewRights(viewID).ControlRight || + !userData.WebSettings.CmdEnabled) + throw new ScadaException(CommonPhrases.NoRights); + + Type viewType = userData.UserViews.GetViewType(viewID); + BaseView view = appData.ViewCache.GetView(viewType, viewID, true); + + if (!view.ContainsCtrlCnl(ctrlCnlNum)) + throw new ScadaException(CommonPhrases.NoRights); + + if (!IsPostBack) + { + // перевод веб-страницы + Translator.TranslatePage(Page, "Scada.Web.Plugins.Table.WFrmCommand"); + + // получение канала управления + CtrlCnlProps ctrlCnlProps = appData.DataAccess.GetCtrlCnlProps(ctrlCnlNum); + + if (ctrlCnlProps == null) + { + // вывести сообщение, что канал не найден, + // элементы управления ввода команды скрыты по умолчанию + lblCtrlCnl.Visible = false; + lblCtrlCnlNotFound.Visible = true; + } + else + { + // вывод информации по каналу управления + lblCtrlCnl.Text = string.Format("[{0}] {1}", ctrlCnlProps.CtrlCnlNum, ctrlCnlProps.CtrlCnlName); + lblObj.Text = string.Format("[{0}] {1}", ctrlCnlProps.ObjNum, ctrlCnlProps.ObjName); + lblDev.Text = string.Format("[{0}] {1}", ctrlCnlProps.KPNum, ctrlCnlProps.KPName); + + // установка видимости поля для ввода пароля + pnlPassword.Visible = userData.WebSettings.CmdPassword; + // настройка элементов управления в зависимости от типа команды + switch (ctrlCnlProps.CmdTypeID) + { + case BaseValues.CmdTypes.Standard: + if (ctrlCnlProps.CmdValArr == null && ctrlCnlProps.CmdValArr.Length > 0) + { + pnlRealValue.Visible = true; + } + else + { + repCommands.DataSource = GetDiscreteCmds(ctrlCnlProps.CmdValArr); + repCommands.DataBind(); + pnlDiscreteValue.Visible = true; + } + hidCmdEnabled.Value = "true"; + break; + case BaseValues.CmdTypes.Binary: + break; + case BaseValues.CmdTypes.Request: + break; + default: + break; + } + } + } } } } \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs index 09421ddad..5cd685cd4 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs @@ -22,21 +22,156 @@ public partial class WFrmCommand { protected global::System.Web.UI.HtmlControls.HtmlForm frmCommand; /// - /// TextBox1 control. + /// hidCmdEnabled control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.WebControls.TextBox TextBox1; + protected global::System.Web.UI.WebControls.HiddenField hidCmdEnabled; /// - /// Button1 control. + /// lblCtrlCnlCaption control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.WebControls.Button Button1; + protected global::System.Web.UI.WebControls.Label lblCtrlCnlCaption; + + /// + /// lblCtrlCnl control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblCtrlCnl; + + /// + /// lblCtrlCnlNotFound control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblCtrlCnlNotFound; + + /// + /// lblObjCaption control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblObjCaption; + + /// + /// lblObj control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblObj; + + /// + /// lblDevCaption control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblDevCaption; + + /// + /// lblDev control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblDev; + + /// + /// pnlPassword control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Panel pnlPassword; + + /// + /// lblPassword control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblPassword; + + /// + /// txtPassword control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox txtPassword; + + /// + /// pnlRealValue control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Panel pnlRealValue; + + /// + /// lblCmdVal control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblCmdVal; + + /// + /// txtCmdVal control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox txtCmdVal; + + /// + /// pnlDiscreteValue control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Panel pnlDiscreteValue; + + /// + /// lblCommand control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblCommand; + + /// + /// repCommands control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Repeater repCommands; } } diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.css new file mode 100644 index 000000000..3c6821e83 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.css @@ -0,0 +1,52 @@ +body { + margin: 0; + padding: 0; + background-color: white; + font-family: 'Open Sans', sans-serif; + font-size: 13px; + min-width: 200px; + overflow: hidden; +} +table { + border-collapse: collapse; +} +label, +.cmd-lbl { + display: inline-block; + font-weight: 600; + margin-bottom: 5px; +} +input[type=text], +input[type=password] { + width: calc(100% - 5px); +} +#tblInfo { + margin-bottom: 10px; +} +#tblInfo th { + font-weight: normal; + padding: 2px 0; + vertical-align: top; +} +#tblInfo td { + color: red; + padding: 2px 10px; + vertical-align: top; +} +#pnlPassword { + margin-bottom: 10px; +} +/* Real Value Commands */ +#txtCmdVal { + color: red; +} +/* Discrete Value Commands */ +#divCommands { + display: flex; + flex-wrap: wrap; +} +#divCommands input { + color: red; + flex-grow: 1; + margin: 0 5px 5px 0; +} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.less b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.less new file mode 100644 index 000000000..53376b4cb --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.less @@ -0,0 +1,70 @@ +@import "../../../css/globalvar.less"; + +@form-back-color: white; +@info-fore-color: red; +@cmd-fore-color: red; + +body { + margin: 0; + padding: 0; + background-color: @form-back-color; + font-family: @default-font-family; + font-size: @form-font-size; + min-width: 200px; + overflow: hidden; +} + +table { + border-collapse: collapse; +} + +label, +.cmd-lbl { + display: inline-block; + font-weight: 600; + margin-bottom: 5px; +} + +input[type=text], +input[type=password] { + width: calc(~"100% - 5px"); +} + +#tblInfo { + margin-bottom: 10px; +} + +#tblInfo th { + font-weight: normal; + padding: 2px 0; + vertical-align: top; +} + +#tblInfo td { + color: @info-fore-color; + padding: 2px 10px; + vertical-align: top; +} + +#pnlPassword { + margin-bottom: 10px; +} + +/* Real Value Commands */ + +#txtCmdVal { + color: @cmd-fore-color; +} + +/* Discrete Value Commands */ + +#divCommands { + display: flex; + flex-wrap: wrap; +} + +#divCommands input { + color: @cmd-fore-color; + flex-grow: 1; + margin: 0 5px 5px 0; +} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.min.css new file mode 100644 index 000000000..0a39137b0 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.min.css @@ -0,0 +1 @@ +body{margin:0;padding:0;background-color:#fff;font-family:'Open Sans',sans-serif;font-size:13px;min-width:200px;overflow:hidden;}table{border-collapse:collapse;}label,.cmd-lbl{display:inline-block;font-weight:600;margin-bottom:5px;}input[type=text],input[type=password]{width:calc(100% - 5px);}#tblInfo{margin-bottom:10px;}#tblInfo th{font-weight:normal;padding:2px 0;vertical-align:top;}#tblInfo td{color:#f00;padding:2px 10px;vertical-align:top;}#pnlPassword{margin-bottom:10px;}#txtCmdVal{color:#f00;}#divCommands{display:flex;flex-wrap:wrap;}#divCommands input{color:#f00;flex-grow:1;margin:0 5px 5px 0;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css index b036da1bf..d3cb6caa3 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.css @@ -4,7 +4,7 @@ background-color: #f1f1f1; color: black; font-family: 'Open Sans', sans-serif; - font-size: 12px; + font-size: 13px; overflow: hidden; } .hidden { diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css index 4c64608e8..3dad79d9e 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/events.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}.hidden{display:none;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a{color:#0073aa;text-decoration:none;}#divTblWrapper td a:hover{color:#00a0d2;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divToolbar .tool-btn.selected{background-color:#0073aa;color:#fff;}#divToolbar .tool-btn.disabled{color:#ccc;cursor:default;}#tblEvents{width:100%;}#divTblWrapper tr.event:hover{background-color:#e0e0e0;color:#000;}#tblEvents td.num,#tblEvents td.time,#tblEvents td.ack{text-align:center;}#tblEvents td.num,#tblEvents td.ack{width:5%;}#tblEvents td.time{width:10%;}#tblEvents td.obj,#tblEvents td.dev,#tblEvents td.cnl{width:15%;}#tblEvents td.text{width:35%;}#divLoading,#divNoEvents{padding:5px 10px;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:13px;overflow:hidden;}.hidden{display:none;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a{color:#0073aa;text-decoration:none;}#divTblWrapper td a:hover{color:#00a0d2;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divToolbar .tool-btn.selected{background-color:#0073aa;color:#fff;}#divToolbar .tool-btn.disabled{color:#ccc;cursor:default;}#tblEvents{width:100%;}#divTblWrapper tr.event:hover{background-color:#e0e0e0;color:#000;}#tblEvents td.num,#tblEvents td.time,#tblEvents td.ack{text-align:center;}#tblEvents td.num,#tblEvents td.ack{width:5%;}#tblEvents td.time{width:10%;}#tblEvents td.obj,#tblEvents td.dev,#tblEvents td.cnl{width:15%;}#tblEvents td.text{width:35%;}#divLoading,#divNoEvents{padding:5px 10px;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css index 98c3cf212..54dfca771 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.css @@ -4,7 +4,7 @@ background-color: #f1f1f1; color: black; font-family: 'Open Sans', sans-serif; - font-size: 12px; + font-size: 13px; overflow: hidden; } .hidden { diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css index 32c4cdcd3..c64921d84 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/table.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}.hidden{display:none;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a{color:#0073aa;text-decoration:none;}#divTblWrapper td a:hover{color:#00a0d2;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td.cap{padding:2px 5px;}#divTblWrapper td.cap img{width:16px;height:16px;margin:0 5px 0 0;border:none;}#divTblWrapper td.cap a{height:18px;margin-right:5px;display:inline-block;line-height:18px;}#divTblWrapper td.cap span.cmd{display:inline-block;width:18px;height:18px;cursor:pointer;color:#a00;font-size:15px;line-height:18px;text-align:center;}#divTblWrapper td.cap span.cmd:hover{color:#f00;}#divTblWrapper td.cap span.cmd::before{content:"";font-family:FontAwesome;}#divTblWrapper td.cap span.hint{background-color:#fff;border:1px solid #888;border-radius:5px;box-shadow:2px 2px 5px #aaa;color:#444;display:none;line-height:normal;padding:5px 10px;position:fixed;z-index:10;}#divTblWrapper td.cap span.hint.show{display:inline-block;}#divTblWrapper td.cur,#divTblWrapper td.hour{text-align:center;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:13px;overflow:hidden;}.hidden{display:none;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a{color:#0073aa;text-decoration:none;}#divTblWrapper td a:hover{color:#00a0d2;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;}#divTblWrapper tr.item:hover{background-color:#e0e0e0;color:#000;}#divTblWrapper td.cap{padding:2px 5px;}#divTblWrapper td.cap img{width:16px;height:16px;margin:0 5px 0 0;border:none;}#divTblWrapper td.cap a{height:18px;margin-right:5px;display:inline-block;line-height:18px;}#divTblWrapper td.cap span.cmd{display:inline-block;width:18px;height:18px;cursor:pointer;color:#a00;font-size:15px;line-height:18px;text-align:center;}#divTblWrapper td.cap span.cmd:hover{color:#f00;}#divTblWrapper td.cap span.cmd::before{content:"";font-family:FontAwesome;}#divTblWrapper td.cap span.hint{background-color:#fff;border:1px solid #888;border-radius:5px;box-shadow:2px 2px 5px #aaa;color:#444;display:none;line-height:normal;padding:5px 10px;position:fixed;z-index:10;}#divTblWrapper td.cap span.hint.show{display:inline-block;}#divTblWrapper td.cur,#divTblWrapper td.hour{text-align:center;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css index e2a215af9..f15c4bb27 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.css @@ -4,7 +4,7 @@ background-color: #f1f1f1; color: black; font-family: 'Open Sans', sans-serif; - font-size: 12px; + font-size: 13px; overflow: hidden; } .hidden { diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less index 76c40eab5..474c6f9ce 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.less @@ -24,7 +24,7 @@ @cell-border-color-hdr: #888; @link-fore-color: #0073aa; @link-hover-fore-color: #00a0d2; -@cell-font-size: @default-font-size; +@cell-font-size: @data-font-size; @cell-height: 18px; body { @@ -33,7 +33,7 @@ body { background-color: @content-back-color; color: @table-fore-color; font-family: @default-font-family; - font-size: @default-font-size; + font-size: @form-font-size; overflow: hidden; } diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css index e2e8d2ce7..9f799f1d6 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/tablecommon.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:12px;overflow:hidden;}.hidden{display:none;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a{color:#0073aa;text-decoration:none;}#divTblWrapper td a:hover{color:#00a0d2;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;} \ No newline at end of file +body{margin:0;padding:30px 0 0 0;background-color:#f1f1f1;color:#000;font-family:'Open Sans',sans-serif;font-size:13px;overflow:hidden;}.hidden{display:none;}#divToolbar{position:fixed;left:0;top:0;width:100%;height:30px;padding:0 10px;background-color:transparent;color:#444;white-space:nowrap;}#divToolbar .tool-ctrl{height:30px;margin:0 10px 0 0;display:inline-block;}#divToolbar .tool-ctrl input,#divToolbar .tool-ctrl select{margin-top:4px;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;font-size:14px;height:22px;}#divToolbar .tool-btn{height:30px;margin:0 5px 0 0;padding:0 10px;background-color:#fff;cursor:pointer;display:inline-block;font-size:13px;line-height:30px;vertical-align:top;}#divToolbar .tool-btn i{color:#8f8f8f;font-size:14px;}#divToolbar .tool-btn:hover,#divToolbar .tool-btn:hover i{color:#00b9eb;}#spanDate{position:relative;}#spanDate input{width:150px;padding-right:20px;}#spanDate i{position:absolute;right:5px;height:30px;line-height:30px;color:#8f8f8f;cursor:pointer;font-size:14px;}#spanDate i:hover{color:#00b9eb;}#spanDate i.error::before{color:#a00;content:"";}#divDebugTools{padding:0 10px;display:none;vertical-align:top;}#divDebugTools.show{display:inline-block;}#divTblWrapper{padding:0 10px;overflow:auto;}#divTblWrapper table{border-collapse:collapse;}#divTblWrapper tr{background-color:#fff;}#divTblWrapper tr.hdr{background-color:#ccc;}#divTblWrapper tr.hdr.fixed-table-header{left:10px;}#divTblWrapper tr.alt{background-color:#f9f9f9;}#divTblWrapper td{border:1px solid #999;font-size:12px;line-height:18px;padding:2px 3px;vertical-align:middle;white-space:nowrap;}#divTblWrapper tr.hdr td{border-color:#888;font-weight:600;padding:2px 7px;text-align:center;}#divTblWrapper td a{color:#0073aa;text-decoration:none;}#divTblWrapper td a:hover{color:#00a0d2;}#divTblWrapper td a,#divTblWrapper td span,#divTblWrapper td img{vertical-align:middle;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less b/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less index 1be849052..f9d3fa89a 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less @@ -2,7 +2,9 @@ @content-fore-color: #444; @panel-border-color: #e5e5e5; @default-font-family: 'Open Sans', sans-serif; -@default-font-size: 12px; +@secondary-font-family: Arial, Helvetica, sans-serif; +@form-font-size: 13px; +@data-font-size: 12px; @menu-back-color: #23282d; @menu-back-color-air: white; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/login.css b/ScadaWeb/ScadaWebShell5Beta/css/login.css index aba5fae15..57ca5becb 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/login.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/login.css @@ -3,7 +3,7 @@ padding: 0; background-color: #f1f1f1; font-family: 'Open Sans', sans-serif; - font-size: 12px; + font-size: 13px; } #divLoginContainer { margin: 40px 0 0 0; @@ -22,7 +22,6 @@ div.alert { margin: 0 0 10px 0; padding-left: 25px; - font-size: 13px; text-align: left; } #divLogin { diff --git a/ScadaWeb/ScadaWebShell5Beta/css/login.less b/ScadaWeb/ScadaWebShell5Beta/css/login.less index e76e3fcba..425026483 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/login.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/login.less @@ -10,7 +10,7 @@ body { padding: 0; background-color: @content-back-color; font-family: @default-font-family; - font-size: @default-font-size; + font-size: @form-font-size; } #divLoginContainer { @@ -34,7 +34,6 @@ body { div.alert { margin: 0 0 10px 0; padding-left: @side-padding; - font-size: 13px; text-align: left; } diff --git a/ScadaWeb/ScadaWebShell5Beta/css/login.min.css b/ScadaWeb/ScadaWebShell5Beta/css/login.min.css index 98594b4c9..7ef97afe2 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/login.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/login.min.css @@ -1 +1 @@ -body{margin:0;padding:0;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}#divLoginContainer{margin:40px 0 0 0;text-align:center;}#divTitle{margin:20px 0;font-size:30px;}#divAlertsOuter{text-align:center;}#divAlertsInner{display:inline-block;}div.alert{margin:0 0 10px 0;padding-left:25px;font-size:13px;text-align:left;}#divLogin{padding:25px 25px 35px;background-color:#fff;border:1px solid #ddd;border-radius:1px;color:#777;display:inline-block;text-align:left;}#divLogin span,#divLogin label{font-size:14px;font-weight:normal;}#divLogin input[type=text],#divLogin input[type=password]{width:265px;margin:0 0 20px 0;color:#000;}#divLogin input[type=submit]{padding-left:20px;padding-right:20px;font-size:14px;} \ No newline at end of file +body{margin:0;padding:0;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:13px;}#divLoginContainer{margin:40px 0 0 0;text-align:center;}#divTitle{margin:20px 0;font-size:30px;}#divAlertsOuter{text-align:center;}#divAlertsInner{display:inline-block;}div.alert{margin:0 0 10px 0;padding-left:25px;text-align:left;}#divLogin{padding:25px 25px 35px;background-color:#fff;border:1px solid #ddd;border-radius:1px;color:#777;display:inline-block;text-align:left;}#divLogin span,#divLogin label{font-size:14px;font-weight:normal;}#divLogin input[type=text],#divLogin input[type=password]{width:265px;margin:0 0 20px 0;color:#000;}#divLogin input[type=submit]{padding-left:20px;padding-right:20px;font-size:14px;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css index e7b18c1ae..dc7285b4f 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.css @@ -3,7 +3,7 @@ padding: 30px 0 0 250px; background-color: #f1f1f1; font-family: 'Open Sans', sans-serif; - font-size: 12px; + font-size: 13px; } a, a:visited, diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less index 512c56317..bff9cb84e 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.less @@ -10,7 +10,7 @@ body { padding: @header-heigth 0 0 @left-pane-width; background-color: @content-back-color; font-family: @default-font-family; - font-size: @default-font-size; + font-size: @form-font-size; } a, diff --git a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css index 59c466821..595529384 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/mastermain.min.css @@ -1 +1 @@ -body{margin:0;padding:30px 0 0 250px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:12px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;}#divMainHeader .hdr-btn{height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;display:inline-block;font-size:13px;text-decoration:none;white-space:nowrap;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenuBtn{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;white-space:nowrap;}#divMainUserMenu{float:right;height:30px;display:inline-block;}#lblMainNormalViewBtn{position:fixed;top:0;right:0;height:30px;padding:0 10px;background-color:#fff;display:none;line-height:30px;opacity:.5;cursor:pointer;color:#9ca1a6;font-size:13px;}#lblMainNormalViewBtn:hover{color:#00b9eb;opacity:1;}#divMainLeftPane{position:fixed;left:0;top:30px;width:250px;min-height:250px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 30px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainLeftPane .tree-view .node:hover{background-color:#191e23;color:#00b9eb;}#divMainLeftPane .tree-view .node.selected{background-color:#0073aa;color:#fff;}#divMainLeftPane .tree-view .node.disabled{color:#9ca1a6;}#divMainLeftPane .tree-view .expander::before{font-size:10px;}#divMainMenu .node[data-level="1"],#divMainMenu .node[data-level="2"],#divMainMenu .node[data-level="3"],#divMainMenu .node[data-level="4"],#divMainMenu .node[data-level="5"]{color:#9ca1a6;font-size:13px;}#divMainMenu .tree-view .node{padding:5px 10px;}#divMainMenu .tree-view .expander.left{display:none;width:0;}#divMainMenu .tree-view .expander.right{display:table-cell;width:20px;}#divMainExplorer{font-size:13px;line-height:13px;}#divMainExplorer .tree-view .node{padding:3px 5px 3px 10px;}#divMainExplorer .tree-view .expander{padding:4px 0 0 0;}#divMainExplorer .tree-view .icon{width:21px;min-width:21px;}#divMainExplorer .tree-view img{position:relative;top:-2px;}#divMainCollapseLeftPaneBtn{position:absolute;bottom:0;width:220px;height:30px;margin:0 0 0 30px;padding:0 10px;color:#9ca1a6;cursor:pointer;font-size:13px;line-height:30px;}#divMainCollapseLeftPaneBtn i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapseLeftPaneBtn:hover{color:#00b9eb;}#divMainContent{overflow:auto;} \ No newline at end of file +body{margin:0;padding:30px 0 0 250px;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:13px;}a,a:visited,a:hover,a:focus,a:active{outline:0;color:inherit;text-decoration:none;}#divMainHeader{position:fixed;left:0;top:0;width:100%;min-width:300px;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;line-height:30px;}#divMainHeader .hdr-btn{height:30px;margin:0 0 0 5px;padding:0 10px;cursor:pointer;display:inline-block;font-size:13px;text-decoration:none;white-space:nowrap;}#divMainHeader .hdr-btn:hover{background-color:#32373c;color:#00b9eb;text-decoration:none;}#divMainHeader .txt-btn{color:#eee;}#divMainHeader .img-btn{color:#9ca1a6;}#divMainHeader .img-btn .glyphicon{position:relative;top:2px;}#divMainHeader #spanMainShowMenuBtn{margin:0 5px 0 -10px;font-size:15px;display:none;}#spanMainAppName{display:inline-block;line-height:29px;font-size:17px;white-space:nowrap;}#divMainUserMenu{float:right;height:30px;display:inline-block;}#lblMainNormalViewBtn{position:fixed;top:0;right:0;height:30px;padding:0 10px;background-color:#fff;display:none;line-height:30px;opacity:.5;cursor:pointer;color:#9ca1a6;font-size:13px;}#lblMainNormalViewBtn:hover{color:#00b9eb;opacity:1;}#divMainLeftPane{position:fixed;left:0;top:30px;width:250px;min-height:250px;background-color:#23282d;color:#eee;font-size:14px;overflow:hidden;}#divMainLeftPane .tool-window{height:calc(100% - 30px);margin:0 0 0 30px;padding:5px 0 0 0;display:none;overflow-x:hidden;overflow-y:auto;}#divMainTabs{position:absolute;left:30px;top:0;height:30px;min-width:200px;background-color:#32373c;overflow:hidden;transform:rotate(90deg);transform-origin:left top 0;}#divMainTabs .tab{margin:0 0 0 5px;padding:0 10px;color:#9ca1a6;display:inline-block;line-height:30px;white-space:nowrap;}#divMainTabs .tab.selected{color:#eee;}#divMainTabs .tab:hover{color:#00b9eb;cursor:pointer;}#divMainLeftPane .tree-view .node:hover{background-color:#191e23;color:#00b9eb;}#divMainLeftPane .tree-view .node.selected{background-color:#0073aa;color:#fff;}#divMainLeftPane .tree-view .node.disabled{color:#9ca1a6;}#divMainLeftPane .tree-view .expander::before{font-size:10px;}#divMainMenu .node[data-level="1"],#divMainMenu .node[data-level="2"],#divMainMenu .node[data-level="3"],#divMainMenu .node[data-level="4"],#divMainMenu .node[data-level="5"]{color:#9ca1a6;font-size:13px;}#divMainMenu .tree-view .node{padding:5px 10px;}#divMainMenu .tree-view .expander.left{display:none;width:0;}#divMainMenu .tree-view .expander.right{display:table-cell;width:20px;}#divMainExplorer{font-size:13px;line-height:13px;}#divMainExplorer .tree-view .node{padding:3px 5px 3px 10px;}#divMainExplorer .tree-view .expander{padding:4px 0 0 0;}#divMainExplorer .tree-view .icon{width:21px;min-width:21px;}#divMainExplorer .tree-view img{position:relative;top:-2px;}#divMainCollapseLeftPaneBtn{position:absolute;bottom:0;width:220px;height:30px;margin:0 0 0 30px;padding:0 10px;color:#9ca1a6;cursor:pointer;font-size:13px;line-height:30px;}#divMainCollapseLeftPaneBtn i{margin-right:10px;font-size:19px;position:relative;top:2px;}#divMainCollapseLeftPaneBtn:hover{color:#00b9eb;}#divMainContent{overflow:auto;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js b/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js index e7099bef2..7f511e0ae 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js @@ -258,15 +258,16 @@ scada.Popup.prototype.showModal = function (url, opt_buttons, opt_callback) { var frameHeight = frameBody.outerHeight(true); modalFrame.css({ - "width": frameWidth ? frameWidth : "100%", + "width": "100%", "height": frameHeight }); - // display the modal + // tune the modal var modalBody = modalElem.find(".modal-body"); var modalPaddings = parseInt(modalBody.css("padding-left")) + parseInt(modalBody.css("padding-right")); modalElem.find(".modal-content").css("min-width", frameWidth + modalPaddings) + // display the modal modalElem .on('shown.bs.modal', function () { modalFrame.focus(); diff --git a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js index 035266ad2..faef185b9 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/mastermain.js @@ -52,7 +52,9 @@ scada.masterMain = { setTimeout(function () { thisObj._checkLoggedOn(); }, thisObj.CHECK_LOGGEDON_RATE); } else { // redirect to login page - setTimeout(function() { location.href = scada.env.rootPath + "Login.aspx" }, thisObj.LOGIN_DELAY); + setTimeout(function () { + location.href = scada.env.rootPath + "Login.aspx?return=" + encodeURIComponent(location.href); + }, thisObj.LOGIN_DELAY); } }); }, From 8cec8af27aea6ed0dab388f09509cd70b84fd5ad Mon Sep 17 00:00:00 2001 From: 2mik Date: Fri, 15 Jul 2016 16:30:33 +0300 Subject: [PATCH 185/382] PlgTable: commands dialog --- ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj | 1 + .../PlgTable/plugins/Table/Command.aspx | 96 +++++++++++------- .../PlgTable/plugins/Table/Command.aspx.cs | 83 ++++++++++++++-- .../plugins/Table/Command.aspx.designer.cs | 99 +++++++++++++++++++ .../PlgTable/plugins/Table/css/command.css | 15 ++- .../PlgTable/plugins/Table/css/command.less | 13 +-- .../plugins/Table/css/command.min.css | 2 +- .../PlgTable/plugins/Table/js/cmdobj.js | 2 +- .../PlgTable/plugins/Table/js/command.js | 41 ++++++++ ScadaWeb/ScadaWebCommon5Beta/AppData.cs | 14 ++- .../css/controls/notifier.css | 1 + .../css/controls/notifier.less | 1 + .../css/controls/notifier.min.css | 2 +- .../ScadaWebShell5Beta/js/api/eventtypes.js | 4 + .../ScadaWebShell5Beta/js/controls/popup.js | 53 ++++++++-- .../js/controls/splitter.js | 4 +- 16 files changed, 350 insertions(+), 81 deletions(-) create mode 100644 ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/command.js diff --git a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj index 67ba2e03f..63ad65bae 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj +++ b/ScadaWeb/OpenPlugins/PlgTable/PlgTable.csproj @@ -118,6 +118,7 @@ + diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx index e240340c9..191b3d418 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx @@ -12,46 +12,68 @@ - + + + + + +
- -
- - - - - - - - - - - - - -
-
- -
-
-
- -
-
-
- -
-
- - - -
-
+ + + + +
+ + + + + + + + + + + + + +
+
+ +
+
+
+ +
+
+
+ +
+
+ + + +
+
+
+ + + + + + + + + + + +
diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs index e893b5af7..510051d38 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs @@ -29,6 +29,7 @@ using Scada.UI; using System; using System.Collections.Generic; +using System.Web.UI.WebControls; namespace Scada.Web.Plugins.Table { @@ -48,6 +49,11 @@ private struct DiscreteCmd } + private AppData appData; // общие данные веб-приложения + private UserData userData; // данные пользователя приложения + private int ctrlCnlNum; // номер канала управления + + /// /// Получить список дискретных команд /// @@ -61,16 +67,64 @@ private List GetDiscreteCmds(string[] cmdValArr) return discreteCmds; } + /// + /// Проверить пароль, если он используется + /// + private bool CheckPassword() + { + if (pnlPassword.Visible) + { + //bool pwdOK = appData. + return true; + } + else + { + return true; + } + } + + /// + /// Отправить стандартную команду + /// + private void SendStandardCmd(double cmdVal) + { + bool result; + bool sendOK = appData.ServerComm.SendStandardCommand( + userData.UserProps.UserID, ctrlCnlNum, cmdVal, out result); + ShowCmdResult(sendOK, result); + } + + /// + /// Отобразить результат выполнения команды + /// + private void ShowCmdResult(bool sendOK, bool result) + { + mvCommand.SetActiveView(viewCmdResult); + + if (sendOK && result) + { + pnlSuccess.Visible = true; + ClientScript.RegisterStartupScript(GetType(), "Startup", "startDowncount();", true); + } + else + { + pnlError.Visible = true; + if (sendOK) + lblCmdRejected.Visible = true; + else + lblCmdNotSent.Visible = true; + } + } + protected void Page_Load(object sender, EventArgs e) { - AppData appData = AppData.GetAppData(); - UserData userData = UserData.GetUserData(); + appData = AppData.GetAppData(); + userData = UserData.GetUserData(); // получение параметров запроса int viewID; int.TryParse(Request.QueryString["viewID"], out viewID); - int ctrlCnlNum; int.TryParse(Request.QueryString["ctrlCnlNum"], out ctrlCnlNum); // проверка прав @@ -104,8 +158,10 @@ protected void Page_Load(object sender, EventArgs e) { // вывод информации по каналу управления lblCtrlCnl.Text = string.Format("[{0}] {1}", ctrlCnlProps.CtrlCnlNum, ctrlCnlProps.CtrlCnlName); - lblObj.Text = string.Format("[{0}] {1}", ctrlCnlProps.ObjNum, ctrlCnlProps.ObjName); - lblDev.Text = string.Format("[{0}] {1}", ctrlCnlProps.KPNum, ctrlCnlProps.KPName); + lblObj.Text = ctrlCnlProps.ObjNum > 0 ? + string.Format("[{0}] {1}", ctrlCnlProps.ObjNum, ctrlCnlProps.ObjName) : ""; + lblDev.Text = ctrlCnlProps.KPNum > 0 ? + string.Format("[{0}] {1}", ctrlCnlProps.KPNum, ctrlCnlProps.KPName) : ""; // установка видимости поля для ввода пароля pnlPassword.Visible = userData.WebSettings.CmdPassword; @@ -114,7 +170,7 @@ protected void Page_Load(object sender, EventArgs e) switch (ctrlCnlProps.CmdTypeID) { case BaseValues.CmdTypes.Standard: - if (ctrlCnlProps.CmdValArr == null && ctrlCnlProps.CmdValArr.Length > 0) + if (ctrlCnlProps.CmdValArr == null) { pnlRealValue.Visible = true; } @@ -136,5 +192,20 @@ protected void Page_Load(object sender, EventArgs e) } } } + + protected void btnSubmit_Click(object sender, EventArgs e) + { + appData.Log.WriteLine("!!!btnSubmit_Click"); + } + + protected void repCommands_ItemCommand(object source, RepeaterCommandEventArgs e) + { + if (CheckPassword()) + { + Button btn = (Button)e.CommandSource; + int cmdVal = int.Parse(btn.Attributes["data-cmdval"]); + SendStandardCmd(cmdVal); + } + } } } \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs index 5cd685cd4..ba55e2d9a 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs @@ -21,6 +21,24 @@ public partial class WFrmCommand { /// protected global::System.Web.UI.HtmlControls.HtmlForm frmCommand; + /// + /// mvCommand control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.MultiView mvCommand; + + /// + /// viewCmdParams control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.View viewCmdParams; + /// /// hidCmdEnabled control. /// @@ -30,6 +48,15 @@ public partial class WFrmCommand { /// protected global::System.Web.UI.WebControls.HiddenField hidCmdEnabled; + /// + /// btnSubmit control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button btnSubmit; + /// /// lblCtrlCnlCaption control. /// @@ -173,5 +200,77 @@ public partial class WFrmCommand { /// To modify move field declaration from designer file to code-behind file. /// protected global::System.Web.UI.WebControls.Repeater repCommands; + + /// + /// viewCmdResult control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.View viewCmdResult; + + /// + /// pnlSuccess control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Panel pnlSuccess; + + /// + /// lblCmdEnqueded control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblCmdEnqueded; + + /// + /// lblCloseAfter control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblCloseAfter; + + /// + /// lblSec control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblSec; + + /// + /// pnlError control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Panel pnlError; + + /// + /// lblCmdNotSent control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblCmdNotSent; + + /// + /// lblCmdRejected control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblCmdRejected; } } diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.css index 3c6821e83..90faabd8b 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.css @@ -1,4 +1,5 @@ -body { +/* Bootstrap danger color */ +body { margin: 0; padding: 0; background-color: white; @@ -20,25 +21,22 @@ input[type=text], input[type=password] { width: calc(100% - 5px); } -#tblInfo { - margin-bottom: 10px; -} #tblInfo th { font-weight: normal; padding: 2px 0; vertical-align: top; } #tblInfo td { - color: red; + color: #d9534f; padding: 2px 10px; vertical-align: top; } -#pnlPassword { - margin-bottom: 10px; +#btnSubmit { + display: none; } /* Real Value Commands */ #txtCmdVal { - color: red; + color: #d9534f; } /* Discrete Value Commands */ #divCommands { @@ -46,7 +44,6 @@ input[type=password] { flex-wrap: wrap; } #divCommands input { - color: red; flex-grow: 1; margin: 0 5px 5px 0; } \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.less b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.less index 53376b4cb..0dfa9d27c 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.less +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.less @@ -1,8 +1,8 @@ @import "../../../css/globalvar.less"; @form-back-color: white; -@info-fore-color: red; -@cmd-fore-color: red; +@info-fore-color: #d9534f; /* Bootstrap danger color */ +@cmd-fore-color: @info-fore-color; body { margin: 0; @@ -30,10 +30,6 @@ input[type=password] { width: calc(~"100% - 5px"); } -#tblInfo { - margin-bottom: 10px; -} - #tblInfo th { font-weight: normal; padding: 2px 0; @@ -46,8 +42,8 @@ input[type=password] { vertical-align: top; } -#pnlPassword { - margin-bottom: 10px; +#btnSubmit { + display: none; } /* Real Value Commands */ @@ -64,7 +60,6 @@ input[type=password] { } #divCommands input { - color: @cmd-fore-color; flex-grow: 1; margin: 0 5px 5px 0; } \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.min.css index 0a39137b0..b8c770815 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.min.css @@ -1 +1 @@ -body{margin:0;padding:0;background-color:#fff;font-family:'Open Sans',sans-serif;font-size:13px;min-width:200px;overflow:hidden;}table{border-collapse:collapse;}label,.cmd-lbl{display:inline-block;font-weight:600;margin-bottom:5px;}input[type=text],input[type=password]{width:calc(100% - 5px);}#tblInfo{margin-bottom:10px;}#tblInfo th{font-weight:normal;padding:2px 0;vertical-align:top;}#tblInfo td{color:#f00;padding:2px 10px;vertical-align:top;}#pnlPassword{margin-bottom:10px;}#txtCmdVal{color:#f00;}#divCommands{display:flex;flex-wrap:wrap;}#divCommands input{color:#f00;flex-grow:1;margin:0 5px 5px 0;} \ No newline at end of file +body{margin:0;padding:0;background-color:#fff;font-family:'Open Sans',sans-serif;font-size:13px;min-width:200px;overflow:hidden;}table{border-collapse:collapse;}label,.cmd-lbl{display:inline-block;font-weight:600;margin-bottom:5px;}input[type=text],input[type=password]{width:calc(100% - 5px);}#tblInfo th{font-weight:normal;padding:2px 0;vertical-align:top;}#tblInfo td{color:#d9534f;padding:2px 10px;vertical-align:top;}#btnSubmit{display:none;}#txtCmdVal{color:#d9534f;}#divCommands{display:flex;flex-wrap:wrap;}#divCommands input{flex-grow:1;margin:0 5px 5px 0;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/cmdobj.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/cmdobj.js index 7c95ae711..c3dee51c5 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/cmdobj.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/cmdobj.js @@ -21,7 +21,7 @@ scada.cmd = { var popup = scada.popupLocator.getPopup(); if (popup) { popup.showModal(rootPath + "plugins/Table/Command.aspx?viewID=" + viewID + "&ctrlCnlNum=" + ctrlCnlNum, - [scada.ModalButtons.EXEC, scada.ModalButtons.CANCEL], opt_callback); + [scada.ModalButtons.EXEC, scada.ModalButtons.CLOSE], opt_callback); } } } \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/command.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/command.js new file mode 100644 index 000000000..ebc559f03 --- /dev/null +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/command.js @@ -0,0 +1,41 @@ +// Popup dialogs manipulation object +var popup = scada.popupLocator.getPopup(); +// Time before closing, sec +var closeTimeout = 3; + + +// Start downcount before closing +function startDowncount() { + var spanDowncount = $("#spanDowncount"); + + var downcountFunc = function () { + if (closeTimeout) { + closeTimeout--; + spanDowncount.text(closeTimeout); + setTimeout(downcountFunc, 1000); + } else { + if (popup) { + popup.closeModal(window, true); + } + } + } + + spanDowncount.text(closeTimeout); + setTimeout(downcountFunc, 1000); +} + +$(document).ready(function () { + // hide execute button for discrete command + if ($("#pnlDiscreteValue").length > 0) { + if (popup) { + popup.setButtonVisible(window, scada.ModalButtons.EXEC, false); + } + } + + // submit the form on modal execute button click + $(window).on(scada.EventTypes.MODAL_BTN_CLICK, function (event, result) { + if (result == scada.ModalButtons.EXEC) { + $("#btnSubmit").click(); + } + }); +}); \ No newline at end of file diff --git a/ScadaWeb/ScadaWebCommon5Beta/AppData.cs b/ScadaWeb/ScadaWebCommon5Beta/AppData.cs index c288ecb9e..d5af69f00 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/AppData.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/AppData.cs @@ -52,7 +52,6 @@ public sealed class AppData private bool inited; // признак инициализации данных приложения private string cultureName; // имя используемой культуры private CommSettings commSettings; // настройки соединения с сервером - private ServerComm serverComm; // объект для обмена данными с сервером private long viewStampCntr; // счётчик для генерации меток представлений private DictUpdater scadaDataDictUpdater; // объект для обновления словаря ScadaData @@ -155,6 +154,11 @@ private AppData() /// public UserMonitor UserMonitor { get; private set; } + /// + /// Получить объект для обмена данными с сервером + /// + public ServerComm ServerComm { get; private set; } + /// /// Получить объект для потокобезопасного доступа к данным кеша клиентов /// @@ -195,10 +199,10 @@ private void InitSettingsUpdaters() /// private void CreateDataObjects() { - serverComm = new ServerComm(commSettings, Log); - DataCache dataCache = new DataCache(serverComm, Log); + ServerComm = new ServerComm(commSettings, Log); + DataCache dataCache = new DataCache(ServerComm, Log); DataAccess = new DataAccess(dataCache, Log); - ViewCache = new ViewCache(serverComm, DataAccess, Log); + ViewCache = new ViewCache(ServerComm, DataAccess, Log); } /// @@ -446,7 +450,7 @@ public bool CheckUser(string username, string password, bool checkPassword, } else { - if (serverComm.CheckUser(username, checkPassword ? password : null, out roleID)) + if (ServerComm.CheckUser(username, checkPassword ? password : null, out roleID)) { if (roleID == BaseValues.Roles.Disabled) { diff --git a/ScadaWeb/ScadaWebShell5Beta/css/controls/notifier.css b/ScadaWeb/ScadaWebShell5Beta/css/controls/notifier.css index c435faa98..cfd2808c2 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/controls/notifier.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/controls/notifier.css @@ -9,6 +9,7 @@ display: none; /*block*/ font-family: 'Open Sans', sans-serif; + font-size: 12px; overflow: auto; white-space: nowrap; } diff --git a/ScadaWeb/ScadaWebShell5Beta/css/controls/notifier.less b/ScadaWeb/ScadaWebShell5Beta/css/controls/notifier.less index 47d933887..f5990c618 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/controls/notifier.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/controls/notifier.less @@ -14,6 +14,7 @@ border-bottom: 1px solid @panel-border-color; display: none; /*block*/ font-family: @default-font-family; + font-size: @data-font-size; overflow: auto; white-space: nowrap; } diff --git a/ScadaWeb/ScadaWebShell5Beta/css/controls/notifier.min.css b/ScadaWeb/ScadaWebShell5Beta/css/controls/notifier.min.css index 424c08c79..d89325153 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/controls/notifier.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/controls/notifier.min.css @@ -1 +1 @@ -.notifier{position:fixed;top:0;left:0;max-height:100px;padding:10px;background-color:#fff;border-bottom:1px solid #e5e5e5;display:none;font-family:'Open Sans',sans-serif;overflow:auto;white-space:nowrap;}.notifier .message{color:#0073aa;}.notifier .message.error{color:#f00;} \ No newline at end of file +.notifier{position:fixed;top:0;left:0;max-height:100px;padding:10px;background-color:#fff;border-bottom:1px solid #e5e5e5;display:none;font-family:'Open Sans',sans-serif;font-size:12px;overflow:auto;white-space:nowrap;}.notifier .message{color:#0073aa;}.notifier .message.error{color:#f00;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/js/api/eventtypes.js b/ScadaWeb/ScadaWebShell5Beta/js/api/eventtypes.js index 77882f744..d7b9d1354 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/api/eventtypes.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/api/eventtypes.js @@ -27,4 +27,8 @@ scada.EventTypes = { // Current view date has been changed // Event parameter: date VIEW_DATE_CHANGED: "scada:viewDateChanged", + + // Modal dialog button is clicked + // Event parameter: dialog result + MODAL_BTN_CLICK: "scada:modalBtnClick" }; diff --git a/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js b/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js index 7f511e0ae..f63561c27 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js @@ -9,14 +9,17 @@ * - jquery * - utils.js * - * Optional: + * Requires for modal dialogs: * - bootstrap + * - eventtypes.js * - scada.modalButtonMap object */ // Rapid SCADA namespace var scada = scada || {}; +/********** Modal Dialog Buttons **********/ + // Modal dialog buttons enumeration scada.ModalButtons = { OK: "ok", @@ -27,6 +30,8 @@ scada.ModalButtons = { CLOSE: "close" }; +/********** Popup **********/ + // Popup dialogs manipulation type scada.Popup = function () { // Window that holds popups @@ -106,15 +111,13 @@ scada.Popup.prototype._genModalButtonsHtml = function (buttons) { btnText = btn; } - if (btn == scada.ModalButtons.CANCEL || btn == scada.ModalButtons.CLOSE) { - html += ""; - } else { - var subclass = btn == scada.ModalButtons.OK || btn == scada.ModalButtons.YES ? "btn-primary" : - (btn == scada.ModalButtons.EXEC ? "btn-danger" : "btn-default"); + var subclass = btn == scada.ModalButtons.OK || btn == scada.ModalButtons.YES ? "btn-primary" : + (btn == scada.ModalButtons.EXEC ? "btn-danger" : "btn-default"); + var dismiss = btn == scada.ModalButtons.CANCEL || btn == scada.ModalButtons.CLOSE ? + " data-dismiss='modal'" : ""; - html += ""; - } + html += ""; } return html; @@ -267,6 +270,16 @@ scada.Popup.prototype.showModal = function (url, opt_buttons, opt_callback) { var modalPaddings = parseInt(modalBody.css("padding-left")) + parseInt(modalBody.css("padding-right")); modalElem.find(".modal-content").css("min-width", frameWidth + modalPaddings) + // raise event on modal button click + modalElem.find(".modal-footer button").click(function () { + var result = $(this).data("result"); + var frameWnd = modalFrame[0].contentWindow; + var frameJq = frameWnd.$; + if (result && frameJq) { + frameJq(frameWnd).trigger(scada.EventTypes.MODAL_BTN_CLICK, result); + } + }); + // display the modal modalElem .on('shown.bs.modal', function () { @@ -279,9 +292,29 @@ scada.Popup.prototype.showModal = function (url, opt_buttons, opt_callback) { .modal("show"); }) .attr("src", url); + + // TODO: callback }; -// Popup instance locator object +// Show the modal dialog +scada.Popup.prototype.closeModal = function (modalWnd, dialogResult, extraParams) { + var frame = $(modalWnd.frameElement); + var modalElem = frame.closest(".modal"); + modalElem.modal("hide"); + // TODO: callback +} + +// Show or hide the modal button +scada.Popup.prototype.setButtonVisible = function (modalWnd, btn, val) { + var frame = $(modalWnd.frameElement); + var modalElem = frame.closest(".modal"); + var btnElem = modalElem.find(".modal-footer button[data-result='" + btn + "']"); + btnElem.css("display", val ? "" : "none"); +} + +/********** Popup Locator **********/ + +// Popup locator object scada.popupLocator = { // Find and return an existing popup object getPopup: function () { diff --git a/ScadaWeb/ScadaWebShell5Beta/js/controls/splitter.js b/ScadaWeb/ScadaWebShell5Beta/js/controls/splitter.js index cc798962e..8ec3a2965 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/controls/splitter.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/controls/splitter.js @@ -13,7 +13,7 @@ // Rapid SCADA namespace var scada = scada || {}; -/********** Splitter bulk **********/ +/********** Splitter Bulk **********/ // Splitter bulk contains the splitter and the resized divs // Splitter bulk type @@ -153,7 +153,7 @@ scada.SplitterBulk.prototype.bindEvents = function () { }); }; -/********** Splitters processing **********/ +/********** Splitters Processing **********/ // Splitters processing object scada.splitter = { From a504095be4547e6ecc13c2a33cb64b1b0459766d Mon Sep 17 00:00:00 2001 From: 2mik Date: Fri, 15 Jul 2016 22:31:30 +0300 Subject: [PATCH 186/382] PlgTable: commands --- .../OpenPlugins/PlgTable/css/globalvar.less | 1 + .../PlgTable/plugins/Table/Command.aspx | 39 +++++++-- .../PlgTable/plugins/Table/Command.aspx.cs | 69 +++++++++++++--- .../plugins/Table/Command.aspx.designer.cs | 80 ++++++++++++++++++- .../PlgTable/plugins/Table/css/command.css | 25 +++--- .../PlgTable/plugins/Table/css/command.less | 30 ++++--- .../plugins/Table/css/command.min.css | 2 +- .../PlgTable/plugins/Table/js/command.js | 22 ++--- ScadaWeb/ScadaWebShell5Beta/css/error.css | 2 + ScadaWeb/ScadaWebShell5Beta/css/error.less | 2 + ScadaWeb/ScadaWebShell5Beta/css/error.min.css | 2 +- .../ScadaWebShell5Beta/css/globalvar.less | 1 + ScadaWeb/ScadaWebShell5Beta/js/login.js | 6 +- 13 files changed, 222 insertions(+), 59 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgTable/css/globalvar.less b/ScadaWeb/OpenPlugins/PlgTable/css/globalvar.less index f9d3fa89a..d85a2272b 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/css/globalvar.less +++ b/ScadaWeb/OpenPlugins/PlgTable/css/globalvar.less @@ -5,6 +5,7 @@ @secondary-font-family: Arial, Helvetica, sans-serif; @form-font-size: 13px; @data-font-size: 12px; +@popup-min-width: 300px; @menu-back-color: #23282d; @menu-back-color-air: white; diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx index 191b3d418..9bfb092a8 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx @@ -23,8 +23,11 @@
- + + + +
@@ -42,16 +45,20 @@
- -
-
+ + + + + + -
+
+ -
+
+ + +
+
+ + +
+
+
+ +
+ - + - + diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs index 510051d38..539439d44 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs @@ -67,6 +67,33 @@ private List GetDiscreteCmds(string[] cmdValArr) return discreteCmds; } + /// + /// Скрыть сообщение об ошибке + /// + private void HideErrMsg() + { + pnlErrMsg.Visible = false; + lblWrongPwdErr.Visible = false; + } + + /// + /// Вывести сообщение об ошибке + /// + private void ShowErrMsg(Label lblMessage) + { + pnlErrMsg.Visible = true; + lblMessage.Visible = true; + } + + /// + /// Вывести информацию о неудачном результате отправки команды + /// + private void ShowFailResult(Label lblMessage) + { + pnlFail.Visible = true; + lblMessage.Visible = true; + } + /// /// Проверить пароль, если он используется /// @@ -74,8 +101,25 @@ private bool CheckPassword() { if (pnlPassword.Visible) { - //bool pwdOK = appData. - return true; + int roleID; + bool reqOK = appData.ServerComm.CheckUser(userData.UserProps.UserName, txtPassword.Text, out roleID); + + if (reqOK) + { + if (roleID == BaseValues.Roles.Err) + { + ShowErrMsg(lblWrongPwdErr); + return false; + } + else + { + return true; + } + } + else + { + return false; + } } else { @@ -104,15 +148,15 @@ private void ShowCmdResult(bool sendOK, bool result) if (sendOK && result) { pnlSuccess.Visible = true; - ClientScript.RegisterStartupScript(GetType(), "Startup", "startDowncount();", true); + ClientScript.RegisterStartupScript(GetType(), "Startup", "startCountdown();", true); + } + else if (sendOK) + { + ShowFailResult(lblCmdRejected); } else { - pnlError.Visible = true; - if (sendOK) - lblCmdRejected.Visible = true; - else - lblCmdNotSent.Visible = true; + ShowFailResult(lblCmdNotSent); } } @@ -139,6 +183,9 @@ protected void Page_Load(object sender, EventArgs e) if (!view.ContainsCtrlCnl(ctrlCnlNum)) throw new ScadaException(CommonPhrases.NoRights); + // скрытие сообщения об ошибке + HideErrMsg(); + if (!IsPostBack) { // перевод веб-страницы @@ -180,13 +227,11 @@ protected void Page_Load(object sender, EventArgs e) repCommands.DataBind(); pnlDiscreteValue.Visible = true; } - hidCmdEnabled.Value = "true"; break; case BaseValues.CmdTypes.Binary: + pnlData.Visible = true; break; - case BaseValues.CmdTypes.Request: - break; - default: + default: // BaseValues.CmdTypes.Request: break; } } diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs index ba55e2d9a..67d7f43c3 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs @@ -40,13 +40,22 @@ public partial class WFrmCommand { protected global::System.Web.UI.WebControls.View viewCmdParams; /// - /// hidCmdEnabled control. + /// pnlErrMsg control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.WebControls.HiddenField hidCmdEnabled; + protected global::System.Web.UI.WebControls.Panel pnlErrMsg; + + /// + /// lblWrongPwdErr control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblWrongPwdErr; /// /// btnSubmit control. @@ -201,6 +210,69 @@ public partial class WFrmCommand { /// protected global::System.Web.UI.WebControls.Repeater repCommands; + /// + /// pnlData control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Panel pnlData; + + /// + /// lblCmdData control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblCmdData; + + /// + /// rbStr control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.RadioButton rbStr; + + /// + /// lblStr control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblStr; + + /// + /// rbHex control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.RadioButton rbHex; + + /// + /// lblHex control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblHex; + + /// + /// txtCmdData control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox txtCmdData; + /// /// viewCmdResult control. /// @@ -247,13 +319,13 @@ public partial class WFrmCommand { protected global::System.Web.UI.WebControls.Label lblSec; /// - /// pnlError control. + /// pnlFail control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.WebControls.Panel pnlError; + protected global::System.Web.UI.WebControls.Panel pnlFail; /// /// lblCmdNotSent control. diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.css index 90faabd8b..bc6cde398 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.css @@ -5,22 +5,25 @@ body { background-color: white; font-family: 'Open Sans', sans-serif; font-size: 13px; - min-width: 200px; + min-width: 300px; overflow: hidden; } table { border-collapse: collapse; } -label, -.cmd-lbl { - display: inline-block; +label { font-weight: 600; - margin-bottom: 5px; } input[type=text], input[type=password] { width: calc(100% - 5px); } +.form-control-feedback { + color: transparent; +} +#btnSubmit { + display: none; +} #tblInfo th { font-weight: normal; padding: 2px 0; @@ -31,14 +34,11 @@ input[type=password] { padding: 2px 10px; vertical-align: top; } -#btnSubmit { - display: none; -} -/* Real Value Commands */ +/* Real Value Command */ #txtCmdVal { color: #d9534f; } -/* Discrete Value Commands */ +/* Discrete Value Command */ #divCommands { display: flex; flex-wrap: wrap; @@ -46,4 +46,9 @@ input[type=password] { #divCommands input { flex-grow: 1; margin: 0 5px 5px 0; +} +/* Binary Command Data */ +#divCmdDataFormat { + position: relative; + top: -5px; } \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.less b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.less index 0dfa9d27c..1784cdb9b 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.less +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.less @@ -10,7 +10,7 @@ body { background-color: @form-back-color; font-family: @default-font-family; font-size: @form-font-size; - min-width: 200px; + min-width: @popup-min-width; overflow: hidden; } @@ -18,11 +18,8 @@ table { border-collapse: collapse; } -label, -.cmd-lbl { - display: inline-block; +label { font-weight: 600; - margin-bottom: 5px; } input[type=text], @@ -30,6 +27,14 @@ input[type=password] { width: calc(~"100% - 5px"); } +.form-control-feedback { + color:transparent; +} + +#btnSubmit { + display: none; +} + #tblInfo th { font-weight: normal; padding: 2px 0; @@ -42,17 +47,13 @@ input[type=password] { vertical-align: top; } -#btnSubmit { - display: none; -} - -/* Real Value Commands */ +/* Real Value Command */ #txtCmdVal { color: @cmd-fore-color; } -/* Discrete Value Commands */ +/* Discrete Value Command */ #divCommands { display: flex; @@ -62,4 +63,11 @@ input[type=password] { #divCommands input { flex-grow: 1; margin: 0 5px 5px 0; +} + +/* Binary Command Data */ + +#divCmdDataFormat { + position: relative; + top: -5px; } \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.min.css index b8c770815..7e6d6c1e2 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.min.css @@ -1 +1 @@ -body{margin:0;padding:0;background-color:#fff;font-family:'Open Sans',sans-serif;font-size:13px;min-width:200px;overflow:hidden;}table{border-collapse:collapse;}label,.cmd-lbl{display:inline-block;font-weight:600;margin-bottom:5px;}input[type=text],input[type=password]{width:calc(100% - 5px);}#tblInfo th{font-weight:normal;padding:2px 0;vertical-align:top;}#tblInfo td{color:#d9534f;padding:2px 10px;vertical-align:top;}#btnSubmit{display:none;}#txtCmdVal{color:#d9534f;}#divCommands{display:flex;flex-wrap:wrap;}#divCommands input{flex-grow:1;margin:0 5px 5px 0;} \ No newline at end of file +body{margin:0;padding:0;background-color:#fff;font-family:'Open Sans',sans-serif;font-size:13px;min-width:300px;overflow:hidden;}table{border-collapse:collapse;}label{font-weight:600;}input[type=text],input[type=password]{width:calc(100% - 5px);}.form-control-feedback{color:transparent;}#btnSubmit{display:none;}#tblInfo th{font-weight:normal;padding:2px 0;vertical-align:top;}#tblInfo td{color:#d9534f;padding:2px 10px;vertical-align:top;}#txtCmdVal{color:#d9534f;}#divCommands{display:flex;flex-wrap:wrap;}#divCommands input{flex-grow:1;margin:0 5px 5px 0;}#divCmdDataFormat{position:relative;top:-5px;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/command.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/command.js index ebc559f03..22ea9a67e 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/command.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/command.js @@ -3,16 +3,15 @@ var popup = scada.popupLocator.getPopup(); // Time before closing, sec var closeTimeout = 3; +// Start countdown before closing +function startCountdown() { + var spanCountdown = $("#spanCountdown"); -// Start downcount before closing -function startDowncount() { - var spanDowncount = $("#spanDowncount"); - - var downcountFunc = function () { + var countdownFunc = function () { if (closeTimeout) { closeTimeout--; - spanDowncount.text(closeTimeout); - setTimeout(downcountFunc, 1000); + spanCountdown.text(closeTimeout); + setTimeout(countdownFunc, 1000); } else { if (popup) { popup.closeModal(window, true); @@ -20,8 +19,8 @@ function startDowncount() { } } - spanDowncount.text(closeTimeout); - setTimeout(downcountFunc, 1000); + spanCountdown.text(closeTimeout); + setTimeout(countdownFunc, 1000); } $(document).ready(function () { @@ -32,6 +31,11 @@ $(document).ready(function () { } } + // highlight password error + if ($("#lblWrongPwdErr").length > 0) { + $("#pnlPassword").addClass("has-error"); + } + // submit the form on modal execute button click $(window).on(scada.EventTypes.MODAL_BTN_CLICK, function (event, result) { if (result == scada.ModalButtons.EXEC) { diff --git a/ScadaWeb/ScadaWebShell5Beta/css/error.css b/ScadaWeb/ScadaWebShell5Beta/css/error.css index 9af499aee..7dd7a041c 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/error.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/error.css @@ -7,6 +7,8 @@ } body.popup { background-color: white; + min-width: 300px; + overflow: hidden; } div { box-sizing: border-box; diff --git a/ScadaWeb/ScadaWebShell5Beta/css/error.less b/ScadaWeb/ScadaWebShell5Beta/css/error.less index d3e77f372..14e7085ca 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/error.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/error.less @@ -14,6 +14,8 @@ body { body.popup { background-color: @popup-back-color; + min-width: @popup-min-width; + overflow: hidden; } div { diff --git a/ScadaWeb/ScadaWebShell5Beta/css/error.min.css b/ScadaWeb/ScadaWebShell5Beta/css/error.min.css index 1fd7fb0d4..7445e648c 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/error.min.css +++ b/ScadaWeb/ScadaWebShell5Beta/css/error.min.css @@ -1 +1 @@ -body{margin:0;padding:0;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:14px;}body.popup{background-color:#fff;}div{box-sizing:border-box;}#divHeader{width:100%;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;font-size:17px;line-height:30px;white-space:nowrap;}#divContent{padding:20px;}h1{font-size:30px;font-weight:500;margin:0 0 10px;color:#f00;}div.error{color:#f00;margin:0 0 10px;} \ No newline at end of file +body{margin:0;padding:0;background-color:#f1f1f1;font-family:'Open Sans',sans-serif;font-size:14px;}body.popup{background-color:#fff;min-width:300px;overflow:hidden;}div{box-sizing:border-box;}#divHeader{width:100%;height:30px;margin:0;padding:0 10px;background-color:#23282d;color:#eee;font-size:17px;line-height:30px;white-space:nowrap;}#divContent{padding:20px;}h1{font-size:30px;font-weight:500;margin:0 0 10px;color:#f00;}div.error{color:#f00;margin:0 0 10px;} \ No newline at end of file diff --git a/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less b/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less index f9d3fa89a..d85a2272b 100644 --- a/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less +++ b/ScadaWeb/ScadaWebShell5Beta/css/globalvar.less @@ -5,6 +5,7 @@ @secondary-font-family: Arial, Helvetica, sans-serif; @form-font-size: 13px; @data-font-size: 12px; +@popup-min-width: 300px; @menu-back-color: #23282d; @menu-back-color-air: white; diff --git a/ScadaWeb/ScadaWebShell5Beta/js/login.js b/ScadaWeb/ScadaWebShell5Beta/js/login.js index a3d6981da..61b5958e2 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/login.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/login.js @@ -3,9 +3,9 @@ var phrases = phrases || {}; // Show alert before login form function showAlert(message) { - var divAlert = $(''); + var divAlert = $('
' + + '' + message + '
'); divAlert.first().outerWidth($("#divLogin").outerWidth()); $("#divAlertsInner").append(divAlert); From 3932d3df983471f7ea64efe4325a5c6d8e444ec7 Mon Sep 17 00:00:00 2001 From: 2mik Date: Mon, 18 Jul 2016 17:21:27 +0300 Subject: [PATCH 187/382] PlgTable: work with commands --- .../PlgTable/plugins/Table/Command.aspx | 3 +- .../PlgTable/plugins/Table/Command.aspx.cs | 56 ++++++++++++------- .../plugins/Table/Command.aspx.designer.cs | 9 +++ .../PlgTable/plugins/Table/js/command.js | 9 ++- ScadaWeb/ScadaWebCommon5Beta/WebPhrases.cs | 2 +- .../ScadaWebShell5Beta/js/controls/popup.js | 22 ++++++-- 6 files changed, 75 insertions(+), 26 deletions(-) diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx index 9bfb092a8..ba9b88b06 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx @@ -24,7 +24,8 @@ - + diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs index 539439d44..c03d264a6 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs @@ -74,6 +74,7 @@ private void HideErrMsg() { pnlErrMsg.Visible = false; lblWrongPwdErr.Visible = false; + lblNoRights.Visible = false; } /// @@ -101,12 +102,18 @@ private bool CheckPassword() { if (pnlPassword.Visible) { - int roleID; - bool reqOK = appData.ServerComm.CheckUser(userData.UserProps.UserName, txtPassword.Text, out roleID); + int roleID = BaseValues.Roles.Err; // неверный пароль + bool reqOK = txtPassword.Text == "" ? + true : appData.ServerComm.CheckUser(userData.UserProps.UserName, txtPassword.Text, out roleID); if (reqOK) { - if (roleID == BaseValues.Roles.Err) + if (roleID == BaseValues.Roles.Disabled || roleID == BaseValues.Roles.App) + { + ShowErrMsg(lblNoRights); + return false; + } + else if (roleID == BaseValues.Roles.Err) { ShowErrMsg(lblWrongPwdErr); return false; @@ -118,6 +125,7 @@ private bool CheckPassword() } else { + ShowCmdResult(false, false); return false; } } @@ -166,28 +174,38 @@ protected void Page_Load(object sender, EventArgs e) appData = AppData.GetAppData(); userData = UserData.GetUserData(); - // получение параметров запроса - int viewID; - int.TryParse(Request.QueryString["viewID"], out viewID); - int.TryParse(Request.QueryString["ctrlCnlNum"], out ctrlCnlNum); - - // проверка прав - if (!userData.LoggedOn || - !userData.UserRights.GetViewRights(viewID).ControlRight || - !userData.WebSettings.CmdEnabled) - throw new ScadaException(CommonPhrases.NoRights); - - Type viewType = userData.UserViews.GetViewType(viewID); - BaseView view = appData.ViewCache.GetView(viewType, viewID, true); - - if (!view.ContainsCtrlCnl(ctrlCnlNum)) + // проверка входа в систему + if (!userData.LoggedOn) throw new ScadaException(CommonPhrases.NoRights); // скрытие сообщения об ошибке HideErrMsg(); - if (!IsPostBack) + if (IsPostBack) { + ctrlCnlNum = (int)ViewState["ctrlCnlNum"]; + } + else + { + // получение параметров запроса + int viewID; + int.TryParse(Request.QueryString["viewID"], out viewID); + int.TryParse(Request.QueryString["ctrlCnlNum"], out ctrlCnlNum); + + // проверка прав + if (!userData.UserRights.GetViewRights(viewID).ControlRight || + !userData.WebSettings.CmdEnabled) + throw new ScadaException(CommonPhrases.NoRights); + + Type viewType = userData.UserViews.GetViewType(viewID); + BaseView view = appData.ViewCache.GetView(viewType, viewID, true); + + if (!view.ContainsCtrlCnl(ctrlCnlNum)) + throw new ScadaException(CommonPhrases.NoRights); + + // сохранение номера канала управления во ViewState + ViewState["ctrlCnlNum"] = ctrlCnlNum; + // перевод веб-страницы Translator.TranslatePage(Page, "Scada.Web.Plugins.Table.WFrmCommand"); diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs index 67d7f43c3..d29763da3 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs @@ -57,6 +57,15 @@ public partial class WFrmCommand { /// protected global::System.Web.UI.WebControls.Label lblWrongPwdErr; + /// + /// lblNoRights control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblNoRights; + /// /// btnSubmit control. /// diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/command.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/command.js index 22ea9a67e..00feb4861 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/command.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/command.js @@ -31,12 +31,19 @@ $(document).ready(function () { } } + // disable execute button if the output channel is not found + if ($("#lblCtrlCnlNotFound").length > 0) { + if (popup) { + popup.setButtonEnabled(window, scada.ModalButtons.EXEC, false); + } + } + // highlight password error if ($("#lblWrongPwdErr").length > 0) { $("#pnlPassword").addClass("has-error"); } - // submit the form on modal execute button click + // submit the form on execute button click $(window).on(scada.EventTypes.MODAL_BTN_CLICK, function (event, result) { if (result == scada.ModalButtons.EXEC) { $("#btnSubmit").click(); diff --git a/ScadaWeb/ScadaWebCommon5Beta/WebPhrases.cs b/ScadaWeb/ScadaWebCommon5Beta/WebPhrases.cs index 185542fa9..b1af6e051 100644 --- a/ScadaWeb/ScadaWebCommon5Beta/WebPhrases.cs +++ b/ScadaWeb/ScadaWebCommon5Beta/WebPhrases.cs @@ -118,7 +118,7 @@ public static void Init() { ServerUnavailable = dict.GetPhrase("ServerUnavailable", ServerUnavailable); WrongPassword = dict.GetPhrase("WrongPassword", WrongPassword); - NoRights = dict.GetPhrase("NoRightsL", NoRights); + NoRights = dict.GetPhrase("NoRights", NoRights); IllegalRole = dict.GetPhrase("IllegalRole", IllegalRole); } diff --git a/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js b/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js index f63561c27..39c3ef660 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js @@ -123,6 +123,13 @@ scada.Popup.prototype._genModalButtonsHtml = function (buttons) { return html; } +// Find modal button by result +scada.Popup.prototype._findModalButton = function (modalWnd, btn) { + var frame = $(modalWnd.frameElement); + var modalElem = frame.closest(".modal"); + return modalElem.find(".modal-footer button[data-result='" + btn + "']"); +} + // Show popup with the specified url as a dropdown menu below the anchorElem. // opt_callback is a function (dialogResult, extraParams) scada.Popup.prototype.showDropdown = function (url, anchorElem, opt_callback) { @@ -306,10 +313,17 @@ scada.Popup.prototype.closeModal = function (modalWnd, dialogResult, extraParams // Show or hide the modal button scada.Popup.prototype.setButtonVisible = function (modalWnd, btn, val) { - var frame = $(modalWnd.frameElement); - var modalElem = frame.closest(".modal"); - var btnElem = modalElem.find(".modal-footer button[data-result='" + btn + "']"); - btnElem.css("display", val ? "" : "none"); + this._findModalButton(modalWnd, btn).css("display", val ? "" : "none"); +} + +// Enable or disable the modal button +scada.Popup.prototype.setButtonEnabled = function (modalWnd, btn, val) { + var btnElem = this._findModalButton(modalWnd, btn); + if (val) { + btnElem.removeAttr("disabled"); + } else { + btnElem.attr("disabled", "disabled"); + } } /********** Popup Locator **********/ From a58ea15a3b4585dac8d88f76836430efda9bf1eb Mon Sep 17 00:00:00 2001 From: 2mik Date: Tue, 19 Jul 2016 12:56:15 +0300 Subject: [PATCH 188/382] PlgTable: commands work --- ScadaData/ScadaData/ScadaUtils.cs | 80 ++++++++++++---- .../PlgTable/plugins/Table/Command.aspx | 21 +++-- .../PlgTable/plugins/Table/Command.aspx.cs | 91 +++++++++++++++++-- .../plugins/Table/Command.aspx.designer.cs | 31 ++++++- .../PlgTable/plugins/Table/css/command.css | 3 + .../PlgTable/plugins/Table/css/command.less | 4 + .../plugins/Table/css/command.min.css | 2 +- .../PlgTable/plugins/Table/js/command.js | 16 +++- 8 files changed, 212 insertions(+), 36 deletions(-) diff --git a/ScadaData/ScadaData/ScadaUtils.cs b/ScadaData/ScadaData/ScadaUtils.cs index bbb586b66..5d6f9d3ca 100644 --- a/ScadaData/ScadaData/ScadaUtils.cs +++ b/ScadaData/ScadaData/ScadaUtils.cs @@ -162,9 +162,10 @@ public static double StrToDouble(string s) } /// - /// Преобразовать строку в вещественное число. Метод работает с разделителями целой части '.' и ',' + /// Преобразовать строку в вещественное число /// - /// Если преобразование невозможно, вызывается исключение FormatException + /// Метод работает с разделителями целой части '.' и ','. + /// Если преобразование невозможно, вызывается исключение FormatException public static double StrToDoubleExc(string s) { try { return ParseDouble(s); } @@ -172,14 +173,31 @@ public static double StrToDoubleExc(string s) } /// - /// Преобразовать строку в вещественное число. Метод работает с разделителями целой части '.' и ',' + /// Преобразовать строку в вещественное число /// + /// Метод работает с разделителями целой части '.' и ',' public static double ParseDouble(string s) { - nfi.NumberDecimalSeparator = s.Contains(".") ? "." : ","; - return double.Parse(s, nfi); + lock (nfi) + { + nfi.NumberDecimalSeparator = s.Contains(".") ? "." : ","; + return double.Parse(s, nfi); + } } - + + /// + /// Попытаться преобразовать строку в вещественное число + /// + /// Метод работает с разделителями целой части '.' и ',' + public static bool TryParseDouble(string s, out double result) + { + lock (nfi) + { + nfi.NumberDecimalSeparator = s.Contains(".") ? "." : ","; + return double.TryParse(s, NumberStyles.Float, nfi, out result); + } + } + /// /// Преобразовать массив байт в строку на основе 16-ричного представления /// @@ -203,26 +221,56 @@ public static string BytesToHex(byte[] bytes, int index, int count) /// /// Преобразовать строку 16-ричных чисел в массив байт /// - public static bool HexToBytes(string s, out byte[] bytes) + public static bool HexToBytes(string s, out byte[] bytes, bool skipWhiteSpace = false) { - int len = s == null ? 0 : s.Length; + int strLen = s == null ? 0 : s.Length; + int bufLen = strLen / 2; + byte[] buf = new byte[bufLen]; + + int strInd = 0; + int bufInd = 0; + bool parseOK = true; - if (len > 0 && len % 2 == 0) + while (strInd < strLen && parseOK) { - bool parseOk = true; - bytes = new byte[len / 2]; + if (skipWhiteSpace) + { + while (strInd < strLen && char.IsWhiteSpace(s[strInd])) + { + strInd++; + } + if (strInd == strLen) + break; + } - for (int i = 0, j = 0; i < len && parseOk; i += 2, j++) + try + { + buf[bufInd] = byte.Parse(s.Substring(strInd, 2), NumberStyles.HexNumber); + bufInd++; + strInd += 2; + } + catch { - try { bytes[j] = (byte)int.Parse(s.Substring(i, 2), NumberStyles.HexNumber); } - catch { parseOk = false; } + parseOK = false; } + } - return parseOk; + if (parseOK && bufInd > 0) + { + if (bufInd < bufLen) + { + bytes = new byte[bufInd]; + Array.Copy(buf, 0, bytes, 0, bufInd); + } + else + { + bytes = buf; + } + return true; } else { - bytes = new byte[0]; + bytes = buf; return false; } } diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx index ba9b88b06..af05978d8 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx @@ -21,11 +21,14 @@ + - + @@ -53,9 +56,10 @@ - -
-
+ + + + @@ -69,7 +73,7 @@ -
+
-
+
- + +
diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs index c03d264a6..49e091b81 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.cs @@ -29,6 +29,7 @@ using Scada.UI; using System; using System.Collections.Generic; +using System.Text; using System.Web.UI.WebControls; namespace Scada.Web.Plugins.Table @@ -51,6 +52,7 @@ private struct DiscreteCmd private AppData appData; // общие данные веб-приложения private UserData userData; // данные пользователя приложения + private bool cmdEnabled; // отправка команды разрешена private int ctrlCnlNum; // номер канала управления @@ -67,14 +69,37 @@ private List GetDiscreteCmds(string[] cmdValArr) return discreteCmds; } + /// + /// Попытаться получить данные бинарной команды, введённые пользователем + /// + private bool TryParseCmdData(out byte[] cmdData) + { + if (txtCmdData.Text == "") + { + cmdData = null; + return false; + } + else if (rbStr.Checked) + { + cmdData = Encoding.Default.GetBytes(txtCmdData.Text); + return true; + } + else + { + return ScadaUtils.HexToBytes(txtCmdData.Text, out cmdData, true); + } + } + /// /// Скрыть сообщение об ошибке /// private void HideErrMsg() { pnlErrMsg.Visible = false; - lblWrongPwdErr.Visible = false; + lblWrongPwd.Visible = false; lblNoRights.Visible = false; + lblIncorrectCmdVal.Visible = false; + lblIncorrectCmdData.Visible = false; } /// @@ -115,7 +140,7 @@ private bool CheckPassword() } else if (roleID == BaseValues.Roles.Err) { - ShowErrMsg(lblWrongPwdErr); + ShowErrMsg(lblWrongPwd); return false; } else @@ -146,12 +171,35 @@ private void SendStandardCmd(double cmdVal) ShowCmdResult(sendOK, result); } + /// + /// Отправить бинарную команду + /// + private void SendBinaryCmd(byte[] cmdData) + { + bool result; + bool sendOK = appData.ServerComm.SendBinaryCommand( + userData.UserProps.UserID, ctrlCnlNum, cmdData, out result); + ShowCmdResult(sendOK, result); + } + + /// + /// Отправить команду опроса КП + /// + private void SendRequestCmd(int kpNum) + { + bool result; + bool sendOK = appData.ServerComm.SendRequestCommand( + userData.UserProps.UserID, ctrlCnlNum, kpNum, out result); + ShowCmdResult(sendOK, result); + } + /// /// Отобразить результат выполнения команды /// private void ShowCmdResult(bool sendOK, bool result) { mvCommand.SetActiveView(viewCmdResult); + hidDisableExecuteBtn.Value = "true"; if (sendOK && result) { @@ -183,7 +231,8 @@ protected void Page_Load(object sender, EventArgs e) if (IsPostBack) { - ctrlCnlNum = (int)ViewState["ctrlCnlNum"]; + cmdEnabled = (bool)ViewState["CmdEnabled"]; + ctrlCnlNum = (int)ViewState["CtrlCnlNum"]; } else { @@ -204,13 +253,14 @@ protected void Page_Load(object sender, EventArgs e) throw new ScadaException(CommonPhrases.NoRights); // сохранение номера канала управления во ViewState - ViewState["ctrlCnlNum"] = ctrlCnlNum; + ViewState["CtrlCnlNum"] = ctrlCnlNum; // перевод веб-страницы Translator.TranslatePage(Page, "Scada.Web.Plugins.Table.WFrmCommand"); // получение канала управления CtrlCnlProps ctrlCnlProps = appData.DataAccess.GetCtrlCnlProps(ctrlCnlNum); + ViewState["CmdEnabled"] = ctrlCnlProps != null; if (ctrlCnlProps == null) { @@ -218,6 +268,7 @@ protected void Page_Load(object sender, EventArgs e) // элементы управления ввода команды скрыты по умолчанию lblCtrlCnl.Visible = false; lblCtrlCnlNotFound.Visible = true; + hidDisableExecuteBtn.Value = "true"; } else { @@ -250,6 +301,7 @@ protected void Page_Load(object sender, EventArgs e) pnlData.Visible = true; break; default: // BaseValues.CmdTypes.Request: + ViewState["KPNum"] = ctrlCnlProps.KPNum; break; } } @@ -258,13 +310,40 @@ protected void Page_Load(object sender, EventArgs e) protected void btnSubmit_Click(object sender, EventArgs e) { - appData.Log.WriteLine("!!!btnSubmit_Click"); + if (cmdEnabled && CheckPassword()) + { + if (pnlRealValue.Visible) + { + // отправка стандартной команды для вещественных значений + double cmdVal; + if (ScadaUtils.TryParseDouble(txtCmdVal.Text, out cmdVal)) + SendStandardCmd(cmdVal); + else + ShowErrMsg(lblIncorrectCmdVal); + } + else if (pnlData.Visible) + { + // отправка бинарной команды + byte[] cmdData; + if (TryParseCmdData(out cmdData)) + SendBinaryCmd(cmdData); + else + ShowErrMsg(lblIncorrectCmdData); + } + else + { + // отправка команды опроса КП + int kpNum = (int)ViewState["KPNum"]; + SendRequestCmd(kpNum); + } + } } protected void repCommands_ItemCommand(object source, RepeaterCommandEventArgs e) { - if (CheckPassword()) + if (cmdEnabled && CheckPassword()) { + // отправка стандартной команды для дискретных значений Button btn = (Button)e.CommandSource; int cmdVal = int.Parse(btn.Attributes["data-cmdval"]); SendStandardCmd(cmdVal); diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs index d29763da3..5136436c5 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/Command.aspx.designer.cs @@ -21,6 +21,15 @@ public partial class WFrmCommand { /// protected global::System.Web.UI.HtmlControls.HtmlForm frmCommand; + /// + /// hidDisableExecuteBtn control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.HiddenField hidDisableExecuteBtn; + /// /// mvCommand control. /// @@ -49,13 +58,13 @@ public partial class WFrmCommand { protected global::System.Web.UI.WebControls.Panel pnlErrMsg; /// - /// lblWrongPwdErr control. + /// lblWrongPwd control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.WebControls.Label lblWrongPwdErr; + protected global::System.Web.UI.WebControls.Label lblWrongPwd; /// /// lblNoRights control. @@ -66,6 +75,24 @@ public partial class WFrmCommand { /// protected global::System.Web.UI.WebControls.Label lblNoRights; + /// + /// lblIncorrectCmdVal control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblIncorrectCmdVal; + + /// + /// lblIncorrectCmdData control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblIncorrectCmdData; + /// /// btnSubmit control. /// diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.css index bc6cde398..4397e88e5 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.css @@ -18,6 +18,9 @@ input[type=text], input[type=password] { width: calc(100% - 5px); } +textarea { + resize: none; +} .form-control-feedback { color: transparent; } diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.less b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.less index 1784cdb9b..d421f8dc7 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.less +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.less @@ -27,6 +27,10 @@ input[type=password] { width: calc(~"100% - 5px"); } +textarea { + resize: none; +} + .form-control-feedback { color:transparent; } diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.min.css b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.min.css index 7e6d6c1e2..b84da6303 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.min.css +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/css/command.min.css @@ -1 +1 @@ -body{margin:0;padding:0;background-color:#fff;font-family:'Open Sans',sans-serif;font-size:13px;min-width:300px;overflow:hidden;}table{border-collapse:collapse;}label{font-weight:600;}input[type=text],input[type=password]{width:calc(100% - 5px);}.form-control-feedback{color:transparent;}#btnSubmit{display:none;}#tblInfo th{font-weight:normal;padding:2px 0;vertical-align:top;}#tblInfo td{color:#d9534f;padding:2px 10px;vertical-align:top;}#txtCmdVal{color:#d9534f;}#divCommands{display:flex;flex-wrap:wrap;}#divCommands input{flex-grow:1;margin:0 5px 5px 0;}#divCmdDataFormat{position:relative;top:-5px;} \ No newline at end of file +body{margin:0;padding:0;background-color:#fff;font-family:'Open Sans',sans-serif;font-size:13px;min-width:300px;overflow:hidden;}table{border-collapse:collapse;}label{font-weight:600;}input[type=text],input[type=password]{width:calc(100% - 5px);}textarea{resize:none;}.form-control-feedback{color:transparent;}#btnSubmit{display:none;}#tblInfo th{font-weight:normal;padding:2px 0;vertical-align:top;}#tblInfo td{color:#d9534f;padding:2px 10px;vertical-align:top;}#txtCmdVal{color:#d9534f;}#divCommands{display:flex;flex-wrap:wrap;}#divCommands input{flex-grow:1;margin:0 5px 5px 0;}#divCmdDataFormat{position:relative;top:-5px;} \ No newline at end of file diff --git a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/command.js b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/command.js index 00feb4861..75c9b86cf 100644 --- a/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/command.js +++ b/ScadaWeb/OpenPlugins/PlgTable/plugins/Table/js/command.js @@ -31,18 +31,28 @@ $(document).ready(function () { } } - // disable execute button if the output channel is not found - if ($("#lblCtrlCnlNotFound").length > 0) { + // disable execute button if it is specified by the server code + if ($("#hidDisableExecuteBtn").val() == "true") { if (popup) { popup.setButtonEnabled(window, scada.ModalButtons.EXEC, false); } } // highlight password error - if ($("#lblWrongPwdErr").length > 0) { + if ($("#lblWrongPwd").length > 0) { $("#pnlPassword").addClass("has-error"); } + // highlight command value error + if ($("#lblIncorrectCmdVal").length > 0) { + $("#pnlRealValue").addClass("has-error"); + } + + // highlight command data error + if ($("#lblIncorrectCmdData").length > 0) { + $("#pnlData").addClass("has-error"); + } + // submit the form on execute button click $(window).on(scada.EventTypes.MODAL_BTN_CLICK, function (event, result) { if (result == scada.ModalButtons.EXEC) { From 0f5746e73ef79bfa9e645c38a37027a285a05f2e Mon Sep 17 00:00:00 2001 From: 2mik Date: Tue, 19 Jul 2016 16:06:20 +0300 Subject: [PATCH 189/382] ScadaWeb5: refactor popups --- .../ScadaWebShell5Beta/js/controls/popup.js | 99 ++++++++++++------- 1 file changed, 61 insertions(+), 38 deletions(-) diff --git a/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js b/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js index 39c3ef660..5df13b2b6 100644 --- a/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js +++ b/ScadaWeb/ScadaWebShell5Beta/js/controls/popup.js @@ -36,32 +36,15 @@ scada.ModalButtons = { scada.Popup = function () { // Window that holds popups this._holderWindow = window; - - // Callback functions of the existing popups - this._popupCallbacks = new Map(); -}; - -// Event handler that removes the popup on press Escape key -scada.Popup.prototype._removePopupOnEscape = function (event) { - if (event.which == 27 /*Escape*/) { - this._cancelDropdown(event.data); - } }; // Close the dropdown popup and execute a callback with a cancel result scada.Popup.prototype._cancelDropdown = function (popupElem) { - var frame = popupElem.find(".popup-frame"); - if (frame.length > 0) { - var frameWnd = frame[0].contentWindow; - var callback = this._popupCallbacks.get(frameWnd); - this._popupCallbacks.delete(frameWnd); - popupElem.remove(); + var callback = popupElem.data("popup-callback"); + popupElem.remove(); - if (callback) { - callback(false); - } - } else { - popupElem.remove(); + if (callback) { + callback(false); } }; @@ -136,6 +119,11 @@ scada.Popup.prototype.showDropdown = function (url, anchorElem, opt_callback) { var thisObj = this; var popupElem = $(""); + + if (opt_callback) { + popupElem.data("popup-callback", opt_callback); + } + $("body").append(popupElem); var overlay = popupElem.find(".popup-overlay"); @@ -156,25 +144,27 @@ scada.Popup.prototype.showDropdown = function (url, anchorElem, opt_callback) { }); // remove the popup on press Escape key in the parent window - var removePopupOnEscape = this._removePopupOnEscape.bind(this); + var removePopupOnEscapeFunc = function (event) { + if (event.which == 27 /*Escape*/) { + thisObj._cancelDropdown(popupElem); + } + } + $(document) - .off("keydown", null, removePopupOnEscape) - .on("keydown", null, popupElem, removePopupOnEscape); + .off("keydown", removePopupOnEscapeFunc) + .on("keydown", removePopupOnEscapeFunc); // load the frame frame .on("load", function () { - // store callback function - var frameWnd = frame[0].contentWindow; - thisObj._popupCallbacks.set(frameWnd, opt_callback); - // remove the popup on press Escape key in the frame + var frameWnd = frame[0].contentWindow; if (frameWnd.$) { var jqFrameDoc = frameWnd.$(frameWnd.document); jqFrameDoc.ready(function () { jqFrameDoc - .off("keydown", null, removePopupOnEscape) - .on("keydown", null, popupElem, removePopupOnEscape); + .off("keydown", removePopupOnEscapeFunc) + .on("keydown", removePopupOnEscapeFunc); }); } }) @@ -229,8 +219,7 @@ scada.Popup.prototype.showDropdown = function (url, anchorElem, opt_callback) { scada.Popup.prototype.closeDropdown = function (popupWnd, dialogResult, extraParams) { var frame = $(popupWnd.frameElement); var popupElem = frame.closest(".popup-dropdown"); - var callback = this._popupCallbacks.get(popupWnd); - this._popupCallbacks.delete(popupWnd); + var callback = popupElem.data("popup-callback"); popupElem.remove(); if (callback) { @@ -255,12 +244,36 @@ scada.Popup.prototype.showModal = function (url, opt_buttons, opt_callback) { "" + footerHtml + "