[
  {
    "path": ".gitattributes",
    "content": "# Auto detect text files and perform LF normalization\n* text=auto\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: alyssaxuu\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019 Alyssa X\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "\n# Flowy\n\n\n![Demo](https://media.giphy.com/media/dv1C56OywrP7Cn20nr/giphy.gif)\n<br>A javascript library to create pretty flowcharts with ease ✨\n\n[Dribbble](https://dribbble.com/shots/8576286-Flowy-Flowchart-Engine) | [Twitter](https://twitter.com/alyssaxuu/status/1199724989353730048) | [Live demo](https://alyssax.com/x/flowy)\n\n\nFlowy makes creating WebApps with flowchart functionality an incredibly simple task. Build automation software, mind mapping tools, or simple programming platforms in minutes by implementing the library into your project. \n\n> You can support this project (and many others) through [GitHub Sponsors](https://github.com/sponsors/alyssaxuu)! ❤️\n\nMade by [Alyssa X](https://alyssax.com)\n\n## Table of contents\n- [Features](#features)\n- [Installation](#installation)\n- [Running Flowy](#running-flowy)\n    - [Initialization](#initialization)\n    - [Example](#example)\n- [Callbacks](#callbacks)\n\t- [On grab](#on-grab)\n\t- [On release](#on-release)\n\t- [On snap](#on-snap)\n\t- [On rearrange](#on-rearrange)\n- [Methods](#methods)\n    - [Get the flowchart data](#get-the-flowchart-data)\n    - [Import the flowchart data](#import-the-flowchart-data)\n    - [Delete all blocks](#delete-all-blocks)\n\n\n## Features\nCurrently, Flowy supports the following:\n\n - [x] Responsive drag and drop\n - [x] Automatic snapping\n - [x] Automatic scrolling\n - [x] Block rearrangement\n - [x] Delete blocks\n - [x] Automatic block centering\n - [x] Conditional snapping\n - [x] Conditional block removal\n - [x] Import saved files\n - [x] Mobile support\n - [x] Vanilla javascript (no dependencies)\n - [ ] [npm install](https://github.com/alyssaxuu/flowy/issues/10)\n \nYou can suggest new features [here](https://github.com/alyssaxuu/flowy/issues)\n \n\n## Installation\nAdding Flowy to your WebApp is incredibly simple:\n\n1.  Link  `flowy.min.js`  and  `flowy.min.css`  to your project. Through jsDelivr: \n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/gh/alyssaxuu/flowy/flowy.min.css\"> \n<script src=\"https://cdn.jsdelivr.net/gh/alyssaxuu/flowy/flowy.min.js\"></script>\n```\n2.  Create a canvas element that will contain the flowchart (for example,  `<div id=\"canvas\"></div>`)\n3.  Create the draggable blocks with the  `.create-flowy`  class (for example,  `<div class=\"create-flowy\">Grab me</div>`)\n\n## Running Flowy\n\n### Initialization\n```javascript\nflowy(canvas, ongrab, onrelease, onsnap, onrearrange, spacing_x, spacing_y);\n```\n\nParameter | Type | Description\n--- | --- | ---\n   `canvas` | *javascript DOM element* | The element that will contain the blocks \n   `ongrab` | *function* (optional) |  Function that gets triggered when a block is dragged\n   `onrelease` | *function* (optional) |  Function that gets triggered when a block is released\n   `onsnap` | *function* (optional) |  Function that gets triggered when a block snaps with another one\n   `onrearrange` | *function* (optional) |  Function that gets triggered when blocks are rearranged\n   `spacing_x` | *integer* (optional) |  Horizontal spacing between blocks (default 20px)\n   `spacing_y` | *integer* (optional) |  Vertical spacing between blocks (default 80px)\n\nTo define the blocks that can be dragged, you need to add the class `.create-flowy`\n\n### Example\n**HTML**\n```html\n<div class=\"create-flowy\">The block to be dragged</div>\n<div id=\"canvas\"></div>\n```\n**Javascript**\n```javascript\nvar spacing_x = 40;\nvar spacing_y = 100;\n// Initialize Flowy\nflowy(document.getElementById(\"canvas\"), onGrab, onRelease, onSnap, onRearrange, spacing_x, spacing_y);\nfunction onGrab(block){\n\t// When the user grabs a block\n}\nfunction onRelease(){\n\t// When the user releases a block\n}\nfunction onSnap(block, first, parent){\n\t// When a block snaps with another one\n}\nfunction onRearrange(block, parent){\n\t// When a block is rearranged\n}\n```\n## Callbacks\nIn order to use callbacks, you need to add the functions when initializing Flowy, as explained before.\n### On grab\n```javascript\nfunction onGrab(block){\n\t// When the user grabs a block\n}\n```\nGets triggered when a user grabs a block with the class `create-flowy`\n\nParameter | Type | Description\n--- | --- | ---\n   `block` | *javascript DOM element* | The block that has been grabbed\n   \n### On release\n```javascript\nfunction onRelease(){\n\t// When the user lets go of a block\n}\n```\nGets triggered when a user lets go of a block, regardless of whether it attaches or even gets released in the canvas.\n\n### On snap\n```javascript\nfunction onSnap(block, first, parent){\n\t// When a block can attach to a parent\n\treturn true;\n}\n```\nGets triggered when a block can attach to another parent block. You can either prevent the attachment, or allow it by using `return true;`\n\nParameter | Type | Description\n--- | --- | ---\n   `block` | *javascript DOM element* | The block that has been grabbed\n   `first` | *boolean* | If true, the block that has been dragged is the first one in the canvas\n   `parent` | *javascript DOM element* | The parent the block can attach to\n   \n### On rearrange\n```javascript\nfunction onRearrange(block, parent){\n\t// When a block is rearranged\n\treturn true;\n}\n```\nGets triggered when blocks are rearranged and are dropped anywhere in the canvas, without a parent to attach to. You can either allow the blocks to be deleted, or prevent it and thus have them re-attach to their previous parent using `return true;`\n\nParameter | Type | Description\n--- | --- | ---\n   `block` | *javascript DOM element* | The block that has been grabbed\n   `parent` | *javascript DOM element* | The parent the block can attach to\n   \n## Methods\n### Get the flowchart data\n```javascript\n// As an object\nflowy.output();\n// As a JSON string\nJSON.stringify(flowy.output());\n```\nThe JSON object that gets outputted looks like this:\n```json\n{\n\t\"html\": \"\",\n\t\"blockarr\": [],\n\t\"blocks\": [\n\t\t{\n\t\t\t\"id\": 1,\n\t\t\t\"parent\": 0,\n\t\t\t\"data\": [\n\t\t\t\t{\n\t\t\t\t\"name\": \"blockid\",\n\t\t\t\t\"value\": \"1\"\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"attr\": [\n\t\t\t\t{\n\t\t\t\t\"id\": \"block-id\",\n\t\t\t\t\"class\": \"block-class\"\n\t\t\t\t}\n\t\t\t]\n\t\t}\n\t]\n}\n```\n\nHere's what each property means:\n\nKey | Value type | Description\n--- | --- | ---\n   `html` | *string* | Contains the canvas data\n   `blockarr` | *array* | Contains the block array generated by the library (for import purposes)  \n   `blocks` | *array* | Contains the readable block array\n   `id` | *integer* | Unique value that identifies a block \n   `parent` | *integer* |  The `id` of the parent a block is attached to (-1 means the block has no parent)\n   `data` | *array of objects* |  An array of all the inputs within a certain block\n   `name` | *string* |  The name attribute of the input\n   `value` | *string* |  The value attribute of the input\n   `attr` | *array of objects* |  Contains all the data attributes of a certain block\n### Import the flowchart data\n```javascript\nflowy.import(output)\n```\nAllows you to import entire flowcharts initially exported using the previous method, `flowy.output()`\n\nParameter | Type | Description\n--- | --- | ---\n   `output` | *javascript DOM element* | The data from `flowy.output()`\n   \n#### Warning\n\nThis method accepts raw HTML and does **not** sanitize it, therefore this method is vulnerable to [XSS](https://owasp.org/www-community/attacks/DOM_Based_XSS). The _only_ safe use for this method is when the input is **absolutely** trusted, if the input is _not_ to be trusted the use this method can introduce a vulnerability in your system.\n\n### Delete all blocks\nTo remove all blocks at once use:\n```javascript\nflowy.deleteBlocks()\n```\nCurrently there is no method to individually remove blocks. The only way to go about it is by splitting branches manually.\n#\n Feel free to reach out to me through email at hi@alyssax.com or [on Twitter](https://twitter.com/alyssaxuu) if you have any questions or feedback! Hope you find this useful 💜\n"
  },
  {
    "path": "demo/index.html",
    "content": " <!DOCTYPE html>\n<html>\n<head>\n    <!-- Primary Meta Tags -->\n<title>Flowy - The simple flowchart engine</title>\n<meta name=\"title\" content=\"Flowy - The simple flowchart engine\">\n<meta name=\"description\" content=\"Flowy is a minimal javascript library to create flowcharts. Use it for automation software, mind mapping tools, programming platforms, and more. Made by Alyssa X.\">\n\n<!-- Open Graph / Facebook -->\n<meta property=\"og:type\" content=\"website\">\n<meta property=\"og:url\" content=\"https://alyssax.com/x/flowy\">\n<meta property=\"og:title\" content=\"Flowy - The simple flowchart engine\">\n<meta property=\"og:description\" content=\"Flowy is a minimal javascript library to create flowcharts. Use it for automation software, mind mapping tools, programming platforms, and more. Made by Alyssa X.\">\n<meta property=\"og:image\" content=\"https://alyssax.com/x/assets/meta.png\">\n\n<!-- Twitter -->\n<meta property=\"twitter:card\" content=\"summary_large_image\">\n<meta property=\"twitter:url\" content=\"https://alyssax.com/x/flowy\">\n<meta property=\"twitter:site\" content=\"alyssaxuu\">\n<meta property=\"twitter:title\" content=\"Flowy - The simple flowchart engine\">\n<meta property=\"twitter:description\" content=\"Flowy is a minimal javascript library to create flowcharts. Use it for automation software, mind mapping tools, programming platforms, and more. Made by Alyssa X.\">\n<meta property=\"twitter:image\" content=\"https://alyssax.com/x/assets/meta.png\">\n    <link href=\"https://fonts.googleapis.com/css?family=Roboto:400,500,700&display=swap\" rel=\"stylesheet\">\n    <link href='styles.css' rel='stylesheet' type='text/css'>\n    <link href='flowy.min.css' rel='stylesheet' type='text/css'>\n    <meta name=\"viewport\" content=\"width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0\">\n    <script src=\"flowy.min.js\"></script>\n    <script src=\"main.js\"></script>   \n</head>\n<body>\n    <div id=\"navigation\">\n        <div id=\"leftside\">\n            <div id=\"details\">\n            <div id=\"back\"><img src=\"assets/arrow.svg\"></div>\n            <div id=\"names\">\n                <p id=\"title\">Your automation pipeline</p>\n                <p id=\"subtitle\">Marketing automation</p>\n            </div>\n        </div>            \n        </div>\n        <div id=\"centerswitch\">\n            <div id=\"leftswitch\">Diagram view</div>\n            <div id=\"rightswitch\">Code editor</div>\n        </div>\n        <div id=\"buttonsright\">\n            <div id=\"discard\">Discard</div>\n            <div id=\"publish\">Publish to site</div>\n        </div>\n    </div>\n    <div id=\"leftcard\">\n        <div id=\"closecard\">\n            <img src=\"assets/closeleft.svg\">\n        </div>\n        <p id=\"header\">Blocks</p>\n        <div id=\"search\">\n            <img src=\"assets/search.svg\">\n            <input type=\"text\" placeholder=\"Search blocks\">\n        </div>\n        <div id=\"subnav\">\n            <div id=\"triggers\" class=\"navactive side\">Triggers</div>\n            <div id=\"actions\" class=\"navdisabled side\">Actions</div>\n            <div id=\"loggers\" class=\"navdisabled side\">Loggers</div>\n        </div>\n        <div id=\"blocklist\">\n            <div class=\"blockelem create-flowy noselect\">\n                <input type=\"hidden\" name='blockelemtype' class=\"blockelemtype\" value=\"1\">\n                <div class=\"grabme\">\n                    <img src=\"assets/grabme.svg\">\n                </div>\n                <div class=\"blockin\">\n                    <div class=\"blockico\">\n                        <span></span>\n                        <img src=\"assets/eye.svg\">\n                    </div>\n                    <div class=\"blocktext\">\n                        <p class=\"blocktitle\">New visitor</p>\n                        <p class=\"blockdesc\">Triggers when somebody visits a specified page</p>\n                    </div>\n                </div>\n            </div>\n            <div class=\"blockelem create-flowy noselect\">\n                <input type=\"hidden\" name='blockelemtype' class=\"blockelemtype\" value=\"2\">\n                <div class=\"grabme\">\n                    <img src=\"assets/grabme.svg\">\n                </div>\n                <div class=\"blockin\">\n                    <div class=\"blockico\">\n                        <span></span>\n                        <img src=\"assets/action.svg\">\n                    </div>\n                    <div class=\"blocktext\">\n                        <p class=\"blocktitle\">Action is performed</p>\n                        <p class=\"blockdesc\">Triggers when somebody performs a specified action</p>\n                    </div>\n                </div>\n            </div>\n            <div class=\"blockelem create-flowy noselect\">\n                <input type=\"hidden\" name='blockelemtype' class=\"blockelemtype\" value=\"3\">\n                <div class=\"grabme\">\n                    <img src=\"assets/grabme.svg\">\n                </div>\n                <div class=\"blockin\">\n                    <div class=\"blockico\">\n                        <span></span>\n                        <img src=\"assets/time.svg\">\n                    </div>\n                    <div class=\"blocktext\">\n                        <p class=\"blocktitle\">Time has passed</p>\n                        <p class=\"blockdesc\">Triggers after a specified amount of time</p>\n                    </div>\n                </div>\n            </div>\n            <div class=\"blockelem create-flowy noselect\">\n                <input type=\"hidden\" name='blockelemtype' class=\"blockelemtype\" value=\"4\">\n                <div class=\"grabme\">\n                    <img src=\"assets/grabme.svg\">\n                </div>\n                <div class=\"blockin\">\n                    <div class=\"blockico\">\n                        <span></span>\n                        <img src=\"assets/error.svg\">\n                    </div>\n                    <div class=\"blocktext\">\n                        <p class=\"blocktitle\">Error prompt</p>\n                        <p class=\"blockdesc\">Triggers when a specified error happens</p>\n                    </div>\n                </div>\n            </div>\n        </div>\n        <div id=\"footer\">\n            <a href=\"https://github.com/alyssaxuu/flowy/\" target=\"_blank\">GitHub</a>\n            <span>·</span>\n            <a href=\"https://twitter.com/alyssaxuu/status/1199724989353730048\" target=\"_blank\">Twitter</a>\n            <span>·</span>\n                <a href=\"https://alyssax.com\" target=\"_blank\"><p>Made with</p><img src=\"assets/heart.svg\"><p>by</p> Alyssa X</a>\n        </div>\n    </div>\n    <div id=\"propwrap\">\n        <div id=\"properties\">\n            <div id=\"close\">\n                <img src=\"assets/close.svg\">\n            </div>\n            <p id=\"header2\">Properties</p>\n            <div id=\"propswitch\">\n                <div id=\"dataprop\">Data</div>\n                <div id=\"alertprop\">Alerts</div>\n                <div id=\"logsprop\">Logs</div>\n            </div>\n            <div id=\"proplist\">\n                <p class=\"inputlabel\">Select database</p>\n                <div class=\"dropme\">Database 1 <img src=\"assets/dropdown.svg\"></div>\n                <p class=\"inputlabel\">Check properties</p>\n                <div class=\"dropme\">All<img src=\"assets/dropdown.svg\"></div>\n                <div class=\"checkus\"><img src=\"assets/checkon.svg\"><p>Log on successful performance</p></div>\n                <div class=\"checkus\"><img src=\"assets/checkoff.svg\"><p>Give priority to this block</p></div>\n            </div>\n            <div id=\"divisionthing\"></div>\n            <div id=\"removeblock\">Delete blocks</div>\n        </div>\n    </div>\n    <div id=\"canvas\">\n    </div>\n</body>\n</html>\n"
  },
  {
    "path": "demo/main.js",
    "content": "document.addEventListener(\"DOMContentLoaded\", function(){\n    var rightcard = false;\n    var tempblock;\n    var tempblock2;\n    document.getElementById(\"blocklist\").innerHTML = '<div class=\"blockelem create-flowy noselect\"><input type=\"hidden\" name=\"blockelemtype\" class=\"blockelemtype\" value=\"1\"><div class=\"grabme\"><img src=\"assets/grabme.svg\"></div><div class=\"blockin\">                  <div class=\"blockico\"><span></span><img src=\"assets/eye.svg\"></div><div class=\"blocktext\">                        <p class=\"blocktitle\">New visitor</p><p class=\"blockdesc\">Triggers when somebody visits a specified page</p>        </div></div></div><div class=\"blockelem create-flowy noselect\"><input type=\"hidden\" name=\"blockelemtype\" class=\"blockelemtype\" value=\"2\"><div class=\"grabme\"><img src=\"assets/grabme.svg\"></div><div class=\"blockin\">                    <div class=\"blockico\"><span></span><img src=\"assets/action.svg\"></div><div class=\"blocktext\">                        <p class=\"blocktitle\">Action is performed</p><p class=\"blockdesc\">Triggers when somebody performs a specified action</p></div></div></div><div class=\"blockelem create-flowy noselect\"><input type=\"hidden\" name=\"blockelemtype\" class=\"blockelemtype\" value=\"3\"><div class=\"grabme\"><img src=\"assets/grabme.svg\"></div><div class=\"blockin\">                    <div class=\"blockico\"><span></span><img src=\"assets/time.svg\"></div><div class=\"blocktext\">                        <p class=\"blocktitle\">Time has passed</p><p class=\"blockdesc\">Triggers after a specified amount of time</p>          </div></div></div><div class=\"blockelem create-flowy noselect\"><input type=\"hidden\" name=\"blockelemtype\" class=\"blockelemtype\" value=\"4\"><div class=\"grabme\"><img src=\"assets/grabme.svg\"></div><div class=\"blockin\">                    <div class=\"blockico\"><span></span><img src=\"assets/error.svg\"></div><div class=\"blocktext\">                        <p class=\"blocktitle\">Error prompt</p><p class=\"blockdesc\">Triggers when a specified error happens</p>              </div></div></div>';\n    flowy(document.getElementById(\"canvas\"), drag, release, snapping);\n    function addEventListenerMulti(type, listener, capture, selector) {\n        var nodes = document.querySelectorAll(selector);\n        for (var i = 0; i < nodes.length; i++) {\n            nodes[i].addEventListener(type, listener, capture);\n        }\n    }\n    function snapping(drag, first) {\n        var grab = drag.querySelector(\".grabme\");\n        grab.parentNode.removeChild(grab);\n        var blockin = drag.querySelector(\".blockin\");\n        blockin.parentNode.removeChild(blockin);\n        if (drag.querySelector(\".blockelemtype\").value == \"1\") {\n            drag.innerHTML += \"<div class='blockyleft'><img src='assets/eyeblue.svg'><p class='blockyname'>New visitor</p></div><div class='blockyright'><img src='assets/more.svg'></div><div class='blockydiv'></div><div class='blockyinfo'>When a <span>new visitor</span> goes to <span>Site 1</span></div>\";\n        } else if (drag.querySelector(\".blockelemtype\").value == \"2\") {\n            drag.innerHTML += \"<div class='blockyleft'><img src='assets/actionblue.svg'><p class='blockyname'>Action is performed</p></div><div class='blockyright'><img src='assets/more.svg'></div><div class='blockydiv'></div><div class='blockyinfo'>When <span>Action 1</span> is performed</div>\";\n        } else if (drag.querySelector(\".blockelemtype\").value == \"3\") {\n            drag.innerHTML += \"<div class='blockyleft'><img src='assets/timeblue.svg'><p class='blockyname'>Time has passed</p></div><div class='blockyright'><img src='assets/more.svg'></div><div class='blockydiv'></div><div class='blockyinfo'>When <span>10 seconds</span> have passed</div>\";\n        } else if (drag.querySelector(\".blockelemtype\").value == \"4\") {\n            drag.innerHTML += \"<div class='blockyleft'><img src='assets/errorblue.svg'><p class='blockyname'>Error prompt</p></div><div class='blockyright'><img src='assets/more.svg'></div><div class='blockydiv'></div><div class='blockyinfo'>When <span>Error 1</span> is triggered</div>\";\n        } else if (drag.querySelector(\".blockelemtype\").value == \"5\") {\n            drag.innerHTML += \"<div class='blockyleft'><img src='assets/databaseorange.svg'><p class='blockyname'>New database entry</p></div><div class='blockyright'><img src='assets/more.svg'></div><div class='blockydiv'></div><div class='blockyinfo'>Add <span>Data object</span> to <span>Database 1</span></div>\";\n        } else if (drag.querySelector(\".blockelemtype\").value == \"6\") {\n            drag.innerHTML += \"<div class='blockyleft'><img src='assets/databaseorange.svg'><p class='blockyname'>Update database</p></div><div class='blockyright'><img src='assets/more.svg'></div><div class='blockydiv'></div><div class='blockyinfo'>Update <span>Database 1</span></div>\";\n        } else if (drag.querySelector(\".blockelemtype\").value == \"7\") {\n            drag.innerHTML += \"<div class='blockyleft'><img src='assets/actionorange.svg'><p class='blockyname'>Perform an action</p></div><div class='blockyright'><img src='assets/more.svg'></div><div class='blockydiv'></div><div class='blockyinfo'>Perform <span>Action 1</span></div>\";\n        } else if (drag.querySelector(\".blockelemtype\").value == \"8\") {\n            drag.innerHTML += \"<div class='blockyleft'><img src='assets/twitterorange.svg'><p class='blockyname'>Make a tweet</p></div><div class='blockyright'><img src='assets/more.svg'></div><div class='blockydiv'></div><div class='blockyinfo'>Tweet <span>Query 1</span> with the account <span>@alyssaxuu</span></div>\";\n        } else if (drag.querySelector(\".blockelemtype\").value == \"9\") {\n            drag.innerHTML += \"<div class='blockyleft'><img src='assets/logred.svg'><p class='blockyname'>Add new log entry</p></div><div class='blockyright'><img src='assets/more.svg'></div><div class='blockydiv'></div><div class='blockyinfo'>Add new <span>success</span> log entry</div>\";\n        } else if (drag.querySelector(\".blockelemtype\").value == \"10\") {\n            drag.innerHTML += \"<div class='blockyleft'><img src='assets/logred.svg'><p class='blockyname'>Update logs</p></div><div class='blockyright'><img src='assets/more.svg'></div><div class='blockydiv'></div><div class='blockyinfo'>Edit <span>Log Entry 1</span></div>\";\n        } else if (drag.querySelector(\".blockelemtype\").value == \"11\") {\n            drag.innerHTML += \"<div class='blockyleft'><img src='assets/errorred.svg'><p class='blockyname'>Prompt an error</p></div><div class='blockyright'><img src='assets/more.svg'></div><div class='blockydiv'></div><div class='blockyinfo'>Trigger <span>Error 1</span></div>\";\n        }\n        return true;\n    }\n    function drag(block) {\n        block.classList.add(\"blockdisabled\");\n        tempblock2 = block;\n    }\n    function release() {\n        if (tempblock2) {\n            tempblock2.classList.remove(\"blockdisabled\");\n        }\n    }\n    var disabledClick = function(){\n        document.querySelector(\".navactive\").classList.add(\"navdisabled\");\n        document.querySelector(\".navactive\").classList.remove(\"navactive\");\n        this.classList.add(\"navactive\");\n        this.classList.remove(\"navdisabled\");\n        if (this.getAttribute(\"id\") == \"triggers\") {\n            document.getElementById(\"blocklist\").innerHTML = '<div class=\"blockelem create-flowy noselect\"><input type=\"hidden\" name=\"blockelemtype\" class=\"blockelemtype\" value=\"1\"><div class=\"grabme\"><img src=\"assets/grabme.svg\"></div><div class=\"blockin\">                  <div class=\"blockico\"><span></span><img src=\"assets/eye.svg\"></div><div class=\"blocktext\">                        <p class=\"blocktitle\">New visitor</p><p class=\"blockdesc\">Triggers when somebody visits a specified page</p>        </div></div></div><div class=\"blockelem create-flowy noselect\"><input type=\"hidden\" name=\"blockelemtype\" class=\"blockelemtype\" value=\"2\"><div class=\"grabme\"><img src=\"assets/grabme.svg\"></div><div class=\"blockin\">                    <div class=\"blockico\"><span></span><img src=\"assets/action.svg\"></div><div class=\"blocktext\">                        <p class=\"blocktitle\">Action is performed</p><p class=\"blockdesc\">Triggers when somebody performs a specified action</p></div></div></div><div class=\"blockelem create-flowy noselect\"><input type=\"hidden\" name=\"blockelemtype\" class=\"blockelemtype\" value=\"3\"><div class=\"grabme\"><img src=\"assets/grabme.svg\"></div><div class=\"blockin\">                    <div class=\"blockico\"><span></span><img src=\"assets/time.svg\"></div><div class=\"blocktext\">                        <p class=\"blocktitle\">Time has passed</p><p class=\"blockdesc\">Triggers after a specified amount of time</p>          </div></div></div><div class=\"blockelem create-flowy noselect\"><input type=\"hidden\" name=\"blockelemtype\" class=\"blockelemtype\" value=\"4\"><div class=\"grabme\"><img src=\"assets/grabme.svg\"></div><div class=\"blockin\">                    <div class=\"blockico\"><span></span><img src=\"assets/error.svg\"></div><div class=\"blocktext\">                        <p class=\"blocktitle\">Error prompt</p><p class=\"blockdesc\">Triggers when a specified error happens</p>              </div></div></div>';\n        } else if (this.getAttribute(\"id\") == \"actions\") {\n            document.getElementById(\"blocklist\").innerHTML = '<div class=\"blockelem create-flowy noselect\"><input type=\"hidden\" name=\"blockelemtype\" class=\"blockelemtype\" value=\"5\"><div class=\"grabme\"><img src=\"assets/grabme.svg\"></div><div class=\"blockin\">                  <div class=\"blockico\"><span></span><img src=\"assets/database.svg\"></div><div class=\"blocktext\">                        <p class=\"blocktitle\">New database entry</p><p class=\"blockdesc\">Adds a new entry to a specified database</p>        </div></div></div><div class=\"blockelem create-flowy noselect\"><input type=\"hidden\" name=\"blockelemtype\" class=\"blockelemtype\" value=\"6\"><div class=\"grabme\"><img src=\"assets/grabme.svg\"></div><div class=\"blockin\">                  <div class=\"blockico\"><span></span><img src=\"assets/database.svg\"></div><div class=\"blocktext\">                        <p class=\"blocktitle\">Update database</p><p class=\"blockdesc\">Edits and deletes database entries and properties</p>        </div></div></div><div class=\"blockelem create-flowy noselect\"><input type=\"hidden\" name=\"blockelemtype\" class=\"blockelemtype\" value=\"7\"><div class=\"grabme\"><img src=\"assets/grabme.svg\"></div><div class=\"blockin\">                  <div class=\"blockico\"><span></span><img src=\"assets/action.svg\"></div><div class=\"blocktext\">                        <p class=\"blocktitle\">Perform an action</p><p class=\"blockdesc\">Performs or edits a specified action</p>        </div></div></div><div class=\"blockelem create-flowy noselect\"><input type=\"hidden\" name=\"blockelemtype\" class=\"blockelemtype\" value=\"8\"><div class=\"grabme\"><img src=\"assets/grabme.svg\"></div><div class=\"blockin\">                  <div class=\"blockico\"><span></span><img src=\"assets/twitter.svg\"></div><div class=\"blocktext\">                        <p class=\"blocktitle\">Make a tweet</p><p class=\"blockdesc\">Makes a tweet with a specified query</p>        </div></div></div>';\n        } else if (this.getAttribute(\"id\") == \"loggers\") {\n            document.getElementById(\"blocklist\").innerHTML = '<div class=\"blockelem create-flowy noselect\"><input type=\"hidden\" name=\"blockelemtype\" class=\"blockelemtype\" value=\"9\"><div class=\"grabme\"><img src=\"assets/grabme.svg\"></div><div class=\"blockin\">                  <div class=\"blockico\"><span></span><img src=\"assets/log.svg\"></div><div class=\"blocktext\">                        <p class=\"blocktitle\">Add new log entry</p><p class=\"blockdesc\">Adds a new log entry to this project</p>        </div></div></div><div class=\"blockelem create-flowy noselect\"><input type=\"hidden\" name=\"blockelemtype\" class=\"blockelemtype\" value=\"10\"><div class=\"grabme\"><img src=\"assets/grabme.svg\"></div><div class=\"blockin\">                  <div class=\"blockico\"><span></span><img src=\"assets/log.svg\"></div><div class=\"blocktext\">                        <p class=\"blocktitle\">Update logs</p><p class=\"blockdesc\">Edits and deletes log entries in this project</p>        </div></div></div><div class=\"blockelem create-flowy noselect\"><input type=\"hidden\" name=\"blockelemtype\" class=\"blockelemtype\" value=\"11\"><div class=\"grabme\"><img src=\"assets/grabme.svg\"></div><div class=\"blockin\">                  <div class=\"blockico\"><span></span><img src=\"assets/error.svg\"></div><div class=\"blocktext\">                        <p class=\"blocktitle\">Prompt an error</p><p class=\"blockdesc\">Triggers a specified error</p>        </div></div></div>';\n        }\n    }\n    addEventListenerMulti(\"click\", disabledClick, false, \".side\");\n    document.getElementById(\"close\").addEventListener(\"click\", function(){\n       if (rightcard) {\n           rightcard = false;\n           document.getElementById(\"properties\").classList.remove(\"expanded\");\n           setTimeout(function(){\n                document.getElementById(\"propwrap\").classList.remove(\"itson\"); \n           }, 300);\n            tempblock.classList.remove(\"selectedblock\");\n       } \n    });\n    \ndocument.getElementById(\"removeblock\").addEventListener(\"click\", function(){\n flowy.deleteBlocks();\n});\nvar aclick = false;\nvar noinfo = false;\nvar beginTouch = function (event) {\n    aclick = true;\n    noinfo = false;\n    if (event.target.closest(\".create-flowy\")) {\n        noinfo = true;\n    }\n}\nvar checkTouch = function (event) {\n    aclick = false;\n}\nvar doneTouch = function (event) {\n    if (event.type === \"mouseup\" && aclick && !noinfo) {\n      if (!rightcard && event.target.closest(\".block\") && !event.target.closest(\".block\").classList.contains(\"dragging\")) {\n            tempblock = event.target.closest(\".block\");\n            rightcard = true;\n            document.getElementById(\"properties\").classList.add(\"expanded\");\n            document.getElementById(\"propwrap\").classList.add(\"itson\");\n            tempblock.classList.add(\"selectedblock\");\n       } \n    }\n}\naddEventListener(\"mousedown\", beginTouch, false);\naddEventListener(\"mousemove\", checkTouch, false);\naddEventListener(\"mouseup\", doneTouch, false);\naddEventListenerMulti(\"touchstart\", beginTouch, false, \".block\");\n});\n"
  },
  {
    "path": "demo/styles.css",
    "content": "body, html {\n    margin: 0px;\n    padding: 0px;\n    overflow: hidden;\n    background-image: url(assets/tile.png);\n    background-repeat: repeat;\n    background-size: 30px 30px;\n    background-color: #FBFBFB;\n    height: 100%;\n}\n#navigation {\n    height: 71px;\n    background-color: #FFF;\n    border: 1px solid #E8E8EF;\n    width: 100%;\n    display: table;\n    box-sizing: border-box;\n    position: fixed;\n    top: 0;\n    z-index: 9\n}\n#back {\n    width: 40px;\n    height: 40px;\n    border-radius: 100px;\n    background-color: #F1F4FC;\n    text-align: center;\n    display: inline-block;\n    vertical-align: top;\n    margin-top: 12px;\n    margin-right: 10px\n}\n#back img {\n    margin-top: 13px;\n}\n#names {\n    display: inline-block;\n    vertical-align: top;\n}\n#title {\n    font-family: Roboto;\n    font-weight: 500;\n    font-size: 16px;\n    color: #393C44;\n    margin-bottom: 0px;\n}\n#subtitle {\n    font-family: Roboto;\n    color: #808292;\n    font-size: 14px;\n    margin-top: 5px;\n}\n#leftside {\n    display: inline-block;\n    vertical-align: middle;\n    margin-left: 20px;\n}\n#centerswitch {\n    position: absolute;\n    width: 222px;\n    left: 50%;\n    margin-left: -111px;\n    top: 15px;\n}\n#leftswitch {\n    border: 1px solid #E8E8EF;\n    background-color: #FBFBFB;\n    width: 111px;\n    height: 39px;\n    line-height: 39px;\n    border-radius: 5px 0px 0px 5px;\n    font-family: Roboto;\n    color: #393C44;\n    display: inline-block;\n    font-size: 14px;\n    text-align: center;\n}\n#rightswitch {\n    font-family: Roboto;\n    color: #808292;\n    border-radius: 0px 5px 5px 0px;\n    border: 1px solid #E8E8EF;\n    height: 39px;\n    width: 102px;\n    display: inline-block;\n    font-size: 14px;\n    line-height: 39px;\n    text-align: center;\n    margin-left: -5px;\n}\n#discard {\n    font-family: Roboto;\n    font-weight: 500;\n    font-size: 14px;\n    color: #A6A6B3;\n    width: 95px;\n    height: 38px;\n    border: 1px solid #E8E8EF;\n    border-radius: 5px;\n    text-align: center;\n    line-height: 38px;\n    display: inline-block;\n    vertical-align: top;\n    transition: all .2s cubic-bezier(.05,.03,.35,1);\n}\n#discard:hover {\n    cursor: pointer;\n    opacity: .7;\n}\n#publish {\n    font-family: Roboto;\n    font-weight: 500;\n    font-size: 14px;\n    color: #FFF;\n    background-color: #217CE8;\n    border-radius: 5px;\n    width: 143px;\n    height: 38px;\n    margin-left: 10px;\n    display: inline-block;\n    vertical-align: top;\n    text-align: center;\n    line-height: 38px;\n    margin-right: 20px;\n    transition: all .2s cubic-bezier(.05,.03,.35,1);\n}\n#publish:hover {\n    cursor: pointer;\n    opacity: .7;\n}\n#buttonsright {\n    float: right;\n    margin-top: 15px;\n}\n#leftcard {\n    width: 363px;\n    background-color: #FFF;\n    border: 1px solid #E8E8EF;\n    box-sizing: border-box;\n    padding-top: 85px;\n    padding-left: 20px;\n    height: 100%;\n    position: absolute;\n    z-index: 2;\n}\n#search input {\n    width: 318px;\n    height: 40px;\n    background-color: #FFF;\n    border: 1px solid #E8E8EF;\n    box-sizing: border-box;\n    box-shadow: 0px 2px 8px rgba(34,34,87,0.05);\n    border-radius: 5px;\n    text-indent: 35px;\n    font-family: Roboto;\n    font-size: 16px;\n}\n::-webkit-input-placeholder { /* Edge */\n  color: #C9C9D5;\n}\n\n:-ms-input-placeholder { /* Internet Explorer 10-11 */\n  color: #C9C9D5\n}\n\n::placeholder {\n  color: #C9C9D5;\n}\n#search img {\n    position: absolute; \n    margin-top: 10px;\n    width: 18px;\n    margin-left: 12px;\n}\n#header {\n    font-size: 20px;\n    font-family: Roboto;\n    font-weight: bold;\n    color: #393C44;\n}\n#subnav {\n    border-bottom: 1px solid #E8E8EF;\n    width: calc(100% + 20px);\n    margin-left: -20px;\n    margin-top: 10px;\n}\n.navdisabled {\n    transition: all .3s cubic-bezier(.05,.03,.35,1);\n}\n.navdisabled:hover {\n    cursor: pointer;\n    opacity: .5;\n}\n.navactive {\n    color: #393C44!important;\n}\n#triggers {\n    margin-left: 20px;\n    font-family: Roboto;\n    font-weight: 500;\n    font-size: 14px;\n    text-align: center;\n    color: #808292;\n    width: calc(88% / 3);\n    height: 48px;\n    line-height: 48px;\n    display: inline-block;\n    float: left;\n}\n.navactive:after {\n    display: block;\n    content: \"\";\n    width:  100%;\n    height: 4px;\n    background-color: #217CE8;\n    margin-top: -4px;\n}\n#actions {\n    display: inline-block;\n    font-family: Roboto;\n    font-weight: 500;\n    color: #808292;\n    font-size: 14px;\n    height: 48px;\n    line-height: 48px;\n    width: calc(88% / 3);\n    text-align: center;\n    float: left;\n}\n#loggers {\n    width: calc(88% / 3);\n    display: inline-block;\n    font-family: Roboto;\n    font-weight: 500;\n    color: #808292;\n    font-size: 14px;\n    height: 48px;\n    line-height: 48px;\n    text-align: center;\n}\n#footer {\n    position: absolute;\n    left: 0;\n    padding-left: 20px;\n    line-height: 40px;\n    bottom: 0;\n    width: 362px;\n    border: 1px solid #E8E8EF;\n    height: 67px;\n    box-sizing: border-box;\n    background-color: #FFF;\n    font-family: Roboto;\n    font-size: 14px;\n}\n#footer a {\n    text-decoration: none;\n    color: #393C44;\n    transition: all .2s cubic-bezier(.05,.03,.35,1);\n}\n#footer a:hover {\n    opacity: .5;\n}\n#footer span {\n    color: #808292;\n}\n#footer p {\n    display: inline-block;\n    color: #808292;\n}\n#footer img {\n    margin-left: 5px;\n    margin-right: 5px;\n}\n.blockelem:first-child {\n    margin-top: 20px\n}\n.blockelem {\n    padding-top: 10px;\n    width: 318px;\n    border: 1px solid transparent;\n    transition-property: box-shadow, height;\n    transition-duration: .2s;\n    transition-timing-function: cubic-bezier(.05,.03,.35,1);\n    border-radius: 5px;\n    box-shadow: 0px 0px 30px rgba(22, 33, 74, 0);\n    box-sizing: border-box;\n}\n.blockelem:hover {\n    box-shadow: 0px 4px 30px rgba(22, 33, 74, 0.08);\n    border-radius: 5px;\n    background-color: #FFF;\n    cursor: pointer;\n}\n.grabme, .blockico {\n    display: inline-block;\n}\n.grabme {\n    margin-top: 10px;\n    margin-left: 10px;\n    margin-bottom: -14px;\n    width: 15px;\n}\n#blocklist {\n    height: calc(100% - 220px);\n    overflow: auto;\n}\n#proplist {\n    height: calc(100% - 305px);\n    overflow: auto;\n    margin-top: -30px;\n    padding-top: 30px;\n}\n.blockin {\n    display: inline-block;\n    vertical-align: top;\n    margin-left: 12px;\n}\n.blockico {\n    width: 36px;\n    height: 36px;\n    background-color: #F1F4FC;\n    border-radius: 5px;\n    text-align: center;\n    white-space: nowrap;\n}\n.blockico span {\n    height: 100%;\n    width: 0px;\n    display: inline-block;\n    vertical-align: middle;\n}\n.blockico img {\n    vertical-align: middle;\n    margin-left: auto;\n    margin-right: auto;\n    display: inline-block;\n}\n.blocktext {\n    display: inline-block;\n    width: 220px;\n    vertical-align: top;\n    margin-left: 12px\n}\n.blocktitle {\n    margin: 0px!important;\n    padding: 0px!important;\n    font-family: Roboto;\n    font-weight: 500;\n    font-size: 16px;\n    color: #393C44;\n}\n.blockdesc {\n    margin-top: 5px;\n    font-family: Roboto;\n    color: #808292;\n    font-size: 14px;\n    line-height: 21px;\n}\n.blockdisabled {\n    background-color: #F0F2F9;\n    opacity: .5;\n}\n#closecard {\n    position: absolute;\n    margin-left: 340px;\n    background-color: #FFF;\n    border-radius: 0px 5px 5px 0px;\n    border-bottom: 1px solid #E8E8EF;\n    border-right: 1px solid #E8E8EF;\n    border-top: 1px solid #E8E8EF;\n    width: 53px;\n    height: 53px;\n    text-align: center; \n    z-index: 10;\n}\n#closecard img {\n    margin-top: 15px\n}\n#canvas {\n    position: absolute;\n    width: calc(100% - 361px);\n    height: calc(100% - 71px);\n    top: 71px;\n    left: 361px;\n    z-index: 0;\n    overflow: auto;\n}\n#propwrap {\n    position: absolute;\n    right: 0;\n    top: 0;\n    width: 311px;\n    height: 100%;\n    padding-left: 20px;\n    overflow: hidden;\n    z-index: -2;\n}\n#properties {\n    position: absolute;\n    height: 100%;\n    width: 311px;\n    background-color: #FFF;\n    right: -150px;\n    opacity: 0;\n    z-index: 2;\n    top: 0px;\n    box-shadow: -4px 0px 40px rgba(26, 26, 73, 0);\n    padding-left: 20px;\n    transition: all .25s cubic-bezier(.05,.03,.35,1);\n}\n.itson {\n    z-index: 2!important;\n}\n.expanded {\n    right: 0!important;\n    opacity: 1!important;\n    box-shadow: -4px 0px 40px rgba(26, 26, 73, 0.05);\n        z-index: 2;\n}\n#header2 {\n    font-size: 20px;\n    font-family: Roboto;\n    font-weight: bold;\n    color: #393C44;\n    margin-top: 101px;\n}\n#close {\n    margin-top: 100px;\n    position: absolute;\n    right: 20px;\n    z-index: 9999;\n    transition: all .25s cubic-bezier(.05,.03,.35,1);\n}\n#close:hover {\n    cursor: pointer;\n    opacity: .7;\n}\n#propswitch {\n    border-bottom: 1px solid #E8E8EF;\n    width: 331px;\n    margin-top: 10px;\n    margin-left: -20px;\n    margin-bottom: 30px;\n}\n#dataprop {\n    font-family: Roboto;\n    font-weight: 500;\n    font-size: 14px;\n    text-align: center;\n    color: #393C44;\n    width: calc(88% / 3);\n    height: 48px;\n    line-height: 48px;\n    display: inline-block;\n    float: left;\n    margin-left: 20px;\n}\n#dataprop:after {\n    display: block;\n    content: \"\";\n    width: 100%;\n    height: 4px;\n    background-color: #217CE8;\n    margin-top: -4px;\n}\n#alertprop {\n    display: inline-block;\n    font-family: Roboto;\n    font-weight: 500;\n    color: #808292;\n    font-size: 14px;\n    height: 48px;\n    line-height: 48px;\n    width: calc(88% / 3);\n    text-align: center;\n    float: left;\n}\n#logsprop {\n    width: calc(88% / 3);\n    display: inline-block;\n    font-family: Roboto;\n    font-weight: 500;\n    color: #808292;\n    font-size: 14px;\n    height: 48px;\n    line-height: 48px;\n    text-align: center;\n}\n.inputlabel {\n    font-family: Roboto;\n    font-size: 14px;\n    color: #253134;\n}\n.dropme {\n    background-color: #FFF;\n    border-radius: 5px;\n    border: 1px solid #E8E8EF;\n    box-shadow: 0px 2px 8px rgba(34, 34, 87, 0.05);\n    font-family: Roboto;\n    font-size: 14px;\n    color: #253134;\n    text-indent: 20px;\n    height: 40px;\n    line-height: 40px;\n    width: 287px;\n    margin-bottom: 25px;\n}\n.dropme img {\n    margin-top: 17px;\n    float: right;\n    margin-right: 15px;\n}\n.checkus {\n    margin-bottom: 10px;\n}\n.checkus img {\n    display: inline-block;\n    vertical-align: middle;\n}\n.checkus p {\n    display: inline-block;\n    font-family: Roboto;\n    font-size: 14px;\n    vertical-align: middle;\n    margin-left: 10px;\n}\n#divisionthing {\n    height: 1px;\n    width: 100%;\n    background-color: #E8E8EF;\n    position: absolute;\n    right: 0px;\n    bottom: 80;\n}\n#removeblock {\n    border-radius: 5px;\n    position: absolute;\n    bottom: 20px;\n    font-family: Roboto;\n    font-size: 14px;\n    text-align: center;\n    width: 287px;\n    height: 38px;\n    line-height: 38px;\n    color: #253134;\n    border: 1px solid #E8E8EF;\n    transition: all .3s cubic-bezier(.05,.03,.35,1);\n}\n#removeblock:hover {\n    cursor: pointer;\n    opacity: .5;\n}\n.noselect {\n  -webkit-touch-callout: none; /* iOS Safari */\n    -webkit-user-select: none; /* Safari */\n     -khtml-user-select: none; /* Konqueror HTML */\n       -moz-user-select: none; /* Old versions of Firefox */\n        -ms-user-select: none; /* Internet Explorer/Edge */\n            user-select: none; /* Non-prefixed version, currently\n                                  supported by Chrome, Opera and Firefox */\n}\n.blockyname {\n    font-family: Roboto;\n    font-weight: 500;\n    color: #253134;\n    display: inline-block;\n    vertical-align: middle;\n    margin-left: 8px;\n    font-size: 16px;\n}\n.blockyleft img {\n    display: inline-block;\n    vertical-align: middle;\n}\n.blockyright {\n    display: inline-block;\n    float: right;\n    vertical-align: middle;\n    margin-right: 20px;\n    margin-top: 10px;\n    width: 28px;\n    height: 28px;\n    border-radius: 5px;\n    text-align: center; \n    background-color: #FFF;\n    transition: all .3s cubic-bezier(.05,.03,.35,1);\n    z-index: 10;\n}\n.blockyright:hover {\n    background-color: #F1F4FC;\n    cursor: pointer;\n}\n.blockyright img {\n    margin-top: 12px;\n}\n.blockyleft {\n    display: inline-block;\n    margin-left: 20px;\n}\n.blockydiv {\n    width: 100%;\n    height: 1px;\n    background-color: #E9E9EF;\n}\n.blockyinfo {\n    font-family: Roboto;\n    font-size: 14px;\n    color: #808292;\n    margin-top: 15px;\n    text-indent: 20px;\n    margin-bottom: 20px;\n}\n.blockyinfo span {\n    color: #253134;\n    font-weight: 500;\n    display: inline-block;\n    border-bottom: 1px solid #D3DCEA;\n    line-height: 20px;\n    text-indent: 0px;\n}\n.block {\n    background-color: #FFF;\n    margin-top: 0px!important;\n    box-shadow: 0px 4px 30px rgba(22, 33, 74, 0.05);\n}\n.selectedblock {\n    border: 2px solid #217CE8;\n    box-shadow: 0px 4px 30px rgba(22, 33, 74, 0.08);\n}\n\n@media only screen and (max-width: 832px) {\n    #centerswitch {\n        display: none;\n    }\n}\n@media only screen and (max-width: 560px) {\n    #names {\n        display: none;\n    }   \n}\n"
  },
  {
    "path": "engine/flowy.css",
    "content": ".dragging{z-index:111!important}.block{position:absolute;z-index:9}.indicator{width:12px;height:12px;border-radius:60px;background-color:#217ce8;margin-top:-5px;opacity:1;transition:all .3s cubic-bezier(.05,.03,.35,1);transform:scale(1);position:absolute;z-index:2}.invisible{opacity:0!important;transform:scale(0)}.indicator:after{content:\"\";display:block;width:12px;height:12px;background-color:#217ce8;transform:scale(1.7);opacity:.2;border-radius:60px}.arrowblock{position:absolute;width:100%;overflow:visible;pointer-events:none}.arrowblock svg{width: -webkit-fill-available;overflow: visible;}"
  },
  {
    "path": "engine/flowy.js",
    "content": "var flowy = function(canvas, grab, release, snapping, rearrange, spacing_x, spacing_y) {\n    if (!grab) {\n        grab = function() {};\n    }\n    if (!release) {\n        release = function() {};\n    }\n    if (!snapping) {\n        snapping = function() {\n            return true;\n        }\n    }\n    if (!rearrange) {\n        rearrange = function() {\n            return false;\n        }\n    }\n    if (!spacing_x) {\n        spacing_x = 20;\n    }\n    if (!spacing_y) {\n        spacing_y = 80;\n    }\n    if (!Element.prototype.matches) {\n        Element.prototype.matches = Element.prototype.msMatchesSelector ||\n            Element.prototype.webkitMatchesSelector;\n    }\n    if (!Element.prototype.closest) {\n        Element.prototype.closest = function(s) {\n            var el = this;\n            do {\n                if (Element.prototype.matches.call(el, s)) return el;\n                el = el.parentElement || el.parentNode;\n            } while (el !== null && el.nodeType === 1);\n            return null;\n        };\n    }\n    var loaded = false;\n    flowy.load = function() {\n        if (!loaded)\n            loaded = true;\n        else\n            return;\n        var blocks = [];\n        var blockstemp = [];\n        var canvas_div = canvas;\n        var absx = 0;\n        var absy = 0;\n        if (window.getComputedStyle(canvas_div).position == \"absolute\" || window.getComputedStyle(canvas_div).position == \"fixed\") {\n            absx = canvas_div.getBoundingClientRect().left;\n            absy = canvas_div.getBoundingClientRect().top;\n        }\n        var active = false;\n        var paddingx = spacing_x;\n        var paddingy = spacing_y;\n        var offsetleft = 0;\n        var rearrange = false;\n        var drag, dragx, dragy, original;\n        var mouse_x, mouse_y;\n        var dragblock = false;\n        var prevblock = 0;\n        var el = document.createElement(\"DIV\");\n        el.classList.add('indicator');\n        el.classList.add('invisible');\n        canvas_div.appendChild(el);\n        flowy.import = function(output) {\n            canvas_div.innerHTML = output.html;\n            for (var a = 0; a < output.blockarr.length; a++) {\n                blocks.push({\n                    childwidth: parseFloat(output.blockarr[a].childwidth),\n                    parent: parseFloat(output.blockarr[a].parent),\n                    id: parseFloat(output.blockarr[a].id),\n                    x: parseFloat(output.blockarr[a].x),\n                    y: parseFloat(output.blockarr[a].y),\n                    width: parseFloat(output.blockarr[a].width),\n                    height: parseFloat(output.blockarr[a].height)\n                })\n            }\n            if (blocks.length > 1) {\n                rearrangeMe();\n                checkOffset();\n            }\n        }\n        flowy.output = function() {\n            var html_ser = canvas_div.innerHTML;\n            var json_data = {\n                html: html_ser,\n                blockarr: blocks,\n                blocks: []\n            };\n            if (blocks.length > 0) {\n                for (var i = 0; i < blocks.length; i++) {\n                    json_data.blocks.push({\n                        id: blocks[i].id,\n                        parent: blocks[i].parent,\n                        data: [],\n                        attr: []\n                    });\n                    var blockParent = document.querySelector(\".blockid[value='\" + blocks[i].id + \"']\").parentNode;\n                    blockParent.querySelectorAll(\"input\").forEach(function(block) {\n                        var json_name = block.getAttribute(\"name\");\n                        var json_value = block.value;\n                        json_data.blocks[i].data.push({\n                            name: json_name,\n                            value: json_value\n                        });\n                    });\n                    Array.prototype.slice.call(blockParent.attributes).forEach(function(attribute) {\n                        var jsonobj = {};\n                        jsonobj[attribute.name] = attribute.value;\n                        json_data.blocks[i].attr.push(jsonobj);\n                    });\n                }\n                return json_data;\n            }\n        }\n        flowy.deleteBlocks = function() {\n            blocks = [];\n            canvas_div.innerHTML = \"<div class='indicator invisible'></div>\";\n        }\n\n        flowy.beginDrag = function(event) {\n            if (window.getComputedStyle(canvas_div).position == \"absolute\" || window.getComputedStyle(canvas_div).position == \"fixed\") {\n                absx = canvas_div.getBoundingClientRect().left;\n                absy = canvas_div.getBoundingClientRect().top;\n            }\n            if (event.targetTouches) {\n                mouse_x = event.changedTouches[0].clientX;\n                mouse_y = event.changedTouches[0].clientY;\n            } else {\n                mouse_x = event.clientX;\n                mouse_y = event.clientY;\n            }\n            if (event.which != 3 && event.target.closest(\".create-flowy\")) {\n                original = event.target.closest(\".create-flowy\");\n                var newNode = event.target.closest(\".create-flowy\").cloneNode(true);\n                event.target.closest(\".create-flowy\").classList.add(\"dragnow\");\n                newNode.classList.add(\"block\");\n                newNode.classList.remove(\"create-flowy\");\n                if (blocks.length === 0) {\n                    newNode.innerHTML += \"<input type='hidden' name='blockid' class='blockid' value='\" + blocks.length + \"'>\";\n                    document.body.appendChild(newNode);\n                    drag = document.querySelector(\".blockid[value='\" + blocks.length + \"']\").parentNode;\n                } else {\n                    newNode.innerHTML += \"<input type='hidden' name='blockid' class='blockid' value='\" + (Math.max.apply(Math, blocks.map(a => a.id)) + 1) + \"'>\";\n                    document.body.appendChild(newNode);\n                    drag = document.querySelector(\".blockid[value='\" + (parseInt(Math.max.apply(Math, blocks.map(a => a.id))) + 1) + \"']\").parentNode;\n                }\n                blockGrabbed(event.target.closest(\".create-flowy\"));\n                drag.classList.add(\"dragging\");\n                active = true;\n                dragx = mouse_x - (event.target.closest(\".create-flowy\").getBoundingClientRect().left);\n                dragy = mouse_y - (event.target.closest(\".create-flowy\").getBoundingClientRect().top);\n                drag.style.left = mouse_x - dragx + \"px\";\n                drag.style.top = mouse_y - dragy + \"px\";\n            }\n        }\n\n        flowy.endDrag = function(event) {\n            if (event.which != 3 && (active || rearrange)) {\n                dragblock = false;\n                blockReleased();\n                if (!document.querySelector(\".indicator\").classList.contains(\"invisible\")) {\n                    document.querySelector(\".indicator\").classList.add(\"invisible\");\n                }\n                if (active) {\n                    original.classList.remove(\"dragnow\");\n                    drag.classList.remove(\"dragging\");\n                }\n                if (parseInt(drag.querySelector(\".blockid\").value) === 0 && rearrange) {\n                    firstBlock(\"rearrange\")    \n                } else if (active && blocks.length == 0 && (drag.getBoundingClientRect().top + window.scrollY) > (canvas_div.getBoundingClientRect().top + window.scrollY) && (drag.getBoundingClientRect().left + window.scrollX) > (canvas_div.getBoundingClientRect().left + window.scrollX)) {\n                    firstBlock(\"drop\");\n                } else if (active && blocks.length == 0) {\n                    removeSelection();\n                } else if (active) {\n                    var blocko = blocks.map(a => a.id);\n                    for (var i = 0; i < blocks.length; i++) {\n                        if (checkAttach(blocko[i])) {\n                            active = false;\n                            if (blockSnap(drag, false, document.querySelector(\".blockid[value='\" + blocko[i] + \"']\").parentNode)) {\n                                snap(drag, i, blocko);\n                            } else {\n                                active = false;\n                                removeSelection();\n                            }\n                            break;\n                        } else if (i == blocks.length - 1) {\n                            active = false;\n                            removeSelection();\n                        }\n                    }\n                } else if (rearrange) {\n                    var blocko = blocks.map(a => a.id);\n                    for (var i = 0; i < blocks.length; i++) {\n                        if (checkAttach(blocko[i])) {\n                            active = false;\n                            drag.classList.remove(\"dragging\");\n                            snap(drag, i, blocko);\n                            break;\n                        } else if (i == blocks.length - 1) {\n                            if (beforeDelete(drag, blocks.filter(id => id.id == blocko[i])[0])) {\n                                active = false;\n                                drag.classList.remove(\"dragging\");\n                                snap(drag, blocko.indexOf(prevblock), blocko);\n                                break;\n                            } else {\n                                rearrange = false;\n                                blockstemp = [];\n                                active = false;\n                                removeSelection();\n                                break;\n                            }\n                        }\n                    }\n                }\n            }\n        }\n        \n        function checkAttach(id) {\n            const xpos = (drag.getBoundingClientRect().left + window.scrollX) + (parseInt(window.getComputedStyle(drag).width) / 2) + canvas_div.scrollLeft - canvas_div.getBoundingClientRect().left;\n            const ypos = (drag.getBoundingClientRect().top + window.scrollY) + canvas_div.scrollTop - canvas_div.getBoundingClientRect().top;\n            if (xpos >= blocks.filter(a => a.id == id)[0].x - (blocks.filter(a => a.id == id)[0].width / 2) - paddingx && xpos <= blocks.filter(a => a.id == id)[0].x + (blocks.filter(a => a.id == id)[0].width / 2) + paddingx && ypos >= blocks.filter(a => a.id == id)[0].y - (blocks.filter(a => a.id == id)[0].height / 2) && ypos <= blocks.filter(a => a.id == id)[0].y + blocks.filter(a => a.id == id)[0].height) {\n                return true;   \n            } else {\n                return false;\n            }\n        }\n        \n        function removeSelection() {\n            canvas_div.appendChild(document.querySelector(\".indicator\"));\n            drag.parentNode.removeChild(drag);\n        }\n        \n        function firstBlock(type) {\n            if (type == \"drop\") {\n                blockSnap(drag, true, undefined);\n                active = false;\n                drag.style.top = (drag.getBoundingClientRect().top + window.scrollY) - (absy + window.scrollY) + canvas_div.scrollTop + \"px\";\n                drag.style.left = (drag.getBoundingClientRect().left + window.scrollX) - (absx + window.scrollX) + canvas_div.scrollLeft + \"px\";\n                canvas_div.appendChild(drag);\n                blocks.push({\n                    parent: -1,\n                    childwidth: 0,\n                    id: parseInt(drag.querySelector(\".blockid\").value),\n                    x: (drag.getBoundingClientRect().left + window.scrollX) + (parseInt(window.getComputedStyle(drag).width) / 2) + canvas_div.scrollLeft - canvas_div.getBoundingClientRect().left,\n                    y: (drag.getBoundingClientRect().top + window.scrollY) + (parseInt(window.getComputedStyle(drag).height) / 2) + canvas_div.scrollTop - canvas_div.getBoundingClientRect().top,\n                    width: parseInt(window.getComputedStyle(drag).width),\n                    height: parseInt(window.getComputedStyle(drag).height)\n                });\n            } else if (type == \"rearrange\") {\n                drag.classList.remove(\"dragging\");\n                rearrange = false;\n                for (var w = 0; w < blockstemp.length; w++) {\n                    if (blockstemp[w].id != parseInt(drag.querySelector(\".blockid\").value)) {\n                        const blockParent = document.querySelector(\".blockid[value='\" + blockstemp[w].id + \"']\").parentNode;\n                        const arrowParent = document.querySelector(\".arrowid[value='\" + blockstemp[w].id + \"']\").parentNode;\n                        blockParent.style.left = (blockParent.getBoundingClientRect().left + window.scrollX) - (window.scrollX) + canvas_div.scrollLeft - 1 - absx + \"px\";\n                        blockParent.style.top = (blockParent.getBoundingClientRect().top + window.scrollY) - (window.scrollY) + canvas_div.scrollTop - absy - 1 + \"px\";\n                        arrowParent.style.left = (arrowParent.getBoundingClientRect().left + window.scrollX) - (window.scrollX) + canvas_div.scrollLeft - absx - 1 + \"px\";\n                        arrowParent.style.top = (arrowParent.getBoundingClientRect().top + window.scrollY) + canvas_div.scrollTop - 1 - absy + \"px\";\n                        canvas_div.appendChild(blockParent);\n                        canvas_div.appendChild(arrowParent);\n                        blockstemp[w].x = (blockParent.getBoundingClientRect().left + window.scrollX) + (parseInt(blockParent.offsetWidth) / 2) + canvas_div.scrollLeft - canvas_div.getBoundingClientRect().left - 1;\n                        blockstemp[w].y = (blockParent.getBoundingClientRect().top + window.scrollY) + (parseInt(blockParent.offsetHeight) / 2) + canvas_div.scrollTop - canvas_div.getBoundingClientRect().top - 1;\n                    }\n                }\n                blockstemp.filter(a => a.id == 0)[0].x = (drag.getBoundingClientRect().left + window.scrollX) + (parseInt(window.getComputedStyle(drag).width) / 2) + canvas_div.scrollLeft - canvas_div.getBoundingClientRect().left;\n                blockstemp.filter(a => a.id == 0)[0].y = (drag.getBoundingClientRect().top + window.scrollY) + (parseInt(window.getComputedStyle(drag).height) / 2) + canvas_div.scrollTop - canvas_div.getBoundingClientRect().top;\n                blocks = blocks.concat(blockstemp);\n                blockstemp = [];\n            }\n        }\n        \n        function drawArrow(arrow, x, y, id) {\n            if (x < 0) {\n                canvas_div.innerHTML += '<div class=\"arrowblock\"><input type=\"hidden\" class=\"arrowid\" value=\"' + drag.querySelector(\".blockid\").value + '\"><svg preserveaspectratio=\"none\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M' + (blocks.filter(a => a.id == id)[0].x - arrow.x + 5) + ' 0L' + (blocks.filter(a => a.id == id)[0].x - arrow.x + 5) + ' ' + (paddingy / 2) + 'L5 ' + (paddingy / 2) + 'L5 ' + y + '\" stroke=\"#C5CCD0\" stroke-width=\"2px\"/><path d=\"M0 ' + (y - 5) + 'H10L5 ' + y + 'L0 ' + (y - 5) + 'Z\" fill=\"#C5CCD0\"/></svg></div>';\n                document.querySelector('.arrowid[value=\"' + drag.querySelector(\".blockid\").value + '\"]').parentNode.style.left = (arrow.x - 5) - (absx + window.scrollX) + canvas_div.scrollLeft + canvas_div.getBoundingClientRect().left + \"px\";\n            } else {\n                canvas_div.innerHTML += '<div class=\"arrowblock\"><input type=\"hidden\" class=\"arrowid\" value=\"' + drag.querySelector(\".blockid\").value + '\"><svg preserveaspectratio=\"none\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M20 0L20 ' + (paddingy / 2) + 'L' + (x) + ' ' + (paddingy / 2) + 'L' + x + ' ' + y + '\" stroke=\"#C5CCD0\" stroke-width=\"2px\"/><path d=\"M' + (x - 5) + ' ' + (y - 5) + 'H' + (x + 5) + 'L' + x + ' ' + y + 'L' + (x - 5) + ' ' + (y - 5) + 'Z\" fill=\"#C5CCD0\"/></svg></div>';\n                document.querySelector('.arrowid[value=\"' + parseInt(drag.querySelector(\".blockid\").value) + '\"]').parentNode.style.left = blocks.filter(a => a.id == id)[0].x - 20 - (absx + window.scrollX) + canvas_div.scrollLeft + canvas_div.getBoundingClientRect().left + \"px\";\n            }\n            document.querySelector('.arrowid[value=\"' + parseInt(drag.querySelector(\".blockid\").value) + '\"]').parentNode.style.top = blocks.filter(a => a.id == id)[0].y + (blocks.filter(a => a.id == id)[0].height / 2) + canvas_div.getBoundingClientRect().top - absy + \"px\";\n        }\n        \n        function updateArrow(arrow, x, y, children) { \n            if (x < 0) {\n                document.querySelector('.arrowid[value=\"' + children.id + '\"]').parentNode.style.left = (arrow.x - 5) - (absx + window.scrollX) + canvas_div.getBoundingClientRect().left + \"px\";\n                document.querySelector('.arrowid[value=\"' + children.id + '\"]').parentNode.innerHTML = '<input type=\"hidden\" class=\"arrowid\" value=\"' + children.id + '\"><svg preserveaspectratio=\"none\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M' + (blocks.filter(id => id.id == children.parent)[0].x - arrow.x + 5) + ' 0L' + (blocks.filter(id => id.id == children.parent)[0].x - arrow.x + 5) + ' ' + (paddingy / 2) + 'L5 ' + (paddingy / 2) + 'L5 ' + y + '\" stroke=\"#C5CCD0\" stroke-width=\"2px\"/><path d=\"M0 ' + (y - 5) + 'H10L5 ' + y + 'L0 ' + (y - 5) + 'Z\" fill=\"#C5CCD0\"/></svg>';\n            } else {\n                document.querySelector('.arrowid[value=\"' + children.id + '\"]').parentNode.style.left = blocks.filter(id => id.id == children.parent)[0].x - 20 - (absx + window.scrollX) + canvas_div.getBoundingClientRect().left + \"px\";\n                document.querySelector('.arrowid[value=\"' + children.id + '\"]').parentNode.innerHTML = '<input type=\"hidden\" class=\"arrowid\" value=\"' + children.id + '\"><svg preserveaspectratio=\"none\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M20 0L20 ' + (paddingy / 2) + 'L' + (x) + ' ' + (paddingy / 2) + 'L' + x + ' ' + y + '\" stroke=\"#C5CCD0\" stroke-width=\"2px\"/><path d=\"M' + (x - 5) + ' ' + (y - 5) + 'H' + (x + 5) + 'L' + x + ' ' + y + 'L' + (x - 5) + ' ' + (y - 5) + 'Z\" fill=\"#C5CCD0\"/></svg>';\n            }\n        }\n\n        function snap(drag, i, blocko) {\n            if (!rearrange) {\n                canvas_div.appendChild(drag);\n            }\n            var totalwidth = 0;\n            var totalremove = 0;\n            var maxheight = 0;\n            for (var w = 0; w < blocks.filter(id => id.parent == blocko[i]).length; w++) {\n                var children = blocks.filter(id => id.parent == blocko[i])[w];\n                if (children.childwidth > children.width) {\n                    totalwidth += children.childwidth + paddingx;\n                } else {\n                    totalwidth += children.width + paddingx;\n                }\n            }\n            totalwidth += parseInt(window.getComputedStyle(drag).width);\n            for (var w = 0; w < blocks.filter(id => id.parent == blocko[i]).length; w++) {\n                var children = blocks.filter(id => id.parent == blocko[i])[w];\n                if (children.childwidth > children.width) {\n                    document.querySelector(\".blockid[value='\" + children.id + \"']\").parentNode.style.left = blocks.filter(a => a.id == blocko[i])[0].x - (totalwidth / 2) + totalremove + (children.childwidth / 2) - (children.width / 2) + \"px\";\n                    children.x = blocks.filter(id => id.parent == blocko[i])[0].x - (totalwidth / 2) + totalremove + (children.childwidth / 2);\n                    totalremove += children.childwidth + paddingx;\n                } else {\n                    document.querySelector(\".blockid[value='\" + children.id + \"']\").parentNode.style.left = blocks.filter(a => a.id == blocko[i])[0].x - (totalwidth / 2) + totalremove + \"px\";\n                    children.x = blocks.filter(id => id.parent == blocko[i])[0].x - (totalwidth / 2) + totalremove + (children.width / 2);\n                    totalremove += children.width + paddingx;\n                }\n            }\n            drag.style.left = blocks.filter(id => id.id == blocko[i])[0].x - (totalwidth / 2) + totalremove - (window.scrollX + absx) + canvas_div.scrollLeft + canvas_div.getBoundingClientRect().left + \"px\";\n            drag.style.top = blocks.filter(id => id.id == blocko[i])[0].y + (blocks.filter(id => id.id == blocko[i])[0].height / 2) + paddingy - (window.scrollY + absy) + canvas_div.getBoundingClientRect().top + \"px\";\n            if (rearrange) {\n                blockstemp.filter(a => a.id == parseInt(drag.querySelector(\".blockid\").value))[0].x = (drag.getBoundingClientRect().left + window.scrollX) + (parseInt(window.getComputedStyle(drag).width) / 2) + canvas_div.scrollLeft - canvas_div.getBoundingClientRect().left;\n                blockstemp.filter(a => a.id == parseInt(drag.querySelector(\".blockid\").value))[0].y = (drag.getBoundingClientRect().top + window.scrollY) + (parseInt(window.getComputedStyle(drag).height) / 2) + canvas_div.scrollTop - canvas_div.getBoundingClientRect().top;\n                blockstemp.filter(a => a.id == drag.querySelector(\".blockid\").value)[0].parent = blocko[i];\n                for (var w = 0; w < blockstemp.length; w++) {\n                    if (blockstemp[w].id != parseInt(drag.querySelector(\".blockid\").value)) {\n                        const blockParent = document.querySelector(\".blockid[value='\" + blockstemp[w].id + \"']\").parentNode;\n                        const arrowParent = document.querySelector(\".arrowid[value='\" + blockstemp[w].id + \"']\").parentNode;\n                        blockParent.style.left = (blockParent.getBoundingClientRect().left + window.scrollX) - (window.scrollX + canvas_div.getBoundingClientRect().left) + canvas_div.scrollLeft + \"px\";\n                        blockParent.style.top = (blockParent.getBoundingClientRect().top + window.scrollY) - (window.scrollY + canvas_div.getBoundingClientRect().top) + canvas_div.scrollTop + \"px\";\n                        arrowParent.style.left = (arrowParent.getBoundingClientRect().left + window.scrollX) - (window.scrollX + canvas_div.getBoundingClientRect().left) + canvas_div.scrollLeft + 20 + \"px\";\n                        arrowParent.style.top = (arrowParent.getBoundingClientRect().top + window.scrollY) - (window.scrollY + canvas_div.getBoundingClientRect().top) + canvas_div.scrollTop + \"px\";\n                        canvas_div.appendChild(blockParent);\n                        canvas_div.appendChild(arrowParent);\n\n                        blockstemp[w].x = (blockParent.getBoundingClientRect().left + window.scrollX) + (parseInt(window.getComputedStyle(blockParent).width) / 2) + canvas_div.scrollLeft - canvas_div.getBoundingClientRect().left;\n                        blockstemp[w].y = (blockParent.getBoundingClientRect().top + window.scrollY) + (parseInt(window.getComputedStyle(blockParent).height) / 2) + canvas_div.scrollTop - canvas_div.getBoundingClientRect().top;\n                    }\n                }\n                blocks = blocks.concat(blockstemp);\n                blockstemp = [];\n            } else {\n                blocks.push({\n                    childwidth: 0,\n                    parent: blocko[i],\n                    id: parseInt(drag.querySelector(\".blockid\").value),\n                    x: (drag.getBoundingClientRect().left + window.scrollX) + (parseInt(window.getComputedStyle(drag).width) / 2) + canvas_div.scrollLeft - canvas_div.getBoundingClientRect().left,\n                    y: (drag.getBoundingClientRect().top + window.scrollY) + (parseInt(window.getComputedStyle(drag).height) / 2) + canvas_div.scrollTop - canvas_div.getBoundingClientRect().top,\n                    width: parseInt(window.getComputedStyle(drag).width),\n                    height: parseInt(window.getComputedStyle(drag).height)\n                });\n            }\n            \n            var arrowblock = blocks.filter(a => a.id == parseInt(drag.querySelector(\".blockid\").value))[0];\n            var arrowx = arrowblock.x - blocks.filter(a => a.id == blocko[i])[0].x + 20;\n            var arrowy = paddingy;\n            drawArrow(arrowblock, arrowx, arrowy, blocko[i]);\n            \n            if (blocks.filter(a => a.id == blocko[i])[0].parent != -1) {\n                var flag = false;\n                var idval = blocko[i];\n                while (!flag) {\n                    if (blocks.filter(a => a.id == idval)[0].parent == -1) {\n                        flag = true;\n                    } else {\n                        var zwidth = 0;\n                        for (var w = 0; w < blocks.filter(id => id.parent == idval).length; w++) {\n                            var children = blocks.filter(id => id.parent == idval)[w];\n                            if (children.childwidth > children.width) {\n                                if (w == blocks.filter(id => id.parent == idval).length - 1) {\n                                    zwidth += children.childwidth;\n                                } else {\n                                    zwidth += children.childwidth + paddingx;\n                                }\n                            } else {\n                                if (w == blocks.filter(id => id.parent == idval).length - 1) {\n                                    zwidth += children.width;\n                                } else {\n                                    zwidth += children.width + paddingx;\n                                }\n                            }\n                        }\n                        blocks.filter(a => a.id == idval)[0].childwidth = zwidth;\n                        idval = blocks.filter(a => a.id == idval)[0].parent;\n                    }\n                }\n                blocks.filter(id => id.id == idval)[0].childwidth = totalwidth;\n            }\n            if (rearrange) {\n                rearrange = false;\n                drag.classList.remove(\"dragging\");\n            }\n            rearrangeMe();\n            checkOffset();\n        }\n\n        function touchblock(event) {\n            dragblock = false;\n            if (hasParentClass(event.target, \"block\")) {\n                var theblock = event.target.closest(\".block\");\n                if (event.targetTouches) {\n                    mouse_x = event.targetTouches[0].clientX;\n                    mouse_y = event.targetTouches[0].clientY;\n                } else {\n                    mouse_x = event.clientX;\n                    mouse_y = event.clientY;\n                }\n                if (event.type !== \"mouseup\" && hasParentClass(event.target, \"block\")) {\n                    if (event.which != 3) {\n                        if (!active && !rearrange) {\n                            dragblock = true;\n                            drag = theblock;\n                            dragx = mouse_x - (drag.getBoundingClientRect().left + window.scrollX);\n                            dragy = mouse_y - (drag.getBoundingClientRect().top + window.scrollY);\n                        }\n                    }\n                }\n            }\n        }\n\n        function hasParentClass(element, classname) {\n            if (element.className) {\n                if (element.className.split(' ').indexOf(classname) >= 0) return true;\n            }\n            return element.parentNode && hasParentClass(element.parentNode, classname);\n        }\n\n        flowy.moveBlock = function(event) {\n            if (event.targetTouches) {\n                mouse_x = event.targetTouches[0].clientX;\n                mouse_y = event.targetTouches[0].clientY;\n            } else {\n                mouse_x = event.clientX;\n                mouse_y = event.clientY;\n            }\n            if (dragblock) {\n                rearrange = true;\n                drag.classList.add(\"dragging\");\n                var blockid = parseInt(drag.querySelector(\".blockid\").value);\n                prevblock = blocks.filter(a => a.id == blockid)[0].parent;\n                blockstemp.push(blocks.filter(a => a.id == blockid)[0]);\n                blocks = blocks.filter(function(e) {\n                    return e.id != blockid\n                });\n                if (blockid != 0) {\n                    document.querySelector(\".arrowid[value='\" + blockid + \"']\").parentNode.remove();\n                }\n                var layer = blocks.filter(a => a.parent == blockid);\n                var flag = false;\n                var foundids = [];\n                var allids = [];\n                while (!flag) {\n                    for (var i = 0; i < layer.length; i++) {\n                        if (layer[i] != blockid) {\n                            blockstemp.push(blocks.filter(a => a.id == layer[i].id)[0]);\n                            const blockParent = document.querySelector(\".blockid[value='\" + layer[i].id + \"']\").parentNode;\n                            const arrowParent = document.querySelector(\".arrowid[value='\" + layer[i].id + \"']\").parentNode;\n                            blockParent.style.left = (blockParent.getBoundingClientRect().left + window.scrollX) - (drag.getBoundingClientRect().left + window.scrollX) + \"px\";\n                            blockParent.style.top = (blockParent.getBoundingClientRect().top + window.scrollY) - (drag.getBoundingClientRect().top + window.scrollY) + \"px\";\n                            arrowParent.style.left = (arrowParent.getBoundingClientRect().left + window.scrollX) - (drag.getBoundingClientRect().left + window.scrollX) + \"px\";\n                            arrowParent.style.top = (arrowParent.getBoundingClientRect().top + window.scrollY) - (drag.getBoundingClientRect().top + window.scrollY) + \"px\";\n                            drag.appendChild(blockParent);\n                            drag.appendChild(arrowParent);\n                            foundids.push(layer[i].id);\n                            allids.push(layer[i].id);\n                        }\n                    }\n                    if (foundids.length == 0) {\n                        flag = true;\n                    } else {\n                        layer = blocks.filter(a => foundids.includes(a.parent));\n                        foundids = [];\n                    }\n                }\n                for (var i = 0; i < blocks.filter(a => a.parent == blockid).length; i++) {\n                    var blocknumber = blocks.filter(a => a.parent == blockid)[i];\n                    blocks = blocks.filter(function(e) {\n                        return e.id != blocknumber\n                    });\n                }\n                for (var i = 0; i < allids.length; i++) {\n                    var blocknumber = allids[i];\n                    blocks = blocks.filter(function(e) {\n                        return e.id != blocknumber\n                    });\n                }\n                if (blocks.length > 1) {\n                    rearrangeMe();\n                }\n                dragblock = false;\n            }\n            if (active) {\n                drag.style.left = mouse_x - dragx + \"px\";\n                drag.style.top = mouse_y - dragy + \"px\";\n            } else if (rearrange) {\n                drag.style.left = mouse_x - dragx - (window.scrollX + absx) + canvas_div.scrollLeft + \"px\";\n                drag.style.top = mouse_y - dragy - (window.scrollY + absy) + canvas_div.scrollTop + \"px\";\n                blockstemp.filter(a => a.id == parseInt(drag.querySelector(\".blockid\").value)).x = (drag.getBoundingClientRect().left + window.scrollX) + (parseInt(window.getComputedStyle(drag).width) / 2) + canvas_div.scrollLeft;\n                blockstemp.filter(a => a.id == parseInt(drag.querySelector(\".blockid\").value)).y = (drag.getBoundingClientRect().top + window.scrollY) + (parseInt(window.getComputedStyle(drag).height) / 2) + canvas_div.scrollTop;\n            }\n            if (active || rearrange) {\n                if (mouse_x > canvas_div.getBoundingClientRect().width + canvas_div.getBoundingClientRect().left - 10 && mouse_x < canvas_div.getBoundingClientRect().width + canvas_div.getBoundingClientRect().left + 10) {\n                    canvas_div.scrollLeft += 10;\n                } else if (mouse_x < canvas_div.getBoundingClientRect().left + 10 && mouse_x > canvas_div.getBoundingClientRect().left - 10) {\n                    canvas_div.scrollLeft -= 10;\n                } else if (mouse_y > canvas_div.getBoundingClientRect().height + canvas_div.getBoundingClientRect().top - 10 && mouse_y < canvas_div.getBoundingClientRect().height + canvas_div.getBoundingClientRect().top + 10) {\n                    canvas_div.scrollTop += 10;\n                } else if (mouse_y < canvas_div.getBoundingClientRect().top + 10 && mouse_y > canvas_div.getBoundingClientRect().top - 10) {\n                    canvas_div.scrollLeft -= 10;\n                }\n                var xpos = (drag.getBoundingClientRect().left + window.scrollX) + (parseInt(window.getComputedStyle(drag).width) / 2) + canvas_div.scrollLeft - canvas_div.getBoundingClientRect().left;\n                var ypos = (drag.getBoundingClientRect().top + window.scrollY) + canvas_div.scrollTop - canvas_div.getBoundingClientRect().top;\n                var blocko = blocks.map(a => a.id);\n                for (var i = 0; i < blocks.length; i++) {\n                    if (checkAttach(blocko[i])) {\n                        document.querySelector(\".blockid[value='\" + blocko[i] + \"']\").parentNode.appendChild(document.querySelector(\".indicator\"));\n                        document.querySelector(\".indicator\").style.left = (document.querySelector(\".blockid[value='\" + blocko[i] + \"']\").parentNode.offsetWidth / 2) - 5 + \"px\";\n                        document.querySelector(\".indicator\").style.top = document.querySelector(\".blockid[value='\" + blocko[i] + \"']\").parentNode.offsetHeight + \"px\";\n                        document.querySelector(\".indicator\").classList.remove(\"invisible\");\n                        break;\n                    } else if (i == blocks.length - 1) {\n                        if (!document.querySelector(\".indicator\").classList.contains(\"invisible\")) {\n                            document.querySelector(\".indicator\").classList.add(\"invisible\");\n                        }\n                    }\n                }\n            }\n        }\n\n        function checkOffset() {\n            offsetleft = blocks.map(a => a.x);\n            var widths = blocks.map(a => a.width);\n            var mathmin = offsetleft.map(function(item, index) {\n                return item - (widths[index] / 2);\n            })\n            offsetleft = Math.min.apply(Math, mathmin);\n            if (offsetleft < (canvas_div.getBoundingClientRect().left + window.scrollX - absx)) {\n                var blocko = blocks.map(a => a.id);\n                for (var w = 0; w < blocks.length; w++) {\n                    document.querySelector(\".blockid[value='\" + blocks.filter(a => a.id == blocko[w])[0].id + \"']\").parentNode.style.left = blocks.filter(a => a.id == blocko[w])[0].x - (blocks.filter(a => a.id == blocko[w])[0].width / 2) - offsetleft + canvas_div.getBoundingClientRect().left - absx + 20 + \"px\";\n                    if (blocks.filter(a => a.id == blocko[w])[0].parent != -1) {\n                        var arrowblock = blocks.filter(a => a.id == blocko[w])[0];\n                        var arrowx = arrowblock.x - blocks.filter(a => a.id == blocks.filter(a => a.id == blocko[w])[0].parent)[0].x;\n                        if (arrowx < 0) {\n                            document.querySelector('.arrowid[value=\"' + blocko[w] + '\"]').parentNode.style.left = (arrowblock.x - offsetleft + 20 - 5) + canvas_div.getBoundingClientRect().left - absx + \"px\";\n                        } else {\n                            document.querySelector('.arrowid[value=\"' + blocko[w] + '\"]').parentNode.style.left = blocks.filter(id => id.id == blocks.filter(a => a.id == blocko[w])[0].parent)[0].x - 20 - offsetleft + canvas_div.getBoundingClientRect().left - absx + 20 + \"px\";\n                        }\n                    }\n                }\n                for (var w = 0; w < blocks.length; w++) {\n                    blocks[w].x = (document.querySelector(\".blockid[value='\" + blocks[w].id + \"']\").parentNode.getBoundingClientRect().left + window.scrollX) + (canvas_div.scrollLeft) + (parseInt(window.getComputedStyle(document.querySelector(\".blockid[value='\" + blocks[w].id + \"']\").parentNode).width) / 2) - 20 - canvas_div.getBoundingClientRect().left;\n                }\n            }\n        }\n\n        function rearrangeMe() {\n            var result = blocks.map(a => a.parent);\n            for (var z = 0; z < result.length; z++) {\n                if (result[z] == -1) {\n                    z++;\n                }\n                var totalwidth = 0;\n                var totalremove = 0;\n                var maxheight = 0;\n                for (var w = 0; w < blocks.filter(id => id.parent == result[z]).length; w++) {\n                    var children = blocks.filter(id => id.parent == result[z])[w];\n                    if (blocks.filter(id => id.parent == children.id).length == 0) {\n                        children.childwidth = 0;\n                    }\n                    if (children.childwidth > children.width) {\n                        if (w == blocks.filter(id => id.parent == result[z]).length - 1) {\n                            totalwidth += children.childwidth;\n                        } else {\n                            totalwidth += children.childwidth + paddingx;\n                        }\n                    } else {\n                        if (w == blocks.filter(id => id.parent == result[z]).length - 1) {\n                            totalwidth += children.width;\n                        } else {\n                            totalwidth += children.width + paddingx;\n                        }\n                    }\n                }\n                if (result[z] != -1) {\n                    blocks.filter(a => a.id == result[z])[0].childwidth = totalwidth;\n                }\n                for (var w = 0; w < blocks.filter(id => id.parent == result[z]).length; w++) {\n                    var children = blocks.filter(id => id.parent == result[z])[w];\n                    const r_block = document.querySelector(\".blockid[value='\" + children.id + \"']\").parentNode;\n                    const r_array = blocks.filter(id => id.id == result[z]);\n                    r_block.style.top = r_array.y + paddingy + canvas_div.getBoundingClientRect().top - absy + \"px\";\n                    r_array.y = r_array.y + paddingy;\n                    if (children.childwidth > children.width) {\n                        r_block.style.left = r_array[0].x - (totalwidth / 2) + totalremove + (children.childwidth / 2) - (children.width / 2) - (absx + window.scrollX) + canvas_div.getBoundingClientRect().left + \"px\";\n                        children.x = r_array[0].x - (totalwidth / 2) + totalremove + (children.childwidth / 2);\n                        totalremove += children.childwidth + paddingx;\n                    } else {\n                        r_block.style.left = r_array[0].x - (totalwidth / 2) + totalremove - (absx + window.scrollX) + canvas_div.getBoundingClientRect().left + \"px\";\n                        children.x = r_array[0].x - (totalwidth / 2) + totalremove + (children.width / 2);\n                        totalremove += children.width + paddingx;\n                    }\n\n                    var arrowblock = blocks.filter(a => a.id == children.id)[0];\n                    var arrowx = arrowblock.x - blocks.filter(a => a.id == children.parent)[0].x + 20;\n                    var arrowy = paddingy;\n                    updateArrow(arrowblock, arrowx, arrowy, children);\n                }\n            }\n        }\n        \n        document.addEventListener(\"mousedown\", flowy.beginDrag);\n        document.addEventListener(\"mousedown\", touchblock, false);\n        document.addEventListener(\"touchstart\", flowy.beginDrag);\n        document.addEventListener(\"touchstart\", touchblock, false);\n        \n\n        document.addEventListener(\"mouseup\", touchblock, false);\n        document.addEventListener(\"mousemove\", flowy.moveBlock, false);\n        document.addEventListener(\"touchmove\", flowy.moveBlock, false);\n\n        document.addEventListener(\"mouseup\", flowy.endDrag, false);\n        document.addEventListener(\"touchend\", flowy.endDrag, false);\n    }\n\n    function blockGrabbed(block) {\n        grab(block);\n    }\n\n    function blockReleased() {\n        release();\n    }\n\n    function blockSnap(drag, first, parent) {\n        return snapping(drag, first, parent);\n    }\n\n    function beforeDelete(drag, parent) {\n        return rearrange(drag, parent);\n    }\n\n    function addEventListenerMulti(type, listener, capture, selector) {\n        var nodes = document.querySelectorAll(selector);\n        for (var i = 0; i < nodes.length; i++) {\n            nodes[i].addEventListener(type, listener, capture);\n        }\n    }\n\n    function removeEventListenerMulti(type, listener, capture, selector) {\n        var nodes = document.querySelectorAll(selector);\n        for (var i = 0; i < nodes.length; i++) {\n            nodes[i].removeEventListener(type, listener, capture);\n        }\n    }\n    \n    flowy.load();\n}\n"
  }
]