From 1f4e14b2e973ee7de337fd4866d9a5ceff5cb6d1 Mon Sep 17 00:00:00 2001 From: Lucas Faria Mendes Date: Mon, 30 Mar 2026 10:38:18 -0300 Subject: chore: location --- .../Gestor.Application/Componentes/WebEditor.cs | 704 +++++++++++++++++++++ 1 file changed, 704 insertions(+) create mode 100644 Codemerx/Gestor.Application/Componentes/WebEditor.cs (limited to 'Codemerx/Gestor.Application/Componentes/WebEditor.cs') diff --git a/Codemerx/Gestor.Application/Componentes/WebEditor.cs b/Codemerx/Gestor.Application/Componentes/WebEditor.cs new file mode 100644 index 0000000..9e0cd81 --- /dev/null +++ b/Codemerx/Gestor.Application/Componentes/WebEditor.cs @@ -0,0 +1,704 @@ +using HtmlAgilityPack; +using Microsoft.Win32; +using mshtml; +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Net; +using System.Runtime.CompilerServices; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Forms; +using System.Windows.Markup; +using System.Windows.Media; + +namespace Gestor.Application.Componentes +{ + public class WebEditor : System.Windows.Controls.UserControl, IComponentConnector + { + private System.Windows.Forms.ColorDialog ColorDialog; + + public readonly static DependencyProperty ImageVisibilityProperty; + + public readonly static DependencyProperty BarVisibilityProperty; + + public readonly static DependencyProperty BodyProperty; + + public readonly static DependencyProperty IsReadOnlyProperty; + + internal WrapPanel PanelControls; + + internal System.Windows.Controls.ComboBox ComboTextFormat; + + internal System.Windows.Controls.ComboBox ComboFontFamily; + + internal System.Windows.Controls.ComboBox ComboFontSize; + + internal System.Windows.Controls.Button SettingsBold; + + internal System.Windows.Controls.Button SettingsItalic; + + internal System.Windows.Controls.Button SettingsUnderLine; + + internal System.Windows.Controls.Button SettingsFontColor; + + internal System.Windows.Controls.Button SettingsBullets; + + internal System.Windows.Controls.Button SettingsNumbered; + + internal System.Windows.Controls.Button SettingsLeftAlign; + + internal System.Windows.Controls.Button SettingsCenter; + + internal System.Windows.Controls.Button SettingsRightAlign; + + internal System.Windows.Controls.Button SettingsJustifyFull; + + internal System.Windows.Controls.Button SettingsLink; + + internal System.Windows.Controls.TextBox ImagemLink; + + internal Gestor.Application.Componentes.WebBrowser WebBrowserEditor; + + internal ItemsControl LinkPanel; + + internal System.Windows.Controls.TextBox HrefLink; + + internal CustomItemControl DescricaoBox; + + internal System.Windows.Controls.TextBox DescricaoLink; + + private bool _contentLoaded; + + public System.Windows.Visibility BarVisibility + { + get + { + return (System.Windows.Visibility)base.GetValue(WebEditor.BarVisibilityProperty); + } + set + { + base.SetValue(WebEditor.BarVisibilityProperty, value); + } + } + + public string Body + { + get + { + return (string)base.GetValue(WebEditor.BodyProperty); + } + set + { + base.SetValue(WebEditor.BodyProperty, value); + } + } + + public System.Windows.Visibility ImageVisibility + { + get + { + return (System.Windows.Visibility)base.GetValue(WebEditor.ImageVisibilityProperty); + } + set + { + base.SetValue(WebEditor.ImageVisibilityProperty, value); + } + } + + public bool IsReadOnly + { + get + { + return (bool)base.GetValue(WebEditor.IsReadOnlyProperty); + } + set + { + base.SetValue(WebEditor.IsReadOnlyProperty, value); + } + } + + [DebuggerNonUserCode] + [GeneratedCode("PresentationBuildTasks", "4.0.0.0")] + internal Delegate _CreateDelegate(Type delegateType, string handler) + { + return Delegate.CreateDelegate(delegateType, this, handler); + } + + static WebEditor() + { + WebEditor.ImageVisibilityProperty = DependencyProperty.Register("ImageVisibility", typeof(System.Windows.Visibility), typeof(WebEditor), new FrameworkPropertyMetadata((object)System.Windows.Visibility.Collapsed, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); + WebEditor.BarVisibilityProperty = DependencyProperty.Register("BarVisibility", typeof(System.Windows.Visibility), typeof(WebEditor), new FrameworkPropertyMetadata((object)System.Windows.Visibility.Visible, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); + WebEditor.BodyProperty = DependencyProperty.Register("Body", typeof(string), typeof(WebEditor), new FrameworkPropertyMetadata(string.Empty, new PropertyChangedCallback(WebEditor.OnBoundBodyChanged))); + WebEditor.IsReadOnlyProperty = DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(WebEditor), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(WebEditor.OnBoundReadOnlyChanged))); + } + + public WebEditor() + { + this.InitializeComponent(); + this.ColorDialog = new System.Windows.Forms.ColorDialog(); + } + + private void CancelLink_OnClick(object sender, RoutedEventArgs e) + { + this.LinkPanel.Visibility = System.Windows.Visibility.Collapsed; + this.WebBrowserEditor.Visibility = System.Windows.Visibility.Visible; + } + + private void ComboFontFamily_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + this.WebBrowserEditor.HtmlDocument = this.WebBrowserEditor.Browser.Document as HTMLDocument; + string str = this.ComboFontFamily.SelectedItem.ToString(); + HTMLDocument htmlDocument = this.WebBrowserEditor.HtmlDocument; + if (htmlDocument == null) + { + return; + } + htmlDocument.execCommand("FontName", false, str); + } + + private void ComboFontSize_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + this.WebBrowserEditor.HtmlDocument = this.WebBrowserEditor.Browser.Document as HTMLDocument; + HTMLDocument htmlDocument = this.WebBrowserEditor.HtmlDocument; + if (htmlDocument == null) + { + return; + } + htmlDocument.execCommand("FontSize", false, this.ComboFontSize.SelectedItem); + } + + private void ComboTextFormat_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + string str = this.ComboTextFormat.SelectedValue.ToString(); + this.WebBrowserEditor.HtmlDocument = this.WebBrowserEditor.Browser.Document as HTMLDocument; + HTMLDocument htmlDocument = this.WebBrowserEditor.HtmlDocument; + if (htmlDocument == null) + { + return; + } + htmlDocument.execCommand("FormatBlock", false, str); + } + + private void DecodeNode(HtmlNode node) + { + if (node.get_HasChildNodes()) + { + foreach (HtmlNode childNode in node.get_ChildNodes()) + { + if (childNode.get_NodeType() != 3) + { + this.DecodeNode(childNode); + } + else + { + childNode.set_InnerHtml(WebUtility.HtmlDecode(childNode.get_InnerHtml())); + } + } + } + else if (node.get_NodeType() == 3) + { + node.set_InnerHtml(WebUtility.HtmlDecode(node.get_InnerHtml())); + } + } + + private void FontFamilyInitialize() + { + this.ComboFontFamily.ItemsSource = Fonts.SystemFontFamilies; + this.ComboFontFamily.Text = "Times New Roman"; + } + + private void FontFormatInitialize() + { + this.ComboTextFormat.ItemsSource = new List>() + { + new KeyValuePair("

", "Parágrafo"), + new KeyValuePair("

", "Título 1"), + new KeyValuePair("

", "Título 2"), + new KeyValuePair("

", "Título 3"), + new KeyValuePair("

", "Título 4"), + new KeyValuePair("
", "Título 5"), + new KeyValuePair("
", "Título 6"), + new KeyValuePair("
", "Endereço"), + new KeyValuePair("
", "Pré-formatação")
+			};
+			this.ComboTextFormat.SelectedValuePath = "Key";
+			this.ComboTextFormat.DisplayMemberPath = "Value";
+			this.ComboTextFormat.SelectedIndex = 0;
+		}
+
+		private void FontSizeInitialize()
+		{
+			List strs = new List();
+			for (int i = 1; i <= 7; i++)
+			{
+				strs.Add(i.ToString());
+			}
+			this.ComboFontSize.ItemsSource = strs;
+			this.ComboFontSize.Text = "3";
+		}
+
+		public string GetHtml()
+		{
+			string htmlDocument = this.WebBrowserEditor.HtmlDocument.documentElement.innerHTML;
+			return this.HtmlDecode(htmlDocument.Replace("about:", string.Empty));
+		}
+
+		public string GetText()
+		{
+			if (this.WebBrowserEditor.HtmlDocument.documentElement.innerText.Trim() == "new")
+			{
+				return "";
+			}
+			return this.WebBrowserEditor.HtmlDocument.documentElement.innerText;
+		}
+
+		public string HtmlDecode(string html)
+		{
+			if (html == null)
+			{
+				return null;
+			}
+			HtmlAgilityPack.HtmlDocument htmlDocument = new HtmlAgilityPack.HtmlDocument();
+			htmlDocument.LoadHtml(html);
+			htmlDocument.OptionWriteEmptyNodes = true;
+			this.DecodeNode(htmlDocument.get_DocumentNode());
+			return htmlDocument.get_DocumentNode().get_InnerHtml();
+		}
+
+		public void Initialize(string htmlData = null)
+		{
+			this.WebBrowserEditor.InstanciateNew(htmlData);
+		}
+
+		[DebuggerNonUserCode]
+		[GeneratedCode("PresentationBuildTasks", "4.0.0.0")]
+		public void InitializeComponent()
+		{
+			if (this._contentLoaded)
+			{
+				return;
+			}
+			this._contentLoaded = true;
+			System.Windows.Application.LoadComponent(this, new Uri("/Gestor.Application;component/componentes/webeditor.xaml", UriKind.Relative));
+		}
+
+		private void InsertLink_OnClick(object sender, RoutedEventArgs e)
+		{
+			if (string.IsNullOrWhiteSpace(this.HrefLink.Text))
+			{
+				return;
+			}
+			IHTMLSelectionObject htmlDocument = this.WebBrowserEditor.HtmlDocument.selection;
+			if (htmlDocument != null)
+			{
+				IHTMLTxtRange variable = htmlDocument.createRange() as IHTMLTxtRange;
+				if (variable != null)
+				{
+					if (string.IsNullOrEmpty(variable.htmlText))
+					{
+						variable.pasteHTML(string.Concat(new string[] { "", this.DescricaoLink.Text, "" }));
+					}
+					else
+					{
+						variable.pasteHTML(string.Concat(new string[] { "", variable.htmlText, "" }));
+					}
+				}
+			}
+			this.LinkPanel.Visibility = System.Windows.Visibility.Collapsed;
+			this.WebBrowserEditor.Visibility = System.Windows.Visibility.Visible;
+		}
+
+		public void InsertText(string textToInsert)
+		{
+			object obj;
+			HTMLDocument htmlDocument = this.WebBrowserEditor.HtmlDocument;
+			if (htmlDocument != null)
+			{
+				IHTMLSelectionObject variable = htmlDocument.selection;
+				obj = (variable != null ? variable.createRange() : null);
+			}
+			else
+			{
+				obj = null;
+			}
+			dynamic obj1 = obj;
+			if (obj1 != null)
+			{
+				obj1.pasteHTML(textToInsert);
+			}
+		}
+
+		private static void OnBoundBodyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+		{
+			string newValue = (string)e.NewValue;
+			if (d != null)
+			{
+				WebEditor webEditor = d as WebEditor;
+				if (webEditor != null)
+				{
+					webEditor.WebBrowserEditor.InstanciateNew(newValue);
+					return;
+				}
+			}
+		}
+
+		private static void OnBoundReadOnlyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+		{
+			bool newValue = (bool)e.NewValue;
+			if (d != null)
+			{
+				WebEditor webEditor = d as WebEditor;
+				if (webEditor != null)
+				{
+					webEditor.WebBrowserEditor.ReadOnly(newValue);
+					webEditor.PanelControls.IsEnabled = !newValue;
+					return;
+				}
+			}
+		}
+
+		public System.Windows.Media.Color Pick()
+		{
+			System.Windows.Media.Color a = new System.Windows.Media.Color();
+			this.ColorDialog.AllowFullOpen = true;
+			this.ColorDialog.FullOpen = true;
+			if (this.ColorDialog.ShowDialog() != DialogResult.OK)
+			{
+				return a;
+			}
+			System.Drawing.Color color = this.ColorDialog.Color;
+			a.A = color.A;
+			color = this.ColorDialog.Color;
+			a.B = color.B;
+			color = this.ColorDialog.Color;
+			a.G = color.G;
+			color = this.ColorDialog.Color;
+			a.R = color.R;
+			return a;
+		}
+
+		private void SettingsBold_Click(object sender, RoutedEventArgs e)
+		{
+			HTMLDocument htmlDocument = this.WebBrowserEditor.HtmlDocument;
+			if (htmlDocument == null)
+			{
+				return;
+			}
+			htmlDocument.execCommand("Bold", false, Type.Missing);
+		}
+
+		private void SettingsBullets_Click(object sender, RoutedEventArgs e)
+		{
+			HTMLDocument htmlDocument = this.WebBrowserEditor.HtmlDocument;
+			if (htmlDocument == null)
+			{
+				return;
+			}
+			htmlDocument.execCommand("InsertUnorderedList", false, Type.Missing);
+		}
+
+		private void SettingsCenter_Click(object sender, RoutedEventArgs e)
+		{
+			HTMLDocument htmlDocument = this.WebBrowserEditor.HtmlDocument;
+			if (htmlDocument == null)
+			{
+				return;
+			}
+			htmlDocument.execCommand("JustifyCenter", false, Type.Missing);
+		}
+
+		private void SettingsFontColor_Click(object sender, RoutedEventArgs e)
+		{
+			this.WebBrowserEditor.HtmlDocument = this.WebBrowserEditor.Browser.Document as HTMLDocument;
+			if (this.WebBrowserEditor.HtmlDocument == null)
+			{
+				return;
+			}
+			System.Windows.Media.Color color = this.Pick();
+			string str = string.Format("#{0:X2}{1:X2}{2:X2}", color.R, color.G, color.B);
+			this.WebBrowserEditor.HtmlDocument.execCommand("ForeColor", false, str);
+		}
+
+		private void SettingsImage_Click(object sender, RoutedEventArgs e)
+		{
+			object obj;
+			Microsoft.Win32.OpenFileDialog openFileDialog = new Microsoft.Win32.OpenFileDialog()
+			{
+				Title = "Selecione a imagem",
+				Filter = "Todos os formatos|*.jpg;*.jpeg;*.png|JPEG (*.jpg;*.jpeg)|*.jpg;*.jpeg|Portable Network Graphic (*.png)|*.png"
+			};
+			if (!openFileDialog.ShowDialog().GetValueOrDefault())
+			{
+				return;
+			}
+			string str = Path.GetExtension(openFileDialog.FileName).ToLower().Substring(1);
+			string base64String = Convert.ToBase64String(File.ReadAllBytes(openFileDialog.FileName));
+			string str1 = string.Concat("data:image/", str, ";base64,", base64String);
+			HTMLDocument htmlDocument = this.WebBrowserEditor.HtmlDocument;
+			if (htmlDocument != null)
+			{
+				IHTMLSelectionObject variable = htmlDocument.selection;
+				obj = (variable != null ? variable.createRange() : null);
+			}
+			else
+			{
+				obj = null;
+			}
+			object obj1 = obj;
+			string str2 = string.Concat("\"Imagem\"");
+			if (!string.IsNullOrEmpty(this.ImagemLink.Text.Trim()))
+			{
+				str2 = string.Concat(new string[] { "", str2, "" });
+			}
+			dynamic obj2 = obj1;
+			if (obj2 != null)
+			{
+				obj2.pasteHTML(str2);
+			}
+		}
+
+		private void SettingsInsertOrderedList_Click(object sender, RoutedEventArgs e)
+		{
+			HTMLDocument htmlDocument = this.WebBrowserEditor.HtmlDocument;
+			if (htmlDocument == null)
+			{
+				return;
+			}
+			htmlDocument.execCommand("InsertOrderedList", false, Type.Missing);
+		}
+
+		private void SettingsItalic_Click(object sender, RoutedEventArgs e)
+		{
+			HTMLDocument htmlDocument = this.WebBrowserEditor.HtmlDocument;
+			if (htmlDocument == null)
+			{
+				return;
+			}
+			htmlDocument.execCommand("Italic", false, Type.Missing);
+		}
+
+		private void SettingsJustifyFull_Click(object sender, RoutedEventArgs e)
+		{
+			HTMLDocument htmlDocument = this.WebBrowserEditor.HtmlDocument;
+			if (htmlDocument == null)
+			{
+				return;
+			}
+			htmlDocument.execCommand("JustifyFull", false, Type.Missing);
+		}
+
+		private void SettingsLeftAlign_Click(object sender, RoutedEventArgs e)
+		{
+			HTMLDocument htmlDocument = this.WebBrowserEditor.HtmlDocument;
+			if (htmlDocument == null)
+			{
+				return;
+			}
+			htmlDocument.execCommand("JustifyLeft", false, Type.Missing);
+		}
+
+		private void SettingsLink_OnClick(object sender, RoutedEventArgs e)
+		{
+			HTMLDocument htmlDocument = this.WebBrowserEditor.HtmlDocument;
+			this.DescricaoBox.Visibility = System.Windows.Visibility.Visible;
+			IHTMLSelectionObject variable = htmlDocument.selection;
+			if (variable != null)
+			{
+				IHTMLTxtRange variable1 = variable.createRange() as IHTMLTxtRange;
+				if (variable1 != null)
+				{
+					this.DescricaoBox.Visibility = (!string.IsNullOrEmpty(variable1.htmlText) ? System.Windows.Visibility.Collapsed : System.Windows.Visibility.Visible);
+				}
+			}
+			this.LinkPanel.Visibility = System.Windows.Visibility.Visible;
+			this.WebBrowserEditor.Visibility = System.Windows.Visibility.Hidden;
+		}
+
+		private void SettingsRightAlign_Click(object sender, RoutedEventArgs e)
+		{
+			HTMLDocument htmlDocument = this.WebBrowserEditor.HtmlDocument;
+			if (htmlDocument == null)
+			{
+				return;
+			}
+			htmlDocument.execCommand("JustifyRight", false, Type.Missing);
+		}
+
+		private void SettingsUnderLine_Click(object sender, RoutedEventArgs e)
+		{
+			HTMLDocument htmlDocument = this.WebBrowserEditor.HtmlDocument;
+			if (htmlDocument == null)
+			{
+				return;
+			}
+			htmlDocument.execCommand("Underline", false, Type.Missing);
+		}
+
+		[DebuggerNonUserCode]
+		[EditorBrowsable(EditorBrowsableState.Never)]
+		[GeneratedCode("PresentationBuildTasks", "4.0.0.0")]
+		void System.Windows.Markup.IComponentConnector.Connect(int connectionId, object target)
+		{
+			switch (connectionId)
+			{
+				case 1:
+				{
+					((WebEditor)target).Loaded += new RoutedEventHandler(this.Window_Loaded);
+					return;
+				}
+				case 2:
+				{
+					this.PanelControls = (WrapPanel)target;
+					return;
+				}
+				case 3:
+				{
+					this.ComboTextFormat = (System.Windows.Controls.ComboBox)target;
+					this.ComboTextFormat.SelectionChanged += new SelectionChangedEventHandler(this.ComboTextFormat_SelectionChanged);
+					return;
+				}
+				case 4:
+				{
+					this.ComboFontFamily = (System.Windows.Controls.ComboBox)target;
+					this.ComboFontFamily.SelectionChanged += new SelectionChangedEventHandler(this.ComboFontFamily_SelectionChanged);
+					return;
+				}
+				case 5:
+				{
+					this.ComboFontSize = (System.Windows.Controls.ComboBox)target;
+					this.ComboFontSize.SelectionChanged += new SelectionChangedEventHandler(this.ComboFontSize_SelectionChanged);
+					return;
+				}
+				case 6:
+				{
+					this.SettingsBold = (System.Windows.Controls.Button)target;
+					this.SettingsBold.Click += new RoutedEventHandler(this.SettingsBold_Click);
+					return;
+				}
+				case 7:
+				{
+					this.SettingsItalic = (System.Windows.Controls.Button)target;
+					this.SettingsItalic.Click += new RoutedEventHandler(this.SettingsItalic_Click);
+					return;
+				}
+				case 8:
+				{
+					this.SettingsUnderLine = (System.Windows.Controls.Button)target;
+					this.SettingsUnderLine.Click += new RoutedEventHandler(this.SettingsUnderLine_Click);
+					return;
+				}
+				case 9:
+				{
+					this.SettingsFontColor = (System.Windows.Controls.Button)target;
+					this.SettingsFontColor.Click += new RoutedEventHandler(this.SettingsFontColor_Click);
+					return;
+				}
+				case 10:
+				{
+					this.SettingsBullets = (System.Windows.Controls.Button)target;
+					this.SettingsBullets.Click += new RoutedEventHandler(this.SettingsBullets_Click);
+					return;
+				}
+				case 11:
+				{
+					this.SettingsNumbered = (System.Windows.Controls.Button)target;
+					this.SettingsNumbered.Click += new RoutedEventHandler(this.SettingsInsertOrderedList_Click);
+					return;
+				}
+				case 12:
+				{
+					this.SettingsLeftAlign = (System.Windows.Controls.Button)target;
+					this.SettingsLeftAlign.Click += new RoutedEventHandler(this.SettingsLeftAlign_Click);
+					return;
+				}
+				case 13:
+				{
+					this.SettingsCenter = (System.Windows.Controls.Button)target;
+					this.SettingsCenter.Click += new RoutedEventHandler(this.SettingsCenter_Click);
+					return;
+				}
+				case 14:
+				{
+					this.SettingsRightAlign = (System.Windows.Controls.Button)target;
+					this.SettingsRightAlign.Click += new RoutedEventHandler(this.SettingsRightAlign_Click);
+					return;
+				}
+				case 15:
+				{
+					this.SettingsJustifyFull = (System.Windows.Controls.Button)target;
+					this.SettingsJustifyFull.Click += new RoutedEventHandler(this.SettingsJustifyFull_Click);
+					return;
+				}
+				case 16:
+				{
+					this.SettingsLink = (System.Windows.Controls.Button)target;
+					this.SettingsLink.Click += new RoutedEventHandler(this.SettingsLink_OnClick);
+					return;
+				}
+				case 17:
+				{
+					this.ImagemLink = (System.Windows.Controls.TextBox)target;
+					return;
+				}
+				case 18:
+				{
+					((System.Windows.Controls.MenuItem)target).Click += new RoutedEventHandler(this.SettingsImage_Click);
+					return;
+				}
+				case 19:
+				{
+					this.WebBrowserEditor = (Gestor.Application.Componentes.WebBrowser)target;
+					return;
+				}
+				case 20:
+				{
+					this.LinkPanel = (ItemsControl)target;
+					return;
+				}
+				case 21:
+				{
+					((System.Windows.Controls.Button)target).Click += new RoutedEventHandler(this.CancelLink_OnClick);
+					return;
+				}
+				case 22:
+				{
+					this.HrefLink = (System.Windows.Controls.TextBox)target;
+					return;
+				}
+				case 23:
+				{
+					this.DescricaoBox = (CustomItemControl)target;
+					return;
+				}
+				case 24:
+				{
+					this.DescricaoLink = (System.Windows.Controls.TextBox)target;
+					return;
+				}
+				case 25:
+				{
+					((System.Windows.Controls.Button)target).Click += new RoutedEventHandler(this.InsertLink_OnClick);
+					return;
+				}
+			}
+			this._contentLoaded = true;
+		}
+
+		private void Window_Loaded(object sender, RoutedEventArgs e)
+		{
+			if (this.WebBrowserEditor.Browser == null)
+			{
+				this.WebBrowserEditor.InstanciateNew(null);
+			}
+			this.FontFamilyInitialize();
+			this.FontSizeInitialize();
+			this.FontFormatInitialize();
+		}
+	}
+}
\ No newline at end of file
-- 
cgit v1.2.3