Example #1
0
 /// <summary>
 /// Constructs the <see cref="RotorConfigureScreen"/>.
 /// </summary>
 /// <param name="filePath">The filepath of the screen text.</param>
 /// <param name="mode">The mode for how the screen receives input.</param>
 public RotorConfigureScreen(string fileName, EnterMode mode) : base(fileName)
 {
     if (mode == EnterMode.File)
     {
         throw new ArgumentException("HTML mode is not supported for rotor configuration!", nameof(mode));
     }
     this.mode = mode;
 }
 /// <summary>
 /// Constructs the <see cref="EncipherMessageScreen"/>.
 /// </summary>
 /// <param name="filePath">The filepath of the screen text.</param>
 /// <param name="mode">The mode for how the screen receives input.</param>
 public EncipherMessageScreen(string fileName, EnterMode mode) : base(fileName)
 {
     if (mode == EnterMode.File)
     {
         throw new ArgumentException("HTML mode is not supported for enciphering!", nameof(mode));
     }
     this.mode = mode;
 }
Example #3
0
        /// <summary>
        /// Runs the rotor configurer.
        /// </summary>
        public void ConfigureRotors(EnterMode mode)
        {
            Console.Write("Enter rotors as indecies (y/n): ");
            string input      = Console.ReadLine().ToLower();
            bool   asIndecies = false;

            if (input == "yes" || input == "y")
            {
                asIndecies = true;
            }
            else if (input != "no" && input != "n")
            {
                PrintError("Invalid input. Must be y/yes/n/no!");
                return;
            }
            Console.WriteLine();
            Console.ForegroundColor = ConsoleColor.Blue;
            if (!asIndecies)
            {
                Console.Write("Enter prime rotor keys: ");
            }
            else
            {
                Console.Write($"Enter rotor key indecies between 0 and {RotorKeys.TotalKeyCount - 1}: ");
            }
            input = string.Empty;
            switch (mode)
            {
            case EnterMode.Input:
                input = Console.ReadLine();
                break;

            case EnterMode.Paste:
                input = TextCopy.Clipboard.GetText();
                if (string.IsNullOrEmpty(input))
                {
                    PrintError("Clipboard has no text!");
                    return;
                }
                Console.WriteLine(input);
                break;
            }
            try {
                rotorConfig.ConfigureRotorKeys(input, asIndecies);
            } catch (Exception ex) {
                PrintError(ex.Message);
                return;
            }
            Console.WriteLine();
            Console.ForegroundColor = ConsoleColor.Blue;
            try {
                Console.WriteLine($"New Rotor Keys: {string.Join(" ", rotorConfig.RotorKeys)}");
            } catch (Exception ex) {
                PrintError(ex.Message);
            }
            Console.ResetColor();
            Console.WriteLine();
        }
Example #4
0
 /// <summary>
 /// Подключение без поднятия формы входа.
 /// </summary>
 /// <param name="connectionName">Имя соединения</param>
 /// <param name="enterMode">Тип входа в систему EnterMode</param>
 /// <param name="loginIN">Логин пользователя прикладной</param>
 /// <param name="passIN">Пароль пользователя прикладной</param>
 /// <returns>Если успешно, то true</returns>
 public static bool ConnectRemoteSilent(string connectionName, EnterMode enterMode, string loginIN = "", string passIN = "")
 {
     if (Var.con != null)
     {
         return(true);
     }
     Var.enterMode = enterMode;
     if (!sys.Enter(connectionName, enterMode, loginIN, passIN))
     {
         return(false);
     }
     return(true);
 }
