Phase 1N
This commit is contained in:
parent
39c72c3b38
commit
cb7405c0e8
@ -1,5 +0,0 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpSUT2/cNhDFv4rAsyjzP0XdDLtFF27qRbxog94ozigWoqUWFLVpYOx3L6jdInFvuQkDveHjb97wjUR/RNKR+9PplzPGvJCa5HErCSYMZYYyfmBtp3SnVaM1Z8aIv0lNxif8RjpiVQg8OKQSDVIFTNNeDoECoOOMAxNalZ7+80K6N+LHBvA8Bmzm5U9MyzhH0pG/xgjz13K2H5swzSs0aZ5wF5fsYyhm/tg9PNHf7/eH5/31rwWXIm5GIB1BUINzwdEh9J6qngvaKmkoE0y5oHpttbvKxpgxRT81C3z5fj7MOWIOnWiEbBgVjlxqAj77Yrn3Cx6+nYqLDdFjqddbefvs3sgZE+lE/R/Ma7u7MI13y9pPeMbp5NOC6S7Mx6OPQGpySvMJUx5xw3LG1M/LmAvREKxvW86h9VZpC63UQlqQDKFlVoMD540ygwhaD9C3zqpBgeAKDCrVW1KTDdw0+TzOsbp5f5hGjJnU5Pml2k8+D3M6vkP/ciW6+wmgxTbpiGRaGmtRSSm9M71gCNYEY0QfPLNDL5AHZNop67hlXDrBnNTGthY8c4HU5IMPr2PEavdIOsI4CpAt0kFwQ1ULnnotHTVGmgF5cFZwUpOHOeYxrvO6VLuY8XPa7ks68qufFrze9D6F1zFjyGsqED6ZEsZ9mmENufoeAM4a1kjGimjNpzVXHxHGhCFjwXFI663f/yTCiE30sKaEMVd7n1+r3/zySjrifBu4d955b0IYUDMcGDBvmAU0YBCUAAvKKTDKsWCU77kCr3k/lAqpCZa8VVvEpfZMDcFSBBRUSSGoM85Q21vVS66Z0/07jtXzVHQ+tEwhSOpwAKoc99QpKSlI7a0wg7Vi+AHJLSycFcCA5+uybg4MQItgOBWKa6qc47R12tLetr3rTYs6FM9PmCJOP4D6MIY0L/OQq1vWqnfoPq6xPDjVlruvY6T/bDN6nMMXTFWZsR9j2a/bWC81OaJf1oTH7b3q3si+LFd12J4toRvWaleTl+xTXk+3sjRNy5W6XC6XfwEAAP//AwBRE7MW9AQAAA==
|
||||
@ -1,5 +0,0 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpRTTW/cNhD9K8KcRZmkKErUzVin6MJNvEgWadEbxRnFhGVqQVFOAmP/e0DtBk5664UgBnwz72P4CsE+E/Rwezq9e6GQFigh+a0kudSMa8bFkXe9avqmqVrdSNmZf6EEf0/foYdWOSecIVaTJqaQN2yoR8cQyQgukMtG5Z72ywL9K1hfIb14R9W8fKa4+DlAD3/7gPPXPNv6yk3zilWcJ9qHJdngMpkP+909++v2cHw4XF4ttGRw5RF6IFSjMc6w0Q2WqUFI1qlaMy65Mk4NTduYC8yHRDHYqVrw6W0+zilQcr2sZF1xJg2cS0CbbKY82IWO30+ZxWbRXa6XW3m79q/wQhF6Wf4089Luxk3+5nkZVj/hzeVMdnnKKk9xPlFMnjZPNpnTZJOfQ3GdtJs8hQQlPHwqDpNN4xyffzPq00X//n/IP+bp776RWxPhbl5Dgh6EqqGE99Y9+kDF/g564IIk1h2xUQrNVIeW2aY2TOtajyScaaWAEnZzSD6s87oU+5DoS9wEQA9/2GmhC/Xb6B59IpfWmFX9o/MuHOKMq0vFm/+CV7yqOc+gNZ3WVHwk9JFcoqzvGNdrv/9ApJYbaLfGSCEVB5seiz/t8gg9GNs5YY011mrnRmo4jRy51bxF0qgJlcQWlVGoleFOKzsIhbYRw5grUALluIttw4xsbK1oYLWqiSmjLRsai2xUynU06lFhpvjmY/EwZZx1HVeENTM0IlNGWGZUXTOsG9tKPbatHH+x5Jq+4NlgpJfLX9kYaMSOUAsmlWiYMkawzjQtG9puMIPuqHGZ8z3FQNMvRr33Ls7LPKbiujzFb9Z9XEP+78W2SF99YN+2jO5m90SxyBlbH/J6X2M9n8/nHwAAAP//AwCu1fIaNgQAAA==
|
||||
@ -1,5 +0,0 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpSUUW/cKBDHv4o1z8YBDNj4Ldq0ulWul1W76lV9wzBuUBy8Apw2ivLdT3j3lObpdC8IjZnhP7/54xcI5hFhgOvT6cMThpyghuy3EKdcEaoIZUfaD0IOUjadarns6Heowd/iMwzQCWuZ1UhaVEiEo5KM7WSJc6gZZY5yKUpN8yPB8ALGNw6fvMVmSV8xJr8EGOBvH9zys9xtfGPnZXVNXGbch5RNsEXMX/vdLfnz+nC8O5xPJUwlufEOBkAnJq2tJpMdDREj46QXrSKUU6GtGGUn9TnNh4wxmLlJ7uHtfrfkgNkOvOFtQwnX8FqDM9kUyaNJeHw+FRUbopsSr7fwth1e4AkjDLz+F+a53JWd/dVjGlc/u6vzmk16SNs6GZuX+Aw1nOJywpg9bnyuU8LHcX4+mvTw8XymbNOHX2jXjG63rCHDAExQqGEDNM8m+yVUF4272WPI28ccfUje/letFmq4+1IdZpOnJT6+m8eXM+b9/6D8ydh7H7Da38AAlCF3bY9k4kwR0TtDjGw1UapVEzKrO86ght0Ssg/rsqZqHzL+iFtLMMBHMyc867uO9t5ntHmNpc9vqvjqEBe32ly9zZLRhjYtLXju1nxac/UZnY9oM5YmjnG91Pv6PoUrviXt1hgx5Opg8n31h0n3MIA2vWVGG22MsnZCSXGijhpFO4fKKXSCu84JLZwSmlolzMiEM5KNU4lADVisU13cagRDKoidOBIhu5b0rZBEIleCtd2IujB541jdzSXP2J4KdC3RODkiNDNEi7YlrpWm42rqOj79huTiB0ZLMYdP53e3KVDO9egUI1wwSYTWjPRadmTs+lGPqkdpi+ZbjAHn30B98jYuaZlydXFI9Q7d5zWUf0e1ueWnD+TXNqObxT5grMqMjQ/lqVzG+vr6+voPAAAA//8DAEU35m+CBAAA
|
||||
@ -1,5 +0,0 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpRTwY7cKBD9FYuz8QDG2Pg26tlVWpPstJJREuWGqSKN4oEWxpNEo/73Fe6OJtnb3tCDV/XqveKFBPOEZCS3p9NfzxjyQmqS/QYJJhRlijL+yIZRdmPXNb3qpeT8C6mJv8efZCS9tJZbjbRFhVQC6+jUOksBUHPGgYlOlprm60LGF2J8A/jsLTZx+Yhp8TGQkXzyAeL30tv4xs5xhSbFGfdhySbYIuaf/e6evr09PD4cLq8WXAq58UBGgiCd1lZTZydD5cQFHWSrKBNMaiunru/0heZDxhTM3Czw7bU/xBww21E0om0YFZqcawImmyJ5Mgs+/jwVFZtFdwWvN3g7ji/kGRMZRf3LzEu5Gzv7GxufnkyAG+eDX46kJqcUT5iyx82ObcJ5NtnHUF2b7GaPIZOaPHyoDrPJLqanPzz6cBl9/z8mf2fs0Qes9ndkJIyjgHZA6gRXVA5gqOlaTZVqlUNudS84qckuhuzDGtel2oeMX9OmkozkbzMveNF3m+zRZ7R5TUX6Z1Wyxh8+7yIUgJGaHFKE1ebq1W7OGta0rFw+rPm05uo9gk9oM5aZHtN6Lf8filBiI+3WlDDk6mDysXpjliMZiTaD5UYbbYyy1mHH0DFgRrEeUIFCkAJ6kFqCkppZJc3EJZiOT64gRXdJt9oWSnVMOdVJ2pvWUolOUdNDT7mQEzrVT0wXi15trR7mwjN2YBKhpRodUKm5oVq2LYW2M71Qru+F+82Sa+KclWKAz5evcVEAMCAoToXkHZVaczrorqdTP0x6UgN2tmi+xxRw/s2od96muESXq+vCVH9Y934N5XtX2/J894H+2CK7i/YbpqpEbnwo23xN+Xw+n/8FAAD//wMAC1T6KCUEAAA=
|
||||
@ -1,5 +0,0 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACoxUTW/jNhD9KwKvFRWSoihRt9TJokaaxFgbbdHbiBzGbGTKoKhkF0H+eyE52XwsCvQiCENy3ps3b+aJBDggacn58Xj5gCGNJCfJLyHBhKJMUcZ3rGll1VZVUau6akr+N8mJv8LvpCW1NIYbjbREhVRaVtGudIZai5ozbpmo5JwT7kbSPhHwhcUHb7AYxj8wjn4IpCV/+mCHxxkbfGH6YbJFHHpchzFBMDOZm/Xqiv5+vtndbk63Rhznx4W3pCVopdPaaOpMB1R2XNBGlooywaQ2sqvqSp+e+ZAwBuiL0d6/4dshBUymFYUoC0aFJs85sZBgptzBiLvvx5nFItHFHM+X8PLbPpEHjKQV+auYp3Rnpvdnh7GbfG/Pli/JyTEOR4zJ46LG7TZ7I8FZwQqhBGMkJ9dg9j5gtr7Ibvu5RDANk2hLqtFZKjUHqmVZUltWUAvl6lo4kpMrjAH7d1mvvYnDOLiUvYicfcDZnq8uA3Q9zhhfoB+R5GQ9bhMEC/0Q8PIbmimdcu3iNB+vhpB8mIZpzNYh4V2El/PX96spRgwp20DaZ7/BuCct0dAYDho0gDLGYcXQMctAsdqisgqtFLa2UkurpGZGSei4tFDxzs0RkpNfZw23kzE4jm9sFpP0/cIhe+nTqvcY0gcZSUsYR2HLBqkTXFHZWKBQlZoqVSqH3Oha8FeYHcQ7TKQlrum6jhnTCam5AaGhtI6VVjktRF2DdGVVS6FUxWowtq4Fl7as0UDpStYIkpNNHOxk0udWl0sDNnH4B02apSItUayqGgNdJ5TkDegGJVeyURprLR0wWZoOeNVA13EGFTO6EQa40iA7W7kfKl2GOx/wwo/HHr6/w20KVZS/GICGO5Cfrn+JcMDHId7fnGxc3FzuSE6+TmHeCNl6tsijD/Sbmif6euqT3+0jgkV7PVj82Uc4j0u2TKjmtdVOM4qWSyqbzlGw3FEHElinWKNxtu/FYO4xZrPDwId5qn4ku91m59HsfUKTpjjz+2vhcbvNNj0kN8TDh03yrrDPAhSi5GzujMWH0zZaOCprG7SKUyF5RaXWnDa6qmlXN53uVIOVmRW7ndJxStlXtD6iSUvFL15cMFd7NPc/i/HqgheLcjZ7bXvaYou0/2uJPefkgDBOEQ/Ltm6fyDoEjAvyxXQaxXW49n3vRzRDsCNpOde8aGRdvjD8z3tClkWtuHh+fn7+FwAA//8DANjfCysbBgAA
|
||||
@ -1,5 +0,0 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpSU227jNhCGX0XgtanwJOpwlzpd1MhmE2zcbdG7ETlyiMikQVHZBoHffUHZQZJd9KJ3woj/HL75yRfiYY+kI5eHw+9P6NNEViS5JSSY0JRpyviWNZ2qOylKqWvesvofsiLuGp9JR2plDDctUokaqbKsor0cDLUWW864ZaJSOSfsJtK9EHClxSdnsAzTN4yTC5505C/nbfiea4MrzRhmW8Yw4sZPCbzJzXzZrK/p58u77e3d6dSEUxaXzpKONLW0gI2ifdNrqoSwFKpaUqWhByt4y8RwkjmfMHoYy8k+vtW3IXlMphOlkCWjoiXHFbGQILfcw4Tb50PuYkF0leOrJbx8di/kCSPpxOoV5indhRndxX7qZzfaizHsds7v1sEPbjdHSLnwihxiOGBMDhc4vzkP8flz2O1yPvIJxgnJimwx7p2H8fTjCgeYx3Qf5riQmezjf50hHYE5BbIiC8lxXMoW52HWo0OfftG+K3x7X9yNkIYQ9x+WdH9iv/kf6G/APDiPxeaKdIRxFFY2SAfBNVWNBQqVbKnWUg/ITVsLTlZkHXxyfg7zVGx8wt2Z2of+LqN5cAlNmmOe6W+dzXYXg51NKt4WzFnJSslYFs3pMKfiK1oX0STMQ2zjfM73k0RosYg+uRF/wbOeY0SfijtID8UfMD2QjrTQGA4ttADamAErhgOzDDSrLWqr0Spha6taZbVqmdEKeq4sVLwfcoSsCGaTFYuveyWlRcGoZBapQq5oLxqgrK4VykHLumo+wC1ux6wD0zCFVtIWB0tVy4G2SkpqZQW10ENdL0t55XQ2BGeZ+nsT/jmhPXvpy8nar7NbfDrd46VPbW2DVnMqFK+oaltOm7aqaV83fdvrBiuTJ1sHP4VXkN8w9mFyKT8iN867PYw/H3nbzDVGj+O77dw4E8MUhlScbVl82NfX2edXrFgs+t15+u9ijKtgHjEW2Vjg/LtlHo/H4w8AAAD//wMAtEuIEAwFAAA=
|
||||
@ -1,5 +0,0 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACoxUTW/bOBD9KwLPpkJSFCUK2IPhtFgjTWMk7u5ibxQ5jInIokFRToMi/31BWa6dooe9CaP5eO/NG/5AvdoDatDycPh0hD4OaIGim0KMMIGJwIRuSd3wqilYXknJKRH/ogVyd/CGGlRxramWgAsQgLkhJW4Lq7ExICmhhrCSp57qeUDND6RcbuDoNOR++AvC4HyPGvS3641/TbOVy3XnR5MH38G6H6LqdQLzdb26w1+Wm+3D5pQ1wJCKc2dQg+qqMApqjtu6FZgzZrAqqwJzoVplGJWE2VOZ6yOEXnX5YF4u842PPUTdsJwVOcFMovcFMiqqBLlVA2zfDgnFJNFtii+m8PTZ/EBHCKhhi7OYp3Y3unM3+6EdXWduogrPEG1Qe3j14QWOqkMLdAj+ACE6mLR5eMoukCjJSc4EIwQt0L3SO9dDtr7NHrpEWOmacDAFlmAN5pIqLHlRYFOUqmLCVtVE+A5CD91V13ungx+8jdksefZhzsr30fWjH4ds3Ud4Diqe6j6rbgC0QJux7dywewRl3rb+cUz/+rFLXFZjCNDHbKPiLvtTDTvUIKlqTZVUUimhtYWSgCWGKEEqA8IIMJyZynDJjeCSaMFVS7lRJW1tiqAFehjjYYxn/b9fgdgGt9+DuSDYThpvOhWtD/u1gT4669JqzhmTobpuYpXNPVedgz5+EBk1iFBgpqgBW0YF5rVRWJWFxEIUwgLVsmL0JJh1z+NPnYi1hhe2rGvRypYSYLaqOalsS4y2hZambKFiFalqqKUmpC05rVupiQFacpPYBW9GHX+1QjEt6HHs03Vm60T71fX4u+AXRTbBR699lxbmOnh6GyLsf7e1ld8f/OBi4m/n1X4b4N736e98/7Nmc/HSx0sM0h1k0+kVlNu2rUosJOeYU8lxq2uNZWEtr4RlYBLwW69fIGTJYMr1007Onnp4ypZB71wEHceQJv8zkTqt8/P5ZC565F8/bVc+wPJwWMzRP45JpFOzswE+vCvfhpS+80O8ojxTe4LOnoFd2cnA8fRUuRQUxtRgBMWM0xJzKSmuZVnhtqpb2YoaSn2xa/YIxgXQceq3DWOaNiv7O18uQ3RW6Tik4/ni9WSn2Z9zyq8gr9b2s/o0/Gp1s5XmTpQkyz6dHs7JQf/r3Xx/f3//DwAA//8DAMe2UmsoBgAA
|
||||
@ -1,5 +0,0 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpSUTW/cNhCG/4rAsyjzS/zQzbBb1HVTL+JFWvRGcka2EK20oKhNA2P/e0DtBol7600Y6J0ZPvPOvJHJH5B05PZ4/OWEU15ITfKwhQQTmjJNGd8z2ynTSdEIyxxX5h9Sk+ERv5KOGBUjjw6pRI1UAWtpkH2kAOg448BEq0pO/7KQ7o34oQE8DRGbefmEaRnmiXTkr2GC+Uup7YcmjvMKTZpHfJiW7KdYmvnz4e6R/nG72z/tLn8tuBRxMwDpiDUSPFpFgw2aKiGA+tZIqrQPHgR3TPQX2TBlTJMfmwU+/6gPc54wx040QjaMCkfONQGffWk5+AX3X4+liw3RfYnXW3j77N7ICRPpRP0d5iXdTRyHmzwfRzzhePRpwXQT58PBT0BqckzzEVMecMOyvXQcfR7mqboWuxsHnDKpydNztRt97ud0eMfq+YLg4X8QOGEKpCOStVIbg0pK6Z0OgiEYHbUWIXpm+iCQR2StU8Zxw7h0gjnZamMNeOYiqcnLOAc//r5s/JxgwQIzQgtU6ITS2kWG0mLfGmTgtQ7aRgfSGqEsWOmd99wFNNHpWAzywcfXYcLq4Z50hHEUIC3SXnBNlQVPfSsd1VrqHnl0RnBSk7t5ysO0zutSPUwZX9LGj3TkVz8ueCF3m+LrkDHmNRWof+tSbJdmWGOufjiAs4Y1krEiWvNxzdVHhCFhzFjw7tN6zfcfidBiE92tKeGUq53Pr9VvfnktVLyN3LvyVB1jjy3DngHzmhlADRpBCTCgnAKtHIta+cAV+JaHvkRITbAYrto83sfohGWaKrSBKgOKBiE5FcJpw4NWCt5zrJ7GovPRMoUgqcMeqHLcU6ekpCBbb4Tujdmc8R3J1XycFcCAp8u2bh1oAIugORWKt1Q5x6l1raHB2OCCttjG0vMjpgnHn0B9GGKal7nP1dW71Tt0H9epXJxq8/GXYaL/bjO6n+NnTFWZsR+msmDXsZ5rckC/rAkP28Hq3siubFe13+6WMI2z2tXkOfuU1+M1zKVuTGvl+Xw+fwMAAP//AwDUFLHF9gQAAA==
|
||||
@ -1,5 +0,0 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpSUW2/cNhCF/4rAZ1HmTbzozbBb1HDTLGKjLfo24oxiIVppQVGbBob/e0HtBqn71jdhoDM8/OYMX9kMR2Iduz2dfjrTnFdWszzuJSWU5cJyIZ+F74zrtGq08qG14i9Ws/GRvrGOOROjjIG4JkvcoGh5r4fIESlIIVGo1pSe8Hll3SuDsUE6j5GaZf2d0jouM+vYH+OMy9dyNoxNnJYNm7RM9DCvGeZYzPz2cPfIf709PH88XP5aaS3iZkTWMe80AnnDe99bbpRCDq3T3FjoAZUMQg0X2ThnSjNMzYpffpyPS54px041SjeCq8DeaoaQoVjuYaXnb6fiYkd0X+r1Xt4/u1d2psQ6VX+HeWl3E6fxZt36ic40nSCtlG7icjzCjKxmp7ScKOWRdixnSv2yjrkQjdGB91KiB2dah163SjvUgtAL12LAANbYQcW2HbD3wZnBoJIGLRnTO1azHdw0QR6Xubp6v5tGmjOr2cen6jBBHpZ0fIf+6UL04X8ALbZZx7RotXWOjNYagu2VIHQ2Wqv6CMINvSIZSbTBuCCdkDooEXRrnXcIIkRWsw8QX8aZqod71jEhSaH2xAclLTcegUOrA7dW24FkDE5JVrO7Zc7jvC3bWj3MmT6n/b6sYz/DtNLlprcpvoyZYt5SgfCnLWE8pAW3mKsfAZCiEY0Wooi2fNpy9YlwTBQzFRzPabv2+49EWbWL7raUaM7VAfJL9QusL6xjAXyUECAA2BgHagUNAgVY4ZAsWkKj0KEJBq0JIloDvTQIreyHUmE1o5K3ao+4HNpB+0FzFbXgBiJwsBi4UUbqqLXS5j3H6uNUdBC9MISaBxqQmyCBB6M1R92CU3Zwbp/kdyTXsEhRACOdL8u6O7CIntBKroxsuQlBch9ax3vn+9BbT20snh8pzTT9C9SHMaZlXYZcXbNWvUP3aZvLg1Ptufs6zvzvfUb3S/xCqSozhnEu+3Ud61vNjgTrlui4v1fdKzuU5aqe92dLuSZ4G2r2lCHl7XQtS20b13r99vb29g8AAAD//wMA0nzqzvUEAAA=
|
||||
@ -1,5 +0,0 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpRTTW/cNhD9KwLPokxSFCXqZqxTdOEmXiSLtOhtxBnFhGVqQVFOAmP/e0DtBol764UgBnwz72P4ygI8E+vZ7en07oVCWljJkt9KSijDheFCHkXX67avu8p0VraN+JeVzN/Td9azVjsnnSVekyGuUTR8qEfHEclKIVGoRuee8GVh/SsDXyG9eEfVvHymuPg5sJ797QPOX/Ns8JWb5hWrOE+0D0uC4DKZD/vdPf/r9nB8OFxeLbRkcOWR9axrawTqNB+6wXCtFHJo2pprAwOgklao8QLzIVEMMFULPv2aj3MKlFyvKlVXgivLziVDSJApD7DQ8fsps9gsusv1citv1/6VvVBkvSp/mnlpd+Mmf/O8DKuf8OZyJliesspTnE8Uk6fNk03mNEHycyiuk3aTp5BYyR4+FYcJ0jjH5zdGfbro3/8P+cc8/d03cmsi3M1rSKxnsrasZO/BPfpAxf6O9UxIUlh3xEclDdcdAoemttyY2owknW2VZCXbzSH5sM7rUuxDoi9xE8B69gdMC12o30b36BO5tMas6h+Td+EQZ1xdKn75L0UlqlqIDFrTaU3FR0IfySXK+o5xvfb7D0QZtYF2a4wUUnGA9Fj8Ccsj65mFzkmwYAGMcyM1gkaBAoxokQwaQq2wRW01Gm2FMxoGqREaOYy5wkpGOe5i2zDCwSllJBdtbbkGZXmn5MiNpRFNI5ua8I2PxcOUceA6oQlrnt9xbSVwq+uaY91Aq8zYtls0Py25pi9FNhjp5fJXNgYGsSM0kistG66tlbyzTcuHthvsYDpqXOZ8TzHQ9JtR772L8zKPqbguT/HGuo9ryP+92Bbpqw/825bR3eyeKBY5Y/Ahr/c11vP5fP4BAAD//wMAgHCTeTYEAAA=
|
||||
@ -1,5 +0,0 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACoxUXU/sNhD9K5Ffuw7+imPnjS5cdUWB1d1VW/VtYk/AJTgrx4F7hfjvVbJw+biq1JcoGttzzpw5M08kwj2ShpweDucPGPNIViSHJSSY0JRpyviemUbVjTSltoLVdfU3WZFwgd9JQ2rlHHcWqUSNVHlW0VZ2jnqPljPumajUnBNuRtI8EQilx4fgsBzGPzCNYYikIX+G6IfHGRtC6fph8mUaetzEMUN0M5mrzfqC/n663V9vj7dGHOfHZfCkIaaWHtAo2ppWUyWEp1DVkioNLXjBLRPd8VmIGVOEvhz93Ru+H3LE7BpRClkyKix5XhEPGWbKLYy4/36YWSwSnc3x1RJefpsn8oCJNGL1KuYx3Ynrw8n92E6h9yfLl6zIIQ0HTDngosb1rngjwVnJSqEFY2RFLsHdhojF5qy47ucSwRmm0EtqsfNUWQ7UKimplxXUQnd1vZR4gSli/y7rZXBpGIcuFy8iFx9wdqfr8whtjzPGF+hHJCuyGXcZood+iHj+Dd2Uj7n2aZqP10PMIU7DNBabmPEmwcv56/v1lBLGXGwh3xa/wXhLGmLBOA4WLIB2rsOKYcc8A81qj9pr9Er42iurvFaWOa2g5cpDxdtujpAV+XXWcDc5h+P4xmYxSd8vHIqXPq37gDF/kJE0hHEUXhqkneCaKuOBQiUt1VrqDrmzteCvMHtIN5hJQzrTti1zrhXKcgfCgvQdk153Voi6BtXJqlZC64rV4HxdC668rNGB7CQzgqzINg1+cvlzq+XSgG0a/kGXZ6lIQzSrKuOgbYVW3IA1qLhWRlusreqAKela4JWBtuUMKuasEQ64tqBaX3U/VDqPNyHiWRgPPXx/h2tKXcpfHIDhHahP178kuMfHId1dHW1cXp3vyYp8neK8EYrNbJHHEOk3PU/05dTnsL9NCB795eDxZx/hPC7FMqHec88MdlR73lFVqZqCc5xK5ZWumKtaNrM5G9wdpmJ2GIQ4T9WPZNe74jS525DR5SnN/P5aeFzvim0PuRvS/YdN8q6wzwKUQnI2d8bjw3EbLRy19wa95lQoXlFlLafGVjVta9PaVhus3MzxesqHKRdf0YeELi8Vv3hxwVzforv7WYxXF7xYlLPZa7vjFluk/V9L7HlF7hHGKeH9sq2bJ7KJEdOCfDYdR3ETL0PfhxHdEP1IGi2lKYU19oXhf95TrCo1Y/L5+fn5XwAAAP//AwB4OEzzGwYAAA==
|
||||
@ -1,5 +0,0 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpSUwW7cIBCGX8Was3EAY2x8izaNukrbrNpVW/WGYZygOHgFOG0U7btXeLdKc6p6QWjMP/zzzeAX8PoRoYfLw+HdE/oUoYTk1hCnXBIqCWV72vWi7euukp1kinc/oAR3g8/QQyuMYUYhqVEiEZY2ZKhHQ6xFxSizlDci59R3EfoX0K6y+OQMVnP8iiG62UMP35y38898t3aVmebFVmGecOtj0t5kM5+2mxvy4XK3v92dTkWMWVw5Cz10bW01doIM3SCJ4NwS3bQ1EVIP2nKmKB9PMucTBq+nKtqH1/vtnDwm0/OK1xUlXMGxBKuTzpYHHXH/fMguVkRXOV6u4XXbv8ATBuh5+QfmKd2FmdzFYxwWN9mL05p0fIjrOmqT5vAMJRzCfMCQHK58LmPEx2F63uv4cH06k7fx3S80S0K7mRefoAdWSyhhBTRNOrnZF2ePm8mhT+vHFJyPzvwrVw0l3H4pdpNO4xwe3/Tjywnz9j8of9Tm3nkstlfQA2XIbd0hGTmTRHRWE93UikhZyxGZUS1nUMJm9sn5ZV5isfUJ78JaEvRwraeIJ3+Xwdy7hCYtIdf5Xea52oXZLiYVr71ktKJVTWkWLemwpOIzWhfQJMxF7MNyzvf1rYRLvoo2SwjoU7HT6b54r+M99KB0Z5hWWmktjRmxoThSS7WkrUVpJVrBbWuFElYKRY0UemDC6oYNY45ACZhHp1inVXE6WCUGIoUQRDSjJB1THWGiHhEZVYxmxSvH4nbKOm06KtDWROFoiVBMEyXqmti60S2XY9uu/P8gOc8DoxmwxafTu1sdSGs7tJIRLlhDhFKMdKppydB2gxpkh43JDm4weJz+AvXRmTDHeUzFeUKKN+g+Lz7/O4p1Wn46T36tPbqazQOGIvdYO5+fyrmtx+Px+BsAAP//AwBa5USZggQAAA==
|
||||
@ -1,5 +0,0 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpRTy27cOBD8FYFnUeZLlKibMd5FBk7Wg8RIFntrsVsZIjI1kCgngTH/vqBmAie55UYUWd3VVc0XFuGJWMduT6e/nimmhZUshQ1SQlkuLBfyUbSdaTrdVtZppZ3+j5Us3NN31rHGeC+9I67JEjcoat7rwXNEclJIFKo2uSZ8Xlj3wiBUSM/BUzUtH2lewhRZxz6FiNPX3BtC5cdpxWqeRtrHJUH0Wcw/+909f3t7eHw4XF4ttGRyFZB1rG00ArWG921vuVEKOdSN5sZCD6ikE2q40EJMNEcYqwW/vPbHKUVKvlOV0pXgyrFzyRASZMk9LPT4/ZRVbBbdZbzc4O3YvbBnmlmnyh9mXsrd+DHc+OnpCSLeDCGG5chKdpqnE80p0GbHNuE4QgpTLK5NdmOgmFjJHj4UhxHSMM1Pv3j04TL6/g8mfwf+GCIV+zvWMSFJoW6JD0pabloEDrV23FptB5LeNUqyku2mmEJcp3Up9jHR53lTyTr2N4wLXfTdzv4YEvm0zln6vzZnTd9C2k2YAcFKdpgnXH0qXu2WohKVFvnyYU2nNRXvCcNMPlGe6XFer+V/oyirNtJunWeKqThAOhZvYDmyjjlovQQHDsB6P1AtaBAowIoGyaIlNAobNM6gNU54a6CXBqGW/ZCRrDunW2wLVdey9uiBtxYUN60i3gogrrw33gDUtc478Gpr8TBmHvhWGELNHQ3IjZPAndGao66hUXZomi2OH5ZcE5ci+430fPkamwKL2BJayZWRNTfOSd66uuF90/auty3VPmu+pznS+JNR74Kfp2UaUnFdmOIX696vMX/vYlueryHyb1tkd5P/QnORI4cQ8zZfUz6fz+f/AQAA//8DALuBJ1glBAAA
|
||||
@ -1,5 +0,0 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpSUTW/cNhCG/4rAsyjzS0NSN8NuUddNvYgXadEbRY5sIVpqQVGbBsb+94DaDRL31psw0DszfOadeSPRHZB05PZ4/OWEMS+kJnncQoIJoAwo43tmOmU6ZhojmTEA/5CajI/4lXREK++5t0glAlIVWEt7OXgaAlrOeGCiVSWne1lI90bc2AQ8jR6befmEaRnnSDry1xjD/KXUdmPjp3kNTZonfIhLdtGXZv58uHukf9zu9k+7y18LLkXcjIF0BCR6BIdUgjVU9RZpb3CgCMogWCXAm4tsjBlTdFOzhM8/6oc5R8y+E42QDaPCknNNgsuutNy7Bfdfj6WLDdF9iddbePvs3sgJE+lE/R3mJd2Nn8abPB8nPOF0dGnBdOPnw8HFQGpyTPMRUx5xw7K9dJpcHudYXYvdTSPGTGry9FztJpeHOR3esXq+IHj4HwROmHrSEclaCVqjklI6C71gGDR4ANF7x/TQC+QeWWuVtlwzLq1gVragjQ6OWU9q8jLNvZt+XzZ+VrDeBKYFCFRohQKwnqE0OLQaWXAAPRhvgzRaKBOMdNY5bnvU3oIvBvng/OsYsXq4Jx1hHEWQBukgOFBlgqOulZYCSBiQe6sFJzW5m2Me4zqvS/UQM76kjR/pyK9uWvBC7jb51zGjz2sqUP+GUmyX5rD6XP1wAGcNayRjRbTm45qrjxjGhD5jwbtP6zXffyQCxCa6W1PCmKudy6/Vb255LVSc8dzZ8lTwfsCW4cACc8B0QAiAQYmgg7IqgLLMg3I9V8G1vB9KhNQEi+GqzeMMhUERDDUDDFQZI2jfD5wysI4NA/Ss9e84Vk9T0TlvmMIgqcUhUGW5o1ZJSYNsnRYwaC2Gn5BczcdZARzwdNnWy5aFYDAAp0LxliprOTW21bTXprc9GGx96fkRU8TpJ1AfRp/mZR5ydfVu9Q7dxzWWi1NtPv4yRvrvNqP72X/GVJUZuzGWBbuO9VyTA7plTXjYDlb3RnZlu6r9dreEbUQrZE2es0t5PV7D0jRatOJ8Pp+/AQAA//8DAPPar0r1BAAA
|
||||
@ -1,5 +0,0 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpSUW2/cNhCF/4rAZ1Hm/aI3w27RhZt6ES/aoG8UZxQL0UoLito0MPa/F9RukbhveRMGOsPDb87wjUzhiKQl96fTL2ec8kJqkoetJJgwlBnK+IG5VrmWucYZq7X0f5OaDE/4jbTEqhh59EglGqQKmKad7CMFQM8ZBya0Kj3D54W0byQMDeB5iNjMy5+YlmGeSEv+GiaYv5azw9DEcV6hSfOIu2nJYYrFzB+7hyf6+/3+8Ly//rXgUsTNAKQlRmJEE5BK4x1VnUfaOewpGuXQeCVMdFfZMGVMUxibBb58Px/mPGGOrWiEbBgVnlxqAiGHYrkLCx6+nYqLDdFjqddbefts38gZE2lF/R/Ma7u7OA53y9qNeMbxFNKC6S7Ox2OYgNTklOYTpjzghuWMqZuXIReiMdrgHOfgglXagpNaSAuSIThmNXjwwSjTi6h1D53zVvUKBFdgUKnOkpps4MYx5GGeqpv3h3HAKZOaPL9U+zHkfk7Hd+hfrkR3PwG02CYtkUxLYy0qKWXwphMMwZpojOhiYLbvBPKITHtlPbeMSy+Yl9pYZyEwH0lNPoT4OkxY7R5JSxhHAdIh7QU3VDkINGjpqTHS9Mijt4KTmjzMUx6mdV6Xajdl/Jy2+5KW/BrGBa83vU/xdcgY85oKhE+mhHGfZlhjrr4HgLOGNZKxIlrzac3VR4QhYcxYcBzSeuv3P4kwYhM9rCnhlKt9yK/Vb2F5JS3xwUUefPAhmBh71Ax7BiwYZgENGAQlwILyCozyLBoVOq4gaN71pUJqgiVv1RZx5rW3ogs09B1QZaKlwfaG9r3XiMxyY/Q7jtXzWHQhOqYQJPXYA1WeB+qVlBSkDlaY3lrR/4DkFhbOCmDA83VZr0sG4BAMp0JxTZX3nDqvLe2s63xnHOpYPD9hmnD8AdSHIaZ5mftc3bJWvUP3cZ3Kg1Ntufs6TPSfbUaPc/yCqSozDsNU9us21ktNjhiWNeFxe6/aN7Ivy1UdtmdL+EZoIWvykkPK6+lWlq6xQovL5XL5FwAA//8DALepIcv0BAAA
|
||||
@ -1,5 +0,0 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpSUXW/jKBSG/4rFtXEBY2x81013tFGn02ianV3tHYbjFNWBCHBnqyr/fYSTqumM9mLvrGPe8/GcF16RU3tAPbo+HH5/BpciKlGyS4gRJjARmNAt6Xre9aSrOilY27b/oBLZW3hBPWq51lRLwDUIwNyQBg/1qLExICmhhrCG55xqF1H/ipStDDxbDZWP3yBE6x3q0V/WGf8911a20pOfTRX8BGsXk3I6N/NlvbrFn6832/vN6VSEmMWVNahHogYNQgGuhewwHyTgoYMRg+AdCMmZ0N1JZl2C4NRURfP0Xt/45CDpnlWsrghmEh1LZFRSueVBRdi+HHIXC6KbHC+X8PLZv6JnCKhn5RvMU7orPdmrfRxmO5mrye921u1W3o12NweVcuESHYI/QEgWFji/WafCy2e/2+V86JOaIqASbSHsrVPT6ccNjGqe0oOfw0Immqf/OoN6pObkUYkWktO0lC3Ow6wmCy79or0ofP9QbCaVRh/2H5b0cGK//h/o75R+tA6K9Q3qEaHATN0BHhkVmHdGYdXUEgtRixGoli2jqEQr75J1s59jsXYJdmdqH/q7DvrRJtBpDnmmv0U22yZ4M+tUvC+YkopUNSFZNKfDnIqvYGwAnSAPsQ3zOd9PEibYIvpkJ/gFz2oOAVwqNio9Fn+o+Ih6JFWnqZJKKiW0HqEhMBJDlCCtAWEEGM5Ma7jkRnBJtOBqoNyohg5jjqASQTZZsfi6rmvTMtZhI6TE3DSAVSM5HmsKvKEwDC18gFvcT1mndEc4mBpLGA3mkioseV1jUzeqZWJsWzZecDobgpJM/dKEf0YwZy99OVn7bXYDz6d7fLp/xnRgBMWM0wZzKSnuZNPioe0GOYgOGp0nW3kX/RvIbxAGH23Kj8iddXavpp+PvG/mFoKD6WI7d1YHH/2YirMtiw/7+jq7/IoVi0W/W4f/XYxx4/UThCIbS1l3sczj8Xj8AQAA//8DAM61a8IMBQAA
|
||||
@ -1,5 +0,0 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACoxUS2/bOBD+KwLPpkJRFCUK2IPhtFgjTWMk7u5ib3wMYyKyaFCU06DIf19Qlmun6GFvwojz+B4zP1Av94BatDwcPh2hjwNaoOimECWUY8IxKbakaVnTEpHTpqzrsv4XLZC7gzfUopppXWgBuAQOmBlSYVVajY0BUZDCEFqxVFM+D6j9gaTLDRydhtwPf0EYnO9Ri/52vfGvqbd0ue78aPLgO1j3Q5S9TsN8Xa/u8JflZvuwOb0aYEjJuTOoRbwEDVwCLrloMFMCsGrAYuCsAS4Y5bo5pbk+Quhllw/m5dLf+NhD1C3NaZkTTAV6XyAjo0wjKznA9u2Qppgouk3xxRSePtsf6AgBtXRxJvNU7kZ37mY/qNF15ibK8AzRBrmHVx9e4Cg7tECH4A8QooOJm4en7DJSQXKSU04JQQt0L/XO9ZCtb7OHLgGWuiEMTIkFWIOZKCQWrCyxKStZU27rmlq0QHcQeuiuqt47HfzgbcxmyrMPfVa+j64f/Thk6z7Cc5DxlPdZdgOgBdqMqnPD7hGkedv6xzH968cuYVmNIUAfs42Mu+xPOexQi4RsdCGFFFJyrS1UBCwxRHJSG+CGg2HU1IYJZjgTRHMmVcGMrAplUwQt0MMYD2M88//9aohtcPs9mMsE24njTSej9WG/NtBHZ12S5vxiMlTXTaiyueaqc9DHDySjFpECqCkbwJYWHLPGSCyrUmDOS26h0KKmxYkw657HnzwRaw0rbdU0XAlVEKC2bhiprSJG21ILUymoaU3qBhqhCVEVKxolNDFQVMwkdMGbUcdfrVBOAj2OfdrObJ1gv7oef+fswsgm+Oi175JgroOntyHC/neqrfz+4AcXE347S/ttgHvfp7/z/s+czclLHy8xSHuQTasHypRKCoJtrRRmlCksjCkwK5iltZIGgKIFuvX6BUKWDCZdP2ly9tTDU7YMeuci6DiG1PmfCdRJzs/nlbnwkX/9tF35AMvDYTFH/zgmkk7Fzgb4cFe+Den5zg/xCvIM7Qk6ex7syk4GjqdT5aYTY0wDhheYsqLCTIgCN6KqsaobJRRvoNIXu2aPYFwAHad62zCmbjOzv/PlMkRnpY5DWp4vXk92mv05P/l1yCvZfmafml9JN1tprlSQZNmn0+GcHPS/7ub7+/v7fwAAAP//AwDjiSUTKAYAAA==
|
||||
@ -1,5 +0,0 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACoxU227cNhD9FYGvFRXeRJF6c20HXaSOjeyiLfo2Ikc2a5laUJSTwPC/F9TauaJAXwRhSM45c+bMPJEID0h6cnY8Xj5izAupSQ5bSDChKdOU8QMzvTI9V41iQnSq+5vUJLzDz6QnnXKOO4tUokaqPGvpIEdHvUfLGfdMtKrkhNuF9E8EQuPxMThs5uUPTEuYI+nJnyH6+WPBhtC4aV59k+YJd3HJEF0h8353/o7+fnZzuL453VpwKY+b4ElPtESHGpBKbQ1Vg0U6GBwpamVQWyW0M6dnIWZMEaZm8fdf8f2cI2bXi0bIhlFhyXNNPGQolAdY8PD5WFhsEl2UeL2Ft9/+iTxiIr2oX8U8pXvjpvDmYRnWMPk325fU5JjmI6YccFPjel99JcFZwxqhBWOkJlfg7kLEandRXU+lRHCGKfSSWhw9VZYDtUpK6mULndBj14mR1OQdpojTN1mvgkvzMo+5ehG5+g5nf3Z+GWGYsGC8hWlBUpPdss8QPUxzxMtP6NZ8ynVIazk+n2MOcZ3XpdrFjLcJXs5f35+vKWHM1Q3ku+o3WO5ITywYx8GCBdDOjdgyHJlnoFnnUXuNXgnfeWWV18oypxUMXHlo+TCWCKnJr0XD/eocLstXNptJpmnjUL306XwKGPN3MpKeMI7CS4N0FFxTZTxQaKWlWks9Ine2E/wV5gDpFjPpyWiGYWDODUJZ7kBYkH5k0uvRCtF1oEbZdkpo3bIOnO86wZWXHTqQo2RGkJrcpNmvLv/Yark14CbN/6DLRapiZNa2xsEwCK24AWtQca2MtthZNQJT0g3AWwPDwBm0zFkjHHBtQQ2+Hb+odBlvQ8SLsBwn+PwNrml0I39xAIaPoH64/jbBA36c0/37k42b95cHUpMPaywbodoVi3wMkX7SZaKv1imHw11C8OivZo8/+wjLuFTbhHpmOyOdogqcoMp3lg6CMQrIJdqRd5IV+17M7h5TVRwGIZap+pLsel+dJXcXMrq8psLvr43H9b66mSCPc3r4bpN8U9iPAjRCclY64/HxtI1OW8R7g15zKhRvqbKWU2Pbjg6dGeygDbauKHa95uOaqw/oQ0KXt4pfvLhhnt+hu/9ZjFcXvFiUs+K1/WmLbdL+ryX2XJMHhGVN+LBt6/6J7GLEtCFfrKdR3MWrME1hQTdHv5C+bblolG35C8P/vqd5ww1Xz8/Pz/8CAAD//wMAYBpYrRsGAAA=
|
||||
@ -1,5 +0,0 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpRU0W7cKhD9FWuejQMYY+O3aNPqrnLbrNpVW/UNw7hBcfAKcNoo2n+vsLdK83R1XxAaOMOZM2d4Aa8fEXq4Pp3ePaFPEUpIbg1xyiWhklB2pF0vup6JqlZCNIx/hxLcLT5DD60whhmFpEaJRFjakKEeDbEWFaPMUt6InFP/iNC/gHaVxSdnsJrjFwzRzR56+Oq8nX/mt7WrzDQvtgrzhHsfk/Ymk/m4392Sf68Px7vDditizODKWehB1mhQaiS1VB0Rg0IydDgSlKJDqQSXpttgzicMXk9VtA+v79s5eUym5xWvK0q4gnMJViedKQ864vH5lFmsEt3keLmG123/Ak8YoOflHzG3dFdmclePcVjcZK+2Nen4ENd11CbN4RlKOIX5hCE5XPW5jhEfh+n5qOPD++1O3sZ3v9AsCe1uXnyCHlgtoYRVoGnSyc2+uHDcTQ59Wg9TcD4681+5aijh7nNxmHQa5/D4ph+fN5n3/0PlD9rcO4/F/gZ6oAy5rTskI2eSiM5qoptaESlrOSIzquUMStjNPjm/zEss9j7hj7CWBD2811PEjd91MPcuoUlLyHV+k9lXhzDbxaTitZeMVrSqKc2gJZ2WVHxC6wKahLmIY1gu+b68hXDJV9BuCQF9Kg463Rf/6HgPPSjdGaaVVlpLY0ZsKI7UUi1pa1FaiVZw21qhhJVCUSOFHpiwumHDmCNQAmbrFKtbkbNxGIUg3TAaIgZNiaaSEqEGbjVT2o65tFcdi7sp47TpqEBbE4WjJUIxTZSoa2LrRrdcjm3Lx78kufiB0Sywxadt7rZ5sbZDKxnhgjVEKMVIp5qWDG03qEF22JjM+RaDx+kvoT44E+Y4j6m4OKR4I92nxee/o1jd8tN58mvt0c1sHjAUucfa+Twql7aez+fzbwAAAP//AwD8UEJ2ggQAAA==
|
||||
@ -1,5 +0,0 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpRTwW7cNhD9FWHOokxSFCXqZqxTdOEmXiSLtOiN4oxiwjK1oCgngbH/HlC7gZPeeiGIAd/Me28eXyHYZ4Iebk+ndy8U0gIlJL+VJJeacc24OPKuV10vVFWbttFt9y+U4O/pO/TQKueEM8Rq0sQU8oYN9egYIhnBBXLZqNzTflmgfwXrK6QX76ial88UFz8H6OFvH3D+mmdbX7lpXrGK80T7sCQbXCbzYb+7Z3/dHo4Ph8urhZYMrjxCD7omR9oSq7XpmBoMsaGjkZFWHWmjpHbdBeZDohjsVC349DYf5xQouV5Wsq44kwbOJaBNNlMe7ELH76fMYrPoLtfLrbxd+1d4oQi9LH+aeWl34yZ/87wMq5/w5nImuzxllac4nygmT5snm8xpssnPobhO2k2eQoISHj4Vh8mmcY7Pvxn16aJ//z/kH/P0d9/IrYlwN68hQQ+iNlDCe+sefaBifwc9cEES647YKIVmqkPLbFMbpnWtRxLOtFJACbs5JB/WeV2KfUj0JW4CoIc/7LTQhfptdI8+kUtrzKr+0TkLhzjj6lLx5r/gFa9qzjNoTac1FR8JfSSXKOs7xvXa7z8QqeUG2q0xUkjFwabH4k+7PEIPxnZOWGONtdq5kRpOI0duNW+RNGpCJbFFZRRqZbjTyg5CoW3EMOYKlEB53cWWsMFRSzg2jHNjmRpQM4Pk2Fi3sh4bbQbKFN98LB6mjLOu44qwZoZGZMoIy4yqa4Z1Y1upx7aV4y+WXLcveDYY6eXyVy4ZR+wItWBSiYYpYwTrTNOyoe0GM+iOGpc531MMNP1i1Hvv4rzMYyqu4Sl+s+7jGvJ/L7YgffWBfdt2dDe7J4pF3rH1Icf7utbz+Xz+AQAA//8DAJKaUgc2BAAA
|
||||
@ -0,0 +1,5 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpSUUW/cKBDHv4rFs3EAY2z8Fm1a3SrXy6pZ9U73NoahQXHwCnDaKMp3P+HdU5unqi8IjfkPM7/541cS4AnJSK5Ppw/PGHIiNcl+CwkmFGWKMn7kbGRy5G2jVSc1a/8lNfG3+EJG0ktjuNFIW1RIpWUdnVpnqLWoOeOWiU6WnPA1kfGVgG8sPnuDzZK+YEx+CWQkf/tgl2/lbvCNmZfVNnGZcR9ShmBKMX/td7f0z+vD8e5wPpUwFXHjLRmJMtr0xmmqcBioVKynA+84Fb0DAeiA93iW+ZAxBpibZB9/3G+XHDCbUTSibRgVmrzVxEKGUvIECY8vp1LFhuimxOstvG3HV/KMkYyi/h/mOd2Vmf3VU5pWP9ur85ohPaZtdWDyEl9ITU5xOWHMHjc+1ynh0zS/HCE9fjyfKdv04TuaNaPdLWvIZCS8HUhNNkDzDNkvobrUuJs9hrx9zNGH5M2vcrWkJnf31WGG7Jb49G4e92fM+9+g/AnMgw9Y7W/ISBhHYdsBqRNcUTlYoNC1mirVKofc6F5wUpPdErIP67Kmah8yfo1bS2QkH2FOJendfXUdzYPPaPIaS5//qOKrQ1zsanL1Y5acNaxpGSuiNZ/WXH1G6yOajKWJY1wv+b68lwglNtFujRFDrg6QH6o/ID2QkWgYDAcNGkAZ47Bj6JhloFhvUVmFVgrbW6mlVVIzoyRMXFro+ORKhNQEi3Wqza2mYwMDJ+ngJk6lU4qCM5YCIpNa8M4y+45jdTcXHZiBSbQt1egslZoD1bJtqW076IVyfS/cT0gufuCsALb4fH535/di7YBWcSok76jUmtNBdz2d+mHSkxqwM6XmW4wB559AffImLmlxubo4pHqH7vMayr+j2tzyzQf6fZvRzWIeMVZlxuBDeSqXsb69vb39BwAA//8DAJ/SwMyCBAAA
|
||||
@ -0,0 +1,5 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACoxUTW/cNhD9KwKvXcr8EkXq5toOunAdG9lFW/Q2Ikc2a5laUJSTwPB/L6i1EztBgV4EYUjOe/PmzTyRCA9IOnJ6OFw8Yswz2ZAc1pBgQlOmKeN7zjqmOi5r23KmLPubbEi4xK+kI61yjjuLVKJGqjxraC8HR71Hyxn3TDSq5ITbmXRPBELt8TE4rKf5D0xzmCLpyJ8h+ulzwYZQu3FafJ2mEbdxzhBdIfNxe3ZJfz+92V/fHG/NOJfHdfCkI9pZ17rBUo3GUKVZSw1vOBXtAAJwAN7i8VmIGVOEsZ79/Xd8P+WI2XWiFrJmVFjyvCEeMhTKPcy4/3ooLFaJzkt8s4bX3+6JPGIindi8inlMd+LGcPIw90sY/cn6JRtySNMBUw64qnG9q76T4KxmtdCCMbIhV+DuQsRqe15dj6VEcIYp9JJaHDxVlgO1SkrqZQOt0EPbioFsyCWmiOObrFfBpWmehly9iFy9w9mdnl1E6EcsGB9gnItO23mXIXoYp4gXX9At+Zhrn5ZyfDbFHOIyLXO1jRlvE7ycv74/W1LCmKsbyHfVbzDfkY5YMI6DBQugnRuwYTgwz0Cz1qP2Gr0SvvXKKq+VZU4r6Lny0PB+KBGyIb8WDXeLczjP39msJhnHlUP10qezMWDM72QkHWEchZcG6SC4psp4oNBIS7WWekDubCv4K8we0i1m0pHB9H3PnOuFstyBsCD9wKTXgxWibUENsmmV0LphLTjftoIrL1t0IAfJjCAbcpMmv7j8Y6vl2oCbNP2DLhepipFZ0xgHfS+04gasQcW1Mtpia9UATEnXA28M9D1n0DBnjXDAtQXV+2b4ptJFvA0Rz8N8GOHrG1xT61r+4gAMH0D9cP1Dggf8PKX7j0cb1x8v9mRDPi2xbIRqWyzyOUT6RZeJvlrGHPZ3CcGjv5o8/uwjLONSrRPayB6RNYaitj1VpmfUKFRUqsZ7Cc5IV9Q4n9w9pqo4DEIsU/Ut2fWuOk3uLmR0eUmF318rj+tddTNCHqb08G6TvCnsRwFqITkrnfH4eNxGxy3ivUGvORWKN1RZy6mxTUv71vS21wYbVxS7XvJhydUn9CGhy2vFL15cMc/u0N3/LMarC14sylnx2u64xVZp/9cSe96QB4R5SfiwbuvuiWxjxLQiny/HUdzGqzCOYUY3RT+TTvPG1K3m+oXhf94TzNbCSvX8/Pz8LwAAAP//AwCR2jkWGwYAAA==
|
||||
@ -0,0 +1,5 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpSUTW/cNhCG/4rAsyjzS/zQzbBbdOumXsSLtOhtRI5sIlppQVGbBsb+94DyBol7600YaGZePvPOvJIJjkg6cns6/XLGKS+kJjluIcGEpkxTxg+cdcx0rG2U1kZo9g+pSXzAr6QjRnnPvUMqUSNVgbW0l4OnIaDjjAcmWlVqwvNCulcCsQl4jh6befmEaYnzRDryV5zC/KX0htj4cV5Dk+YRd9OSYfJFzJ+7uwf6x+3+8Lh/+2vBpSQ3MZCOOFA9Wh2otaaninGk1jtLBXBurETlXBEBsYlTxjTB2Czh84/+Yc4TZt+JRsiGUeHIpSYBMhTJPSx4+HoqKjZE9yVeb+Hts3slZ0ykE/V3mG/lbvwYb/J8GvGM4wnSgunGz8cjTIHU5JTmE6YcccOyvXQcIcd5qq7N7saIUyY1eXyq9iPkYU7Hd6ye3hDs/geBM6aedESyVmpjUEkpweleMAxGe61F74GZoRfIPbLWKeO4YVw6wZxstbEmAHOe1OR5nHsYf182fk6w3gZmhBao0AmltfMMpcWhNcgCaN1r612Q1ghlg5XgALjr0XinfVH2AfxLnLDa3ZOOMI4iSIt0EFxTZQNQaKWjWks9IPfOCE5qcjdPOU7rvC7Vbsr4nDZ+pCO/wrjgG7nb5F9iRp/XVKD+rUuzfZrD6nP1wwGcNayRjJWkNZ/WXH3EEBP6jAXvIa3Xev9JEVpsSXdrSjjlag/5pfoNlpdtJtZzcOWp2vsBW4YDCww0MwF10BiUCCYop4JWjnmtoOcqQMv7oURITbAYrto8zl0QDgZHBQdFlWKaQguGSudkb4zCtmfvOFaPY8kDb5nCIKnDIVDlOFCnpKRBtmCEHowRw09IrubjrAAOeH7b1k2BDsFi0JwKxVuqnOPUutbQ3tje9dpi64vmB0wTjj+B+hB9mpd5yNXVu9U7dB/XqVycavPxlzjRf7cZ3c/+M6aqzBjiVBbsOtZLTY4Iy5rwuB2s7pXsy3ZVh+1uibaRVtbkKUPK6+kabXXDneOXy+XyDQAA//8DAA1lMYT0BAAA
|
||||
@ -0,0 +1,5 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpSUTW/cNhCG/4rAsyjz+0M3w25Rw02ziI226G1EjmIiWmlBUZsGxv73gvIWiXvrjRhwhi+feWdeyQxHJD25PZ1+OuNcVtKSkvaQYMJQZijjz5z1zPZMd5pp5rX5i7QkPeI30hOrQuDBI5VokKrINB3kGGiM6DnjkQmtak34vJL+lUDqIp5TwG5Zf8e8pmUmPfkjzXH5Wt+G1IVp2WKXlwkf5rXAHKqY3x7uHumvt4fnj4e3WyuuNblLkfTEgxrQmUidswNVjCN1wTsqgHPrJCrvqwhIXZoL5hmmbo1fvr8flzJjCb3ohOwYFZ5cWhKhQJU8wIrP305VxY7ovsbbPbwf+1dyxkx60f4L863cTZjSzboNE55xOkFeMd+E5XiEOZKWnPJywlwS7ljOmIdlTaUSDcGCc5xHB1ZpG53UQtooGUbHrI4+ejDKjCJoPcbBeatGFQVX0aBSgyUt2cFNE5S0zM1V+92UcC6kJR+fmsMEZVzy8R36pzeiD/8DaJVNeiKZlsZaVFJK8GYQDKM1wRgxBGB2HATygEx7ZT23jEsvmJfaWGcjMB9ISz5AeEkzNg/3pCeMo4jSIR0FN1S5CBS09NQYaUbkwVvBSUvulrmkeVu2tXmYC37O+39JT36GacW3n97m8JIKhrLlCuFPU2Uf8hK3UJrvBuCsY51krCZt5bSV5hPGlDEUrDie83at958UYcSedLfljHNpDlBeml9gfdkZusDBgwcwIYyoGY4sMjDMRjTRYFQi2qi8ikZ5FoyCgasImg9jjZCWYPVbs1vcOmYAnKRMiJGq0RoKSo5UKxiUAe/C4N9xbD5ONQ+CYwqjpB7HSJXnQL2SkkapwQozWivGH5BczcJZBRzx/DasuwITo8NoOBWKa6q859R5belg3eAH41CHquAR84zTD6A+pJCXdRlLc/Va8w7dp22uC6fZffc1zfTvvUf3S/iCuak9hjTX+bq29dKSI8K6ZTzu+6p/JYc6XM3zvraE7qSTLXkqkMt2uka16bj3/HK5XP4BAAD//wMAAznKIvMEAAA=
|
||||
@ -0,0 +1,5 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACoxUT2/jKBT/KhZn4wLG2ETaQ5TOaKNOp1Gb2V3tDcOjQXVMhHE61ajffYXjTNLRHPZmPcN7v3+PH6hXe0ALtDwcPh2hjwPKUXRTiREmMBGY0C0lC1IvSFVI3nBZiX9RjtwdvKEFqrnWVEvAJQjA3JAKt6XV2BiQlFBDWMVTT/U8oMUPpFxh4Og0FH74C8LgfI8W6G/XG/+aZitX6M6Ppgi+g3U/RNXrBObrenWHvyw324fN6dQAQ7pcOIMWSCreQiMMbpq6xZxQwI2WDWaK0ropgUuZQChXuD5C6FVXDOblMt/42EPUC1awsiCYSfSeI6OiSpBbNcD27ZBQTBLdpno+lafPxQ90hIAWLD+LeWp3ozt3sx/a0XXmJqrwDNEGtYdXH17gqDqUo0PwBwjRwaTNw1N2gURJQQomGCEoR/dK71wP2fo2e+gSYaUbwsGUWII1mEuqsORliU1ZqZoJW9fMohzdQeihu+p673Twg7cxmyXPPsxZ+T66fvTjkK37CM9BxdO9z6obAOVoM7adG3aPoMzb1j+O6V8/donLagwB+phtVNxlf6phN/nSaKqkkkoJrS1UBCwxRAlSGxBGgOHM1IZLbgSXRAuuWsqNqmhrUwXl6GGMhzGe9f9+BWIb3H4P5oJgO2m86VS0PuzXBvrorEvWnE9Mgeq6iVU291x1Dvr4QWS0QIQCM2UD2DIqMG+MwqoqJRaiFBaoljWjJ8Gsex5/6kSsNby0VdOIVraUALN1w0ltW2K0LbU0VQs1q0ndQCM1IW3FadNKTQzQipvELngz6vhrFMrJoMexT9uZrRPtV9fj74JfFNkEH732XTLMdfD0NkTY/861ld8f/OBi4m9na78NcO/79Hfe/1mz+fLSx0sN0h5k0+pRA5WudYUr0C3mlnLctopjyyrGKC9Bihrl6NbrFwhZCphy/eTJOVMPT9ky6J2LoOMY0uR/JlInOz+fV+aiR/H103blAywPh3yu/nFMIp2anQPw4V35NqTjOz/EK8oztSfo7BnYVZwMHE9PlUtFYUwDRlDMOK0wl5LiRlY1buumla1ooNKXuGaPYFwAHad+2zCmabOyv8vlMkRnlY5DWp4vXk9xmvM5H/kV5JVtP2+fhl9ZN0dp7kRJiuzT6eGcEvS/3s339/f3/wAAAP//AwBZ8qiSKAYAAA==
|
||||
@ -0,0 +1,5 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpSUUW/jKBDHv4rFs3EBY2z81ktvdVG322ib2zvdG4ZxiupABLh7VZXvvsJJ1XRX93Bv1piZ+c9v/vCKnNoD6tH14fD7M7gUUYmSXUKMMIGJwIRuKelJ25OmampOJO/+QSWyt/CCetRyramWgGsQgLkhDR7qUWNjQFJCDWENzzXVLqL+FSlbGXi2Giofv0GI1jvUo7+sM/577q1spSc/myr4CdYuJuV0FvNlvbrFn6832/vN6VSEmJMra1CPpOIDdMLgrmsHzAkF3GnZYaYobbsauJRZhLKVdQmCU1MVzdN7f+OTg6R7VrG6IphJdCyRUUllyYOKsH05ZBULopscL5fw8tm/omcIqGflG8xTuSs92at9HGY7mavJ73bW7VbejXY3B5Vy4xIdgj9ASBYWOL9Zp8LLZ7/b5Xrok5oioBJtIeytU9Ppxw2Map7Sg5/DQiaap/86g3qk5uRRiRaS07S0Lc7DrCYLLv2Se9H4/qHYTCqNPuw/LOnhxH79P9DfKf1oHRTrG9QjQoGZugM8Miow74zCqqklFqIWI1AtW0ZRiVbeJetmP8di7RLsztQ+6LsO+tEm0GkOeaa/RW62Cd7MOhXvC6akIlVNSE6a02FOxVcwNoBOkIfYhvlc76cUJtiS9MlO8Aue1RwCuFRsVHos/lDxccHRaaqkkkoJrUdoCIzEECVIa0AYAYYz0xouuRFcEi24Gig3qqHDmCOoRJBNViy+boUGM3YdbuuWYd4OFA/SGMwG2Q3jCHTk8AFucT/lPKU7wsHUWMJoMJdUYcnrGpu6US0TY9uy8YLT2RCUZOqXJvwzgjl76cvJ2m+zG3g+3eNFpzCmAyMoZpw2mEtJcSebFg9tN8hBdNDoPNnKu+jfQH6DMPhoU35E7qyzezX9fOR9M7cQHEwX27mzOvjox1ScbVl82NfX2eVXrFgs+t06/O9ijBuvnyAU2VjKuotlHo/H4w8AAAD//wMAFYxWiAwFAAA=
|
||||
@ -0,0 +1,5 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpSTwW7cOAyGX8XQ2XIkWZYt34LJLjpIuxm0QVv0Rot0R6gjD2Q5bRHMuy/kmSLt3vYm/BLJnx+pFxbgiVjPbk+nv54ppIWVLPlNUkIZLgwX8lGKXrS9lJVqbNe28gsrmb+nn6xnrXZOOku8JkNco2j4UI+OI5KVQqJQjc454evC+hcGvkJ69o6qeflIcfFzYD375APO33Nt8JWb5hWrOE+0D0uC4LKZf/a7e/729vD4cLi8WmjJwZVH1jMLeqDOIO+6duBaSOKdsx1XIGXb1aStzSbAVz4kigGmasFvr/VxToGS61Wl6kpwZdm5ZAgJsuUBFnr8ecouNkR3WS83eTv2L+yZIutV+QvmJd2Nm/yNm5+eIODN6INfjqxkpzifKCZPG46tw2mC5OdQXIvsJk8hsZI9fCgOE6Rxjk9/MPpwaX3/Pzp/B+7oAxX7O9YzIUlh3REflTRcdwgcmtpyY2ozknS2VZKVbDeH5MM6r0uxD4m+xs0l69nfMC108Xcb3dEncmmN2fpnk4vRD592M2ZBsJId4oyrS8UrbikqUdUiXz6s6bSm4j2hj+QS5Z4e43pN/58QZdQWtFtjpJCKA6Rj8QaW4waicxIsWADj3EiNoFGgACNaJIOGUCtsUVuNRlvhjIZBaoRGDmNWsu883WJbKNM4I6lDPgxkuB4Gy622AwdrEGrV0NjkiFesxcOU48B1QhPW3NKIXFsJ3Oq65lg30Coztq0af0NynbgUmTfS8+VrXBwgdoRGcqVlw7W1kne2afnQdoMdTEeNyw7uKQaafgP1zrs4L/OYiuvCFH+ge7+G/L2LbXm++8B/bCO7m903ikUeOfiQt/k65fP5fP4XAAD//wMAyKqZXSUEAAA=
|
||||
@ -0,0 +1,5 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACoxU227cNhD9FYGvFRXexIveXNtBF6ljI7toi76NyJHNWqYWEuUkMPzvBbV2rijQF0EYknPOnDkzTyTBA5KOnB2Pl4+Y8kJqkuMWEkxoyjRl/MBZx0zHeSM0Y9KJv0lN4jv8TDpilPfcO6QSNVIVWEt7OXgaAjrOeGCiVSUn3C6keyIQm4CP0WMzLX/gvMQpkY78GVOYPhZsiI0fpzU08zTiLi0Zki9k3u/O39Hfz24O1zenWwsu5XETA+mIA9Wj1YFaa3qqGEdqvbNUAOfGSlTOFRIQm5gyzgnGZgn3X/HDlBNm34lGyIZR4chzTQJkKJR7WPDw+VhYbBJdlHi9hbff7ok84kw6Ub+KeUr3xo/xzcPSr3EMb7Yvqclxno4454ibGtf76isJzhrWCC0YIzW5An8XE1a7i+p6LCWCt0xhkNThEKhyHKhTUtIgWzBCD8aIgdTkHc4Jx2+yXkU/T8s05OpF5Oo7nP3Z+WWCfsSC8RbGBUlNdss+QwowTgkvP6Ff8ynXYV7L8fmUckzrtC7VLmW8neHl/PX9+TrPmHJ1A/mu+g2Wu61F1nNw4AC09wO2DAcWGGhmAuqgMSgRTFBOBa0c81pBz1WAlvdDiZCa/Fo03K/e47J8ZbOZZBw3DtVLn87HiCl/JyPpCOMogrRIB8E1VTYAhVY6qrXUA3LvjOCvMAeYbzGTjgy273vmfS+U4x6EAxkGJoMenBDGgBpka5TQumUGfDBGcBWkQQ9ykMwKUpObeQqrzz+2Wm4NuJmnf9DnIhXpiGZtaz30vdCKW3AWFdfKaofGqQGYkr4H3lroe86gZd5Z4YHrMgChHb6odJluY8KLuBxH+PwNrm10I3/xAJYPoH64/naGB/w4zffvTzZu3l8eSE0+rKlshGpXLPIxJvpJl2G6WsccD3czQsBwNQX82UdYxqXaJhSCYb0znEoYLFVMCQq+FdT3ivUemfZYXlxM/h7nqjgMYipT9SXZ9b46m/1dzOjzOhd+f208rvfVzQh5mOaH7zbJN4X9KEAjJGelMwEfT9to46hDsBg0p0LxlirnOLWuNbQ3tne9ttj6otj1mo9rrj5giDP6vFX84sUN8/wO/f3PYry64MWinBWv7U9bbJP2fy2x55o8ICzrjA/btu6eyC4lnDfki/U0irt0FccxLuinFBbStUa2jRNSvjD873vWNM4I8/z8/PwvAAAA//8DAK8xVv8bBgAA
|
||||
@ -0,0 +1,5 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpSTQW/cNhCF/4rA81ImKYoidTPWKbpwUy+SRVv0NuKMYsIytaAoJ4Hh/15Qu4WT3noRhAEf571vhq8swjOxnt2ezx9eKOaF7VgOW0kJZbgwXMiTFL3oeilr1XbCafs327FwT99ZzzrtvfSOeEOGuEbR8qEZPUckJ4VEoVpd7oQvC+tfGYQa6SV4quflD0pLmCPr2Z8h4vy19IZQ+2lesU7zRIe4ZIi+mPn9sL/nv90eTw/Hy6mFliKuA7KeOdADWYPc2m7gWkji1jvLFUjZ2Ya0c8UEhDrETCnCVC/49N4f5xwp+17VqqkFV4697RhChmJ5gIVO38/FxYbortR3W3n77V/ZCyXWq92/MC/X3fgp3DwvwxomvLl8MyxPJeU5zWdKOdDGZIs5TZDDHKtrp/0UKGa2Yw+fq+MEeZzT80+gPl/yH/5H/FPp/uEb+TUT7uc1ZtYzqSXbsY/gH0Ok6nDHeiYkKWws8VFJw7VF4NA2jhvTmJGkd50qmv0cc4jrvC7VIWb6krYArGe/wLTQxfpt8o8hk89rKqn+MsXHMc24+ly985eiFnUjRBGt+bzm6hNhSOQzlXyntF7v+49EGbWJ9mtKFHN1hPxY/QrL4wbFegkOHIDxfqRW0ChQgBEdkkFDqBV2qJ1Go53wRsMgNUIrh7FU2I5RGXe1bVgroRkctFxB03INFrj1jeaGvLUo2w6oWHznWD1MRQfeCk3YcEcjcu0kcKebhmPTQqfM2HVq/AHJdfpSFMBIL5e3sjkwiJbQSK60bLl2TnLr2o4PnR3cYCy1vni+pxRp+gHUx+DTvMxjrq7LU/2E7tMay3uvtkX6GiL/ts3obvZPlKoyYwixrPd1rG9vb2//AAAA//8DAMn6CWw2BAAA
|
||||
@ -0,0 +1,5 @@
|
||||
https://dc.services.visualstudio.com/v2/track
|
||||
Content-Type:application/x-json-stream
|
||||
Content-Encoding:gzip
|
||||
|
||||
H4sIAAAAAAAACpSUTW/cNhCG/4rAsyjzS5Som7FO0IWbepEs0qI3ijOKCcvUgqScGMb+94LaLRKfil4IYsiZeeeZId9IsM9IBnJ7On14wZATqUn2m0kwoSnTlPEjZwPrBs4b0apWcvk3qYm/x1cykE45x51BKlEjVcBaOsrJUQA0nHFgolUlpv2WyPBGrG8AX7zDZklfMSa/BDKQP32A5XvJbX3j5mWFJi4z7kPKNrgi5o/97p7+fns4PhwutxKm4tx4IAMxVo3Ya6B9341UMY60d6anwnLe9RKVMUWE9Y0PGWOwc5Pg6Wd+WHLA7AbRCNkwKgw51wRstkXyaBMeX09FxYbortjrzbxthzfygpEMov4X5iXcjZv9zXMaVz/DzWXNNj2lbZ2sy0t8JTU5xeWEMXvc+NymhM/j/Hq06enj5U7Zpg8/0K0ZYbesIZOBcNmTmmyA5tlmv4TqqnE3ewx5O8zRh+Tdf8WSpCYPX6rDbPO0xOd3/fhywbz/H5Q/WffoA1b7OzIQxlGA7JFOgmuqerDUttJQraWekDvTCU5qsltC9mFd1lTtQ8ZvcSuJDOSjnRNe9N1G9+gzurzGUudfuiQ7xAVWl6ufveSsYY1krDit+bTm6jOCj+gyliKOcb3G+/reRWixOe3WGDHk6mDzY/WbTY9b5b3j1lhjrXZuwpbhxIBZzTpADRpBCehAGQVaGea0siNXYFs+TsVCaoJldKptWrlxndbA6Qi8pUpaoNYqRp2TTpgOnWvhHcfqYS5+1vVMIUhqcAKqDLfUKCkpyNZ2Qk9dJ6ZfkFzngbMCGPDl8u42BRqgR9CcClUUGMNpb9qOjl0/mlH32Lqi+R5jwPkXUJ+8i0taplxdJ6R6h+7zGsrfUW3T8t0H+mPr0d3injBWpcfWh/JUrm09n8/nfwAAAP//AwCG30+3ggQAAA==
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -59,6 +59,18 @@ public sealed class LocationsController(ILocationService locations) : Controller
|
||||
return RedirectToAction(nameof(Details), new { id = model.FromLocationID });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> DropRelationship(LocationRelationshipEditViewModel model)
|
||||
{
|
||||
if (model.FromLocationID != model.ToLocationID)
|
||||
{
|
||||
await locations.SaveRelationshipAsync(model);
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(Index), new { projectId = model.ProjectID });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> ArchiveRelationship(int id, int returnLocationId)
|
||||
|
||||
@ -59,6 +59,18 @@ public sealed class StoryAssetsController(IAssetService assets) : Controller
|
||||
return RedirectToAction(nameof(Details), new { id = returnAssetId });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> DropDependency(AssetDependencyEditViewModel model)
|
||||
{
|
||||
if (model.SourceAssetID != model.TargetAssetID)
|
||||
{
|
||||
await assets.SaveDependencyAsync(model);
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(Index), new { projectId = model.ProjectID });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> DeleteDependency(int id, int returnAssetId)
|
||||
|
||||
@ -47,4 +47,61 @@ public sealed class TimelineController(ITimelineService timeline) : Controller
|
||||
await timeline.ArchivePresetAsync(id);
|
||||
return RedirectToAction(nameof(Index), new { projectId, bookId });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> DropMoveScene(TimelineSceneDropMoveViewModel model)
|
||||
{
|
||||
var summary = await timeline.MoveSceneByDropAsync(model);
|
||||
if (summary is not null)
|
||||
{
|
||||
TempData["TimelineDragMessage"] = $"Scene moved. Validation found {summary.WarningCount} warning{(summary.WarningCount == 1 ? "" : "s")}.";
|
||||
}
|
||||
return RedirectToAction(nameof(Index), new { projectId = model.ProjectID, bookId = model.BookID, selectedSceneId = model.SceneID });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> DropCharacter(TimelineSceneCharacterDropViewModel model)
|
||||
{
|
||||
await RunDropActionAsync(() => timeline.AddCharacterByDropAsync(model), "Character added to scene.");
|
||||
return RedirectToAction(nameof(Index), new { projectId = model.ReturnProjectID, bookId = model.ReturnBookID, selectedSceneId = model.SceneID });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> DropAssetEvent(TimelineAssetEventDropViewModel model)
|
||||
{
|
||||
await RunDropActionAsync(() => timeline.AddAssetEventByDropAsync(model), "Asset event added to scene.");
|
||||
return RedirectToAction(nameof(Index), new { projectId = model.ReturnProjectID, bookId = model.ReturnBookID, selectedSceneId = model.SceneID });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> DropAssetDependency(TimelineAssetDependencyDropViewModel model)
|
||||
{
|
||||
await RunDropActionAsync(() => timeline.AddAssetDependencyByDropAsync(model), "Asset dependency saved.");
|
||||
return RedirectToAction(nameof(Index), new { projectId = model.ReturnProjectID, bookId = model.ReturnBookID });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> DropLocationRelationship(TimelineLocationRelationshipDropViewModel model)
|
||||
{
|
||||
await RunDropActionAsync(() => timeline.AddLocationRelationshipByDropAsync(model), "Location relationship saved.");
|
||||
return RedirectToAction(nameof(Index), new { projectId = model.ProjectID, bookId = model.ReturnBookID });
|
||||
}
|
||||
|
||||
private async Task RunDropActionAsync(Func<Task> action, string successMessage)
|
||||
{
|
||||
try
|
||||
{
|
||||
await action();
|
||||
TempData["TimelineDragMessage"] = successMessage;
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
TempData["TimelineDragMessage"] = ex.Message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,6 +40,7 @@ public interface ISceneRepository
|
||||
Task<IReadOnlyList<ChapterOption>> ListChapterOptionsAsync(int projectId);
|
||||
Task<SceneMovePreview?> GetMovePreviewAsync(int sceneId, int targetChapterId, string position);
|
||||
Task MoveToChapterAsync(int sceneId, int targetChapterId, string position, bool renumber);
|
||||
Task MoveRelativeAsync(int sceneId, int anchorSceneId, string position);
|
||||
Task RenumberChapterAsync(int chapterId);
|
||||
}
|
||||
|
||||
@ -752,6 +753,15 @@ public sealed class SceneRepository(ISqlConnectionFactory connectionFactory) : I
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
|
||||
public async Task MoveRelativeAsync(int sceneId, int anchorSceneId, string position)
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
await connection.ExecuteAsync(
|
||||
"dbo.Scene_MoveRelative",
|
||||
new { SceneID = sceneId, AnchorSceneID = anchorSceneId, Position = position },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
|
||||
public async Task RenumberChapterAsync(int chapterId)
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
|
||||
@ -39,6 +39,7 @@ public interface ISceneService
|
||||
Task<int> SaveAsync(SceneEditViewModel model);
|
||||
Task ArchiveAsync(int sceneId);
|
||||
Task MoveAsync(int sceneId, string direction);
|
||||
Task MoveRelativeAsync(int sceneId, int anchorSceneId, string position);
|
||||
Task<SceneMovePreviewViewModel?> GetMovePreviewAsync(SceneMoveRequestViewModel request);
|
||||
Task<ContinuityValidationSummary?> MoveToChapterAsync(SceneMoveRequestViewModel request);
|
||||
Task RenumberChapterAsync(int chapterId);
|
||||
@ -132,6 +133,11 @@ public interface ITimelineService
|
||||
Task<int> SavePresetAsync(TimelinePresetSaveViewModel model);
|
||||
Task<TimelineViewPreset?> GetPresetAsync(int timelineViewPresetId);
|
||||
Task ArchivePresetAsync(int timelineViewPresetId);
|
||||
Task<ContinuityValidationSummary?> MoveSceneByDropAsync(TimelineSceneDropMoveViewModel model);
|
||||
Task AddCharacterByDropAsync(TimelineSceneCharacterDropViewModel model);
|
||||
Task AddAssetEventByDropAsync(TimelineAssetEventDropViewModel model);
|
||||
Task AddAssetDependencyByDropAsync(TimelineAssetDependencyDropViewModel model);
|
||||
Task AddLocationRelationshipByDropAsync(TimelineLocationRelationshipDropViewModel model);
|
||||
}
|
||||
|
||||
public interface IAnalyticsService
|
||||
@ -487,6 +493,9 @@ public sealed class SceneService(
|
||||
}
|
||||
}
|
||||
|
||||
public Task MoveRelativeAsync(int sceneId, int anchorSceneId, string position) =>
|
||||
scenes.MoveRelativeAsync(sceneId, anchorSceneId, position);
|
||||
|
||||
private async Task<(Project Project, Book Book, Chapter Chapter)?> GetContextAsync(int chapterId)
|
||||
{
|
||||
var chapter = await chapters.GetAsync(chapterId);
|
||||
@ -770,6 +779,9 @@ public sealed class TimelineService(
|
||||
|
||||
var allBooks = await books.ListByProjectAsync(projectId);
|
||||
var lookupData = await lookups.GetAllAsync();
|
||||
var assetLookupData = await assets.GetLookupsAsync();
|
||||
var characterLookupData = await characters.GetLookupsAsync();
|
||||
var locationLookupData = await locations.GetLookupsAsync();
|
||||
var plotLines = await plots.ListPlotLinesAsync(projectId);
|
||||
var plotThreads = await plots.ListPlotThreadsByProjectAsync(projectId);
|
||||
var allAssets = await assets.ListAssetsAsync(projectId);
|
||||
@ -830,9 +842,15 @@ public sealed class TimelineService(
|
||||
PlotLineOptions = plotLines.Select(x => new SelectListItem(x.PlotLineName, x.PlotLineID.ToString(), x.PlotLineID == filter.PlotLineID)).ToList(),
|
||||
PlotThreadOptions = plotThreads.Select(x => new SelectListItem(x.ThreadTitle, x.PlotThreadID.ToString(), x.PlotThreadID == filter.PlotThreadID)).ToList(),
|
||||
AssetOptions = allAssets.Select(x => new SelectListItem(x.AssetName, x.StoryAssetID.ToString(), x.StoryAssetID == filter.StoryAssetID)).ToList(),
|
||||
AssetEventTypeOptions = ChapterService.ToSelectList(assetLookupData.AssetEventTypes, x => x.AssetEventTypeID, x => x.TypeName),
|
||||
AssetStateOptions = assetLookupData.AssetStates.Select(x => new SelectListItem(x.StateName, x.AssetStateID.ToString())).ToList(),
|
||||
AssetDependencyTypeOptions = ChapterService.ToSelectList(assetLookupData.AssetDependencyTypes, x => x.AssetDependencyTypeID, x => x.TypeName),
|
||||
CharacterOptions = allCharacters.Select(x => new SelectListItem(x.CharacterName, x.CharacterID.ToString(), x.CharacterID == filter.CharacterID)).ToList(),
|
||||
CharacterRoleOptions = characterLookupData.RoleTypes.Select(x => new SelectListItem(x.TypeName, x.CharacterRoleInSceneTypeID.ToString())).ToList(),
|
||||
PresenceTypeOptions = characterLookupData.PresenceTypes.Select(x => new SelectListItem(x.TypeName, x.PresenceTypeID.ToString())).ToList(),
|
||||
PovCharacterOptions = allCharacters.Select(x => new SelectListItem(x.CharacterName, x.CharacterID.ToString(), x.CharacterID == filter.POVCharacterID)).ToList(),
|
||||
LocationOptions = allLocations.Select(x => new SelectListItem(x.LocationPath, x.LocationID.ToString(), x.LocationID == filter.LocationID)).ToList(),
|
||||
LocationRelationshipTypeOptions = ChapterService.ToSelectList(locationLookupData.RelationshipTypes, x => x.LocationRelationshipTypeID, x => x.TypeName),
|
||||
WarningSeverityOptions = warningLookups.Severities.Select(x => new SelectListItem(x.SeverityName, x.WarningSeverityID.ToString(), x.WarningSeverityID == filter.WarningSeverityID)).ToList(),
|
||||
WarningTypeOptions = warningLookups.WarningTypes.Select(x => new SelectListItem(x.TypeName, x.WarningTypeID.ToString(), x.WarningTypeID == filter.WarningTypeID)).ToList(),
|
||||
RevisionStatusOptions = lookupData.RevisionStatuses.Select(x => new SelectListItem(x.StatusName, x.RevisionStatusID.ToString(), x.RevisionStatusID == filter.RevisionStatusID)).ToList(),
|
||||
@ -865,6 +883,134 @@ public sealed class TimelineService(
|
||||
|
||||
public Task ArchivePresetAsync(int timelineViewPresetId) => presets.ArchiveAsync(timelineViewPresetId);
|
||||
|
||||
public async Task<ContinuityValidationSummary?> MoveSceneByDropAsync(TimelineSceneDropMoveViewModel model)
|
||||
{
|
||||
if (model.SceneID == model.AnchorSceneID)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
await scenes.MoveRelativeAsync(model.SceneID, model.AnchorSceneID, model.Position == "Before" ? "Before" : "After");
|
||||
await warnings.FinaliseSceneDependencyWarningsAsync(model.ProjectID, model.BookID);
|
||||
return model.BookID.HasValue
|
||||
? await warnings.ValidateBookAsync(model.BookID.Value)
|
||||
: await warnings.ValidateProjectAsync(model.ProjectID);
|
||||
}
|
||||
|
||||
public async Task AddCharacterByDropAsync(TimelineSceneCharacterDropViewModel model)
|
||||
{
|
||||
var characterId = model.CharacterID.GetValueOrDefault();
|
||||
if (characterId == 0)
|
||||
{
|
||||
throw new InvalidOperationException("Choose a character to drop onto the scene.");
|
||||
}
|
||||
|
||||
await characters.SaveSceneCharacterAsync(new SceneCharacter
|
||||
{
|
||||
SceneID = model.SceneID,
|
||||
CharacterID = characterId,
|
||||
RoleInSceneTypeID = model.RoleInSceneTypeID,
|
||||
PresenceTypeID = model.PresenceTypeID,
|
||||
LocationID = model.LocationID
|
||||
});
|
||||
}
|
||||
|
||||
public async Task AddAssetEventByDropAsync(TimelineAssetEventDropViewModel model)
|
||||
{
|
||||
var assetId = model.StoryAssetID.GetValueOrDefault();
|
||||
if (assetId == 0)
|
||||
{
|
||||
throw new InvalidOperationException("Choose an asset to drop onto the scene.");
|
||||
}
|
||||
|
||||
var lookupData = await assets.GetLookupsAsync();
|
||||
var eventType = lookupData.AssetEventTypes.FirstOrDefault(x => x.AssetEventTypeID == model.AssetEventTypeID)
|
||||
?? lookupData.AssetEventTypes.First();
|
||||
var title = string.IsNullOrWhiteSpace(model.EventTitle) ? eventType.TypeName : model.EventTitle.Trim();
|
||||
var description = model.EventDescription;
|
||||
if (!string.IsNullOrWhiteSpace(model.CustodyNote))
|
||||
{
|
||||
description = string.IsNullOrWhiteSpace(description)
|
||||
? $"Custody note: {model.CustodyNote.Trim()}"
|
||||
: $"{description.Trim()}\n\nCustody note: {model.CustodyNote.Trim()}";
|
||||
}
|
||||
|
||||
await assets.SaveEventAsync(new AssetEvent
|
||||
{
|
||||
StoryAssetID = assetId,
|
||||
SceneID = model.SceneID,
|
||||
AssetEventTypeID = eventType.AssetEventTypeID,
|
||||
FromStateID = model.FromStateID,
|
||||
ToStateID = model.ToStateID,
|
||||
EventTitle = title,
|
||||
EventDescription = description
|
||||
});
|
||||
|
||||
if (model.LocationID.HasValue)
|
||||
{
|
||||
await locations.SaveSceneAssetLocationAsync(new SceneAssetLocation
|
||||
{
|
||||
SceneID = model.SceneID,
|
||||
StoryAssetID = assetId,
|
||||
LocationID = model.LocationID.Value,
|
||||
Description = title
|
||||
}, updateCurrentLocation: true);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task AddAssetDependencyByDropAsync(TimelineAssetDependencyDropViewModel model)
|
||||
{
|
||||
if (model.SourceAssetID == model.TargetAssetID)
|
||||
{
|
||||
throw new InvalidOperationException("An asset cannot depend on itself.");
|
||||
}
|
||||
|
||||
var existing = await assets.ListDependenciesByAssetAsync(model.SourceAssetID);
|
||||
if (existing.Any(x => x.SourceAssetID == model.SourceAssetID && x.TargetAssetID == model.TargetAssetID && x.AssetDependencyTypeID == model.AssetDependencyTypeID))
|
||||
{
|
||||
throw new InvalidOperationException("That asset dependency already exists.");
|
||||
}
|
||||
|
||||
await assets.SaveDependencyAsync(new AssetDependency
|
||||
{
|
||||
SourceAssetID = model.SourceAssetID,
|
||||
TargetAssetID = model.TargetAssetID,
|
||||
AssetDependencyTypeID = model.AssetDependencyTypeID,
|
||||
SourceRequiredStateID = model.SourceRequiredStateID,
|
||||
TargetBlockedStateID = model.TargetBlockedStateID,
|
||||
Description = model.Description
|
||||
});
|
||||
}
|
||||
|
||||
public async Task AddLocationRelationshipByDropAsync(TimelineLocationRelationshipDropViewModel model)
|
||||
{
|
||||
if (model.FromLocationID == model.ToLocationID)
|
||||
{
|
||||
throw new InvalidOperationException("A location cannot be related to itself.");
|
||||
}
|
||||
|
||||
var existing = await locations.ListRelationshipsByLocationAsync(model.FromLocationID);
|
||||
if (existing.Any(x => x.FromLocationID == model.FromLocationID && x.ToLocationID == model.ToLocationID && x.LocationRelationshipTypeID == model.LocationRelationshipTypeID))
|
||||
{
|
||||
throw new InvalidOperationException("That location relationship already exists.");
|
||||
}
|
||||
|
||||
await locations.SaveRelationshipAsync(new LocationRelationship
|
||||
{
|
||||
FromLocationID = model.FromLocationID,
|
||||
ToLocationID = model.ToLocationID,
|
||||
LocationRelationshipTypeID = model.LocationRelationshipTypeID,
|
||||
IsBidirectional = model.IsBidirectional,
|
||||
Strength = model.Strength,
|
||||
CanHearNormalSpeech = model.CanHearNormalSpeech,
|
||||
CanHearLoudNoise = model.CanHearLoudNoise,
|
||||
CanSeeMovement = model.CanSeeMovement,
|
||||
CanSeeClearly = model.CanSeeClearly,
|
||||
CanTravelDirectly = model.CanTravelDirectly,
|
||||
Notes = model.Notes
|
||||
});
|
||||
}
|
||||
|
||||
public Task<int> SavePresetAsync(TimelinePresetSaveViewModel model)
|
||||
{
|
||||
var filter = model.Filter;
|
||||
@ -1553,10 +1699,13 @@ public sealed class AssetService(IProjectRepository projects, IAssetRepository a
|
||||
public async Task<StoryAssetListViewModel?> GetAssetsAsync(int projectId)
|
||||
{
|
||||
var project = await projects.GetAsync(projectId);
|
||||
var lookupData = await assets.GetLookupsAsync();
|
||||
return project is null ? null : new StoryAssetListViewModel
|
||||
{
|
||||
Project = project,
|
||||
Assets = await assets.ListAssetsAsync(projectId)
|
||||
Assets = await assets.ListAssetsAsync(projectId),
|
||||
AssetDependencyTypeOptions = ChapterService.ToSelectList(lookupData.AssetDependencyTypes, x => x.AssetDependencyTypeID, x => x.TypeName),
|
||||
AssetStateOptions = OptionalStateList(lookupData.AssetStates)
|
||||
};
|
||||
}
|
||||
|
||||
@ -1754,10 +1903,12 @@ public sealed class LocationService(IProjectRepository projects, ILocationReposi
|
||||
public async Task<LocationListViewModel?> GetLocationsAsync(int projectId)
|
||||
{
|
||||
var project = await projects.GetAsync(projectId);
|
||||
var lookupData = await locations.GetLookupsAsync();
|
||||
return project is null ? null : new LocationListViewModel
|
||||
{
|
||||
Project = project,
|
||||
Locations = await locations.ListByProjectAsync(projectId)
|
||||
Locations = await locations.ListByProjectAsync(projectId),
|
||||
RelationshipTypeOptions = ChapterService.ToSelectList(lookupData.RelationshipTypes, x => x.LocationRelationshipTypeID, x => x.TypeName)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
58
PlotLine/Sql/013_Phase1N_DragDrop.sql
Normal file
58
PlotLine/Sql/013_Phase1N_DragDrop.sql
Normal file
@ -0,0 +1,58 @@
|
||||
CREATE OR ALTER PROCEDURE dbo.Scene_MoveRelative
|
||||
@SceneID int,
|
||||
@AnchorSceneID int,
|
||||
@Position nvarchar(10)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
IF @SceneID = @AnchorSceneID
|
||||
RETURN;
|
||||
|
||||
DECLARE @SourceChapterID int = (SELECT ChapterID FROM dbo.Scenes WHERE SceneID = @SceneID);
|
||||
DECLARE @TargetChapterID int;
|
||||
DECLARE @AnchorSortOrder int;
|
||||
DECLARE @NewSortOrder int;
|
||||
|
||||
SELECT @TargetChapterID = ChapterID, @AnchorSortOrder = SortOrder
|
||||
FROM dbo.Scenes
|
||||
WHERE SceneID = @AnchorSceneID AND IsArchived = 0;
|
||||
|
||||
IF @TargetChapterID IS NULL
|
||||
RETURN;
|
||||
|
||||
IF @Position = N'Before'
|
||||
BEGIN
|
||||
SELECT @NewSortOrder = (ISNULL(MAX(SortOrder), @AnchorSortOrder - 20) + @AnchorSortOrder) / 2
|
||||
FROM dbo.Scenes
|
||||
WHERE ChapterID = @TargetChapterID
|
||||
AND IsArchived = 0
|
||||
AND SceneID NOT IN (@SceneID, @AnchorSceneID)
|
||||
AND SortOrder < @AnchorSortOrder;
|
||||
|
||||
IF @NewSortOrder >= @AnchorSortOrder
|
||||
SET @NewSortOrder = @AnchorSortOrder - 5;
|
||||
END
|
||||
ELSE
|
||||
BEGIN
|
||||
SELECT @NewSortOrder = (@AnchorSortOrder + ISNULL(MIN(SortOrder), @AnchorSortOrder + 20)) / 2
|
||||
FROM dbo.Scenes
|
||||
WHERE ChapterID = @TargetChapterID
|
||||
AND IsArchived = 0
|
||||
AND SceneID NOT IN (@SceneID, @AnchorSceneID)
|
||||
AND SortOrder > @AnchorSortOrder;
|
||||
|
||||
IF @NewSortOrder <= @AnchorSortOrder
|
||||
SET @NewSortOrder = @AnchorSortOrder + 5;
|
||||
END;
|
||||
|
||||
UPDATE dbo.Scenes
|
||||
SET ChapterID = @TargetChapterID,
|
||||
SortOrder = @NewSortOrder,
|
||||
UpdatedDate = SYSUTCDATETIME()
|
||||
WHERE SceneID = @SceneID;
|
||||
|
||||
EXEC dbo.Scene_NormalizeSortOrder @SourceChapterID;
|
||||
EXEC dbo.Scene_NormalizeSortOrder @TargetChapterID;
|
||||
END;
|
||||
GO
|
||||
@ -203,9 +203,15 @@ public sealed class TimelineViewModel
|
||||
public IReadOnlyList<SelectListItem> PlotLineOptions { get; set; } = [];
|
||||
public IReadOnlyList<SelectListItem> PlotThreadOptions { get; set; } = [];
|
||||
public IReadOnlyList<SelectListItem> AssetOptions { get; set; } = [];
|
||||
public IReadOnlyList<SelectListItem> AssetEventTypeOptions { get; set; } = [];
|
||||
public IReadOnlyList<SelectListItem> AssetStateOptions { get; set; } = [];
|
||||
public IReadOnlyList<SelectListItem> AssetDependencyTypeOptions { get; set; } = [];
|
||||
public IReadOnlyList<SelectListItem> CharacterOptions { get; set; } = [];
|
||||
public IReadOnlyList<SelectListItem> CharacterRoleOptions { get; set; } = [];
|
||||
public IReadOnlyList<SelectListItem> PresenceTypeOptions { get; set; } = [];
|
||||
public IReadOnlyList<SelectListItem> PovCharacterOptions { get; set; } = [];
|
||||
public IReadOnlyList<SelectListItem> LocationOptions { get; set; } = [];
|
||||
public IReadOnlyList<SelectListItem> LocationRelationshipTypeOptions { get; set; } = [];
|
||||
public IReadOnlyList<SelectListItem> WarningSeverityOptions { get; set; } = [];
|
||||
public IReadOnlyList<SelectListItem> WarningTypeOptions { get; set; } = [];
|
||||
public IReadOnlyList<SelectListItem> RevisionStatusOptions { get; set; } = [];
|
||||
@ -265,6 +271,71 @@ public sealed class TimelinePresetSaveViewModel
|
||||
public string VisibilityJson { get; set; } = "{}";
|
||||
}
|
||||
|
||||
public sealed class TimelineSceneDropMoveViewModel
|
||||
{
|
||||
public int ProjectID { get; set; }
|
||||
public int? BookID { get; set; }
|
||||
public int SceneID { get; set; }
|
||||
public int AnchorSceneID { get; set; }
|
||||
public string Position { get; set; } = "After";
|
||||
}
|
||||
|
||||
public sealed class TimelineSceneCharacterDropViewModel
|
||||
{
|
||||
public int SceneID { get; set; }
|
||||
public int? CharacterID { get; set; }
|
||||
public int? RoleInSceneTypeID { get; set; }
|
||||
public int? PresenceTypeID { get; set; }
|
||||
public int? LocationID { get; set; }
|
||||
public int? ReturnProjectID { get; set; }
|
||||
public int? ReturnBookID { get; set; }
|
||||
}
|
||||
|
||||
public sealed class TimelineAssetEventDropViewModel
|
||||
{
|
||||
public int SceneID { get; set; }
|
||||
public int? StoryAssetID { get; set; }
|
||||
public int AssetEventTypeID { get; set; }
|
||||
public int? FromStateID { get; set; }
|
||||
public int? ToStateID { get; set; }
|
||||
public string EventTitle { get; set; } = string.Empty;
|
||||
public string? EventDescription { get; set; }
|
||||
public int? ReturnProjectID { get; set; }
|
||||
public int? ReturnBookID { get; set; }
|
||||
public int? LocationID { get; set; }
|
||||
public string? CustodyNote { get; set; }
|
||||
}
|
||||
|
||||
public sealed class TimelineAssetDependencyDropViewModel
|
||||
{
|
||||
public int ProjectID { get; set; }
|
||||
public int SourceAssetID { get; set; }
|
||||
public int TargetAssetID { get; set; }
|
||||
public int AssetDependencyTypeID { get; set; }
|
||||
public int? SourceRequiredStateID { get; set; }
|
||||
public int? TargetBlockedStateID { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public int? ReturnProjectID { get; set; }
|
||||
public int? ReturnBookID { get; set; }
|
||||
}
|
||||
|
||||
public sealed class TimelineLocationRelationshipDropViewModel
|
||||
{
|
||||
public int ProjectID { get; set; }
|
||||
public int FromLocationID { get; set; }
|
||||
public int ToLocationID { get; set; }
|
||||
public int LocationRelationshipTypeID { get; set; }
|
||||
public bool IsBidirectional { get; set; }
|
||||
public int? Strength { get; set; }
|
||||
public bool CanHearNormalSpeech { get; set; }
|
||||
public bool CanHearLoudNoise { get; set; }
|
||||
public bool CanSeeMovement { get; set; }
|
||||
public bool CanSeeClearly { get; set; }
|
||||
public bool CanTravelDirectly { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
public int? ReturnBookID { get; set; }
|
||||
}
|
||||
|
||||
public sealed class BookTimelineViewModel
|
||||
{
|
||||
public Book Book { get; set; } = new();
|
||||
@ -421,6 +492,8 @@ public sealed class StoryAssetListViewModel
|
||||
{
|
||||
public Project Project { get; set; } = new();
|
||||
public IReadOnlyList<StoryAsset> Assets { get; set; } = [];
|
||||
public IReadOnlyList<SelectListItem> AssetDependencyTypeOptions { get; set; } = [];
|
||||
public IReadOnlyList<SelectListItem> AssetStateOptions { get; set; } = [];
|
||||
}
|
||||
|
||||
public sealed class StoryAssetEditViewModel
|
||||
@ -674,6 +747,7 @@ public sealed class LocationListViewModel
|
||||
{
|
||||
public Project Project { get; set; } = new();
|
||||
public IReadOnlyList<LocationItem> Locations { get; set; } = [];
|
||||
public IReadOnlyList<SelectListItem> RelationshipTypeOptions { get; set; } = [];
|
||||
}
|
||||
|
||||
public sealed class LocationEditViewModel
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
<div class="asset-card-grid">
|
||||
@foreach (var character in Model.Characters)
|
||||
{
|
||||
<article class="asset-card character-card">
|
||||
<article class="asset-card character-card" draggable="true" data-drag-type="character" data-drag-id="@character.CharacterID" data-drag-label="@character.CharacterName">
|
||||
<div>
|
||||
<p class="eyebrow">Character</p>
|
||||
<h2><a asp-action="Details" asp-route-id="@character.CharacterID">@character.CharacterName</a></h2>
|
||||
|
||||
@ -24,6 +24,38 @@
|
||||
<a class="btn btn-primary" asp-action="Create" asp-route-projectId="@Model.Project.ProjectID">New location</a>
|
||||
</div>
|
||||
|
||||
<section class="drag-drop-panel d-none" data-drag-drop-panel>
|
||||
<div class="drag-drop-panel-header">
|
||||
<div>
|
||||
<p class="eyebrow" data-drag-panel-kicker>Location relationship</p>
|
||||
<h2 data-drag-panel-title>Create location relationship</h2>
|
||||
</div>
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm" data-drag-cancel>Cancel</button>
|
||||
</div>
|
||||
<p class="muted" data-drag-panel-summary></p>
|
||||
<form asp-controller="Locations" asp-action="DropRelationship" method="post" class="drag-drop-form" data-drag-form="location-relationship">
|
||||
<input type="hidden" name="ProjectID" value="@Model.Project.ProjectID" />
|
||||
<input type="hidden" name="FromLocationID" />
|
||||
<input type="hidden" name="ToLocationID" />
|
||||
<div class="row g-2">
|
||||
<div class="col-12"><label class="form-label">Relationship type</label><select class="form-select form-select-sm" name="LocationRelationshipTypeID" asp-items="Model.RelationshipTypeOptions"></select></div>
|
||||
<div class="col-12 drag-check-grid">
|
||||
<label><input type="checkbox" name="IsBidirectional" value="true" /> Bidirectional</label>
|
||||
<label><input type="checkbox" name="CanHearNormalSpeech" value="true" /> Can hear speech</label>
|
||||
<label><input type="checkbox" name="CanHearLoudNoise" value="true" /> Can hear loud noise</label>
|
||||
<label><input type="checkbox" name="CanSeeMovement" value="true" /> Can see movement</label>
|
||||
<label><input type="checkbox" name="CanSeeClearly" value="true" /> Can see clearly</label>
|
||||
<label><input type="checkbox" name="CanTravelDirectly" value="true" /> Can travel directly</label>
|
||||
</div>
|
||||
<div class="col-12"><label class="form-label">Notes</label><textarea class="form-control form-control-sm" name="Notes" rows="2"></textarea></div>
|
||||
</div>
|
||||
<div class="button-row mt-2">
|
||||
<button class="btn btn-primary btn-sm" type="submit">Save</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" type="button" data-drag-cancel>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
@if (!Model.Locations.Any())
|
||||
{
|
||||
<section class="empty-panel">
|
||||
@ -36,7 +68,7 @@ else
|
||||
<section class="edit-panel location-tree">
|
||||
@foreach (var location in Model.Locations)
|
||||
{
|
||||
<div class="location-tree-row" style="--location-depth:@location.Depth">
|
||||
<div class="location-tree-row" draggable="true" data-drag-type="location" data-drag-id="@location.LocationID" data-drag-label="@location.LocationName" style="--location-depth:@location.Depth">
|
||||
<div>
|
||||
<a asp-action="Details" asp-route-id="@location.LocationID"><strong>@location.LocationName</strong></a>
|
||||
@if (!string.IsNullOrWhiteSpace(location.LocationTypeName))
|
||||
|
||||
@ -27,6 +27,33 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="drag-drop-panel d-none" data-drag-drop-panel>
|
||||
<div class="drag-drop-panel-header">
|
||||
<div>
|
||||
<p class="eyebrow" data-drag-panel-kicker>Asset dependency</p>
|
||||
<h2 data-drag-panel-title>Create asset dependency</h2>
|
||||
</div>
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm" data-drag-cancel>Cancel</button>
|
||||
</div>
|
||||
<p class="muted" data-drag-panel-summary></p>
|
||||
<form asp-controller="StoryAssets" asp-action="DropDependency" method="post" class="drag-drop-form" data-drag-form="asset-dependency">
|
||||
<input type="hidden" name="ProjectID" value="@Model.Project.ProjectID" />
|
||||
<input type="hidden" name="SourceAssetID" />
|
||||
<input type="hidden" name="TargetAssetID" />
|
||||
<div class="row g-2">
|
||||
<div class="col-12"><label class="form-label">Dependency type</label><select class="form-select form-select-sm" name="AssetDependencyTypeID" asp-items="Model.AssetDependencyTypeOptions"></select></div>
|
||||
<div class="col-6"><label class="form-label">Source required state</label><select class="form-select form-select-sm" name="SourceRequiredStateID" asp-items="Model.AssetStateOptions"><option value="">Optional</option></select></div>
|
||||
<div class="col-6"><label class="form-label">Target blocked state</label><select class="form-select form-select-sm" name="TargetBlockedStateID" asp-items="Model.AssetStateOptions"><option value="">Optional</option></select></div>
|
||||
<div class="col-12"><label class="form-label">Notes</label><textarea class="form-control form-control-sm" name="Description" rows="2"></textarea></div>
|
||||
</div>
|
||||
<p class="plain-preview" data-asset-dependency-preview></p>
|
||||
<div class="button-row mt-2">
|
||||
<button class="btn btn-primary btn-sm" type="submit">Save Dependency</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" type="button" data-drag-cancel>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="list-section">
|
||||
@if (!Model.Assets.Any())
|
||||
{
|
||||
@ -37,7 +64,7 @@
|
||||
<div class="asset-card-grid">
|
||||
@foreach (var asset in Model.Assets)
|
||||
{
|
||||
<article class="asset-card">
|
||||
<article class="asset-card" draggable="true" data-drag-type="asset" data-drag-id="@asset.StoryAssetID" data-drag-label="@asset.AssetName" data-asset-drop-target="true" data-asset-id="@asset.StoryAssetID" data-asset-label="@asset.AssetName">
|
||||
<div>
|
||||
<p class="eyebrow">@asset.KindName</p>
|
||||
<h2><a asp-action="Details" asp-route-id="@asset.StoryAssetID">@asset.AssetName</a></h2>
|
||||
|
||||
@ -179,8 +179,9 @@
|
||||
</form>
|
||||
</details>
|
||||
|
||||
<div class="timeline-visibility-panel">
|
||||
<span class="eyebrow">Visibility</span>
|
||||
<details class="timeline-control-group">
|
||||
<summary>View</summary>
|
||||
<div class="timeline-visibility-panel">
|
||||
<label class="timeline-control-select">Density
|
||||
<select class="form-select form-select-sm" data-timeline-density>
|
||||
<option value="compact">Compact</option>
|
||||
@ -202,10 +203,12 @@
|
||||
<label><input type="checkbox" data-timeline-toggle="locations" checked /> Locations</label>
|
||||
<label><input type="checkbox" data-timeline-toggle="warnings" checked /> Warnings</label>
|
||||
<label><input type="checkbox" data-timeline-toggle="metrics" checked /> Metrics</label>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<div class="timeline-jump-panel">
|
||||
<span class="eyebrow">Jump / search</span>
|
||||
<details class="timeline-control-group">
|
||||
<summary>Jump</summary>
|
||||
<div class="timeline-jump-panel">
|
||||
<input class="form-control form-control-sm" type="search" placeholder="Search timeline..." data-timeline-search />
|
||||
<select class="form-select form-select-sm" data-timeline-jump>
|
||||
<option value="">Jump to scene</option>
|
||||
@ -259,9 +262,12 @@
|
||||
}
|
||||
</select>
|
||||
<span class="timeline-shortcut-hint">Shortcuts: arrows scroll, +/- zoom, Esc clears focus</span>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<div class="timeline-preset-panel">
|
||||
<details class="timeline-control-group">
|
||||
<summary>Saved Views</summary>
|
||||
<div class="timeline-preset-panel">
|
||||
<form asp-action="SavePreset" method="post" data-timeline-preset-form>
|
||||
<input type="hidden" name="ProjectID" value="@Model.Project.ProjectID" />
|
||||
<input type="hidden" name="BookID" value="@Model.SelectedBookID" />
|
||||
@ -308,7 +314,8 @@
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
|
||||
@if (!string.IsNullOrWhiteSpace(Model.FocusLabel))
|
||||
@ -320,6 +327,32 @@
|
||||
</section>
|
||||
}
|
||||
|
||||
@if (TempData["TimelineDragMessage"] is string dragMessage)
|
||||
{
|
||||
<section class="timeline-focus-banner drag-message">
|
||||
<strong>@dragMessage</strong>
|
||||
<a class="btn btn-outline-secondary btn-sm" asp-controller="Warnings" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID" asp-route-bookId="@Model.SelectedBookID">Review warnings</a>
|
||||
</section>
|
||||
}
|
||||
|
||||
<details class="timeline-drag-palette" data-drag-palette>
|
||||
<summary>Drag Sources</summary>
|
||||
<div class="drag-chip-group">
|
||||
@foreach (var character in Model.CharacterOptions.Take(10))
|
||||
{
|
||||
<span class="drag-chip character" draggable="true" data-drag-type="character" data-drag-id="@character.Value" data-drag-label="@character.Text">@character.Text</span>
|
||||
}
|
||||
@foreach (var asset in Model.AssetOptions.Take(10))
|
||||
{
|
||||
<span class="drag-chip asset" draggable="true" data-drag-type="asset" data-drag-id="@asset.Value" data-drag-label="@asset.Text">@asset.Text</span>
|
||||
}
|
||||
@foreach (var location in Model.LocationOptions.Take(10))
|
||||
{
|
||||
<span class="drag-chip location" draggable="true" data-drag-type="location" data-drag-id="@location.Value" data-drag-label="@location.Text">@location.Text</span>
|
||||
}
|
||||
</div>
|
||||
</details>
|
||||
|
||||
@if (Model.Books.Any())
|
||||
{
|
||||
<section class="timeline-minimap" data-timeline-minimap>
|
||||
@ -339,6 +372,110 @@
|
||||
</section>
|
||||
}
|
||||
|
||||
<section class="drag-drop-panel d-none" data-drag-drop-panel>
|
||||
<div class="drag-drop-panel-header">
|
||||
<div>
|
||||
<p class="eyebrow" data-drag-panel-kicker>Drag action</p>
|
||||
<h2 data-drag-panel-title>Confirm action</h2>
|
||||
</div>
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm" data-drag-cancel>Cancel</button>
|
||||
</div>
|
||||
<p class="muted" data-drag-panel-summary></p>
|
||||
|
||||
<form asp-controller="Timeline" asp-action="DropMoveScene" method="post" class="drag-drop-form d-none" data-drag-form="scene">
|
||||
<input type="hidden" name="ProjectID" value="@Model.Project.ProjectID" />
|
||||
<input type="hidden" name="BookID" value="@Model.SelectedBookID" />
|
||||
<input type="hidden" name="SceneID" />
|
||||
<input type="hidden" name="AnchorSceneID" />
|
||||
<input type="hidden" name="Position" />
|
||||
<div class="focused-validation-summary">
|
||||
<span>After moving, PlotLine will run continuity validation.</span>
|
||||
<span>Existing links, events, metrics, purposes, characters, assets, warnings and dependencies stay attached to the same SceneID.</span>
|
||||
</div>
|
||||
<div class="button-row mt-2">
|
||||
<button class="btn btn-primary btn-sm" type="submit">Move Scene</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" type="button" data-drag-cancel>Cancel</button>
|
||||
<a class="btn btn-outline-secondary btn-sm" asp-controller="Warnings" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID" asp-route-bookId="@Model.SelectedBookID">Review Warnings</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<form asp-controller="Timeline" asp-action="DropCharacter" method="post" class="drag-drop-form d-none" data-drag-form="character">
|
||||
<input type="hidden" name="SceneID" />
|
||||
<input type="hidden" name="CharacterID" />
|
||||
<input type="hidden" name="ReturnProjectID" value="@Model.Project.ProjectID" />
|
||||
<input type="hidden" name="ReturnBookID" value="@Model.SelectedBookID" />
|
||||
<div class="row g-2">
|
||||
<div class="col-6"><label class="form-label">Role in scene</label><select class="form-select form-select-sm" name="RoleInSceneTypeID" asp-items="Model.CharacterRoleOptions"><option value="">Not set</option></select></div>
|
||||
<div class="col-6"><label class="form-label">Presence type</label><select class="form-select form-select-sm" name="PresenceTypeID" asp-items="Model.PresenceTypeOptions"><option value="">Not set</option></select></div>
|
||||
<div class="col-12"><label class="form-label">Current location</label><select class="form-select form-select-sm" name="LocationID" asp-items="Model.LocationOptions"><option value="">Optional</option></select></div>
|
||||
</div>
|
||||
<div class="button-row mt-2">
|
||||
<button class="btn btn-primary btn-sm" type="submit">Add Character</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" type="button" data-drag-cancel>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<form asp-controller="Timeline" asp-action="DropAssetEvent" method="post" class="drag-drop-form d-none" data-drag-form="asset-event">
|
||||
<input type="hidden" name="SceneID" />
|
||||
<input type="hidden" name="StoryAssetID" />
|
||||
<input type="hidden" name="ReturnProjectID" value="@Model.Project.ProjectID" />
|
||||
<input type="hidden" name="ReturnBookID" value="@Model.SelectedBookID" />
|
||||
<div class="row g-2">
|
||||
<div class="col-6"><label class="form-label">Event type</label><select class="form-select form-select-sm" name="AssetEventTypeID" asp-items="Model.AssetEventTypeOptions"></select></div>
|
||||
<div class="col-6"><label class="form-label">State transition</label><select class="form-select form-select-sm" name="ToStateID" asp-items="Model.AssetStateOptions"><option value="">Optional</option></select></div>
|
||||
<div class="col-12"><label class="form-label">Location</label><select class="form-select form-select-sm" name="LocationID" asp-items="Model.LocationOptions"><option value="">Optional</option></select></div>
|
||||
<div class="col-12"><label class="form-label">Event title</label><input class="form-control form-control-sm" name="EventTitle" /></div>
|
||||
<div class="col-12"><label class="form-label">Custody note</label><textarea class="form-control form-control-sm" name="CustodyNote" rows="2"></textarea></div>
|
||||
</div>
|
||||
<div class="button-row mt-2">
|
||||
<button class="btn btn-primary btn-sm" type="submit">Add Asset Event</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" type="button" data-drag-cancel>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<form asp-controller="Timeline" asp-action="DropAssetDependency" method="post" class="drag-drop-form d-none" data-drag-form="asset-dependency">
|
||||
<input type="hidden" name="ProjectID" value="@Model.Project.ProjectID" />
|
||||
<input type="hidden" name="ReturnProjectID" value="@Model.Project.ProjectID" />
|
||||
<input type="hidden" name="ReturnBookID" value="@Model.SelectedBookID" />
|
||||
<input type="hidden" name="SourceAssetID" />
|
||||
<input type="hidden" name="TargetAssetID" />
|
||||
<div class="row g-2">
|
||||
<div class="col-12"><label class="form-label">Dependency type</label><select class="form-select form-select-sm" name="AssetDependencyTypeID" asp-items="Model.AssetDependencyTypeOptions"></select></div>
|
||||
<div class="col-6"><label class="form-label">Source required state</label><select class="form-select form-select-sm" name="SourceRequiredStateID" asp-items="Model.AssetStateOptions"><option value="">Optional</option></select></div>
|
||||
<div class="col-6"><label class="form-label">Target blocked state</label><select class="form-select form-select-sm" name="TargetBlockedStateID" asp-items="Model.AssetStateOptions"><option value="">Optional</option></select></div>
|
||||
<div class="col-12"><label class="form-label">Notes</label><textarea class="form-control form-control-sm" name="Description" rows="2"></textarea></div>
|
||||
</div>
|
||||
<p class="plain-preview" data-asset-dependency-preview></p>
|
||||
<div class="button-row mt-2">
|
||||
<button class="btn btn-primary btn-sm" type="submit">Save Dependency</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" type="button" data-drag-cancel>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<form asp-controller="Timeline" asp-action="DropLocationRelationship" method="post" class="drag-drop-form d-none" data-drag-form="location-relationship">
|
||||
<input type="hidden" name="ProjectID" value="@Model.Project.ProjectID" />
|
||||
<input type="hidden" name="ReturnBookID" value="@Model.SelectedBookID" />
|
||||
<input type="hidden" name="FromLocationID" />
|
||||
<input type="hidden" name="ToLocationID" />
|
||||
<div class="row g-2">
|
||||
<div class="col-12"><label class="form-label">Relationship type</label><select class="form-select form-select-sm" name="LocationRelationshipTypeID" asp-items="Model.LocationRelationshipTypeOptions"></select></div>
|
||||
<div class="col-12 drag-check-grid">
|
||||
<label><input type="checkbox" name="IsBidirectional" value="true" /> Bidirectional</label>
|
||||
<label><input type="checkbox" name="CanHearNormalSpeech" value="true" /> Can hear speech</label>
|
||||
<label><input type="checkbox" name="CanHearLoudNoise" value="true" /> Can hear loud noise</label>
|
||||
<label><input type="checkbox" name="CanSeeMovement" value="true" /> Can see movement</label>
|
||||
<label><input type="checkbox" name="CanSeeClearly" value="true" /> Can see clearly</label>
|
||||
<label><input type="checkbox" name="CanTravelDirectly" value="true" /> Can travel directly</label>
|
||||
</div>
|
||||
<div class="col-12"><label class="form-label">Notes</label><textarea class="form-control form-control-sm" name="Notes" rows="2"></textarea></div>
|
||||
</div>
|
||||
<div class="button-row mt-2">
|
||||
<button class="btn btn-primary btn-sm" type="submit">Save</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" type="button" data-drag-cancel>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
@if (!Model.Books.Any())
|
||||
{
|
||||
<section class="empty-panel">
|
||||
@ -350,7 +487,7 @@ else
|
||||
{
|
||||
<div class="workspace-layout timeline-filtered-root" data-timeline-root>
|
||||
<main class="timeline-workspace">
|
||||
<section class="timeline-shell workspace-timeline" data-timeline-section="scene-cards">
|
||||
<section class="timeline-shell workspace-timeline" data-timeline-section="scene-cards" style="--scene-count:@Model.OrderedScenes.Count">
|
||||
<div class="timeline-section-header" data-collapse-target="scene-cards">
|
||||
<button type="button" data-collapse-toggle="scene-cards">Scene Cards</button>
|
||||
<span>@Model.OrderedScenes.Count scene@(Model.OrderedScenes.Count == 1 ? "" : "s")</span>
|
||||
@ -392,7 +529,13 @@ else
|
||||
var warningSearch = scene.WarningCount > 0 ? "warning warnings continuity" : "";
|
||||
var searchText = $"{scene.SceneNumber} {scene.SceneTitle} {scene.Summary} {povLabel} {scene.PrimaryLocationName} {string.Join(' ', scene.PurposeLabels.Select(x => x.PurposeName))} {plotSearch} {assetSearch} {characterSearch} {warningSearch}";
|
||||
<a class="timeline-scene-card @selectedClass @matchClass @focusClass"
|
||||
draggable="true"
|
||||
data-scene-id="@scene.SceneID"
|
||||
data-drag-type="scene"
|
||||
data-drag-id="@scene.SceneID"
|
||||
data-drag-label="Scene @scene.SceneNumber: @scene.SceneTitle"
|
||||
data-chapter-id="@chapter.Chapter.ChapterID"
|
||||
data-chapter-label="Chapter @chapter.Chapter.ChapterNumber: @chapter.Chapter.ChapterTitle"
|
||||
data-timeline-search-text="@searchText"
|
||||
asp-controller="Timeline"
|
||||
asp-action="Index"
|
||||
@ -453,7 +596,7 @@ else
|
||||
@if (Model.PlotLanes.Any() && Model.OrderedScenes.Any())
|
||||
{
|
||||
<div class="plot-lane-board" data-timeline-section="plot-lines" style="--scene-count:@Model.OrderedScenes.Count">
|
||||
<div class="plot-lane-board-heading">
|
||||
<div class="lane-board-heading plot-lane-board-heading">
|
||||
<button type="button" data-collapse-toggle="plot-lines">Plot line lanes</button>
|
||||
<small>Thread event markers by scene</small>
|
||||
</div>
|
||||
@ -515,7 +658,7 @@ else
|
||||
@if (Model.AssetLanes.Any() && Model.OrderedScenes.Any())
|
||||
{
|
||||
<div class="asset-lane-board" data-timeline-section="assets" style="--scene-count:@Model.OrderedScenes.Count">
|
||||
<div class="plot-lane-board-heading">
|
||||
<div class="lane-board-heading asset-lane-board-heading">
|
||||
<button type="button" data-collapse-toggle="assets">Story asset lanes</button>
|
||||
<small>Asset event markers by scene</small>
|
||||
</div>
|
||||
@ -530,7 +673,7 @@ else
|
||||
if ((lane.Asset.Importance >= 8 ? "High importance" : "Other assets") != group.Key) { continue; }
|
||||
var assetFocusClass = Model.Filter.FocusType == "asset" && Model.Filter.FocusID.HasValue && Model.Filter.FocusID != lane.Asset.StoryAssetID ? "lane-dim" : "";
|
||||
<div class="asset-lane-row @assetFocusClass">
|
||||
<a class="asset-lane-label" asp-controller="StoryAssets" asp-action="Details" asp-route-id="@lane.Asset.StoryAssetID">
|
||||
<a class="asset-lane-label" draggable="true" data-drag-type="asset" data-drag-id="@lane.Asset.StoryAssetID" data-drag-label="@lane.Asset.AssetName" data-asset-drop-target="true" data-asset-id="@lane.Asset.StoryAssetID" data-asset-label="@lane.Asset.AssetName" asp-controller="StoryAssets" asp-action="Details" asp-route-id="@lane.Asset.StoryAssetID">
|
||||
<strong>@lane.Asset.AssetName</strong>
|
||||
<span>@lane.Asset.KindName / @lane.Asset.Importance</span>
|
||||
</a>
|
||||
@ -572,7 +715,7 @@ else
|
||||
@if (Model.CharacterLanes.Any() && Model.OrderedScenes.Any())
|
||||
{
|
||||
<div class="character-lane-board" data-timeline-section="characters" style="--scene-count:@Model.OrderedScenes.Count">
|
||||
<div class="plot-lane-board-heading">
|
||||
<div class="lane-board-heading character-lane-board-heading">
|
||||
<button type="button" data-collapse-toggle="characters">Character appearances</button>
|
||||
<small>First twelve project characters</small>
|
||||
</div>
|
||||
|
||||
@ -497,19 +497,31 @@ a {
|
||||
padding-top: 14px;
|
||||
}
|
||||
|
||||
.lane-board-heading,
|
||||
.plot-lane-board-heading {
|
||||
position: sticky;
|
||||
left: 0;
|
||||
display: grid;
|
||||
width: fit-content;
|
||||
z-index: 7;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 14px;
|
||||
width: calc(var(--timeline-label-width) + (var(--scene-count) * var(--timeline-scene-width)) + (var(--scene-count) * var(--timeline-grid-gap)));
|
||||
min-width: 100%;
|
||||
margin-bottom: 2px;
|
||||
border-radius: 8px;
|
||||
padding: 8px 10px;
|
||||
color: var(--plotline-accent-dark);
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
font-weight: 700;
|
||||
box-shadow: 0 8px 16px rgba(30, 37, 43, 0.05);
|
||||
}
|
||||
|
||||
.lane-board-heading small,
|
||||
.plot-lane-board-heading small {
|
||||
color: var(--plotline-muted);
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.plot-lane-row {
|
||||
@ -1735,21 +1747,133 @@ a {
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.timeline-drag-palette,
|
||||
.drag-drop-panel {
|
||||
border: 1px solid rgba(47, 111, 99, 0.18);
|
||||
border-radius: 10px;
|
||||
margin-bottom: 12px;
|
||||
padding: 12px;
|
||||
background: rgba(255, 255, 255, 0.84);
|
||||
box-shadow: 0 10px 24px rgba(55, 48, 39, 0.05);
|
||||
}
|
||||
|
||||
.timeline-drag-palette {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(220px, 0.35fr) minmax(0, 1fr);
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.drag-chip-group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.drag-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
border: 1px solid var(--plotline-line);
|
||||
border-radius: 999px;
|
||||
padding: 4px 9px;
|
||||
background: #fff;
|
||||
color: var(--plotline-ink);
|
||||
font-size: 0.78rem;
|
||||
font-weight: 800;
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.drag-chip.character {
|
||||
border-color: #dacb9e;
|
||||
background: #fff8e8;
|
||||
}
|
||||
|
||||
.drag-chip.asset {
|
||||
border-color: #ded9ea;
|
||||
background: #f4f0ff;
|
||||
}
|
||||
|
||||
.drag-chip.location {
|
||||
border-color: rgba(47, 111, 99, 0.18);
|
||||
background: #eef8f4;
|
||||
}
|
||||
|
||||
.is-dragging {
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.drop-target-active {
|
||||
outline: 3px solid rgba(47, 111, 99, 0.28);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.timeline-scene-card.drop-before {
|
||||
box-shadow: inset 5px 0 0 #c58b2b, 0 12px 26px rgba(197, 139, 43, 0.16);
|
||||
}
|
||||
|
||||
.timeline-scene-card.drop-after {
|
||||
box-shadow: inset -5px 0 0 #c58b2b, 0 12px 26px rgba(197, 139, 43, 0.16);
|
||||
}
|
||||
|
||||
.drag-drop-panel {
|
||||
position: sticky;
|
||||
top: 132px;
|
||||
z-index: 19;
|
||||
}
|
||||
|
||||
.drag-drop-panel-header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.drag-drop-panel h2 {
|
||||
margin: 0;
|
||||
font-size: 1.08rem;
|
||||
}
|
||||
|
||||
.drag-drop-form {
|
||||
border-top: 1px solid var(--plotline-line);
|
||||
margin-top: 10px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.plain-preview {
|
||||
border: 1px solid rgba(197, 139, 43, 0.28);
|
||||
border-radius: 8px;
|
||||
margin: 10px 0 0;
|
||||
padding: 8px 10px;
|
||||
color: #5e4518;
|
||||
background: #fff8e8;
|
||||
font-size: 0.86rem;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.drag-check-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 6px 10px;
|
||||
color: var(--plotline-muted);
|
||||
font-size: 0.82rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.timeline-section-header,
|
||||
.plot-lane-board-heading,
|
||||
.lane-board-heading,
|
||||
.pacing-heading {
|
||||
position: sticky;
|
||||
left: 0;
|
||||
z-index: 7;
|
||||
}
|
||||
|
||||
.timeline-section-header,
|
||||
.plot-lane-board-heading {
|
||||
.timeline-section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
width: var(--timeline-label-width);
|
||||
width: calc(var(--timeline-label-width) + (var(--scene-count) * var(--timeline-scene-width)) + (var(--scene-count) * var(--timeline-grid-gap)));
|
||||
min-width: 100%;
|
||||
margin-bottom: 10px;
|
||||
border-radius: 8px;
|
||||
padding: 8px 10px;
|
||||
@ -1758,6 +1882,7 @@ a {
|
||||
}
|
||||
|
||||
.timeline-section-header button,
|
||||
.lane-board-heading button,
|
||||
.plot-lane-board-heading button,
|
||||
.pacing-heading button,
|
||||
.lane-group-heading {
|
||||
@ -1771,6 +1896,7 @@ a {
|
||||
}
|
||||
|
||||
.timeline-section-header button::before,
|
||||
.lane-board-heading button::before,
|
||||
.plot-lane-board-heading button::before,
|
||||
.pacing-heading button::before,
|
||||
.lane-group-heading::before {
|
||||
@ -1782,6 +1908,7 @@ a {
|
||||
}
|
||||
|
||||
.timeline-section-header button.is-collapsed::before,
|
||||
.lane-board-heading button.is-collapsed::before,
|
||||
.plot-lane-board-heading button.is-collapsed::before,
|
||||
.pacing-heading button.is-collapsed::before,
|
||||
.lane-group-heading.is-collapsed::before {
|
||||
@ -1905,6 +2032,204 @@ a {
|
||||
min-height: 230px;
|
||||
}
|
||||
|
||||
/* Timeline stabilization: keep controls compact and story lanes readable. */
|
||||
.timeline-control-panel {
|
||||
position: relative;
|
||||
top: auto;
|
||||
z-index: 4;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
padding: 8px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.timeline-filter-drawer,
|
||||
.timeline-control-group {
|
||||
border: 1px solid var(--plotline-line);
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.82);
|
||||
}
|
||||
|
||||
.timeline-filter-drawer[open],
|
||||
.timeline-control-group[open] {
|
||||
border-radius: 10px;
|
||||
flex-basis: 100%;
|
||||
}
|
||||
|
||||
.timeline-filter-drawer summary,
|
||||
.timeline-control-group summary {
|
||||
min-width: max-content;
|
||||
padding: 5px 10px;
|
||||
color: var(--plotline-accent-dark);
|
||||
font-size: 0.82rem;
|
||||
font-weight: 900;
|
||||
cursor: pointer;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.timeline-filter-drawer summary::-webkit-details-marker,
|
||||
.timeline-control-group summary::-webkit-details-marker,
|
||||
.timeline-drag-palette summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.timeline-filter-drawer summary::after,
|
||||
.timeline-control-group summary::after,
|
||||
.timeline-drag-palette summary::after {
|
||||
content: " v";
|
||||
color: var(--plotline-muted);
|
||||
font-size: 0.72rem;
|
||||
}
|
||||
|
||||
.timeline-filter-drawer[open] summary::after,
|
||||
.timeline-control-group[open] summary::after,
|
||||
.timeline-drag-palette[open] summary::after {
|
||||
content: " ^";
|
||||
}
|
||||
|
||||
.timeline-filter-grid {
|
||||
grid-template-columns: repeat(auto-fit, minmax(145px, 1fr));
|
||||
gap: 8px;
|
||||
max-height: min(48vh, 430px);
|
||||
overflow: auto;
|
||||
padding: 0 10px 10px;
|
||||
}
|
||||
|
||||
.timeline-filter-grid .form-label {
|
||||
margin-bottom: 2px;
|
||||
font-size: 0.72rem;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.timeline-filter-checks,
|
||||
.timeline-filter-actions {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.timeline-visibility-panel,
|
||||
.timeline-jump-panel,
|
||||
.timeline-preset-panel {
|
||||
padding: 0 10px 10px;
|
||||
}
|
||||
|
||||
.timeline-jump-panel {
|
||||
grid-template-columns: minmax(180px, 1.5fr) repeat(3, minmax(150px, 1fr));
|
||||
}
|
||||
|
||||
.timeline-shortcut-hint {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.timeline-preset-panel form {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.timeline-preset-panel .form-control {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.timeline-preset-panel .btn,
|
||||
.timeline-filter-actions .btn {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.timeline-drag-palette {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
padding: 7px 9px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.timeline-drag-palette summary {
|
||||
color: var(--plotline-accent-dark);
|
||||
font-size: 0.82rem;
|
||||
font-weight: 900;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.timeline-drag-palette .drag-chip-group {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.drag-drop-panel {
|
||||
position: relative;
|
||||
top: auto;
|
||||
z-index: 9;
|
||||
box-shadow: 0 12px 28px rgba(30, 37, 43, 0.08);
|
||||
}
|
||||
|
||||
.timeline-filtered-root,
|
||||
.timeline-filtered-root.density-compact,
|
||||
.timeline-filtered-root.zoom-book {
|
||||
--timeline-scene-width: 220px;
|
||||
--timeline-grid-gap: 10px;
|
||||
}
|
||||
|
||||
.timeline-filtered-root.density-expanded,
|
||||
.timeline-filtered-root.zoom-scene-detail {
|
||||
--timeline-scene-width: 290px;
|
||||
}
|
||||
|
||||
.timeline-filtered-root.density-compact.zoom-book {
|
||||
--timeline-scene-width: 200px;
|
||||
}
|
||||
|
||||
.timeline-chapter,
|
||||
.timeline-scene-card,
|
||||
.timeline-empty-scene {
|
||||
overflow-wrap: normal;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
.timeline-scene-card strong,
|
||||
.timeline-chapter-title strong,
|
||||
.plot-lane-label strong,
|
||||
.asset-lane-label strong,
|
||||
.character-lane-label strong {
|
||||
min-width: 0;
|
||||
white-space: normal;
|
||||
word-break: normal;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.timeline-scene-card {
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.timeline-scene-card:active,
|
||||
.drag-chip:active,
|
||||
[draggable="true"]:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
body.drag-active .timeline-scene-card,
|
||||
body.dragging-asset [data-asset-drop-target],
|
||||
body.dragging-location [data-drag-type="location"] {
|
||||
transition: outline-color 0.12s ease, box-shadow 0.12s ease, opacity 0.12s ease;
|
||||
}
|
||||
|
||||
body.dragging-character .timeline-scene-card,
|
||||
body.dragging-asset .timeline-scene-card,
|
||||
body.dragging-scene .timeline-scene-card,
|
||||
body.dragging-asset [data-asset-drop-target],
|
||||
body.dragging-location [data-drag-type="location"] {
|
||||
outline: 1px dashed rgba(47, 111, 99, 0.22);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.is-dragging {
|
||||
opacity: 0.42;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.drop-target-active {
|
||||
outline: 3px solid rgba(47, 111, 99, 0.42) !important;
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
.analytics-metric-row {
|
||||
display: grid;
|
||||
grid-template-columns: 170px minmax(560px, 1fr);
|
||||
|
||||
@ -529,3 +529,213 @@
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
(() => {
|
||||
const root = document.querySelector("[data-timeline-root]");
|
||||
const panel = document.querySelector("[data-drag-drop-panel]");
|
||||
if (!panel) {
|
||||
return;
|
||||
}
|
||||
|
||||
const forms = new Map([...panel.querySelectorAll("[data-drag-form]")].map((form) => [form.dataset.dragForm, form]));
|
||||
const title = panel.querySelector("[data-drag-panel-title]");
|
||||
const kicker = panel.querySelector("[data-drag-panel-kicker]");
|
||||
const summary = panel.querySelector("[data-drag-panel-summary]");
|
||||
let currentDrag = null;
|
||||
|
||||
const setValue = (form, name, value) => {
|
||||
const field = form?.querySelector(`[name='${CSS.escape(name)}']`);
|
||||
if (field) {
|
||||
field.value = value ?? "";
|
||||
}
|
||||
};
|
||||
|
||||
const selectText = (select) => select?.selectedOptions?.[0]?.textContent?.trim() || "";
|
||||
|
||||
const showForm = (key, heading, body, actionLabel) => {
|
||||
forms.forEach((form, formKey) => form.classList.toggle("d-none", formKey !== key));
|
||||
panel.classList.remove("d-none");
|
||||
title.textContent = heading;
|
||||
kicker.textContent = actionLabel;
|
||||
summary.textContent = body;
|
||||
panel.scrollIntoView({ behavior: "smooth", block: "center" });
|
||||
};
|
||||
|
||||
const hidePanel = () => {
|
||||
panel.classList.add("d-none");
|
||||
forms.forEach((form) => form.classList.add("d-none"));
|
||||
};
|
||||
|
||||
const readPayload = (event) => {
|
||||
try {
|
||||
const payload = JSON.parse(event.dataTransfer.getData("application/json") || "{}");
|
||||
return payload.type ? payload : currentDrag || {};
|
||||
} catch {
|
||||
return currentDrag || {};
|
||||
}
|
||||
};
|
||||
|
||||
const writePayload = (event, element) => {
|
||||
const payload = {
|
||||
type: element.dataset.dragType,
|
||||
id: element.dataset.dragId,
|
||||
label: element.dataset.dragLabel || element.textContent.trim()
|
||||
};
|
||||
event.dataTransfer.effectAllowed = "copyMove";
|
||||
event.dataTransfer.setData("application/json", JSON.stringify(payload));
|
||||
event.dataTransfer.setData("text/plain", `${payload.type}:${payload.id}`);
|
||||
currentDrag = payload;
|
||||
document.body.classList.add("drag-active", `dragging-${payload.type}`);
|
||||
element.classList.add("is-dragging");
|
||||
};
|
||||
|
||||
document.querySelectorAll("[data-drag-type][draggable='true']").forEach((item) => {
|
||||
item.addEventListener("dragstart", (event) => writePayload(event, item));
|
||||
item.addEventListener("dragend", () => {
|
||||
item.classList.remove("is-dragging");
|
||||
document.body.classList.remove("drag-active", "dragging-scene", "dragging-character", "dragging-asset", "dragging-location");
|
||||
currentDrag = null;
|
||||
document.querySelectorAll(".drop-target-active, .drop-before, .drop-after").forEach((target) => {
|
||||
target.classList.remove("drop-target-active", "drop-before", "drop-after");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
panel.querySelectorAll("[data-drag-cancel]").forEach((button) => button.addEventListener("click", hidePanel));
|
||||
|
||||
root?.querySelectorAll(".timeline-scene-card[data-scene-id]").forEach((card) => {
|
||||
card.addEventListener("dragover", (event) => {
|
||||
const payload = readPayload(event);
|
||||
if (["scene", "character", "asset"].includes(payload.type)) {
|
||||
event.preventDefault();
|
||||
event.dataTransfer.dropEffect = payload.type === "scene" ? "move" : "copy";
|
||||
card.classList.add("drop-target-active");
|
||||
card.classList.toggle("drop-before", payload.type === "scene" && event.offsetX < card.clientWidth / 2);
|
||||
card.classList.toggle("drop-after", payload.type === "scene" && event.offsetX >= card.clientWidth / 2);
|
||||
}
|
||||
});
|
||||
|
||||
card.addEventListener("dragleave", () => card.classList.remove("drop-target-active", "drop-before", "drop-after"));
|
||||
|
||||
card.addEventListener("drop", (event) => {
|
||||
const payload = readPayload(event);
|
||||
if (!["scene", "character", "asset"].includes(payload.type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
card.classList.remove("drop-target-active", "drop-before", "drop-after");
|
||||
const sceneId = card.dataset.sceneId;
|
||||
|
||||
if (payload.type === "scene") {
|
||||
if (payload.id === sceneId) {
|
||||
showForm("scene", "Cannot move scene", "Drop a scene before or after a different scene.", "Scene move");
|
||||
return;
|
||||
}
|
||||
|
||||
const form = forms.get("scene");
|
||||
const position = event.offsetX < card.clientWidth / 2 ? "Before" : "After";
|
||||
setValue(form, "SceneID", payload.id);
|
||||
setValue(form, "AnchorSceneID", sceneId);
|
||||
setValue(form, "Position", position);
|
||||
showForm("scene", "Move scene?", `Move ${payload.label} ${position.toLowerCase()} ${card.dataset.dragLabel}.`, "Scene move");
|
||||
return;
|
||||
}
|
||||
|
||||
if (payload.type === "character") {
|
||||
const form = forms.get("character");
|
||||
setValue(form, "SceneID", sceneId);
|
||||
setValue(form, "CharacterID", payload.id);
|
||||
showForm("character", "Add character to scene", `Add ${payload.label} to ${card.dataset.dragLabel}.`, "Character drop");
|
||||
return;
|
||||
}
|
||||
|
||||
const form = forms.get("asset-event");
|
||||
setValue(form, "SceneID", sceneId);
|
||||
setValue(form, "StoryAssetID", payload.id);
|
||||
setValue(form, "EventTitle", "");
|
||||
showForm("asset-event", "What happens to this asset?", `Add an event for ${payload.label} in ${card.dataset.dragLabel}.`, "Asset drop");
|
||||
});
|
||||
});
|
||||
|
||||
const assetDependencyForm = forms.get("asset-dependency");
|
||||
const updateAssetPreview = () => {
|
||||
const source = assetDependencyForm?.dataset.sourceLabel || "Source asset";
|
||||
const target = assetDependencyForm?.dataset.targetLabel || "Target asset";
|
||||
const dependency = selectText(assetDependencyForm?.querySelector("[name='AssetDependencyTypeID']")) || "Requires";
|
||||
const sourceState = selectText(assetDependencyForm?.querySelector("[name='SourceRequiredStateID']")) || "the required state";
|
||||
const targetState = selectText(assetDependencyForm?.querySelector("[name='TargetBlockedStateID']")) || "the blocked state";
|
||||
const preview = panel.querySelector("[data-asset-dependency-preview]");
|
||||
if (preview) {
|
||||
preview.textContent = `${source} ${dependency.toLowerCase()} ${target}. ${source} should be ${sourceState} before ${target} can be ${targetState}.`;
|
||||
}
|
||||
};
|
||||
|
||||
assetDependencyForm?.querySelectorAll("select").forEach((select) => select.addEventListener("change", updateAssetPreview));
|
||||
|
||||
document.querySelectorAll("[data-asset-drop-target], .drag-chip.asset").forEach((target) => {
|
||||
target.addEventListener("dragover", (event) => {
|
||||
const payload = readPayload(event);
|
||||
if (payload.type === "asset") {
|
||||
event.preventDefault();
|
||||
event.dataTransfer.dropEffect = "copy";
|
||||
target.classList.add("drop-target-active");
|
||||
}
|
||||
});
|
||||
target.addEventListener("dragleave", () => target.classList.remove("drop-target-active"));
|
||||
target.addEventListener("drop", (event) => {
|
||||
const payload = readPayload(event);
|
||||
const targetId = target.dataset.assetId || target.dataset.dragId;
|
||||
const targetLabel = target.dataset.assetLabel || target.dataset.dragLabel || target.textContent.trim();
|
||||
if (payload.type !== "asset" || !targetId) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
target.classList.remove("drop-target-active");
|
||||
if (payload.id === targetId) {
|
||||
showForm("asset-dependency", "Cannot create dependency", "Drop one asset onto a different asset.", "Asset dependency");
|
||||
return;
|
||||
}
|
||||
|
||||
setValue(assetDependencyForm, "SourceAssetID", payload.id);
|
||||
setValue(assetDependencyForm, "TargetAssetID", targetId);
|
||||
assetDependencyForm.dataset.sourceLabel = payload.label;
|
||||
assetDependencyForm.dataset.targetLabel = targetLabel;
|
||||
updateAssetPreview();
|
||||
showForm("asset-dependency", "Create asset dependency", `${payload.label} -> ${targetLabel}`, "Asset dependency");
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll("[data-drag-type='location']").forEach((target) => {
|
||||
target.addEventListener("dragover", (event) => {
|
||||
const payload = readPayload(event);
|
||||
if (payload.type === "location") {
|
||||
event.preventDefault();
|
||||
event.dataTransfer.dropEffect = "copy";
|
||||
target.classList.add("drop-target-active");
|
||||
}
|
||||
});
|
||||
target.addEventListener("dragleave", () => target.classList.remove("drop-target-active"));
|
||||
target.addEventListener("drop", (event) => {
|
||||
const payload = readPayload(event);
|
||||
const targetId = target.dataset.dragId;
|
||||
const targetLabel = target.dataset.dragLabel || target.textContent.trim();
|
||||
if (payload.type !== "location") {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
target.classList.remove("drop-target-active");
|
||||
if (payload.id === targetId) {
|
||||
showForm("location-relationship", "Cannot create relationship", "Drop one location onto a different location.", "Location relationship");
|
||||
return;
|
||||
}
|
||||
|
||||
const form = forms.get("location-relationship");
|
||||
setValue(form, "FromLocationID", payload.id);
|
||||
setValue(form, "ToLocationID", targetId);
|
||||
showForm("location-relationship", "Create location relationship", `${payload.label} -> ${targetLabel}`, "Location drop");
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user