text:
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;
using InternalRealtimeCSG;

namespace RealtimeCSG
{
	internal static class MeshToolGUI
	{
		static int SceneViewMeshOverlayHash = "SceneViewMeshOverlay".GetHashCode();

		static GUIContent			ContentMeshLabel;
//		static GUIContent			ContentBrushesLabel;
//		static GUIContent			ContentEdgesLabel;
		private static readonly GUIContent	ContentDefaultMaterial	= new GUIContent("Default");

		private static readonly GUILayoutOption labelWidth			= GUILayout.Width(23);
		private static readonly GUILayoutOption largeLabelWidth		= GUILayout.Width(80);
		private static readonly GUILayoutOption[] InSceneWidth		= new GUILayoutOption[] { GUILayout.Width(150) };

		/*
		static readonly CSGOperationType[] operationValues = new CSGOperationType[]
			{
				CSGOperationType.Additive,
				CSGOperationType.Subtractive,
				CSGOperationType.Intersecting
			};
		*/
		static void InitLocalStyles()
		{
			if (ContentMeshLabel != null)
				return;
			ContentMeshLabel	= new GUIContent(GUIStyleUtility.brushEditModeNames[(int)ToolEditMode.Mesh]);
//			ContentBrushesLabel	= new GUIContent(GUIStyleUtility.brushEditModeNames[(int)BrushEditMode.Brushes]);
//			ContentEdgesLabel	= new GUIContent("Edges");
		}

		static GUIContent	ContentFlip			= new GUIContent("Flip");
		static GUIContent	ContentFlipX		= new GUIContent("X");
		static ToolTip		TooltipFlipX		= new ToolTip("Flip X", "Flip the selection in the x direction", Keys.FlipSelectionX);
		static GUIContent	ContentFlipY		= new GUIContent("Y");
		static ToolTip		TooltipFlipY		= new ToolTip("Flip Y", "Flip the selection in the y direction", Keys.FlipSelectionY);
		static GUIContent	ContentFlipZ		= new GUIContent("Z");
		static ToolTip		TooltipFlipZ		= new ToolTip("Flip Z", "Flip the selection in the z direction", Keys.FlipSelectionZ);
		static GUIContent	ContentSnapToGrid	= new GUIContent("Snap to grid");
		static ToolTip		TooltipSnapToGrid	= new ToolTip(ContentSnapToGrid.text, "Snap the selection to the closest grid lines", Keys.SnapToGridKey);

		static Rect lastGuiRect;
		public static Rect GetLastSceneGUIRect(MeshEditBrushTool tool)
		{
			return lastGuiRect;
		}

		public static void OnSceneGUI(MeshEditBrushTool tool)
		{
			GUIStyleUtility.InitStyles();
			InitLocalStyles();
			GUILayout.BeginHorizontal(GUIStyleUtility.ContentEmpty);
			{
				GUILayout.BeginVertical(GUIStyleUtility.ContentEmpty);
				{
					GUILayout.BeginVertical(GUIStyleUtility.ContentEmpty);
					{
						GUILayout.FlexibleSpace();

						GUIStyleUtility.ResetGUIState();

						GUIStyle windowStyle = GUI.skin.window;
						GUILayout.BeginVertical(ContentMeshLabel, windowStyle, GUIStyleUtility.ContentEmpty);
						{
							OnGUIContents(true, tool);
						}
						GUILayout.EndVertical();

						var currentArea = GUILayoutUtility.GetLastRect();
						lastGuiRect = currentArea;

						var buttonArea = currentArea;
						buttonArea.x += buttonArea.width - 17;
						buttonArea.y += 2;
						buttonArea.height = 13;
						buttonArea.width = 13;
						if (GUI.Button(buttonArea, GUIContent.none, "WinBtnClose"))
							CSGBrushEditorWindow.GetWindow();
						TooltipUtility.SetToolTip(GUIStyleUtility.PopOutTooltip, buttonArea);

						int controlID = GUIUtility.GetControlID(SceneViewMeshOverlayHash, FocusType.Keyboard, currentArea);
						switch (Event.current.GetTypeForControl(controlID))
						{
							case EventType.MouseDown: { if (currentArea.Contains(Event.current.mousePosition)) { GUIUtility.hotControl = controlID; GUIUtility.keyboardControl = controlID; Event.current.Use(); } break; }
							case EventType.MouseMove: { if (currentArea.Contains(Event.current.mousePosition)) { Event.current.Use(); } break; }
							case EventType.MouseUp: { if (GUIUtility.hotControl == controlID) { GUIUtility.hotControl = 0; GUIUtility.keyboardControl = 0; Event.current.Use(); } break; }
							case EventType.MouseDrag: { if (GUIUtility.hotControl == controlID) { Event.current.Use(); } break; }
							case EventType.ScrollWheel: { if (currentArea.Contains(Event.current.mousePosition)) { Event.current.Use(); } break; }
						}
					}
					GUILayout.EndVertical();
				}
				GUILayout.EndVertical();
				GUILayout.FlexibleSpace();
			}
			GUILayout.EndHorizontal();
		}

		public static void OnInspectorGUI(EditorWindow window)
		{
			lastGuiRect = Rect.MinMaxRect(-1, -1, -1, -1);
			var tool = CSGBrushEditorManager.ActiveTool as MeshEditBrushTool;
			GUIStyleUtility.InitStyles();
			InitLocalStyles();
			OnGUIContents(false, tool);
		}

		static void ChooseCSGOperation(FilteredSelection filteredSelection, bool isSceneGUI, MeshEditBrushTool tool)
		{
			bool operations_enabled = tool != null &&
									(filteredSelection.NodeTargets.Length > 0 && filteredSelection.ModelTargets.Length == 0);

			EditorGUI.BeginDisabledGroup(!operations_enabled);
			{
				bool mixedValues = tool == null || ((filteredSelection.BrushTargets.Length == 0) && (filteredSelection.OperationTargets.Length == 0));
				CSGOperationType operationType = CSGOperationType.Additive;
				if (tool != null)
				{
					if (filteredSelection.BrushTargets.Length > 0)
					{
						operationType = filteredSelection.BrushTargets[0].OperationType;
						for (int i = 1; i < filteredSelection.BrushTargets.Length; i++)
						{
							if (filteredSelection.BrushTargets[i].OperationType != operationType)
							{
								mixedValues = true;
							}
						}
					}
					else
					if (filteredSelection.OperationTargets.Length > 0)
					{
						operationType = filteredSelection.OperationTargets[0].OperationType;
					}

					if (filteredSelection.OperationTargets.Length > 0)
					{
						for (int i = 0; i < filteredSelection.OperationTargets.Length; i++)
						{
							if (filteredSelection.OperationTargets[i].OperationType != operationType)
							{
								mixedValues = true;
							}
						}
					}
				}

				GUILayout.BeginVertical(isSceneGUI ? GUI.skin.box : GUIStyle.none);
				{
					bool passThroughValue = false;
					if (tool != null &&
						//filteredSelection.BrushTargets.Length == 0 &&
						filteredSelection.OperationTargets.Length > 0 &&
						filteredSelection.OperationTargets.Length == filteredSelection.NodeTargets.Length) // only operations
					{
						bool? passThrough = filteredSelection.OperationTargets[0].PassThrough;
						for (int i = 1; i < filteredSelection.OperationTargets.Length; i++)
						{
							if (passThrough.HasValue && passThrough.Value != filteredSelection.OperationTargets[i].PassThrough)
							{
								passThrough = null;
								break;
							}
						}

						mixedValues = !passThrough.HasValue || passThrough.Value;

						var ptMixedValues = !passThrough.HasValue;
						passThroughValue = passThrough.HasValue ? passThrough.Value : false;
						if (GUIStyleUtility.PassThroughButton(passThroughValue, ptMixedValues))
						{
							Undo.RecordObjects(filteredSelection.OperationTargets, "Changed CSG operation of nodes");
							foreach (var operation in filteredSelection.OperationTargets)
							{
								operation.PassThrough = true;
							}
							InternalCSGModelManager.Refresh();
							EditorApplication.RepaintHierarchyWindow();
						}

						if (passThroughValue)
							operationType = (CSGOperationType)255;
					}
					EditorGUI.BeginChangeCheck();
					{
						operationType = GUIStyleUtility.ChooseOperation(operationType, mixedValues);
					}
					if (EditorGUI.EndChangeCheck() && tool != null)
					{
						Undo.RecordObjects(filteredSelection.NodeTargets, "Changed CSG operation of nodes");
						for (int i = 0; i < filteredSelection.BrushTargets.Length; i++)
						{
							filteredSelection.BrushTargets[i].OperationType = operationType;
						}
						for (int i = 0; i < filteredSelection.OperationTargets.Length; i++)
						{
							filteredSelection.OperationTargets[i].PassThrough = false;
							filteredSelection.OperationTargets[i].OperationType = operationType;
						}
						InternalCSGModelManager.Refresh();
						EditorApplication.RepaintHierarchyWindow();
					}
				}
				GUILayout.EndVertical();
			}
			EditorGUI.EndDisabledGroup();
		}