Example #5
0
        /// <summary>
        /// Добавление в историю входов. Функция только для Postgre и MSSQL.
        /// </summary>
        /// <param name="connectionNameIN">Имя соединения с БД или сервером приложений</param>
        /// <param name="computerNameIN">Имя компьютера пользователя</param>
        /// <param name="computerUserNameIN">Имя пользователя системы</param>
        /// <param name="userProjectIN">Имя </param>
        /// <param name="userIDIN"></param>
        /// <param name="systemNameIN">Какой Exe клиент подключается. ClientApp, Utility и др.</param>
        /// <param name="enterModeIN">Тип входа: Work, Test, Develop</param>
        /// <returns>Если успешно, то true</returns>
        public static bool AddEnterHist(string connectionNameIN,
                                        string computerNameIN,
                                        string computerUserNameIN,
                                        string userProjectIN,
                                        string userIDIN,
                                        string systemNameIN,
                                        EnterMode enterModeIN)
        {
            string sql = "INSERT INTO fbaEnterHist (EntityID, ConnectionName, ComputerName, ComputerUserName, UserForm, UserID, SystemName, EnterDate, EnterMode) VALUES (" +
                         //sys.GetEntityID("EnterHist") +
                         "0, '" + connectionNameIN + "','" + computerNameIN + "','" + computerUserNameIN + "','" + userProjectIN + "'," + userIDIN + ",'" + systemNameIN + "', " + sys.DateTimeCurrent() + ",'" + enterModeIN.ToString() + "');";

            return(Exec(DirectionQuery.Remote, sql));
        }
Example #6
0
        /// <summary>
        /// Добавление в историю входов. Функция только для SQLite.
        /// </summary>
        /// <param name="connectionNameIN">Имя соединения с БД или сервером приложений</param>
        /// <param name="systemNameIN">Имя прикладной подсистемы</param>
        /// <param name="enterModeIN">Тип входа в программу</param>
        /// <returns></returns>
        public static bool AddEnterLast(string connectionNameIN, string systemNameIN, EnterMode enterModeIN)
        {
            const string localdate = "datetime('now', 'localtime')";
            string       sql       = "INSERT INTO fbaEnterLast (EntityID, ConnectionName, SystemName, EnterDate, EnterMode) VALUES (0, '" + connectionNameIN + "', '" + systemNameIN + "'," + localdate + ", '" + enterModeIN.ToString() + "');";

            return(Exec(DirectionQuery.Local, sql));
        }
