@qe-libs/refi-qda 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +83 -2
- package/dist/refi-qda.es.js +562 -0
- package/dist/refi-qda.es.js.map +1 -0
- package/dist/refi-qda.umd.js +7 -0
- package/dist/refi-qda.umd.js.map +1 -0
- package/package.json +27 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
- First release, from QE Studio's importer (`js/importers/refi-qda.js`):
|
|
6
|
+
`readProject`, `toTable`, `toCSV`, `writeCSV`, `QdaError`.
|
|
7
|
+
- Runs in the browser (its own `DOMParser`) as well as Node (pass one in);
|
|
8
|
+
fflate is bundled; CSV writing no longer needs papaparse.
|
|
9
|
+
- Coders: `coder: 'all'` (every coder agrees) and `coder: 'each'` (one row
|
|
10
|
+
per utterance per coder, with a Coder column) and `coder: 'columns'` (each
|
|
11
|
+
code once per coder, "Code [Coder A]") besides `'any'` and a single
|
|
12
|
+
coder; `codersOf(project)` lists the coders and their coded spans; the
|
|
13
|
+
report records the `coder` used.
|
package/README.md
CHANGED
|
@@ -1,3 +1,84 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @qe-libs/refi-qda
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Reads REFI-QDA projects (`.qdpx`, exported by NVivo, ATLAS.ti, MAXQDA,
|
|
4
|
+
QualCoder and others) into the table ENA works on: one row per utterance,
|
|
5
|
+
a 0/1 column per code. Runs in the browser and in Node; about 6 KB gzipped
|
|
6
|
+
(the unzip library, fflate, is bundled).
|
|
7
|
+
|
|
8
|
+
REFI-QDA is the open exchange standard from the Rotterdam Exchange Format
|
|
9
|
+
Initiative. A `.qdpx` is a zip of `project.qde` (XML: codebook, coders,
|
|
10
|
+
sources, coded spans) and the source texts.
|
|
11
|
+
|
|
12
|
+
## Use
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
import { toCSV } from '@qe-libs/refi-qda';
|
|
16
|
+
|
|
17
|
+
const { csv, report, coders, textColumn } = toCSV(bytes, { coder: 'any' });
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- `bytes`: an `ArrayBuffer`, `Uint8Array` or `Buffer`.
|
|
21
|
+
- `coder`: whose coding becomes the code columns, for projects coded by
|
|
22
|
+
more than one person (see `codersOf`):
|
|
23
|
+
|
|
24
|
+
| `coder` | Rows | A line is 1 for a code when… |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| `'any'` (default) | one per utterance | any coder coded it |
|
|
27
|
+
| `'all'` | one per utterance | every coder coded it |
|
|
28
|
+
| `'each'` | one per utterance per coder, with a `Coder` column | that row's coder coded it |
|
|
29
|
+
| `'columns'` | one per utterance; each code once per coder, `Code [Coder A]` | that column's coder coded it |
|
|
30
|
+
| a coder's name or GUID | one per utterance | that coder coded it |
|
|
31
|
+
|
|
32
|
+
`'each'` and `'columns'` keep every coder's coding: `'each'` so coders can
|
|
33
|
+
be compared or combined in the model (Coder as a unit or a group),
|
|
34
|
+
`'columns'` so each coder's version of a code can be chosen or compared as
|
|
35
|
+
codes.
|
|
36
|
+
- In Node, pass an XML parser: `toCSV(bytes, { DOMParser: require('@xmldom/xmldom').DOMParser })`.
|
|
37
|
+
The browser's own `DOMParser` is used otherwise.
|
|
38
|
+
|
|
39
|
+
In a page without a bundler, `dist/refi-qda.umd.js` defines `window.RefiQda`.
|
|
40
|
+
|
|
41
|
+
| Function | Returns |
|
|
42
|
+
|---|---|
|
|
43
|
+
| `codersOf(project)` | `[{ guid, name, spans }]`: the coders who coded something, with their number of coded spans |
|
|
44
|
+
| `readProject(bytes, { DOMParser })` | The project: `codes`, `users`, `sources` (text, variables, coded spans), `skipped` |
|
|
45
|
+
| `toTable(project, { coder })` | `{ fields, rows, report }` |
|
|
46
|
+
| `toCSV(bytes, { coder, DOMParser })` | `{ csv, fields, rows, report, coders, textColumn }` |
|
|
47
|
+
| `writeCSV(fields, rows)` | CSV text |
|
|
48
|
+
|
|
49
|
+
Errors are `QdaError`s with a `code`: `not-zip`, `no-project`, `bad-xml`,
|
|
50
|
+
`no-coder`, `misaligned`, `no-sources`. Their messages are written for the
|
|
51
|
+
person importing.
|
|
52
|
+
|
|
53
|
+
## The table
|
|
54
|
+
|
|
55
|
+
Columns: `Source`, `Line`, `UserName` (the speaker), `Coder` (with
|
|
56
|
+
`coder: 'each'`), the sources' variables,
|
|
57
|
+
`Text`, then one column per codable code in codebook order (a repeated name
|
|
58
|
+
gets its parent's: `Design talk / Cost`).
|
|
59
|
+
|
|
60
|
+
Rows are the transcript lines that look like `Speaker: text`; a transcript
|
|
61
|
+
without such lines gives one row per non-empty line. A line is 1 for a code
|
|
62
|
+
when a span coded with it (by the chosen coder) overlaps the line.
|
|
63
|
+
|
|
64
|
+
## Positions
|
|
65
|
+
|
|
66
|
+
Coded spans are character positions in each source's text, and exporters
|
|
67
|
+
count them differently: code points, UTF-16 units, or with CRLF line ends as
|
|
68
|
+
two characters. Each source's spans are checked against the span text the
|
|
69
|
+
project records, the reading that matches is used, and `report.notes` says
|
|
70
|
+
when it isn't plain code points. When fewer than 90% match, the import stops
|
|
71
|
+
(`misaligned`) rather than placing codes on the wrong lines.
|
|
72
|
+
|
|
73
|
+
`report` also gives counts, the coders, skipped sources (audio, PDF, images)
|
|
74
|
+
and how many spans were checked.
|
|
75
|
+
|
|
76
|
+
## Develop
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npm install
|
|
80
|
+
npm test # builds dist/, then runs tests/ (node --test)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`tests/fixtures/` holds a small invented project and the CSV expected for
|
|
84
|
+
each coder; `make_sample.py` regenerates them.
|
|
@@ -0,0 +1,562 @@
|
|
|
1
|
+
var F = Uint8Array, Q = Uint16Array, $e = Int32Array, de = new F([
|
|
2
|
+
0,
|
|
3
|
+
0,
|
|
4
|
+
0,
|
|
5
|
+
0,
|
|
6
|
+
0,
|
|
7
|
+
0,
|
|
8
|
+
0,
|
|
9
|
+
0,
|
|
10
|
+
1,
|
|
11
|
+
1,
|
|
12
|
+
1,
|
|
13
|
+
1,
|
|
14
|
+
2,
|
|
15
|
+
2,
|
|
16
|
+
2,
|
|
17
|
+
2,
|
|
18
|
+
3,
|
|
19
|
+
3,
|
|
20
|
+
3,
|
|
21
|
+
3,
|
|
22
|
+
4,
|
|
23
|
+
4,
|
|
24
|
+
4,
|
|
25
|
+
4,
|
|
26
|
+
5,
|
|
27
|
+
5,
|
|
28
|
+
5,
|
|
29
|
+
5,
|
|
30
|
+
0,
|
|
31
|
+
/* unused */
|
|
32
|
+
0,
|
|
33
|
+
0,
|
|
34
|
+
/* impossible */
|
|
35
|
+
0
|
|
36
|
+
]), we = new F([
|
|
37
|
+
0,
|
|
38
|
+
0,
|
|
39
|
+
0,
|
|
40
|
+
0,
|
|
41
|
+
1,
|
|
42
|
+
1,
|
|
43
|
+
2,
|
|
44
|
+
2,
|
|
45
|
+
3,
|
|
46
|
+
3,
|
|
47
|
+
4,
|
|
48
|
+
4,
|
|
49
|
+
5,
|
|
50
|
+
5,
|
|
51
|
+
6,
|
|
52
|
+
6,
|
|
53
|
+
7,
|
|
54
|
+
7,
|
|
55
|
+
8,
|
|
56
|
+
8,
|
|
57
|
+
9,
|
|
58
|
+
9,
|
|
59
|
+
10,
|
|
60
|
+
10,
|
|
61
|
+
11,
|
|
62
|
+
11,
|
|
63
|
+
12,
|
|
64
|
+
12,
|
|
65
|
+
13,
|
|
66
|
+
13,
|
|
67
|
+
/* unused */
|
|
68
|
+
0,
|
|
69
|
+
0
|
|
70
|
+
]), Te = new F([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]), xe = function(e, r) {
|
|
71
|
+
for (var n = new Q(31), t = 0; t < 31; ++t)
|
|
72
|
+
n[t] = r += 1 << e[t - 1];
|
|
73
|
+
for (var o = new $e(n[30]), t = 1; t < 30; ++t)
|
|
74
|
+
for (var c = n[t]; c < n[t + 1]; ++c)
|
|
75
|
+
o[c] = c - n[t] << 5 | t;
|
|
76
|
+
return { b: n, r: o };
|
|
77
|
+
}, be = xe(de, 2), Ae = be.b, Ee = be.r;
|
|
78
|
+
Ae[28] = 258, Ee[258] = 28;
|
|
79
|
+
var Fe = xe(we, 0), Me = Fe.b, ae = new Q(32768);
|
|
80
|
+
for (var w = 0; w < 32768; ++w) {
|
|
81
|
+
var q = (w & 43690) >> 1 | (w & 21845) << 1;
|
|
82
|
+
q = (q & 52428) >> 2 | (q & 13107) << 2, q = (q & 61680) >> 4 | (q & 3855) << 4, ae[w] = ((q & 65280) >> 8 | (q & 255) << 8) >> 1;
|
|
83
|
+
}
|
|
84
|
+
var H = (function(e, r, n) {
|
|
85
|
+
for (var t = e.length, o = 0, c = new Q(r); o < t; ++o)
|
|
86
|
+
e[o] && ++c[e[o] - 1];
|
|
87
|
+
var a = new Q(r);
|
|
88
|
+
for (o = 1; o < r; ++o)
|
|
89
|
+
a[o] = a[o - 1] + c[o - 1] << 1;
|
|
90
|
+
var s;
|
|
91
|
+
if (n) {
|
|
92
|
+
s = new Q(1 << r);
|
|
93
|
+
var m = 15 - r;
|
|
94
|
+
for (o = 0; o < t; ++o)
|
|
95
|
+
if (e[o])
|
|
96
|
+
for (var v = o << 4 | e[o], d = r - e[o], i = a[e[o] - 1]++ << d, p = i | (1 << d) - 1; i <= p; ++i)
|
|
97
|
+
s[ae[i] >> m] = v;
|
|
98
|
+
} else
|
|
99
|
+
for (s = new Q(t), o = 0; o < t; ++o)
|
|
100
|
+
e[o] && (s[o] = ae[a[e[o] - 1]++] >> 15 - e[o]);
|
|
101
|
+
return s;
|
|
102
|
+
}), J = new F(288);
|
|
103
|
+
for (var w = 0; w < 144; ++w)
|
|
104
|
+
J[w] = 8;
|
|
105
|
+
for (var w = 144; w < 256; ++w)
|
|
106
|
+
J[w] = 9;
|
|
107
|
+
for (var w = 256; w < 280; ++w)
|
|
108
|
+
J[w] = 7;
|
|
109
|
+
for (var w = 280; w < 288; ++w)
|
|
110
|
+
J[w] = 8;
|
|
111
|
+
var Ce = new F(32);
|
|
112
|
+
for (var w = 0; w < 32; ++w)
|
|
113
|
+
Ce[w] = 5;
|
|
114
|
+
var Ue = /* @__PURE__ */ H(J, 9, 1), De = /* @__PURE__ */ H(Ce, 5, 1), ne = function(e) {
|
|
115
|
+
for (var r = e[0], n = 1; n < e.length; ++n)
|
|
116
|
+
e[n] > r && (r = e[n]);
|
|
117
|
+
return r;
|
|
118
|
+
}, N = function(e, r, n) {
|
|
119
|
+
var t = r / 8 | 0;
|
|
120
|
+
return (e[t] | e[t + 1] << 8) >> (r & 7) & n;
|
|
121
|
+
}, te = function(e, r) {
|
|
122
|
+
var n = r / 8 | 0;
|
|
123
|
+
return (e[n] | e[n + 1] << 8 | e[n + 2] << 16) >> (r & 7);
|
|
124
|
+
}, Ne = function(e) {
|
|
125
|
+
return (e + 7) / 8 | 0;
|
|
126
|
+
}, fe = function(e, r, n) {
|
|
127
|
+
return (r == null || r < 0) && (r = 0), (n == null || n > e.length) && (n = e.length), new F(e.subarray(r, n));
|
|
128
|
+
}, Oe = [
|
|
129
|
+
"unexpected EOF",
|
|
130
|
+
"invalid block type",
|
|
131
|
+
"invalid length/literal",
|
|
132
|
+
"invalid distance",
|
|
133
|
+
"stream finished",
|
|
134
|
+
"no stream handler",
|
|
135
|
+
,
|
|
136
|
+
// determined by compression function
|
|
137
|
+
"no callback",
|
|
138
|
+
"invalid UTF-8 data",
|
|
139
|
+
"extra field too long",
|
|
140
|
+
"date not in range 1980-2099",
|
|
141
|
+
"filename too long",
|
|
142
|
+
"stream finishing",
|
|
143
|
+
"invalid zip data"
|
|
144
|
+
// determined by unknown compression method
|
|
145
|
+
], E = function(e, r, n) {
|
|
146
|
+
var t = new Error(r || Oe[e]);
|
|
147
|
+
if (t.code = e, Error.captureStackTrace && Error.captureStackTrace(t, E), !n)
|
|
148
|
+
throw t;
|
|
149
|
+
return t;
|
|
150
|
+
}, Pe = function(e, r, n, t) {
|
|
151
|
+
var o = e.length, c = t ? t.length : 0;
|
|
152
|
+
if (!o || r.f && !r.l)
|
|
153
|
+
return n || new F(0);
|
|
154
|
+
var a = !n, s = a || r.i != 2, m = r.i;
|
|
155
|
+
a && (n = new F(o * 3));
|
|
156
|
+
var v = function(pe) {
|
|
157
|
+
var ve = n.length;
|
|
158
|
+
if (pe > ve) {
|
|
159
|
+
var ge = new F(Math.max(ve * 2, pe));
|
|
160
|
+
ge.set(n), n = ge;
|
|
161
|
+
}
|
|
162
|
+
}, d = r.f || 0, i = r.p || 0, p = r.b || 0, u = r.l, A = r.d, x = r.m, g = r.n, k = o * 8;
|
|
163
|
+
do {
|
|
164
|
+
if (!u) {
|
|
165
|
+
d = N(e, i, 1);
|
|
166
|
+
var f = N(e, i + 1, 3);
|
|
167
|
+
if (i += 3, f)
|
|
168
|
+
if (f == 1)
|
|
169
|
+
u = Ue, A = De, x = 9, g = 5;
|
|
170
|
+
else if (f == 2) {
|
|
171
|
+
var R = N(e, i, 31) + 257, z = N(e, i + 10, 15) + 4, W = R + N(e, i + 5, 31) + 1;
|
|
172
|
+
i += 14;
|
|
173
|
+
for (var B = new F(W), j = new F(19), y = 0; y < z; ++y)
|
|
174
|
+
j[Te[y]] = N(e, i + y * 3, 7);
|
|
175
|
+
i += z * 3;
|
|
176
|
+
for (var Y = ne(j), K = (1 << Y) - 1, l = H(j, Y, 1), y = 0; y < W; ) {
|
|
177
|
+
var C = l[N(e, i, K)];
|
|
178
|
+
i += C & 15;
|
|
179
|
+
var h = C >> 4;
|
|
180
|
+
if (h < 16)
|
|
181
|
+
B[y++] = h;
|
|
182
|
+
else {
|
|
183
|
+
var T = 0, b = 0;
|
|
184
|
+
for (h == 16 ? (b = 3 + N(e, i, 3), i += 2, T = B[y - 1]) : h == 17 ? (b = 3 + N(e, i, 7), i += 3) : h == 18 && (b = 11 + N(e, i, 127), i += 7); b--; )
|
|
185
|
+
B[y++] = T;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
var M = B.subarray(0, R), O = B.subarray(R);
|
|
189
|
+
x = ne(M), g = ne(O), u = H(M, x, 1), A = H(O, g, 1);
|
|
190
|
+
} else
|
|
191
|
+
E(1);
|
|
192
|
+
else {
|
|
193
|
+
var h = Ne(i) + 4, S = e[h - 4] | e[h - 3] << 8, $ = h + S;
|
|
194
|
+
if ($ > o) {
|
|
195
|
+
m && E(0);
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
s && v(p + S), n.set(e.subarray(h, $), p), r.b = p += S, r.p = i = $ * 8, r.f = d;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (i > k) {
|
|
202
|
+
m && E(0);
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
s && v(p + 131072);
|
|
207
|
+
for (var ke = (1 << x) - 1, ye = (1 << g) - 1, _ = i; ; _ = i) {
|
|
208
|
+
var T = u[te(e, i) & ke], L = T >> 4;
|
|
209
|
+
if (i += T & 15, i > k) {
|
|
210
|
+
m && E(0);
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
if (T || E(2), L < 256)
|
|
214
|
+
n[p++] = L;
|
|
215
|
+
else if (L == 256) {
|
|
216
|
+
_ = i, u = null;
|
|
217
|
+
break;
|
|
218
|
+
} else {
|
|
219
|
+
var le = L - 254;
|
|
220
|
+
if (L > 264) {
|
|
221
|
+
var y = L - 257, X = de[y];
|
|
222
|
+
le = N(e, i, (1 << X) - 1) + Ae[y], i += X;
|
|
223
|
+
}
|
|
224
|
+
var ee = A[te(e, i) & ye], re = ee >> 4;
|
|
225
|
+
ee || E(3), i += ee & 15;
|
|
226
|
+
var O = Me[re];
|
|
227
|
+
if (re > 3) {
|
|
228
|
+
var X = we[re];
|
|
229
|
+
O += te(e, i) & (1 << X) - 1, i += X;
|
|
230
|
+
}
|
|
231
|
+
if (i > k) {
|
|
232
|
+
m && E(0);
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
s && v(p + 131072);
|
|
236
|
+
var ue = p + le;
|
|
237
|
+
if (p < O) {
|
|
238
|
+
var he = c - O, Se = Math.min(O, ue);
|
|
239
|
+
for (he + p < 0 && E(3); p < Se; ++p)
|
|
240
|
+
n[p] = t[he + p];
|
|
241
|
+
}
|
|
242
|
+
for (; p < ue; ++p)
|
|
243
|
+
n[p] = n[p - O];
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
r.l = u, r.p = _, r.b = p, r.f = d, u && (d = 1, r.m = x, r.d = A, r.n = g);
|
|
247
|
+
} while (!d);
|
|
248
|
+
return p != n.length && a ? fe(n, 0, p) : n.subarray(0, p);
|
|
249
|
+
}, Ve = /* @__PURE__ */ new F(0), V = function(e, r) {
|
|
250
|
+
return e[r] | e[r + 1] << 8;
|
|
251
|
+
}, D = function(e, r) {
|
|
252
|
+
return (e[r] | e[r + 1] << 8 | e[r + 2] << 16 | e[r + 3] << 24) >>> 0;
|
|
253
|
+
}, oe = function(e, r) {
|
|
254
|
+
return D(e, r) + D(e, r + 4) * 4294967296;
|
|
255
|
+
};
|
|
256
|
+
function Re(e, r) {
|
|
257
|
+
return Pe(e, { i: 2 }, r && r.out, r && r.dictionary);
|
|
258
|
+
}
|
|
259
|
+
var se = typeof TextDecoder < "u" && /* @__PURE__ */ new TextDecoder(), Be = 0;
|
|
260
|
+
try {
|
|
261
|
+
se.decode(Ve, { stream: !0 }), Be = 1;
|
|
262
|
+
} catch {
|
|
263
|
+
}
|
|
264
|
+
var qe = function(e) {
|
|
265
|
+
for (var r = "", n = 0; ; ) {
|
|
266
|
+
var t = e[n++], o = (t > 127) + (t > 223) + (t > 239);
|
|
267
|
+
if (n + o > e.length)
|
|
268
|
+
return { s: r, r: fe(e, n - 1) };
|
|
269
|
+
o ? o == 3 ? (t = ((t & 15) << 18 | (e[n++] & 63) << 12 | (e[n++] & 63) << 6 | e[n++] & 63) - 65536, r += String.fromCharCode(55296 | t >> 10, 56320 | t & 1023)) : o & 1 ? r += String.fromCharCode((t & 31) << 6 | e[n++] & 63) : r += String.fromCharCode((t & 15) << 12 | (e[n++] & 63) << 6 | e[n++] & 63) : r += String.fromCharCode(t);
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
function ie(e, r) {
|
|
273
|
+
if (r) {
|
|
274
|
+
for (var n = "", t = 0; t < e.length; t += 16384)
|
|
275
|
+
n += String.fromCharCode.apply(null, e.subarray(t, t + 16384));
|
|
276
|
+
return n;
|
|
277
|
+
} else {
|
|
278
|
+
if (se)
|
|
279
|
+
return se.decode(e);
|
|
280
|
+
var o = qe(e), c = o.s, n = o.r;
|
|
281
|
+
return n.length && E(8), c;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
var ze = function(e, r) {
|
|
285
|
+
return r + 30 + V(e, r + 26) + V(e, r + 28);
|
|
286
|
+
}, je = function(e, r, n) {
|
|
287
|
+
var t = V(e, r + 28), o = V(e, r + 30), c = ie(e.subarray(r + 46, r + 46 + t), !(V(e, r + 8) & 2048)), a = r + 46 + t, s = Ie(e, a, o, n, D(e, r + 20), D(e, r + 24), D(e, r + 42)), m = s[0], v = s[1], d = s[2];
|
|
288
|
+
return [V(e, r + 10), m, v, c, a + o + V(e, r + 32), d];
|
|
289
|
+
}, Ie = function(e, r, n, t, o, c, a) {
|
|
290
|
+
var s = o == 4294967295, m = c == 4294967295, v = a == 4294967295, d = r + n, i = s + m + v;
|
|
291
|
+
if (t && i) {
|
|
292
|
+
for (; r + 4 < d; r += 4 + V(e, r + 2))
|
|
293
|
+
if (V(e, r) == 1)
|
|
294
|
+
return [
|
|
295
|
+
s ? oe(e, r + 4 + 8 * m) : o,
|
|
296
|
+
m ? oe(e, r + 4) : c,
|
|
297
|
+
v ? oe(e, r + 4 + 8 * (m + s)) : a,
|
|
298
|
+
1
|
|
299
|
+
];
|
|
300
|
+
t < 2 && E(13);
|
|
301
|
+
}
|
|
302
|
+
return [o, c, a, 0];
|
|
303
|
+
};
|
|
304
|
+
function Le(e, r) {
|
|
305
|
+
for (var n = {}, t = e.length - 22; D(e, t) != 101010256; --t)
|
|
306
|
+
(!t || e.length - t > 65558) && E(13);
|
|
307
|
+
var o = V(e, t + 8);
|
|
308
|
+
if (!o)
|
|
309
|
+
return {};
|
|
310
|
+
var c = D(e, t + 16), a = D(e, t - 20) == 117853008;
|
|
311
|
+
if (a) {
|
|
312
|
+
var s = D(e, t - 12);
|
|
313
|
+
a = D(e, s) == 101075792, a && (o = D(e, s + 32), c = D(e, s + 48));
|
|
314
|
+
}
|
|
315
|
+
for (var m = 0; m < o; ++m) {
|
|
316
|
+
var v = je(e, c, a), d = v[0], i = v[1], p = v[2], u = v[3], A = v[4], x = v[5], g = ze(e, x);
|
|
317
|
+
c = A, d ? d == 8 ? n[u] = Re(e.subarray(g, g + i), { out: new F(p) }) : E(14, "unknown compression type " + d) : n[u] = fe(e, g, g + i);
|
|
318
|
+
}
|
|
319
|
+
return n;
|
|
320
|
+
}
|
|
321
|
+
class G extends Error {
|
|
322
|
+
constructor(r, n) {
|
|
323
|
+
super(r), this.name = "QdaError", this.code = n;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
const U = (e, r) => e ? Array.from(e.childNodes).filter((n) => n.nodeType === 1 && (!r || n.localName === r)) : [], P = (e, r) => U(e, r)[0], Z = (e) => e && e.textContent || "";
|
|
327
|
+
function Qe(e, r) {
|
|
328
|
+
const n = [];
|
|
329
|
+
let t;
|
|
330
|
+
try {
|
|
331
|
+
t = new e({ onError: (c, a) => {
|
|
332
|
+
c !== "warning" && n.push(a);
|
|
333
|
+
} }).parseFromString(r, "text/xml");
|
|
334
|
+
} catch (c) {
|
|
335
|
+
n.push(c.message);
|
|
336
|
+
}
|
|
337
|
+
const o = t && t.getElementsByTagName("parsererror")[0];
|
|
338
|
+
return o && n.push(Z(o).trim().split(`
|
|
339
|
+
`)[0]), { root: n.length ? null : t && t.documentElement, error: n[0] };
|
|
340
|
+
}
|
|
341
|
+
function me(e, r) {
|
|
342
|
+
const n = r.toLowerCase(), t = Object.keys(e).find((o) => o.toLowerCase() === n || o.toLowerCase().endsWith("/" + n));
|
|
343
|
+
return t ? e[t] : null;
|
|
344
|
+
}
|
|
345
|
+
function Ge(e, { DOMParser: r = globalThis.DOMParser } = {}) {
|
|
346
|
+
if (!r) throw new Error("readProject needs a DOMParser: pass { DOMParser } (in Node, from @xmldom/xmldom).");
|
|
347
|
+
let n;
|
|
348
|
+
try {
|
|
349
|
+
n = Le(e instanceof Uint8Array ? e : new Uint8Array(e));
|
|
350
|
+
} catch {
|
|
351
|
+
throw new G("This file isn't a .qdpx project (it can't be opened as a zip archive).", "not-zip");
|
|
352
|
+
}
|
|
353
|
+
const t = me(n, "project.qde");
|
|
354
|
+
if (!t) throw new G("This .qdpx has no project.qde inside, so it isn't a REFI-QDA project.", "no-project");
|
|
355
|
+
const { root: o, error: c } = Qe(r, ie(t));
|
|
356
|
+
if (!o || o.localName !== "Project")
|
|
357
|
+
throw new G(`project.qde couldn't be read as a REFI-QDA project${c ? `: ${c}` : ""}.`, "bad-xml");
|
|
358
|
+
const a = new Map(U(P(o, "Users"), "User").map((u) => [u.getAttribute("guid"), u.getAttribute("name") || u.getAttribute("guid")])), s = [];
|
|
359
|
+
(function u(A, x) {
|
|
360
|
+
for (const g of U(A, "Code")) {
|
|
361
|
+
const k = g.getAttribute("name") || "(unnamed code)";
|
|
362
|
+
s.push({
|
|
363
|
+
guid: g.getAttribute("guid"),
|
|
364
|
+
name: k,
|
|
365
|
+
parent: x,
|
|
366
|
+
codable: g.getAttribute("isCodable") !== "false",
|
|
367
|
+
description: Z(P(g, "Description")).trim()
|
|
368
|
+
}), u(g, k);
|
|
369
|
+
}
|
|
370
|
+
})(P(P(o, "CodeBook"), "Codes"), null);
|
|
371
|
+
const m = new Map(U(P(o, "Variables"), "Variable").map((u) => [u.getAttribute("guid"), u.getAttribute("name")])), v = (u) => {
|
|
372
|
+
var g;
|
|
373
|
+
const A = (g = P(u, "VariableRef")) == null ? void 0 : g.getAttribute("targetGUID"), x = U(u).find((k) => k.localName !== "VariableRef");
|
|
374
|
+
return [m.get(A) || A, Z(x).trim()];
|
|
375
|
+
}, d = /* @__PURE__ */ new Map();
|
|
376
|
+
for (const u of U(P(o, "Cases"), "Case")) {
|
|
377
|
+
const A = U(u, "SourceRef");
|
|
378
|
+
if (u.getAttribute("name") || A.length !== 1) continue;
|
|
379
|
+
const x = A[0].getAttribute("targetGUID"), g = d.get(x) || {};
|
|
380
|
+
for (const k of U(u, "VariableValue")) {
|
|
381
|
+
const [f, h] = v(k);
|
|
382
|
+
g[f] = h;
|
|
383
|
+
}
|
|
384
|
+
d.set(x, g);
|
|
385
|
+
}
|
|
386
|
+
const i = [], p = [];
|
|
387
|
+
for (const u of U(P(o, "Sources"))) {
|
|
388
|
+
const A = u.getAttribute("name") || u.getAttribute("guid");
|
|
389
|
+
if (u.localName !== "TextSource") {
|
|
390
|
+
p.push(`${A} (${u.localName.replace(/Source$/, "").toLowerCase()})`);
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
let x = null;
|
|
394
|
+
const g = P(u, "PlainTextContent");
|
|
395
|
+
if (g) x = Z(g);
|
|
396
|
+
else {
|
|
397
|
+
const h = u.getAttribute("plainTextPath") || "", S = /^internal:\/\/(.+)$/.exec(h);
|
|
398
|
+
if (!S) {
|
|
399
|
+
p.push(`${A} (its text is stored outside the project: ${h || "no path"})`);
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
const $ = me(n, "Sources/" + S[1]);
|
|
403
|
+
if (!$) {
|
|
404
|
+
p.push(`${A} (Sources/${S[1]} is missing)`);
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
x = ie($);
|
|
408
|
+
}
|
|
409
|
+
x = x.replace(/^/, "");
|
|
410
|
+
const k = { ...d.get(u.getAttribute("guid")) || {} };
|
|
411
|
+
for (const h of U(u, "VariableValue")) {
|
|
412
|
+
const [S, $] = v(h);
|
|
413
|
+
k[S] = $;
|
|
414
|
+
}
|
|
415
|
+
const f = U(u, "PlainTextSelection").map((h) => ({
|
|
416
|
+
start: +h.getAttribute("startPosition"),
|
|
417
|
+
end: +h.getAttribute("endPosition"),
|
|
418
|
+
name: h.getAttribute("name"),
|
|
419
|
+
codings: U(h, "Coding").map((S) => {
|
|
420
|
+
var $;
|
|
421
|
+
return {
|
|
422
|
+
code: ($ = P(S, "CodeRef")) == null ? void 0 : $.getAttribute("targetGUID"),
|
|
423
|
+
user: S.getAttribute("creatingUser") || h.getAttribute("creatingUser")
|
|
424
|
+
};
|
|
425
|
+
})
|
|
426
|
+
}));
|
|
427
|
+
i.push({ guid: u.getAttribute("guid"), name: A, raw: x, vars: k, selections: f });
|
|
428
|
+
}
|
|
429
|
+
return { name: o.getAttribute("name") || "", users: a, codes: s, sources: i, skipped: p };
|
|
430
|
+
}
|
|
431
|
+
function We(e) {
|
|
432
|
+
const r = Array.from(e), n = [];
|
|
433
|
+
let t = 0;
|
|
434
|
+
for (let s = 0; s < r.length; s++)
|
|
435
|
+
n.push(t), !(r[s] === "\r" && r[s + 1] === `
|
|
436
|
+
`) && t++;
|
|
437
|
+
n.push(t);
|
|
438
|
+
const o = e.replace(/\r\n?/g, `
|
|
439
|
+
`), c = [];
|
|
440
|
+
let a = 0;
|
|
441
|
+
for (const s of o)
|
|
442
|
+
c.push(a), s.length === 2 && c.push(a), a++;
|
|
443
|
+
return c.push(a), {
|
|
444
|
+
text: o,
|
|
445
|
+
cps: Array.from(o),
|
|
446
|
+
ways: {
|
|
447
|
+
"code points": (s) => s,
|
|
448
|
+
"code points, CRLF as two": (s) => n[s],
|
|
449
|
+
"UTF-16 units": (s) => c[s]
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
const ce = (e) => String(e || "").replace(/\s+/g, " ").trim();
|
|
454
|
+
function Ke(e, r, n) {
|
|
455
|
+
let t = 0;
|
|
456
|
+
for (const o of r) {
|
|
457
|
+
const c = n(o.start), a = n(o.end);
|
|
458
|
+
if (c === void 0 || a === void 0 || c > a) continue;
|
|
459
|
+
const s = ce(e.slice(c, a).join("")), m = ce(o.name);
|
|
460
|
+
s && (s === m || m.length >= 20 && s.startsWith(m)) && t++;
|
|
461
|
+
}
|
|
462
|
+
return t;
|
|
463
|
+
}
|
|
464
|
+
const Xe = new RegExp("^\\s*(\\p{L}[^:\\n]{0,39}?)\\s*:\\s+(\\S.*)$", "u"), I = "(no coder)";
|
|
465
|
+
function He(e) {
|
|
466
|
+
const r = new Set(e.codes.filter((a) => a.codable).map((a) => a.guid)), n = /* @__PURE__ */ new Map();
|
|
467
|
+
for (const a of e.sources) for (const s of a.selections)
|
|
468
|
+
new Set(s.codings.filter((v) => r.has(v.code)).map((v) => v.user || I)).forEach((v) => n.set(v, (n.get(v) || 0) + 1));
|
|
469
|
+
const t = [...e.users].filter(([a]) => n.has(a)).map(([a, s]) => ({ guid: a, name: s, spans: n.get(a) })), o = [...n.keys()].filter((a) => a !== I && !e.users.has(a)).map((a) => ({ guid: a, name: a, spans: n.get(a) })), c = n.has(I) ? [{ guid: I, name: I, spans: n.get(I) }] : [];
|
|
470
|
+
return [...t, ...o, ...c];
|
|
471
|
+
}
|
|
472
|
+
function Je(e, { coder: r = "any" } = {}) {
|
|
473
|
+
const n = { sources: 0, rows: 0, codes: 0, coders: [...new Set(e.users.values())], skippedSources: e.skipped, notes: [] }, t = He(e);
|
|
474
|
+
n.coder = r;
|
|
475
|
+
let o = null;
|
|
476
|
+
if (!["any", "all", "each", "columns"].includes(r) && (o = [...e.users].filter(([f, h]) => h === r || f === r).map(([f]) => f), !o.length && t.some((f) => f.guid === r) && (o = [r]), !o.length))
|
|
477
|
+
throw new G(`There's no coder called “${r}” in this project.`, "no-coder");
|
|
478
|
+
const c = r === "each", a = r === "columns", s = e.codes.filter((f) => f.codable), m = {};
|
|
479
|
+
s.forEach((f) => {
|
|
480
|
+
m[f.name] = (m[f.name] || 0) + 1;
|
|
481
|
+
});
|
|
482
|
+
const v = s.map((f) => m[f.name] > 1 && f.parent ? `${f.parent} / ${f.name}` : f.name);
|
|
483
|
+
n.codes = v.length;
|
|
484
|
+
const d = [];
|
|
485
|
+
e.sources.forEach((f) => Object.keys(f.vars).forEach((h) => {
|
|
486
|
+
d.includes(h) || d.push(h);
|
|
487
|
+
}));
|
|
488
|
+
const i = ["Source", "Line", "UserName", ...c ? ["Coder"] : [], ...d, "Text"], p = a ? v.flatMap((f) => t.map((h) => `${f} [${h.name}]`)) : v.map((f) => i.includes(f) ? `${f} (code)` : f), u = [...i, ...p];
|
|
489
|
+
n.columns = p.length;
|
|
490
|
+
const A = new Map(s.map((f, h) => [f.guid, h]));
|
|
491
|
+
let x = 0, g = 0;
|
|
492
|
+
const k = [];
|
|
493
|
+
for (const f of e.sources) {
|
|
494
|
+
n.sources++;
|
|
495
|
+
const { text: h, cps: S, ways: $ } = We(f.raw), R = f.selections.filter((l) => ce(l.name));
|
|
496
|
+
let z = "code points";
|
|
497
|
+
if (R.length) {
|
|
498
|
+
let l = -1;
|
|
499
|
+
for (const [C, T] of Object.entries($)) {
|
|
500
|
+
const b = Ke(S, R, T);
|
|
501
|
+
b > l && (l = b, z = C);
|
|
502
|
+
}
|
|
503
|
+
if (x += R.length, g += l, l < R.length * 0.9)
|
|
504
|
+
throw new G(`The coded spans in “${f.name}” don't line up with its text (${l} of ${R.length} match), so the codes can't be placed on lines. The file may have been changed after it was coded.`, "misaligned");
|
|
505
|
+
z !== "code points" && n.notes.push(`“${f.name}”: positions counted as ${z}.`);
|
|
506
|
+
}
|
|
507
|
+
const W = $[z], B = [];
|
|
508
|
+
let j = 0;
|
|
509
|
+
for (const l of h.split(`
|
|
510
|
+
`)) {
|
|
511
|
+
const C = Array.from(l).length;
|
|
512
|
+
B.push({ text: l, start: j, end: j + C }), j += C + 1;
|
|
513
|
+
}
|
|
514
|
+
let y = B.map((l, C) => {
|
|
515
|
+
const T = Xe.exec(l.text);
|
|
516
|
+
return T && { ...l, i: C, speaker: T[1], said: T[2].trim() };
|
|
517
|
+
}).filter(Boolean);
|
|
518
|
+
y.length || (y = B.map((l, C) => l.text.trim() && { ...l, i: C, speaker: "", said: l.text.trim() }).filter(Boolean), n.notes.push(`“${f.name}” has no “Name: text” lines, so each non-empty line is a row with no speaker.`));
|
|
519
|
+
const Y = f.selections.map((l) => ({
|
|
520
|
+
a: W(l.start),
|
|
521
|
+
b: W(l.end),
|
|
522
|
+
codings: l.codings.filter((C) => A.has(C.code)).map((C) => [A.get(C.code), C.user || I])
|
|
523
|
+
})).filter((l) => l.codings.length && l.a !== void 0 && l.b !== void 0), K = d.map((l) => f.vars[l] ?? "");
|
|
524
|
+
for (const l of y) {
|
|
525
|
+
const C = v.map(() => /* @__PURE__ */ new Set());
|
|
526
|
+
for (const b of Y) b.a < l.end && b.b > l.start && b.codings.forEach(([M, O]) => C[M].add(O));
|
|
527
|
+
if (a) {
|
|
528
|
+
k.push([f.name, l.i + 1, l.speaker, ...K, l.said, ...C.flatMap((b) => t.map((M) => +b.has(M.guid)))]);
|
|
529
|
+
continue;
|
|
530
|
+
}
|
|
531
|
+
if (c) {
|
|
532
|
+
for (const b of t) k.push([f.name, l.i + 1, l.speaker, b.name, ...K, l.said, ...C.map((M) => +M.has(b.guid))]);
|
|
533
|
+
continue;
|
|
534
|
+
}
|
|
535
|
+
const T = r === "any" ? (b) => b.size > 0 : r === "all" ? (b) => t.length > 0 && t.every((M) => b.has(M.guid)) : (b) => o.some((M) => b.has(M));
|
|
536
|
+
k.push([f.name, l.i + 1, l.speaker, ...K, l.said, ...C.map((b) => +T(b))]);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
if (n.rows = k.length, n.positions = x ? `${g} of ${x} coded spans checked against their text` : "no span text to check positions against", !n.sources) throw new G(`This project has no text sources to import${e.skipped.length ? ` (skipped: ${e.skipped.join("; ")})` : ""}.`, "no-sources");
|
|
540
|
+
return { fields: u, rows: k, report: n };
|
|
541
|
+
}
|
|
542
|
+
const Ye = (e) => {
|
|
543
|
+
const r = e == null ? "" : String(e);
|
|
544
|
+
return /[",\r\n]/.test(r) || r[0] === " " || r[r.length - 1] === " " ? `"${r.replace(/"/g, '""')}"` : r;
|
|
545
|
+
};
|
|
546
|
+
function Ze(e, r) {
|
|
547
|
+
return [e, ...r].map((n) => n.map(Ye).join(",")).join(`
|
|
548
|
+
`);
|
|
549
|
+
}
|
|
550
|
+
function _e(e, { coder: r = "any", DOMParser: n } = {}) {
|
|
551
|
+
const { fields: t, rows: o, report: c } = Je(Ge(e, { DOMParser: n }), { coder: r });
|
|
552
|
+
return { csv: Ze(t, o), fields: t, rows: o, report: c, coders: c.coders, textColumn: "Text" };
|
|
553
|
+
}
|
|
554
|
+
export {
|
|
555
|
+
G as QdaError,
|
|
556
|
+
He as codersOf,
|
|
557
|
+
Ge as readProject,
|
|
558
|
+
_e as toCSV,
|
|
559
|
+
Je as toTable,
|
|
560
|
+
Ze as writeCSV
|
|
561
|
+
};
|
|
562
|
+
//# sourceMappingURL=refi-qda.es.js.map
|