		static void OnGUIContents(bool isSceneGUI, MeshEditBrushTool tool)
		{
			CommonGUI.StartToolGUI();

			var filteredSelection = CSGBrushEditorManager.FilteredSelection;

			var defaultMaterial = CSGSettings.DefaultMaterial;
			GUILayout.BeginVertical(isSceneGUI ? InSceneWidth : GUIStyleUtility.ContentEmpty);
			{/*
				GUILayout.BeginHorizontal(GUIStyleUtility.ContentEmpty);
				{

					GUILayout.BeginVertical(isSceneGUI ? GUI.skin.box : GUIStyle.none);
					{
						//GUILayout.Label(Keys.VerticalMoveMode.ToString() + " to dragging brush up/down", EditorStyles.miniLabel);
						GUILayout.Label("Control (hold) to drag polygon on it's plane", EditorStyles.miniLabel);
						GUILayout.Label("Shift (hold) to drag extrude polygon", EditorStyles.miniLabel);
						GUILayout.Label("Shift (hold) to chamfer edges and vertices", EditorStyles.miniLabel);
					}
					GUILayout.EndVertical();
				}
				GUILayout.EndHorizontal();*/
				ChooseCSGOperation(filteredSelection, isSceneGUI, tool);
				GUILayout.BeginHorizontal(GUIStyleUtility.ContentEmpty);
				{
					if (isSceneGUI)
					{
						GUILayout.BeginVertical(GUIStyleUtility.ContentEmpty);
						{
							EditorGUI.BeginChangeCheck();
							{
								defaultMaterial = GUIStyleUtility.MaterialImage(defaultMaterial, small: true);
							}
							if (EditorGUI.EndChangeCheck() && defaultMaterial)
							{
								CSGSettings.DefaultMaterial = defaultMaterial;
								CSGSettings.Save();
							}
						}
						GUILayout.EndVertical();
					}
					GUILayout.BeginVertical(GUIStyleUtility.ContentEmpty);
					{
						bool have_nodes = tool != null && (filteredSelection.NodeTargets.Length > 0);

						EditorGUI.BeginDisabledGroup(!have_nodes);
						{
							GUIStyle left = EditorStyles.miniButtonLeft;
							GUIStyle middle = EditorStyles.miniButtonMid;
							GUIStyle right = EditorStyles.miniButtonRight;

							GUILayout.BeginHorizontal(GUIStyleUtility.ContentEmpty);
							{
								if (isSceneGUI)
									EditorGUILayout.LabelField(ContentFlip, labelWidth);
								else
									EditorGUILayout.LabelField(ContentFlip, largeLabelWidth);
								if (GUILayout.Button(ContentFlipX, left)) { tool.FlipX(); }
								TooltipUtility.SetToolTip(TooltipFlipX);
								if (GUILayout.Button(ContentFlipY, middle)) { tool.FlipY(); }
								TooltipUtility.SetToolTip(TooltipFlipY);
								if (GUILayout.Button(ContentFlipZ, right)) { tool.FlipZ(); }
								TooltipUtility.SetToolTip(TooltipFlipZ);
							}
							GUILayout.EndHorizontal();

							/*
							EditorGUILayout.LabelField(ContentEdgesLabel);
							GUILayout.BeginHorizontal(GUIStyleUtility.ContentEmpty);
							{
								EditorGUI.BeginDisabledGroup(!tool.CanSmooth());
								{
									if (GUILayout.Button("Smooth"))		{ tool.Smooth(); }
								}
								EditorGUI.EndDisabledGroup();
								EditorGUI.BeginDisabledGroup(!tool.CanUnSmooth());
								{
									if (GUILayout.Button("Un-smooth"))	{ tool.UnSmooth(); }
								}
								EditorGUI.EndDisabledGroup();
							}
							GUILayout.EndHorizontal();
							*/

							if (GUILayout.Button(ContentSnapToGrid)) { tool.SnapToGrid(); }
							TooltipUtility.SetToolTip(TooltipSnapToGrid);
						}
						EditorGUI.EndDisabledGroup();
					}
					GUILayout.EndVertical();
				}
				GUILayout.EndHorizontal();
				GUILayout.Space(2);
				if (!isSceneGUI)
				{
					EditorGUILayout.Space();
					GUILayout.BeginHorizontal(GUIStyleUtility.ContentEmpty);
					{
						EditorGUILayout.LabelField(ContentDefaultMaterial, largeLabelWidth);
						GUILayout.BeginVertical(GUIStyleUtility.ContentEmpty);
						{
							EditorGUI.BeginChangeCheck();
							{
								defaultMaterial = EditorGUILayout.ObjectField(defaultMaterial, typeof(Material), true) as Material;
							}
							if (EditorGUI.EndChangeCheck() && defaultMaterial)
							{
								CSGSettings.DefaultMaterial = defaultMaterial;
								CSGSettings.Save();
							}
						}
						GUILayout.Space(2);
						GUILayout.BeginHorizontal(GUIStyleUtility.ContentEmpty);
						{
							GUILayout.Space(5);
							defaultMaterial = GUIStyleUtility.MaterialImage(defaultMaterial, small: false);
						}
						GUILayout.EndHorizontal();
						GUILayout.EndVertical();
					}
					GUILayout.EndHorizontal();
					/*
					// Unity won't let us do this
					GUILayout.BeginVertical(GUIStyleUtility.ContentEmpty);
					OnGUIContentsMaterialInspector(first_material, multiple_materials);
					GUILayout.EndVertical();
					*/
				} else
				{
					EditorGUI.BeginChangeCheck();
					{
						defaultMaterial = EditorGUILayout.ObjectField(defaultMaterial, typeof(Material), true) as Material;
					}
					if (EditorGUI.EndChangeCheck() && defaultMaterial)
					{
						CSGSettings.DefaultMaterial = defaultMaterial;
						CSGSettings.Save();
					}
				}
			}
			GUILayout.EndVertical();
			EditorGUI.showMixedValue = false;
		}
	}
}


text:
package org.ands.rifcs.base;

import java.util.ArrayList;
import java.util.List;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * Class representing a RIF-CS electronic address object.
 *
 * @author Scott Yeadon
 *
 */
public class Electronic extends RIFCSElement {
    /** List of Notes nodes.
     * @since 3.0.0
     */
    private List<Notes> notes = new ArrayList<Notes>();
    /** List of MediaType nodes.
     * @since 3.0.0
     */
    private List<MediaType> mediaTypes = new ArrayList<MediaType>();
    /** List of Arg nodes. */
    private List<Arg> args = new ArrayList<Arg>();

    /**
     * Construct an Electronic address object.
     *
     * @param n
     *        A w3c Node, typically an Element
     *
     * @throws RIFCSException A RIFCSException
     */
    protected Electronic(final Node n) throws RIFCSException {
        super(n, Constants.ELEMENT_ELECTRONIC);
        initStructures();
    }

    /**
     * Set the type.
     *
     * @param type
     *      The electronic address type
     */
    public final void setType(final String type) {
        super.setAttributeValue(Constants.ATTRIBUTE_TYPE, type);
    }

    /**
     * return the type.
     *
     * @return
     *      The type attribute value or empty string if attribute
     *      is empty or not present
     */
    public final String getType() {
        return super.getAttributeValue(Constants.ATTRIBUTE_TYPE);
    }

    /**
     * Set the target.
     *
     * @param target
     *      The electronic target
     * @since 3.0.0
     */
    public final void setTarget(final String target) {
        super.setAttributeValue(Constants.ATTRIBUTE_TARGET, target);
    }

    /**
     * Return the target.
     *
     * @return
     *      The target attribute value or empty string if attribute
     *      is empty or not present
     * @since 3.0.0
     */
    public final String getTarget() {
        return super.getAttributeValue(Constants.ATTRIBUTE_TARGET);
    }

    /**
     * Set the title.
     *
     * @param title
     *    The title of the electronic address
     * @since 3.0.0
     */
    public final void setTitle(final String title) {
        Element e = this.newElement(Constants.ELEMENT_TITLE);
        e.setTextContent(title);
        this.getElement().appendChild(e);
    }

    /**
     * Get the title.
     *
     * @return String
     *    The title of the electronic address
     * @since 3.0.0
     */
    public final String getTitle() {
        NodeList nl = super.getElements(Constants.ELEMENT_TITLE);
        if (nl.getLength() == 1) {
            return nl.item(0).getTextContent();
        }

        return null;
    }

    /**
     * Add a notes element to the electronic address.
     *
     * @param notesObject
     *    a Notes object
     * @since 3.0.0
     */
    public final void addNotes(final Notes notesObject) {
        this.getElement().appendChild(notesObject.getElement());
        this.notes.add(notesObject);
    }

    /**
     * Obtain the notes elements of this electronic address.
     *
     * @return
     *      A list of Notes objects
     * @since 3.0.0
     */
    public final List<Notes> getNotes() {
        return notes;
    }

    /**
     * Create and return an empty notes element.
     *
     * The returned object has no properties or content and is not part
     * of the RIF-CS document, it is essentially a constructor of an object
     * owned by the RIF-CS document. The returned object needs to be
     * "filled out" (e.g. with properties, additional sub-elements, etc)
     * before being added to the RIF-CS document.
     *
     * @return the new Notes object
     *
     * @throws RIFCSException A RIFCSException
     * @since 3.0.0
     *
     */
    public final Notes newNotes() throws RIFCSException {
        return new Notes(this.newElement(Constants.ELEMENT_NOTES));
    }

    /**
     * Convenience method to add an notes element to the electronic object.
     *
     * @param notesText
     *            notes to be added
     *
     * @throws RIFCSException A RIFCSException
     * @since 3.0.0
     *
     */
    public final void addNotes(final String notesText)
            throws RIFCSException {
        Notes n = newNotes();
        n.setValue(notesText);
        addNotes(n);
    }

    /**
     * Add a media type to the electronic address.
     *
     * @param mediaType
     *    a MediaType object
     * @since 3.0.0
     */
    public final void addMediaType(final MediaType mediaType) {
        this.getElement().appendChild(mediaType.getElement());
        this.mediaTypes.add(mediaType);
    }

    /**
     * Obtain the media types of this electronic resource.
     *
     * @return
     *      A list of MediaType objects
     * @since 3.0.0
     */
    public final List<MediaType> getMediaTypes() {
        return mediaTypes;
    }