Example #7
0
        ///Вход в систему. Основной метод входа в систему. Для всех приложений ServerApp, ClientApp.
        public static bool Enter(string connectionName,
                                 EnterMode enterModeIN,
                                 string userLoginIN,
                                 string userPassIN)
        {
            //Проверка наличия всех папок системы.
            FBAPath.CheckPath();

            if (connectionName == "")
            {
                SM("Не задано имя подключения!");
                return(false);
            }

            //Имя последнего подключения. Сохраняется на локальной БД, если это не проверка соединения.
            sys.AddEnterLast(connectionName, Var.SystemName, enterModeIN);

            string connectionID;
            string conServerType;
            string conServerName;
            string conDatabaseName;
            string conDatabaseLogin;
            string conDatabasePass;
            string conUserForm;
            string conUserLogin;
            string conUserPass;
            string conWindowsLogin;

            if (Var.conLite.ConnectionGetParamName(
                    connectionName,
                    out connectionID,
                    out conServerType,
                    out conServerName,
                    out conDatabaseName,
                    out conDatabaseLogin,
                    out conDatabasePass,
                    out conUserForm,
                    out conUserLogin,
                    out conUserPass,
                    out conWindowsLogin) == false)
            {
                return(false);
            }

            //Серверу приложений нельзя подключаться через другой сервер приложений.
            if ((Var.SystemName == "ServerApp") && (conServerType == "ServerApp"))
            {
                SM("Ошибка: Серверу приложений нельзя подключаться через другой сервер приложений!");
                return(false);
            }
            if ((Var.SystemName != "ServerApp") && (Var.SystemName != "ParserApp") && (conUserForm == ""))
            {
                SM("Не указана форма для запуска!");
                return(false);
            }

            //Вход может быть не с теми UserLogin и UserPass, которые указаны в ConnectionList.
            //Но остальные данные берутся по ID соединения.
            if ((userLoginIN != "") && (userPassIN != ""))
            {
                conUserLogin = userLoginIN;
                conUserPass  = userPassIN;   //sys.GetUserPassCrypt(UserLoginIN,
            }

            Var.con = new Connection();
            Var.con.ConnectionID   = connectionID;
            Var.con.ConnectionName = connectionName;
            Var.con.UserForm       = conUserForm;
            Var.con.UserLogin      = conUserLogin;
            Var.con.UserPass       = conUserPass;
            Var.con.WindowsLogin   = conWindowsLogin;

            ServerType serverType = ServerType.NotAssigned;

            if (conServerType == "MSSQL")
            {
                serverType = ServerType.MSSQL;
            }
            if (conServerType == "SQLite")
            {
                serverType = ServerType.SQLite;
            }
            if (conServerType == "Postgre")
            {
                serverType = ServerType.Postgre;
            }
            if (conServerType == "ServerApp")
            {
                serverType = ServerType.ServerApp;
            }

            //Что-то непонятное. Тип сервера неизвестен.
            if (serverType == ServerType.NotAssigned)
            {
                return(false);
            }

            if (!Var.con.ConnectionInit(serverType,
                                        conServerName,
                                        conDatabaseLogin,
                                        conDatabasePass,
                                        conDatabaseName))
            {
                return(false);
            }


            string sql = "";

            //Если авторизация Windows.
            if (Var.con.WindowsLogin == "1")
            {
                sql = string.Format("SELECT t1.ID AS UserID, t1.Name AS UserName, t2.Brief AS RoleBrief, t2.ID AS RoleID FROM fbaUser t1 " +
                                    "LEFT JOIN fbaRole t2 ON t1.RoleID = t2.ID " +
                                    "WHERE  Upper(t1.Login) = '{0}'", Environment.UserName.ToUpper());
            }
            else
            {
                sql = string.Format("SELECT t1.ID AS UserID, t1.Name AS UserName, t2.Brief AS RoleBrief, t2.ID AS RoleID FROM fbaUser t1 " +
                                    "LEFT JOIN fbaRole t2 ON t1.RoleID = t2.ID " +
                                    "WHERE Upper(t1.Login) = '{0}' AND t1.Pass = '******'", conUserLogin.ToUpper(), conUserPass);
            }
            sys.GetValue(DirectionQuery.Remote, sql, out Var.UserID, out Var.UserName, out Var.RoleBrief, out Var.RoleID);
            if (Var.UserID == "")
            {
                sys.SM("Подключение к базе данных выполнено успешно, но логин или пароль системы указан неверно!");
                return(false);
            }


            Var.UserIsAdmin     = (Var.RoleBrief.ToUpper() == "ADMIN");
            Var.enterMode       = enterModeIN;
            Var.ProjectMainName = conUserForm; //con.UserForm;

            if (!Var.UserIsAdmin)
            {
                if ((";Utility;ServerApp;ParserApp;").IndexOf(Var.SystemName, StringComparison.OrdinalIgnoreCase) > -1)
                {
                    sys.SM("У Вас нет прав на запуск данного функционала!");
                    return(false);
                }
            }
            if (enterModeIN == EnterMode.Develop)
            {
                return(true);
            }


            //Авторизацию прошли.
            if (conWindowsLogin == "1")
            {
                Var.UserLogin = Environment.UserName;
                Var.UserPass  = "";
            }
            else
            {
                Var.UserLogin = conUserLogin;
                Var.UserPass  = conUserPass;
            }

            //Вошли в систему, сохраняем историю. История входов сохраняется на удаленной БД.
            if (Var.SystemName != "Utility")
            {
                AddEnterHist(connectionName,
                             Var.ComputerName,
                             Var.ComputerUserName,
                             Var.ProjectMainName,
                             Var.UserID,
                             Var.SystemName,
                             Var.enterMode);
            }

            //Установка соединения для доступа к системным таблицам.
            //SetSystemConnection();
            //Включаем процесс охранения ошибок на сервере.
            //sys.Error.SaveError = false;
            return(true);
        }
