mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-22 10:04:16 +00:00
commit pack
fixed blocked cheat mls modal! remove import fixed fixed added
This commit is contained in:
|
Before Width: | Height: | Size: 440 B After Width: | Height: | Size: 440 B |
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="150px" height="150px" viewBox="0 0 150 150" version="1.1">
|
||||
<g id="surface1">
|
||||
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(89.803922%,0%,6.666667%);fill-opacity:1;" d="M 63.75 46.140625 C 65.632812 46.222656 67.515625 46.277344 69.402344 46.304688 C 69.507812 47.109375 69.546875 47.925781 69.511719 48.75 C 73.460938 48.75 77.410156 48.75 81.359375 48.75 C 81.359375 47.917969 81.359375 47.082031 81.359375 46.25 C 83.3125 46.140625 85.269531 46.140625 87.226562 46.25 C 88.027344 52.207031 86.214844 57.191406 81.792969 61.195312 C 80.757812 62.011719 79.617188 62.644531 78.371094 63.097656 C 78.316406 63.042969 78.261719 62.988281 78.207031 62.933594 C 80.804688 60.832031 82.816406 58.277344 84.238281 55.273438 C 85.253906 52.914062 85.742188 50.449219 85.707031 47.878906 C 84.808594 47.855469 83.902344 47.855469 82.988281 47.878906 C 83.023438 48.667969 82.988281 49.449219 82.878906 50.21875 C 77.878906 50.289062 72.878906 50.289062 67.878906 50.21875 C 67.773438 49.449219 67.738281 48.667969 67.773438 47.878906 C 66.902344 47.878906 66.03125 47.878906 65.164062 47.878906 C 65.0625 52.378906 66.421875 56.367188 69.238281 59.835938 C 70.3125 60.945312 71.417969 62.015625 72.554688 63.042969 C 72.375 63.117188 72.191406 63.117188 72.011719 63.042969 C 67.910156 61.148438 65.246094 58.015625 64.023438 53.640625 C 63.363281 51.285156 63.183594 48.894531 63.476562 46.46875 C 63.554688 46.339844 63.644531 46.230469 63.75 46.140625 Z M 63.75 46.140625 "/>
|
||||
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(89.803922%,0%,6.666667%);fill-opacity:1;" d="M 69.621094 51.140625 C 73.425781 51.140625 77.226562 51.140625 81.03125 51.140625 C 81.070312 53.027344 81.089844 54.910156 81.085938 56.792969 C 80.78125 59.800781 79.261719 61.898438 76.523438 63.097656 C 76.085938 67.097656 76.828125 70.863281 78.75 74.402344 C 78.695312 74.4375 78.640625 74.476562 78.585938 74.511719 C 76.421875 73.386719 74.230469 73.351562 72.011719 74.402344 C 72.277344 73.5 72.621094 72.59375 73.042969 71.683594 C 74.175781 68.851562 74.519531 65.933594 74.074219 62.933594 C 71.65625 61.886719 70.1875 60.058594 69.675781 57.445312 C 69.621094 55.34375 69.601562 53.242188 69.621094 51.140625 Z M 69.621094 51.140625 "/>
|
||||
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(89.803922%,0%,6.666667%);fill-opacity:1;" d="M 60.164062 55.925781 C 60.613281 56.386719 61.066406 56.859375 61.523438 57.335938 C 61.558594 57.445312 61.59375 57.554688 61.628906 57.664062 C 61.621094 63.316406 61.640625 68.96875 61.683594 74.621094 C 64.039062 74.582031 66.394531 74.621094 68.75 74.726562 C 68.484375 75.097656 68.230469 75.480469 67.988281 75.871094 C 65.390625 75.960938 62.800781 75.941406 60.21875 75.816406 C 60.164062 69.183594 60.144531 62.554688 60.164062 55.925781 Z M 60.164062 55.925781 "/>
|
||||
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(89.803922%,0%,6.666667%);fill-opacity:1;" d="M 90.378906 55.925781 C 90.449219 55.9375 90.5 55.972656 90.542969 56.03125 C 90.597656 62.664062 90.617188 69.292969 90.597656 75.925781 C 88.0625 76.03125 85.527344 76.050781 82.988281 75.976562 C 82.582031 75.613281 82.328125 75.164062 82.226562 74.621094 C 84.546875 74.621094 86.867188 74.621094 89.183594 74.621094 C 89.21875 69.054688 89.183594 63.492188 89.074219 57.933594 C 89.144531 57.703125 89.199219 57.46875 89.238281 57.226562 C 89.683594 56.835938 90.066406 56.402344 90.378906 55.925781 Z M 90.378906 55.925781 "/>
|
||||
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(89.803922%,0%,6.666667%);fill-opacity:1;" d="M 87.664062 62.121094 C 87.878906 65.597656 87.953125 69.109375 87.878906 72.664062 C 86.105469 72.699219 84.328125 72.664062 82.554688 72.554688 C 82.742188 72.117188 82.960938 71.699219 83.207031 71.304688 C 84.292969 71.25 85.378906 71.230469 86.46875 71.25 C 86.449219 68.640625 86.46875 66.03125 86.523438 63.425781 C 86.882812 62.972656 87.265625 62.535156 87.664062 62.121094 Z M 87.664062 62.121094 "/>
|
||||
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(89.803922%,0%,6.666667%);fill-opacity:1;" d="M 62.878906 62.226562 C 62.960938 62.214844 63.035156 62.230469 63.097656 62.28125 C 63.4375 62.730469 63.820312 63.148438 64.238281 63.53125 C 64.273438 66.070312 64.3125 68.605469 64.347656 71.140625 C 65.515625 71.230469 66.695312 71.285156 67.878906 71.304688 C 68.164062 71.722656 68.308594 72.15625 68.316406 72.609375 C 66.539062 72.699219 64.761719 72.679688 62.988281 72.554688 C 62.976562 69.125 62.941406 65.683594 62.878906 62.226562 Z M 62.878906 62.226562 "/>
|
||||
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(89.803922%,0%,6.666667%);fill-opacity:1;" d="M 71.25 74.726562 C 73.96875 74.710938 76.683594 74.726562 79.402344 74.78125 C 79.710938 75.054688 79.890625 75.398438 79.945312 75.816406 C 77.644531 75.890625 75.34375 75.855469 73.042969 75.707031 C 72.292969 75.722656 71.550781 75.796875 70.816406 75.925781 C 70.875 75.488281 71.019531 75.089844 71.25 74.726562 Z M 71.25 74.726562 "/>
|
||||
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(89.803922%,0%,6.666667%);fill-opacity:1;" d="M 68.75 77.011719 C 73.132812 76.992188 77.519531 77.011719 81.902344 77.066406 C 82.257812 77.867188 82.585938 78.679688 82.878906 79.511719 C 77.84375 79.65625 72.808594 79.65625 67.773438 79.511719 C 68.078125 78.667969 68.402344 77.832031 68.75 77.011719 Z M 68.75 77.011719 "/>
|
||||
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(89.803922%,0%,6.666667%);fill-opacity:1;" d="M 34.183594 83.425781 C 37.273438 83.191406 39.410156 84.515625 40.597656 87.390625 C 40.59375 88.339844 40.59375 89.28125 40.597656 90.21875 C 39.734375 92.585938 37.976562 93.835938 35.324219 93.96875 C 32.457031 93.921875 30.574219 92.546875 29.675781 89.835938 C 29.210938 87.015625 30.277344 85.003906 32.878906 83.804688 C 33.328125 83.675781 33.761719 83.546875 34.183594 83.425781 Z M 34.292969 86.46875 C 36.238281 86.09375 37.289062 86.875 37.445312 88.804688 C 37.167969 90.449219 36.1875 91.171875 34.511719 90.976562 C 33.714844 90.835938 33.191406 90.382812 32.933594 89.621094 C 32.566406 88.25 33.019531 87.199219 34.292969 86.46875 Z M 34.292969 86.46875 "/>
|
||||
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(89.803922%,0%,6.666667%);fill-opacity:1;" d="M 94.726562 83.425781 C 96.710938 83.179688 98.394531 83.757812 99.78125 85.164062 C 100.015625 85.796875 100.378906 86.339844 100.871094 86.792969 C 101.839844 90.300781 100.605469 92.65625 97.175781 93.859375 C 93.859375 94.355469 91.503906 93.015625 90.109375 89.835938 C 89.78125 86.363281 91.320312 84.226562 94.726562 83.425781 Z M 94.835938 86.46875 C 96.695312 86.101562 97.746094 86.84375 97.988281 88.695312 C 97.527344 90.867188 96.257812 91.519531 94.183594 90.652344 C 93.34375 89.851562 93.070312 88.890625 93.371094 87.773438 C 93.746094 87.195312 94.238281 86.757812 94.835938 86.46875 Z M 94.835938 86.46875 "/>
|
||||
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(89.803922%,0%,6.666667%);fill-opacity:1;" d="M 41.46875 83.640625 C 43.503906 83.59375 45.53125 83.648438 47.554688 83.804688 C 49.953125 84.585938 50.96875 86.214844 50.597656 88.695312 C 50.089844 89.929688 49.203125 90.671875 47.933594 90.925781 C 46.886719 91.1875 45.816406 91.296875 44.726562 91.25 C 44.738281 92.089844 44.703125 92.902344 44.621094 93.695312 C 43.578125 93.785156 42.546875 93.765625 41.523438 93.640625 C 41.46875 90.308594 41.449219 86.976562 41.46875 83.640625 Z M 46.25 86.359375 C 47.25 86.503906 47.59375 87.046875 47.28125 87.988281 C 47.140625 88.207031 46.941406 88.332031 46.683594 88.371094 C 46.03125 88.441406 45.378906 88.441406 44.726562 88.371094 C 44.835938 87.746094 44.871094 87.113281 44.835938 86.46875 C 45.328125 86.503906 45.800781 86.464844 46.25 86.359375 Z M 46.25 86.359375 "/>
|
||||
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(90.196079%,0%,7.058824%);fill-opacity:1;" d="M 51.46875 83.640625 C 54.074219 83.640625 56.683594 83.640625 59.292969 83.640625 C 59.417969 84.535156 59.417969 85.421875 59.292969 86.304688 C 57.804688 86.324219 56.320312 86.375 54.835938 86.46875 C 54.738281 86.75 54.703125 87.039062 54.726562 87.335938 C 56.105469 87.320312 57.480469 87.335938 58.859375 87.390625 C 59.003906 88.226562 59.003906 89.058594 58.859375 89.890625 C 57.925781 89.847656 56.964844 89.832031 55.976562 89.835938 C 55.59375 89.851562 55.210938 89.90625 54.835938 90 C 54.734375 90.367188 54.695312 90.75 54.726562 91.140625 C 56.3125 91.105469 57.890625 91.140625 59.457031 91.25 C 59.476562 92.039062 59.53125 92.816406 59.621094 93.585938 C 59.582031 93.625 59.546875 93.660156 59.511719 93.695312 C 56.867188 93.769531 54.222656 93.769531 51.574219 93.695312 C 51.46875 90.351562 51.429688 87 51.46875 83.640625 Z M 51.46875 83.640625 "/>
|
||||
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(89.803922%,0%,6.666667%);fill-opacity:1;" d="M 60.597656 83.640625 C 61.503906 83.625 62.410156 83.640625 63.316406 83.695312 C 64.535156 85.171875 65.765625 86.640625 67.011719 88.097656 C 67.171875 87.8125 67.242188 87.503906 67.226562 87.175781 C 67.15625 86.066406 67.121094 84.945312 67.121094 83.804688 C 68.1875 83.644531 69.277344 83.589844 70.378906 83.640625 C 70.378906 87.011719 70.378906 90.378906 70.378906 93.75 C 69.425781 93.796875 68.484375 93.742188 67.554688 93.585938 C 66.667969 92.410156 65.777344 91.230469 64.890625 90.054688 C 64.558594 89.722656 64.234375 89.394531 63.914062 89.074219 C 63.746094 90.554688 63.730469 92.058594 63.859375 93.585938 C 63.332031 93.710938 62.789062 93.765625 62.226562 93.75 C 61.722656 93.730469 61.214844 93.714844 60.707031 93.695312 C 60.632812 93.625 60.5625 93.550781 60.488281 93.476562 C 60.597656 90.207031 60.632812 86.929688 60.597656 83.640625 Z M 60.597656 83.640625 "/>
|
||||
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(90.196079%,0%,7.058824%);fill-opacity:1;" d="M 71.683594 83.640625 C 74.292969 83.625 76.902344 83.640625 79.511719 83.695312 C 79.65625 84.566406 79.65625 85.433594 79.511719 86.304688 C 77.988281 86.359375 76.46875 86.375 74.945312 86.359375 C 74.863281 86.820312 74.863281 87.273438 74.945312 87.71875 C 76.359375 87.773438 77.769531 87.789062 79.183594 87.773438 C 79.320312 88.675781 79.320312 89.582031 79.183594 90.488281 C 77.765625 90.453125 76.351562 90.492188 74.945312 90.597656 C 74.835938 91.644531 74.800781 92.695312 74.835938 93.75 C 73.785156 93.75 72.734375 93.75 71.683594 93.75 C 71.683594 90.378906 71.683594 87.011719 71.683594 83.640625 Z M 71.683594 83.640625 "/>
|
||||
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(89.803922%,0%,6.666667%);fill-opacity:1;" d="M 80.488281 83.640625 C 82.375 83.625 84.257812 83.640625 86.140625 83.695312 C 88.582031 84.324219 89.707031 85.882812 89.511719 88.371094 C 89.425781 88.8125 89.261719 89.230469 89.023438 89.621094 C 88.671875 89.96875 88.328125 90.3125 87.988281 90.652344 C 88.433594 91.425781 88.925781 92.167969 89.457031 92.878906 C 89.570312 93.125 89.695312 93.359375 89.835938 93.585938 C 89.800781 93.625 89.765625 93.660156 89.726562 93.695312 C 88.675781 93.769531 87.625 93.769531 86.574219 93.695312 C 86.066406 92.910156 85.539062 92.128906 85 91.359375 C 84.792969 91.289062 84.613281 91.179688 84.457031 91.03125 C 84.261719 91.113281 84.0625 91.183594 83.859375 91.25 C 83.78125 92.050781 83.710938 92.847656 83.640625 93.640625 C 82.601562 93.785156 81.570312 93.785156 80.542969 93.640625 C 80.488281 90.308594 80.472656 86.976562 80.488281 83.640625 Z M 83.75 86.46875 C 84.425781 86.441406 85.09375 86.480469 85.761719 86.574219 C 86.117188 86.699219 86.28125 86.953125 86.25 87.335938 C 86.257812 87.691406 86.148438 88 85.925781 88.261719 C 85.210938 88.410156 84.484375 88.464844 83.75 88.425781 C 83.75 87.773438 83.75 87.121094 83.75 86.46875 Z M 83.75 86.46875 "/>
|
||||
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(89.803922%,0%,6.666667%);fill-opacity:1;" d="M 102.011719 83.640625 C 102.917969 83.625 103.824219 83.640625 104.726562 83.695312 C 105.167969 84.539062 105.765625 85.320312 106.523438 86.03125 C 106.800781 86.527344 107.160156 86.964844 107.609375 87.335938 C 107.644531 87.300781 107.679688 87.265625 107.71875 87.226562 C 107.960938 87.460938 108.179688 87.714844 108.371094 87.988281 C 108.476562 87.671875 108.53125 87.347656 108.53125 87.011719 C 108.445312 85.933594 108.410156 84.867188 108.425781 83.804688 C 109.5 83.648438 110.585938 83.59375 111.683594 83.640625 C 111.683594 87.011719 111.683594 90.378906 111.683594 93.75 C 110.8125 93.769531 109.945312 93.75 109.074219 93.695312 C 108.085938 92.304688 107.050781 90.949219 105.976562 89.621094 C 105.792969 89.398438 105.574219 89.214844 105.324219 89.074219 C 105.289062 90.597656 105.253906 92.121094 105.21875 93.640625 C 104.167969 93.785156 103.117188 93.785156 102.066406 93.640625 C 102.027344 92.753906 101.972656 91.863281 101.902344 90.976562 C 102 88.46875 102.035156 86.023438 102.011719 83.640625 Z M 102.011719 83.640625 "/>
|
||||
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(89.803922%,0%,6.666667%);fill-opacity:1;" d="M 112.226562 83.640625 C 115.199219 83.625 118.171875 83.640625 121.140625 83.695312 C 121.285156 84.566406 121.285156 85.433594 121.140625 86.304688 C 120.234375 86.359375 119.332031 86.375 118.425781 86.359375 C 118.421875 88.789062 118.421875 91.199219 118.425781 93.585938 C 117.390625 93.765625 116.339844 93.800781 115.273438 93.695312 C 115.179688 93.523438 115.105469 93.34375 115.054688 93.152344 C 115.054688 90.949219 115.089844 88.8125 115.164062 86.738281 C 115.066406 86.578125 114.921875 86.488281 114.726562 86.46875 C 114.003906 86.390625 113.28125 86.335938 112.554688 86.304688 C 112.410156 86.125 112.265625 85.941406 112.121094 85.761719 C 112.085938 85.023438 112.121094 84.316406 112.226562 83.640625 Z M 112.226562 83.640625 "/>
|
||||
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(89.803922%,0%,6.666667%);fill-opacity:1;" d="M 67.226562 96.03125 C 67.738281 96.015625 68.242188 96.03125 68.75 96.085938 C 69.75 96.304688 69.878906 96.757812 69.128906 97.445312 C 68.617188 97.179688 68.09375 97.160156 67.554688 97.390625 C 68.085938 97.625 68.628906 97.84375 69.183594 98.042969 C 70.230469 98.984375 70.15625 99.820312 68.96875 100.542969 C 67.976562 100.925781 67.050781 100.796875 66.195312 100.164062 C 66.160156 100.300781 66.109375 100.425781 66.03125 100.542969 C 65.597656 100.617188 65.164062 100.617188 64.726562 100.542969 C 64.726562 100.355469 64.652344 100.15625 64.511719 99.945312 C 63.972656 99.820312 63.425781 99.800781 62.878906 99.890625 C 62.75 100.097656 62.640625 100.316406 62.554688 100.542969 C 61.601562 100.523438 60.660156 100.523438 59.726562 100.542969 C 59.640625 99.992188 59.585938 99.429688 59.566406 98.859375 C 59.289062 99.21875 59.070312 99.617188 58.914062 100.054688 C 58.585938 100.199219 58.261719 100.199219 57.933594 100.054688 C 57.867188 99.953125 57.886719 99.859375 57.988281 99.78125 C 57.6875 99.527344 57.453125 99.21875 57.28125 98.859375 C 57.128906 99.425781 57.078125 100.003906 57.121094 100.597656 C 56.691406 100.628906 56.273438 100.59375 55.871094 100.488281 C 55.816406 99.039062 55.796875 97.589844 55.816406 96.140625 C 56.214844 96.125 56.613281 96.140625 57.011719 96.195312 C 57.464844 96.882812 57.933594 97.554688 58.425781 98.207031 C 58.917969 97.582031 59.355469 96.914062 59.726562 96.195312 C 60.125 96.140625 60.523438 96.125 60.925781 96.140625 C 60.886719 97.554688 60.925781 98.96875 61.03125 100.378906 C 61.707031 99.03125 62.324219 97.65625 62.878906 96.25 C 63.386719 96.105469 63.894531 96.105469 64.402344 96.25 C 64.917969 97.460938 65.460938 98.65625 66.03125 99.835938 C 66.242188 99.601562 66.421875 99.347656 66.574219 99.074219 C 67.117188 99.429688 67.695312 99.539062 68.316406 99.402344 C 68.460938 99.292969 68.460938 99.183594 68.316406 99.074219 C 67.695312 98.980469 67.113281 98.78125 66.574219 98.476562 C 65.84375 97.402344 66.058594 96.585938 67.226562 96.03125 Z M 63.53125 97.878906 C 63.746094 98.066406 63.890625 98.300781 63.96875 98.585938 C 63.753906 98.640625 63.535156 98.660156 63.316406 98.640625 C 63.355469 98.378906 63.429688 98.125 63.53125 97.878906 Z M 63.53125 97.878906 "/>
|
||||
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(89.803922%,0%,6.666667%);fill-opacity:1;" d="M 84.945312 96.03125 C 87.058594 95.84375 87.730469 96.710938 86.957031 98.640625 C 86.746094 98.824219 86.546875 99.023438 86.359375 99.238281 C 86.71875 99.292969 87.082031 99.3125 87.445312 99.292969 C 87.445312 99.726562 87.445312 100.164062 87.445312 100.597656 C 86.324219 100.597656 85.199219 100.597656 84.074219 100.597656 C 83.914062 100.226562 83.933594 99.867188 84.128906 99.511719 C 84.792969 98.90625 85.390625 98.253906 85.925781 97.554688 C 85.886719 97.375 85.777344 97.265625 85.597656 97.226562 C 85.320312 97.328125 85.050781 97.433594 84.78125 97.554688 C 84.476562 97.402344 84.1875 97.21875 83.914062 97.011719 C 84.089844 96.527344 84.433594 96.199219 84.945312 96.03125 Z M 84.945312 96.03125 "/>
|
||||
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(89.803922%,0%,6.666667%);fill-opacity:1;" d="M 89.074219 96.03125 C 90.355469 95.882812 91.171875 96.425781 91.523438 97.664062 C 91.820312 98.917969 91.441406 99.878906 90.378906 100.542969 C 88.644531 100.800781 87.703125 100.042969 87.554688 98.261719 C 87.621094 97.195312 88.128906 96.453125 89.074219 96.03125 Z M 89.511719 97.226562 C 89.714844 97.265625 89.878906 97.375 90 97.554688 C 90.199219 98.097656 90.199219 98.644531 90 99.183594 C 89.757812 99.457031 89.503906 99.457031 89.238281 99.183594 C 88.960938 98.46875 89.050781 97.816406 89.511719 97.226562 Z M 89.511719 97.226562 "/>
|
||||
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(89.803922%,0%,6.666667%);fill-opacity:1;" d="M 92.554688 96.03125 C 93.492188 95.835938 94.269531 96.089844 94.890625 96.792969 C 95.214844 97.886719 94.867188 98.703125 93.859375 99.238281 C 94.292969 99.292969 94.726562 99.3125 95.164062 99.292969 C 95.140625 99.421875 95.175781 99.53125 95.273438 99.621094 C 95.390625 99.414062 95.535156 99.234375 95.707031 99.074219 C 96.21875 99.386719 96.761719 99.460938 97.335938 99.292969 C 97.46875 99.160156 97.46875 99.035156 97.335938 98.914062 C 96.71875 98.910156 96.121094 98.820312 95.542969 98.640625 C 95.519531 97.808594 95.539062 96.976562 95.597656 96.140625 C 96.613281 96.140625 97.625 96.140625 98.640625 96.140625 C 98.640625 96.539062 98.640625 96.9375 98.640625 97.335938 C 98.0625 97.335938 97.480469 97.335938 96.902344 97.335938 C 96.902344 97.445312 96.902344 97.554688 96.902344 97.664062 C 98.476562 97.589844 99.074219 98.3125 98.695312 99.835938 C 97.652344 100.863281 96.511719 100.96875 95.273438 100.164062 C 95.171875 100.292969 95.132812 100.4375 95.164062 100.597656 C 94.011719 100.632812 92.871094 100.597656 91.738281 100.488281 C 91.667969 100.199219 91.667969 99.910156 91.738281 99.621094 C 92.355469 99.003906 92.972656 98.386719 93.585938 97.773438 C 93.640625 97.632812 93.660156 97.484375 93.640625 97.335938 C 93.171875 97.210938 92.792969 97.355469 92.5 97.773438 C 92.140625 97.503906 91.796875 97.210938 91.46875 96.902344 C 91.757812 96.503906 92.121094 96.214844 92.554688 96.03125 Z M 92.554688 96.03125 "/>
|
||||
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(89.803922%,0%,6.666667%);fill-opacity:1;" d="M 69.835938 96.140625 C 71.105469 96.140625 72.375 96.140625 73.640625 96.140625 C 73.640625 96.539062 73.640625 96.9375 73.640625 97.335938 C 73.25 97.304688 72.867188 97.339844 72.5 97.445312 C 72.445312 98.496094 72.425781 99.546875 72.445312 100.597656 C 71.980469 100.628906 71.527344 100.59375 71.085938 100.488281 C 71.03125 99.4375 71.015625 98.386719 71.03125 97.335938 C 70.632812 97.335938 70.234375 97.335938 69.835938 97.335938 C 69.835938 96.9375 69.835938 96.539062 69.835938 96.140625 Z M 69.835938 96.140625 "/>
|
||||
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(90.196079%,0%,7.058824%);fill-opacity:1;" d="M 73.96875 96.140625 C 75.164062 96.140625 76.359375 96.140625 77.554688 96.140625 C 77.554688 96.539062 77.554688 96.9375 77.554688 97.335938 C 76.828125 97.335938 76.105469 97.335938 75.378906 97.335938 C 75.378906 97.445312 75.378906 97.554688 75.378906 97.664062 C 75.996094 97.664062 76.613281 97.664062 77.226562 97.664062 C 77.226562 98.0625 77.226562 98.460938 77.226562 98.859375 C 76.613281 98.859375 75.996094 98.859375 75.378906 98.859375 C 75.378906 99.003906 75.378906 99.148438 75.378906 99.292969 C 76.140625 99.292969 76.902344 99.292969 77.664062 99.292969 C 77.664062 99.726562 77.664062 100.164062 77.664062 100.597656 C 76.441406 100.632812 75.226562 100.597656 74.023438 100.488281 C 73.96875 99.039062 73.949219 97.589844 73.96875 96.140625 Z M 73.96875 96.140625 "/>
|
||||
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(89.803922%,0%,6.666667%);fill-opacity:1;" d="M 77.988281 96.140625 C 78.96875 96.125 79.945312 96.140625 80.925781 96.195312 C 81.925781 96.625 82.269531 97.367188 81.957031 98.425781 C 81.796875 98.691406 81.597656 98.925781 81.359375 99.128906 C 81.652344 99.558594 81.90625 100.011719 82.121094 100.488281 C 81.652344 100.617188 81.179688 100.632812 80.707031 100.542969 C 80.445312 100.246094 80.230469 99.921875 80.054688 99.566406 C 79.839844 99.511719 79.621094 99.492188 79.402344 99.511719 C 79.402344 99.875 79.402344 100.234375 79.402344 100.597656 C 78.9375 100.628906 78.484375 100.59375 78.042969 100.488281 C 77.988281 99.039062 77.972656 97.589844 77.988281 96.140625 Z M 79.511719 97.335938 C 79.765625 97.320312 80.019531 97.335938 80.273438 97.390625 C 80.65625 97.589844 80.691406 97.84375 80.378906 98.152344 C 80.054688 98.207031 79.730469 98.222656 79.402344 98.207031 C 79.378906 97.90625 79.414062 97.613281 79.511719 97.335938 Z M 79.511719 97.335938 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 21 KiB |
@@ -184,5 +184,86 @@
|
||||
},
|
||||
"select_lang": {
|
||||
"title": "Select Language"
|
||||
},
|
||||
"flag_input": {
|
||||
"title": "Flag Selector",
|
||||
"real": "Real Flags",
|
||||
"custom": "Custom Flags",
|
||||
"select_layer": "Select a Layer",
|
||||
"max_layer_reached_1": "You've reached the maximum number of layers.",
|
||||
"max_layer_reached_2": "Please remove some to add new ones.",
|
||||
"preview": "Preview",
|
||||
"apply": "Apply Custom Flag",
|
||||
"layer": "Layer",
|
||||
"fixed": "fixed",
|
||||
"remove": "Remove",
|
||||
"color": "Color",
|
||||
"layers": {
|
||||
"frame": "Frame",
|
||||
"full": "Full Background",
|
||||
"center_circle": "Center Circle",
|
||||
"center_star": "Center Star",
|
||||
"center_flower": "Center Flower",
|
||||
"center_hline": "Horizontal Stripe",
|
||||
"center_vline": "Vertical Stripe",
|
||||
"flower_tc": "Top Center Flower",
|
||||
"flower_tl": "Top Left Flower",
|
||||
"flower_tr": "Top Right Flower",
|
||||
"diag_bl": "Bottom Left Diagonal",
|
||||
"diag_br": "Bottom Right Diagonal",
|
||||
"half_l": "Left Half",
|
||||
"half_r": "Right Half",
|
||||
"half_t": "Top Half",
|
||||
"half_b": "Bottom Half",
|
||||
"triangle_t": "Top Triangle",
|
||||
"triangle_b": "Bottom Triangle",
|
||||
"triangle_l": "Left Triangle",
|
||||
"triangle_r": "Right Triangle",
|
||||
"triangle_tl": "Top Left Triangle",
|
||||
"triangle_tr": "Top Right Triangle",
|
||||
"triangle_bl": "Bottom Left Triangle",
|
||||
"triangle_br": "Bottom Right Triangle",
|
||||
"tricolor_l": "Left Tricolor",
|
||||
"tricolor_c": "Center Tricolor",
|
||||
"tricolor_r": "Right Tricolor",
|
||||
"tricolor_t": "Top Tricolor",
|
||||
"tricolor_m": "Middle Tricolor",
|
||||
"tricolor_b": "Bottom Tricolor",
|
||||
"mini_tr_tl": "Mini Top Left Triangle",
|
||||
"mini_tr_tr": "Mini Top Right Triangle",
|
||||
"mini_tr_bl": "Mini Bottom Left Triangle",
|
||||
"mini_tr_br": "Mini Bottom Right Triangle",
|
||||
"nato_emblem": "NATO Emblem",
|
||||
"eu_star": "EU Stars",
|
||||
"laurel_wreath": "Laurel Wreath",
|
||||
"octagram": "Octagram",
|
||||
"octagram_2": "Diagonal Octagram",
|
||||
"rocket": "Rocket",
|
||||
"rocket_mini": "Mini Rocket",
|
||||
"ofm_2025": "OFM 2025",
|
||||
"translator": "Translator",
|
||||
"beta_tester": "Beta Tester",
|
||||
"beta_tester_circle": "Beta Circle",
|
||||
"admin_contributors": "Contributors Emblem",
|
||||
"admin_shield": "Admin Shield",
|
||||
"admin_shield_r": "Right Admin Shield",
|
||||
"admin_evan": "Evan's Crown",
|
||||
"og": "OG Emblem",
|
||||
"og_plus": "OG+ Emblem"
|
||||
},
|
||||
"reason": {
|
||||
"admin_evan": "Only Evan can use this layer",
|
||||
"admin": "Admin only",
|
||||
"contributors": "Contributors only",
|
||||
"beta": "Beta testers only",
|
||||
"supporters": "Supporters only",
|
||||
"og": "OG players only",
|
||||
"og100": "OG100 players only",
|
||||
"translator": "Translator only",
|
||||
"well_known": "Well-known players only",
|
||||
"known": "Known players only",
|
||||
"seen": "Seen players only",
|
||||
"login": "Login required"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,5 +176,86 @@
|
||||
},
|
||||
"select_lang": {
|
||||
"title": "言語を選択"
|
||||
},
|
||||
"flag_input": {
|
||||
"title": "旗選択",
|
||||
"real": "実在の旗",
|
||||
"custom": "カスタム旗",
|
||||
"select_layer": "レイヤーを選択",
|
||||
"max_layer_reached_1": "レイヤー数の上限に達しています。",
|
||||
"max_layer_reached_2": "新しく追加するには、いくつか削除してください。",
|
||||
"preview": "プレビュー",
|
||||
"apply": "カスタム旗を適用",
|
||||
"layer": "レイヤー",
|
||||
"fixed": "固定",
|
||||
"remove": "削除",
|
||||
"color": "色",
|
||||
"layers": {
|
||||
"frame": "フレーム",
|
||||
"full": "背景",
|
||||
"center_circle": "中央の円",
|
||||
"center_star": "中央の星",
|
||||
"center_flower": "中央の花",
|
||||
"center_hline": "横ストライプ",
|
||||
"center_vline": "縦ストライプ",
|
||||
"flower_tc": "上中央の花",
|
||||
"flower_tl": "左上の花",
|
||||
"flower_tr": "右上の花",
|
||||
"diag_bl": "左下の斜線",
|
||||
"diag_br": "右下の斜線",
|
||||
"half_l": "左半分",
|
||||
"half_r": "右半分",
|
||||
"half_t": "上半分",
|
||||
"half_b": "下半分",
|
||||
"triangle_t": "上三角形",
|
||||
"triangle_b": "下三角形",
|
||||
"triangle_l": "左三角形",
|
||||
"triangle_r": "右三角形",
|
||||
"triangle_tl": "左上三角形",
|
||||
"triangle_tr": "右上三角形",
|
||||
"triangle_bl": "左下三角形",
|
||||
"triangle_br": "右下三角形",
|
||||
"tricolor_l": "三色旗(左)",
|
||||
"tricolor_c": "三色旗(中央)",
|
||||
"tricolor_r": "三色旗(右)",
|
||||
"tricolor_t": "三色旗(上)",
|
||||
"tricolor_m": "三色旗(中央)",
|
||||
"tricolor_b": "三色旗(下)",
|
||||
"mini_tr_tl": "ミニ左上三角形",
|
||||
"mini_tr_tr": "ミニ右上三角形",
|
||||
"mini_tr_bl": "ミニ左下三角形",
|
||||
"mini_tr_br": "ミニ右下三角形",
|
||||
"nato_emblem": "NATOエンブレム",
|
||||
"eu_star": "EUの星",
|
||||
"laurel_wreath": "月桂冠",
|
||||
"octagram": "八芒星",
|
||||
"octagram_2": "斜めの八芒星",
|
||||
"rocket": "ロケット",
|
||||
"rocket_mini": "ミニロケット",
|
||||
"ofm_2025": "OFM 2025",
|
||||
"translator": "翻訳者マーク",
|
||||
"beta_tester": "ベータテスター",
|
||||
"beta_tester_circle": "ベータサークル",
|
||||
"admin_contributors": "貢献者エンブレム",
|
||||
"admin_shield": "管理者の盾",
|
||||
"admin_shield_r": "管理者の盾(右)",
|
||||
"admin_evan": "Evan様の冠",
|
||||
"og": "OGエンブレム",
|
||||
"og_plus": "OG+エンブレム"
|
||||
},
|
||||
"reason": {
|
||||
"admin_evan": "このレイヤーはEvanのみ使用できます",
|
||||
"admin": "管理者専用",
|
||||
"contributors": "貢献者専用",
|
||||
"beta": "ベータテスター専用",
|
||||
"supporters": "支援者専用",
|
||||
"og": "OGプレイヤー専用",
|
||||
"og100": "OG100プレイヤー専用",
|
||||
"translator": "翻訳者専用",
|
||||
"well_known": "有名プレイヤー専用",
|
||||
"known": "知られているプレイヤー専用",
|
||||
"seen": "確認済みプレイヤー専用",
|
||||
"login": "ログインが必要です"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+106
-646
@@ -1,11 +1,8 @@
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import Countries from "./data/countries.json";
|
||||
import { translateText } from "../client/Utils";
|
||||
const flagKey: string = "flag";
|
||||
|
||||
import disabled from "../../resources/images/DisabledIcon.svg";
|
||||
import locked from "../../resources/images/Locked.svg";
|
||||
|
||||
import frame from "../../resources/flags/custom/frame.svg";
|
||||
|
||||
import center_circle from "../../resources/flags/custom/center_circle.svg";
|
||||
@@ -32,6 +29,7 @@ import mini_tr_tr from "../../resources/flags/custom/mini_tr_tr.svg";
|
||||
import nato_emblem from "../../resources/flags/custom/nato_emblem.svg";
|
||||
import octagram from "../../resources/flags/custom/octagram.svg";
|
||||
import octagram_2 from "../../resources/flags/custom/octagram_2.svg";
|
||||
import ofm_2025 from "../../resources/flags/custom/ofm_2025.svg";
|
||||
import triangle_b from "../../resources/flags/custom/triangle_b.svg";
|
||||
import triangle_bl from "../../resources/flags/custom/triangle_bl.svg";
|
||||
import triangle_br from "../../resources/flags/custom/triangle_br.svg";
|
||||
@@ -56,7 +54,7 @@ import og_plus from "../../resources/flags/custom/og_plus.svg";
|
||||
import translator from "../../resources/flags/custom/translator.svg";
|
||||
|
||||
import beta_tester from "../../resources/flags/custom/beta_tester.svg";
|
||||
import beta_tester_hole from "../../resources/flags/custom/beta_tester_hole.svg";
|
||||
import beta_tester_circle from "../../resources/flags/custom/beta_tester_circle.svg";
|
||||
|
||||
import admin_contributors from "../../resources/flags/custom/admin_contributors.svg";
|
||||
import admin_shield from "../../resources/flags/custom/admin_shield.svg";
|
||||
@@ -103,8 +101,9 @@ export const FlagMap: Record<string, string> = {
|
||||
laurel_wreath,
|
||||
octagram,
|
||||
octagram_2,
|
||||
ofm_2025,
|
||||
beta_tester,
|
||||
beta_tester_hole,
|
||||
beta_tester_circle,
|
||||
rocket,
|
||||
rocket_mini,
|
||||
admin_contributors,
|
||||
@@ -155,12 +154,13 @@ export const LayerShortNames: Record<string, string> = {
|
||||
nato_emblem: "ne",
|
||||
eu_star: "es",
|
||||
laurel_wreath: "lw",
|
||||
ofm_2025: "ofm25",
|
||||
octagram: "oc",
|
||||
octagram_2: "oc2",
|
||||
og: "og",
|
||||
og_plus: "ogp",
|
||||
beta_tester: "bt",
|
||||
beta_tester_hole: "bth",
|
||||
beta_tester_circle: "btc",
|
||||
rocket: "rc",
|
||||
rocket_mini: "rcm",
|
||||
translator: "tlr",
|
||||
@@ -213,39 +213,41 @@ export const ColorShortNames: Record<string, string> = {
|
||||
water: "wt", // soft blue breathing animation
|
||||
};
|
||||
|
||||
let isDebug_: boolean = false;
|
||||
const isEvan: boolean = false;
|
||||
const isAdmin: boolean = false;
|
||||
const isOg: boolean = false;
|
||||
const isOg100: boolean = false;
|
||||
const isSupporters: boolean = false;
|
||||
const isBetaTester: boolean = false;
|
||||
const isContributors: boolean = false;
|
||||
const isTranslator: boolean = false;
|
||||
const isWellKnownPlayer: boolean = false;
|
||||
const isKnownPlayer: boolean = false;
|
||||
const isSeenplayer: boolean = false;
|
||||
const isLoginPlayer: boolean = false;
|
||||
export const isDebug_: boolean = true;
|
||||
export const isEvan: boolean = false;
|
||||
export const isAdmin: boolean = false;
|
||||
export const isOg: boolean = false;
|
||||
export const isOg100: boolean = false;
|
||||
export const isSupporters: boolean = false;
|
||||
export const isBetaTester: boolean = false;
|
||||
export const isContributors: boolean = false;
|
||||
export const isTranslator: boolean = false;
|
||||
export const isWellKnownPlayer: boolean = false;
|
||||
export const isKnownPlayer: boolean = false;
|
||||
export const isSeenplayer: boolean = false;
|
||||
export const isLoginPlayer: boolean = false;
|
||||
|
||||
let MAX_LAYER = 50;
|
||||
export let MAX_LAYER = 50;
|
||||
|
||||
type LockReasonMap = Record<string, string>;
|
||||
|
||||
function checkPermission(): [string[], string[], LockReasonMap] {
|
||||
export function checkPermission(): [string[], string[], LockReasonMap, number] {
|
||||
const lockedLayers_: string[] = [];
|
||||
const lockedColors_: string[] = [];
|
||||
const lockedReasons_: LockReasonMap = {};
|
||||
|
||||
MAX_LAYER = 50;
|
||||
|
||||
const lock = (list: string[], reason: string) => {
|
||||
const lock = (list: string[], reasonKey: string) => {
|
||||
const reason = translateText(reasonKey);
|
||||
for (const item of list) {
|
||||
lockedLayers_.push(item);
|
||||
lockedReasons_[item] = reason;
|
||||
}
|
||||
};
|
||||
|
||||
const lockColor = (list: string[], reason: string) => {
|
||||
const lockColor = (list: string[], reasonKey: string) => {
|
||||
const reason = translateText(reasonKey);
|
||||
for (const color of list) {
|
||||
lockedColors_.push(color);
|
||||
lockedReasons_[color] = reason;
|
||||
@@ -254,13 +256,13 @@ function checkPermission(): [string[], string[], LockReasonMap] {
|
||||
|
||||
if (isEvan || isDebug_) {
|
||||
MAX_LAYER = 50;
|
||||
return [lockedLayers_, lockedColors_, lockedReasons_];
|
||||
return [lockedLayers_, lockedColors_, lockedReasons_, MAX_LAYER];
|
||||
}
|
||||
|
||||
lock(["admin_evan"], "Only Evan can use this layer");
|
||||
lock(["admin_evan"], "flag_input.reason.admin_evan");
|
||||
|
||||
if (!isAdmin) {
|
||||
lock(["admin_shield", "admin_shield_r"], "Admin only");
|
||||
lock(["admin_shield", "admin_shield_r"], "flag_input.reason.admin");
|
||||
}
|
||||
|
||||
if (isAdmin) {
|
||||
@@ -282,15 +284,15 @@ function checkPermission(): [string[], string[], LockReasonMap] {
|
||||
}
|
||||
|
||||
if (!isContributors) {
|
||||
lock(["admin_contributors"], "Contributors only");
|
||||
lock(["admin_contributors"], "flag_input.reason.contributors");
|
||||
}
|
||||
|
||||
if (!isBetaTester) {
|
||||
lock(["beta_tester", "beta_tester_hole"], "Beta testers only");
|
||||
lock(["beta_tester", "beta_tester_circle"], "flag_input.reason.beta");
|
||||
}
|
||||
|
||||
if (!isSupporters) {
|
||||
lock(["rocket_mini", "rocket"], "Supporters only");
|
||||
lock(["rocket_mini", "rocket"], "flag_input.reason.supporters");
|
||||
lockColor(
|
||||
[
|
||||
"rainbow",
|
||||
@@ -302,22 +304,22 @@ function checkPermission(): [string[], string[], LockReasonMap] {
|
||||
"glitch",
|
||||
"water",
|
||||
],
|
||||
"Supporters only",
|
||||
"flag_input.reason.supporters",
|
||||
);
|
||||
} else {
|
||||
return [lockedLayers_, lockedColors_, lockedReasons_];
|
||||
return [lockedLayers_, lockedColors_, lockedReasons_, MAX_LAYER];
|
||||
}
|
||||
|
||||
if (!isOg) {
|
||||
lock(["og_plus"], "OG players only");
|
||||
lock(["og_plus"], "flag_input.reason.og");
|
||||
}
|
||||
|
||||
if (!isOg100) {
|
||||
lock(["og"], "OG100 players only");
|
||||
lock(["og"], "flag_input.reason.og100");
|
||||
}
|
||||
|
||||
if (!isTranslator) {
|
||||
lock(["beta_tester", "beta_tester_hole"], "Beta testers only");
|
||||
lock(["translator"], "flag_input.reason.translator");
|
||||
}
|
||||
|
||||
if (!isWellKnownPlayer) {
|
||||
@@ -335,7 +337,7 @@ function checkPermission(): [string[], string[], LockReasonMap] {
|
||||
"octagram",
|
||||
"octagram_2",
|
||||
],
|
||||
"Well-known players only",
|
||||
"flag_input.reason.well_known",
|
||||
);
|
||||
lockColor(
|
||||
[
|
||||
@@ -354,7 +356,7 @@ function checkPermission(): [string[], string[], LockReasonMap] {
|
||||
"#36454f",
|
||||
"#fffff0",
|
||||
],
|
||||
"Well-known players only",
|
||||
"flag_input.reason.well_known",
|
||||
);
|
||||
|
||||
if (!isKnownPlayer) {
|
||||
@@ -375,7 +377,7 @@ function checkPermission(): [string[], string[], LockReasonMap] {
|
||||
"mini_tr_br",
|
||||
"mini_tr_bl",
|
||||
],
|
||||
"Known players only",
|
||||
"flag_input.reason.known",
|
||||
);
|
||||
lockColor(
|
||||
[
|
||||
@@ -390,91 +392,33 @@ function checkPermission(): [string[], string[], LockReasonMap] {
|
||||
"#8b0000",
|
||||
"#191970",
|
||||
],
|
||||
"Known players only",
|
||||
"flag_input.reason.known",
|
||||
);
|
||||
|
||||
if (!isSeenplayer) {
|
||||
lock(["half_l", "half_r", "half_b", "half_t"], "Seen players only");
|
||||
lockColor(["#ffa500", "#00ffff"], "Seen players only");
|
||||
lock(
|
||||
["half_l", "half_r", "half_b", "half_t"],
|
||||
"flag_input.reason.seen",
|
||||
);
|
||||
lockColor(["#ffa500", "#00ffff"], "flag_input.reason.seen");
|
||||
|
||||
if (!isLoginPlayer) {
|
||||
lock(
|
||||
["triangle_br", "triangle_bl", "triangle_tr", "triangle_tl"],
|
||||
"Login required",
|
||||
"flag_input.reason.login",
|
||||
);
|
||||
lockColor(["#ffff00", "#008000"], "Login required");
|
||||
lockColor(["#ffff00", "#008000"], "flag_input.reason.login");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return [lockedLayers_, lockedColors_, lockedReasons_];
|
||||
|
||||
return [lockedLayers_, lockedColors_, lockedReasons_, MAX_LAYER];
|
||||
}
|
||||
|
||||
@customElement("flag-input")
|
||||
export class FlagInput extends LitElement {
|
||||
@state() private flag: string = "";
|
||||
@state() private search: string = "";
|
||||
@state() private showModal: boolean = false;
|
||||
@state() private activeTab: "real" | "custom" = "real";
|
||||
@state() private selectedColor: string = "#ff0000";
|
||||
@state() private openColorIndex: number | null = null;
|
||||
|
||||
private readonly colorOptions: string[] = Object.keys(ColorShortNames);
|
||||
|
||||
@state() private customLayers: { name: string; color: string }[] = [];
|
||||
|
||||
@state() private hoveredColor: string | null = null;
|
||||
@state() private hoverPosition = { x: 0, y: 0 };
|
||||
@state() private hoverReason: string | null = null;
|
||||
|
||||
private addLayer(name: string) {
|
||||
const totalLayers = this.customLayers.length;
|
||||
|
||||
if (totalLayers >= MAX_LAYER) {
|
||||
alert(`You can only add up to ${MAX_LAYER} layers.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const newLayer = { name, color: this.selectedColor };
|
||||
this.customLayers = [
|
||||
this.customLayers[0],
|
||||
newLayer,
|
||||
...this.customLayers.slice(1),
|
||||
];
|
||||
}
|
||||
private removeLayer(index: number) {
|
||||
this.customLayers = this.customLayers.filter((_, i) => i !== index);
|
||||
}
|
||||
|
||||
private moveLayerUp(index: number) {
|
||||
if (index === 0) return;
|
||||
const newLayers = [...this.customLayers];
|
||||
[newLayers[index - 1], newLayers[index]] = [
|
||||
newLayers[index],
|
||||
newLayers[index - 1],
|
||||
];
|
||||
this.customLayers = newLayers;
|
||||
}
|
||||
|
||||
private moveLayerDown(index: number) {
|
||||
if (index === this.customLayers.length - 1) return;
|
||||
const newLayers = [...this.customLayers];
|
||||
[newLayers[index], newLayers[index + 1]] = [
|
||||
newLayers[index + 1],
|
||||
newLayers[index],
|
||||
];
|
||||
this.customLayers = newLayers;
|
||||
}
|
||||
|
||||
private updateLayerColor(index: number, color: string) {
|
||||
const newLayers = [...this.customLayers];
|
||||
newLayers[index].color = color;
|
||||
this.customLayers = newLayers;
|
||||
}
|
||||
|
||||
private toggleColorPicker(index: number) {
|
||||
this.openColorIndex = this.openColorIndex === index ? null : index;
|
||||
}
|
||||
@state() public flag: string = "";
|
||||
|
||||
static styles = css`
|
||||
@media (max-width: 768px) {
|
||||
@@ -488,19 +432,6 @@ export class FlagInput extends LitElement {
|
||||
}
|
||||
`;
|
||||
|
||||
private handleSearch(e: Event) {
|
||||
this.search = String((e.target as HTMLInputElement).value);
|
||||
}
|
||||
|
||||
private setFlag(flag: string) {
|
||||
if (flag == "xx") {
|
||||
flag = "";
|
||||
}
|
||||
this.flag = flag;
|
||||
this.showModal = false;
|
||||
this.storeFlag(flag);
|
||||
}
|
||||
|
||||
public getCurrentFlag(): string {
|
||||
return this.flag;
|
||||
}
|
||||
@@ -513,14 +444,6 @@ export class FlagInput extends LitElement {
|
||||
return "";
|
||||
}
|
||||
|
||||
private storeFlag(flag: string) {
|
||||
if (flag) {
|
||||
localStorage.setItem(flagKey, flag);
|
||||
} else if (flag === "") {
|
||||
localStorage.removeItem(flagKey);
|
||||
}
|
||||
}
|
||||
|
||||
private dispatchFlagEvent() {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("flag-change", {
|
||||
@@ -535,543 +458,26 @@ export class FlagInput extends LitElement {
|
||||
super.connectedCallback();
|
||||
this.flag = this.getStoredFlag();
|
||||
this.dispatchFlagEvent();
|
||||
|
||||
if (this.isCustomFlag(this.flag)) {
|
||||
this.customLayers = this.decodeCustomFlag(this.flag);
|
||||
} else {
|
||||
if (this.customLayers.length === 0) {
|
||||
this.customLayers = [
|
||||
{ name: "full", color: "#ffffff" },
|
||||
{ name: "frame", color: "#000000" },
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
|
||||
private lockedLayers: string[] = [];
|
||||
|
||||
private lockedColors: string[] = [];
|
||||
|
||||
private lockedReasons: Record<string, string> = {};
|
||||
|
||||
render() {
|
||||
isDebug_ = true;
|
||||
const result = checkPermission();
|
||||
this.lockedLayers = Array.isArray(result[0]) ? result[0] : [result[0]];
|
||||
this.lockedColors = Array.isArray(result[1]) ? result[1] : [result[1]];
|
||||
this.lockedReasons = result[2] || {};
|
||||
return html`
|
||||
<div class="flex relative">
|
||||
<button
|
||||
@click=${() => (this.showModal = true)}
|
||||
id="flag-input_"
|
||||
class="border p-[4px] rounded-lg flex cursor-pointer border-black/30 dark:border-gray-300/60 bg-white/70 dark:bg-[rgba(55,65,81,0.7)]"
|
||||
title="Pick a flag!"
|
||||
>
|
||||
${this.renderFlagPreview(this.flag)}
|
||||
</button>
|
||||
|
||||
${this.showModal
|
||||
? html`
|
||||
<div
|
||||
class="text-white flex flex-col gap-[0.5rem] absolute top-[60px] left-[0px] w-[880%] h-[500px] max-h-[50vh] max-w-[87vw] bg-gray-900/80 backdrop-blur-md p-[10px] rounded-[8px] z-[3]"
|
||||
>
|
||||
<!-- tab -->
|
||||
<div class="flex gap-2 mb-2">
|
||||
<button
|
||||
class="px-4 py-1 rounded-lg font-bold ${this.activeTab ===
|
||||
"real"
|
||||
? "bg-blue-500 text-white"
|
||||
: "bg-gray-300 text-black"}"
|
||||
@click=${() => (this.activeTab = "real")}
|
||||
>
|
||||
Real Flags
|
||||
</button>
|
||||
<button
|
||||
class="px-4 py-1 rounded-lg font-bold ${this.activeTab ===
|
||||
"custom"
|
||||
? "bg-blue-500 text-white"
|
||||
: "bg-gray-300 text-black"}"
|
||||
@click=${() => (this.activeTab = "custom")}
|
||||
>
|
||||
Custom Flags
|
||||
</button>
|
||||
</div>
|
||||
|
||||
${this.activeTab === "real"
|
||||
? html`
|
||||
<input
|
||||
class="h-[2rem] border-none text-center border border-gray-300 rounded-xl shadow-sm text-2xl text-center focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-black dark:border-gray-300/60 dark:bg-gray-700 dark:text-white"
|
||||
type="text"
|
||||
placeholder="Search..."
|
||||
@change=${this.handleSearch}
|
||||
@keyup=${this.handleSearch}
|
||||
/>
|
||||
<div
|
||||
class="flex flex-wrap justify-evenly gap-[1rem] overflow-y-auto overflow-x-hidden"
|
||||
>
|
||||
${Countries.filter(
|
||||
(country) =>
|
||||
country.name
|
||||
.toLowerCase()
|
||||
.includes(this.search.toLowerCase()) ||
|
||||
country.code
|
||||
.toLowerCase()
|
||||
.includes(this.search.toLowerCase()),
|
||||
).map(
|
||||
(country) => html`
|
||||
<button
|
||||
@click=${() => this.setFlag(country.code)}
|
||||
class="text-center cursor-pointer border-none bg-none opacity-70 sm:w-[calc(33.3333%-15px)] w-[calc(100%/3-15px)] md:w-[calc(100%/4-15px)]"
|
||||
>
|
||||
<img
|
||||
class="country-flag w-full h-auto"
|
||||
src="/flags/${country.code}.svg"
|
||||
/>
|
||||
<span class="country-name">${country.name}</span>
|
||||
</button>
|
||||
`,
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: html`
|
||||
<div class="grid grid-cols-[1fr_2fr] gap-4 p-2 h-full">
|
||||
<!-- left -->
|
||||
<div
|
||||
class="flex flex-col items-center gap-2 overflow-y-auto max-h-[calc(50vh-4rem)]"
|
||||
>
|
||||
<div class="flex flex-wrap gap-1 mb-2 justify-center">
|
||||
${this.colorOptions.map((color) => {
|
||||
const isLocked =
|
||||
this.lockedColors.includes(color);
|
||||
const isSpecial = !color.startsWith("#");
|
||||
const colorClass = isSpecial
|
||||
? `flag-color-${color}`
|
||||
: "";
|
||||
const inlineStyle = isSpecial
|
||||
? ""
|
||||
: `background-color: ${color};`;
|
||||
const isSelected = this.selectedColor === color;
|
||||
return html`
|
||||
<button
|
||||
class="w-4 h-4 rounded-full border-2 relative
|
||||
${isSelected ? "border-white" : "border-gray-400"}
|
||||
${isLocked ? "opacity-40 cursor-not-allowed" : ""}
|
||||
${colorClass}"
|
||||
style=${inlineStyle}
|
||||
@click=${() =>
|
||||
!isLocked && (this.selectedColor = color)}
|
||||
@mouseenter=${(e: MouseEvent) => {
|
||||
if (isLocked) {
|
||||
this.hoveredColor = color;
|
||||
}
|
||||
}}
|
||||
@mousemove=${(e: MouseEvent) => {
|
||||
if (isLocked) {
|
||||
this.hoverPosition = {
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
};
|
||||
}
|
||||
}}
|
||||
@mouseleave=${() => {
|
||||
this.hoveredColor = null;
|
||||
}}
|
||||
title=${color}
|
||||
>
|
||||
${isLocked
|
||||
? html`<img
|
||||
src=${locked}
|
||||
alt="Locked"
|
||||
class="absolute top-1/2 left-1/2 w-5 h-5 -translate-x-1/2 -translate-y-1/2 pointer-events-none"
|
||||
/>`
|
||||
: ""}
|
||||
</button>
|
||||
`;
|
||||
})}
|
||||
</div>
|
||||
|
||||
<p class="text-lg font-bold text-white self-start">
|
||||
Select a Layer
|
||||
</p>
|
||||
|
||||
${this.customLayers.length >= MAX_LAYER
|
||||
? html`
|
||||
<p
|
||||
class="text-sm text-red-400 self-start -mt-1 mb-2"
|
||||
>
|
||||
You've reached the maximum number of
|
||||
layers.<br />
|
||||
Please remove some to add new ones.
|
||||
</p>
|
||||
`
|
||||
: null}
|
||||
|
||||
<div
|
||||
class="grid grid-cols-2 gap-2 w-full max-h-[300px] overflow-y-auto pr-1 mb-2"
|
||||
>
|
||||
${Object.entries(FlagMap)
|
||||
.filter(
|
||||
([name]) => name !== "frame" && name !== "full",
|
||||
)
|
||||
.map(([name, src]) => {
|
||||
const isLocked =
|
||||
this.lockedLayers.includes(name);
|
||||
const isDisabled =
|
||||
!isLocked &&
|
||||
this.customLayers.length >= MAX_LAYER;
|
||||
|
||||
const isSpecial =
|
||||
!this.selectedColor.startsWith("#");
|
||||
const colorClass = isSpecial
|
||||
? `flag-color-${this.selectedColor}`
|
||||
: "";
|
||||
const colorStyle = isSpecial
|
||||
? ""
|
||||
: `background-color: ${this.selectedColor};`;
|
||||
|
||||
const reason = isLocked
|
||||
? this.lockedReasons[name] || "Locked"
|
||||
: isDisabled
|
||||
? `You can only add up to ${MAX_LAYER} layers.`
|
||||
: "";
|
||||
|
||||
return html`
|
||||
<button
|
||||
@click=${() => {
|
||||
if (!isLocked && !isDisabled)
|
||||
this.addLayer(name);
|
||||
}}
|
||||
class="p-1 border border-gray-600 rounded-md transition relative
|
||||
${isLocked || isDisabled
|
||||
? "opacity-40 cursor-not-allowed"
|
||||
: "hover:border-white"}"
|
||||
?disabled=${isLocked || isDisabled}
|
||||
@mouseenter=${(e: MouseEvent) => {
|
||||
if (reason) {
|
||||
this.hoveredColor = name;
|
||||
this.hoverReason = reason;
|
||||
this.hoverPosition = {
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
};
|
||||
}
|
||||
}}
|
||||
@mousemove=${(e: MouseEvent) => {
|
||||
if (reason) {
|
||||
this.hoverPosition = {
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
};
|
||||
}
|
||||
}}
|
||||
@mouseleave=${() => {
|
||||
this.hoveredColor = null;
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class="w-full h-14 rounded relative"
|
||||
title=${name}
|
||||
>
|
||||
<!-- black frame background -->
|
||||
<div
|
||||
class="absolute inset-0 rounded"
|
||||
style="
|
||||
background-color: black;
|
||||
-webkit-mask: url(${FlagMap.frame}) center / contain no-repeat;
|
||||
mask: url(${FlagMap.frame}) center / contain no-repeat;
|
||||
"
|
||||
></div>
|
||||
|
||||
<!-- selected color mask -->
|
||||
<div
|
||||
class="absolute inset-0 rounded ${colorClass}"
|
||||
style="
|
||||
${colorStyle}
|
||||
-webkit-mask: url(${src}) center / contain no-repeat;
|
||||
mask: url(${src}) center / contain no-repeat;
|
||||
"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
${isLocked
|
||||
? html`<img
|
||||
src=${locked}
|
||||
alt="Locked"
|
||||
class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-10 h-10 opacity-60 drop-shadow-md pointer-events-none"
|
||||
/>`
|
||||
: isDisabled
|
||||
? html`<img
|
||||
src=${disabled}
|
||||
alt="Disabled"
|
||||
class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-10 h-10 opacity-60 drop-shadow-md pointer-events-none"
|
||||
/>`
|
||||
: null}
|
||||
</button>
|
||||
`;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- right -->
|
||||
<div
|
||||
class="flex flex-col items-center h-full overflow-hidden max-h-[calc(50vh-4rem)]"
|
||||
>
|
||||
<p class="text-lg font-bold text-white mb-2">
|
||||
Preview
|
||||
</p>
|
||||
<div
|
||||
class="relative w-[160px] h-[100px] min-w-[160px] min-h-[100px] max-w-[160px] max-h-[100px] flex-shrink-0 border border-gray-400 rounded bg-white"
|
||||
>
|
||||
${this.customLayers.map(({ name, color }) => {
|
||||
const src = FlagMap[name];
|
||||
if (!src) return null;
|
||||
console.log("color", color);
|
||||
const isSpecial = !color.startsWith("#");
|
||||
const colorClass = isSpecial
|
||||
? `flag-color-${color}`
|
||||
: "";
|
||||
const bgStyle = isSpecial
|
||||
? ""
|
||||
: `background-color: ${color};`;
|
||||
|
||||
return html`
|
||||
<div
|
||||
class="absolute top-0 left-0 w-full h-full ${colorClass}"
|
||||
style="
|
||||
${bgStyle}
|
||||
-webkit-mask: url(${src}) center / contain no-repeat;
|
||||
mask: url(${src}) center / contain no-repeat;
|
||||
"
|
||||
></div>
|
||||
`;
|
||||
})}
|
||||
</div>
|
||||
|
||||
<button
|
||||
@click=${() => {
|
||||
const code = this.encodeCustomFlag();
|
||||
navigator.clipboard.writeText(code);
|
||||
console.log("Copied: " + code);
|
||||
this.setFlag(code);
|
||||
}}
|
||||
class="mt-2 px-4 py-1 bg-green-600 text-white rounded hover:bg-green-500"
|
||||
>
|
||||
Copy Flag Code
|
||||
</button>
|
||||
|
||||
<div
|
||||
class="mt-4 w-full max-h-[300px] overflow-y-auto mb-2"
|
||||
>
|
||||
<p class="text-lg font-bold text-white mb-2">
|
||||
Layers (${this.customLayers.length})
|
||||
</p>
|
||||
<ul class="text-white space-y-1">
|
||||
${this.customLayers.map((_, i, arr) => {
|
||||
const index = arr.length - 1 - i;
|
||||
const { name, color } = arr[index];
|
||||
const isFixed =
|
||||
name === "full" || name === "frame";
|
||||
|
||||
const isSpecial = !color.startsWith("#");
|
||||
const colorClass = isSpecial
|
||||
? `flag-color-${color}`
|
||||
: "";
|
||||
const inlineStyle = isSpecial
|
||||
? ""
|
||||
: `background-color: ${color};`;
|
||||
|
||||
return html`
|
||||
<li
|
||||
class="flex flex-col gap-1 py-1 px-2 bg-gray-800 rounded"
|
||||
>
|
||||
<div
|
||||
class="flex justify-between items-center"
|
||||
>
|
||||
<span class="flex items-center gap-2">
|
||||
<span
|
||||
class="inline-block w-4 h-4 rounded-full ${colorClass}"
|
||||
style=${inlineStyle}
|
||||
></span>
|
||||
${name}
|
||||
${isFixed
|
||||
? html`<span
|
||||
class="text-xs text-gray-400"
|
||||
>(fixed)</span
|
||||
>`
|
||||
: ""}
|
||||
</span>
|
||||
<div class="flex gap-1">
|
||||
${!isFixed
|
||||
? html`
|
||||
${index > 1 &&
|
||||
this.customLayers[index - 1]
|
||||
.name !== "full"
|
||||
? html`
|
||||
<button
|
||||
@click=${() =>
|
||||
this.moveLayerUp(index)}
|
||||
title="Move Up"
|
||||
class="text-sm px-2 py-1 bg-gray-600 rounded hover:bg-gray-500"
|
||||
>
|
||||
↓
|
||||
</button>
|
||||
`
|
||||
: ""}
|
||||
${index <
|
||||
this.customLayers.length - 2 &&
|
||||
this.customLayers[index + 1]
|
||||
.name !== "frame"
|
||||
? html`
|
||||
<button
|
||||
@click=${() =>
|
||||
this.moveLayerDown(
|
||||
index,
|
||||
)}
|
||||
title="Move Down"
|
||||
class="text-sm px-2 py-1 bg-gray-600 rounded hover:bg-gray-500"
|
||||
>
|
||||
↑
|
||||
</button>
|
||||
`
|
||||
: ""}
|
||||
<button
|
||||
@click=${() =>
|
||||
this.removeLayer(index)}
|
||||
class="text-red-400 hover:text-red-600"
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
`
|
||||
: null}
|
||||
<button
|
||||
@click=${() =>
|
||||
this.toggleColorPicker(index)}
|
||||
title="Change Color"
|
||||
class="text-sm px-2 py-1 bg-blue-600 rounded hover:bg-blue-500 text-white"
|
||||
>
|
||||
Color
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${this.openColorIndex === index
|
||||
? html`
|
||||
<div
|
||||
class="flex flex-wrap gap-1 justify-start mt-1"
|
||||
>
|
||||
${this.colorOptions.map((color) => {
|
||||
const isLocked =
|
||||
this.lockedColors.includes(
|
||||
color,
|
||||
);
|
||||
const isSpecial =
|
||||
!color.startsWith("#");
|
||||
const colorClass = isSpecial
|
||||
? `flag-color-${color}`
|
||||
: "";
|
||||
const inlineStyle = isSpecial
|
||||
? ""
|
||||
: `background-color: ${color};`;
|
||||
|
||||
return html`
|
||||
<button
|
||||
class="w-3 h-3 rounded-full border-2 relative
|
||||
${this.selectedColor === color
|
||||
? "border-white"
|
||||
: "border-gray-400"}
|
||||
${isLocked ? "opacity-40 cursor-not-allowed" : ""}
|
||||
${colorClass}"
|
||||
style=${inlineStyle}
|
||||
@click=${() => {
|
||||
if (
|
||||
!isLocked &&
|
||||
this.openColorIndex !==
|
||||
null
|
||||
) {
|
||||
this.updateLayerColor(
|
||||
this.openColorIndex,
|
||||
color,
|
||||
);
|
||||
}
|
||||
}}
|
||||
@mouseenter=${(
|
||||
e: MouseEvent,
|
||||
) => {
|
||||
if (isLocked) {
|
||||
this.hoveredColor = color;
|
||||
}
|
||||
}}
|
||||
@mousemove=${(
|
||||
e: MouseEvent,
|
||||
) => {
|
||||
if (isLocked) {
|
||||
this.hoverPosition = {
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
};
|
||||
}
|
||||
}}
|
||||
@mouseleave=${() => {
|
||||
this.hoveredColor = null;
|
||||
}}
|
||||
title=${color}
|
||||
>
|
||||
${isLocked
|
||||
? html`<img
|
||||
src=${locked}
|
||||
alt="Locked"
|
||||
class="absolute top-1/2 left-1/2 w-5 h-5 -translate-x-1/2 -translate-y-1/2 pointer-events-none"
|
||||
/>`
|
||||
: ""}
|
||||
</button>
|
||||
`;
|
||||
})}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</li>
|
||||
`;
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
${this.hoveredColor && this.lockedReasons[this.hoveredColor]
|
||||
? html`
|
||||
<div
|
||||
class="fixed z-50 px-3 py-2 rounded bg-black text-white text-sm pointer-events-none shadow-md"
|
||||
style="top: ${this.hoverPosition.y + 12}px; left: ${this
|
||||
.hoverPosition.x + 12}px;"
|
||||
>
|
||||
${this.lockedReasons[this.hoveredColor]}
|
||||
</div>
|
||||
`
|
||||
: null}
|
||||
`;
|
||||
}
|
||||
|
||||
private encodeCustomFlag(): string {
|
||||
return (
|
||||
"ctmfg" +
|
||||
this.customLayers
|
||||
.map(({ name, color }) => {
|
||||
const shortName = LayerShortNames[name] || name;
|
||||
const shortColor = ColorShortNames[color] || color.replace("#", "");
|
||||
return `${shortName}-${shortColor}`;
|
||||
})
|
||||
.join("_")
|
||||
);
|
||||
}
|
||||
|
||||
private isCustomFlag(flag: string): boolean {
|
||||
return flag.startsWith("ctmfg");
|
||||
}
|
||||
@@ -1129,6 +535,17 @@ export class FlagInput extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
const animationDurations: Record<string, number> = {
|
||||
rainbow: 4000,
|
||||
"bright-rainbow": 4000,
|
||||
"copper-glow": 3000,
|
||||
"silver-glow": 3000,
|
||||
"gold-glow": 3000,
|
||||
neon: 3000,
|
||||
glitch: 600,
|
||||
water: 6200,
|
||||
};
|
||||
|
||||
export function renderPlayerFlag(flagCode: string, target: HTMLElement) {
|
||||
const reverseNameMap = Object.fromEntries(
|
||||
Object.entries(LayerShortNames).map(([k, v]) => [v, k]),
|
||||
@@ -1167,7 +584,12 @@ export function renderPlayerFlag(flagCode: string, target: HTMLElement) {
|
||||
const isSpecial = !color.startsWith("#");
|
||||
|
||||
if (isSpecial) {
|
||||
const duration = animationDurations[color] ?? 5000;
|
||||
const now = performance.now();
|
||||
const offset = now % duration;
|
||||
if (!duration) console.warn(`No animation duration for: ${color}`);
|
||||
layer.classList.add(`flag-color-${color}`);
|
||||
layer.style.animationDelay = `-${offset}ms`;
|
||||
} else {
|
||||
layer.style.backgroundColor = color;
|
||||
}
|
||||
@@ -1185,3 +607,41 @@ export function renderPlayerFlag(flagCode: string, target: HTMLElement) {
|
||||
target.appendChild(layer);
|
||||
}
|
||||
}
|
||||
|
||||
export function analyzePlayerFlag(flagCode: string): {
|
||||
colors: string[];
|
||||
layers: string[];
|
||||
count: number;
|
||||
} {
|
||||
if (!flagCode.startsWith("ctmfg"))
|
||||
return { colors: [], layers: [], count: 0 };
|
||||
|
||||
const reverseNameMap = Object.fromEntries(
|
||||
Object.entries(LayerShortNames).map(([k, v]) => [v, k]),
|
||||
);
|
||||
|
||||
const reverseColorMap = Object.fromEntries(
|
||||
Object.entries(ColorShortNames).map(([k, v]) => [v, k]),
|
||||
);
|
||||
|
||||
const code = flagCode.replace("ctmfg", "");
|
||||
const segments = code.split("_");
|
||||
|
||||
const layers: string[] = [];
|
||||
const colors: string[] = [];
|
||||
|
||||
for (const segment of segments) {
|
||||
const [shortName, shortColor] = segment.split("-");
|
||||
const name = reverseNameMap[shortName] || shortName;
|
||||
const color = reverseColorMap[shortColor] || shortColor;
|
||||
|
||||
layers.push(name);
|
||||
colors.push(color);
|
||||
}
|
||||
|
||||
return {
|
||||
colors,
|
||||
layers,
|
||||
count: layers.length,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,680 @@
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { customElement, query, state } from "lit/decorators.js";
|
||||
import { translateText } from "../client/Utils";
|
||||
import Countries from "./data/countries.json";
|
||||
import {
|
||||
ColorShortNames,
|
||||
FlagMap,
|
||||
LayerShortNames,
|
||||
MAX_LAYER,
|
||||
checkPermission,
|
||||
} from "./FlagInput";
|
||||
|
||||
import { FlagInput } from "./FlagInput";
|
||||
|
||||
import disabled from "../../resources/images/DisabledIcon.svg";
|
||||
import locked from "../../resources/images/Locked.svg";
|
||||
|
||||
const flagKey: string = "flag";
|
||||
|
||||
@customElement("flag-input-modal")
|
||||
export class FlagInputModal extends LitElement {
|
||||
@query("o-modal") private modalEl!: HTMLElement & {
|
||||
open: () => void;
|
||||
close: () => void;
|
||||
};
|
||||
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@state() private flag: string = "";
|
||||
@state() private search: string = "";
|
||||
@state() private showModal: boolean = false;
|
||||
@state() private activeTab: "real" | "custom" = "real";
|
||||
@state() private selectedColor: string = "#ff0000";
|
||||
@state() private openColorIndex: number | null = null;
|
||||
|
||||
private readonly colorOptions: string[] = Object.keys(ColorShortNames);
|
||||
|
||||
@state() private customLayers: { name: string; color: string }[] = [];
|
||||
|
||||
@state() private hoveredColor: string | null = null;
|
||||
@state() private hoverPosition = { x: 0, y: 0 };
|
||||
@state() private hoverReason: string | null = null;
|
||||
|
||||
private addLayer(name: string) {
|
||||
const totalLayers = this.customLayers.length;
|
||||
|
||||
if (totalLayers >= MAX_LAYER) {
|
||||
alert(`You can only add up to ${MAX_LAYER} layers.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const newLayer = { name, color: this.selectedColor };
|
||||
this.customLayers = [
|
||||
this.customLayers[0],
|
||||
newLayer,
|
||||
...this.customLayers.slice(1),
|
||||
];
|
||||
}
|
||||
|
||||
private removeLayer(index: number) {
|
||||
this.customLayers = this.customLayers.filter((_, i) => i !== index);
|
||||
}
|
||||
|
||||
private moveLayerUp(index: number) {
|
||||
if (index === 0) return;
|
||||
const newLayers = [...this.customLayers];
|
||||
[newLayers[index - 1], newLayers[index]] = [
|
||||
newLayers[index],
|
||||
newLayers[index - 1],
|
||||
];
|
||||
this.customLayers = newLayers;
|
||||
}
|
||||
|
||||
private moveLayerDown(index: number) {
|
||||
if (index === this.customLayers.length - 1) return;
|
||||
const newLayers = [...this.customLayers];
|
||||
[newLayers[index], newLayers[index + 1]] = [
|
||||
newLayers[index + 1],
|
||||
newLayers[index],
|
||||
];
|
||||
this.customLayers = newLayers;
|
||||
}
|
||||
|
||||
private updateLayerColor(index: number, color: string) {
|
||||
const newLayers = [...this.customLayers];
|
||||
newLayers[index].color = color;
|
||||
this.customLayers = newLayers;
|
||||
}
|
||||
|
||||
private toggleColorPicker(index: number) {
|
||||
this.openColorIndex = this.openColorIndex === index ? null : index;
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
@media (max-width: 768px) {
|
||||
.flag-modal {
|
||||
width: 80vw;
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
width: calc(100% / 3 - 15px);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
private handleSearch(e: Event) {
|
||||
this.search = String((e.target as HTMLInputElement).value);
|
||||
}
|
||||
|
||||
private setFlag(flag: string) {
|
||||
if (flag == "xx") {
|
||||
flag = "";
|
||||
}
|
||||
this.flag = flag;
|
||||
this.showModal = false;
|
||||
this.storeFlag(flag);
|
||||
this.close();
|
||||
const el = document.querySelector("flag-input") as FlagInput;
|
||||
el.flag = this.flag;
|
||||
el.requestUpdate();
|
||||
}
|
||||
|
||||
public getCurrentFlag(): string {
|
||||
return this.flag;
|
||||
}
|
||||
|
||||
private getStoredFlag(): string {
|
||||
const storedFlag = localStorage.getItem(flagKey);
|
||||
if (storedFlag) {
|
||||
return storedFlag;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private storeFlag(flag: string) {
|
||||
if (flag) {
|
||||
localStorage.setItem(flagKey, flag);
|
||||
} else if (flag === "") {
|
||||
localStorage.removeItem(flagKey);
|
||||
}
|
||||
}
|
||||
|
||||
private dispatchFlagEvent() {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("flag-change", {
|
||||
detail: { flag: this.flag },
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.flag = this.getStoredFlag();
|
||||
this.dispatchFlagEvent();
|
||||
|
||||
if (this.isCustomFlag(this.flag)) {
|
||||
this.customLayers = this.decodeCustomFlag(this.flag);
|
||||
} else {
|
||||
if (this.customLayers.length === 0) {
|
||||
this.customLayers = [
|
||||
{ name: "full", color: "#ffffff" },
|
||||
{ name: "frame", color: "#000000" },
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private lockedLayers: string[] = [];
|
||||
|
||||
private lockedColors: string[] = [];
|
||||
|
||||
private lockedReasons: Record<string, string> = {};
|
||||
|
||||
private encodeCustomFlag(): string {
|
||||
return (
|
||||
"ctmfg" +
|
||||
this.customLayers
|
||||
.map(({ name, color }) => {
|
||||
const shortName = LayerShortNames[name] || name;
|
||||
const shortColor = ColorShortNames[color] || color.replace("#", "");
|
||||
return `${shortName}-${shortColor}`;
|
||||
})
|
||||
.join("_")
|
||||
);
|
||||
}
|
||||
|
||||
private isCustomFlag(flag: string): boolean {
|
||||
return flag.startsWith("ctmfg");
|
||||
}
|
||||
|
||||
private decodeCustomFlag(code: string): { name: string; color: string }[] {
|
||||
if (!this.isCustomFlag(code)) return [];
|
||||
|
||||
const short = code.replace("ctmfg", "");
|
||||
const reverseNameMap = Object.fromEntries(
|
||||
Object.entries(LayerShortNames).map(([k, v]) => [v, k]),
|
||||
);
|
||||
const reverseColorMap = Object.fromEntries(
|
||||
Object.entries(ColorShortNames).map(([k, v]) => [v, k]),
|
||||
);
|
||||
|
||||
return short.split("_").map((segment) => {
|
||||
const [shortName, shortColor] = segment.split("-");
|
||||
const name = reverseNameMap[shortName] || shortName;
|
||||
const color = reverseColorMap[shortColor] || `#${shortColor}`;
|
||||
return { name, color };
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const result = checkPermission();
|
||||
this.lockedLayers = Array.isArray(result[0]) ? result[0] : [result[0]];
|
||||
this.lockedColors = Array.isArray(result[1]) ? result[1] : [result[1]];
|
||||
this.lockedReasons = result[2] || {};
|
||||
return html`
|
||||
${this.hoveredColor && this.lockedReasons[this.hoveredColor]
|
||||
? html`
|
||||
<div
|
||||
class="fixed z-[9999] px-3 py-2 rounded bg-black text-white text-sm pointer-events-none shadow-md"
|
||||
style="top: ${this.hoverPosition.y + 12}px; left: ${this
|
||||
.hoverPosition.x + 12}px;"
|
||||
>
|
||||
${this.lockedReasons[this.hoveredColor]}
|
||||
</div>
|
||||
`
|
||||
: null}
|
||||
<o-modal
|
||||
id="flaginputModal"
|
||||
title="Flag Input"
|
||||
translationKey="flag_input.title"
|
||||
>
|
||||
<!-- tab -->
|
||||
<div class="flex gap-2 mb-2">
|
||||
<button
|
||||
class="px-4 py-1 rounded-lg font-bold ${this.activeTab === "real"
|
||||
? "bg-blue-500 text-white"
|
||||
: "bg-gray-300 text-black"}"
|
||||
@click=${() => (this.activeTab = "real")}
|
||||
>
|
||||
${translateText(`flag_input.real`)}
|
||||
</button>
|
||||
<button
|
||||
class="px-4 py-1 rounded-lg font-bold ${this.activeTab === "custom"
|
||||
? "bg-blue-500 text-white"
|
||||
: "bg-gray-300 text-black"}"
|
||||
@click=${() => (this.activeTab = "custom")}
|
||||
>
|
||||
${translateText(`flag_input.custom`)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
${this.activeTab === "real"
|
||||
? html`
|
||||
<input
|
||||
class="h-[2rem] border-none text-center border border-gray-300 rounded-xl shadow-sm text-2xl text-center focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-black dark:border-gray-300/60 dark:bg-gray-700 dark:text-white"
|
||||
type="text"
|
||||
placeholder="Search..."
|
||||
@change=${this.handleSearch}
|
||||
@keyup=${this.handleSearch}
|
||||
/>
|
||||
<div
|
||||
class="flex flex-wrap justify-evenly gap-[1rem] overflow-y-auto overflow-x-hidden"
|
||||
>
|
||||
${Countries.filter(
|
||||
(country) =>
|
||||
country.name
|
||||
.toLowerCase()
|
||||
.includes(this.search.toLowerCase()) ||
|
||||
country.code
|
||||
.toLowerCase()
|
||||
.includes(this.search.toLowerCase()),
|
||||
).map(
|
||||
(country) => html`
|
||||
<button
|
||||
@click=${() => this.setFlag(country.code)}
|
||||
class="text-center cursor-pointer border-none bg-none opacity-70
|
||||
w-[calc(100%/2-15px)] sm:w-[calc(100%/3-15px)]
|
||||
md:w-[calc(100%/4-15px)] lg:w-[calc(100%/5-15px)]
|
||||
xl:w-[calc(100%/6-15px)]"
|
||||
>
|
||||
<img
|
||||
class="country-flag w-full h-auto"
|
||||
src="/flags/${country.code}.svg"
|
||||
/>
|
||||
<span class="country-name">${country.name}</span>
|
||||
</button>
|
||||
`,
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: html`
|
||||
<div class="grid grid-cols-[1fr_2fr] gap-4 p-2 h-full">
|
||||
<!-- left -->
|
||||
<div
|
||||
class="flex flex-col items-center gap-2 overflow-y-auto max-h-[calc(50vh-4rem)]"
|
||||
>
|
||||
<div class="flex flex-wrap gap-1 mb-2 justify-center">
|
||||
${this.colorOptions.map((color) => {
|
||||
const isLocked = this.lockedColors.includes(color);
|
||||
const isSpecial = !color.startsWith("#");
|
||||
const colorClass = isSpecial ? `flag-color-${color}` : "";
|
||||
const inlineStyle = isSpecial
|
||||
? ""
|
||||
: `background-color: ${color};`;
|
||||
const isSelected = this.selectedColor === color;
|
||||
return html`
|
||||
<button
|
||||
class="w-4 h-4 rounded-full border-2 relative
|
||||
${isSelected ? "border-white" : "border-gray-400"}
|
||||
${isLocked ? "opacity-40 cursor-not-allowed" : ""}
|
||||
${colorClass}"
|
||||
style=${inlineStyle}
|
||||
@click=${() =>
|
||||
!isLocked && (this.selectedColor = color)}
|
||||
@mouseenter=${(e: MouseEvent) => {
|
||||
if (isLocked) {
|
||||
this.hoveredColor = color;
|
||||
}
|
||||
}}
|
||||
@mousemove=${(e: MouseEvent) => {
|
||||
if (isLocked) {
|
||||
this.hoverPosition = {
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
};
|
||||
}
|
||||
}}
|
||||
@mouseleave=${() => {
|
||||
this.hoveredColor = null;
|
||||
}}
|
||||
title=${color}
|
||||
>
|
||||
${isLocked
|
||||
? html`<img
|
||||
src=${locked}
|
||||
alt="Locked"
|
||||
class="absolute top-1/2 left-1/2 w-5 h-5 -translate-x-1/2 -translate-y-1/2 pointer-events-none"
|
||||
/>`
|
||||
: ""}
|
||||
</button>
|
||||
`;
|
||||
})}
|
||||
</div>
|
||||
|
||||
<p class="text-lg font-bold text-white self-start">
|
||||
${translateText(`flag_input.select_layer`)}
|
||||
</p>
|
||||
|
||||
${this.customLayers.length >= MAX_LAYER
|
||||
? html`
|
||||
<p class="text-sm text-red-400 self-start -mt-1 mb-2">
|
||||
${translateText("flag_input.max_layer_reached_1")}<br />
|
||||
${translateText("flag_input.max_layer_reached_2")}
|
||||
</p>
|
||||
`
|
||||
: null}
|
||||
|
||||
<div
|
||||
class="grid grid-cols-2 gap-2 w-full max-h-[300px] overflow-y-auto pr-1 mb-2"
|
||||
>
|
||||
${Object.entries(FlagMap)
|
||||
.filter(([name]) => name !== "frame" && name !== "full")
|
||||
.map(([name, src]) => {
|
||||
const isLocked = this.lockedLayers.includes(name);
|
||||
const isDisabled =
|
||||
!isLocked && this.customLayers.length >= MAX_LAYER;
|
||||
|
||||
const isSpecial = !this.selectedColor.startsWith("#");
|
||||
const colorClass = isSpecial
|
||||
? `flag-color-${this.selectedColor}`
|
||||
: "";
|
||||
const colorStyle = isSpecial
|
||||
? ""
|
||||
: `background-color: ${this.selectedColor};`;
|
||||
|
||||
const reason = isLocked
|
||||
? this.lockedReasons[name] || "Locked"
|
||||
: isDisabled
|
||||
? `You can only add up to ${MAX_LAYER} layers.`
|
||||
: "";
|
||||
|
||||
return html`
|
||||
<button
|
||||
@click=${() => {
|
||||
if (!isLocked && !isDisabled) this.addLayer(name);
|
||||
}}
|
||||
class="p-1 border border-gray-600 rounded-md transition relative
|
||||
${isLocked || isDisabled
|
||||
? "opacity-40 cursor-not-allowed"
|
||||
: "hover:border-white"}"
|
||||
?disabled=${isLocked || isDisabled}
|
||||
@mouseenter=${(e: MouseEvent) => {
|
||||
if (reason) {
|
||||
this.hoveredColor = name;
|
||||
this.hoverReason = reason;
|
||||
this.hoverPosition = {
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
};
|
||||
}
|
||||
}}
|
||||
@mousemove=${(e: MouseEvent) => {
|
||||
if (reason) {
|
||||
this.hoverPosition = {
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
};
|
||||
}
|
||||
}}
|
||||
@mouseleave=${() => {
|
||||
this.hoveredColor = null;
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class="w-full h-14 rounded relative"
|
||||
title=${name}
|
||||
>
|
||||
<!-- black frame background -->
|
||||
<div
|
||||
class="absolute inset-0 rounded"
|
||||
style="
|
||||
background-color: black;
|
||||
-webkit-mask: url(${FlagMap.frame}) center / contain no-repeat;
|
||||
mask: url(${FlagMap.frame}) center / contain no-repeat;
|
||||
"
|
||||
></div>
|
||||
|
||||
<!-- selected color mask -->
|
||||
<div
|
||||
class="absolute inset-0 rounded ${colorClass}"
|
||||
style="
|
||||
${colorStyle}
|
||||
-webkit-mask: url(${src}) center / contain no-repeat;
|
||||
mask: url(${src}) center / contain no-repeat;
|
||||
"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
${isLocked
|
||||
? html`<img
|
||||
src=${locked}
|
||||
alt="Locked"
|
||||
class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-10 h-10 opacity-60 drop-shadow-md pointer-events-none"
|
||||
/>`
|
||||
: isDisabled
|
||||
? html`<img
|
||||
src=${disabled}
|
||||
alt="Disabled"
|
||||
class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-10 h-10 opacity-60 drop-shadow-md pointer-events-none"
|
||||
/>`
|
||||
: null}
|
||||
</button>
|
||||
`;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- right -->
|
||||
<div
|
||||
class="flex flex-col items-center h-full overflow-hidden max-h-[calc(50vh-4rem)]"
|
||||
>
|
||||
<p class="text-lg font-bold text-white mb-2">
|
||||
${translateText(`flag_input.preview`)}
|
||||
</p>
|
||||
<div
|
||||
class="relative w-[160px] h-[100px] min-w-[160px] min-h-[100px] max-w-[160px] max-h-[100px] flex-shrink-0 border border-gray-400 rounded bg-white"
|
||||
>
|
||||
${this.customLayers.map(({ name, color }) => {
|
||||
const src = FlagMap[name];
|
||||
if (!src) return null;
|
||||
console.log("color", color);
|
||||
const isSpecial = !color.startsWith("#");
|
||||
const colorClass = isSpecial ? `flag-color-${color}` : "";
|
||||
const bgStyle = isSpecial
|
||||
? ""
|
||||
: `background-color: ${color};`;
|
||||
|
||||
return html`
|
||||
<div
|
||||
class="absolute top-0 left-0 w-full h-full ${colorClass}"
|
||||
style="
|
||||
${bgStyle}
|
||||
-webkit-mask: url(${src}) center / contain no-repeat;
|
||||
mask: url(${src}) center / contain no-repeat;
|
||||
"
|
||||
></div>
|
||||
`;
|
||||
})}
|
||||
</div>
|
||||
|
||||
<button
|
||||
@click=${() => {
|
||||
const code = this.encodeCustomFlag();
|
||||
navigator.clipboard.writeText(code);
|
||||
console.log("Applied: " + code);
|
||||
this.setFlag(code);
|
||||
}}
|
||||
class="mt-2 px-4 py-1 bg-green-600 text-white rounded hover:bg-green-500"
|
||||
>
|
||||
${translateText("flag_input.apply")}
|
||||
</button>
|
||||
|
||||
<div class="mt-4 w-full max-h-[300px] overflow-y-auto mb-2">
|
||||
<p class="text-lg font-bold text-white mb-2">
|
||||
${translateText("flag_input.layer")}
|
||||
(${this.customLayers.length})
|
||||
</p>
|
||||
<ul class="text-white space-y-1">
|
||||
${this.customLayers.map((_, i, arr) => {
|
||||
const index = arr.length - 1 - i;
|
||||
const { name, color } = arr[index];
|
||||
const isFixed = name === "full" || name === "frame";
|
||||
|
||||
const isSpecial = !color.startsWith("#");
|
||||
const colorClass = isSpecial
|
||||
? `flag-color-${color}`
|
||||
: "";
|
||||
const inlineStyle = isSpecial
|
||||
? ""
|
||||
: `background-color: ${color};`;
|
||||
|
||||
return html`
|
||||
<li
|
||||
class="flex flex-col gap-1 py-1 px-2 bg-gray-800 rounded"
|
||||
>
|
||||
<div
|
||||
class="flex flex-wrap justify-between items-center"
|
||||
>
|
||||
<span class="flex items-center gap-2">
|
||||
<span
|
||||
class="inline-block w-4 h-4 rounded-full ${colorClass}"
|
||||
style=${inlineStyle}
|
||||
></span>
|
||||
${translateText(`flag_input.layers.${name}`)}
|
||||
${isFixed
|
||||
? html`<span class="text-xs text-gray-400"
|
||||
>(${translateText(
|
||||
"flag_input.fixed",
|
||||
)})</span
|
||||
>`
|
||||
: ""}
|
||||
</span>
|
||||
<div class="flex gap-1">
|
||||
${!isFixed
|
||||
? html`
|
||||
${index > 1 &&
|
||||
this.customLayers[index - 1].name !==
|
||||
"full"
|
||||
? html`
|
||||
<button
|
||||
@click=${() =>
|
||||
this.moveLayerUp(index)}
|
||||
title="Move Up"
|
||||
class="text-sm px-2 py-1 bg-gray-600 rounded hover:bg-gray-500"
|
||||
>
|
||||
↓
|
||||
</button>
|
||||
`
|
||||
: ""}
|
||||
${index < this.customLayers.length - 2 &&
|
||||
this.customLayers[index + 1].name !==
|
||||
"frame"
|
||||
? html`
|
||||
<button
|
||||
@click=${() =>
|
||||
this.moveLayerDown(index)}
|
||||
title="Move Down"
|
||||
class="text-sm px-2 py-1 bg-gray-600 rounded hover:bg-gray-500"
|
||||
>
|
||||
↑
|
||||
</button>
|
||||
`
|
||||
: ""}
|
||||
<button
|
||||
@click=${() => this.removeLayer(index)}
|
||||
class="text-red-400 hover:text-red-600"
|
||||
>
|
||||
${translateText("flag_input.remove")}
|
||||
</button>
|
||||
`
|
||||
: null}
|
||||
<button
|
||||
@click=${() => this.toggleColorPicker(index)}
|
||||
title="Change Color"
|
||||
class="text-sm px-2 py-1 bg-blue-600 rounded hover:bg-blue-500 text-white"
|
||||
>
|
||||
${translateText("flag_input.color")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${this.openColorIndex === index
|
||||
? html`
|
||||
<div
|
||||
class="flex flex-wrap gap-1 justify-start mt-1"
|
||||
>
|
||||
${this.colorOptions.map((color) => {
|
||||
const isLocked =
|
||||
this.lockedColors.includes(color);
|
||||
const isSpecial = !color.startsWith("#");
|
||||
const colorClass = isSpecial
|
||||
? `flag-color-${color}`
|
||||
: "";
|
||||
const inlineStyle = isSpecial
|
||||
? ""
|
||||
: `background-color: ${color};`;
|
||||
|
||||
return html`
|
||||
<button
|
||||
class="w-3 h-3 rounded-full border-2 relative
|
||||
${this.selectedColor === color ? "border-white" : "border-gray-400"}
|
||||
${isLocked ? "opacity-40 cursor-not-allowed" : ""}
|
||||
${colorClass}"
|
||||
style=${inlineStyle}
|
||||
@click=${() => {
|
||||
if (
|
||||
!isLocked &&
|
||||
this.openColorIndex !== null
|
||||
) {
|
||||
this.updateLayerColor(
|
||||
this.openColorIndex,
|
||||
color,
|
||||
);
|
||||
}
|
||||
}}
|
||||
@mouseenter=${(e: MouseEvent) => {
|
||||
if (isLocked) {
|
||||
this.hoveredColor = color;
|
||||
}
|
||||
}}
|
||||
@mousemove=${(e: MouseEvent) => {
|
||||
if (isLocked) {
|
||||
this.hoverPosition = {
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
};
|
||||
}
|
||||
}}
|
||||
@mouseleave=${() => {
|
||||
this.hoveredColor = null;
|
||||
}}
|
||||
title=${color}
|
||||
>
|
||||
${isLocked
|
||||
? html`<img
|
||||
src=${locked}
|
||||
alt="Locked"
|
||||
class="absolute top-1/2 left-1/2 w-5 h-5 -translate-x-1/2 -translate-y-1/2 pointer-events-none"
|
||||
/>`
|
||||
: ""}
|
||||
</button>
|
||||
`;
|
||||
})}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</li>
|
||||
`;
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`}
|
||||
</o-modal>
|
||||
`;
|
||||
}
|
||||
|
||||
public open() {
|
||||
this.modalEl?.open();
|
||||
}
|
||||
|
||||
public close() {
|
||||
this.modalEl?.close();
|
||||
}
|
||||
}
|
||||
@@ -173,6 +173,7 @@ export class LangSelector extends LitElement {
|
||||
"help-modal",
|
||||
"username-input",
|
||||
"public-lobby",
|
||||
"flag-input-modal",
|
||||
"o-modal",
|
||||
"o-button",
|
||||
];
|
||||
|
||||
+32
-5
@@ -9,7 +9,8 @@ import { joinLobby } from "./ClientGameRunner";
|
||||
import "./DarkModeButton";
|
||||
import { DarkModeButton } from "./DarkModeButton";
|
||||
import "./FlagInput";
|
||||
import { FlagInput } from "./FlagInput";
|
||||
import { FlagInput, analyzePlayerFlag, checkPermission } from "./FlagInput";
|
||||
import { FlagInputModal } from "./FlagInputModal";
|
||||
import { GameStartingModal } from "./GameStartingModal";
|
||||
import "./GoogleAdElement";
|
||||
import GoogleAdElement from "./GoogleAdElement";
|
||||
@@ -119,6 +120,14 @@ class Client {
|
||||
hlpModal.open();
|
||||
});
|
||||
|
||||
const flagInputModal = document.querySelector(
|
||||
"flag-input-modal",
|
||||
) as FlagInputModal;
|
||||
flagInputModal instanceof FlagInputModal;
|
||||
document.getElementById("flag-input_").addEventListener("click", () => {
|
||||
flagInputModal.open();
|
||||
});
|
||||
|
||||
const settingsModal = document.querySelector(
|
||||
"user-setting",
|
||||
) as UserSettingModal;
|
||||
@@ -194,14 +203,32 @@ class Client {
|
||||
}
|
||||
const config = await getServerConfigFromClient();
|
||||
|
||||
let rawFlag = this.flagInput.getCurrentFlag();
|
||||
if (rawFlag.startsWith("ctmfg")) {
|
||||
const result = checkPermission();
|
||||
const lockedLayers = Array.isArray(result[0]) ? result[0] : [result[0]];
|
||||
const lockedColors = Array.isArray(result[1]) ? result[1] : [result[1]];
|
||||
const MAX_LAYER = result[3];
|
||||
const flagInfo = analyzePlayerFlag(rawFlag);
|
||||
const hasLockedLayer = flagInfo.layers.some((layer) =>
|
||||
lockedLayers.includes(layer),
|
||||
);
|
||||
const hasLockedColor = flagInfo.colors.some((color) =>
|
||||
lockedColors.includes(color),
|
||||
);
|
||||
const isLayerCountExceeded = flagInfo.count > MAX_LAYER;
|
||||
if (hasLockedLayer || hasLockedColor || isLayerCountExceeded) {
|
||||
rawFlag = "xx";
|
||||
console.warn("Blocked custom flag code.");
|
||||
}
|
||||
}
|
||||
const flag = rawFlag === "xx" ? "" : rawFlag;
|
||||
|
||||
this.gameStop = joinLobby(
|
||||
{
|
||||
gameID: lobby.gameID,
|
||||
serverConfig: config,
|
||||
flag:
|
||||
this.flagInput.getCurrentFlag() == "xx"
|
||||
? ""
|
||||
: this.flagInput.getCurrentFlag(),
|
||||
flag: flag,
|
||||
playerName: this.usernameInput.getCurrentUsername(),
|
||||
persistentID: getPersistentIDFromCookie(),
|
||||
clientID: lobby.clientID,
|
||||
|
||||
@@ -349,6 +349,7 @@
|
||||
<dark-mode-button></dark-mode-button>
|
||||
<user-setting></user-setting>
|
||||
<multi-tab-modal></multi-tab-modal>
|
||||
<flag-input-modal></flag-input-modal>
|
||||
<div
|
||||
id="language-modal"
|
||||
class="fixed inset-0 bg-black bg-opacity-50 z-50 hidden flex justify-center items-center"
|
||||
|
||||
Reference in New Issue
Block a user