    /**
     * Create and return an empty media type element.
     *
     * The returned object has no properties or content and is not part
     * of the RIF-CS document, it is essentially a constructor of an object
     * owned by the RIF-CS document. The returned object needs to be
     * "filled out" (e.g. with properties, additional sub-elements, etc)
     * before being added to the RIF-CS document.
     *
     * @return the new MediaType object
     *
     * @throws RIFCSException A RIFCSException
     * @since 3.0.0
     *
     */
    public final MediaType newMediaType() throws RIFCSException {
        return new MediaType(this.newElement(Constants.ELEMENT_MEDIATYPE));
    }

    /**
     * Convenience method to add a media type element to the electronic object.
     *
     * @param mediaTypeText
     *            media type to be added
     *
     * @throws RIFCSException A RIFCSException
     * @since 3.0.0
     *
     */
    public final void addMediaType(final String mediaTypeText)
            throws RIFCSException {
        MediaType m = newMediaType();
        m.setValue(mediaTypeText);
        addMediaType(m);
    }

    /**
     * Set the byteSize.
     *
     * @param byteSize
     *    The byte size of the electronic address
     * @since 3.0.0
     */
    public final void setByteSize(final String byteSize) {
        Element e = this.newElement(Constants.ELEMENT_BYTESIZE);
        e.setTextContent(byteSize);
        this.getElement().appendChild(e);
    }

    /**
     * Get the byte size.
     *
     * @return String
     *    The byte size of the electronic address
     * @since 3.0.0
     */
    public final String getByteSize() {
        NodeList nl = super.getElements(Constants.ELEMENT_BYTESIZE);
        if (nl.getLength() == 1) {
            return nl.item(0).getTextContent();
        }

        return null;
    }

    /**
     * Create and return an empty Arg object.
     *
     * The returned object has no properties or content and is not part
     * of the RIF-CS document, it is essentially a constructor of an object
     * owned by the RIF-CS document. The returned object needs to be
     * "filled out" (e.g. with properties, additional sub-elements, etc)
     * before being added to the RIF-CS document.
     *
     * @return the new Arg object
     *
     * @throws RIFCSException A RIFCSException
     *
     */
    public final Arg newArg() throws RIFCSException {
        return new Arg(this.newElement(Constants.ELEMENT_ARG));
    }

    /**
     * Add an argument to the electroinc address object.
     *
     * @param name
     *    the name of the argument
     * @param required
     *    true if the argument is required else false
     * @param type
     *    the argument type
     * @param use
     *    the argument use
     * @throws RIFCSException A RIFCSException
     */
    public final void addArg(final String name,
                       final String required,
                       final String type,
                       final String use) throws RIFCSException {
        Arg arg = newArg();
        arg.setName(name);
        arg.setRequired(required);
        arg.setType(type);
        arg.setUse(use);
        addArg(arg);
    }

    /**
     * Obtain the arguments for this electronic address.
     *
     * @return
     *      A list of Arg objects
     */
    public final List<Arg> getArgs() {
        return args;
    }

    /**
     * Add an argument to the electronic address object.
     *
     * @param arg
     *    a completed Arg object
     */
    public final void addArg(final Arg arg) {
       this.getElement().appendChild(arg.getElement());
       this.args.add(arg);
    }

    /**
     * Set the electronic address URI.
     *
     * @param valueUri
     *    a resolvable URI representing the electronic address
     *    of the containing registry object
     */
    public final void setValue(final String valueUri) {
        Element value = this.newElement(Constants.ELEMENT_VALUE);
        value.setTextContent(valueUri);
        this.getElement().appendChild(value);
    }

    /**
     * Return the electronic address URI.
     *
     * @return
     *    a resolvable URI representing the electronic address
     *    of the containing registry object
     */
    public final String getValue() {
        NodeList nl = super.getElements(Constants.ELEMENT_VALUE);
        if (nl.getLength() == 1) {
            return nl.item(0).getTextContent();
        }

        return null;
    }

    /** Initialisation code for existing documents.
     *
     * @throws RIFCSException A RIFCSException
     *
     */
    private void initStructures() throws RIFCSException {
        NodeList nl = super.getElements(Constants.ELEMENT_ARG);

        for (int i = 0; i < nl.getLength(); i++) {
            args.add(new Arg(nl.item(i)));
        }
    }
}

redpajama_set_name: RedPajamaGithub


text:
<!DOCTYPE>

        <title>Star React Wars!</title>
        <meta charset="utf-8" />
        <link href="https://fonts.googleapis.com/css?family=Poller+One" rel="stylesheet">
        <link href="css/styles.css" rel="stylesheet">

Star Wars using React JS!


text:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

	<title>CSS Visible Rule</title>

	In this text here is a span with content "test" that has visibility: hidden -
	test. You don't see the span, but it takes space

	In this text here is a span with content "test" that has visibility: visible -
	test. Now you see the span, but the surrounding text is the same

	In this text here is a span with content "test" that has display:none -
	test. You don't see the span and it doesnt take space


text:
<?php

namespace OwenIt\Auditing\Tests;

use Carbon\Carbon;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Foundation\Testing\Assert;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Str;
use OwenIt\Auditing\Contracts\Auditable;
use OwenIt\Auditing\Encoders\Base64Encoder;
use OwenIt\Auditing\Exceptions\AuditableTransitionException;
use OwenIt\Auditing\Exceptions\AuditingException;
use OwenIt\Auditing\Models\Audit;
use OwenIt\Auditing\Redactors\LeftRedactor;
use OwenIt\Auditing\Redactors\RightRedactor;
use OwenIt\Auditing\Tests\Models\ApiModel;
use OwenIt\Auditing\Tests\Models\Article;
use OwenIt\Auditing\Tests\Models\ArticleExcludes;
use OwenIt\Auditing\Tests\Models\User;
use ReflectionClass;

class AuditableTest extends AuditingTestCase
{
    use WithFaker;
    /**
     * {@inheritdoc}
     */
    public function setUp(): void
    {
        parent::setUp();

        // Clear morph maps
        Relation::morphMap([], false);
    }

    /**
     * @group Auditable::isAuditingEnabled
     * @test
     */
    public function itWillNotAuditModelsWhenRunningFromTheConsole()
    {
        $this->app['config']->set('audit.console', false);

        $this->assertFalse(Article::isAuditingEnabled());
    }

    /**
     * @group Auditable::isAuditingEnabled
     * @test
     */
    public function itWillAuditModelsWhenRunningFromTheConsole()
    {
        $this->app['config']->set('audit.console', true);

        $this->assertTrue(Article::isAuditingEnabled());
    }

    /**
     * @group Auditable::isAuditingEnabled
     * @test
     */
    public function itWillAlwaysAuditModelsWhenNotRunningFromTheConsole()
    {
        App::shouldReceive('runningInConsole')
            ->andReturn(false);

        $this->app['config']->set('audit.console', false);

        $this->assertTrue(Article::isAuditingEnabled());
    }

    /**
     * @group Auditable::bootAuditable
     * @test
     */
    public function itWillNotBootTraitWhenStaticFlagIsSet()
    {
        App::spy();

        Article::$auditingDisabled = true;

        new Article();

        App::shouldNotHaveReceived('runningInConsole');

        Article::$auditingDisabled = false;
    }

    /**
     * @group Auditable::getAuditEvent
     * @test
     */
    public function itReturnsNullWhenTheAuditEventIsNotSet()
    {
        $model = new Article();

        $this->assertNull($model->getAuditEvent());
    }

    /**
     * @group Auditable::getAuditEvent
     * @test
     */
    public function itReturnsTheAuditEventThatHasBeenSet()
    {
        $model = new Article();
        $model->setAuditEvent('created');

        $this->assertSame('created', $model->getAuditEvent());
    }

    /**
     * @group Auditable::getAuditEvents
     * @test
     */
    public function itReturnsTheDefaultAuditEvents()
    {
        $model = new Article();

        self::Assert()::assertArraySubset([
            'created',
            'updated',
            'deleted',
            'restored',
        ], $model->getAuditEvents(), true);
    }

    /**
     * @group Auditable::getAuditEvents
     * @test
     */
    public function itReturnsTheCustomAuditEventsFromAttribute()
    {
        $model = new Article();

        $model->auditEvents = [
            'published' => 'getPublishedEventAttributes',
            'archived',
        ];

        self::Assert()::assertArraySubset([
            'published' => 'getPublishedEventAttributes',
            'archived',
        ], $model->getAuditEvents(), true);
    }

    /**
     * @group Auditable::getAuditEvents
     * @test
     */
    public function itReturnsTheCustomAuditEventsFromConfig()
    {
        $this->app['config']->set('audit.events', [
            'published' => 'getPublishedEventAttributes',
            'archived',
        ]);

        $model = new Article();

        self::Assert()::assertArraySubset([
            'published' => 'getPublishedEventAttributes',
            'archived',
        ], $model->getAuditEvents(), true);
    }

    /**
     * @group Auditable::setAuditEvent
     * @group Auditable::readyForAuditing
     * @test
     */
    public function itIsNotReadyForAuditingWithCustomEvent()
    {
        $model = new Article();

        $model->setAuditEvent('published');
        $this->assertFalse($model->readyForAuditing());
    }

    /**
     * @group Auditable::setAuditEvent
     * @group Auditable::readyForAuditing
     * @test
     */
    public function itIsReadyForAuditingWithCustomEvents()
    {
        $model = new Article();

        $model->auditEvents = [
            'published' => 'getPublishedEventAttributes',
            '*ted'      => 'getMultiEventAttributes',
            'archived',
        ];

        $model->setAuditEvent('published');
        $this->assertTrue($model->readyForAuditing());

        $model->setAuditEvent('archived');
        $this->assertTrue($model->readyForAuditing());

        $model->setAuditEvent('redacted');
        $this->assertTrue($model->readyForAuditing());
    }