Example #8
0
        /// <summary>
        /// Runs the Enigma Machine decipherer.
        /// </summary>
        /// <returns>The decipherer string.</returns>
        public void RunDecipherer(EnterMode mode)
        {
            try {
                if (!machineConfig.IsSetup)
                {
                    PrintError("The Enigma Machine has not been fully configured!");
                    return;
                }
                string line = string.Empty;
                switch (mode)
                {
                case EnterMode.Input:
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.Write("Enciphered: ");
                    line = Console.ReadLine();
                    break;

                case EnterMode.Paste:
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.Write("Enciphered: ");
                    line = TextCopy.Clipboard.GetText();
                    if (string.IsNullOrEmpty(line))
                    {
                        PrintError("Clipboard has no text!");
                        return;
                    }
                    Console.WriteLine(line);
                    break;

                case EnterMode.File:
                    Console.ForegroundColor = ConsoleColor.DarkYellow;
                    //Console.WriteLine($"Default: \"{DefaultHtmlFile}\"");
                    Console.Write("Enter the input Enciphered HTML file path: ");
                    PrintWatermark(DefaultHtmlFile);
                    string path = PrepareHtmlPath(Console.ReadLine());
                    try {
                        line = HtmlIO.Read(path);
                    } catch (Exception ex) {
                        PrintError(ex.Message);
                        return;
                    }
                    Console.WriteLine();
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.Write("Enciphered: ");
                    Console.WriteLine(line);
                    break;
                }
                line = line.Replace("\t", "    ");
                if (string.IsNullOrEmpty(line))
                {
                    Console.WriteLine();
                    return;
                }
                string deciphered = encipherer.Decipher(machineConfig.Machine, line);
                Console.WriteLine();
                Console.ForegroundColor = ConsoleColor.Cyan;
                Console.Write("Deciphered: ");
                Console.WriteLine(deciphered);
                Console.ResetColor();
                Console.WriteLine();
                TextCopy.Clipboard.SetText(deciphered);
                Console.Write("Copied Deciphered Text! ");
            }
            finally {
                Console.ResetColor();
            }
        }
Example #9
0
        /// <summary>
        /// Animates the tiles using the supplied entrance mode, Y direction, Z direction and duration.
        /// </summary>
        /// <param name="mode">The entrance mode (Enter or Exit).</param>
        /// <param name="yDirection">The direction of the animation in the vertical axis (TopToBottom or BottomToTop)</param>
        /// <param name="zDirection">The direction of the animation in the depth axis (FrontToBack or BackToFront)</param>
        /// <param name="duration">The duration of the animation for each tile</param>
        public void AnimateTiles(EnterMode mode, YDirection yDirection, ZDirection zDirection, TimeSpan duration)
        {
            // If the control has not been rendered, cancel the animation
            if (ActualWidth <= 0 || ActualHeight <= 0)
            {
                return;
            }

            // If the control it's empty, after position calculation is ran, cancel the animation
            CalculateElementPositions();
            if (_positions == null || _positions.Count <= 0)
            {
                return;
            }

            // Calculates start and end heights
            double startHeight = 0;
            double endHeight = ActualHeight;

            startHeight = 0;
            endHeight = _scrollViewer.ViewportHeight;

            // Get the visible tiles for the current configuration
            // Tiles that are partially visible are also counted
            var visibleTiles = _positions.Where(x => x.Value.Y + x.Key.ActualHeight >= startHeight && x.Value.Y <= startHeight + endHeight);

            // No visible tiles, do nothing
            var visibleCount = visibleTiles.Count();
            if (visibleCount <= 0)
            {
                return;
            }

            // The Y coordinate of the lowest element is useful
            // when we animate from bottom to top
            double lowestY = visibleTiles.Max(el => el.Value.Y);

            // Store the animations to group them in one Storyboard in the end
            var animations = new List<Timeline>();
            var lastAnimations = new List<Timeline>();

            foreach (var tilePosition in visibleTiles)
            {
                var currentTileAnimationDuration = duration;
                bool isTileSelected = tilePosition.Key.DataContext == SelectedItem;
                if (isTileSelected)
                {
                    currentTileAnimationDuration
                        = currentTileAnimationDuration.Multiply(0.8d);
                }

                var tile = tilePosition.Key;
                var position = tilePosition.Value;
                var projection = tile.Projection as PlaneProjection;
                double rotationFrom, rotationTo, opacityTo;

                // Reset all children's opacity regardless of their animations
                if (mode == EnterMode.Exit)
                {
                    tile.Opacity = 1;
                    opacityTo = 0;
                    rotationFrom = 0;
                    rotationTo = zDirection == ZDirection.BackToFront ? -90 : 90;
                }
                else
                {
                    tile.Opacity = 0;
                    opacityTo = 1;
                    rotationFrom = zDirection == ZDirection.BackToFront ? -90 : 90;
                    rotationTo = 0;
                }

                // Used to determine begin time - depends if we're moving from bottom or from top
                double relativeY;

                if (yDirection == YDirection.BottomToTop)
                {
                    // The lowest element should have relativeY == 0
                    relativeY = lowestY - position.Y;
                }
                else
                {
                    relativeY = position.Y;
                }

                var easing = new QuadraticEase() { EasingMode = EasingMode.EaseInOut };

                var rotationAnimation = new DoubleAnimation { From = rotationFrom, To = rotationTo, EasingFunction = easing };
                rotationAnimation.Duration = currentTileAnimationDuration;
                if (isTileSelected)
                {
                    //animate tile as it's lower than all others (so it will be later even it's last in the list)
                    rotationAnimation.BeginTime =
                           currentTileAnimationDuration.Multiply(GetBeginTimeFactor(position.X, lowestY * 1.2d, mode));
                }
                else
                {
                    rotationAnimation.BeginTime =
                        currentTileAnimationDuration.Multiply(GetBeginTimeFactor(position.X, relativeY, mode));
                }
                rotationAnimation.SetTargetAndProperty(projection, PlaneProjection.RotationYProperty);

                var opacityAnimation = new DoubleAnimation { To = opacityTo, EasingFunction = easing };
                // The opacity animation takes the last 60% of the rotation animation
                opacityAnimation.Duration = currentTileAnimationDuration.Multiply(0.6);
                opacityAnimation.BeginTime = rotationAnimation.BeginTime;
                if (mode == EnterMode.Exit)
                    opacityAnimation.BeginTime += currentTileAnimationDuration - opacityAnimation.Duration.TimeSpan;
                opacityAnimation.SetTargetAndProperty(tile, UIElement.OpacityProperty);

                animations.Add(rotationAnimation);
                animations.Add(opacityAnimation);
            }
            animations.AddRange(lastAnimations);

            // Begin all animations
            var sb = new Storyboard();

            foreach (var a in animations)
            {
                sb.Children.Add(a);
            }

            sb.SpeedRatio = SpeedRatio;
            sb.Completed += sb_Completed;
            sb.Begin();
        }
 /// <summary>
 /// Constructs the <see cref="DecipherMessageScreen"/>.
 /// </summary>
 /// <param name="filePath">The filepath of the screen text.</param>
 /// <param name="mode">The mode for how the screen receives input.</param>
 public DecipherMessageScreen(string fileName, EnterMode mode) : base(fileName)
 {
     this.mode = mode;
 }
