41 | This Website uses Cookies (Google Analytics).
42 | For more information see legal notice.
43 |
44 |
45 |
Show
46 | details
47 |
Ok
48 |
49 |
50 |
51 | We use cookies to personalize content and ads, to provide social media features and to analyse our traffic.
52 | We also share information about your use of our site with our social media, advertising and analytics partners
53 | who
54 | may combine it with other information that you've provided to them or that they've collected from your use of
55 | their services.
56 |
57 |
58 |
To analyse traffic we use Google Analytics, to opt-out click here
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
Edit CSV
69 |
70 |
71 |
- Website Terms and Conditions of Use -
72 |
73 |
Terms
74 |
75 | By accessing this Website, accessible from edit-csv.net, you are agreeing to be bound by these Website Terms and Conditions of Use and agree that you are responsible for the agreement with any applicable local laws. If you disagree with any of these terms, you are prohibited from accessing this site. The materials contained in this Website are protected by copyright and trade mark law.
76 |
77 |
Disclaimer
78 |
79 | All the materials on Dähne Solutions's Website are provided “as is”. Dähne Solutions makes no warranties, may it be expressed or implied, therefore negates all other warranties. Furthermore, Dähne Solutions does not make any representations concerning the accuracy or reliability of the use of the materials on its Website or otherwise relating to such materials or any sites linked to this Website.
80 |
81 |
Limitations
82 |
83 | Dähne Solutions or its suppliers will not be hold accountable for any damages that will arise with the use or inability to use the materials on Dähne Solutions's Website, even if Dähne Solutions or an authorize representative of this Website has been notified, orally or written, of the possibility of such damage. Some jurisdiction does not allow limitations on implied warranties or limitations of liability for incidental damages, these limitations may not apply to you.
84 |
85 |
Revisions and Errata
86 |
87 | The materials appearing on Dähne Solutions's Website may include technical, typographical, or photographic errors. Dähne Solutions will not promise that any of the materials in this Website are accurate, complete, or current. Dähne Solutions may change the materials contained on its Website at any time without notice. Dähne Solutions does not make any commitment to update the materials.
88 |
89 |
Links
90 |
91 | Dähne Solutions has not reviewed all of the sites linked to its Website and is not responsible for the contents of any such linked site. The presence of any link does not imply endorsement by Dähne Solutions of the site. The use of any linked website is at the user's own risk.
92 |
93 |
Site Terms of Use Modifications
94 |
95 | Dähne Solutions may revise these Terms of Use for its Website at any time without prior notice. By using this Website, you are agreeing to be bound by the current version of these Terms and Conditions of Use.
96 |
97 |
Governing Law
98 |
99 | Any claim related to Dähne Solutions's Website shall be governed by the laws of Germany without regards to its conflict of law provisions.
100 |
101 |
102 |
103 |
104 |
- Impressum -
105 |
106 |
Angaben gemäß § 5 TMG
107 |
108 |
109 |
110 |
111 |
Kontakt
112 |
113 |
114 |
115 |
116 |
Umsatzsteuer-ID
117 |
Umsatzsteuer-Identifikationsnummer gemäß §27 a Umsatzsteuergesetz:
118 | DE 325 460 253
119 |
120 |
Wir sind nicht bereit oder verpflichtet, an Streitbeilegungsverfahren vor einer Verbraucherschlichtungsstelle
121 | teilzunehmen.
122 |
123 |
Haftung für Inhalte
124 |
125 |
Als Diensteanbieter sind wir gemäß § 7 Abs.1 TMG für eigene Inhalte auf diesen Seiten nach
126 | den allgemeinen Gesetzen verantwortlich. Nach §§ 8 bis 10 TMG sind wir als Diensteanbieter jedoch
127 | nicht
128 | verpflichtet, übermittelte oder gespeicherte fremde Informationen zu überwachen oder nach
129 | Umständen
130 | zu forschen, die auf eine rechtswidrige Tätigkeit hinweisen.
131 |
Verpflichtungen zur Entfernung oder Sperrung der Nutzung von Informationen nach den allgemeinen Gesetzen
132 | bleiben
133 | hiervon unberührt. Eine diesbezügliche Haftung ist jedoch erst ab dem Zeitpunkt der Kenntnis einer
134 | konkreten Rechtsverletzung möglich. Bei Bekanntwerden von entsprechenden Rechtsverletzungen werden wir
135 | diese
136 | Inhalte umgehend entfernen.
137 |
138 |
Haftung für Links
139 |
140 |
Unser Angebot enthält Links zu externen Websites Dritter, auf deren Inhalte wir keinen Einfluss haben.
141 | Deshalb können wir für diese fremden Inhalte auch keine Gewähr übernehmen. Für die
142 | Inhalte der verlinkten Seiten ist stets der jeweilige Anbieter oder Betreiber der Seiten verantwortlich. Die
143 | verlinkten Seiten wurden zum Zeitpunkt der Verlinkung auf mögliche Rechtsverstöße
144 | überprüft. Rechtswidrige Inhalte waren zum Zeitpunkt der Verlinkung nicht erkennbar.
145 |
146 |
Eine permanente inhaltliche Kontrolle der verlinkten Seiten ist jedoch ohne konkrete Anhaltspunkte einer
147 | Rechtsverletzung nicht zumutbar. Bei Bekanntwerden von Rechtsverletzungen werden wir derartige Links umgehend
148 | entfernen.
149 |
150 |
Urheberrecht
151 |
152 |
Die durch die Seitenbetreiber erstellten Inhalte und Werke auf diesen Seiten unterliegen dem deutschen
153 | Urheberrecht. Die Vervielfältigung, Bearbeitung, Verbreitung und jede Art der Verwertung außerhalb
154 | der
155 | Grenzen des Urheberrechtes bedürfen der schriftlichen Zustimmung des jeweiligen Autors bzw. Erstellers.
156 | Downloads und Kopien dieser Seite sind nur für den privaten, nicht kommerziellen Gebrauch gestattet.
157 |
158 |
Soweit die Inhalte auf dieser Seite nicht vom Betreiber erstellt wurden, werden die Urheberrechte Dritter
159 | beachtet. Insbesondere werden Inhalte Dritter als solche gekennzeichnet. Sollten Sie trotzdem auf eine
160 | Urheberrechtsverletzung aufmerksam werden, bitten wir um einen entsprechenden Hinweis. Bei Bekanntwerden von
161 | Rechtsverletzungen werden wir derartige Inhalte umgehend entfernen.
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
--------------------------------------------------------------------------------
/csvEditorHtml/browser/robots.txt:
--------------------------------------------------------------------------------
1 |
2 | User-agent: *
3 | Disallow: /legalNotice.html
4 | Disallow: /privacy.html
--------------------------------------------------------------------------------
/csvEditorHtml/browser/third-party/LICENSE-iconv-lite.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2011 Alexander Shtuchkin
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/csvEditorHtml/browser/third-party/LICENSE-node-chardet.txt:
--------------------------------------------------------------------------------
1 | Copyright (C) 2020 Dmitry Shirokov
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
--------------------------------------------------------------------------------
/csvEditorHtml/browser/third-party/bulma-toast/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Rafael Franco
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/csvEditorHtml/browser/third-party/bulma-toast/bulma-toast.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * bulma-toast 2.0.3
3 | * (c) 2018-present @rfoel
4 | * Released under the MIT License.
5 | */
6 | (function(a,b){"object"==typeof exports&&"undefined"!=typeof module?b(exports):"function"==typeof define&&define.amd?define(["exports"],b):(a=a||self,b(a.bulmaToast={}))})(this,function(a){'use strict';function b(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}function c(a,b){for(var c,d=0;d=a.children.length&&a.remove()}},{key:"onAnimationEnd",value:function(){var a=0 td,
121 | .vscode-dark .ht_clone_left tr:nth-of-type(odd)> td,
122 | .vscode-dark .ht_clone_top_left_corner tr:nth-of-type(odd)> td {
123 | background-color: #262c2e;
124 | border-color: #5c5c5c;
125 | }
126 |
127 | .vscode-dark .ht_clone_top tr:nth-of-type(even)> td,
128 | .vscode-dark .ht_clone_left tr:nth-of-type(even)> td,
129 | .vscode-dark .ht_clone_top_left_corner tr:nth-of-type(even)> td {
130 | background-color: #2d3436;
131 | border-color: #5c5c5c;
132 | }
133 |
134 | .vscode-dark .ht_clone_top tr:last-child > td,
135 | .vscode-dark .ht_clone_top_left_corner tr:last-child > td {
136 | border-bottom-color: #b7b7b7;
137 | }
138 |
139 | .vscode-dark .ht_clone_left tr > td:last-child,
140 | .vscode-dark .ht_clone_top_left_corner tr > td:last-child {
141 | border-right-color: #b7b7b7;
142 | }
143 |
144 | .vscode-dark .handsontable tr {
145 | background: #5c5c5c;
146 | }
147 |
148 | /* normal rows */
149 | .vscode-dark .ht_master tr:nth-of-type(odd) > td {
150 | background-color: #262c2e;
151 | border-color: #5c5c5c;
152 | }
153 |
154 | .vscode-dark .ht_master tr:nth-of-type(even) > td {
155 | background-color: #2d3436;
156 | border-color: #5c5c5c;
157 | }
158 |
159 | .vscode-dark .ht_clone_left th {
160 | background-color:rgb(26, 29, 29);
161 | border-color: #5c5c5c !important;
162 | }
163 |
164 | .vscode-dark .ht_clone_top th {
165 | background-color: rgb(26, 29, 29);
166 | border-color: #5c5c5c !important;
167 | }
168 |
169 | .vscode-dark .handsontable tbody th.ht__highlight.foo,
170 | .vscode-dark .handsontable thead th.ht__highlight.foo {
171 | background-color: #0251b5;
172 | }
173 |
174 | /* we don't need this... first real column... light css doesn't even have this*/
175 | .vscode-dark .handsontable th:nth-child(2) {
176 | /* border-left: 1px solid #5c5c5c !important; */
177 | }
178 | .vscode-dark .ht_clone_top thead tr:nth-child(2) {
179 | border: 0 !important;
180 | }
181 |
182 | .vscode-dark .ht_clone_top_left_corner th {
183 | border-color: #5c5c5c!important;
184 | }
185 |
186 | .vscode-dark .ht_clone_top_left_corner thead tr:nth-child(1) {
187 | border-right: 0 !important;
188 | }
189 |
190 | .vscode-dark .comment-row {
191 | /* background-color: #5f5f5f!important; */
192 | color: #888787;
193 | }
194 |
195 | .vscode-dark .comment-row td:nth-child(n+3) {
196 | color: red;
197 | }
198 |
199 | /* context menu */
200 | .vscode-dark .htContextMenu table tbody tr td {
201 | background-color: #262c2e !important;
202 | border-color: #5c5c5c !important;
203 | }
204 |
205 | .vscode-dark .htContextMenu table tbody tr td:hover {
206 | background-color: #4e4e4e !important;
207 | }
208 | .vscode-dark .htContextMenu table tbody tr td.htDisabled:hover {
209 | background-color: #262c2e !important;
210 | }
211 |
212 | .vscode-dark .htContextMenu table.htCore {
213 | border-color: #5c5c5c !important;
214 | }
215 |
216 | .vscode-dark .ht_master tr> td.search-result-cell {
217 | background-color: #585802; /* this is better for comments */
218 | /* background-color: #7b7b03; */
219 | }
220 |
221 | .vscode-dark .find-widget {
222 | background-color: #2a2a2a;
223 | /* filter: brightness(120%); */
224 | }
225 |
226 | .vscode-dark .find-widget .divider {
227 | background-color: #4c4c4c;
228 | }
229 |
230 | .vscode-dark .find-widget .btn.active {
231 | border-color: rgba(0, 122, 204, 0);
232 | background-color: rgba(0, 127, 212, 0.4);
233 | }
234 |
235 | .vscode-dark .find-widget .error-message {
236 |
237 | }
238 |
239 | .vscode-dark .options-title {
240 | color: #d0d0d0;
241 | }
242 |
243 | .vscode-dark .all-options table {
244 | border: 1px solid #5e5e5e;
245 | }
246 |
247 | .vscode-dark a {
248 | color: #5c9bff;
249 | }
250 |
251 | .vscode-dark .handsontable .htDimmed {
252 | /*color: #b7b7b7*/ /*harder to read*/
253 | color: inherit;
254 | }
255 |
256 | .vscode-dark .row-col-insert-btns {
257 | border-left: 1px solid #5e5e5e;
258 | }
259 |
260 | .vscode-dark .btn-with-menu-wrapper .menu {
261 | border: 1px solid var(--dropdown-border);
262 | background-color: var(--dropdown-background);
263 | }
264 |
--------------------------------------------------------------------------------
/csvEditorHtml/high_contrast.css:
--------------------------------------------------------------------------------
1 | body.vscode-high-contrast {
2 | color: white;
3 | }
4 |
5 |
6 | .vscode-high-contrast .button.is-light {
7 | color: black;
8 | }
9 |
10 | .vscode-high-contrast .button.is-outlined:hover {
11 | background-color: white;
12 | color: black;
13 | }
14 |
15 | .vscode-high-contrast .button.is-outlined:focus {
16 | background-color: transparent;
17 | color: white;
18 | }
19 |
20 | .vscode-high-contrast .select select {
21 | color: black;
22 | }
23 |
24 | .vscode-high-contrast .title {
25 | color: white;
26 | }
27 |
28 | .vscode-dark .radio:hover {
29 | color: white;
30 | }
31 |
32 | .vscode-high-contrast .box {
33 | background-color: black;
34 | color: white;
35 | }
36 |
37 | .vscode-high-contrast .keys {
38 | color: white;
39 | background: #404040;
40 | border-color: #ccc #aaa #888 #bbb;
41 | -moz-box-shadow: 0 2px 0 rgba(0, 0, 0, 0.2), 0 0 0 1px #888 inset;
42 | -webkit-box-shadow: 0 2px 0 rgba(0, 0, 0, 0.2), 0 0 0 1px #888 inset;
43 | box-shadow: 0 2px 0 rgba(0, 0, 0, 0.2), 0 0 0 1px #888 inset;
44 | }
45 |
46 | .vscode-high-contrast a {
47 | color: #f9f500;
48 | }
49 |
50 | .vscode-high-contrast a:hover {
51 | color: red;
52 | }
53 |
54 | * handson table styles */
55 |
56 | .vscode-high-contrast .ht_master,
57 | .vscode-high-contrast .handsontable th {
58 | color: #d0d0d0 !important;
59 | }
60 |
61 |
62 | /* Edit mode */
63 | .vscode-high-contrast .handsontableInput {
64 | background-color: #504f4f!important;
65 | color: #d0d0d0;
66 | }
67 |
68 | /* All headers */
69 | .vscode-high-contrast .handsontable th {
70 | background-color: rgb(26, 29, 29);
71 | color: white;
72 | }
73 |
74 | .vscode-high-contrast .handsontable span.colHeader.columnSorting::before {
75 | filter: invert(100%);
76 | }
77 |
78 | .vscode-high-contrast .ht_clone_top th {
79 | border-color: #5c5c5c;
80 | }
81 |
82 | /* fixed rows top */
83 | .vscode-high-contrast .ht_clone_top tr:nth-of-type(odd)> td {
84 | background-color: black;
85 | border-color: #5c5c5c;
86 | }
87 |
88 | .vscode-high-contrast .ht_clone_top tr:nth-of-type(even)> td {
89 | background-color: #1d1d1d;
90 | border-color: #5c5c5c;
91 | }
92 |
93 | .vscode-high-contrast .ht_clone_top tr:last-child > td {
94 | border-bottom-color: red;
95 | }
96 |
97 | .vscode-dark .handsontable tr {
98 | background: #5c5c5c;
99 | }
100 |
101 | /* normal rows */
102 | .vscode-high-contrast .ht_master tr:nth-of-type(odd) > td {
103 | background-color: black;
104 | border-color: #5c5c5c;
105 | }
106 |
107 | .vscode-high-contrast .ht_master tr:nth-of-type(even) > td {
108 | background-color: #1d1d1d;
109 | border-color: #5c5c5c;
110 | }
111 |
112 | .vscode-high-contrast .ht_clone_left th {
113 | background-color:rgb(26, 29, 29);
114 | border-color: #5c5c5c !important;
115 | }
116 |
117 | .vscode-high-contrast .ht_clone_top th {
118 | background-color: rgb(26, 29, 29);
119 | border-color: #5c5c5c !important;
120 | }
121 |
122 | .vscode-high-contrast .handsontable tbody th.ht__highlight.foo,
123 | .vscode-high-contrast .handsontable thead th.ht__highlight.foo {
124 | background-color: red;
125 | }
126 |
127 | /* we don't need this... first real column...*/
128 | .vscode-high-contrast .handsontable th:nth-child(2) {
129 | /* border-left: 1px solid #5c5c5c !important; */
130 | }
131 |
132 | .vscode-high-contrast .ht_clone_top thead tr:nth-child(2) {
133 | border: 0 !important;
134 | }
135 |
136 | .vscode-high-contrast .ht_clone_top_left_corner th {
137 | border-color: #5c5c5c!important;
138 | }
139 |
140 | .vscode-high-contrast .ht_clone_top_left_corner thead tr:nth-child(1) {
141 | border-right: 0 !important;
142 | }
143 |
144 | .vscode-high-contrast .comment-comment-row {
145 | /* background-color: #f7f7f7!important; */
146 | color: #ff9d00;
147 | }
148 |
149 | .vscode-high-contrast .comment-row td:nth-child(n+3) {
150 | color: red;
151 | }
152 |
153 | /* context menu */
154 | .vscode-high-contrast .htContextMenu table tbody tr td {
155 | background-color: black !important;
156 | border-color: #5c5c5c !important;
157 | }
158 |
159 | .vscode-high-contrast .htContextMenu table tbody tr td:hover {
160 | background-color: #ff0d0d !important;
161 | }
162 | .vscode-high-contrast .htContextMenu table tbody tr td.htDisabled:hover {
163 | background-color: black !important;
164 | }
165 |
166 | .vscode-high-contrast .htContextMenu table.htCore {
167 | border-color: #5c5c5c !important;
168 | }
169 |
170 | .vscode-high-contrast .ht_master tr> td.search-result-cell {
171 | background-color: #9d00ff!important;
172 | }
173 |
174 | .vscode-high-contrast .find-widget{
175 | background-color: black;
176 | color: white;
177 | border: 2px solid #6fc3df;
178 | }
179 |
180 | .vscode-high-contrast .find-widget .divider {
181 | background-color: #676767;
182 | }
183 |
184 | .vscode-high-contrast .find-widget .btn.active {
185 | background-color: #28ff00;
186 | }
187 |
188 | .vscode-high-contrast .find-widget .error-message {
189 | color: white;
190 | }
191 |
192 | .vscode-high-contrast .options-title {
193 | font-size: 130%;
194 | color: #d0d0d0;
195 | }
196 |
197 | .vscode-high-contrast .all-options table {
198 | border: 1px solid white;
199 | border-collapse: initial;
200 | border-radius: 10px;
201 | }
202 |
203 | .vscode-high-contrast .handsontable .htDimmed {
204 | /*color: #cacaca;*/ /*harder to read*/
205 | cursor: pointer; /*as color does not really work maybe this helps?*/
206 | color: inherit;
207 | }
208 |
209 | .vscode-light .row-col-insert-btns {
210 | border-left: 1px solid white;
211 | }
212 |
--------------------------------------------------------------------------------
/csvEditorHtml/light.css:
--------------------------------------------------------------------------------
1 | body.vscode-light {
2 | --text-color: #657b83;
3 | color: #657b83;
4 |
5 | --button-icon-background: transparent;
6 | --button-icon-hover-background: rgba(90, 93, 94, 0.31);
7 |
8 | --input-foreground: #586e75;
9 | --input-background: #ddd6c1;
10 | --dropdown-border: #d3af86;
11 | --dropdown-background: #eee8d5;
12 | --checkbox-background: #eee8d5;
13 | --checkbox-border: #d3af86;
14 |
15 | --button-secondary-hover-background: #4c5561;
16 |
17 | --button-secondary-background: #5f6a79;
18 | --button-secondary-foreground: #ffffff;
19 | --foreground: #616161;
20 | --input-foreground: #586e75;
21 | --focus-border: #d3af86;
22 | --list-active-selection-background: #dfca88;
23 | --list-active-selection-foreground: #6c6c6c;
24 | --list-hover-background: rgba(223, 202, 136, 0.27);
25 | }
26 |
27 | .vscode-light .button.is-light {
28 | color: #657b83;
29 | }
30 |
31 | .vscode-light .button.is-outlined {
32 | color: #657b83;
33 | background-color: transparent;
34 | }
35 |
36 | .vscode-light .button.is-outlined:hover {
37 | background-color: #404040; /* maybe #676767 ??*/
38 | color: #d0d0d0;
39 | }
40 | .vscode-light .button.is-outlined:focus {
41 | background-color:transparent;
42 | color: #404040;
43 | }
44 |
45 | .vscode-light .radio:hover {
46 | color: #657b83;
47 | }
48 |
49 | /* handson table styles */
50 |
51 | .vscode-light .ht_master a[data-link-is-openable="1"] {
52 | cursor: default;
53 | text-decoration: underline;
54 | color: var(--text-color, #657b83);
55 | }
56 |
57 | .vscode-light .ht_master a[data-link-is-openable="1"].link-hovered.is-open-url-key-down {
58 | color: var(--vscode-editorLink-activeForeground, #0000ff); /* default for browser */
59 | cursor: pointer;
60 | }
61 |
62 | .vscode-light .ht_master,
63 | .vscode-light .handsontable th {
64 | color: var(--text-color, #657b83) !important;
65 | }
66 |
67 | /* fixed rows/columns */
68 | .vscode-light .ht_clone_top tr:nth-of-type(odd)> td,
69 | .vscode-light .ht_clone_left tr:nth-of-type(odd)> td,
70 | .vscode-light .ht_clone_top_left_corner tr:nth-of-type(odd)> td {
71 | background-color: white;
72 | }
73 |
74 | .vscode-light .ht_clone_top tr:nth-of-type(even)> td,
75 | .vscode-light .ht_clone_left tr:nth-of-type(even)> td,
76 | .vscode-light .ht_clone_top_left_corner tr:nth-of-type(even)> td {
77 | background-color: #f7f7f7;
78 | }
79 |
80 | .vscode-light .ht_clone_top tr:last-child > td,
81 | .vscode-light .ht_clone_top_left_corner tr:last-child > td {
82 | border-bottom-color: #3c3c3c;
83 | }
84 |
85 | .vscode-light .ht_clone_left tr > td:last-child,
86 | .vscode-light .ht_clone_top_left_corner tr > td:last-child {
87 | border-right-color: #3c3c3c;
88 | }
89 |
90 | /* normal rows */
91 | .vscode-light .ht_master tr:nth-of-type(odd) > td {
92 | background-color: white;
93 | }
94 |
95 | .vscode-light .ht_master tr:nth-of-type(even) > td {
96 | background-color: #f7f7f7;
97 | }
98 |
99 |
100 | .vscode-light .comment-row {
101 | /* background-color: #f7f7f7!important; */
102 | color: #b1b1b1;
103 | }
104 |
105 | .vscode-light .comment-row td:nth-child(n+3) {
106 | color: red;
107 | }
108 |
109 | /* context menu */
110 | .vscode-light .htContextMenu table tbody tr td {
111 | background-color: white !important;
112 | }
113 |
114 | .vscode-light .htContextMenu table tbody tr td:hover {
115 | background-color: #e4e4e4 !important;
116 | }
117 | .vscode-light .htContextMenu table tbody tr td.htDisabled:hover {
118 | background-color: white !important;
119 | }
120 |
121 | .vscode-light .htContextMenu table.htCore {
122 | /* border-color: #5c5c5c !important; */
123 | }
124 |
125 | .vscode-light .ht_master tr> td.search-result-cell {
126 | background-color: #ffff5f!important;
127 | }
128 |
129 | .vscode-light .find-widget {
130 | background-color: #f3f3f3; /*from vs code light */
131 | /* filter: brightness(80%); */
132 | }
133 |
134 | .vscode-light .find-widget .divider {
135 | background-color: #c7c7c7;
136 | }
137 |
138 | .vscode-light .find-widget .btn.active {
139 | border-color: rgba(0, 122, 204, 0);
140 | background-color:rgba(0, 144, 241, 0.2)
141 | }
142 |
143 | .vscode-light .find-widget .error-message {
144 | color: white;
145 | }
146 |
147 | .vscode-light .options-title {
148 | color: #657b83;
149 | }
150 |
151 | .vscode-light .all-options table {
152 | border: 1px solid #5e5e5e;
153 | }
154 |
155 | .vscode-light a {
156 | color: #0866ff;
157 | }
158 |
159 | .vscode-light .handsontable .htDimmed {
160 | /*color: #8c8c8c*/ /*harder to read*/
161 | color: inherit;
162 | }
163 |
164 | .vscode-light .row-col-insert-btns {
165 | border-left: 1px solid #5e5e5e;
166 | }
167 |
168 | .vscode-light .btn-with-menu-wrapper .menu {
169 | border: 1px solid var(--dropdown-border);
170 | background-color: var(--dropdown-background);
171 | }
172 |
173 |
--------------------------------------------------------------------------------
/csvEditorHtml/progressbar.ts:
--------------------------------------------------------------------------------
1 |
2 | class Progressbar {
3 |
4 | el: HTMLDivElement
5 |
6 | constructor(public id: string) {
7 |
8 | this.el = document.getElementById(id) as HTMLDivElement
9 |
10 | if (!this.el) {
11 | throw new Error(`could not find element with id ${id}`)
12 | }
13 | }
14 |
15 | setValue(percentage: number) {
16 | this.el.style.right = `${100-percentage}%`
17 | }
18 |
19 | show() {
20 | this.el.style.display = 'block'
21 | }
22 |
23 | hide() {
24 | this.el.style.display = 'none'
25 | }
26 |
27 | }
--------------------------------------------------------------------------------
/csvEditorHtml/settingsOverwrite.css:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | this file is used to apply extension settings via css variables
4 | */
5 |
6 | html {
7 | --extension-font-size: 16px;
8 | --hot-font-size-add-modifier: 1;
9 | }
10 |
11 | /* use font size from editor */
12 |
13 | body.vs-code-settings-font-size {
14 | font-size: calc(1 * var(--vscode-editor-font-size));
15 | }
16 |
17 | body.vs-code-settings-font-size .handsontable td {
18 | font-size: calc(var(--vscode-editor-font-size) + var(--hot-font-size-add-modifier));
19 | height: calc(var(--vscode-editor-font-size) + var(--hot-font-size-add-modifier) + 6px);
20 | line-height: calc(var(--vscode-editor-font-size) + var(--hot-font-size-add-modifier) + 5px);
21 | }
22 | body.vs-code-settings-font-size .handsontableInputHolder textarea {
23 | font-size: calc(var(--vscode-editor-font-size) + var(--hot-font-size-add-modifier));
24 | height: calc(var(--vscode-editor-font-size) + var(--hot-font-size-add-modifier) + 6px);
25 | line-height: calc(var(--vscode-editor-font-size) + var(--hot-font-size-add-modifier) + 5px);
26 | }
27 |
28 | body.vs-code-settings-font-size .input {
29 | font-size: calc(1 * var(--vscode-editor-font-size));
30 | }
31 |
32 | body.vs-code-settings-font-size .button {
33 | font-size: calc(1 * var(--vscode-editor-font-size));
34 | }
35 |
36 | body.vs-code-settings-font-size .switch[type=checkbox]+label {
37 | font-size: calc(1 * var(--vscode-editor-font-size));
38 | padding-left: calc(4 * var(--vscode-editor-font-size));
39 | padding-top: 0;
40 | }
41 |
42 | body.vs-code-settings-font-size .switch[type=checkbox]+label span {
43 | /* margin-left: calc(1 * var(--vscode-editor-font-size)); */
44 | }
45 |
46 | body.vs-code-settings-font-size .switch[type=checkbox]+label::before, .switch[type=checkbox]+label:before {
47 | height: calc(1.3 * var(--vscode-editor-font-size));
48 | width: calc(3 * var(--vscode-editor-font-size));
49 | }
50 |
51 | body.vs-code-settings-font-size .switch[type=checkbox]+label::after, .switch[type=checkbox]+label:after {
52 | height: calc(1 * var(--vscode-editor-font-size));
53 | width: calc(1 * var(--vscode-editor-font-size));
54 | top: calc(0.2 * var(--vscode-editor-font-size));
55 | }
56 |
57 | body.vs-code-settings-font-size .textarea {
58 | font-size: calc(1 * var(--vscode-editor-font-size));
59 | }
60 |
61 | /* END use font size from editor */
62 |
63 | /* use font size from extension settings */
64 |
65 | body.extension-settings-font-size {
66 | font-size: calc(1 * var(--extension-font-size));
67 | }
68 |
69 | body.extension-settings-font-size .handsontable td {
70 | font-size: calc(var(--extension-font-size) + var(--hot-font-size-add-modifier));
71 | height: calc(var(--extension-font-size) + var(--hot-font-size-add-modifier) + 6px);
72 | line-height: calc(var(--extension-font-size) + var(--hot-font-size-add-modifier) + 5px);
73 | }
74 | body.extension-settings-font-size .handsontableInputHolder textarea {
75 | font-size: calc(var(--extension-font-size) + var(--hot-font-size-add-modifier));
76 | height: calc(var(--extension-font-size) + var(--hot-font-size-add-modifier) + 6px);
77 | line-height: calc(var(--extension-font-size) + var(--hot-font-size-add-modifier) + 5px);
78 | }
79 |
80 | body.extension-settings-font-size .input {
81 | font-size: calc(1 * var(--extension-font-size));
82 | }
83 |
84 | body.extension-settings-font-size .button {
85 | font-size: calc(1 * var(--extension-font-size));
86 | }
87 |
88 | body.extension-settings-font-size .switch[type=checkbox]+label {
89 | font-size: calc(1 * var(--extension-font-size));
90 | padding-left: calc(4 * var(--extension-font-size));
91 | padding-top: 0;
92 | }
93 |
94 | body.extension-settings-font-size .switch[type=checkbox]+label span {
95 | /* margin-left: calc(1 * var(--extension-font-size)); */
96 | }
97 |
98 | body.extension-settings-font-size .switch[type=checkbox]+label::before, .switch[type=checkbox]+label:before {
99 | height: calc(1.3 * var(--extension-font-size));
100 | width: calc(3 * var(--extension-font-size));
101 | }
102 |
103 | body.extension-settings-font-size .switch[type=checkbox]+label::after, .switch[type=checkbox]+label:after {
104 | height: calc(1 * var(--extension-font-size));
105 | width: calc(1 * var(--extension-font-size));
106 | top: calc(0.2 * var(--extension-font-size));
107 | }
108 | body.extension-settings-font-size .textarea {
109 | font-size: calc(1 * var(--extension-font-size));
110 | }
111 |
112 | /* END use font size from extension settings */
--------------------------------------------------------------------------------
/csvEditorHtml/test/init.ts:
--------------------------------------------------------------------------------
1 |
2 | window.vscode = null
3 | window.papaCsv = window.Papa
4 |
5 | const defaultInitialVars = {
6 | isWatchingSourceFile: false,
7 | sourceFileCursorLineIndex: null,
8 | sourceFileCursorColumnIndex: null,
9 | isCursorPosAfterLastColumn: false,
10 | openTableAndSelectCellAtCursorPos: 'initialOnly_correctRowAlwaysFirstColumn',
11 | os: 'web',
12 | }
13 | window.initialVars = defaultInitialVars
14 |
15 | let defaultCsvReadOptions: CsvReadOptions = {
16 | header: false, //always use false to get an array of arrays
17 | comments: '#',
18 | delimiter: '', //auto detect
19 | delimitersToGuess: [',', '\t', '|', ';',
20 | String.fromCharCode(30), //Papa.RECORD_SEP // \u001e" // INFORMATION SEPARATOR TWO
21 | String.fromCharCode(31), //Papa.UNIT_SEP // \u001f" // INFORMATION SEPARATOR ONE
22 | ],
23 | newline: '', //auto detect
24 | quoteChar: '"',
25 | escapeChar: '"',
26 | skipEmptyLines: true,
27 | dynamicTyping: false,
28 | _hasHeader: false,
29 | }
30 | window.defaultCsvReadOptions = defaultCsvReadOptions
31 |
32 |
33 | //this gets overwritten with the real configuration in setCsvWriteOptionsInitial
34 | let defaultCsvWriteOptions: CsvWriteOptions = {
35 | header: false,
36 | comments: '#',
37 | delimiter: '', //'' = use from input, will be set from empty to string when exporting (or earlier)
38 | newline: '', //set by editor
39 | quoteChar: '"',
40 | escapeChar: '"',
41 | quoteAllFields: false,
42 | quoteEmptyOrNullFields: false,
43 | }
44 | window.defaultCsvWriteOptions = defaultCsvWriteOptions
45 |
46 |
47 | //do not use original type here, else we get some error that modules are not found...
48 | window.knownNumberStylesMap = {
49 | "en": {
50 | key: 'en',
51 | /**
52 | * this allows:
53 | * 0(000)
54 | * 0(000).0(000)
55 | * .0(000)
56 | * all repeated with - in front (negative numbers)
57 | * all repeated with e0(000) | e+0(000) | e-0(000)
58 | */
59 | regex: /-?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?/,
60 | regexStartToEnd: /^-?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/,
61 | thousandSeparator: /(\,| )/gm,
62 | thousandSeparatorReplaceRegex: /((\,| )\d{3})+/gm
63 | },
64 | "non-en": {
65 | key: 'non-en',
66 | /**
67 | * this allows:
68 | * 0(000)
69 | * 0(000),0(000)
70 | * ,0(000)
71 | * all repeated with - in front (negative numbers)
72 | * all repeated with e0(000) | e+0(000) | e-0(000)
73 | */
74 | regex: /-?(\d+(\,\d*)?|\,\d+)(e[+-]?\d+)?/,
75 | regexStartToEnd: /^-?(\d+(\,\d*)?|\,\d+)(e[+-]?\d+)?$/,
76 | thousandSeparator: /(\.| )/gm,
77 | thousandSeparatorReplaceRegex: /((\.| )\d{3})+/gm
78 | }
79 | }
80 |
81 | window.newColumnQuoteInformationIsQuoted = false
82 |
83 |
84 | //ui mocks
85 |
86 | window.newlineSameSsInputOptionText = `Same as input`
87 | window.readDelimiterTooltipText = "Empty to auto detect"
88 |
89 | window.newlineSameSsInputOption = {
90 | innerText: '',
91 | }
92 | window.readDelimiterTooltip = {
93 | setAttribute() {
94 | }
95 | }
96 |
97 | window._setHasUnsavedChangesUiIndicator = function () {
98 | }
--------------------------------------------------------------------------------
/csvEditorHtml/test/suite/autoFill/altKey.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, test, suite } from 'vitest'
2 |
3 |
4 | suite("alt key pressed tests", () => {
5 |
6 | // special test
7 | test('holding alt will copy only (uses handsontable default auto fill func)', async function () {
8 | let result = customAutoFillFunc(['1', '2', '3'], 1, true, { altKey: true } as MouseEvent)
9 | expect(result).toEqual([])
10 | })
11 |
12 | })
13 |
14 |
--------------------------------------------------------------------------------
/csvEditorHtml/test/suite/autoFill/endsWithFloat.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, test, suite } from 'vitest'
2 | import { AutoFillTestData, AutoFillTestSuit } from './types'
3 |
4 |
5 | let tests_containsNumbersInts_StartsWithNumber_singleGroup: AutoFillTestData[] = [
6 | {
7 | name: 'ends with number 0, 1 cell, 1 target count',
8 | data: ['test -5.3'],
9 | targetCount: 1,
10 | isNormalDirection: true,
11 | expected: ['test -4.3']
12 | },
13 | {
14 | name: 'ends with number 0, 1 cell, 1 target count',
15 | data: ['test 0.7'],
16 | targetCount: 1,
17 | isNormalDirection: true,
18 | expected: ['test 1.7']
19 | },
20 | {
21 | name: 'ends with number 99, 1 cell, 1 target count',
22 | data: ['test 99.2'],
23 | targetCount: 1,
24 | isNormalDirection: true,
25 | expected: ['test 100.2']
26 | },
27 | {
28 | name: 'ends with number 1, 1 cell, 1 target count',
29 | data: ['test 1.9'],
30 | targetCount: 1,
31 | isNormalDirection: true,
32 | expected: ['test 2.9']
33 | },
34 | {
35 | name: 'ends with number 1, 1 cell, 2 target count',
36 | data: ['test 1.8'],
37 | targetCount: 2,
38 | isNormalDirection: true,
39 | expected: ['test 2.8', 'test 3.8']
40 | },
41 | {
42 | name: 'ends with number 1, 1 cell, 3 target count',
43 | data: ['test 1.6'],
44 | targetCount: 3,
45 | isNormalDirection: true,
46 | expected: ['test 2.6', 'test 3.6', 'test 4.6']
47 | },
48 |
49 | {
50 | name: 'ends with number 1, contains 2 numbers, 1 cell, 3 target count',
51 | data: ['test 2.1 test 1.1'],
52 | targetCount: 3,
53 | isNormalDirection: true,
54 | expected: ['test 2.1 test 2.1', 'test 2.1 test 3.1', 'test 2.1 test 4.1']
55 | },
56 |
57 |
58 |
59 | //--- other direction
60 | {
61 | name: 'ends with number -5, 1 cell, 1 target count (other dir)',
62 | data: ['test -5.5'],
63 | targetCount: 1,
64 | isNormalDirection: false,
65 | expected: ['test -6.5']
66 | },
67 | {
68 | name: 'ends with number 0, 1 cell, 1 target count (other dir)',
69 | data: ['test 0.4'],
70 | targetCount: 1,
71 | isNormalDirection: false,
72 | expected: ['test -0.6']
73 | },
74 | {
75 | name: 'ends with number 99, 1 cell, 1 target count (other dir) [trailing zeros are removed]',
76 | data: ['test 99.0'],
77 | targetCount: 1,
78 | isNormalDirection: false,
79 | expected: ['test 98']
80 | },
81 | {
82 | name: 'ends with number 1, 1 cell, 1 target count (other dir) [trailing zeros are removed]',
83 | data: ['test 1.0'],
84 | targetCount: 1,
85 | isNormalDirection: false,
86 | expected: ['test 0']
87 | },
88 | {
89 | name: 'ends with number 1, 1 cell, 2 target count (other dir)',
90 | data: ['test 1.1'],
91 | targetCount: 2,
92 | isNormalDirection: false,
93 | expected: ['test 0.1', 'test -0.9'].reverse()
94 | },
95 | {
96 | name: 'ends with number 1, 1 cell, 3 target count (other dir)',
97 | data: ['test 1.8'],
98 | targetCount: 3,
99 | isNormalDirection: false,
100 | expected: ['test 0.8', 'test -0.2', 'test -1.2'].reverse()
101 | },
102 |
103 | {
104 | name: 'ends with number 1, contains 2 numbers, 1 cell, 3 target count (other dir)',
105 | data: ['test 2 test 1.3'],
106 | targetCount: 3,
107 | isNormalDirection: false,
108 | expected: ['test 2 test 0.3', 'test 2 test -0.7', 'test 2 test -1.7'].reverse()
109 | },
110 |
111 | ]
112 |
113 |
114 | let tests_containsNumbersInts_StartsWithNumber_multiCells: AutoFillTestData[] = [
115 | {
116 | name: '2 consecutive numbered cells [0, 1], contains number does only constant delta (1 here)',
117 | data: ['test 0.1', 'test 1.1'],
118 | targetCount: 1,
119 | isNormalDirection: true,
120 | expected: ['test 2.1']
121 | },
122 | {
123 | name: '2 consecutive numbered cells [7, 8], contains number does only constant delta (1 here)',
124 | data: ['test 7.15', 'test 8.15'],
125 | targetCount: 3,
126 | isNormalDirection: true,
127 | expected: ['test 9.15', 'test 10.15', 'test 11.15']
128 | },
129 | {
130 | name: '3 consecutive numbered cells [7, 8, 9], contains number does only constant delta (1 here)',
131 | data: ['test 7.99', 'test 8.99', 'test 9.99'],
132 | targetCount: 3,
133 | isNormalDirection: true,
134 | expected: ['test 10.99', 'test 11.99', 'test 12.99']
135 | },
136 | {
137 | name: '3 not consecutive numbered cells [7, 8, 9], contains number does only constant delta (2 here)',
138 | data: ['test 7.3', 'test 9.3', 'test 11.3'],
139 | targetCount: 3,
140 | isNormalDirection: true,
141 | expected: ['test 13.3', 'test 15.3', 'test 17.3']
142 | },
143 |
144 | {
145 | name: '3 not consecutive numbered cells [7, 9, 12], defaults to copy only',
146 | data: ['test 7.56', 'test 9.56', 'test 12.56'],
147 | targetCount: 7,
148 | isNormalDirection: true,
149 | expected: ['test 7.56', 'test 9.56', 'test 12.56', 'test 7.56', 'test 9.56', 'test 12.56', 'test 7.56']
150 | },
151 | {
152 | name: '3 not consecutive numbered cells [7, 9, 12, 15] (delta 2, delta 3), defaults to copy only',
153 | data: ['test 7.0123456789', 'test 9.0123456789', 'test 12.0123456789', 'test 15.0123456789', 'test 18.0123456789'],
154 | targetCount: 5,
155 | isNormalDirection: true,
156 | expected: ['test 7.0123456789', 'test 9.0123456789', 'test 12.0123456789', 'test 15.0123456789', 'test 18.0123456789'],
157 | },
158 |
159 | //--- other direction
160 |
161 | {
162 | name: '2 consecutive numbered cells [0, 1], contains number does only constant delta (1 here) (other dir)',
163 | data: ['test 0.3', 'test 1.3'],
164 | targetCount: 1,
165 | isNormalDirection: false,
166 | expected: ['test -0.7']
167 | },
168 | {
169 | name: '2 consecutive numbered cells [7, 8], contains number does only constant delta (1 here) (other dir)',
170 | data: ['test 7.7', 'test 8.7'],
171 | targetCount: 3,
172 | isNormalDirection: false,
173 | expected: ['test 6.7', 'test 5.7', 'test 4.7'].reverse()
174 | },
175 | {
176 | name: '3 consecutive numbered cells [7, 8, 9], contains number does only constant delta (1 here) (other dir)',
177 | data: ['test 7.1', 'test 8.1', 'test 9.1'],
178 | targetCount: 3,
179 | isNormalDirection: false,
180 | expected: ['test 6.1', 'test 5.1', 'test 4.1'].reverse()
181 | },
182 | {
183 | name: '3 not consecutive numbered cells [7, 8, 9], contains number does only constant delta (2 here) (other dir)',
184 | data: ['test 7.1', 'test 9.1', 'test 11.1'],
185 | targetCount: 3,
186 | isNormalDirection: false,
187 | expected: ['test 5.1', 'test 3.1', 'test 1.1'].reverse()
188 | },
189 |
190 | {
191 | name: '3 not consecutive numbered cells [7, 9, 12], defaults to copy only (other dir)',
192 | data: ['test 7.1', 'test 9.1', 'test 12.1'],
193 | targetCount: 7,
194 | isNormalDirection: false,
195 | expected: ['test 12.1', 'test 9.1', 'test 7.1', 'test 12.1', 'test 9.1', 'test 7.1', 'test 12.1'].reverse()
196 | },
197 | {
198 | name: '3 not consecutive numbered cells [7, 9, 12, 15] (delta 2, delta 3), defaults to copy only',
199 | data: ['test 7.1', 'test 9.1', 'test 12.1', 'test 15.1', 'test 18.1'],
200 | targetCount: 5,
201 | isNormalDirection: false,
202 | expected: ['test 18.1', 'test 15.1', 'test 12.1', 'test 9.1', 'test 7.1'].reverse()
203 | },
204 |
205 | ]
206 |
207 | let tests_containsNumbersInts_EndsWithNumber_special: AutoFillTestData[] = [
208 | {
209 | name: 'ends with float precision test 1 (big properly configured, at least 20 fract digits)',
210 | data: ['xyz abc 1.01234567890123456789'],
211 | targetCount: 1,
212 | isNormalDirection: true,
213 | expected: ['xyz abc 2.01234567890123456789']
214 | },
215 | {
216 | name: 'ends with float precision test -1 (big properly configured, at least 20 fract digits)',
217 | data: ['xyz abc -1.01234567890123456789'],
218 | targetCount: 1,
219 | isNormalDirection: true,
220 | expected: ['xyz abc -0.01234567890123456789']
221 | },
222 | {
223 | name: 'ends with float large values test 5 (big properly configured, at least 20 fract digits)',
224 | //unsigned long: 4,294,967,295 -> 4294967295 -> just a bit more
225 | data: ['xyz abc 5294967295.01234567890123456789'],
226 | targetCount: 1,
227 | isNormalDirection: true,
228 | expected: ['xyz abc 5294967296.01234567890123456789']
229 | },
230 | {
231 | name: 'ends with float large values test -5 (big properly configured, at least 20 fract digits)',
232 | //unsigned long: 4,294,967,295 -> 4294967295
233 | data: ['xyz abc -5294967295.01234567890123456789'],
234 | targetCount: 1,
235 | isNormalDirection: true,
236 | expected: ['xyz abc -5294967294.01234567890123456789']
237 | },
238 |
239 | {
240 | name: 'ends with float precision test 1 (big properly configured, at least 20 fract digits)',
241 | data: ['xyz abc 1.0123456789012345678913333333334000000000000000000000000000000000000000000000000000000000004564560198'],
242 | targetCount: 1,
243 | isNormalDirection: true,
244 | expected: ['xyz abc 2.0123456789012345678913333333334000000000000000000000000000000000000000000000000000000000004564560198']
245 | },
246 | {
247 | name: 'ends with float precision test 1 (big properly configured, at least 20 fract digits)',
248 | //100 digits . 100 digits
249 | data: ['xyz abc 1123456789012345678913333333334000000000000000000000000000000000000000000000000000000000004564560198.1123456789012345678913333333334000000000000000000000000000000000000000000000000000000000004564560198'],
250 | targetCount: 1,
251 | isNormalDirection: true,
252 | expected: ['xyz abc 1123456789012345678913333333334000000000000000000000000000000000000000000000000000000000004564560199.1123456789012345678913333333334000000000000000000000000000000000000000000000000000000000004564560198']
253 | },
254 | ]
255 |
256 | let allTests: AutoFillTestSuit[] = [
257 | {
258 | name: 'ends with number tests, single group',
259 | tests: tests_containsNumbersInts_StartsWithNumber_singleGroup
260 | },
261 | {
262 | name: 'ends with number tests, multiple groups',
263 | tests: tests_containsNumbersInts_StartsWithNumber_multiCells
264 | },
265 | {
266 | name: 'ends with number tests, special',
267 | tests: tests_containsNumbersInts_EndsWithNumber_special
268 | },
269 | ]
270 |
271 |
272 | for (let i = 0; i < allTests.length; i++) {
273 | const testSuit = allTests[i]
274 |
275 | suite(testSuit.name, () => {
276 |
277 | for (let j = 0; j < testSuit.tests.length; j++) {
278 | const testCase = testSuit.tests[j]
279 |
280 | test(testCase.name, () => {
281 | let result = customAutoFillFunc(
282 | testCase.data,
283 | testCase.targetCount,
284 | testCase.isNormalDirection,
285 | { altKey: false } as MouseEvent
286 | )
287 | expect(result).toEqual(testCase.expected)
288 | })
289 |
290 | }
291 |
292 | })
293 |
294 | }
295 |
--------------------------------------------------------------------------------
/csvEditorHtml/test/suite/autoFill/endsWithInt.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, test, suite } from 'vitest'
2 | import { AutoFillTestData, AutoFillTestSuit } from './types'
3 |
4 |
5 | let tests_containsNumbersInts_EdnsWithNumber_singleGroup: AutoFillTestData[] = [
6 | {
7 | name: 'ends with number -5, 1 cell, 1 target count',
8 | data: ['test -5'],
9 | targetCount: 1,
10 | isNormalDirection: true,
11 | expected: ['test -4']
12 | },
13 | {
14 | name: 'ends with number 0, 1 cell, 1 target count',
15 | data: ['test 0'],
16 | targetCount: 1,
17 | isNormalDirection: true,
18 | expected: ['test 1']
19 | },
20 | {
21 | name: 'ends with number 99, 1 cell, 1 target count',
22 | data: ['test 99'],
23 | targetCount: 1,
24 | isNormalDirection: true,
25 | expected: ['test 100']
26 | },
27 | {
28 | name: 'ends with number 1, 1 cell, 1 target count',
29 | data: ['test 1'],
30 | targetCount: 1,
31 | isNormalDirection: true,
32 | expected: ['test 2']
33 | },
34 | {
35 | name: 'ends with number 1, 1 cell, 2 target count',
36 | data: ['test 1'],
37 | targetCount: 2,
38 | isNormalDirection: true,
39 | expected: ['test 2', 'test 3']
40 | },
41 | {
42 | name: 'ends with number 1, 1 cell, 3 target count',
43 | data: ['test 1'],
44 | targetCount: 3,
45 | isNormalDirection: true,
46 | expected: ['test 2', 'test 3', 'test 4']
47 | },
48 |
49 | {
50 | name: 'ends with number 1, contains 2 numbers, 1 cell, 3 target count',
51 | data: ['test 1 test 2'],
52 | targetCount: 3,
53 | isNormalDirection: true,
54 | expected: ['test 1 test 3', 'test 1 test 4', 'test 1 test 5']
55 | },
56 |
57 | // {
58 | // name: 'ends with number has priority, 1 cell, 3 target count', //start has priority
59 | // data: ['2 test 3'],
60 | // targetCount: 3,
61 | // isNormalDirection: true,
62 | // expected: ['3 test 3', '4 test 3', '5 test 3']
63 | // },
64 |
65 | //--- other direction
66 | {
67 | name: 'ends with number -5, 1 cell, 1 target count (other dir)',
68 | data: ['test -5'],
69 | targetCount: 1,
70 | isNormalDirection: false,
71 | expected: ['test -6']
72 | },
73 | {
74 | name: 'ends with number 0, 1 cell, 1 target count (other dir)',
75 | data: ['test 0'],
76 | targetCount: 1,
77 | isNormalDirection: false,
78 | expected: ['test -1']
79 | },
80 | {
81 | name: 'ends with number 99, 1 cell, 1 target count (other dir)',
82 | data: ['test 99'],
83 | targetCount: 1,
84 | isNormalDirection: false,
85 | expected: ['test 98']
86 | },
87 | {
88 | name: 'ends with number 1, 1 cell, 1 target count (other dir)',
89 | data: ['test 1'],
90 | targetCount: 1,
91 | isNormalDirection: false,
92 | expected: ['test 0']
93 | },
94 | {
95 | name: 'ends with number 1, 1 cell, 2 target count (other dir)',
96 | data: ['test 1'],
97 | targetCount: 2,
98 | isNormalDirection: false,
99 | expected: ['test 0', 'test -1'].reverse()
100 | },
101 | {
102 | name: 'ends with number 1, 1 cell, 3 target count (other dir)',
103 | data: ['test 1'],
104 | targetCount: 3,
105 | isNormalDirection: false,
106 | expected: ['test 0', 'test -1', 'test -2'].reverse()
107 | },
108 |
109 | {
110 | name: 'ends with number 1, contains 2 numbers, 1 cell, 3 target count (other dir)',
111 | data: ['test 1 test 2'],
112 | targetCount: 3,
113 | isNormalDirection: false,
114 | expected: ['test 1 test 1', 'test 1 test 0', 'test 1 test -1'].reverse()
115 | },
116 | ]
117 |
118 | //TODO
119 | let tests_containsNumbersInts_EndsWithNumber_multiCells: AutoFillTestData[] = [
120 | {
121 | name: '2 consecutive numbered cells [0, 1], contains number does only constant delta (1 here)',
122 | data: ['test 0', 'test 1'],
123 | targetCount: 1,
124 | isNormalDirection: true,
125 | expected: ['test 2']
126 | },
127 | {
128 | name: '2 consecutive numbered cells [7, 8], contains number does only constant delta (1 here)',
129 | data: ['test 7', 'test 8'],
130 | targetCount: 3,
131 | isNormalDirection: true,
132 | expected: ['test 9', 'test 10', 'test 11']
133 | },
134 | {
135 | name: '3 consecutive numbered cells [7, 8, 9], contains number does only constant delta (1 here)',
136 | data: ['test 7', 'test 8', 'test 9'],
137 | targetCount: 3,
138 | isNormalDirection: true,
139 | expected: ['test 10', 'test 11', 'test 12']
140 | },
141 | {
142 | name: '3 not consecutive numbered cells [7, 8, 9], contains number does only constant delta (2 here)',
143 | data: ['test 7', 'test 9', 'test 11'],
144 | targetCount: 3,
145 | isNormalDirection: true,
146 | expected: ['test 13', 'test 15', 'test 17']
147 | },
148 |
149 | {
150 | name: '3 not consecutive numbered cells [7, 9, 12], defaults to copy only',
151 | data: ['test 7', 'test 9', 'test 12'],
152 | targetCount: 7,
153 | isNormalDirection: true,
154 | expected: ['test 7', 'test 9', 'test 12', 'test 7', 'test 9', 'test 12', 'test 7']
155 | },
156 | {
157 | name: '3 not consecutive numbered cells [7, 9, 12, 15] (delta 2, delta 3), defaults to copy only',
158 | data: ['test 7', 'test 9', 'test 12', 'test 15', 'test 18'],
159 | targetCount: 5,
160 | isNormalDirection: true,
161 | expected: ['test 7', 'test 9', 'test 12', 'test 15', 'test 18'],
162 | },
163 |
164 | //--- other direction
165 |
166 | {
167 | name: '2 consecutive numbered cells [0, 1], contains number does only constant delta (1 here) (other dir)',
168 | data: ['test 0', 'test 1'],
169 | targetCount: 1,
170 | isNormalDirection: false,
171 | expected: ['test -1']
172 | },
173 | {
174 | name: '2 consecutive numbered cells [7, 8], contains number does only constant delta (1 here) (other dir)',
175 | data: ['test 7', 'test 8'],
176 | targetCount: 3,
177 | isNormalDirection: false,
178 | expected: ['test 6', 'test 5', 'test 4'].reverse()
179 | },
180 | {
181 | name: '3 consecutive numbered cells [7, 8, 9], contains number does only constant delta (1 here) (other dir)',
182 | data: ['test 7', 'test 8', 'test 9'],
183 | targetCount: 3,
184 | isNormalDirection: false,
185 | expected: ['test 6', 'test 5', 'test 4'].reverse()
186 | },
187 | {
188 | name: '3 not consecutive numbered cells [7, 8, 9], contains number does only constant delta (2 here) (other dir)',
189 | data: ['test 7', 'test 9', 'test 11'],
190 | targetCount: 3,
191 | isNormalDirection: false,
192 | expected: ['test 5', 'test 3', 'test 1'].reverse()
193 | },
194 |
195 | {
196 | name: '3 not consecutive numbered cells [7, 9, 12], defaults to copy only (other dir)',
197 | data: ['test 7', 'test 9', 'test 12'],
198 | targetCount: 7,
199 | isNormalDirection: false,
200 | expected: ['test 12', 'test 9', 'test 7', 'test 12', 'test 9', 'test 7', 'test 12'].reverse()
201 | },
202 | {
203 | name: '3 not consecutive numbered cells [7, 9, 12, 15] (delta 2, delta 3), defaults to copy only',
204 | data: ['test 7', 'test 9', 'test 12', 'test 15', 'test 18'],
205 | targetCount: 5,
206 | isNormalDirection: false,
207 | expected: ['test 18', 'test 15', 'test 12', 'test 9', 'test 7'].reverse()
208 | },
209 | ]
210 |
211 | let allTests: AutoFillTestSuit[] = [
212 | {
213 | name: 'ends with number tests, single group',
214 | tests: tests_containsNumbersInts_EdnsWithNumber_singleGroup
215 | },
216 | {
217 | name: 'ends with number tests, multiple groups',
218 | tests: tests_containsNumbersInts_EndsWithNumber_multiCells
219 | },
220 | ]
221 |
222 |
223 | for (let i = 0; i < allTests.length; i++) {
224 | const testSuit = allTests[i]
225 |
226 | suite(testSuit.name, () => {
227 |
228 | for (let j = 0; j < testSuit.tests.length; j++) {
229 | const testCase = testSuit.tests[j]
230 |
231 | test(testCase.name, () => {
232 | let result = customAutoFillFunc(
233 | testCase.data,
234 | testCase.targetCount,
235 | testCase.isNormalDirection,
236 | { altKey: false } as MouseEvent
237 | )
238 | expect(result).toEqual(testCase.expected)
239 | })
240 |
241 | }
242 |
243 | })
244 |
245 | }
246 |
--------------------------------------------------------------------------------
/csvEditorHtml/test/suite/autoFill/floats.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, test, suite } from 'vitest'
2 | import { AutoFillTestData, AutoFillTestSuit } from './types'
3 |
4 |
5 | let tests_onlyFloatsLinearRegression: AutoFillTestData[] = [
6 | {
7 | name: 'increment by 1, 1 cell, 1 target count',
8 | data: ['1.3'],
9 | targetCount: 3,
10 | isNormalDirection: true,
11 | expected: ['2.3', '3.3', '4.3']
12 | },
13 | {
14 | name: 'increment by 1, int: 5, 3 target count',
15 | data: ['5.05'],
16 | targetCount: 3,
17 | isNormalDirection: true,
18 | expected: ['6.05', '7.05', '8.05']
19 | },
20 | {
21 | name: 'increment by 1, int: -5, 3 target count',
22 | data: ['-5.9'],
23 | targetCount: 3,
24 | isNormalDirection: true,
25 | expected: ['-4.9', '-3.9', '-2.9']
26 | },
27 | {
28 | name: 'increment by 2, int: 5',
29 | data: ['5.3', '7'],
30 | targetCount: 3,
31 | isNormalDirection: true,
32 | expected: ['8.7', '10.4', '12.1']
33 | },
34 | {
35 | name: 'increment by 2, int: [1, 3], always rounded to 2 decimals (only when interpolating)',
36 | data: ['1.5555', '3.5555'],
37 | targetCount: 3,
38 | isNormalDirection: true,
39 | expected: ['5.56', '7.56', '9.56']
40 | },
41 | {
42 | name: 'real regression 1, int: [1, 3, 8], always rounded to 2 decimals (only when interpolating)',
43 | data: ['1.5', '3.3', '8.9111111111'],
44 | targetCount: 5,
45 | isNormalDirection: true,
46 | expected: ['11.99', '15.7', '19.41', '23.12', '26.83']
47 | },
48 | {
49 | name: 'floats are rounded, even when only incrementing because of interpolation (not just plain add/sub)',
50 | data: ['1.5555'],
51 | targetCount: 3,
52 | isNormalDirection: true,
53 | expected: ['2.56', '3.56', '4.56']
54 | },
55 | {
56 | name: 'real regression 2, int: [23, 67, 89, 101]',
57 | data: ['23.5', '67.1', '89', '101.1'],
58 | targetCount: 7,
59 | isNormalDirection: true,
60 | expected: ['133.85', '159.32', '184.79', '210.26', '235.73', '261.2', '286.67']
61 | },
62 | {
63 | name: 'real regression 3, int: [5, 8, -2, 8, -4]',
64 | data: ['5.3', '8.3', '-2.3', '8.3', '-4.4'],
65 | targetCount: 5,
66 | isNormalDirection: true,
67 | expected: ['-2.78', '-4.72', '-6.66', '-8.6', '-10.54']
68 | },
69 |
70 | //--- other direction
71 |
72 | {
73 | name: 'decrement by 1, 1 cell, 1 target count (other dir)',
74 | data: ['1.3'],
75 | targetCount: 3,
76 | isNormalDirection: false,
77 | expected: ['0.3', '-0.7', '-1.7'].reverse()
78 | },
79 | {
80 | name: 'decrement by 1, int: 5, 3 target count (other dir)',
81 | data: ['5.05'],
82 | targetCount: 3,
83 | isNormalDirection: false,
84 | expected: ['4.05', '3.05', '2.05'].reverse()
85 | },
86 | {
87 | name: 'decrement by 1, int: -5, 3 target count (other dir)',
88 | data: ['-5.9'],
89 | targetCount: 3,
90 | isNormalDirection: false,
91 | expected: ['-6.9', '-7.9', '-8.9'].reverse()
92 | },
93 | {
94 | name: 'decrement by 2, int: 5 (other dir)',
95 | data: ['5.3', '7'],
96 | targetCount: 3,
97 | isNormalDirection: false,
98 | expected: ['3.6', '1.9', '0.2'].reverse()
99 | },
100 | {
101 | name: 'floats are rounded, even when only incrementing because of interpolation (not just plain add/sub) (other dir)',
102 | data: ['1.5555', '3.5555'],
103 | targetCount: 3,
104 | isNormalDirection: false,
105 | expected: ['-0.44', '-2.44', '-4.44'].reverse()
106 | },
107 | {
108 | name: 'real regression 1, int: [1, 3, 8], always rounded to 2 decimals (only when interpolating) (other dir)',
109 | data: ['1.5', '3.3', '8.9111111111'],
110 | targetCount: 5,
111 | isNormalDirection: false,
112 | expected: ['-2.85', '-6.56', '-10.27', '-13.98', '-17.69'].reverse()
113 | },
114 | {
115 | name: 'no rounding when only decrementing (other dir)',
116 | data: ['2.5555'],
117 | targetCount: 3,
118 | isNormalDirection: false,
119 | expected: ['1.56', '0.56', '-0.44'].reverse()
120 | },
121 | {
122 | name: 'real regression 2, int: [23, 67, 89, 101] (other dir)',
123 | data: ['23.5', '67.1', '89', '101.1'],
124 | targetCount: 7,
125 | isNormalDirection: false,
126 | expected: ['6.5', '-18.97', '-44.44', '-69.91', '-95.38', '-120.85', '-146.32'].reverse()
127 | },
128 | {
129 | name: 'real regression 3, int: [5, 8, -2, 8, -4] (other dir)',
130 | data: ['5.3', '8.3', '-2.3', '8.3', '-4.4'],
131 | targetCount: 5,
132 | isNormalDirection: false,
133 | expected: ['8.86', '10.8', '12.74', '14.68', '16.62'].reverse()
134 | },
135 | ]
136 |
137 | let allTests: AutoFillTestSuit[] = [
138 | {
139 | name: 'test only floats linear regression',
140 | tests: tests_onlyFloatsLinearRegression
141 | },
142 | ]
143 |
144 |
145 | for (let i = 0; i < allTests.length; i++) {
146 | const testSuit = allTests[i]
147 |
148 | suite(testSuit.name, () => {
149 |
150 | for (let j = 0; j < testSuit.tests.length; j++) {
151 | const testCase = testSuit.tests[j]
152 |
153 | test(testCase.name, () => {
154 | let result = customAutoFillFunc(
155 | testCase.data,
156 | testCase.targetCount,
157 | testCase.isNormalDirection,
158 | { altKey: false } as MouseEvent
159 | )
160 | expect(result).toEqual(testCase.expected)
161 | })
162 |
163 | }
164 |
165 | })
166 |
167 | }
168 |
--------------------------------------------------------------------------------
/csvEditorHtml/test/suite/autoFill/ints.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, test, suite } from 'vitest'
2 | import { AutoFillTestData, AutoFillTestSuit } from './types'
3 |
4 |
5 | let tests_onlyIntsLinearRegression: AutoFillTestData[] = [
6 | {
7 | name: 'increment by 1, 1 cell, 1 target count',
8 | data: ['1'],
9 | targetCount: 3,
10 | isNormalDirection: true,
11 | expected: ['2', '3', '4']
12 | },
13 | {
14 | name: 'increment by 1, int: 5, 3 target count',
15 | data: ['5'],
16 | targetCount: 3,
17 | isNormalDirection: true,
18 | expected: ['6', '7', '8']
19 | },
20 | {
21 | name: 'increment by 1, int: -5, 3 target count',
22 | data: ['-5'],
23 | targetCount: 3,
24 | isNormalDirection: true,
25 | expected: ['-4', '-3', '-2']
26 | },
27 | {
28 | name: 'increment by 2, int: 5',
29 | data: ['5', '7'],
30 | targetCount: 3,
31 | isNormalDirection: true,
32 | expected: ['9', '11', '13']
33 | },
34 | {
35 | name: 'real regression 1, int: [1, 3, 8]',
36 | data: ['1', '3', '8'],
37 | targetCount: 5,
38 | isNormalDirection: true,
39 | expected: ['11', '14.5', '18', '21.5', '25']
40 | },
41 | {
42 | name: 'real regression 2, int: [23, 67, 89, 101]',
43 | data: ['23', '67', '89', '101'],
44 | targetCount: 7,
45 | isNormalDirection: true,
46 | expected: ['134', '159.6', '185.2', '210.8', '236.4', '262', '287.6']
47 | },
48 | {
49 | name: 'real regression 3, int: [5, 8, -2, 8, -4]',
50 | data: ['5', '8', '-2', '8', '-4'],
51 | targetCount: 5,
52 | isNormalDirection: true,
53 | expected: ['-2.4', '-4.2', '-6', '-7.8', '-9.6']
54 | },
55 | //--- large numbers with big.js
56 | {
57 | name: 'larger than unsigned long, 1 cell, 1 target count',
58 | //unsigned long: 4,294,967,295 -> 4294967295 -> just a bit more
59 | data: ['01234567890123456789012345678901234567890123456789'],
60 | targetCount: 1,
61 | isNormalDirection: true,
62 | expected: ['1234567890123456789012345678901234567890123456790']
63 | },
64 | {
65 | name: 'larger than unsigned long, 1 cell, 1 target count (end is same as [1, 3, 8])',
66 | //unsigned long: 4,294,967,295 -> 4294967295 -> just a bit more
67 | //end is same as [1, 3, 8]
68 | data: ['12345678901234567890123456789012345678901234567891', '12345678901234567890123456789012345678901234567893', '12345678901234567890123456789012345678901234567898'],
69 | targetCount: 2,
70 | isNormalDirection: true,
71 | expected: ['12345678901234567890123456789012345678901234567901', '12345678901234567890123456789012345678901234567904.5']
72 | },
73 |
74 | //--- other direction
75 |
76 | {
77 | name: 'decrement by 1, 1 cell, 1 target count',
78 | data: ['1'],
79 | targetCount: 3,
80 | isNormalDirection: false,
81 | expected: ['0', '-1', '-2'].reverse()
82 | },
83 | {
84 | name: 'decrement by 1, int: 5, 3 target count',
85 | data: ['5'],
86 | targetCount: 3,
87 | isNormalDirection: false,
88 | expected: ['4', '3', '2'].reverse()
89 | },
90 | {
91 | name: 'decrement by 1, int: -5, 3 target count',
92 | data: ['-5'],
93 | targetCount: 3,
94 | isNormalDirection: false,
95 | expected: ['-6', '-7', '-8'].reverse()
96 | },
97 | {
98 | name: 'decrement by 2, int: 5',
99 | data: ['5', '7'],
100 | targetCount: 3,
101 | isNormalDirection: false,
102 | expected: ['3', '1', '-1'].reverse()
103 | },
104 | {
105 | name: 'real regression 1, int: [1, 3, 8] (other dir)',
106 | data: ['1', '3', '8'],
107 | targetCount: 5,
108 | isNormalDirection: false,
109 | expected: ['-3', '-6.5', '-10', '-13.5', '-17'].reverse()
110 | },
111 | {
112 | name: 'real regression 2, int: [23, 67, 89, 101] (other dir)',
113 | data: ['23', '67', '89', '101'],
114 | targetCount: 7,
115 | isNormalDirection: false,
116 | expected: ['6', '-19.6', '-45.2', '-70.8', '-96.4', '-122', '-147.6'].reverse()
117 | },
118 | {
119 | name: 'real regression 3, int: [5, 8, -2, 8, -4] (other dir)',
120 | data: ['5', '8', '-2', '8', '-4'],
121 | targetCount: 5,
122 | isNormalDirection: false,
123 | expected: ['8.4', '10.2', '12', '13.8', '15.6'].reverse()
124 | },
125 | //--- large numbers with big.js
126 | {
127 | name: 'larger than unsigned long, 1 cell, 1 target count (other dir)',
128 | //unsigned long: 4,294,967,295 -> 4294967295 -> just a bit more
129 | data: ['01234567890123456789012345678901234567890123456789'],
130 | targetCount: 1,
131 | isNormalDirection: false,
132 | expected: ['1234567890123456789012345678901234567890123456788']
133 | },
134 | {
135 | name: 'larger than unsigned long, 1 cell, 1 target count (end is same as [1, 3, 8]) (other dir)',
136 | //unsigned long: 4,294,967,295 -> 4294967295 -> just a bit more
137 | //end is same as [1, 3, 8]
138 | data: ['12345678901234567890123456789012345678901234567891', '12345678901234567890123456789012345678901234567893', '12345678901234567890123456789012345678901234567898'],
139 | targetCount: 2,
140 | isNormalDirection: false,
141 | expected: ['12345678901234567890123456789012345678901234567887', '12345678901234567890123456789012345678901234567883.5'].reverse()
142 | },
143 | ]
144 |
145 | let allTests: AutoFillTestSuit[] = [
146 | {
147 | name: 'test only ints linear regression',
148 | tests: tests_onlyIntsLinearRegression
149 | },
150 | ]
151 |
152 |
153 | for (let i = 0; i < allTests.length; i++) {
154 | const testSuit = allTests[i]
155 |
156 | suite(testSuit.name, () => {
157 |
158 | for (let j = 0; j < testSuit.tests.length; j++) {
159 | const testCase = testSuit.tests[j]
160 |
161 | test(testCase.name, () => {
162 | let result = customAutoFillFunc(
163 | testCase.data,
164 | testCase.targetCount,
165 | testCase.isNormalDirection,
166 | { altKey: false } as MouseEvent
167 | )
168 | expect(result).toEqual(testCase.expected)
169 | })
170 |
171 | }
172 |
173 | })
174 |
175 | }
176 |
--------------------------------------------------------------------------------
/csvEditorHtml/test/suite/autoFill/justCopy.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, test, suite } from 'vitest'
2 | import { AutoFillTestData, AutoFillTestSuit } from './types'
3 |
4 |
5 | let tests_justCopy: AutoFillTestData[] = [
6 | {
7 | name: 'just copy, 1 cell, 1 target',
8 | data: ['test'],
9 | targetCount: 1,
10 | isNormalDirection: true,
11 | expected: ['test']
12 | },
13 | {
14 | name: 'just copy, 1 cell, 3 target',
15 | data: ['test'],
16 | targetCount: 3,
17 | isNormalDirection: true,
18 | expected: ['test', 'test', 'test']
19 | },
20 |
21 | {
22 | name: 'just copy, 2 cells, 1 target',
23 | data: ['test', 'xyz'],
24 | targetCount: 1,
25 | isNormalDirection: true,
26 | expected: ['test']
27 | },
28 | {
29 | name: 'just copy, 2 cells, 2 target',
30 | data: ['test', 'xyz'],
31 | targetCount: 2,
32 | isNormalDirection: true,
33 | expected: ['test', 'xyz']
34 | },
35 | {
36 | name: 'just copy, 2 cells, 3 target',
37 | data: ['test', 'xyz'],
38 | targetCount: 3,
39 | isNormalDirection: true,
40 | expected: ['test', 'xyz', 'test']
41 | },
42 | {
43 | name: 'just copy, 2 cells, 4 target',
44 | data: ['test', 'xyz'],
45 | targetCount: 4,
46 | isNormalDirection: true,
47 | expected: ['test', 'xyz', 'test', 'xyz']
48 | },
49 | {
50 | name: 'just copy, 1 cell, 1 target (ends with number)',
51 | data: ['test', 'test2'],
52 | targetCount: 2,
53 | isNormalDirection: true,
54 | expected: ['test', 'test3']
55 | },
56 | //--- other dir
57 | {
58 | name: 'just copy, 1 cell, 1 targe target (other dir)t',
59 | data: ['test'],
60 | targetCount: 1,
61 | isNormalDirection: false,
62 | expected: ['test']
63 | },
64 | {
65 | name: 'just copy, 1 cell, 3 target target (other dir)',
66 | data: ['test'],
67 | targetCount: 3,
68 | isNormalDirection: false,
69 | expected: ['test', 'test', 'test'].reverse()
70 | },
71 |
72 | {
73 | name: 'just copy, 2 cells, 1 target target (other dir)',
74 | data: ['test', 'xyz'],
75 | targetCount: 1,
76 | isNormalDirection: false,
77 | expected: ['xyz']
78 | },
79 | {
80 | name: 'just copy, 2 cells, 2 target target (other dir)',
81 | data: ['test', 'xyz'],
82 | targetCount: 2,
83 | isNormalDirection: false,
84 | expected: ['xyz', 'test'].reverse()
85 | },
86 | {
87 | name: 'just copy, 2 cells, 3 target target (other dir)',
88 | data: ['test', 'xyz'],
89 | targetCount: 3,
90 | isNormalDirection: false,
91 | expected: ['xyz', 'test', 'xyz'].reverse()
92 | },
93 | {
94 | name: 'just copy, 2 cells, 4 target (other dir)',
95 | data: ['test', 'xyz'],
96 | targetCount: 4,
97 | isNormalDirection: false,
98 | expected: ['xyz', 'test', 'xyz', 'test'].reverse()
99 | },
100 | {
101 | name: 'just copy, 1 cell, 1 target (ends with number) (other dir)',
102 | data: ['test', 'test2'],
103 | targetCount: 2,
104 | isNormalDirection: false,
105 | expected: ['test1', 'test'].reverse()
106 | },
107 | ]
108 |
109 | let allTests: AutoFillTestSuit[] = [
110 | {
111 | name: 'just copy',
112 | tests: tests_justCopy
113 | },
114 | ]
115 |
116 |
117 | for (let i = 0; i < allTests.length; i++) {
118 | const testSuit = allTests[i]
119 |
120 | suite(testSuit.name, () => {
121 |
122 | for (let j = 0; j < testSuit.tests.length; j++) {
123 | const testCase = testSuit.tests[j]
124 |
125 | test(testCase.name, () => {
126 | let result = customAutoFillFunc(
127 | testCase.data,
128 | testCase.targetCount,
129 | testCase.isNormalDirection,
130 | { altKey: false } as MouseEvent
131 | )
132 | expect(result).toEqual(testCase.expected)
133 | })
134 |
135 | }
136 |
137 | })
138 |
139 | }
140 |
--------------------------------------------------------------------------------
/csvEditorHtml/test/suite/autoFill/startsWithInt.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, test, suite } from 'vitest'
2 | import { AutoFillTestData, AutoFillTestSuit } from './types'
3 |
4 |
5 | let tests_containsNumbersInts_StartsWithNumber_singleGroup: AutoFillTestData[] = [
6 | {
7 | name: 'starts with number 0, 1 cell, 1 target count',
8 | data: ['-5 test'],
9 | targetCount: 1,
10 | isNormalDirection: true,
11 | expected: ['-4 test']
12 | },
13 | {
14 | name: 'starts with number 0, 1 cell, 1 target count',
15 | data: ['0 test'],
16 | targetCount: 1,
17 | isNormalDirection: true,
18 | expected: ['1 test']
19 | },
20 | {
21 | name: 'starts with number 99, 1 cell, 1 target count',
22 | data: ['99 test'],
23 | targetCount: 1,
24 | isNormalDirection: true,
25 | expected: ['100 test']
26 | },
27 | {
28 | name: 'starts with number 1, 1 cell, 1 target count',
29 | data: ['1 test'],
30 | targetCount: 1,
31 | isNormalDirection: true,
32 | expected: ['2 test']
33 | },
34 | {
35 | name: 'starts with number 1, 1 cell, 2 target count',
36 | data: ['1 test'],
37 | targetCount: 2,
38 | isNormalDirection: true,
39 | expected: ['2 test', '3 test']
40 | },
41 | {
42 | name: 'starts with number 1, 1 cell, 3 target count',
43 | data: ['1 test'],
44 | targetCount: 3,
45 | isNormalDirection: true,
46 | expected: ['2 test', '3 test', '4 test']
47 | },
48 |
49 | {
50 | name: 'starts with number 1, contains 2 numbers, 1 cell, 3 target count',
51 | data: ['1 test 2 test'],
52 | targetCount: 3,
53 | isNormalDirection: true,
54 | expected: ['2 test 2 test', '3 test 2 test', '4 test 2 test']
55 | },
56 |
57 | {
58 | name: 'start with number has priority, 1 cell, 3 target count',
59 | data: ['2 test 3'],
60 | targetCount: 3,
61 | isNormalDirection: true,
62 | expected: ['3 test 3', '4 test 3', '5 test 3']
63 | },
64 |
65 | //--- other direction
66 | {
67 | name: 'starts with number 0, 1 cell, 1 target count (other dir)',
68 | data: ['-5 test'],
69 | targetCount: 1,
70 | isNormalDirection: false,
71 | expected: ['-6 test']
72 | },
73 | {
74 | name: 'starts with number 0, 1 cell, 1 target count (other dir)',
75 | data: ['0 test'],
76 | targetCount: 1,
77 | isNormalDirection: false,
78 | expected: ['-1 test']
79 | },
80 | {
81 | name: 'starts with number 99, 1 cell, 1 target count (other dir)',
82 | data: ['99 test'],
83 | targetCount: 1,
84 | isNormalDirection: false,
85 | expected: ['98 test']
86 | },
87 | {
88 | name: 'starts with number 1, 1 cell, 1 target count (other dir)',
89 | data: ['1 test'],
90 | targetCount: 1,
91 | isNormalDirection: false,
92 | expected: ['0 test']
93 | },
94 | {
95 | name: 'starts with number 1, 1 cell, 2 target count (other dir)',
96 | data: ['1 test'],
97 | targetCount: 2,
98 | isNormalDirection: false,
99 | expected: ['0 test', '-1 test'].reverse()
100 | },
101 | {
102 | name: 'starts with number 1, 1 cell, 3 target count (other dir)',
103 | data: ['1 test'],
104 | targetCount: 3,
105 | isNormalDirection: false,
106 | expected: ['0 test', '-1 test', '-2 test'].reverse()
107 | },
108 |
109 | {
110 | name: 'starts with number 1, contains 2 numbers, 1 cell, 3 target count (other dir)',
111 | data: ['1 test 2 test'],
112 | targetCount: 3,
113 | isNormalDirection: false,
114 | expected: ['0 test 2 test', '-1 test 2 test', '-2 test 2 test'].reverse()
115 | },
116 |
117 | {
118 | name: 'start with number has priority, 1 cell, 3 target count (other dir)',
119 | data: ['2 test 3'],
120 | targetCount: 3,
121 | isNormalDirection: false,
122 | expected: ['1 test 3', '0 test 3', '-1 test 3'].reverse()
123 | }
124 | ]
125 |
126 |
127 | let tests_containsNumbersInts_StartsWithNumber_multiCells: AutoFillTestData[] = [
128 | {
129 | name: '2 consecutive numbered cells [0, 1], contains number does only constant delta (1 here)',
130 | data: ['0 test', '1 test'],
131 | targetCount: 1,
132 | isNormalDirection: true,
133 | expected: ['2 test']
134 | },
135 | {
136 | name: '2 consecutive numbered cells [7, 8], contains number does only constant delta (1 here)',
137 | data: ['7 test', '8 test'],
138 | targetCount: 3,
139 | isNormalDirection: true,
140 | expected: ['9 test', '10 test', '11 test']
141 | },
142 | {
143 | name: '3 consecutive numbered cells [7, 8, 9], contains number does only constant delta (1 here)',
144 | data: ['7 test', '8 test', '9 test'],
145 | targetCount: 3,
146 | isNormalDirection: true,
147 | expected: ['10 test', '11 test', '12 test']
148 | },
149 | {
150 | name: '3 not consecutive numbered cells [7, 8, 9], contains number does only constant delta (2 here)',
151 | data: ['7 test', '9 test', '11 test'],
152 | targetCount: 3,
153 | isNormalDirection: true,
154 | expected: ['13 test', '15 test', '17 test']
155 | },
156 |
157 | {
158 | name: '3 not consecutive numbered cells [7, 9, 12], defaults to copy only',
159 | data: ['7 test', '9 test', '12 test'],
160 | targetCount: 7,
161 | isNormalDirection: true,
162 | expected: ['7 test', '9 test', '12 test', '7 test', '9 test', '12 test', '7 test']
163 | },
164 | {
165 | name: '3 not consecutive numbered cells [7, 9, 12, 15] (delta 2, delta 3), defaults to copy only',
166 | data: ['7 test', '9 test', '12 test', '15 test', '18 test'],
167 | targetCount: 5,
168 | isNormalDirection: true,
169 | expected: ['7 test', '9 test', '12 test', '15 test', '18 test'],
170 | },
171 |
172 | //--- other direction
173 |
174 | {
175 | name: '2 consecutive numbered cells [0, 1], contains number does only constant delta (1 here) (other dir)',
176 | data: ['0 test', '1 test'],
177 | targetCount: 1,
178 | isNormalDirection: false,
179 | expected: ['-1 test']
180 | },
181 | {
182 | name: '2 consecutive numbered cells [7, 8], contains number does only constant delta (1 here) (other dir)',
183 | data: ['7 test', '8 test'],
184 | targetCount: 3,
185 | isNormalDirection: false,
186 | expected: ['6 test', '5 test', '4 test'].reverse()
187 | },
188 | {
189 | name: '3 consecutive numbered cells [7, 8, 9], contains number does only constant delta (1 here) (other dir)',
190 | data: ['7 test', '8 test', '9 test'],
191 | targetCount: 3,
192 | isNormalDirection: false,
193 | expected: ['6 test', '5 test', '4 test'].reverse()
194 | },
195 | {
196 | name: '3 not consecutive numbered cells [7, 8, 9], contains number does only constant delta (2 here) (other dir)',
197 | data: ['7 test', '9 test', '11 test'],
198 | targetCount: 3,
199 | isNormalDirection: false,
200 | expected: ['5 test', '3 test', '1 test'].reverse()
201 | },
202 |
203 | {
204 | name: '3 not consecutive numbered cells [7, 9, 12], defaults to copy only (other dir)',
205 | data: ['7 test', '9 test', '12 test'],
206 | targetCount: 7,
207 | isNormalDirection: false,
208 | expected: ['12 test', '9 test', '7 test', '12 test', '9 test', '7 test', '12 test'].reverse()
209 | },
210 | {
211 | name: '3 not consecutive numbered cells [7, 9, 12, 15] (delta 2, delta 3), defaults to copy only',
212 | data: ['7 test', '9 test', '12 test', '15 test', '18 test'],
213 | targetCount: 5,
214 | isNormalDirection: false,
215 | expected: ['18 test', '15 test', '12 test', '9 test', '7 test'].reverse()
216 | },
217 |
218 | ]
219 |
220 | let allTests: AutoFillTestSuit[] = [
221 | {
222 | name: 'starts with number tests, single group',
223 | tests: tests_containsNumbersInts_StartsWithNumber_singleGroup
224 | },
225 | {
226 | name: 'starts with number tests, multiple groups',
227 | tests: tests_containsNumbersInts_StartsWithNumber_multiCells
228 | },
229 | ]
230 |
231 |
232 | for (let i = 0; i < allTests.length; i++) {
233 | const testSuit = allTests[i]
234 |
235 | suite(testSuit.name, () => {
236 |
237 | for (let j = 0; j < testSuit.tests.length; j++) {
238 | const testCase = testSuit.tests[j]
239 |
240 | test(testCase.name, () => {
241 | let result = customAutoFillFunc(
242 | testCase.data,
243 | testCase.targetCount,
244 | testCase.isNormalDirection,
245 | { altKey: false } as MouseEvent
246 | )
247 | expect(result).toEqual(testCase.expected)
248 | })
249 |
250 | }
251 |
252 | })
253 |
254 | }
255 |
--------------------------------------------------------------------------------
/csvEditorHtml/test/suite/autoFill/types.ts:
--------------------------------------------------------------------------------
1 | import { expect, test, suite } from 'vitest'
2 |
3 | export type AutoFillTestSuit = {
4 | name: string
5 | tests: AutoFillTestData[]
6 | }
7 |
8 | export type AutoFillTestData = {
9 | name: string
10 | data: string[]
11 | targetCount: number
12 | /**
13 | * true: is down or right, false: is up or left (reverse)
14 | */
15 | isNormalDirection: boolean
16 | expected: string[]
17 | }
18 |
--------------------------------------------------------------------------------
/csvEditorHtml/test/suite/tryToGuessHasHeader/guessHasHeader.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, test, suite } from 'vitest'
2 |
3 | type HasHeaderTestData = {
4 | name: string,
5 | csv: string,
6 | expected: boolean
7 | }
8 |
9 | type MyTestSuit = {
10 | name: string
11 | tests: HasHeaderTestData[]
12 | }
13 |
14 |
15 | //comment, numbers
16 |
17 | let test_normal: HasHeaderTestData[] = [
18 | {
19 | name: 'normal data',
20 | csv: `
21 | UserId,FirstName,ShouldNotCount,PLangName,ParticipationCount,MaxNormalTestPoints,MaxSubmitTestPoints
22 | 513,Alaa,False,Java,1,3,2
23 | 329,Hanna,False,Java,1,3,2
24 | 588,David,False,Java,1,3,2
25 | `,
26 | expected: true,
27 | },
28 | {
29 | name: 'no header, normal data',
30 | csv: `
31 | 512,Max,False,Python,1,3,2
32 | 513,Alaa,False,Java,1,3,2
33 | 329,Hanna,False,Java,1,3,2
34 | 588,David,False,Java,1,3,2
35 | `,
36 | expected: false,
37 | },
38 | {
39 | name: 'threshold many unique header values (X,Y,Z, not part of other values in same column) -> has header',
40 | csv: `
41 | X,Y,Z,Online,M
42 | Central America and the Caribbean,Belize,Household,Offline,H
43 | Europe,Denmark,Clothes,Online,C
44 | Europe,Germany,Cosmetics,Offline,M
45 | `,
46 | expected: true,
47 | },
48 | {
49 | name: 'less than threshold many unique header values -> no header', //because Germany,Online,M is repeated
50 | csv: `
51 | X,Germany,Personal Care,Online,M
52 | Central America and the Caribbean,Belize,Household,Offline,H
53 | Europe,Denmark,Clothes,Online,C
54 | Europe,Germany,Cosmetics,Offline,M
55 | `,
56 | expected: false,
57 | },
58 | {
59 | name: 'normal data, with comment',
60 | csv: `
61 | # just a comment
62 | UserId,FirstName,ShouldNotCount,PLangName,ParticipationCount,MaxNormalTestPoints,MaxSubmitTestPoints
63 | 513,Alaa,False,Java,1,3,2
64 | 329,Hanna,False,Java,1,3,2
65 | 588,David,False,Java,1,3,2
66 | `,
67 | expected: true,
68 | },
69 | {
70 | name: 'no header, normal data',
71 | csv: `
72 | # just a comment
73 | 512,Max,False,Python,1,3,2
74 | 513,Alaa,False,Java,1,3,2
75 | 329,Hanna,False,Java,1,3,2
76 | 588,David,False,Java,1,3,2
77 | `,
78 | expected: false,
79 | },
80 | {
81 | name: 'normal data, with comment',
82 | csv: `
83 | # just a comment
84 | # another comment
85 | UserId,FirstName,ShouldNotCount,PLangName,ParticipationCount,MaxNormalTestPoints,MaxSubmitTestPoints
86 | 513,Alaa,False,Java,1,3,2
87 | 329,Hanna,False,Java,1,3,2
88 | 588,David,False,Java,1,3,2
89 | `,
90 | expected: true,
91 | },
92 | {
93 | name: 'no header, normal data',
94 | csv: `
95 | # just a comment
96 | # another comment
97 | 512,Max,False,Python,1,3,2
98 | 513,Alaa,False,Java,1,3,2
99 | 329,Hanna,False,Java,1,3,2
100 | 588,David,False,Java,1,3,2
101 | `,
102 | expected: false,
103 | },
104 |
105 | // numbers
106 | {
107 | name: 'ints in header, more than threshold many numbers in header -> no header',
108 | csv: `
109 | UserId,123,123,123
110 | 513,Alaa,False,Java,1,3,2
111 | 329,Hanna,False,Java,1,3,2
112 | 588,David,False,Java,1,3,2
113 | `,
114 | expected: false,
115 | },
116 | {
117 | name: 'floats in header',
118 | csv: `
119 | UserId,9.0,9.1,9.2
120 | 513,Alaa,False,Java,1,3,2
121 | 329,Hanna,False,Java,1,3,2
122 | 588,David,False,Java,1,3,2
123 | `,
124 | expected: false,
125 | },
126 | {
127 | name: 'floats with separators in header',
128 | csv: `
129 | UserId,"9,000.0","9,876.5","9,000,000.0"
130 | 513,Alaa,False,Java,1,3,2
131 | 329,Hanna,False,Java,1,3,2
132 | 588,David,False,Java,1,3,2
133 | `,
134 | expected: false,
135 | },
136 | {
137 | name: 'non-en floats in header',
138 | csv: `
139 | UserId,"9,0","9,1","9,2"
140 | 513,Alaa,False,Java,1,3,2
141 | 329,Hanna,False,Java,1,3,2
142 | 588,David,False,Java,1,3,2
143 | `,
144 | expected: false,
145 | },
146 | {
147 | name: 'non-enfloats with separators in header',
148 | csv: `
149 | UserId,"9.000,0","9.876,5","9.000.000,0"
150 | 513,Alaa,False,Java,1,3,2
151 | 329,Hanna,False,Java,1,3,2
152 | 588,David,False,Java,1,3,2
153 | `,
154 | expected: false,
155 | },
156 |
157 | // mixed string & number in cell --> not a single number -> text
158 |
159 | {
160 | name: 'non-enfloats with separators in header',
161 | csv: `
162 | UserId,"a 9.000,0","9.876,5","9.000.000,0"
163 | 513,Alaa,False,Java,1,3,2
164 | 329,Hanna,False,Java,1,3,2
165 | 588,David,False,Java,1,3,2
166 | `,
167 | expected: false,
168 | },
169 |
170 | ]
171 |
172 |
173 | let test_threshold: HasHeaderTestData[] = [
174 | {
175 | name: 'at least 3 columns look like header',
176 | csv: `
177 | UserId,Max,Passed,123,
178 | 513,Alaa,False,Java,1,3,2
179 | 329,Hanna,False,Java,1,3,2
180 | 588,David,False,Java,1,3,2
181 | `,
182 | expected: true,
183 | },
184 | {
185 | name: 'at least 3 columns look like numbers', //numbers are normally not in the header
186 | csv: `
187 | UserId,Max,999,999,999
188 | 513,Alaa,False,Java,1,3,2
189 | 329,Hanna,False,Java,1,3,2
190 | 588,David,False,Java,1,3,2
191 | `,
192 | expected: false,
193 | },
194 | {
195 | name: 'threshold number, threshold normal values -> numbers take precedence', //numbers are normally not in the header
196 | csv: `
197 | UserId,Max,Passed,999,999,999
198 | 513,Alaa,False,Java,1,3,2
199 | 329,Hanna,False,Java,1,3,2
200 | 588,David,False,Java,1,3,2
201 | `,
202 | expected: false,
203 | },
204 |
205 | {
206 | name: 'header with numbers (we count it as body)',
207 | csv: `
208 | UserId,123,a 123,b123
209 | 513,Alaa,False,Java,1,3,2
210 | 329,Hanna,False,Java,1,3,2
211 | 588,David,False,Java,1,3,2
212 | `,
213 | expected: true,
214 | },
215 | {
216 | name: 'header with floats (we count it as body)',
217 | csv: `
218 | UserId,Test 1, Test 2, Test 3
219 | 513,Alaa,False,Java,1,3,2
220 | 329,Hanna,False,Java,1,3,2
221 | 588,David,False,Java,1,3,2
222 | `,
223 | expected: true,
224 | },
225 |
226 | // known cell values
227 | {
228 | name: 'true/false are known values',
229 | csv: `
230 | UserId,false,true,Language,true
231 | 513,Alaa,False,Java,1,3,2
232 | 329,Hanna,False,Java,1,3,2
233 | 588,David,False,Java,1,3,2
234 | `,
235 | expected: false,
236 | },
237 | {
238 | name: 'true/false are known values but 3 normal columns',
239 | csv: `
240 | UserId,false,true,Language,true,Count
241 | 513,Alaa,False,Java,1,3,2
242 | 329,Hanna,False,Java,1,3,2
243 | 588,David,False,Java,1,3,2
244 | `,
245 | expected: true,
246 | },
247 | ]
248 |
249 | let allTests: MyTestSuit[] = [
250 | {
251 | name: 'test normal',
252 | tests: test_normal
253 | },
254 | {
255 | name: 'test threshold',
256 | tests: test_threshold
257 | },
258 | ]
259 |
260 |
261 | for (let i = 0; i < allTests.length; i++) {
262 | const testSuit = allTests[i]
263 |
264 | suite(testSuit.name, () => {
265 |
266 | for (let j = 0; j < testSuit.tests.length; j++) {
267 | const testCase = testSuit.tests[j]
268 |
269 | test(testCase.name, () => {
270 | const parsedCsv = parseCsv(testCase.csv.trim(), defaultCsvReadOptions)
271 |
272 | _normalizeDataArray(parsedCsv, defaultCsvReadOptions)
273 |
274 | window.knownNumberStylesMap = {
275 | "en": {
276 | key: 'en',
277 | /**
278 | * this allows:
279 | * 0(000)
280 | * 0(000).0(000)
281 | * .0(000)
282 | * all repeated with - in front (negative numbers)
283 | * all repeated with e0(000) | e+0(000) | e-0(000)
284 | */
285 | regex: /-?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?/,
286 | regexStartToEnd: /^-?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/,
287 | thousandSeparator: /(\,| )/gm,
288 | thousandSeparatorReplaceRegex: /((\,| )\d{3})+/gm
289 | },
290 | "non-en": {
291 | key: 'non-en',
292 | /**
293 | * this allows:
294 | * 0(000)
295 | * 0(000),0(000)
296 | * ,0(000)
297 | * all repeated with - in front (negative numbers)
298 | * all repeated with e0(000) | e+0(000) | e-0(000)
299 | */
300 | regex: /-?(\d+(\,\d*)?|\,\d+)(e[+-]?\d+)?/,
301 | regexStartToEnd: /^-?(\d+(\,\d*)?|\,\d+)(e[+-]?\d+)?$/,
302 | thousandSeparator: /(\.| )/gm,
303 | thousandSeparatorReplaceRegex: /((\.| )\d{3})+/gm
304 | }
305 | }
306 |
307 | let hasHeader = tryToGuessHasHeader(
308 | parsedCsv.data,
309 | defaultCsvReadOptions
310 | )
311 | expect(hasHeader).toEqual(testCase.expected)
312 | })
313 |
314 | }
315 |
316 | })
317 |
318 | }
319 |
--------------------------------------------------------------------------------
/csvEditorHtml/test/vitest.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vitest/config'
2 |
3 | export default defineConfig({
4 |
5 | test: {
6 | browser: {
7 | enabled: true,
8 | headless: true,
9 | screenshotFailures: false,
10 | name: 'chromium',
11 | provider: 'playwright',
12 | testerScripts: [
13 | {
14 | src: './node_modules/dayjs/dayjs.min.js',
15 | type: 'text/javascript',
16 | },
17 | {
18 | src: './node_modules/dayjs/plugin/customParseFormat.js',
19 | type: 'text/javascript',
20 | },
21 | {
22 | src: './thirdParty/big.js/big.min.js',
23 | type: 'text/javascript',
24 | },
25 | {
26 | src: './thirdParty/toFormat/toFormat.min.js',
27 | type: 'text/javascript',
28 | },
29 | {
30 | src: './thirdParty/regression/regression.min.js', //our regression uses big.js
31 | type: 'text/javascript',
32 | },
33 | {
34 | src: './thirdParty/papaparse/papaparse.min.js',
35 | type: 'text/javascript',
36 | },
37 | {
38 | src: './csvEditorHtml/util.ts',
39 | type: 'text/javascript',
40 | },
41 | {
42 | src: './csvEditorHtml/io.ts',
43 | type: 'text/javascript',
44 | },
45 | {
46 | src: './csvEditorHtml/autoFill.ts',
47 | type: 'text/javascript',
48 | },
49 | //could also be moved to init...
50 | {
51 | content: `
52 | window.numbersStyleEnRadio = {
53 | checked: true
54 | }
55 | //add toFormat to big numbers
56 | toFormat(Big)
57 | //for custom formatted dates
58 | dayjs.extend(dayjs_plugin_customParseFormat);
59 |
60 | `,
61 | type: 'text/javascript',
62 | },
63 | {
64 | src: './csvEditorHtml/test/init.ts',
65 | type: 'text/javascript',
66 | },
67 |
68 | ],
69 | },
70 | include: ['csvEditorHtml/test/**/*.test.ts'],
71 | },
72 | })
--------------------------------------------------------------------------------
/csvEditorHtml/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "none",
4 | "target": "es2021",
5 | "moduleResolution": "node",
6 | "outDir": "./out",
7 | "lib": [
8 | "ES2021", "dom"
9 | ],
10 | "sourceMap": true,
11 | /* Strict Type-Checking Option */
12 | "strict": true, /* enable all strict type-checking options */
13 | /* Additional Checks */
14 | "noUnusedLocals": true, /* Report errors on unused locals. */
15 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
16 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
17 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
18 | "skipLibCheck": true,
19 | "removeComments": true
20 | },
21 | "exclude": [
22 | "../node_modules",
23 | "../src",
24 | "out",
25 | "test"
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/docs/autoFillBehavior.md:
--------------------------------------------------------------------------------
1 | # Excel like Auto Fill Behavior
2 |
3 | I've done my best to replicate Excel's auto-fill from observations....
4 |
5 | If possible, the selected values are grouped in consecutive sequences.
6 | These groups are then used to determine the interpolations.
7 |
8 | e.g. `2,4,a` will create the groups `2,4` and `a`.
9 | Then `2,4` is used to interpolate the next values (`6,8,10,...`),
10 | while `a` ist just copies over and over
11 |
12 | e.g. `2,4,a,01.01.2024,03.01.2024` will create the groups `2,4`, `a` and `01.01.2024, 03.01.2024`
13 |
14 | Again, `2,4` is used to interpolate the next numbers,
15 | `a` is just copied
16 | `01.01.2024,03.01.2024` is used to interpolate the next dates `05.01.2024, 07.01.2024, 09.01.2024, ...`
17 |
18 |
19 | The formation of groups enables interpolation, even if heterogeneous data has been selected.
20 |
21 | However, consecutive sequences must have the same *delta* to be a correct sequence.
22 |
23 | For numbers, linear regression can always form a line, but no *model* is used for month names, numbers with text and dates.
24 |
25 |
26 | This means that if the distance between 3 month names in the sequences is different, the autofill will fall back to simply copying the sequence over and over again.
27 | Example: `jan,feb,apr` with the distances/deltas of `1,2`. If the deltas are different, it is not clear what the next value should be.
28 |
29 | Example: `jan,feb,mar` with the distances/deltas of `1,1`. All deltas are the same, so we can continue with this delta and always add `1` month.
30 |
31 | This also applies to dates when the spacing is used in days.
32 |
33 | Below is a flowchart explaining the individual steps in a bit more detail.
34 |
35 | #### Numbers
36 |
37 | The cell text is a number (no other content).
38 |
39 |
40 | The language settings are important for numbers (floats), e.g. 3.45 or 3.45
41 |
42 | For the automatic filling of numbers, the setting `numbers style` is taken from the table UI. You can find it when you open the statistics panel on the left-hand side (by clicking on the arrow next to `add row`).
43 |
44 | If you mainly work with a number style, you can change the default via the extension setting `initialNumbersStyle`.
45 |
46 | Automatic filling numbers uses [linear regression](https://en.wikipedia.org/wiki/Linear_regression) to determine the values to be filled in (Excel also uses linear regression).
47 |
48 | Two decimal places are used for Int/Float interpolation.
49 |
50 | ### Contains Number
51 |
52 | This differs from normal numbers, as here the cell text must begin or end with a number (or both).
53 |
54 | In this case, the interpolation is only a constant delta.
55 | The delta is determined by the first two cell values in the selection.
56 |
57 | e.g. `2. test, 4. test` will calculate a delta of `2`, so the next value will be `6. test`
58 |
59 | If there are different deltas in the selected cells, the auto fill function will default to copy only.
60 |
61 | e.g. `2. test, 4. test, 5. test` will calculate a delta of `2`, so the next values will be `2. test, 4. test, 5. test, 2. test, ...`
62 |
63 | If there is a number at the beginning and at the end of the cell, the number at the beginning takes precedence.
64 |
65 |
66 | #### Dates
67 |
68 | The following formats are supported/known for dates:
69 |
70 | - `YYYY-MM-DD`
71 | - `YYYY-M-DD`
72 | - `YYYY-MM-D`
73 | - `YYYY-M-D`
74 | - `DD-MM-YYYY`
75 | - `DD-M-YYYY`
76 | - `D-MM-YYYY`
77 | - `D-M-YYYY`
78 | - `DD-MM-YY`
79 | - `DD-M-YY`
80 | - `D-MM-YY`
81 | - `D-M-YY`
82 |
83 | where `YY/YYYY` stands for the year, e.g. 24/2024, `M/MM` stands for the month, e.g. 5/05 and `D/DD` stands for the day, e.g. 5/05
84 |
85 | The separator `-` can actually be one of the following: `- / .`
86 |
87 | **Yes**, there is no `MM-DD-YYYY` format!
88 |
89 | Only the first two selected data are used for the interpolation (to determine the delta).
90 |
91 |
92 | Then the diff in `days`, `months` and `years` is calculated, ignoring the other parts of the date.
93 | e.g. the diff/delta in days for `25.05.2024` - `26.07.2024` is still only 1 `day`
94 |
95 | If the diff in `days` is 0 and diff in `months` is 0, use the diff in years for interpolation.
96 | This ensures days and months will stay the same: `25.05.2024, 25.05.2026` -> `25.05.2028`
97 |
98 | If only the diff in `days` is 0, use the diff in month for interpolation.
99 | e.g. `25.05.2024, 25.07.2026` -> `25.09.2028` (delta in months: 26)
100 |
101 | In any other case, use the difference in `days`, but this time consider all parts of the date as delta for the interpolation.
102 | e.g. `01.01.2024, 02.02.2024` gives a delta of 32 `days`
103 | and the next date will be `05.03.2024`
104 |
105 | If there are more than 2 pieces of data in the group sequence, they must have the same difference/delta.
106 | If this is not the case, the data is copied again and again as a sequence by default.
107 |
108 | If the interpolation would result in an invalid date such as `30.02.20`, `dayjs` will convert the date into a valid date (here `28.02.20`).
109 |
110 | #### Month Names
111 |
112 | Only English names are supported for month names: `january|february|march|april|may|june|july|august|september|october|november|december`
113 | The first 3 letters can also be used as month names: `jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec`
114 |
115 | In order for the month names to be filled in automatically, the month name must be the only text in the cell.
116 |
117 |
118 | #### Shortcut for just coyping
119 |
120 | Similar to Excel, you can hold down the `alt` key before releasing the mouse button to copy the cell values. No interpolation is carried out.
121 |
122 | #### Differences to Excel
123 |
124 | If only 1 cell is selected, interpolation increases
125 |
126 | - `+1/-1` for numbers
127 | - next/previous month name
128 | - `+1/-1` day for dates
129 |
130 | where Excel simply copies the value
131 |
132 | There could be other differences...
133 |
134 |
135 | #### Auto Fill Flowchart
136 |
137 | If it's too small, open it from `docs/autoFillDiagram.jpg`
138 |
139 | 
--------------------------------------------------------------------------------
/docs/autoFillDiagram.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/janisdd/vscode-edit-csv/7f06cf1c718cf27dfb287ee9d2176d5381487fa4/docs/autoFillDiagram.jpg
--------------------------------------------------------------------------------
/docs/quotes.md:
--------------------------------------------------------------------------------
1 | # Some notes about quotes and related settings
2 |
3 | ### Papaparse options
4 |
5 | `quoteChar` is used to quote fields. A field has to be quoted if it contains the delimiter, `quoteChar`, `\n`, `\r` a trailing or leading space.
6 |
7 | `escapeChar` is used to escape fields. A field has to be escaped if it contains the `quoteChar`. It is escaped by prepended the `escapeChar` `quoteChar`.
8 |
9 | Example:
10 | `escapeChar: +`, `quoteChar:"`
11 | Input: `a"b`
12 |
13 | field contains the quote char -> has to be quoted --> `"a"b"`
14 | the inner quotes has to be escaped -> `"a+"b"`
15 |
16 |
17 | ### Leading or trainling spaces and quotes
18 |
19 | If we have
20 | ```
21 | a,b,c
22 | ```
23 |
24 | we expect the result to be `a`,`b`,`c`
25 |
26 | If we have
27 |
28 | ```
29 | a, b, c
30 | ```
31 |
32 | we expect the result to be `a`, ` b`, ` c`
33 | (b,c have a leading space)
34 |
35 | If we have
36 |
37 | ```
38 | a, b," c"
39 | ```
40 |
41 | we expect the same result. The quotes are option but make the space more "visible".
42 |
43 | However, if we have
44 |
45 | ```
46 | a, b, "c"
47 | ```
48 |
49 | It is not clear what to expect here...
50 |
51 | - the quotes indicate the intend to not have a leading space
52 | - the space between the `,` and the `"` indicate a space
53 |
54 | Papaparse chooses to include the leading space, resulting in `a`, ` b`, ` "c"`
55 | Probably because the csv rfc states:
56 | >spaces are considered part of a field and should not be ignored.
57 |
58 | This also means that the `"` in this field are **not** used to quote the field! (you could imageine swapping the `"` with any other character without special meaning like `@` -> `a`, ` b`, ` @c@`).
59 |
60 | The implication is that text like
61 |
62 | ```
63 | a, b, "c,"
64 | ```
65 |
66 | will not work because the delimiter inside the `"` is **not** escaped by the quotes.
67 |
68 | As there is no *correct* decision here, we are not changing the current behaviour in this case.
69 |
--------------------------------------------------------------------------------
/docs/visualAndPhysicalIndices.md:
--------------------------------------------------------------------------------
1 | # Visual and Physical Indices
2 |
3 |
4 |
5 | ### Current Mapping
6 |
7 | To get the current mapping, run
8 |
9 | ```js
10 | # rows
11 | Array(hot.countRows()).fill(0).map((p,i) => hot.toPhysicalRow(i))
12 | # columns
13 | Array(hot.countCols()).fill(0).map((p,i) => hot.toPhysicalColumn(i))
14 | ```
15 |
16 | ### Inserting a row/column befor/after uses visual indices
17 |
18 | When you insert a row/col they are inserted at the visual (displayed) location.
19 | If you sort the data, insert a row and then revert the sorting, the new row/col will stay at the inserted position.
20 |
21 | Here is why:
22 |
23 | NOTE: the physical and visual index handling is messed up in handsontable...
24 |
25 | e.g. in core.js > alter(action, index, ...) the index is expected to be a visual index
26 | for 'insert_row' it calls 'datamap.createRow(index, ...)' (the index is not changed)
27 | the docs of DataMap.prototype.createRow = function(index, ...) says it's expecting a physical index!
28 | this is true because spliceData(index, ...) is called, which modified the source data directly at the given index (so it must be physical!)
29 | after that the afterCreateRow hooks are run which updates the visual <-> physical index mapping (the index is still not changed)
30 | the mapping is stored in the manualRowMove.js plugin, the hook 'onAfterCreateRow' is triggered with the index and 'this.rowsMapper.shiftItems(index, amount)' is called
31 | shiftItems shifts all indices greater than index (and increases them) and then inserts the index itself, so array[index] = index
32 | this means that the mapping for the new row is inserted at the correct position (when we look up a physical index, we would execute array[visualIndex] where the new index is stored)
33 | however, array[index] = index, so the physical index of the new row is the visual index
34 | it would be more intuitive if we would insert the new physical index but this is not possible by the shiftItems method because the entry for array[index] is always the index itself
35 | to reproduce this, create a table with 1,2,3,4,5, and some data between, sort it, and then insert a row before row 3
36 | the row will be displayed at the correct position but the physical index 'wrong' because if you revert the sort, the row will not be before row 3 but at the visual index it was inserted
37 | THIS IS THE CURRENT behavior of handsontable, even in version 12.x
38 | fixing this is not easy, was we would have to pass the physical index to the alter method (in oder to correctly 'spliceData') but all other hooks expect the visual index!!
39 | also, when we use 'insert_row' we don't know the real physical index because we don't know if the row should be above or below the given row and we would not know the correct visual index
40 | handsontable removed this method in favor of 'insert_row_below' and 'insert_row_above', this way we could compute the correct visual index (by toVisualIndex(index) and then +/-1)
41 | BUT FOR NOW we keep the current bahavior of handsontable
--------------------------------------------------------------------------------
/exampleCSV/autoFill.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/janisdd/vscode-edit-csv/7f06cf1c718cf27dfb287ee9d2176d5381487fa4/exampleCSV/autoFill.xlsx
--------------------------------------------------------------------------------
/images/logo.afdesign:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/janisdd/vscode-edit-csv/7f06cf1c718cf27dfb287ee9d2176d5381487fa4/images/logo.afdesign
--------------------------------------------------------------------------------
/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/janisdd/vscode-edit-csv/7f06cf1c718cf27dfb287ee9d2176d5381487fa4/images/logo.png
--------------------------------------------------------------------------------
/images/logo_old.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/janisdd/vscode-edit-csv/7f06cf1c718cf27dfb287ee9d2176d5381487fa4/images/logo_old.png
--------------------------------------------------------------------------------
/images/titleImg.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/janisdd/vscode-edit-csv/7f06cf1c718cf27dfb287ee9d2176d5381487fa4/images/titleImg.gif
--------------------------------------------------------------------------------
/out/configurationHelper.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | Object.defineProperty(exports, "__esModule", { value: true });
3 | exports.overwriteConfiguration = exports.getExtensionConfiguration = void 0;
4 | const vscode = require("vscode");
5 | const extension_1 = require("./extension");
6 | const util_1 = require("./util");
7 | const defaultConfig = {
8 | highlightCsvComments: true,
9 | lastRowEnterBehavior: 'default',
10 | lastColumnTabBehavior: 'default',
11 | lastRowOrFirstRowNavigationBehavior: 'wrap',
12 | lastColumnOrFirstColumnNavigationBehavior: 'wrap',
13 | optionsBarAppearance: "collapsed",
14 | tryToGuessHasHeader: false,
15 | readOption_comment: "#",
16 | readOption_quoteChar: '"',
17 | readOption_escapeChar: '"',
18 | readOption_delimiter: "",
19 | readOption_delimitersToGuess: [",", "\t", "|", ";", "\u001e", "\u001f"],
20 | readOption_hasHeader: "false",
21 | writeOption_comment: "#",
22 | writeOption_delimiter: "",
23 | writeOption_quoteChar: '"',
24 | writeOption_escapeChar: '"',
25 | writeOption_hasHeader: "false",
26 | doubleClickColumnHandleForcedWith: 200,
27 | doubleClickRowHandleForcedHeight: 106,
28 | openSourceFileAfterApply: false,
29 | selectTextAfterBeginEditCell: false,
30 | quoteAllFields: false,
31 | quoteEmptyOrNullFields: 'false',
32 | initiallyHideComments: false,
33 | enableWrapping: true,
34 | initialColumnWidth: 0,
35 | autoColumnWidthsIgnoreComments: true,
36 | retainQuoteInformation: 'full',
37 | forceQuoteLeadingWhitespace: false,
38 | forceQuoteTrailingWhitespace: false,
39 | newColumnQuoteInformationIsQuoted: false,
40 | disableBorders: false,
41 | initiallyFixedRowsTop: 0,
42 | initiallyFixedColumnsLeft: 0,
43 | fontSizeInPx: 16,
44 | showColumnHeaderNamesWithLettersLikeExcel: false,
45 | shouldWatchCsvSourceFile: 'yesAndNotify',
46 | sidePanelAppearance: 'collapsed',
47 | initialNumbersStyle: 'en',
48 | insertRowBehavior: 'keepRowKeepColumn',
49 | insertColBehavior: 'keepRowKeepColumn',
50 | initiallyIsInReadonlyMode: false,
51 | hideOpenCsvEditorUiActions: false,
52 | openTableAndSelectCellAtCursorPos: "initialOnly_correctRowAlwaysFirstColumn",
53 | pasteMode: 'normal',
54 | pasteBehavior: 'overwrite',
55 | pasteScrollBehavior: 'scrollToLastPastedCell',
56 | fontFamilyInTable: 'default',
57 | showDeleteColumnHeaderButton: true,
58 | showDeleteRowHeaderButton: true,
59 | finalNewLine: 'sameAsSourceFile',
60 | darkThemeTextColor: '#d0d0d0',
61 | lightThemeTextColor: '#657b83',
62 | convertUrlsToLinkTags: true,
63 | dragToAutoFill: "excelLike",
64 | initiallyHiddenColumnNames: [],
65 | initiallyHiddenColumnNumbers: [],
66 | useSaveButtonsAsAdditionalUnsavedChangesIndicator: true,
67 | copyColumnHeaderNamesSeparator: `, `,
68 | };
69 | /**
70 | * returns the configuration for this extension
71 | */
72 | function getExtensionConfiguration() {
73 | const configObj = vscode.workspace.getConfiguration(extension_1.editorUriScheme);
74 | const copy = Object.assign({}, defaultConfig);
75 | for (const key in defaultConfig) {
76 | const optionValue = configObj.get(key);
77 | if (optionValue === undefined) {
78 | vscode.window.showWarningMessage(`Could not find option: ${key} in csv-edit configuration`);
79 | continue;
80 | }
81 | //@ts-ignore
82 | copy[key] = optionValue;
83 | }
84 | //ensure single character requirements
85 | copy.readOption_quoteChar = (0, util_1.limitSingleCharacterString)(copy.readOption_quoteChar);
86 | copy.readOption_escapeChar = (0, util_1.limitSingleCharacterString)(copy.readOption_escapeChar);
87 | copy.writeOption_quoteChar = (0, util_1.limitSingleCharacterString)(copy.writeOption_quoteChar);
88 | copy.writeOption_escapeChar = (0, util_1.limitSingleCharacterString)(copy.writeOption_escapeChar);
89 | console.log(`[edit csv] settings`, copy);
90 | return copy;
91 | }
92 | exports.getExtensionConfiguration = getExtensionConfiguration;
93 | function overwriteConfiguration(currentConfig, overwriteConfigObj) {
94 | for (const key in overwriteConfigObj) {
95 | if (!currentConfig.hasOwnProperty(key)) {
96 | vscode.window.showWarningMessage(`unknown setting '${key}', skipping this setting`);
97 | continue;
98 | }
99 | //@ts-ignore
100 | currentConfig[key] = overwriteConfigObj[key];
101 | console.log(`[edit csv] overwrote config key: '${key}' with value: `, overwriteConfigObj[key]);
102 | }
103 | console.log(`[edit csv] resulting settings`, currentConfig);
104 | }
105 | exports.overwriteConfiguration = overwriteConfiguration;
106 | //# sourceMappingURL=configurationHelper.js.map
--------------------------------------------------------------------------------
/out/instanceManager.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | Object.defineProperty(exports, "__esModule", { value: true });
3 | exports.InstanceManager = void 0;
4 | /**
5 | * we need keep track of all editor instances
6 | * so we can ensure that e.g. we open only one editor per csv file,
7 | * find the source file for an editor...
8 | */
9 | class InstanceManager {
10 | constructor() {
11 | this.instances = {};
12 | }
13 | getAllInstances() {
14 | const keys = Object.keys(this.instances);
15 | const allInstances = keys.map(p => this.instances[p]);
16 | return allInstances;
17 | }
18 | addInstance(instance) {
19 | const oldInstance = this.instances[instance.sourceUri.toString()];
20 | if (oldInstance) {
21 | throw new Error('tried to add a new instance but we got old one (with the source uri)');
22 | }
23 | this.instances[instance.sourceUri.toString()] = instance;
24 | }
25 | removeInstance(instance) {
26 | const oldInstance = this.instances[instance.sourceUri.toString()];
27 | if (!oldInstance) {
28 | throw new Error('could not find old instance');
29 | }
30 | delete this.instances[instance.sourceUri.toString()];
31 | }
32 | findInstanceBySourceUri(sourceUri) {
33 | //key is the source uri
34 | const instance = this.instances[sourceUri.toString()];
35 | // const instance = this.getAllInstances().find(p => p.sourceUri.toString() == sourceUri.toString())
36 | if (!instance)
37 | return null;
38 | return instance;
39 | }
40 | // public findInstanceByEditorUri(editorUri: vscode.Uri): SomeInstance | null {
41 | // const instance = this.getAllInstances().find(p => p.editorUri === editorUri)
42 | // if (!instance) return null
43 | // return instance
44 | // }
45 | hasActiveEditorInstance() {
46 | const activeInstances = this.getAllInstances().filter(p => p.panel.active);
47 | return activeInstances.length > 0; // or === 1 ?
48 | }
49 | getActiveEditorInstance() {
50 | const activeInstances = this.getAllInstances().filter(p => p.panel.active);
51 | if (activeInstances.length === 0) {
52 | throw new Error('no active editor found');
53 | }
54 | if (activeInstances.length > 1) {
55 | throw new Error('too many active editors found');
56 | }
57 | return activeInstances[0];
58 | }
59 | }
60 | exports.InstanceManager = InstanceManager;
61 | //# sourceMappingURL=instanceManager.js.map
--------------------------------------------------------------------------------
/out/util.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | Object.defineProperty(exports, "__esModule", { value: true });
3 | exports.partitionString = exports.isCsvFile = exports.debounce = exports.limitSingleCharacterString = exports.getCurrentViewColumn = exports.debugLog = void 0;
4 | const vscode = require("vscode");
5 | function debugLog(msg) {
6 | // console.log(msg)
7 | }
8 | exports.debugLog = debugLog;
9 | /**
10 | * gets the current view column (e.g. we could have split view)
11 | */
12 | function getCurrentViewColumn() {
13 | return vscode.window.activeTextEditor && vscode.window.activeTextEditor.viewColumn
14 | ? vscode.window.activeTextEditor.viewColumn
15 | : vscode.ViewColumn.One;
16 | }
17 | exports.getCurrentViewColumn = getCurrentViewColumn;
18 | function limitSingleCharacterString(value) {
19 | if (value.length > 1) {
20 | //using last char is more user friendly as we can click and press a key to use the new char
21 | value = value.substring(value.length - 1);
22 | }
23 | return value;
24 | }
25 | exports.limitSingleCharacterString = limitSingleCharacterString;
26 | //from https://davidwalsh.name/javascript-debounce-function
27 | function debounce(func, wait, immediate = false) {
28 | var timeout;
29 | return function () {
30 | var context = this, args = arguments;
31 | var later = function () {
32 | timeout = null;
33 | if (!immediate)
34 | func.apply(context, args);
35 | };
36 | var callNow = immediate && !timeout;
37 | clearTimeout(timeout);
38 | timeout = setTimeout(later, wait);
39 | if (callNow)
40 | func.apply(context, args);
41 | };
42 | }
43 | exports.debounce = debounce;
44 | //inspired from https://github.com/jjuback/gc-excelviewer/blob/master/src/extension.ts
45 | function isCsvFile(document) {
46 | if (!document)
47 | return false;
48 | let lang = document.languageId.toLowerCase();
49 | let possible = ['csv', 'tsv', 'plaintext',
50 | //rainbow csv extension types, see https://github.com/mechatroner/vscode_rainbow_csv
51 | 'csv (semicolon)', 'csv (pipe)', 'csv (whitespace)', 'csv (tilde)', 'csv (caret)', 'csv (colon)', 'csv (double quote)', 'csv (equals)', 'csv (dot)', 'csv (hyphen)', 'dynamic csv'
52 | ];
53 | const _isCsvFile = possible.find(p => p === lang) && document.uri.scheme !== 'csv-edit';
54 | return _isCsvFile;
55 | }
56 | exports.isCsvFile = isCsvFile;
57 | function partitionString(text, sliceLength) {
58 | const slices = [];
59 | const totalSlices = Math.ceil(text.length / sliceLength);
60 | for (let i = 0; i < totalSlices; i++) {
61 | const _part = text.substr(i * sliceLength, sliceLength);
62 | slices.push({
63 | text: _part,
64 | sliceNr: i + 1,
65 | totalSlices
66 | });
67 | }
68 | return slices;
69 | }
70 | exports.partitionString = partitionString;
71 | //# sourceMappingURL=util.js.map
--------------------------------------------------------------------------------
/promtUploadbuild.js:
--------------------------------------------------------------------------------
1 | const readline = require('readline')
2 |
3 |
4 | //see https://stackoverflow.com/questions/18193953/waiting-for-user-to-enter-input-in-node-js
5 |
6 | /**
7 | *
8 | * @param {string} query
9 | * @returns {Promise}
10 | */
11 | function askQuestion(query) {
12 | const rl = readline.createInterface({
13 | input: process.stdin,
14 | output: process.stdout,
15 | })
16 |
17 | return new Promise(resolve => rl.question(query, ans => {
18 | rl.close();
19 | resolve(ans);
20 | }))
21 | }
22 |
23 | //see https://stackoverflow.com/questions/46515764/how-can-i-use-async-await-at-the-top-level
24 |
25 | /**
26 | * @returns {Promise} exit code
27 | */
28 | async function main() {
29 | const answer = await askQuestion('Did you upload the .vsix file? (y/n)')
30 | return answer === 'y' ? 0 : 1
31 | }
32 |
33 | // main().then(p => {
34 | // console.log(p);
35 | // })
36 |
37 | // const exitCode = await main()
38 | // process.exit()
39 | (async () => {
40 | try {
41 | const exitCode = await main()
42 | process.exit(exitCode)
43 | } catch (e) {
44 | }
45 | })();
46 |
47 |
48 |
--------------------------------------------------------------------------------
/src/configurationHelper.ts:
--------------------------------------------------------------------------------
1 | import * as vscode from 'vscode';
2 | import { editorUriScheme } from './extension';
3 | import { limitSingleCharacterString } from "./util";
4 |
5 |
6 |
7 | const defaultConfig: EditCsvConfig = {
8 | highlightCsvComments: true,
9 | lastRowEnterBehavior: 'default',
10 | lastColumnTabBehavior: 'default',
11 | lastRowOrFirstRowNavigationBehavior: 'wrap',
12 | lastColumnOrFirstColumnNavigationBehavior: 'wrap',
13 | optionsBarAppearance: "collapsed",
14 | tryToGuessHasHeader: false,
15 | readOption_comment: "#",
16 | readOption_quoteChar: '"',
17 | readOption_escapeChar: '"',
18 | readOption_delimiter: "",
19 | readOption_delimitersToGuess: [",", "\t", "|", ";", "\u001e", "\u001f"],
20 | readOption_hasHeader: "false",
21 | writeOption_comment: "#",
22 | writeOption_delimiter: "",
23 | writeOption_quoteChar: '"',
24 | writeOption_escapeChar: '"',
25 | writeOption_hasHeader: "false",
26 | doubleClickColumnHandleForcedWith: 200,
27 | doubleClickRowHandleForcedHeight: 106, //this gives us 5 rows with the default font size
28 | openSourceFileAfterApply: false,
29 | selectTextAfterBeginEditCell: false,
30 | quoteAllFields: false,
31 | quoteEmptyOrNullFields: 'false',
32 | initiallyHideComments: false,
33 | enableWrapping: true,
34 | initialColumnWidth: 0,
35 | autoColumnWidthsIgnoreComments: true,
36 | retainQuoteInformation: 'full',
37 | forceQuoteLeadingWhitespace: false,
38 | forceQuoteTrailingWhitespace: false,
39 | newColumnQuoteInformationIsQuoted: false,
40 | disableBorders: false,
41 | initiallyFixedRowsTop: 0,
42 | initiallyFixedColumnsLeft: 0,
43 | fontSizeInPx: 16,
44 | showColumnHeaderNamesWithLettersLikeExcel: false,
45 | shouldWatchCsvSourceFile: 'yesAndNotify',
46 | sidePanelAppearance: 'collapsed',
47 | initialNumbersStyle: 'en',
48 | insertRowBehavior: 'keepRowKeepColumn',
49 | insertColBehavior: 'keepRowKeepColumn',
50 | initiallyIsInReadonlyMode: false,
51 | hideOpenCsvEditorUiActions: false, //noop, has only effect if set inside the user settings
52 | openTableAndSelectCellAtCursorPos: "initialOnly_correctRowAlwaysFirstColumn",
53 | pasteMode: 'normal',
54 | pasteBehavior: 'overwrite',
55 | pasteScrollBehavior: 'scrollToLastPastedCell',
56 | fontFamilyInTable: 'default',
57 | showDeleteColumnHeaderButton: true,
58 | showDeleteRowHeaderButton: true,
59 | finalNewLine: 'sameAsSourceFile',
60 | darkThemeTextColor: '#d0d0d0',
61 | lightThemeTextColor: '#657b83',
62 | convertUrlsToLinkTags: true,
63 | dragToAutoFill: "excelLike",
64 | initiallyHiddenColumnNames: [],
65 | initiallyHiddenColumnNumbers: [],
66 | useSaveButtonsAsAdditionalUnsavedChangesIndicator: true,
67 | copyColumnHeaderNamesSeparator: `, `,
68 | }
69 |
70 | /**
71 | * returns the configuration for this extension
72 | */
73 | export function getExtensionConfiguration(): EditCsvConfig {
74 | const configObj = vscode.workspace.getConfiguration(editorUriScheme)
75 |
76 | const copy: EditCsvConfig = {
77 | ...defaultConfig
78 | }
79 |
80 | for (const key in defaultConfig) {
81 | const optionValue = configObj.get(key)
82 |
83 | if (optionValue === undefined) {
84 | vscode.window.showWarningMessage(`Could not find option: ${key} in csv-edit configuration`)
85 | continue
86 | }
87 |
88 | //@ts-ignore
89 | copy[key] = optionValue
90 | }
91 |
92 | //ensure single character requirements
93 | copy.readOption_quoteChar = limitSingleCharacterString(copy.readOption_quoteChar)
94 | copy.readOption_escapeChar = limitSingleCharacterString(copy.readOption_escapeChar)
95 | copy.writeOption_quoteChar = limitSingleCharacterString(copy.writeOption_quoteChar)
96 | copy.writeOption_escapeChar = limitSingleCharacterString(copy.writeOption_escapeChar)
97 |
98 | console.log(`[edit csv] settings`, copy)
99 |
100 | return copy
101 | }
102 |
103 | export function overwriteConfiguration(currentConfig: EditCsvConfig, overwriteConfigObj: EditCsvConfigOverwrite): void{
104 |
105 | for (const key in overwriteConfigObj) {
106 |
107 | if (!currentConfig.hasOwnProperty(key)) {
108 | vscode.window.showWarningMessage(`unknown setting '${key}', skipping this setting`)
109 | continue
110 | }
111 |
112 | //@ts-ignore
113 | currentConfig[key] = overwriteConfigObj[key] as any
114 |
115 | console.log(`[edit csv] overwrote config key: '${key}' with value: `, (overwriteConfigObj as any)[key])
116 | }
117 |
118 | console.log(`[edit csv] resulting settings`, currentConfig)
119 | }
--------------------------------------------------------------------------------
/src/instanceManager.ts:
--------------------------------------------------------------------------------
1 | import * as vscode from 'vscode';
2 |
3 | export interface Instance {
4 | /**
5 | * the panel with the editor
6 | */
7 | panel: vscode.WebviewPanel
8 | /**
9 | * the uri of the source file
10 | * this is the same as in {@link document}
11 | */
12 | sourceUri: vscode.Uri
13 | /**
14 | * the uri of the editor webview
15 | */
16 | editorUri: vscode.Uri
17 |
18 | /**
19 | * the source file reference
20 | * might be closed
21 | * or out of sync (for non-workspace files)
22 | */
23 | document: vscode.TextDocument
24 |
25 | /**
26 | * the last known content of the file (set by this extension on save)
27 | *
28 | * we need this to check if the file content really changed
29 | * sometimes fake change events are triggered and then we compare the file contents
30 | */
31 | lastCommittedContent: string
32 |
33 | /**
34 | * true: edit has unsaved changes, false: not
35 | */
36 | hasChanges: boolean
37 |
38 | /**
39 | * the original title for the tab
40 | */
41 | originalTitle: string
42 |
43 | /**
44 | * when the table saves the file we need to ignore the next change else the disk change will trigger the table to reload (losing undo, ...)
45 | */
46 | ignoreChangeEvents: boolean
47 |
48 | /**
49 | * used to watch the source file and notify the extension view
50 | */
51 | sourceFileWatcher: vscode.FileSystemWatcher | null
52 |
53 | unsubscribeWatcher: vscode.Disposable | null
54 | }
55 |
56 | export interface InstanceWorkspaceSourceFile extends Instance {
57 | kind: 'workspaceFile'
58 | }
59 |
60 | export interface InstanceExternalFile extends Instance {
61 | kind: 'externalFile'
62 | }
63 |
64 | export type SomeInstance = InstanceWorkspaceSourceFile | InstanceExternalFile
65 |
66 | export interface InstanceStorage {
67 | /**
68 | * the key is the source uri to string
69 | */
70 | [sourceUriString: string]: SomeInstance
71 | }
72 |
73 | /**
74 | * we need keep track of all editor instances
75 | * so we can ensure that e.g. we open only one editor per csv file,
76 | * find the source file for an editor...
77 | */
78 | export class InstanceManager {
79 |
80 | private instances: InstanceStorage = {}
81 |
82 |
83 | public getAllInstances(): SomeInstance[] {
84 | const keys = Object.keys(this.instances)
85 | const allInstances = keys.map(p => this.instances[p])
86 | return allInstances
87 | }
88 |
89 | public addInstance(instance: SomeInstance) {
90 |
91 | const oldInstance = this.instances[instance.sourceUri.toString()]
92 |
93 | if (oldInstance) {
94 | throw new Error('tried to add a new instance but we got old one (with the source uri)')
95 | }
96 |
97 | this.instances[instance.sourceUri.toString()] = instance
98 | }
99 |
100 | public removeInstance(instance: SomeInstance) {
101 | const oldInstance = this.instances[instance.sourceUri.toString()]
102 |
103 | if (!oldInstance) {
104 | throw new Error('could not find old instance')
105 | }
106 |
107 | delete this.instances[instance.sourceUri.toString()]
108 | }
109 |
110 | public findInstanceBySourceUri(sourceUri: vscode.Uri): SomeInstance | null {
111 |
112 | //key is the source uri
113 | const instance = this.instances[sourceUri.toString()]
114 | // const instance = this.getAllInstances().find(p => p.sourceUri.toString() == sourceUri.toString())
115 |
116 | if (!instance) return null
117 |
118 | return instance
119 | }
120 |
121 | // public findInstanceByEditorUri(editorUri: vscode.Uri): SomeInstance | null {
122 |
123 | // const instance = this.getAllInstances().find(p => p.editorUri === editorUri)
124 |
125 | // if (!instance) return null
126 |
127 | // return instance
128 | // }
129 |
130 | public hasActiveEditorInstance(): boolean {
131 | const activeInstances = this.getAllInstances().filter(p => p.panel.active)
132 | return activeInstances.length > 0 // or === 1 ?
133 | }
134 |
135 | public getActiveEditorInstance(): SomeInstance {
136 | const activeInstances = this.getAllInstances().filter(p => p.panel.active)
137 |
138 | if (activeInstances.length === 0) {
139 | throw new Error('no active editor found')
140 | }
141 |
142 | if (activeInstances.length > 1) {
143 | throw new Error('too many active editors found')
144 | }
145 |
146 | return activeInstances[0]
147 | }
148 |
149 | }
--------------------------------------------------------------------------------
/src/test/suite/extension.test.ts:
--------------------------------------------------------------------------------
1 | //
2 | // Note: This example test is leveraging the Mocha test framework.
3 | // Please refer to their documentation on https://mochajs.org/ for help.
4 | //
5 |
6 | // The module 'assert' provides assertion methods from node
7 | import * as assert from 'assert';
8 | // import * as vscode from 'vscode'
9 | import { partitionString } from '../../util';
10 |
11 |
12 | // Defines a Mocha unit test
13 | // test("Something 1", function() {
14 | // assert.equal(-1, [1, 2, 3].indexOf(5));
15 | // assert.equal(-1, [1, 2, 3].indexOf(0));
16 | // });
17 |
18 | //see https://vscode.rocks/testing/
19 | // const newFile = vscode.Uri.parse('untitled:Untitled-2')
20 | // const document = await vscode.workspace.openTextDocument(newFile)
21 | // const textEditor = await vscode.window.showTextDocument(document)
22 |
23 | // await vscode.commands.executeCommand('edit-csv.edit')
24 |
25 | // // await sleep(1000)
26 | // assert.equal(textEditor, vscode.window.activeTextEditor)
27 |
28 | // function sleep(ms: number): Promise {
29 | // return new Promise(resolve => {
30 | // setTimeout(resolve, ms)
31 | // })
32 | // }
33 |
34 | //to run tests start tsc -w and then go to the debug tab and select "EXtension Tests" and run
35 |
36 | // Defines a Mocha test suite to group tests of similar kind together
37 | suite("partitionString working properly", function () {
38 |
39 | test('partition size not fitting', async function () {
40 |
41 | const text = '0123456789'
42 |
43 | const parts = partitionString(text, 3)
44 |
45 | const margedText = parts.map(p => p.text).join('')
46 |
47 | assert.equal(margedText, text)
48 | })
49 |
50 | test('partition size larger than text', async function () {
51 |
52 | const text = '0123456789'
53 |
54 | const parts = partitionString(text, text.length + 10)
55 |
56 | const margedText = parts.map(p => p.text).join('')
57 |
58 | assert.equal(margedText, text)
59 | })
60 |
61 | test('partition size perfect fit', async function () {
62 |
63 | const text = '0123456789'
64 |
65 | const parts = partitionString(text, text.length)
66 |
67 | const margedText = parts.map(p => p.text).join('')
68 |
69 | assert.equal(margedText, text)
70 | })
71 |
72 |
73 | })
74 |
75 |
76 | suite('some frontend func tests', function () {
77 |
78 | test('excel like column names (letters) func is correct (like handsontable)', async function () {
79 |
80 | const COLUMN_LABEL_BASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
81 | const COLUMN_LABEL_BASE_LENGTH = COLUMN_LABEL_BASE.length
82 |
83 | function spreadsheetColumnLetterLabel(index: number): string {
84 | let num = index
85 | let columnLabel = ''
86 | //see https://stackoverflow.com/questions/34813980/getting-an-array-of-column-names-at-sheetjs
87 | while (num >= 0) {
88 | columnLabel = COLUMN_LABEL_BASE[num % 26] + columnLabel
89 | num = Math.floor(num / 26) - 1
90 | }
91 | return columnLabel
92 | }
93 |
94 | function spreadsheetColumnLabel(index: number): string {
95 | let dividend = index + 1
96 | let columnLabel = ''
97 | let modulo
98 |
99 | while (dividend > 0) {
100 | modulo = (dividend - 1) % COLUMN_LABEL_BASE_LENGTH;
101 | columnLabel = String.fromCharCode(65 + modulo) + columnLabel;
102 | dividend = parseInt((dividend - modulo) / COLUMN_LABEL_BASE_LENGTH as any, 10);
103 | }
104 |
105 | return columnLabel;
106 | }
107 |
108 |
109 | for (let i = 0; i < 1_000_000; i++) {
110 | let correct = spreadsheetColumnLabel(i)
111 | let test = spreadsheetColumnLetterLabel(i)
112 |
113 | assert.equal(test, correct)
114 | }
115 |
116 | })
117 |
118 | })
119 |
120 |
121 |
--------------------------------------------------------------------------------
/src/util.ts:
--------------------------------------------------------------------------------
1 | import * as vscode from 'vscode';
2 |
3 | export function debugLog(msg: any) {
4 | // console.log(msg)
5 | }
6 |
7 | /**
8 | * gets the current view column (e.g. we could have split view)
9 | */
10 | export function getCurrentViewColumn(): vscode.ViewColumn {
11 | return vscode.window.activeTextEditor && vscode.window.activeTextEditor.viewColumn
12 | ? vscode.window.activeTextEditor.viewColumn
13 | : vscode.ViewColumn.One
14 | }
15 |
16 | export function limitSingleCharacterString(value: string): string {
17 |
18 | if (value.length > 1) {
19 | //using last char is more user friendly as we can click and press a key to use the new char
20 | value = value.substring(value.length-1)
21 | }
22 |
23 | return value
24 | }
25 |
26 | //from https://davidwalsh.name/javascript-debounce-function
27 | export function debounce(func: T, wait: number, immediate = false): T {
28 | var timeout: any;
29 | return function (this: any) {
30 | var context = this, args = arguments;
31 | var later = function () {
32 | timeout = null;
33 | if (!immediate) func.apply(context, args);
34 | };
35 | var callNow = immediate && !timeout;
36 | clearTimeout(timeout);
37 | timeout = setTimeout(later, wait);
38 | if (callNow) func.apply(context, args);
39 | } as any
40 | }
41 |
42 | //inspired from https://github.com/jjuback/gc-excelviewer/blob/master/src/extension.ts
43 | export function isCsvFile(document: vscode.TextDocument) {
44 | if (!document) return false
45 |
46 | let lang = document.languageId.toLowerCase()
47 | let possible = ['csv', 'tsv', 'plaintext',
48 | //rainbow csv extension types, see https://github.com/mechatroner/vscode_rainbow_csv
49 | 'csv (semicolon)', 'csv (pipe)', 'csv (whitespace)', 'csv (tilde)', 'csv (caret)', 'csv (colon)', 'csv (double quote)', 'csv (equals)', 'csv (dot)', 'csv (hyphen)', 'dynamic csv'
50 | ]
51 | const _isCsvFile = possible.find(p => p === lang) && document.uri.scheme !== 'csv-edit'
52 | return _isCsvFile
53 | }
54 |
55 | export function partitionString(text: string, sliceLength: number): StringSlice[] {
56 |
57 | const slices: StringSlice[] = []
58 | const totalSlices = Math.ceil(text.length / sliceLength)
59 |
60 | for (let i = 0; i < totalSlices; i++) {
61 | const _part = text.substr(i * sliceLength, sliceLength)
62 |
63 | slices.push({
64 | text: _part,
65 | sliceNr: i + 1,
66 | totalSlices
67 | })
68 | }
69 |
70 | return slices
71 | }
--------------------------------------------------------------------------------
/thirdParty/big.js/LICENCE:
--------------------------------------------------------------------------------
1 | The MIT Licence (Expat).
2 |
3 | Copyright (c) 2018 Michael Mclaughlin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining
6 | a copy of this software and associated documentation files (the
7 | 'Software'), to deal in the Software without restriction, including
8 | without limitation the rights to use, copy, modify, merge, publish,
9 | distribute, sublicense, and/or sell copies of the Software, and to
10 | permit persons to whom the Software is furnished to do so, subject to
11 | the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be
14 | included in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 |
24 |
--------------------------------------------------------------------------------
/thirdParty/big.js/big.min.js:
--------------------------------------------------------------------------------
1 | /* big.js v5.2.2 https://github.com/MikeMcl/big.js/LICENCE */
2 | !function(e){"use strict";var r,i=20,s=1,P=1e6,o=-7,f=21,c="[big.js] ",u=c+"Invalid ",b=u+"decimal places",h=u+"rounding mode",x=c+"Division by zero",l={},D=void 0,a=/^-?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i;function R(e,r,t,n){var i=e.c,s=e.e+r+1;if(s++n&&R(e,t,o.RM),2==r&&(n=e.e+t+1);e.c.length=o.PE))s=s.charAt(0)+(1t)for(i-=t;i--;)s+="0";else ii[s]^r?1:-1;return f==c?0:cw[l]?1:-1;break}if(!(h<0))break;for(c=d==f?i:a;d;){if(w[--d]>=1;)r=r.times(r);return i?t.div(n):n},l.round=function(e,r){var t=this.constructor;if(e===D)e=0;else if(e!==~~e||e<-P||P
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining
7 | a copy of this software and associated documentation files (the
8 | 'Software'), to deal in the Software without restriction, including
9 | without limitation the rights to use, copy, modify, merge, publish,
10 | distribute, sublicense, and/or sell copies of the Software, and to
11 | permit persons to whom the Software is furnished to do so, subject to
12 | the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be
15 | included in all copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 |
--------------------------------------------------------------------------------
/thirdParty/handsontable/info.md:
--------------------------------------------------------------------------------
1 | source: https://github.com/janisdd/handsontable
2 | version: 6.5.5 [branch forCsvEdit6.2.2] (custom for this project)
3 |
4 |
--------------------------------------------------------------------------------
/thirdParty/info.md:
--------------------------------------------------------------------------------
1 | when we need some more files add these to package.json (to get the right version of the files):
2 |
3 | ```json
4 | "devDependencies": {
5 | ...
6 | "handsontable": "github:janisdd/handsontable#6.3.0",
7 | "mousetrap": "1.6.3",
8 | "@fortawesome/fontawesome-free": "5.6.3",
9 | }
10 | ```
11 |
--------------------------------------------------------------------------------
/thirdParty/mousetrap/info.md:
--------------------------------------------------------------------------------
1 | source: https://github.com/ccampbell/mousetrap
2 | version: 1.6.3
3 |
4 |
--------------------------------------------------------------------------------
/thirdParty/mousetrap/mousetrap.min.js:
--------------------------------------------------------------------------------
1 | /* mousetrap v1.6.3 craig.is/killing/mice */
2 | (function(q,u,c){function v(a,b,g){a.addEventListener?a.addEventListener(b,g,!1):a.attachEvent("on"+b,g)}function z(a){if("keypress"==a.type){var b=String.fromCharCode(a.which);a.shiftKey||(b=b.toLowerCase());return b}return n[a.which]?n[a.which]:r[a.which]?r[a.which]:String.fromCharCode(a.which).toLowerCase()}function F(a){var b=[];a.shiftKey&&b.push("shift");a.altKey&&b.push("alt");a.ctrlKey&&b.push("ctrl");a.metaKey&&b.push("meta");return b}function w(a){return"shift"==a||"ctrl"==a||"alt"==a||
3 | "meta"==a}function A(a,b){var g,d=[];var e=a;"+"===e?e=["+"]:(e=e.replace(/\+{2}/g,"+plus"),e=e.split("+"));for(g=0;gc||n.hasOwnProperty(c)&&(p[n[c]]=c)}g=p[e]?"keydown":"keypress"}"keypress"==g&&d.length&&(g="keydown");return{key:m,modifiers:d,action:g}}function D(a,b){return null===a||a===u?!1:a===b?!0:D(a.parentNode,b)}function d(a){function b(a){a=
4 | a||{};var b=!1,l;for(l in p)a[l]?b=!0:p[l]=0;b||(x=!1)}function g(a,b,t,f,g,d){var l,E=[],h=t.type;if(!k._callbacks[a])return[];"keyup"==h&&w(a)&&(b=[a]);for(l=0;l":".","?":"/","|":"\\"},B={option:"alt",command:"meta","return":"enter",
9 | escape:"esc",plus:"+",mod:/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"meta":"ctrl"},p;for(c=1;20>c;++c)n[111+c]="f"+c;for(c=0;9>=c;++c)n[c+96]=c.toString();d.prototype.bind=function(a,b,c){a=a instanceof Array?a:[a];this._bindMultiple.call(this,a,b,c);return this};d.prototype.unbind=function(a,b){return this.bind.call(this,a,function(){},b)};d.prototype.trigger=function(a,b){if(this._directMap[a+":"+b])this._directMap[a+":"+b]({},a);return this};d.prototype.reset=function(){this._callbacks={};
10 | this._directMap={};return this};d.prototype.stopCallback=function(a,b){if(-1<(" "+b.className+" ").indexOf(" mousetrap ")||D(b,this.target))return!1;if("composedPath"in a&&"function"===typeof a.composedPath){var c=a.composedPath()[0];c!==a.target&&(b=c)}return"INPUT"==b.tagName||"SELECT"==b.tagName||"TEXTAREA"==b.tagName||b.isContentEditable};d.prototype.handleKey=function(){return this._handleKey.apply(this,arguments)};d.addKeycodes=function(a){for(var b in a)a.hasOwnProperty(b)&&(n[b]=a[b]);p=null};
11 | d.init=function(){var a=d(u),b;for(b in a)"_"!==b.charAt(0)&&(d[b]=function(b){return function(){return a[b].apply(a,arguments)}}(b))};d.init();q.Mousetrap=d;"undefined"!==typeof module&&module.exports&&(module.exports=d);"function"===typeof define&&define.amd&&define(function(){return d})}})("undefined"!==typeof window?window:null,"undefined"!==typeof window?document:null);
12 |
--------------------------------------------------------------------------------
/thirdParty/mousetrap/plugins/global-bind/README.md:
--------------------------------------------------------------------------------
1 | # Global Bind
2 |
3 | This extension allows you to specify keyboard events that will work anywhere including inside textarea/input fields.
4 |
5 | Usage looks like:
6 |
7 | ```javascript
8 | Mousetrap.bindGlobal('ctrl+s', function() {
9 | _save();
10 | });
11 | ```
12 |
13 | This means that a keyboard event bound using ``Mousetrap.bind`` will only work outside of form input fields, but using ``Moustrap.bindGlobal`` will work in both places.
14 |
15 | If you wanted to create keyboard shortcuts that only work when you are inside a specific textarea you can do that too by creating your own extension.
16 |
--------------------------------------------------------------------------------
/thirdParty/mousetrap/plugins/global-bind/mousetrap-global-bind.js:
--------------------------------------------------------------------------------
1 | /**
2 | * adds a bindGlobal method to Mousetrap that allows you to
3 | * bind specific keyboard shortcuts that will still work
4 | * inside a text input field
5 | *
6 | * usage:
7 | * Mousetrap.bindGlobal('ctrl+s', _saveChanges);
8 | */
9 | /* global Mousetrap:true */
10 | (function(Mousetrap) {
11 | var _globalCallbacks = {};
12 | var _originalStopCallback = Mousetrap.prototype.stopCallback;
13 |
14 | Mousetrap.prototype.stopCallback = function(e, element, combo, sequence) {
15 | var self = this;
16 |
17 | if (self.paused) {
18 | return true;
19 | }
20 |
21 | if (_globalCallbacks[combo] || _globalCallbacks[sequence]) {
22 | return false;
23 | }
24 |
25 | return _originalStopCallback.call(self, e, element, combo);
26 | };
27 |
28 | Mousetrap.prototype.bindGlobal = function(keys, callback, action) {
29 | var self = this;
30 | self.bind(keys, callback, action);
31 |
32 | if (keys instanceof Array) {
33 | for (var i = 0; i < keys.length; i++) {
34 | _globalCallbacks[keys[i]] = true;
35 | }
36 | return;
37 | }
38 |
39 | _globalCallbacks[keys] = true;
40 | };
41 |
42 | Mousetrap.init();
43 | }) (Mousetrap);
44 |
--------------------------------------------------------------------------------
/thirdParty/mousetrap/plugins/global-bind/mousetrap-global-bind.min.js:
--------------------------------------------------------------------------------
1 | (function(a){var c={},d=a.prototype.stopCallback;a.prototype.stopCallback=function(e,b,a,f){return this.paused?!0:c[a]||c[f]?!1:d.call(this,e,b,a)};a.prototype.bindGlobal=function(a,b,d){this.bind(a,b,d);if(a instanceof Array)for(b=0;b we never stop at `"..."\n` because we actually could have `"..."\r\n` so after the quote the `\r` follows, not the new line character
27 |
28 | - added option to parsing to retain quote information for columns
29 | - the returned type is now: oldResult & {columnIsQuoted: boolean[] or null if option is not set}
30 | - the parse option is: retainQuoteInformation: {boolean}
31 |
32 | - added parse/unparse option `rowInsertCommentLines_commentsString`
33 | - used to treat comments as normal 1 cell rows
34 | - parse: rowInsertCommentLines_commentsString !== null, left trimmed strings starting with it are treated as comments and are parsed into a row with 1 cell
35 | - unparse: rowInsertCommentLines_commentsString !== null, left trimmed strings first cells will be trimmed left and only the first cell will be exported
36 |
37 | - fixed issue https://github.com/mholt/PapaParse/issues/1035 (same as https://github.com/janisdd/vscode-edit-csv/issues/167)
38 | - also fixes https://github.com/mholt/PapaParse/issues/1068
39 | - issue: the escape char was not properly set when only the quoteChar was changed
40 | - subsequent issue: do determine if a field must be quoted `BAD_DELIMITERS` was used, which always includes `"` and `Papa.BYTE_ORDER_MARK`
41 | - it also didn't check the actual quoteChar
42 |
43 | - added option `_quoteLeadingSpace` and `_quoteTrailingSpace`
44 | - `_quoteLeadingSpace` defaults to true: if a field starts with a whitespace, should it be quoted (true) or not (false)
45 | - `_quoteTrailingSpace` defaults to true: if a field ends with a whitespace, should it be quoted (true) or not (false)
46 |
47 | - added option `_determineFieldHasQuotesFunc` to determine if a field should be quoted (it cannot remove quotes!!)
48 | - if a field contains some special characters, it is quoted, e.g. delimiter, quotes, new line, ...
49 | - this func can be used to add quotes to fields (but not to remove quotes!) it is OR-ed with the other indicators
50 |
51 | - when setting `retainQuoteInformation` to `true`, we now also output `cellIsQuotedInfo` which contains the information if a cell was quoted or not
52 | - `cellIsQuotedInfo` now respects `skipEmptyLines` and returns the same amount of rows as the data array
53 |
54 | --- all further changes are directly noted in the `.js` file ---
55 |
56 | ## Minified version
57 |
58 | minified with https://javascript-minifier.com/
--------------------------------------------------------------------------------
/thirdParty/papaparse/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Matthew Holt
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/thirdParty/papaparse/info.md:
--------------------------------------------------------------------------------
1 | source: https://github.com/mholt/PapaParse
2 | version: 5.0.0 (modified, custom version branch: fix609_main)
3 | modified version: https://github.com/janisdd/PapaParse
4 |
--------------------------------------------------------------------------------
/thirdParty/papaparse/papaparse.d.ts:
--------------------------------------------------------------------------------
1 | export type FieldPosition = {
2 | start: number;
3 | end: number;
4 | };
5 | export type ParseConfigAll = {
6 | /**
7 | * empty for auto-detect
8 | */
9 | delimiter: '' | string;
10 | /**
11 | * empty for auto-detect
12 | */
13 | newline: '' | '\r' | '\n' | '\r\n';
14 | /**
15 | * when a cell starts with this string, it is treated as a comment and the row is ignored
16 | *
17 | * if you want to include comment rows in the parse result, use {@link rowInsertCommentLines_commentsString} and set this to null
18 | */
19 | comments: string | null;
20 | /**
21 | * used to treat comments as normal 1 cell rows
22 | * - parse: rowInsertCommentLines_commentsString !== null, left trimmed strings starting with it are treated as comments and are parsed into a row with 1 cell
23 | * - unparse: rowInsertCommentLines_commentsString !== null, left trimmed strings first cells will be trimmed left and only the first cell will be exported
24 | */
25 | rowInsertCommentLines_commentsString: string | null;
26 | /**
27 | * if a field should contain the delimiter but as data and not as delimiter, it must be quoted
28 | */
29 | quoteChar: string;
30 | /**
31 | * quotes are normally ignored as they don't change the resulting data
32 | * but for some applications we need to know if a cell was quoted
33 | * true: the result will contain the information if a cell was quoted or not
34 | * see {@link ParseResult.columnIsQuoted} and {@link ParseResult.cellIsQuotedInfo}
35 | * false: information will be null
36 | */
37 | retainQuoteInformation: boolean;
38 | /**
39 | * if a field should contain the quoteChar but as data and not as quoteChar, it must be escaped
40 | * empty to use quote char
41 | */
42 | escapeChar: '' | string;
43 | /**
44 | * f true, lines that are completely empty (those which evaluate to an empty string) will be skipped. If set to 'greedy',
45 | * lines that evaluate to empty strings after processing will also be skipped.
46 | */
47 | skipEmptyLines: boolean | 'greedy';
48 | delimitersToGuess: string[];
49 | /**
50 | * the max number of characters of the input to guess the delimiter
51 | */
52 | maxDelimiterGuessLength: number;
53 | /**
54 | * If > 0, only that many rows will be parsed.
55 | * null or <= 0 to not use preview
56 | */
57 | previewInRows: number | null;
58 | calcLineIndexToCsvLineIndexMapping: boolean;
59 | calcColumnIndexToCsvColumnIndexMapping: boolean;
60 | calcCsvFieldToInputPositionMapping: boolean;
61 | };
62 | export type ParseConfig = Partial;
63 | export type ParseResult = {
64 | data: string[][];
65 | errors: ParseError[];
66 | /**
67 | * meta information about the parsing
68 | */
69 | meta: ParseResultMeta;
70 | };
71 | export interface ParseResultMeta {
72 | /**
73 | * Delimiter used
74 | */
75 | delimiter: string;
76 | /**
77 | * Line break sequence used
78 | */
79 | linebreak: string;
80 | cursor: number;
81 | /**
82 | * when {@link ParseConfigAll.retainQuoteInformation} is set to true, this array contains the information if a column was quoted or not
83 | * a column is quoted if the first cell of the column was quoted
84 | *
85 | * @deprecated
86 | * this is more a legacy feature, use {@link cellIsQuotedInfo} instead
87 | */
88 | columnIsQuoted: boolean[] | null;
89 | /**
90 | * when {@link ParseConfigAll.retainQuoteInformation} is set to true, this array contains the information if a cell was quoted or not
91 | */
92 | cellIsQuotedInfo: boolean[][] | null;
93 | /**
94 | * for each line index in the input text the csv line index it refers to
95 | */
96 | outLineIndexToCsvLineIndexMapping: number[] | null;
97 | outColumnIndexToCsvColumnIndexMapping: number[][] | null;
98 | outCsvFieldToInputPositionMapping: FieldPosition[][] | null;
99 | }
100 | export interface ParseError {
101 | /**
102 | * A generalization of the error
103 | */
104 | type: string;
105 | /**
106 | * Standardized error code
107 | */
108 | code: string;
109 | /**
110 | * Human-readable details
111 | */
112 | message: string;
113 | /**
114 | * Row index of parsed data where error is
115 | */
116 | row?: number;
117 | /**
118 | * column index (cursor position) of the error
119 | */
120 | index?: number;
121 | }
122 | export type UnparseResult = {
123 | csv: string;
124 | /**
125 | * meta information about the unparsing
126 | */
127 | meta: UnparseResultMeta;
128 | };
129 | export interface UnparseResultMeta {
130 | outCsvFieldToInputPositionMapping: FieldPosition[][] | null;
131 | }
132 | export type UnparseConfigAll = {
133 | delimiter: string;
134 | newline: string;
135 | quoteChar: string;
136 | /**
137 | * empty to use quote char
138 | */
139 | escapeChar: '' | string;
140 | skipEmptyLines: boolean | 'greedy';
141 | /**
142 | * If true, forces all fields to be enclosed in quotes.
143 | * If an array of true/false values, specifies which fields should be force-quoted (first boolean is for the first column, second boolean for the second column, ...)
144 | *
145 | * @note
146 | * old version used option columnIsQuoted for the array but we changed it back to one option
147 | */
148 | quotes: boolean | boolean[];
149 | /**
150 | * true: quote empty/null/undefined fields
151 | */
152 | quoteEmptyOrNullFields: boolean;
153 | quoteLeadingSpace: boolean;
154 | quoteTrailingSpace: boolean;
155 | determineFieldHasQuotesFunc?: ((field: string, row: number, col: number) => boolean);
156 | /**
157 | * see {@link ParseConfigAll.rowInsertCommentLines_commentsString}
158 | */
159 | rowInsertCommentLines_commentsString: string | null;
160 | calcCsvFieldToInputPositionMapping: boolean;
161 | };
162 | export type UnparseConfig = Partial;
163 | /**
164 | * some options might be unset or will bet auto-detected,
165 | * this is the effective configuration
166 | */
167 | export interface ParseConfigEffective extends ParseConfigAll {
168 | delimiter: string;
169 | newline: '\r' | '\n' | '\r\n';
170 | comments: string;
171 | previewInRows: number;
172 | }
173 | /**
174 | * only exports to inspect defaults
175 | */
176 | export declare const __parseConfigUserDefaults: ParseConfigAll;
177 | /**
178 | * only exports to inspect defaults
179 | */
180 | export declare const __unparseConfigUserDefaults: UnparseConfigAll;
181 | export declare class Papa {
182 | static RECORD_SEP: string;
183 | static UNIT_SEP: string;
184 | static BYTE_ORDER_MARK: string;
185 | static BAD_DELIMITERS: string[];
186 | static NEED_QUOTES_CHARS: string[];
187 | static DefaultDelimiter: string;
188 | static DefaultQuoteChar: string;
189 | static DefaultEscapeChar: string;
190 | static parse(input: string, _config?: ParseConfig): ParseResult;
191 | static unparse(data: Array>, _config?: UnparseConfig): UnparseResult;
192 | }
193 | export declare class Parser {
194 | _config: ParseConfigEffective;
195 | _input: string;
196 | _quoteSearch: number;
197 | _nextNewline: number;
198 | _cursor: number;
199 | _lastCursor: number;
200 | _data: string[][];
201 | _row: string[];
202 | _errors: ParseError[];
203 | _delim: string;
204 | _quoteChar: string;
205 | _newlineString: string;
206 | _inputLen: number;
207 | _escapeChar: string;
208 | _previewInRows: number;
209 | _firstQuoteInformationRowFound: boolean;
210 | _maxGuessLength: number;
211 | _isGuessingDelimiter: boolean;
212 | /**
213 | * normally when parsing quotes are discarded as they don't change the retrieved data
214 | * true: collect information about quotes (cells, columns)
215 | * false: do not collect quote information
216 | * see {@link _columnIsQuoted}, {@link _cellIsQuotedInfo}
217 | *
218 | * NOTE: if false -> we keep the arrays empty and in post-processing we set them to null (not here)
219 | *
220 | *
221 | * to determine if a column is quoted we use the first cell only (if a column has no cells then it's not quoted)
222 | * so if the first line has only 3 columns and all other more than 3 (e.g. 4) then all columns starting from 4 are treated as not quoted!!
223 | * not that there is no difference if we have column headers (first row is used)
224 | * comment rows are ignored for this
225 | */
226 | _retainQuoteInformation: boolean;
227 | _columnIsQuoted: boolean[];
228 | /** @type {boolean[][]} for each cell the info if it was quoted originally */
229 | _cellIsQuotedInfo: boolean[][];
230 | _cellIsQuotedInfoRow: boolean[];
231 | _currentRowStartIndex: number;
232 | _comments: string;
233 | _rowInsertCommentLines_commentsString: string | null;
234 | _outColumnIndexToCsvColumnIndexMapping: number[][] | null;
235 | _currSingleRowColumnIndexToCsvColumnIndexMapping: number[];
236 | _outFieldPositionMapping: Array> | null;
237 | _currentRowFieldPositions: Array;
238 | _fieldStart: number;
239 | constructor(config: ParseConfigEffective, isGuessingDelimiter: boolean);
240 | parse(input: string): ParseResult;
241 | pushRow(row: string[]): void;
242 | /**
243 | * adds the given index to the column mapping if we still calculate it
244 | * @param cumulativeColumnIndex
245 | */
246 | addColumnIndexMapping(cumulativeColumnIndex: number): void;
247 | /**
248 | * Appends the remaining input from cursor to the end into
249 | * row, saves the row, calls step, and returns the results.
250 | */
251 | finish(value?: string): ParseResult;
252 | /**
253 | * Appends the current row to the results. It sets the cursor
254 | * to newCursor and finds the nextNewline. The caller should
255 | * take care to execute user's step function and check for
256 | * preview and end parsing if necessary.
257 | */
258 | saveRow(newCursor: number): void;
259 | /** Returns an object with the results, errors, and meta. */
260 | returnable(): ParseResult;
261 | /** Gets the delimiter character, which is not inside the quoted field */
262 | getNextUnqotedDelimiter(nextDelim: number, quoteSearch: number, nextNewline: number): {
263 | nextDelim: number | null;
264 | quoteSearch: number | null;
265 | };
266 | /**
267 | * checks if there are extra spaces after closing quote and given index without any text
268 | * if Yes, returns the number of spaces
269 | */
270 | extraSpaces(index: number): number;
271 | private addFieldPosition;
272 | }
273 |
--------------------------------------------------------------------------------
/thirdParty/regression/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) Tom Alexander
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/thirdParty/regression/info.md:
--------------------------------------------------------------------------------
1 | source: https://github.com/janisdd/regression-js
2 | is a fork from https://github.com/Tom-Alexander/regression-js
3 | version: 3.0.0 (modified)
4 |
5 |
--------------------------------------------------------------------------------
/thirdParty/regression/regression.d.ts:
--------------------------------------------------------------------------------
1 | import { default as Big } from 'big.js';
2 | type DataPoint = number[];
3 | type DataPointBig = Big[];
4 | type DEFAULT_OPTIONS_TYPE = {
5 | [key in keyof typeof DEFAULT_OPTIONS]?: typeof DEFAULT_OPTIONS[key];
6 | };
7 | declare const DEFAULT_OPTIONS: {
8 | precision: number;
9 | precisionBig: number;
10 | predictPoints: boolean;
11 | bigRoundingMode: number;
12 | };
13 | declare const _default: {
14 | linear: (data: DataPoint[], options?: DEFAULT_OPTIONS_TYPE) => {
15 | points: number[][];
16 | predict: (x: number) => number[];
17 | equation: number[];
18 | string: string;
19 | };
20 | linearBig: (data: DataPointBig[], options?: DEFAULT_OPTIONS_TYPE) => {
21 | points: Big.Big[][];
22 | predict: (x: Big) => Big.Big[];
23 | equation: Big.Big[];
24 | string: string;
25 | };
26 | };
27 | export default _default;
28 |
--------------------------------------------------------------------------------
/thirdParty/regression/regression.js:
--------------------------------------------------------------------------------
1 | (function (global, factory) {
2 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('big.js')) :
3 | typeof define === 'function' && define.amd ? define(['big.js'], factory) :
4 | (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.regression = factory(global.Big));
5 | })(this, (function (Big) { 'use strict';
6 |
7 | const DEFAULT_OPTIONS = {
8 | precision: 2,
9 | precisionBig: 20,
10 | //default for Big.DP https://mikemcl.github.io/big.js/#dp
11 | predictPoints: false,
12 | bigRoundingMode: 1
13 | };
14 | function round(number, precision) {
15 | const factor = 10 ** precision;
16 | return Math.round(number * factor) / factor;
17 | }
18 | function roundBig(number, precision, roundingMode) {
19 | return number.round(precision, Big.roundHalfUp);
20 | }
21 | function _linear(data, options) {
22 | const sum = [0, 0, 0, 0, 0];
23 | let len = 0;
24 | for (let n = 0; n < data.length; n++) {
25 | if (data[n][1] !== null) {
26 | len++;
27 | sum[0] += data[n][0];
28 | sum[1] += data[n][1];
29 | sum[2] += data[n][0] * data[n][0];
30 | sum[3] += data[n][0] * data[n][1];
31 | sum[4] += data[n][1] * data[n][1];
32 | }
33 | }
34 | const run = len * sum[2] - sum[0] * sum[0];
35 | const rise = len * sum[3] - sum[0] * sum[1];
36 | const gradient = run === 0 ? 0 : round(rise / run, options.precision);
37 | const intercept = round(sum[1] / len - gradient * sum[0] / len, options.precision);
38 | const predict = (x) => [
39 | round(x, options.precision),
40 | round(gradient * x + intercept, options.precision)
41 | ];
42 | return {
43 | points: options.predictPoints ? data.map((point) => predict(point[0])) : [],
44 | predict,
45 | equation: [gradient, intercept],
46 | string: intercept === 0 ? `y = ${gradient}x` : `y = ${gradient}x + ${intercept}`
47 | };
48 | }
49 | function _linearBig(data, options) {
50 | const sum = [Big(0), Big(0), Big(0), Big(0), Big(0)];
51 | let len = Big(0);
52 | for (let n = 0; n < data.length; n++) {
53 | if (data[n][1] !== null) {
54 | len = len.add(1);
55 | sum[0] = sum[0].add(data[n][0]);
56 | sum[1] = sum[1].add(data[n][1]);
57 | sum[2] = sum[2].add(data[n][0].mul(data[n][0]));
58 | sum[3] = sum[3].add(data[n][0].mul(data[n][1]));
59 | sum[4] = sum[4].add(data[n][1].mul(data[n][1]));
60 | }
61 | }
62 | const run = len.mul(sum[2]).minus(sum[0].mul(sum[0]));
63 | const rise = len.mul(sum[3]).minus(sum[0].mul(sum[1]));
64 | const zero = Big(0);
65 | const gradient = run.cmp(zero) === 0 ? zero : roundBig(rise.div(run), options.precisionBig, options.bigRoundingMode);
66 | const intercept = roundBig(sum[1].div(len).sub(gradient.mul(sum[0]).div(len)), options.precisionBig, options.bigRoundingMode);
67 | const predict = (x) => [
68 | roundBig(x, options.precisionBig, options.bigRoundingMode),
69 | roundBig(gradient.mul(x).add(intercept), options.precisionBig, options.bigRoundingMode)
70 | ];
71 | return {
72 | points: options.predictPoints ? data.map((point) => predict(point[0])) : [],
73 | predict,
74 | equation: [gradient, intercept],
75 | string: intercept.cmp(zero) === 0 ? `y = ${gradient}x` : `y = ${gradient}x + ${intercept}`
76 | };
77 | }
78 | const regression = {
79 | linear: (data, options) => {
80 | return _linear(data, {
81 | ...DEFAULT_OPTIONS,
82 | ...options
83 | });
84 | },
85 | linearBig: (data, options) => {
86 | return _linearBig(data, {
87 | ...DEFAULT_OPTIONS,
88 | ...options
89 | });
90 | }
91 | };
92 |
93 | return regression;
94 |
95 | }));
96 |
--------------------------------------------------------------------------------
/thirdParty/regression/regression.min.js:
--------------------------------------------------------------------------------
1 | (function(o,l){typeof exports=="object"&&typeof module<"u"?module.exports=l(require("big.js")):typeof define=="function"&&define.amd?define(["big.js"],l):(o=typeof globalThis<"u"?globalThis:o||self,o.regression=l(o.Big))})(this,function(o){"use strict";const l={precision:2,precisionBig:20,predictPoints:!1,bigRoundingMode:1};function f(e,i){const n=10**i;return Math.round(e*n)/n}function m(e,i,n){return e.round(i,o.roundHalfUp)}function b(e,i){const n=[0,0,0,0,0];let s=0;for(let r=0;r[f(r,i.precision),f(t*r+c,i.precision)];return{points:i.predictPoints?e.map(r=>d(r[0])):[],predict:d,equation:[t,c],string:c===0?`y = ${t}x`:`y = ${t}x + ${c}`}}function M(e,i){const n=[o(0),o(0),o(0),o(0),o(0)];let s=o(0);for(let u=0;u[m(u,i.precisionBig,i.bigRoundingMode),m(c.mul(u).add(d),i.precisionBig,i.bigRoundingMode)];return{points:i.predictPoints?e.map(u=>r(u[0])):[],predict:r,equation:[c,d],string:d.cmp(t)===0?`y = ${c}x`:`y = ${c}x + ${d}`}}return{linear:(e,i)=>b(e,{...l,...i}),linearBig:(e,i)=>M(e,{...l,...i})}});
2 |
--------------------------------------------------------------------------------
/thirdParty/toFormat/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017 Michael Mclaughlin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/thirdParty/toFormat/info.md:
--------------------------------------------------------------------------------
1 | source: https://github.com/MikeMcl/toFormat
2 | version: 2.0.0
3 |
4 |
--------------------------------------------------------------------------------
/thirdParty/toFormat/toFormat.min.js:
--------------------------------------------------------------------------------
1 | /* toFormat.js v2.0.0 https://github.com/MikeMcl/toFormat */
2 | function toFormat(r){"use strict";return r.prototype.toFormat=function(r,o,t){if(!this.e&&0!==this.e)return this.toString();var e,a,i,p,u,c,s,n,S,f,d,m,g,l,z,G=this.format||{},h=this.constructor.format||{};if(r!=u?"object"==typeof r?(t=r,r=u):o!=u?"object"==typeof o?(t=o,o=u):"object"!=typeof t&&(t={}):t={}:t={},e=this.toFixed(r,o).split("."),n=e[0],S=e[1],s=this.s<0?n.slice(1):n,c=s.length,f=t.decimalSeparator,f==u&&(f=G.decimalSeparator,f==u&&(f=h.decimalSeparator,f==u&&(f="."))),d=t.groupSeparator,d==u&&(d=G.groupSeparator,d==u&&(d=h.groupSeparator)),d&&(m=t.groupSize,m==u&&(m=G.groupSize,m==u&&(m=h.groupSize,m==u&&(m=0))),g=t.secondaryGroupSize,g==u&&(g=G.secondaryGroupSize,g==u&&(g=h.secondaryGroupSize,g==u&&(g=0))),g?(a=+g,i=+m,c-=i):(a=+m,i=+g),a>0&&c>0)){for(p=c%a||a,n=s.substr(0,p);c>p;p+=a)n+=d+s.substr(p,a);i>0&&(n+=d+s.slice(p)),this.s<0&&(n="-"+n)}return S?(l=t.fractionGroupSeparator,l==u&&(l=G.fractionGroupSeparator,l==u&&(l=h.fractionGroupSeparator)),l&&(z=t.fractionGroupSize,z==u&&(z=G.fractionGroupSize,z==u&&(z=h.fractionGroupSize,z==u&&(z=0))),z=+z,z&&(S=S.replace(new RegExp("\\d{"+z+"}\\B","g"),"$&"+l))),n+f+S):n},r.format={decimalSeparator:".",groupSeparator:",",groupSize:3,secondaryGroupSize:0,fractionGroupSeparator:"",fractionGroupSize:0},r}"undefined"!=typeof module&&module.exports&&(module.exports=toFormat);
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "target": "es6",
5 | "outDir": "out",
6 | "lib": [
7 | "es6"
8 | ],
9 | "sourceMap": true,
10 | "rootDir": "src",
11 | /* Strict Type-Checking Option */
12 | "strict": true, /* enable all strict type-checking options */
13 | /* Additional Checks */
14 | "noUnusedLocals": true, /* Report errors on unused locals. */
15 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
16 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
17 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
18 | "skipLibCheck": true
19 | },
20 | "files": [
21 | "csvEditorHtml/types.d.ts"
22 | ],
23 | "exclude": [
24 | "node_modules",
25 | ".vscode-test",
26 | "csvEditorHtml/**/*"
27 | ],
28 | "include": [
29 | "src/**/*"
30 | ]
31 | }
32 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "no-string-throw": true,
4 | "no-unused-expression": true,
5 | "no-duplicate-variable": true,
6 | "curly": false,
7 | "class-name": true,
8 | "space-before-function-paren":false,
9 | "space-within-parens":false,
10 | "semicolon": [
11 | false,
12 | "always"
13 | ],
14 | "triple-equals": true
15 | },
16 | "defaultSeverity": "error"
17 | }
18 |
--------------------------------------------------------------------------------