    /**
     * @group Auditable::setAuditEvent
     * @group Auditable::readyForAuditing
     * @test
     */
    public function itIsReadyForAuditingWithRegularEvents()
    {
        $model = new Article();

        $model->setAuditEvent('created');
        $this->assertTrue($model->readyForAuditing());

        $model->setAuditEvent('updated');
        $this->assertTrue($model->readyForAuditing());

        $model->setAuditEvent('deleted');
        $this->assertTrue($model->readyForAuditing());

        $model->setAuditEvent('restored');
        $this->assertTrue($model->readyForAuditing());
    }

    /**
     * @group Auditable::setAuditEvent
     * @group Auditable::toAudit
     * @test
     */
    public function itFailsWhenAnInvalidAuditEventIsSet()
    {
        $this->expectException(AuditingException::class);
        $this->expectExceptionMessage('A valid audit event has not been set');

        $model = new Article();

        $model->setAuditEvent('published');

        $model->toAudit();
    }

    /**
     * @group Auditable::setAuditEvent
     * @group Auditable::toAudit
     * @test
     *
     * @dataProvider auditCustomAttributeGetterFailTestProvider
     *
     * @param string $event
     * @param array $auditEvents
     * @param string $exceptionMessage
     */
    public function itFailsWhenTheCustomAttributeGettersAreMissing(
        string $event,
        array $auditEvents,
        string $exceptionMessage
    ) {
        $this->expectException(AuditingException::class);
        $this->expectExceptionMessage($exceptionMessage);

        $model = new Article();

        $model->auditEvents = $auditEvents;

        $model->setAuditEvent($event);

        $model->toAudit();
    }

    /**
     * @return array
     */
    public function auditCustomAttributeGetterFailTestProvider(): array
    {
        return [
            [
                'published',
                [
                    'published' => 'getPublishedEventAttributes',
                ],
                'Unable to handle "published" event, getPublishedEventAttributes() method missing',
            ],
            [
                'archived',
                [
                    'archived',
                ],
                'Unable to handle "archived" event, getArchivedEventAttributes() method missing',
            ],
            [
                'redacted',
                [
                    '*ed',
                ],
                'Unable to handle "redacted" event, getRedactedEventAttributes() method missing',
            ],
            [
                'redacted',
                [
                    '*ed' => 'getMultiEventAttributes',
                ],
                'Unable to handle "redacted" event, getMultiEventAttributes() method missing',
            ],
        ];
    }

    /**
     * @group Auditable::setAuditEvent
     * @group Auditable::toAudit
     * @test
     */
    public function itFailsWhenTheIpAddressResolverImplementationIsInvalid()
    {
        $this->expectException(AuditingException::class);
        $this->expectExceptionMessage('Invalid Resolver implementation for: ip_address');

        $this->app['config']->set('audit.resolvers.ip_address', Audit::class);

        $model = new Article();

        $model->setAuditEvent('created');

        $model->toAudit();
    }

    /**
     * @group Auditable::setAuditEvent
     * @group Auditable::toAudit
     * @test
     */
    public function itFailsWhenTheUrlResolverImplementationIsInvalid()
    {
        $this->expectException(AuditingException::class);
        $this->expectExceptionMessage('Invalid Resolver implementation for: url');

        $this->app['config']->set('audit.resolvers.url', Audit::class);

        $model = new Article();

        $model->setAuditEvent('created');

        $model->toAudit();
    }

    /**
     * @group Auditable::setAuditEvent
     * @group Auditable::toAudit
     * @test
     */
    public function itFailsWhenTheUserAgentResolverImplementationIsInvalid()
    {
        $this->expectException(AuditingException::class);
        $this->expectExceptionMessage('Invalid Resolver implementation for: user_agent');

        $this->app['config']->set('audit.resolvers.user_agent', Audit::class);

        $model = new Article();

        $model->setAuditEvent('created');

        $model->toAudit();
    }

    /**
     * @group Auditable::setAuditEvent
     * @group Auditable::toAudit
     * @test
     */
    public function itFailsWhenTheUserResolverImplementationIsInvalid()
    {
        $this->expectException(AuditingException::class);
        $this->expectExceptionMessage('Invalid UserResolver implementation');

        $this->app['config']->set('audit.user.resolver', null);

        $model = new Article();

        $model->setAuditEvent('created');

        $model->toAudit();
    }

    /**
     * @group Auditable::setAuditEvent
     * @group Auditable::toAudit
     * @test
     */
    public function itReturnsTheAuditData()
    {
        $now = Carbon::now();

        $model = factory(Article::class)->make([
            'title'        => 'How To Audit Eloquent Models',
            'content'      => 'First step: install the laravel-auditing package.',
            'reviewed'     => 1,
            'published_at' => $now,
        ]);

        $model->setAuditEvent('created');

        $this->assertCount(11, $auditData = $model->toAudit());

        self::Assert()::assertArraySubset([
            'old_values'     => [],
            'new_values'     => [
                'title'        => 'How To Audit Eloquent Models',
                'content'      => 'First step: install the laravel-auditing package.',
                'reviewed'     => 1,
                'published_at' => $now->toDateTimeString(),
            ],
            'event'          => 'created',
            'auditable_id'   => null,
            'auditable_type' => Article::class,
            'user_id'        => null,
            'user_type'      => null,
            'url'            => 'console',
            'ip_address'     => '127.0.0.1',
            'user_agent'     => 'Symfony',
            'tags'           => null,
        ], $auditData, true);
    }

    /**
     * @group Auditable::setAuditEvent
     * @group Auditable::toAudit
     * @test
     *
     * @dataProvider userResolverProvider
     *
     * @param string $guard
     * @param string $driver
     * @param int $id
     * @param string $type
     */
    public function itReturnsTheAuditDataIncludingUserAttributes(
        string $guard,
        string $driver,
        int $id = null,
        string $type = null
    ) {
        $this->app['config']->set('audit.user.guards', [
            $guard,
        ]);

        $user = factory(User::class)->create();

        $this->actingAs($user, $driver);

        $now = Carbon::now();

        $model = factory(Article::class)->make([
            'title'        => 'How To Audit Eloquent Models',
            'content'      => 'First step: install the laravel-auditing package.',
            'reviewed'     => 1,
            'published_at' => $now,
        ]);

        $model->setAuditEvent('created');

        $this->assertCount(11, $auditData = $model->toAudit());

        self::Assert()::assertArraySubset([
            'old_values'     => [],
            'new_values'     => [
                'title'        => 'How To Audit Eloquent Models',
                'content'      => 'First step: install the laravel-auditing package.',
                'reviewed'     => 1,
                'published_at' => $now->toDateTimeString(),
            ],
            'event'          => 'created',
            'auditable_id'   => null,
            'auditable_type' => Article::class,
            'user_id'        => $id,
            'user_type'      => $type,
            'url'            => 'console',
            'ip_address'     => '127.0.0.1',
            'user_agent'     => 'Symfony',
            'tags'           => null,
        ], $auditData, true);
    }

    /**
     * @return array
     */
    public function userResolverProvider(): array
    {
        return [
            [
                'api',
                'web',
                null,
                null,
            ],
            [
                'web',
                'api',
                null,
                null,
            ],
            [
                'api',
                'api',
                1,
                User::class,
            ],
            [
                'web',
                'web',
                1,
                User::class,
            ],
        ];
    }

    /**
     * @group Auditable::setAuditEvent
     * @group Auditable::toAudit
     * @test
     */
    public function itExcludesAttributesFromTheAuditDataWhenInStrictMode()
    {
        $this->app['config']->set('audit.strict', true);

        $model = factory(Article::class)->make([
            'title'        => 'How To Audit Eloquent Models',
            'content'      => 'First step: install the laravel-auditing package.',
            'reviewed'     => 1,
            'published_at' => Carbon::now(),
        ]);

        $model->setHidden([
            'reviewed',
        ]);

        $model->setVisible([
            'title',
            'content',
        ]);

        $model->setAuditEvent('created');

        $this->assertCount(11, $auditData = $model->toAudit());

        self::Assert()::assertArraySubset([
            'old_values'     => [],
            'new_values'     => [
                'title'   => 'How To Audit Eloquent Models',
                'content' => 'First step: install the laravel-auditing package.',
            ],
            'event'          => 'created',
            'auditable_id'   => null,
            'auditable_type' => Article::class,
            'user_id'        => null,
            'user_type'      => null,
            'url'            => 'console',
            'ip_address'     => '127.0.0.1',
            'user_agent'     => 'Symfony',
            'tags'           => null,
        ], $auditData, true);
    }

    /**
     * @group Auditable::setAuditEvent
     * @group Auditable::toAudit
     * @test
     */
    public function itFailsWhenTheAttributeModifierImplementationIsInvalid()
    {
        $this->expectException(AuditingException::class);
        $this->expectExceptionMessage('Invalid AttributeModifier implementation: invalidAttributeRedactorOrEncoder');

        $model = factory(Article::class)->make();

        $model->attributeModifiers = [
            'title' => 'invalidAttributeRedactorOrEncoder',
        ];

        $model->setAuditEvent('created');

        $model->toAudit();
    }