Example #11
0
 /// <summary>
 /// Animates the tiles using the supplied entrance mode, Y direction and Z direction, with a duration
 /// of 600 milliseconds per tile.
 /// </summary>
 /// <param name="mode">The entrance mode (Enter or Exit).</param>
 /// <param name="yDirection">The direction of the animation in the vertical axis (TopToBottom or BottomToTop)</param>
 /// <param name="zDirection">The direction of the animation in the depth axis (FrontToBack or BackToFront)</param>
 public void AnimateTiles(EnterMode mode, YDirection yDirection, ZDirection zDirection)
 {
     AnimateTiles(mode, yDirection, zDirection, TimeSpan.FromMilliseconds(600));
 }
Example #12
0
        /// <summary>
        /// Gets the BeginTime factor (BeginTime / Duration) for an animation of a tile based on its position and if it's entering or exiting.
        /// </summary>
        /// <param name="x">The tile's horizontal position relative to the parent control.</param>
        /// <param name="y">The tile's vertical position relative to the parent control. If the animation is from Bottom to Top,
        /// this parameter should be measured from Bottom to Top, starting on the lowest element.</param>
        /// <param name="mode">The animation entrance mode (Enter or Exit).</param>
        /// <returns>The factor that characterizes the BeginTime of this tile's animation.</returns>
        private double GetBeginTimeFactor(double x, double y, EnterMode mode)
        {
            // These numbers were tweaked through trial and error.
            // The main idea is that the weight of the Y coordinate must be
            // much more important than the X coordinate and the randomness factor.
            // Also, remember that X and Y are in pixels, so in absolute value
            // y * yFactor >> x * xFactor >> randomFactor
            const double xFactor = 4.7143E-4;
            const double yFactor = 0.001714;
            const double randomFactor = 0.0714;

            // The rightmost element must start first when exiting and last when entering
            var columnFactor = mode == EnterMode.Enter ? xFactor : -1 * xFactor;

            var result = y * yFactor + x * columnFactor + _random.Next(-1, 1) * randomFactor;
            return result;
        }
