first commit
55
icons/back.svg
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="32"
|
||||||
|
height="32"
|
||||||
|
viewBox="0 0 32 32.000001"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||||
|
sodipodi:docname="back.svg"
|
||||||
|
xml:space="preserve"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"><defs
|
||||||
|
id="defs2" /><sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#808080"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="7.919596"
|
||||||
|
inkscape:cx="10.669736"
|
||||||
|
inkscape:cy="13.131983"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
units="px"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1053"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:showpageshadow="0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#808080"
|
||||||
|
showguides="true" /><metadata
|
||||||
|
id="metadata5"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-1090.5197)"><path
|
||||||
|
style="fill:none;stroke:#ffffff;stroke-width:3.199;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 15.92301,1097.1929 -9.2381147,9.2381 9.4155007,9.4155"
|
||||||
|
id="path11921" /><path
|
||||||
|
style="fill:none;stroke:#ffffff;stroke-width:3.199;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 25.315019,1106.6491 H 7.0812466"
|
||||||
|
id="path11925" /></g></svg>
|
After Width: | Height: | Size: 2.1 KiB |
72
icons/copy.svg
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="32"
|
||||||
|
height="32"
|
||||||
|
viewBox="0 0 32 32.000001"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||||
|
sodipodi:docname="copy.svg">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#808080"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="11.2"
|
||||||
|
inkscape:cx="18.306958"
|
||||||
|
inkscape:cy="14.832193"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
units="px"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1027"
|
||||||
|
inkscape:window-x="-8"
|
||||||
|
inkscape:window-y="22"
|
||||||
|
inkscape:window-maximized="1" />
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-1090.5197)">
|
||||||
|
<rect
|
||||||
|
style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1.706;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal"
|
||||||
|
id="rect1373"
|
||||||
|
width="15.616629"
|
||||||
|
height="15.616629"
|
||||||
|
x="3.8530145"
|
||||||
|
y="1094.2299" />
|
||||||
|
<rect
|
||||||
|
y="1103.1393"
|
||||||
|
x="12.762503"
|
||||||
|
height="15.616629"
|
||||||
|
width="15.616629"
|
||||||
|
id="rect1375"
|
||||||
|
style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1.706;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
117
icons/salish-keyboard-icon.svg
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="192"
|
||||||
|
height="192"
|
||||||
|
viewBox="0 0 192 192.00001"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
sodipodi:docname="salish-keyboard-icon.svg"
|
||||||
|
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||||
|
inkscape:export-filename="C:\dofdev\Salish Keyboard\salish-keyboard-icon.png"
|
||||||
|
inkscape:export-xdpi="256"
|
||||||
|
inkscape:export-ydpi="256">
|
||||||
|
<defs
|
||||||
|
id="defs2">
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="linearGradient1965">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#1c161b;stop-opacity:0.5"
|
||||||
|
offset="0"
|
||||||
|
id="stop1961" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#1c161b;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop1963" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient1965"
|
||||||
|
id="linearGradient1971"
|
||||||
|
x1="92.405533"
|
||||||
|
y1="1010.9304"
|
||||||
|
x2="147.82085"
|
||||||
|
y2="1062.9532"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#000000"
|
||||||
|
borderopacity="0.56078431"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="1.979899"
|
||||||
|
inkscape:cx="66.492719"
|
||||||
|
inkscape:cy="101.6195"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:snap-global="false"
|
||||||
|
showguides="true"
|
||||||
|
inkscape:guide-bbox="true"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1027"
|
||||||
|
inkscape:window-x="-8"
|
||||||
|
inkscape:window-y="22"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
units="px"
|
||||||
|
inkscape:showpageshadow="false"
|
||||||
|
inkscape:pagecheckerboard="false"
|
||||||
|
inkscape:snap-bbox="true"
|
||||||
|
inkscape:snap-page="true"
|
||||||
|
borderlayer="true">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid832" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-930.51965)">
|
||||||
|
<path
|
||||||
|
style="fill:#1c161b;fill-opacity:1;stroke:none;stroke-width:3.14762044;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 31.950295,959.96696 c 43.876553,8.69094 87.154865,9.20369 127.509105,-0.32554 7.89365,-1.77643 8.66265,2.45107 9.76665,8.1389 l 22.34605,103.84488 c 0,0 0.10261,8.0315 -0.26872,13.0593 -10.89084,1.4332 -181.055533,1.5621 -190.78290754,0 -0.24551432,-4.7122 0.0141647,-12.1647 0.0141647,-12.1669 L 23.378019,968.11152 c 1.011881,-10.09604 4.723823,-9.39183 8.572937,-8.13889 z"
|
||||||
|
id="path4518"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cccccccccc"
|
||||||
|
inkscape:export-filename="C:\dofdev\Web Development\dofdev\v2\dof-covers\salishkeyboard.png"
|
||||||
|
inkscape:export-xdpi="256"
|
||||||
|
inkscape:export-ydpi="256" />
|
||||||
|
<path
|
||||||
|
style="fill:#f2e2c8;fill-opacity:1;stroke-width:1.14920998"
|
||||||
|
d="m 97.615778,1000.7522 c -2.570331,-0.022 -5.195887,0.1495 -7.893649,0.5072 -9.29379,1.2326 -14.97792,3.6691 -24.031342,10.3096 -5.486743,4.0244 -11.152758,6.6213 -22.386836,10.2575 -17.945921,5.8086 -20.58188,7.9444 -9.276871,7.5126 2.856277,-0.1087 8.77396,0.1305 13.149924,0.5316 7.344547,0.6734 8.801895,1.2072 18.984084,6.95 12.832991,7.2378 20.042316,9.3802 31.135002,9.2523 10.44938,-0.1195 20.60068,-3.5352 29.13701,-9.7963 8.76773,-6.4311 15.83965,-8.4179 29.95773,-8.4224 17.3159,0 15.20394,-2.2798 -7.30346,-7.8568 -12.54179,-3.1076 -14.65166,-3.932 -19.83861,-7.7645 -10.39837,-7.683 -20.49494,-11.386 -31.632982,-11.4808 z"
|
||||||
|
id="path892"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="ccccccscscssc" />
|
||||||
|
<path
|
||||||
|
style="fill:#5a2927;stroke-width:1.16148973"
|
||||||
|
d="m 97.291872,1007.5482 c -18.181882,0.026 -35.947996,10.1211 -29.106612,21.1816 1.890807,3.0566 3.608117,4.2497 9.820261,6.8159 4.141025,1.7107 10.708268,3.3939 14.592148,3.7405 11.821361,1.0551 28.091391,-5.1275 31.414891,-11.9358 2.75289,-5.6394 -0.94597,-12.0604 -9.29523,-16.1363 -5.25722,-2.5663 -11.36484,-3.6745 -17.425458,-3.6659 z"
|
||||||
|
id="path894"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="ccccssc" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 4.9 KiB |
BIN
icons/tap.png
Normal file
After Width: | Height: | Size: 4 KiB |
BIN
icons/tung.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
91
icons/tung.svg
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="32"
|
||||||
|
height="32"
|
||||||
|
viewBox="0 0 32 32.000001"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||||
|
sodipodi:docname="tongue.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#808080"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="11.2"
|
||||||
|
inkscape:cx="10.044643"
|
||||||
|
inkscape:cy="14.553571"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
units="px"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1053"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:showpageshadow="0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#808080"
|
||||||
|
showguides="true" />
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-1090.5197)">
|
||||||
|
<circle
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke-width:1.44342;stroke-dasharray:none"
|
||||||
|
id="path217"
|
||||||
|
cx="3.315028"
|
||||||
|
cy="1102.2913"
|
||||||
|
r="2.2093179" />
|
||||||
|
<circle
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke-width:1.44342;stroke-dasharray:none"
|
||||||
|
id="circle950"
|
||||||
|
cx="28.684973"
|
||||||
|
cy="1102.2913"
|
||||||
|
r="2.2093179" />
|
||||||
|
<circle
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke-width:1.44342;stroke-dasharray:none"
|
||||||
|
id="circle952"
|
||||||
|
cx="15.999997"
|
||||||
|
cy="1102.2913"
|
||||||
|
r="2.2093179" />
|
||||||
|
<circle
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke-width:1.44342;stroke-dasharray:none"
|
||||||
|
id="circle954"
|
||||||
|
cx="9.6575136"
|
||||||
|
cy="1111.1942"
|
||||||
|
r="2.2093179" />
|
||||||
|
<circle
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke-width:1.44342;stroke-dasharray:none"
|
||||||
|
id="circle956"
|
||||||
|
cx="22.342487"
|
||||||
|
cy="1111.1942"
|
||||||
|
r="2.2093179" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.6 KiB |
57
icons/tungs.svg
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="32"
|
||||||
|
height="32"
|
||||||
|
viewBox="0 0 32 32.000001"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||||
|
sodipodi:docname="tongues.svg"
|
||||||
|
xml:space="preserve"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"><defs
|
||||||
|
id="defs2" /><sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#808080"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="11.2"
|
||||||
|
inkscape:cx="24.419643"
|
||||||
|
inkscape:cy="14.553571"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
units="px"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1053"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:showpageshadow="0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#808080" /><metadata
|
||||||
|
id="metadata5"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-1090.5197)"><path
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2.55999;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 4.1490411,1098.2809 H 27.850958"
|
||||||
|
id="path1874" /><path
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2.55999;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 4.1490411,1106.5197 H 27.850958"
|
||||||
|
id="path1876" /><path
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2.55999;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 4.1490411,1114.7585 H 27.850958"
|
||||||
|
id="path1878" /></g></svg>
|
After Width: | Height: | Size: 2.2 KiB |
164
index.html
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html class="notranslate" translate="no">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="interactive-widget=resizes-content, width=device-width, initial-scale=1.0, user-scalable=no">
|
||||||
|
<meta name="google" content="notranslate" />
|
||||||
|
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="icons/tung.svg">
|
||||||
|
<link rel="icon" type="image/svg+xml" sizes="any" href="icons/tung.svg">
|
||||||
|
<link rel="manifest" href="manifest.webmanifest">
|
||||||
|
<meta name="description" content="Augment your keyboard with the symbols you need to type in your language.">
|
||||||
|
<meta name="author" content="Ethan Merchant">
|
||||||
|
<title style="display: none;">tung-tap</title>
|
||||||
|
<link rel="stylesheet" href="style.css?v1">
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||||
|
|
||||||
|
<script src="script.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- <div id="home" style="
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 10px 20px;
|
||||||
|
padding-top: 20px;
|
||||||
|
background-color: #79394f;
|
||||||
|
">
|
||||||
|
<div style="
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
">
|
||||||
|
<img src="icons/tongue.png" style="
|
||||||
|
width: 32px; height: 32px;
|
||||||
|
transform: translateY(-5px) rotate(-5deg);
|
||||||
|
">
|
||||||
|
<img src="icons/tap.png" style="
|
||||||
|
width: 32px; height: 32px;
|
||||||
|
margin-right: -40px;
|
||||||
|
transform: translate(-32px, 7px) rotate(-45deg) scale(0.8);
|
||||||
|
">
|
||||||
|
<h1 style="font-weight: unset; font-size: 24px; line-height: 24px; margin: 0; color: #fff">
|
||||||
|
TongueTap<span style="font-size: 14px; letter-spacing: 1px;">.app</span>
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
<p style="
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 24px;
|
||||||
|
font-style: italic;
|
||||||
|
margin: 0;
|
||||||
|
color: #fff;
|
||||||
|
">
|
||||||
|
Unicode key-swatch webapp for any cultures tongue!
|
||||||
|
</p>
|
||||||
|
</div> -->
|
||||||
|
|
||||||
|
<div style="display: block; position: relative; background-color: white;">
|
||||||
|
<div hidden="true" id="tung" onclick="layout()" oninput="layout()" contenteditable="true" spellcheck="false" style="
|
||||||
|
user-select: text;
|
||||||
|
touch-action: none;
|
||||||
|
overflow: none;
|
||||||
|
|
||||||
|
|
||||||
|
white-space: pre;
|
||||||
|
width: auto;
|
||||||
|
height: 100px;
|
||||||
|
padding: 10px;
|
||||||
|
font-family: 'DM Mono', monospace;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 20px;
|
||||||
|
background-color: #ff79a1;
|
||||||
|
color: #79394f;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* display: none; */
|
||||||
|
"></div>
|
||||||
|
<div onclick="copyToClipboard('#tung')" style="
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
position: absolute;
|
||||||
|
right: 10px;
|
||||||
|
bottom: 10px;
|
||||||
|
"><img class="icon-btn" src="icons/copy.svg" style="mix-blend-mode: difference;"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
width: 40px; height: 40px;
|
||||||
|
margin: 0 auto;
|
||||||
|
margin-right: 80px;
|
||||||
|
width: 100%;
|
||||||
|
/* background-color: #eb8bb3; */
|
||||||
|
background-color: #ff79a1c0;
|
||||||
|
color: white;
|
||||||
|
user-select: none;
|
||||||
|
line-height: 40px;
|
||||||
|
">
|
||||||
|
<img onclick="$('#nav').toggle()" class="icon-btn" src="icons/tungs.svg">
|
||||||
|
<img onclick="TopToggle()" class="icon-btn" src="icons/tung.svg">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div style="flex-grow: 1; overflow: hidden; position: relative; background-color: white;">
|
||||||
|
<div id="text" onclick="$('#text').focus()" contenteditable="true" spellcheck="false" style="
|
||||||
|
user-select: text;
|
||||||
|
touch-action: pan-y;
|
||||||
|
overflow-y: scroll;
|
||||||
|
overscroll-behavior: contain;
|
||||||
|
|
||||||
|
|
||||||
|
white-space: pre-wrap;
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: 0; left: 0; right: 0; bottom: 0;
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
background-color: rgb(var(--field));
|
||||||
|
color: rgb(var(--txt));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* padding-bottom: 600px; */
|
||||||
|
"></div>
|
||||||
|
<div onclick="copyToClipboard('#text')" style="
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
position: absolute;
|
||||||
|
right: 10px;
|
||||||
|
bottom: 10px;
|
||||||
|
">
|
||||||
|
<img class="icon-btn" src="icons/copy.svg" style="mix-blend-mode: difference;">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="nav" hidden="true"></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="keyboard" onclick="$('#text').focus()"></div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
TODO
|
||||||
|
migrate to textareas
|
||||||
|
contenteditable divs are a can of worms
|
||||||
|
esp. when it comes to conistent behaviour across devices
|
||||||
|
|
||||||
|
spellcheck toggle~
|
||||||
|
|
||||||
|
|
||||||
|
-->
|
138
inobounce.js
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
/*! iNoBounce - v0.2.0
|
||||||
|
* https://github.com/lazd/iNoBounce/
|
||||||
|
* Copyright (c) 2013 Larry Davis <lazdnet@gmail.com>; Licensed BSD */
|
||||||
|
(function(global) {
|
||||||
|
// Stores the Y position where the touch started
|
||||||
|
var startY = 0;
|
||||||
|
|
||||||
|
// Store enabled status
|
||||||
|
var enabled = false;
|
||||||
|
|
||||||
|
var supportsPassiveOption = false;
|
||||||
|
try {
|
||||||
|
var opts = Object.defineProperty({}, 'passive', {
|
||||||
|
get: function() {
|
||||||
|
supportsPassiveOption = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
window.addEventListener('test', null, opts);
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
var handleTouchmove = function(evt) {
|
||||||
|
// Get the element that was scrolled upon
|
||||||
|
var el = evt.target;
|
||||||
|
|
||||||
|
// Allow zooming
|
||||||
|
var zoom = window.innerWidth / window.document.documentElement.clientWidth;
|
||||||
|
if (evt.touches.length > 1 || zoom !== 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check all parent elements for scrollability
|
||||||
|
while (el !== document.body && el !== document) {
|
||||||
|
// Get some style properties
|
||||||
|
var style = window.getComputedStyle(el);
|
||||||
|
|
||||||
|
if (!style) {
|
||||||
|
// If we've encountered an element we can't compute the style for, get out
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore range input element
|
||||||
|
if (el.nodeName === 'INPUT' && el.getAttribute('type') === 'range') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var scrolling = style.getPropertyValue('-webkit-overflow-scrolling');
|
||||||
|
var overflowY = style.getPropertyValue('overflow-y');
|
||||||
|
var height = parseInt(style.getPropertyValue('height'), 10);
|
||||||
|
|
||||||
|
// Determine if the element should scroll
|
||||||
|
var isScrollable = scrolling === 'touch' && (overflowY === 'auto' || overflowY === 'scroll');
|
||||||
|
var canScroll = el.scrollHeight > el.offsetHeight;
|
||||||
|
|
||||||
|
if (isScrollable && canScroll) {
|
||||||
|
// Get the current Y position of the touch
|
||||||
|
var curY = evt.touches ? evt.touches[0].screenY : evt.screenY;
|
||||||
|
|
||||||
|
// Determine if the user is trying to scroll past the top or bottom
|
||||||
|
// In this case, the window will bounce, so we have to prevent scrolling completely
|
||||||
|
var isAtTop = (startY <= curY && el.scrollTop === 0);
|
||||||
|
var isAtBottom = (startY >= curY && el.scrollHeight - el.scrollTop === height);
|
||||||
|
|
||||||
|
// Stop a bounce bug when at the bottom or top of the scrollable element
|
||||||
|
if (isAtTop || isAtBottom) {
|
||||||
|
evt.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
// No need to continue up the DOM, we've done our job
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test the next parent
|
||||||
|
el = el.parentNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the bouncing -- no parents are scrollable
|
||||||
|
evt.preventDefault();
|
||||||
|
};
|
||||||
|
|
||||||
|
var handleTouchstart = function(evt) {
|
||||||
|
// Store the first Y position of the touch
|
||||||
|
startY = evt.touches ? evt.touches[0].screenY : evt.screenY;
|
||||||
|
};
|
||||||
|
|
||||||
|
var enable = function() {
|
||||||
|
// Listen to a couple key touch events
|
||||||
|
window.addEventListener('touchstart', handleTouchstart, supportsPassiveOption ? { passive : false } : false);
|
||||||
|
window.addEventListener('touchmove', handleTouchmove, supportsPassiveOption ? { passive : false } : false);
|
||||||
|
enabled = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
var disable = function() {
|
||||||
|
// Stop listening
|
||||||
|
window.removeEventListener('touchstart', handleTouchstart, false);
|
||||||
|
window.removeEventListener('touchmove', handleTouchmove, false);
|
||||||
|
enabled = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
var isEnabled = function() {
|
||||||
|
return enabled;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Enable by default if the browser supports -webkit-overflow-scrolling
|
||||||
|
// Test this by setting the property with JavaScript on an element that exists in the DOM
|
||||||
|
// Then, see if the property is reflected in the computed style
|
||||||
|
var testDiv = document.createElement('div');
|
||||||
|
document.documentElement.appendChild(testDiv);
|
||||||
|
testDiv.style.WebkitOverflowScrolling = 'touch';
|
||||||
|
var isScrollSupported = 'getComputedStyle' in window && window.getComputedStyle(testDiv)['-webkit-overflow-scrolling'] === 'touch';
|
||||||
|
document.documentElement.removeChild(testDiv);
|
||||||
|
|
||||||
|
if (isScrollSupported) {
|
||||||
|
enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
// A module to support enabling/disabling iNoBounce
|
||||||
|
var iNoBounce = {
|
||||||
|
enable: enable,
|
||||||
|
disable: disable,
|
||||||
|
isEnabled: isEnabled,
|
||||||
|
isScrollSupported: isScrollSupported
|
||||||
|
};
|
||||||
|
|
||||||
|
if (typeof module !== 'undefined' && module.exports) {
|
||||||
|
// Node.js Support
|
||||||
|
module.exports = iNoBounce;
|
||||||
|
}
|
||||||
|
if (typeof global.define === 'function') {
|
||||||
|
// AMD Support
|
||||||
|
(function(define) {
|
||||||
|
define('iNoBounce', [], function() { return iNoBounce; });
|
||||||
|
}(global.define));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Browser support
|
||||||
|
global.iNoBounce = iNoBounce;
|
||||||
|
}
|
||||||
|
}(this));
|
16
manifest.webmanifest
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"background_color": "#ff79a1",
|
||||||
|
"description": "Augment your keyboard with the symbols you need to type in your language.",
|
||||||
|
"display": "fullscreen",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/icons/tung.svg",
|
||||||
|
"sizes": "any",
|
||||||
|
"type": "image/svg+xml",
|
||||||
|
"purpose": "any"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "tung-tap.app",
|
||||||
|
"short_name": "tung-tap",
|
||||||
|
"start_url": "/index.html"
|
||||||
|
}
|
410
script.js
Normal file
|
@ -0,0 +1,410 @@
|
||||||
|
var tung = ""
|
||||||
|
|
||||||
|
var topToggle = false
|
||||||
|
function TopToggle(show = null) {
|
||||||
|
topToggle = show === null ? !topToggle : show
|
||||||
|
|
||||||
|
if (topToggle) {
|
||||||
|
$('#tung').show()
|
||||||
|
} else {
|
||||||
|
$('#tung').hide()
|
||||||
|
}
|
||||||
|
layout()
|
||||||
|
}
|
||||||
|
|
||||||
|
const sfx = new Audio('sfx-click.ogg')
|
||||||
|
|
||||||
|
$(document).ready(() => {
|
||||||
|
if (localStorage.getItem('tung') === null || localStorage.getItem('tung') == "") {
|
||||||
|
tung = "201C 2014 00D7 00B0 00B7 2022 221E 00B1 2023 201D"
|
||||||
|
localStorage.setItem('tung', tung)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tung = localStorage.getItem('tung')
|
||||||
|
}
|
||||||
|
$('#tung').html(tung)
|
||||||
|
layout()
|
||||||
|
|
||||||
|
// persistent text
|
||||||
|
if (localStorage.getItem('text') === null || localStorage.getItem('text') == "") {
|
||||||
|
localStorage.setItem('text', "")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$('#text').html(localStorage.getItem('text'))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// $('body').on('keydown', '[contenteditable]', function(e) {
|
||||||
|
// if (e.keyCode === 13) {
|
||||||
|
// e.preventDefault() // prevent the default newline insertion
|
||||||
|
// var range = window.getSelection().getRangeAt(0) // get the current selection range
|
||||||
|
// var newline = document.createTextNode("\n") // create a new text node with the newline character
|
||||||
|
// range.insertNode(newline) // insert the new text node at the current cursor position
|
||||||
|
// range.setStartAfter(newline) // set the cursor after the new text node
|
||||||
|
// range.setEndAfter(newline) // set the end of the selection after the new text node
|
||||||
|
// window.getSelection().removeAllRanges() // remove the old selection
|
||||||
|
// window.getSelection().addRange(range) // set the new selection to the updated range
|
||||||
|
// layout()
|
||||||
|
// }
|
||||||
|
// console.log(e.keyCode)
|
||||||
|
// });
|
||||||
|
|
||||||
|
|
||||||
|
let url = '/tungs.txt'
|
||||||
|
$.get(url, function (data) {
|
||||||
|
lines = data.split('\n')
|
||||||
|
nav()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
var lines = null
|
||||||
|
var indexPath = [ 0 ]
|
||||||
|
|
||||||
|
function indentation(line) {
|
||||||
|
let lineIndent = 0
|
||||||
|
while (line.charAt(lineIndent) == '\t') { lineIndent++ }
|
||||||
|
return lineIndent
|
||||||
|
}
|
||||||
|
|
||||||
|
function nav() {
|
||||||
|
let rootIndex = indexPath[indexPath.length - 1]
|
||||||
|
let root = lines[rootIndex]
|
||||||
|
let rootIndent = indentation(root)
|
||||||
|
|
||||||
|
let html = ``
|
||||||
|
for (let i = rootIndex; i < lines.length; i++) {
|
||||||
|
let line = lines[i]
|
||||||
|
let lineIndent = indentation(line)
|
||||||
|
|
||||||
|
if (lineIndent < rootIndent) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lineIndent > rootIndent) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
let option = line.trim()
|
||||||
|
let pick = false
|
||||||
|
if (option.includes(':')) {
|
||||||
|
option = option.split(':')[0].trim()
|
||||||
|
pick = true
|
||||||
|
}
|
||||||
|
|
||||||
|
let disabled = false
|
||||||
|
// if next line has doesn't have a greater indent, disable the option and make it gray
|
||||||
|
if (!pick && i + 1 < lines.length) {
|
||||||
|
let nextLineIndent = indentation(lines[i + 1])
|
||||||
|
if (nextLineIndent <= rootIndent) {
|
||||||
|
disabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let classes = `class="option
|
||||||
|
${disabled ? 'disabled' : ''}
|
||||||
|
${pick ? 'pick' : ''}
|
||||||
|
${(selected != null && i == selected) ? 'selected' : ''}
|
||||||
|
"`
|
||||||
|
let click = `${disabled ? '' : `onclick="select(${i}, ${pick})"`}`
|
||||||
|
|
||||||
|
html += `<div ${classes} ${click}>${option}</div>`
|
||||||
|
}
|
||||||
|
|
||||||
|
let top = indexPath.length < 2
|
||||||
|
let from = top ? 'tungs' : lines[rootIndex - 1].trim()
|
||||||
|
let back = `
|
||||||
|
<div class="option back" onclick="back()">
|
||||||
|
<div style="">
|
||||||
|
<img class="icon-btn" src="icons/back.svg" style="margin: 0; transform: rotate(${top ? 45 : 0}deg);">
|
||||||
|
<div style="margin-top: 16px; font-size: 24px; font-weight: bold;">${from}</div>
|
||||||
|
</div>
|
||||||
|
<div><a href="https://github.com/spatialfree/tung-tap">contribute</a></div>
|
||||||
|
</div>`
|
||||||
|
|
||||||
|
let head = `
|
||||||
|
<div style="display: flex; justify-content: space-around; align-items: center; padding: 0 10px; height: 36px; background-color: #ffb5dd; color: #fff;">
|
||||||
|
<div style="font-weight: bold;">tung-tap</div>
|
||||||
|
<img onclick="$('#nav').toggle()" class="icon-btn" src="icons/tungs.svg">
|
||||||
|
</div>`
|
||||||
|
|
||||||
|
let links = `
|
||||||
|
<div id="links" style="
|
||||||
|
padding: 10px 20px;
|
||||||
|
margin: 0px;
|
||||||
|
font-size: 14px;
|
||||||
|
background-color: #ff79a1;
|
||||||
|
color: #79394f;
|
||||||
|
">
|
||||||
|
Find more character codes in
|
||||||
|
<a target="_blank" href="https://unicode.org/charts/">unicode.org</a>
|
||||||
|
reference charts
|
||||||
|
or sketching in <a target="_blank" href="https://shapecatcher.com/">shapecatcher.com</a>
|
||||||
|
drawbox search!
|
||||||
|
</div>`
|
||||||
|
|
||||||
|
$('#nav').html(links + back + html)
|
||||||
|
}
|
||||||
|
|
||||||
|
function back() {
|
||||||
|
if (indexPath.length == 1) {
|
||||||
|
$('#nav').toggle()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
indexPath.pop()
|
||||||
|
nav()
|
||||||
|
}
|
||||||
|
|
||||||
|
var selected = null
|
||||||
|
function select(index, pick) {
|
||||||
|
let line = lines[index]
|
||||||
|
if (pick) {
|
||||||
|
$('#tung').html(line.split(':')[1].trim())
|
||||||
|
layout()
|
||||||
|
selected = index
|
||||||
|
} else {
|
||||||
|
indexPath.push(index + 1)
|
||||||
|
}
|
||||||
|
nav()
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLines(divId) {
|
||||||
|
var div = document.getElementById(divId)
|
||||||
|
var lineHeight = parseInt(window.getComputedStyle(div).getPropertyValue('line-height'))
|
||||||
|
var height = div.getBoundingClientRect().height
|
||||||
|
var lineCount = Math.floor(height / lineHeight)
|
||||||
|
var newLines = []
|
||||||
|
for (var i = 0; i < lineCount; i++) {
|
||||||
|
// var lineTop = i * lineHeight
|
||||||
|
// var lineBottom = (i + 1) * lineHeight
|
||||||
|
var lineText = div.innerText.substring(
|
||||||
|
div.getClientRects()[i].left - div.getClientRects()[0].left,
|
||||||
|
div.getClientRects()[i].right - div.getClientRects()[0].left
|
||||||
|
)
|
||||||
|
newLines.push(lineText)
|
||||||
|
}
|
||||||
|
return newLines
|
||||||
|
}
|
||||||
|
|
||||||
|
function layout() {
|
||||||
|
// keymap (tung is the proprietary name)
|
||||||
|
let tungText = $('#tung').html() //.replace('\r\n', '\n')
|
||||||
|
tungText = tungText.replace(/<div>/g, '\n').replace(/<\/div>/g, '')
|
||||||
|
if (tung != tungText) {
|
||||||
|
tung = tungText
|
||||||
|
localStorage.setItem('tung', tung)
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log(tungText)
|
||||||
|
|
||||||
|
let html = ``
|
||||||
|
let rows = tung.split('\n')
|
||||||
|
rows.forEach(row => {
|
||||||
|
row = row.trim()
|
||||||
|
if (row.length > 0) {
|
||||||
|
html += `<div class="row">\n`
|
||||||
|
let keys = row.split(' ')
|
||||||
|
keys.forEach(key => {
|
||||||
|
// print the newkey string
|
||||||
|
// and the unicode character
|
||||||
|
html += `<div class="key" charcode="${key}" ${topToggle ? `style="position: relative; padding-top: 10px;` : ``}">
|
||||||
|
<span style="
|
||||||
|
font-family: 'DM Mono', monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 12px;
|
||||||
|
position: absolute;
|
||||||
|
top: 2px; left: 0; right: 0;
|
||||||
|
color: #aaa;
|
||||||
|
${topToggle ? `` : `display: none;`}
|
||||||
|
">${key}</span>
|
||||||
|
${String.fromCharCode("0x" + key)}
|
||||||
|
</div>\n`
|
||||||
|
})
|
||||||
|
html += `</div>\n`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
$('#keyboard').html(html)
|
||||||
|
|
||||||
|
$('.key').click(key => {
|
||||||
|
// override default behavior
|
||||||
|
key.preventDefault()
|
||||||
|
|
||||||
|
// if text is not focused then set the cursor to the end of $('#text')
|
||||||
|
let textEl = $('#text')
|
||||||
|
if (document.activeElement != textEl[0] && window.getSelection().rangeCount == 0) {
|
||||||
|
let selection = window.getSelection()
|
||||||
|
let range = document.createRange()
|
||||||
|
range.selectNodeContents(textEl[0])
|
||||||
|
range.collapse(false)
|
||||||
|
selection.removeAllRanges()
|
||||||
|
selection.addRange(range)
|
||||||
|
}
|
||||||
|
textEl.focus()
|
||||||
|
|
||||||
|
// insert key character into contenteditable div $('#text') at cursor position
|
||||||
|
let char = String.fromCharCode("0x" + $(key.target).attr('charcode'))
|
||||||
|
insertTextAtCursor(char)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// navigator.vibrate(20);
|
||||||
|
// sfx.play()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function insertTextAtCursor(text) {
|
||||||
|
let selection = window.getSelection()
|
||||||
|
let range = selection.getRangeAt(0)
|
||||||
|
range.deleteContents()
|
||||||
|
let node = document.createTextNode(text)
|
||||||
|
range.insertNode(node)
|
||||||
|
range.setStartAfter(node)
|
||||||
|
range.setEndAfter(node)
|
||||||
|
selection.removeAllRanges()
|
||||||
|
selection.addRange(range)
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyToClipboard(id) {
|
||||||
|
var textEl = $(id)
|
||||||
|
|
||||||
|
// select all the text on the contenteditable div textEl to show it was copied
|
||||||
|
let selection = window.getSelection()
|
||||||
|
let range = document.createRange()
|
||||||
|
range.selectNodeContents(textEl[0])
|
||||||
|
selection.removeAllRanges()
|
||||||
|
selection.addRange(range)
|
||||||
|
|
||||||
|
if (!navigator.clipboard) {
|
||||||
|
console.error('Clipboard API not supported');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
navigator.clipboard.writeText(textEl.html())
|
||||||
|
.then(() => {
|
||||||
|
console.log('Text copied to clipboard')
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error('Failed to copy text: ', err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// const newPosition = toolbar.getBoundingClientRect().top
|
||||||
|
// toolbar.classList.add('down')
|
||||||
|
|
||||||
|
|
||||||
|
function getVisibleHeight() {
|
||||||
|
var pixelRatio = window.devicePixelRatio || 1
|
||||||
|
var viewportHeight = window.visualViewport.height * pixelRatio
|
||||||
|
var windowHeight = window.innerHeight * pixelRatio
|
||||||
|
var keyboardHeight = viewportHeight - windowHeight
|
||||||
|
// $('#tung').html(`viewport ${viewportHeight}px | window ${windowHeight}px | keyboard ${keyboardHeight}px`)
|
||||||
|
|
||||||
|
return Math.abs(keyboardHeight) / pixelRatio || 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var out = 400;
|
||||||
|
function loop(timestamp) {
|
||||||
|
var delta = timestamp - lastRender
|
||||||
|
time += delta
|
||||||
|
|
||||||
|
let height = getVisibleHeight()
|
||||||
|
|
||||||
|
// use the bottom of the #tung element position to use for the body top
|
||||||
|
// to be able to toggle the top of the page
|
||||||
|
let top = $('#tung').outerHeight(true) //+ $('#links').outerHeight(true) // + $('#home').outerHeight(true)
|
||||||
|
|
||||||
|
if (topToggle)
|
||||||
|
top *= 0
|
||||||
|
|
||||||
|
// lerp out to top
|
||||||
|
if (time > 100) {
|
||||||
|
out = lerp(out, top, 0.01 * delta)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 'bottom': height + 'px',
|
||||||
|
// $('body').css({
|
||||||
|
// 'top': -out + 'px'
|
||||||
|
// })
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// $('#keyboard').css({
|
||||||
|
// 'bottom': height + 'px',
|
||||||
|
// })
|
||||||
|
|
||||||
|
|
||||||
|
// check if need to backup the text
|
||||||
|
let text = $('#text').html()
|
||||||
|
if (text != lastText) {
|
||||||
|
localStorage.setItem('text', text)
|
||||||
|
lastText = text
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
lastRender = timestamp
|
||||||
|
window.requestAnimationFrame(loop)
|
||||||
|
}
|
||||||
|
var lastText = ''
|
||||||
|
var lastRender = 0
|
||||||
|
var time = 0
|
||||||
|
window.requestAnimationFrame(loop)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let ua = navigator.userAgent.toLowerCase()
|
||||||
|
let safari = ua.indexOf('safari') != -1 && !(ua.indexOf('chrome') > -1)
|
||||||
|
|
||||||
|
// dev toggle override
|
||||||
|
// safari = !safari
|
||||||
|
|
||||||
|
$('html').css('--bg', safari ? '214, 216, 221' : '16, 16, 16')
|
||||||
|
$('html').css('--fg', safari ? '214, 216, 221' : '240, 240, 240')
|
||||||
|
$('html').css('--key', safari ? '255, 255, 255' : '48, 48, 48')
|
||||||
|
$('html').css('--key-height', safari ? '40px' : '48px')
|
||||||
|
$('html').css('--key-radius', safari ? '5px' : '8px')
|
||||||
|
$('html').css('--txt', safari ? '0, 0, 0' : '255, 255, 255')
|
||||||
|
$('html').css('--txt-size', safari ? '22px' : '26px')
|
||||||
|
$('html').css('--field', safari ? '255, 255, 255' : '0, 0, 0')
|
||||||
|
$('html').css('--gap', safari ? '6px' : '5px')
|
||||||
|
|
||||||
|
window.addEventListener("beforeinstallprompt", (e) => {
|
||||||
|
console.log('beforeinstallprompt')
|
||||||
|
// // Prevent Chrome 67 and earlier from automatically showing the prompt
|
||||||
|
// e.preventDefault()
|
||||||
|
// // Stash the event so it can be triggered later.
|
||||||
|
// deferredPrompt = e
|
||||||
|
// // // Update UI to notify the user they can add to home screen
|
||||||
|
// // addBtn.style.display = "block"
|
||||||
|
|
||||||
|
// // addBtn.addEventListener("click", (e) => {
|
||||||
|
// // // hide our user interface that shows our A2HS button
|
||||||
|
// // addBtn.style.display = "none"
|
||||||
|
// // })
|
||||||
|
// // Show the prompt
|
||||||
|
// deferredPrompt.prompt()
|
||||||
|
// // Wait for the user to respond to the prompt
|
||||||
|
// deferredPrompt.userChoice.then((choiceResult) => {
|
||||||
|
// if (choiceResult.outcome === "accepted") {
|
||||||
|
// console.log("User accepted the A2HS prompt")
|
||||||
|
// } else {
|
||||||
|
// console.log("User dismissed the A2HS prompt")
|
||||||
|
// }
|
||||||
|
// deferredPrompt = null
|
||||||
|
// })
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function lerp(a, b, n) {
|
||||||
|
return (1 - n) * a + n * b
|
||||||
|
}
|
BIN
sfx-click.ogg
Normal file
215
style.css
Normal file
|
@ -0,0 +1,215 @@
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible:ital,wght@0,400;0,700;1,400&family=DM+Mono&display=swap');
|
||||||
|
|
||||||
|
:root {
|
||||||
|
/* --bg: 16, 16, 16;
|
||||||
|
--fg: 240, 240, 240;
|
||||||
|
--key: 32, 32, 32;
|
||||||
|
--txt: 255, 255, 255;
|
||||||
|
--field: 255, 255, 255; */
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
/* all: unset; */
|
||||||
|
outline: none;
|
||||||
|
/* box-sizing: border-box; */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* *::selection {
|
||||||
|
background-color: rgba(255, 255, 255, 0.333);
|
||||||
|
} */
|
||||||
|
|
||||||
|
/* *::marker {
|
||||||
|
color: rgb(var(--txt));
|
||||||
|
} */
|
||||||
|
|
||||||
|
|
||||||
|
html {
|
||||||
|
/* font-family: sans-serif; */
|
||||||
|
font-family: 'Atkinson Hyperlegible', sans-serif;
|
||||||
|
font-size: 16px;
|
||||||
|
|
||||||
|
background-color: rgb(var(--field));
|
||||||
|
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
/* start from bottom */
|
||||||
|
justify-content: flex-end;
|
||||||
|
position: absolute;
|
||||||
|
top: 0; left: 0; right: 0; bottom: 0;
|
||||||
|
background-color: rgb(var(--field));
|
||||||
|
|
||||||
|
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
touch-action: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* hide scrollbar */
|
||||||
|
*::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
textarea,
|
||||||
|
button,
|
||||||
|
select,
|
||||||
|
div,
|
||||||
|
a {
|
||||||
|
-webkit-tap-highlight-color: rgba(0,0,0,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav {
|
||||||
|
background-color: rgb(var(--field));
|
||||||
|
color: rgb(var(--txt));
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: -webkit-fill-available;
|
||||||
|
/* margin-top: 120px; */
|
||||||
|
position: fixed;
|
||||||
|
top: 0; left: 0; right: 0; bottom: 0;
|
||||||
|
|
||||||
|
/* opacity: 0.999; weird fix... */
|
||||||
|
|
||||||
|
padding-bottom: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option {
|
||||||
|
padding: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
background-color: rgb(var(--bg));
|
||||||
|
border-top: 0.5px solid #80808042;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option:nth-child(2n) {
|
||||||
|
background-color: rgb(var(--field));
|
||||||
|
}
|
||||||
|
|
||||||
|
.option.pick {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option.selected {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option.disabled {
|
||||||
|
color: rgba(var(--txt), 0.333);
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* textarea {
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.5;
|
||||||
|
padding: 1.5rem 1rem;
|
||||||
|
width: 100%;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
margin: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
outline: none;
|
||||||
|
resize: none;
|
||||||
|
|
||||||
|
overflow: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
|
||||||
|
height: -webkit-fill-available;
|
||||||
|
|
||||||
|
background-color: rgb(var(--field));
|
||||||
|
color: rgb(var(--txt));
|
||||||
|
} */
|
||||||
|
|
||||||
|
#keyboard {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
background-color: rgb(var(--bg));
|
||||||
|
z-index: 100;
|
||||||
|
user-select: none;
|
||||||
|
border-top: 0.5px solid rgba(128, 128, 128, 0.333);
|
||||||
|
box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
/* position: fixed;
|
||||||
|
bottom: 0; left: 0; right: 0; */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); */
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: var(--gap);
|
||||||
|
margin: 9px 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.key {
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-size: var(--txt-size);
|
||||||
|
line-height: var(--key-height);
|
||||||
|
display: inline-block;
|
||||||
|
flex: 1;
|
||||||
|
max-width: 64px;
|
||||||
|
height: var(--key-height);
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
border-top: 0.5px solid rgba(255, 255, 255, 0);
|
||||||
|
margin-bottom: -0.5px;
|
||||||
|
border-radius: var(--key-radius);
|
||||||
|
background-color: rgba(var(--key));
|
||||||
|
color: rgb(var(--txt));
|
||||||
|
|
||||||
|
box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.333);
|
||||||
|
/* box-shadow: 0 0 0.5rem 0rem rgba(255, 255, 255, 0.0) inset; */
|
||||||
|
transition: 0.1s;
|
||||||
|
transition-timing-function: cubic-bezier(.51,.12,0,-0.27);
|
||||||
|
}
|
||||||
|
|
||||||
|
.key:active {
|
||||||
|
transition: 0.0s;
|
||||||
|
/* transition-timing-function: cubic-bezier(0.1, 1, 0.77, 0.62); */
|
||||||
|
|
||||||
|
border-color: rgba(255, 255, 255, 0.333);
|
||||||
|
box-shadow: 0 7px 4px 0 rgba(0, 0, 0, 0.333);
|
||||||
|
transform: translateY(-6px);
|
||||||
|
/* box-shadow: 0 0px 6px 0 rgba(0, 0, 0, 0.333) inset;
|
||||||
|
transform: translateY(2.0px); */
|
||||||
|
/* flex: 1.2; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.active {
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-btn {
|
||||||
|
display: block;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
margin: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* #copybtn:active {
|
||||||
|
transition: 0.1s;
|
||||||
|
box-shadow: 0 0 0.5rem 0rem rgba(0, 0, 0, 0.333) inset;
|
||||||
|
} */
|
7
tungs.txt
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
Misc.
|
||||||
|
Test : 201C 2014 00D7 00B0 00B7 2022 221E 00B1 2023 201D
|
||||||
|
Salishan
|
||||||
|
Bella Coola
|
||||||
|
Coast Salish
|
||||||
|
Interior Salish
|
||||||
|
Spokane : 0142 019B 0323 02B7 203F 0301 030C 0294 0295 0313
|