    /**
     * @group Auditable::setAuditEvent
     * @group Auditable::toAudit
     * @test
     */
    public function itModifiesTheAuditAttributesSuccessfully()
    {
        $model = factory(Article::class)->make([
            'title'        => 'How To Audit Models',
            'content'      => 'N/A',
            'reviewed'     => 0,
            'published_at' => null,
        ]);

        $now = Carbon::now();

        $model->syncOriginal();

        $model->title = 'How To Audit Eloquent Models';
        $model->content = 'First step: install the laravel-auditing package.';
        $model->reviewed = 1;
        $model->published_at = $now;

        $model->setAuditEvent('updated');

        $model->attributeModifiers = [
            'title'    => RightRedactor::class,
            'content'  => LeftRedactor::class,
            'reviewed' => Base64Encoder::class,
        ];

        self::Assert()::assertArraySubset([
            'old_values' => [
                'title'        => 'Ho#################',
                'content'      => '##A',
                'published_at' => null,
                'reviewed'     => 'MA==',
            ],
            'new_values' => [
                'title'        => 'How#########################',
                'content'      => '############################################kage.',
                'published_at' => $now->toDateTimeString(),
                'reviewed'     => 'MQ==',
            ],
        ], $model->toAudit(), true);
    }

    /**
     * @group Auditable::setAuditEvent
     * @group Auditable::transformAudit
     * @group Auditable::toAudit
     * @test
     */
    public function itTransformsTheAuditData()
    {
        $model = new class () extends Article {
            protected $attributes = [
                'title'        => 'How To Audit Eloquent Models',
                'content'      => 'First step: install the laravel-auditing package.',
                'reviewed'     => 1,
                'published_at' => '2012-06-14 15:03:00',
            ];

            public function transformAudit(array $data): array
            {
                $data['new_values']['slug'] = Str::slug($data['new_values']['title']);

                return $data;
            }
        };

        $model->setAuditEvent('created');

        $this->assertCount(11, $auditData = $model->toAudit());

        self::Assert()::assertArraySubset([
            'new_values' => [
                'title'        => 'How To Audit Eloquent Models',
                'content'      => 'First step: install the laravel-auditing package.',
                'reviewed'     => 1,
                'published_at' => '2012-06-14 15:03:00',
                'slug'         => 'how-to-audit-eloquent-models',
            ],
        ], $auditData, true);
    }

    /**
     * @group Auditable::getAuditInclude
     * @test
     */
    public function itReturnsTheDefaultAttributesToBeIncludedInTheAudit()
    {
        $model = new Article();

        self::Assert()::assertArraySubset([], $model->getAuditInclude(), true);
    }

    /**
     * @group Auditable::getAuditInclude
     * @test
     */
    public function itReturnsTheCustomAttributesToBeIncludedInTheAudit()
    {
        $model = new Article();

        $model->auditInclude = [
            'title',
            'content',
        ];

        self::Assert()::assertArraySubset([
            'title',
            'content',
        ], $model->getAuditInclude(), true);
    }

    /**
     * @group Auditable::getAuditExclude
     * @test
     */
    public function itReturnsTheDefaultAttributesToBeExcludedFromTheAudit()
    {
        $model = new Article();

        self::Assert()::assertArraySubset([], $model->getAuditExclude(), true);
    }

    /**
     * @group Auditable::getAuditExclude
     * @test
     */
    public function itReturnsTheCustomAttributesToBeExcludedFromTheAudit()
    {
        $model = new Article();

        $model->auditExclude = [
            'published_at',
        ];

        self::Assert()::assertArraySubset([
            'published_at',
        ], $model->getAuditExclude(), true);
    }

    /**
     * @test
     * @return void
     */
    public function itExcludesAttributesFromExclude()
    {
        $model = new ArticleExcludes();

        $model->title = 'Darth Vader announces new paternity leave option for stormtroopers';
        $model->content = 'Storm troopers has for a long time wanted a more flexible schedule... ';
        $model->reviewed = 1;
        $model->save();

        /** @var Audit $audit */
        $audit = Audit::all()->first();
        $this->assertArrayNotHasKey('title', $audit->getModified());
    }

    /**
     * @group Auditable::getAuditStrict
     * @test
     */
    public function itReturnsTheDefaultAuditStrictValue()
    {
        $model = new Article();

        $this->assertFalse($model->getAuditStrict());
    }

    /**
     * @group Auditable::getAuditStrict
     * @test
     */
    public function itReturnsTheCustomAuditStrictValueFromAttribute()
    {
        $model = new Article();

        $model->auditStrict = true;

        $this->assertTrue($model->getAuditStrict());
    }

    /**
     * @group Auditable::getAuditStrict
     * @test
     */
    public function itReturnsTheCustomAuditStrictValueFromConfig()
    {
        $this->app['config']->set('audit.strict', true);

        $model = new Article();

        $this->assertTrue($model->getAuditStrict());
    }

    /**
     * @group Auditable::getAuditTimestamps
     * @test
     */
    public function itReturnsTheDefaultAuditTimestampsValue()
    {
        $model = new Article();

        $this->assertFalse($model->getAuditTimestamps());
    }

    /**
     * @group Auditable::getAuditTimestamps
     * @test
     */
    public function itReturnsTheCustomAuditTimestampsValueFromAttribute()
    {
        $model = new Article();

        $model->auditTimestamps = true;

        $this->assertTrue($model->getAuditTimestamps());
    }

    /**
     * @group Auditable::getAuditTimestamps
     * @test
     */
    public function itReturnsTheCustomAuditTimestampsValueFromConfig()
    {
        $this->app['config']->set('audit.timestamps', true);

        $model = new Article();

        $this->assertTrue($model->getAuditTimestamps());
    }

    /**
     * @group Auditable::getAuditDriver
     * @test
     */
    public function itReturnsTheDefaultAuditDriverValue()
    {
        $model = new Article();

        $this->assertSame('database', $model->getAuditDriver());
    }

    /**
     * @group Auditable::getAuditDriver
     * @test
     */
    public function itReturnsTheCustomAuditDriverValueFromAttribute()
    {
        $model = new Article();

        $model->auditDriver = 'RedisDriver';

        $this->assertSame('RedisDriver', $model->getAuditDriver());
    }

    /**
     * @group Auditable::getAuditDriver
     * @test
     */
    public function itReturnsTheCustomAuditDriverValueFromConfig()
    {
        $this->app['config']->set('audit.driver', 'RedisDriver');

        $model = new Article();

        $this->assertSame('RedisDriver', $model->getAuditDriver());
    }

    /**
     * @group Auditable::getAuditThreshold
     * @test
     */
    public function itReturnsTheDefaultAuditThresholdValue()
    {
        $model = new Article();

        $this->assertSame(0, $model->getAuditThreshold());
    }

    /**
     * @group Auditable::getAuditThreshold
     * @test
     */
    public function itReturnsTheCustomAuditThresholdValueFromAttribute()
    {
        $model = new Article();

        $model->auditThreshold = 10;

        $this->assertSame(10, $model->getAuditThreshold());
    }

    /**
     * @group Auditable::getAuditThreshold
     * @test
     */
    public function itReturnsTheCustomAuditThresholdValueFromConfig()
    {
        $this->app['config']->set('audit.threshold', 200);

        $model = new Article();

        $this->assertSame(200, $model->getAuditThreshold());
    }

    /**
     * @group Auditable::generateTags
     * @test
     */
    public function itReturnsTheDefaultGeneratedAuditTags()
    {
        $model = new Article();

        self::Assert()::assertArraySubset([], $model->generateTags(), true);
    }

    /**
     * @group Auditable::generateTags
     * @test
     */
    public function itReturnsTheCustomGeneratedAuditTags()
    {
        $model = new class () extends Article {
            public function generateTags(): array
            {
                return [
                    'foo',
                    'bar',
                ];
            }
        };

        self::Assert()::assertArraySubset([
            'foo',
            'bar',
        ], $model->generateTags(), true);
    }

    /**
     * @group Auditable::transitionTo
     * @test
     */
    public function itFailsToTransitionWhenTheAuditAuditableTypeDoesNotMatchTheModelType()
    {
        $this->expectException(AuditableTransitionException::class);
        $this->expectExceptionMessage('Expected Auditable type OwenIt\Auditing\Tests\Models\Article, got OwenIt\Auditing\Tests\Models\User instead');

        $audit = factory(Audit::class)->make([
            'auditable_type' => User::class,
        ]);

        $model = new Article();

        $model->transitionTo($audit);
    }

    /**
     * @group Auditable::transitionTo
     * @test
     */
    public function itFailsToTransitionWhenTheAuditAuditableTypeDoesNotMatchTheMorphMapValue()
    {
        $this->expectException(AuditableTransitionException::class);
        $this->expectExceptionMessage('Expected Auditable type articles, got users instead');

        Relation::morphMap([
            'articles' => Article::class,
        ]);

        $audit = factory(Audit::class)->make([
            'auditable_type' => 'users',
        ]);

        $model = new Article();

        $model->transitionTo($audit);
    }

    /**
     * @group Auditable::transitionTo
     * @test
     */
    public function itFailsToTransitionWhenTheAuditAuditableIdDoesNotMatchTheModelId()
    {
        $this->expectException(AuditableTransitionException::class);
        $this->expectExceptionMessage('Expected Auditable id 2, got 1 instead');

        $firstAudit = factory(Article::class)->create()->audits()->first();
        $secondModel = factory(Article::class)->create();

        $secondModel->transitionTo($firstAudit);
    }

    /**
     * @group Auditable::transitionTo
     * @test
     */
    public function itFailsToTransitionWhenTheAuditAuditableIdTypeDoesNotMatchTheModelIdType()
    {
        $this->expectException(AuditableTransitionException::class);
        $this->expectExceptionMessage('Expected Auditable id 1, got 1 instead');

        $model = factory(Article::class)->create();

        $audit = factory(Audit::class)->create([
            'auditable_type' => Article::class,
            'auditable_id'   => (string)$model->id,
        ]);

        // Make sure the auditable_id isn't being cast
        $auditReflection = new ReflectionClass($audit);

        $auditCastsProperty = $auditReflection->getProperty('casts');
        $auditCastsProperty->setAccessible(true);
        $auditCastsProperty->setValue($audit, [
            'old_values' => 'json',
            'new_values' => 'json',
        ]);

        $model->transitionTo($audit);
    }