Example #13
0
 private double GetBeginTimeFactorFavorite(double x, double y, EnterMode mode)
 {
     const double yFactor = 4.7143E-4;
     const double xFactor = 0.001714;
     const double randomFactor = 0.0714;
     var columnFactor = mode == EnterMode.Enter ? xFactor : -1 * xFactor;
     return y * yFactor + x * columnFactor + this._random.Next(-1, 1) * randomFactor;
 }
Example #14
0
        private void AnimateTiles(EnterMode mode, XDirection xDirection, ZDirection zDirection, TimeSpan duration, Action callback)
        {
            // If the control has not been rendered or it's empty, cancel the animation
            if (this.ActualWidth <= 0 || this.ActualHeight <= 0 || this._positions == null || this._positions.Count <= 0) return;

            // Get the visible tiles for the current configuration
            // Tiles that are partially visible are also counted
            var visibleitems = this._positions.Where(x => x.Value.X + x.Key.ActualWidth >= 0 && x.Value.X <= this.ActualWidth &&
                                                     x.Value.Y + x.Key.ActualHeight >= 0 && x.Value.Y <= this.ActualHeight);

            // No visible tiles, do nothing
            if (visibleitems.Count() <= 0) return;

            // The Y coordinate of the lowest element is useful 
            // when we animate from bottom to top
            double lowestX = visibleitems.Max(el => el.Value.X);

            // Store the animations to group them in one Storyboard in the end
            var animations = new List<Timeline>();

            foreach (var tilePosition in visibleitems)
            {
                // To make syntax lighter
                var tile = tilePosition.Key;
                var position = tilePosition.Value;
                var projection = tile.Projection as PlaneProjection;

                double rotationFrom, rotationTo, opacityTo;

                // Reset all children's opacity regardless of their animations
                if (mode == EnterMode.Exit)
                {
                    tile.Opacity = 1;
                    opacityTo = 0;
                    rotationFrom = 0;
                    rotationTo = zDirection == ZDirection.BackToFront ? -90 : 90;
                }
                else
                {
                    tile.Opacity = 0;
                    opacityTo = 1;
                    rotationFrom = zDirection == ZDirection.BackToFront ? -90 : 90;
                    rotationTo = 0;
                }

                // Used to determine begin time - depends if we're moving from bottom or from top
                double relativeX;

                if (xDirection == XDirection.LeftToRight)
                {
                    // The lowest element should have relativeY == 0
                    relativeX = lowestX - position.X;
                }
                else
                {
                    relativeX = position.X;
                }

                var easing = new QuadraticEase() { EasingMode = EasingMode.EaseInOut };

                var rotationAnimation = new DoubleAnimation { From = rotationFrom, To = rotationTo, EasingFunction = easing };
                rotationAnimation.Duration = duration;
                rotationAnimation.BeginTime = duration.Multiply(this.GetBeginTimeFactorFavorite(relativeX, position.Y, mode));
                rotationAnimation.SetTargetAndProperty(projection, PlaneProjection.RotationYProperty);

                var opacityAnimation = new DoubleAnimation { To = opacityTo, EasingFunction = easing };
                // The opacity animation takes the last 60% of the rotation animation
                opacityAnimation.Duration = duration.Multiply(0.6);
                opacityAnimation.BeginTime = rotationAnimation.BeginTime;
                if (mode == EnterMode.Exit)
                    opacityAnimation.BeginTime += duration - opacityAnimation.Duration.TimeSpan;
                opacityAnimation.SetTargetAndProperty(tile, UIElement.OpacityProperty);

                animations.Add(rotationAnimation);
                animations.Add(opacityAnimation);
            }

            // Begin all animations
            var sb = new Storyboard();
            sb.Completed += (sender, args) =>
            {
                if (callback != null)
                {
                    callback();
                }
            };

            foreach (var a in animations)
                sb.Children.Add(a);
            sb.Begin();
        }
Example #15
0
 public void AnimateTiles(EnterMode mode, XDirection xDirection, ZDirection zDirection, Action callback, int time = SpeedAnimation)
 {
     this.AnimateTiles(mode, xDirection, zDirection, TimeSpan.FromMilliseconds(time), callback);
 }