    /**
     * @group Auditable::transitionTo
     * @test
     */
    public function itTransitionsWhenTheAuditAuditableIdTypeDoesNotMatchTheModelIdType()
    {
        $model = factory(Article::class)->create();

        // Key depends on type
        if ($model->getKeyType() == 'string') {
            $key = (string)$model->id;
        } else {
            $key = (int)$model->id;
        }

        $audit = factory(Audit::class)->create([
            'auditable_type' => Article::class,
            'auditable_id'   => $key,
        ]);

        $this->assertInstanceOf(Auditable::class, $model->transitionTo($audit));
    }

    /**
     * @group Auditable::transitionTo
     * @test
     */
    public function itFailsToTransitionWhenAnAttributeRedactorIsSet()
    {
        $this->expectException(AuditableTransitionException::class);
        $this->expectExceptionMessage('Cannot transition states when an AttributeRedactor is set');

        $model = factory(Article::class)->create();

        $model->attributeModifiers = [
            'title' => RightRedactor::class,
        ];

        $audit = factory(Audit::class)->create([
            'auditable_id'   => $model->getKey(),
            'auditable_type' => Article::class,
        ]);

        $model->transitionTo($audit);
    }

    /**
     * @group Auditable::transitionTo
     * @test
     */
    public function itFailsToTransitionWhenTheAuditableAttributeCompatibilityIsNotMet()
    {
        $model = factory(Article::class)->create();

        $incompatibleAudit = factory(Audit::class)->create([
            'event'          => 'created',
            'auditable_id'   => $model->getKey(),
            'auditable_type' => Article::class,
            'old_values'     => [],
            'new_values'     => [
                'subject' => 'Culpa qui rerum excepturi quisquam quia officiis.',
                'text'    => 'Magnam enim suscipit officiis tempore ut quis harum.',
            ],
        ]);

        try {
            $model->transitionTo($incompatibleAudit);
        } catch (AuditableTransitionException $e) {
            $this->assertSame(
                'Incompatibility between [OwenIt\Auditing\Tests\Models\Article:1] and [OwenIt\Auditing\Models\Audit:3]',
                $e->getMessage()
            );

            self::Assert()::assertArraySubset([
                'subject',
                'text',
            ], $e->getIncompatibilities(), true);
        }
    }

    /**
     * @group Auditable::transitionTo
     * @test
     *
     * @dataProvider auditableTransitionTestProvider
     *
     * @param bool $morphMap
     * @param array $oldValues
     * @param array $newValues
     * @param array $oldExpectation
     * @param array $newExpectation
     */
    public function itTransitionsToAnotherModelState(
        bool $morphMap,
        array $oldValues,
        array $newValues,
        array $oldExpectation,
        array $newExpectation
    ) {
        $models = factory(Article::class, 2)->create([
            'title'   => 'Facilis voluptas qui impedit deserunt vitae quidem.',
            'content' => 'Consectetur distinctio nihil eveniet cum. Expedita dolores animi dolorum eos repellat rerum.',
        ]);

        if ($morphMap) {
            Relation::morphMap([
                'articles' => Article::class,
            ]);
        }

        $auditableType = $morphMap ? 'articles' : Article::class;

        $audits = $models->map(function (Article $model) use ($auditableType, $oldValues, $newValues) {
            return factory(Audit::class)->create([
                'auditable_id'   => $model->getKey(),
                'auditable_type' => $auditableType,
                'old_values'     => $oldValues,
                'new_values'     => $newValues,
            ]);
        });

        // Transition with old values
        $this->assertInstanceOf(Auditable::class, $models[0]->transitionTo($audits[0], true));
        $this->assertSame($oldExpectation, $models[0]->getDirty());

        // Transition with new values
        $this->assertInstanceOf(Auditable::class, $models[1]->transitionTo($audits[1]));
        $this->assertSame($newExpectation, $models[1]->getDirty());
    }

    /**
     * @test
     */
    public function itWorksWithStringKeyModels()
    {
        $model = factory(ApiModel::class)->create();
        $model->save();
        $model->refresh();

        $this->assertCount(1, $model->audits);

        $model->content = 'Something else';
        $model->save();
        $model->refresh();

        $this->assertCount(2, $model->audits);
    }

    /**
     * @return array
     */
    public function auditableTransitionTestProvider(): array
    {
        return [
            //
            // Audit data and expectations for retrieved event
            //
            [
                // Morph Map
                false,

                // Old values
                [],

                // New values
                [],

                // Expectation when transitioning with old values
                [],

                // Expectation when transitioning with new values
                [],
            ],

            //
            // Audit data and expectations for created/restored event
            //
            [
                // Morph Map
                true,

                // Old values
                [],

                // New values
                [
                    'title'   => 'Nullam egestas interdum eleifend.',
                    'content' => 'Morbi consectetur laoreet sem, eu tempus odio tempor id.',
                ],

                // Expectation when transitioning with old values
                [],

                // Expectation when transitioning with new values
                [
                    'title'   => 'NULLAM EGESTAS INTERDUM ELEIFEND.',
                    'content' => 'Morbi consectetur laoreet sem, eu tempus odio tempor id.',
                ],
            ],

            //
            // Audit data and expectations for updated event
            //
            [
                // Morph Map
                false,

                // Old values
                [
                    'title'   => 'Vivamus a urna et lorem faucibus malesuada nec nec magna.',
                    'content' => 'Mauris ipsum erat, semper non quam vel, sodales tincidunt ligula.',
                ],

                // New values
                [
                    'title'   => 'Nullam egestas interdum eleifend.',
                    'content' => 'Morbi consectetur laoreet sem, eu tempus odio tempor id.',
                ],

                // Expectation when transitioning with old values
                [
                    'title'   => 'VIVAMUS A URNA ET LOREM FAUCIBUS MALESUADA NEC NEC MAGNA.',
                    'content' => 'Mauris ipsum erat, semper non quam vel, sodales tincidunt ligula.',
                ],

                // Expectation when transitioning with new values
                [
                    'title'   => 'NULLAM EGESTAS INTERDUM ELEIFEND.',
                    'content' => 'Morbi consectetur laoreet sem, eu tempus odio tempor id.',
                ],
            ],

            //
            // Audit data and expectations for deleted event
            //
            [
                // Morph Map
                true,

                // Old values
                [
                    'title'   => 'Vivamus a urna et lorem faucibus malesuada nec nec magna.',
                    'content' => 'Mauris ipsum erat, semper non quam vel, sodales tincidunt ligula.',
                ],

                // New values
                [],

                // Expectation when transitioning with old values
                [
                    'title'   => 'VIVAMUS A URNA ET LOREM FAUCIBUS MALESUADA NEC NEC MAGNA.',
                    'content' => 'Mauris ipsum erat, semper non quam vel, sodales tincidunt ligula.',
                ],

                // Expectation when transitioning with new values
                [],
            ],
        ];
    }
}


text:
package org.apache.ignite.internal.binary;

import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.ignite.IgniteException;
import org.apache.ignite.binary.BinaryObject;
import org.apache.ignite.binary.BinaryObjectBuilder;
import org.apache.ignite.binary.BinaryObjectException;
import org.apache.ignite.binary.BinaryType;
import org.apache.ignite.internal.binary.builder.BinaryObjectBuilderImpl;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.SB;
import org.apache.ignite.lang.IgniteUuid;
import org.apache.ignite.thread.IgniteThread;
import org.jetbrains.annotations.Nullable;

/**
 * Internal binary object interface.
 */
public abstract class BinaryObjectExImpl implements BinaryObjectEx {
    /**
     * @return Length.
     */
    public abstract int length();

    /**
     * @return Object start.
     */
    public abstract int start();

    /**
     * @return {@code True} if object is array based.
     */
    public abstract boolean hasArray();

    /**
     * @return Object array if object is array based, otherwise {@code null}.
     */
    public abstract byte[] array();

    /**
     * @return Object offheap address is object is offheap based, otherwise 0.
     */
    public abstract long offheapAddress();

    /**
     * Gets field value.
     *
     * @param fieldId Field ID.
     * @return Field value.
     * @throws org.apache.ignite.binary.BinaryObjectException In case of any other error.
     */
    @Nullable public abstract <F> F field(int fieldId) throws BinaryObjectException;

    /** {@inheritDoc} */
    @Override public int enumOrdinal() throws BinaryObjectException {
        throw new BinaryObjectException("Object is not enum.");
    }

    /** {@inheritDoc} */
    @Override public String enumName() throws BinaryObjectException {
        throw new BinaryObjectException("Object is not enum.");
    }

    /**
     * Get offset of data begin.
     *
     * @return Field value.
     */
    public abstract int dataStartOffset();

    /**
     * Get offset of the footer begin.
     *
     * @return Field value.
     */
    public abstract int footerStartOffset();

    /**
     * Get field by offset.
     *
     * @param order Field offset.
     * @return Field value.
     */
    @Nullable public abstract <F> F fieldByOrder(int order);

    /**
     * Create field comparator.
     *
     * @return Comparator.
     */
    public abstract BinarySerializedFieldComparator createFieldComparator();

    /**
     * Writes field value defined by the given field offset to the given byte buffer.
     *
     * @param fieldOffset Field offset.
     * @return Boolean flag indicating whether the field was successfully written to the buffer, {@code false}
     *      if there is no enough space for the field in the buffer.
     */
    protected abstract boolean writeFieldByOrder(int fieldOffset, ByteBuffer buf);

    /**
     * @param ctx Reader context.
     * @param fieldName Field name.
     * @return Field value.
     */
    @Nullable protected abstract <F> F field(BinaryReaderHandles ctx, String fieldName);

    /**
     * @return {@code True} if object has schema.
     */
    public abstract boolean hasSchema();

    /**
     * Get schema ID.
     *
     * @return Schema ID.
     */
    public abstract int schemaId();

    /**
     * Create schema for object.
     *
     * @return Schema.
     */
    public abstract BinarySchema createSchema();

    /**
     * Get binary context.
     *
     * @return Binary context.
     */
    public abstract BinaryContext context();

    /** {@inheritDoc} */
    @Override public BinaryObjectBuilder toBuilder() throws BinaryObjectException {
        return BinaryObjectBuilderImpl.wrap(this);
    }

    /** {@inheritDoc} */
    @Override public BinaryObject clone() throws CloneNotSupportedException {
        return (BinaryObject)super.clone();
    }

    /** {@inheritDoc} */
    @Override public boolean equals(Object other) {
        if (other == this)
            return true;

        if (!(other instanceof BinaryObject))
            return false;

        BinaryIdentityResolver identity = context().identity(typeId());

        return identity.equals(this, (BinaryObject)other);
    }

    /** {@inheritDoc} */
    @Override public String toString() {
        try {
            BinaryReaderHandles ctx = new BinaryReaderHandles();

            ctx.put(start(), this);

            return toString(ctx, new IdentityHashMap<BinaryObject, Integer>());
        }
        catch (BinaryObjectException e) {
            throw new IgniteException("Failed to create string representation of binary object.", e);
        }
    }

    /**
     * @param ctx Reader context.
     * @param handles Handles for already traversed objects.
     * @return String representation.
     */
    private String toString(BinaryReaderHandles ctx, IdentityHashMap<BinaryObject, Integer> handles) {
        int idHash = System.identityHashCode(this);
        int hash = hashCode();

        BinaryType meta;

        IgniteThread.onForbidBinaryMetadataRequestSectionEntered();

        try {
            meta = rawType();
        }
        catch (BinaryObjectException ignore) {
            meta = null;
        }
        finally {
            IgniteThread.onForbidBinaryMetadataRequestSectionLeft();
        }

        if (meta == null || !S.includeSensitive())
            return S.toString(S.includeSensitive() ? BinaryObject.class.getSimpleName() : "BinaryObject",
                "idHash", idHash, false,
                "hash", hash, false,
                "typeId", typeId(), true);

        handles.put(this, idHash);

        SB buf = new SB(meta.typeName());

        if (meta.fieldNames() != null) {
            buf.a(" [idHash=").a(idHash).a(", hash=").a(hash);

            for (String name : meta.fieldNames()) {
                Object val = field(ctx, name);

                buf.a(", ").a(name).a('=');

                appendValue(val, buf, ctx, handles);
            }

            buf.a(']');
        }

        return buf.toString();
    }

    /**
     * @param val Value to append.
     * @param buf Buffer to append to.
     * @param ctx Reader context.
     * @param handles Handles for already traversed objects.
     */
    private void appendValue(Object val, SB buf, BinaryReaderHandles ctx,
        IdentityHashMap<BinaryObject, Integer> handles) {
        if (val instanceof byte[])
            buf.a(Arrays.toString((byte[])val));
        else if (val instanceof short[])
            buf.a(Arrays.toString((short[])val));
        else if (val instanceof int[])
            buf.a(Arrays.toString((int[])val));
        else if (val instanceof long[])
            buf.a(Arrays.toString((long[])val));
        else if (val instanceof float[])
            buf.a(Arrays.toString((float[])val));
        else if (val instanceof double[])
            buf.a(Arrays.toString((double[])val));
        else if (val instanceof char[])
            buf.a(Arrays.toString((char[])val));
        else if (val instanceof boolean[])
            buf.a(Arrays.toString((boolean[])val));
        else if (val instanceof BigDecimal[])
            buf.a(Arrays.toString((BigDecimal[])val));
        else if (val instanceof IgniteUuid)
            buf.a(val);
        else if (val instanceof BinaryObjectExImpl) {
            BinaryObjectExImpl po = (BinaryObjectExImpl)val;

            Integer idHash0 = handles.get(val);

            if (idHash0 != null) {  // Circular reference.
                BinaryType meta0 = po.rawType();

                assert meta0 != null;

                buf.a(meta0.typeName()).a(" [hash=").a(idHash0).a(", ...]");
            }
            else
                buf.a(po.toString(ctx, handles));
        }
        else if (val instanceof Object[]) {
            Object[] arr = (Object[])val;

            buf.a('[');

            for (int i = 0; i < arr.length; i++) {
                Object o = arr[i];

                appendValue(o, buf, ctx, handles);

                if (i < arr.length - 1)
                    buf.a(", ");
            }
        }
        else if (val instanceof Iterable) {
            Iterable<Object> col = (Iterable<Object>)val;

            buf.a(col.getClass().getSimpleName()).a(" {");

            Iterator it = col.iterator();

            while (it.hasNext()) {
                Object o = it.next();

                appendValue(o, buf, ctx, handles);

                if (it.hasNext())
                    buf.a(", ");
            }

            buf.a('}');
        }
        else if (val instanceof Map) {
            Map<Object, Object> map = (Map<Object, Object>)val;

            buf.a(map.getClass().getSimpleName()).a(" {");

            Iterator<Map.Entry<Object, Object>> it = map.entrySet().iterator();

            while (it.hasNext()) {
                Map.Entry<Object, Object> e = it.next();

                appendValue(e.getKey(), buf, ctx, handles);

                buf.a('=');

                appendValue(e.getValue(), buf, ctx, handles);

                if (it.hasNext())
                    buf.a(", ");
            }

            buf.a('}');
        }
        else
            buf.a(val);
    }

    /**
     * Check if object graph has circular references.
     *
     * @return {@code true} if object has circular references.
     */
    public boolean hasCircularReferences() {
        try {
            BinaryReaderHandles ctx = new BinaryReaderHandles();

            ctx.put(start(), this);

            return hasCircularReferences(ctx, new IdentityHashMap<BinaryObject, Integer>());
        }
        catch (BinaryObjectException e) {
            throw new IgniteException("Failed to check binary object for circular references", e);
        }
    }

    /**
     * @param ctx Reader context.
     * @param handles Handles for already traversed objects.
     * @return {@code true} if has circular reference.
     */
    private boolean hasCircularReferences(BinaryReaderHandles ctx, IdentityHashMap<BinaryObject, Integer> handles) {
        BinaryType meta;

        try {
            meta = rawType();
        }
        catch (BinaryObjectException ignore) {
            meta = null;
        }

        if (meta == null)
            return false;

        int idHash = System.identityHashCode(this);

        handles.put(this, idHash);

        if (meta.fieldNames() != null) {
            ctx.put(start(), this);

            for (String name : meta.fieldNames()) {
                Object val = field(ctx, name);

                if (val instanceof BinaryObjectExImpl) {
                    BinaryObjectExImpl po = (BinaryObjectExImpl)val;

                    Integer idHash0 = handles.get(val);

                    // Check for circular reference.
                    if (idHash0 != null || po.hasCircularReferences(ctx, handles))
                        return true;
                }
            }
        }

        return false;
    }
}


text:
<!DOCTYPE html>
<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">

There should be 6 identical cyan rectangles below, all with a box shadow.


text:
namespace tik4net.entitygenerator
{
    partial class EntityCodeGenaratorMainForm
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            System.Windows.Forms.Label label1;
            System.Windows.Forms.Label label2;
            this.btnGenerate = new System.Windows.Forms.Button();
            this.tbPath = new System.Windows.Forms.TextBox();
            this.tbSourceCode = new System.Windows.Forms.TextBox();
            this.cbIncludeDetails = new System.Windows.Forms.CheckBox();
            this.tbHost = new System.Windows.Forms.TextBox();
            this.tbUser = new System.Windows.Forms.TextBox();
            this.tbPass = new System.Windows.Forms.TextBox();
            this.cbExecuteAsync = new System.Windows.Forms.CheckBox();
            this.tbParameters = new System.Windows.Forms.TextBox();
            this.cbTikApi = new System.Windows.Forms.ComboBox();
            this.label3 = new System.Windows.Forms.Label();
            label1 = new System.Windows.Forms.Label();
            label2 = new System.Windows.Forms.Label();
            this.SuspendLayout();
            //
            // label1
            //
            label1.AutoSize = true;
            label1.Location = new System.Drawing.Point(12, 42);
            label1.Name = "label1";
            label1.Size = new System.Drawing.Size(57, 13);
            label1.TabIndex = 3;
            label1.Text = "Entity path";
            //
            // label2
            //
            label2.AutoSize = true;
            label2.Location = new System.Drawing.Point(12, 15);
            label2.Name = "label2";
            label2.Size = new System.Drawing.Size(81, 13);
            label2.TabIndex = 8;
            label2.Text = "Host/user/pass";
            //
            // btnGenerate
            //
            this.btnGenerate.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
            this.btnGenerate.Location = new System.Drawing.Point(619, 405);
            this.btnGenerate.Name = "btnGenerate";
            this.btnGenerate.Size = new System.Drawing.Size(130, 23);
            this.btnGenerate.TabIndex = 0;
            this.btnGenerate.Text = "Load and generate";
            this.btnGenerate.UseVisualStyleBackColor = true;
            this.btnGenerate.Click += new System.EventHandler(this.btnGenerate_Click);
            //
            // tbPath
            //
            this.tbPath.Location = new System.Drawing.Point(75, 39);
            this.tbPath.Name = "tbPath";
            this.tbPath.Size = new System.Drawing.Size(157, 20);
            this.tbPath.TabIndex = 1;
            this.tbPath.Text = "/print";
            //
            // tbSourceCode
            //
            this.tbSourceCode.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
            | System.Windows.Forms.AnchorStyles.Left)
            | System.Windows.Forms.AnchorStyles.Right)));
            this.tbSourceCode.Location = new System.Drawing.Point(12, 88);
            this.tbSourceCode.Multiline = true;
            this.tbSourceCode.Name = "tbSourceCode";
            this.tbSourceCode.Size = new System.Drawing.Size(737, 311);
            this.tbSourceCode.TabIndex = 2;
            //
            // cbIncludeDetails
            //
            this.cbIncludeDetails.AutoSize = true;
            this.cbIncludeDetails.Checked = true;
            this.cbIncludeDetails.CheckState = System.Windows.Forms.CheckState.Checked;
            this.cbIncludeDetails.Location = new System.Drawing.Point(15, 65);
            this.cbIncludeDetails.Name = "cbIncludeDetails";
            this.cbIncludeDetails.Size = new System.Drawing.Size(94, 17);
            this.cbIncludeDetails.TabIndex = 4;
            this.cbIncludeDetails.Text = "Include details";
            this.cbIncludeDetails.UseVisualStyleBackColor = true;
            //
            // tbHost
            //
            this.tbHost.Location = new System.Drawing.Point(246, 13);
            this.tbHost.Name = "tbHost";
            this.tbHost.Size = new System.Drawing.Size(172, 20);
            this.tbHost.TabIndex = 5;
            //
            // tbUser
            //
            this.tbUser.Location = new System.Drawing.Point(425, 13);
            this.tbUser.Name = "tbUser";
            this.tbUser.Size = new System.Drawing.Size(179, 20);
            this.tbUser.TabIndex = 6;
            //
            // tbPass
            //
            this.tbPass.Location = new System.Drawing.Point(611, 12);
            this.tbPass.Name = "tbPass";
            this.tbPass.Size = new System.Drawing.Size(138, 20);
            this.tbPass.TabIndex = 7;
            //
            // cbExecuteAsync
            //
            this.cbExecuteAsync.AutoSize = true;
            this.cbExecuteAsync.Location = new System.Drawing.Point(136, 65);
            this.cbExecuteAsync.Name = "cbExecuteAsync";
            this.cbExecuteAsync.Size = new System.Drawing.Size(96, 17);
            this.cbExecuteAsync.TabIndex = 9;
            this.cbExecuteAsync.Text = "Execute async";
            this.cbExecuteAsync.UseVisualStyleBackColor = true;
            //
            // tbParameters
            //
            this.tbParameters.AccessibleRole = System.Windows.Forms.AccessibleRole.ScrollBar;
            this.tbParameters.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
            | System.Windows.Forms.AnchorStyles.Right)));
            this.tbParameters.Location = new System.Drawing.Point(246, 39);
            this.tbParameters.Name = "tbParameters";
            this.tbParameters.Size = new System.Drawing.Size(503, 20);
            this.tbParameters.TabIndex = 10;
            //
            // comboBox1
            //
            this.cbTikApi.FormattingEnabled = true;
            this.cbTikApi.Location = new System.Drawing.Point(276, 63);
            this.cbTikApi.Name = "comboBox1";
            this.cbTikApi.Size = new System.Drawing.Size(121, 21);
            this.cbTikApi.TabIndex = 11;
            //
            // label3
            //
            this.label3.AutoSize = true;
            this.label3.Location = new System.Drawing.Point(243, 66);
            this.label3.Name = "label3";
            this.label3.Size = new System.Drawing.Size(27, 13);
            this.label3.TabIndex = 12;
            this.label3.Text = "API:";
            //
            // EntityCodeGenaratorMainForm
            //
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(761, 440);
            this.Controls.Add(this.label3);
            this.Controls.Add(this.cbTikApi);
            this.Controls.Add(this.tbParameters);
            this.Controls.Add(this.cbExecuteAsync);
            this.Controls.Add(label2);
            this.Controls.Add(this.tbPass);
            this.Controls.Add(this.tbUser);
            this.Controls.Add(this.tbHost);
            this.Controls.Add(this.cbIncludeDetails);
            this.Controls.Add(label1);
            this.Controls.Add(this.tbSourceCode);
            this.Controls.Add(this.tbPath);
            this.Controls.Add(this.btnGenerate);
            this.Name = "EntityCodeGenaratorMainForm";
            this.Text = "Entity source code generator";
            this.Load += new System.EventHandler(this.Form1_Load);
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.Button btnGenerate;
        private System.Windows.Forms.TextBox tbPath;
        private System.Windows.Forms.TextBox tbSourceCode;
        private System.Windows.Forms.CheckBox cbIncludeDetails;
        private System.Windows.Forms.TextBox tbHost;
        private System.Windows.Forms.TextBox tbUser;
        private System.Windows.Forms.TextBox tbPass;
        private System.Windows.Forms.CheckBox cbExecuteAsync;
        private System.Windows.Forms.TextBox tbParameters;
        private System.Windows.Forms.ComboBox cbTikApi;
        private System.Windows.Forms.Label label3;
    }
}


text:
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;

namespace Infrastructure
{
    [ValueConversion(typeof(double), typeof(double))]
    public class DoubleScaleConverter : MarkupExtension, IValueConverter
    {
        #region Markup

        private static DoubleScaleConverter converter;

        public DoubleScaleConverter() { }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            if (converter == null)
                converter = new DoubleScaleConverter();

            return converter;
        }

        #endregion

        #region Converter

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            double currentValue = System.Convert.ToDouble(value);

            double scale;
            if (double.TryParse(parameter as string, NumberStyles.Any, CultureInfo.InvariantCulture, out scale))
                return currentValue * scale;
            else
                return DependencyProperty.UnsetValue;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            double currentValue = System.Convert.ToDouble(value);

            double scale;
            if (double.TryParse(parameter as string, NumberStyles.Any, CultureInfo.InvariantCulture, out scale) && (scale != 0.0f))
                return currentValue / scale;
            else
                return DependencyProperty.UnsetValue;
        }

        #endregion
    }
}


text:
DIR=$1

echo ""
echo "==================================================="
echo "Encrypting client side - all files in directory: ${DIR:?}"
read -p "press enter to continue"

./tinkproxy vanish ${DIR}
echo "==================================================="
echo ""
read -p "press enter to continue"

echo "==================================================="
echo "Uploading all files into bucket: ${TINKPROXY_BUCKET_NAME:?}"
read -p "press enter to continue"
# TINKPROXY encrypts files and encode it changing suffix to .enc
gsutil -m cp ${DIR}/*.enc gs://${TINKPROXY_BUCKET_NAME}
echo "==================================================="
read -p "press enter to continue"

echo ""
echo "Success"
echo ""


text:
ZUSE.WebGL.Pin = function ( node, params ) {

	var x = parseInt( node.getAttribute( 'x' ) );
	var y = parseInt( node.getAttribute( 'y' ) );
	this.l1 = parseInt( node.getAttribute( 'l1' ) );
	this.l2 = parseInt( node.getAttribute( 'l2' ) );

	if ( node.childElementCount > 0 ) {
		var anim = node.firstElementChild;
		var xMove = anim.getAttribute( 'x' ) === 'true';
		var yMove = anim.getAttribute( 'y' ) === 'true';
		if ( isNaN( this.l1 ) ) { this.l1 = 0; } else { this.l1 = this.l1*2; }
		if ( isNaN( this.l2 ) ) { this.l2 = 2 * params.levels; } else { this.l2 = 2*this.l2+2; }
	} else {
		if ( isNaN( this.l1 ) ) { this.l1 = 0; }
		if ( isNaN( this.l2 ) ) { this.l2 = 2 * params.levels; }
	}

	var radius = parseInt( node.getAttribute( 'radius' ) ) || 4;

	var arcShape = new THREE.Shape();
	arcShape.moveTo( x, y );

	switch ( radius ) {

		case 4:

			arcShape.moveBy( 5, 5 );
			arcShape.circle( 4 );
			break;

		case 6:

			arcShape.moveBy( 10, 10 );
			arcShape.circle( 6 );
			break;

	}

	var extrudeSettings = {	amount: 1, bevelEnabled: false, steps: 1 };
	var geometry = arcShape.extrude( extrudeSettings );

	this.material = ( xMove || yMove ) ? ZUSE.Materials.MovingPin.shader : ZUSE.Materials.StaticPin.shader;

	this.mesh = new THREE.Mesh( geometry, this.material );
	this.mesh.guardian = this;

	this.setHeight( params.spacing );

};

ZUSE.WebGL.Pin.prototype = {

	constructor: ZUSE.WebGL.Pin,

	setHeight: function ( spacing ) {

		var z1 = this.l1 * spacing;
		var z2 = this.l2 * spacing;

		this.mesh.position.z = z1;
		this.mesh.scale.z = z2 - z1;

	},

	setHighlight: function ( bool ) {

		if ( bool && ZUSE.adderObj.cycleControl.highlightMoving ) {

			this.mesh.material = ZUSE.Materials.Highlight.shader;

		} else {

			this.mesh.material = this.material;

		}

	},

	move: function ( tact, value ) {

		switch ( tact ) {

			case 1:

				this.mesh.position.y = -value;
				break;

			case 2:

				this.mesh.position.x = value;
				break;

			case 3:

				this.mesh.position.y = -10 + value;
				break;

			case 4:

				this.mesh.position.x = 10 - value;
				break;

		}

	}

};