@peaceroad/markdown-it-table-ex 0.2.0 → 0.3.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/index.js CHANGED
@@ -1,4 +1,58 @@
1
- const checkedTdReg = /^\*\*[\s\S]*?\*\*$/;
1
+ const getLeadingStrongCloseIndex = (inline) => {
2
+ if (!inline || !Array.isArray(inline.children) || inline.children.length < 3) {
3
+ return -1;
4
+ }
5
+ const children = inline.children;
6
+ let start = 0;
7
+ while (start < children.length &&
8
+ children[start].type === 'text' &&
9
+ children[start].content === '') {
10
+ start++;
11
+ }
12
+ if (start >= children.length || children[start].type !== 'strong_open') {
13
+ return -1;
14
+ }
15
+ let depth = 0;
16
+ for (let i = start; i < children.length; i++) {
17
+ const type = children[i].type;
18
+ if (type === 'strong_open') {
19
+ depth++;
20
+ } else if (type === 'strong_close') {
21
+ depth--;
22
+ if (depth === 0) {
23
+ return i;
24
+ }
25
+ if (depth < 0) {
26
+ return -1;
27
+ }
28
+ }
29
+ }
30
+ return -1;
31
+ };
32
+
33
+ const isStrongWrappedInline = (inline, allowFallback) => {
34
+ if (!inline || typeof inline.content !== 'string') return false;
35
+ const content = inline.content;
36
+ if (!content.startsWith('**') || !content.endsWith('**')) return false;
37
+ if (!Array.isArray(inline.children)) return !!allowFallback;
38
+ if (getLeadingStrongCloseIndex(inline) !== -1) return true;
39
+ return !!allowFallback;
40
+ };
41
+
42
+ const hasLeadingStrongMarker = (inline, allowFallback) => {
43
+ if (getLeadingStrongCloseIndex(inline) !== -1) return true;
44
+ if (!allowFallback || !inline || typeof inline.content !== 'string') return false;
45
+ return inline.content.startsWith('**');
46
+ };
47
+
48
+ const hasInlineRule = (md, name) => {
49
+ const rules = md && md.inline && md.inline.ruler && md.inline.ruler.__rules__;
50
+ if (!Array.isArray(rules)) return false;
51
+ for (let i = 0; i < rules.length; i++) {
52
+ if (rules[i].name === name) return true;
53
+ }
54
+ return false;
55
+ };
2
56
 
3
57
  const createTokenTemplate = (tokenType, tag, nesting, level) => ({
4
58
  type: tokenType,
@@ -17,117 +71,123 @@ const createTokenTemplate = (tokenType, tag, nesting, level) => ({
17
71
  });
18
72
 
19
73
  // Helper function to remove strong tags that wrap the entire content
20
- const removeStrongWrappers = (inline) => {
74
+ const removeStrongWrappers = (inline, allowFallback) => {
21
75
  if (!Array.isArray(inline.children) || inline.children.length === 0) {
22
76
  return;
23
77
  }
24
78
 
25
79
  const children = inline.children;
26
- const content = inline.content;
27
-
28
- // Quick check to avoid regex if obviously not matching
29
- if (!content.startsWith('**') || !content.endsWith('**')) {
80
+ if (!isStrongWrappedInline(inline, allowFallback)) {
30
81
  return;
31
82
  }
32
-
33
- // Check if the content matches the pattern **...** where entire content is wrapped
34
- if (!checkedTdReg.test(content)) {
83
+
84
+ if (children.length === 3 &&
85
+ children[0].type === 'strong_open' &&
86
+ children[2].type === 'strong_close' &&
87
+ children[1].type === 'text') {
88
+ inline.children = [children[1]];
89
+ inline.content = children[1].content;
35
90
  return;
36
91
  }
37
92
 
38
- // Find all strong open/close positions in one pass
39
- const strongPositions = [];
93
+ // Find strong open/close positions in one pass
94
+ let openCount = 0;
95
+ let closeCount = 0;
96
+ let firstOpen = -1;
97
+ let firstClose = -1;
98
+ let firstPair = null;
99
+ let lastPair = null;
100
+ const stack = [];
101
+
40
102
  for (let i = 0; i < children.length; i++) {
41
103
  const type = children[i].type;
42
104
  if (type === 'strong_open') {
43
- strongPositions.push({ type: 'open', index: i });
105
+ if (firstOpen === -1) {
106
+ firstOpen = i;
107
+ }
108
+ openCount++;
109
+ stack.push(i);
44
110
  } else if (type === 'strong_close') {
45
- strongPositions.push({ type: 'close', index: i });
111
+ if (firstClose === -1) {
112
+ firstClose = i;
113
+ }
114
+ closeCount++;
115
+ if (stack.length > 0) {
116
+ const openIdx = stack.pop();
117
+ if (!firstPair) {
118
+ firstPair = { open: openIdx, close: i };
119
+ }
120
+ lastPair = { open: openIdx, close: i };
121
+ }
46
122
  }
47
123
  }
48
124
 
49
- const openCount = strongPositions.filter(p => p.type === 'open').length;
50
- const closeCount = strongPositions.filter(p => p.type === 'close').length;
51
-
52
125
  if (openCount !== closeCount || openCount === 0) {
53
126
  return;
54
127
  }
55
128
 
56
- if (openCount >= 2) {
129
+ if (openCount >= 2 && firstPair && lastPair) {
57
130
  // Multiple strong pairs case - find pairs by stack matching
58
- const pairs = [];
59
- const stack = [];
131
+ const newChildren = [];
132
+ const contentParts = [];
60
133
 
61
- for (const pos of strongPositions) {
62
- if (pos.type === 'open') {
63
- stack.push(pos.index);
64
- } else if (stack.length > 0) {
65
- const openIdx = stack.pop();
66
- pairs.push({ open: openIdx, close: pos.index });
134
+ // Helper function to add children and build content
135
+ const addChildrenRange = (start, end) => {
136
+ for (let i = start; i < end; i++) {
137
+ const child = children[i];
138
+ newChildren.push(child);
139
+ if (child.type === 'text') {
140
+ contentParts.push(child.content);
141
+ } else if (child.type === 'em_open' || child.type === 'em_close') {
142
+ contentParts.push('*');
143
+ }
67
144
  }
68
- }
145
+ };
69
146
 
70
- if (pairs.length >= 2) {
71
- const firstPair = pairs[0];
72
- const lastPair = pairs[pairs.length - 1];
73
-
74
- const newChildren = [];
75
- const contentParts = [];
76
-
77
- // Helper function to add children and build content
78
- const addChildrenRange = (start, end) => {
79
- for (let i = start; i < end; i++) {
80
- newChildren.push(children[i]);
81
- if (children[i].type === 'text') {
82
- contentParts.push(children[i].content);
83
- } else if (children[i].type === 'em_open' || children[i].type === 'em_close') {
84
- contentParts.push('*');
85
- }
86
- }
87
- };
88
-
89
- // Add content before first pair
90
- addChildrenRange(0, firstPair.open);
91
-
92
- // Add content from first pair (without the strong tags)
93
- addChildrenRange(firstPair.open + 1, firstPair.close);
94
-
95
- // Add content between first and last pairs with strong wrapping
96
- for (let i = firstPair.close + 1; i < lastPair.open; i++) {
97
- if (children[i].type === 'text' && children[i].content.trim() !== '') {
98
- // Create strong tokens more efficiently
99
- const strongOpen = Object.create(children[i].constructor.prototype);
100
- Object.assign(strongOpen, createTokenTemplate('strong_open', 'strong', 1, children[i].level));
101
-
102
- const strongClose = Object.create(children[i].constructor.prototype);
103
- Object.assign(strongClose, createTokenTemplate('strong_close', 'strong', -1, children[i].level));
104
-
105
- newChildren.push(strongOpen, children[i], strongClose);
106
- contentParts.push('**', children[i].content, '**');
107
- } else {
108
- newChildren.push(children[i]);
109
- if (children[i].type === 'text') {
110
- contentParts.push(children[i].content);
111
- } else if (children[i].type === 'em_open' || children[i].type === 'em_close') {
112
- contentParts.push('*');
113
- }
147
+ // Add content before first pair
148
+ addChildrenRange(0, firstPair.open);
149
+
150
+ // Add content from first pair (without the strong tags)
151
+ addChildrenRange(firstPair.open + 1, firstPair.close);
152
+
153
+ // Add content between first and last pairs with strong wrapping
154
+ for (let i = firstPair.close + 1; i < lastPair.open; i++) {
155
+ const child = children[i];
156
+ if (child.type === 'text' && child.content.trim() !== '') {
157
+ // Create strong tokens more efficiently
158
+ const strongOpen = Object.create(child.constructor.prototype);
159
+ Object.assign(strongOpen, createTokenTemplate('strong_open', 'strong', 1, child.level));
160
+
161
+ const strongClose = Object.create(child.constructor.prototype);
162
+ Object.assign(strongClose, createTokenTemplate('strong_close', 'strong', -1, child.level));
163
+
164
+ newChildren.push(strongOpen, child, strongClose);
165
+ contentParts.push('**', child.content, '**');
166
+ } else {
167
+ newChildren.push(child);
168
+ if (child.type === 'text') {
169
+ contentParts.push(child.content);
170
+ } else if (child.type === 'em_open' || child.type === 'em_close') {
171
+ contentParts.push('*');
114
172
  }
115
173
  }
116
-
117
- // Add content from last pair (without the strong tags)
118
- addChildrenRange(lastPair.open + 1, lastPair.close);
119
-
120
- // Add content after last pair
121
- addChildrenRange(lastPair.close + 1, children.length);
122
-
123
- inline.content = contentParts.join('');
124
- inline.children = newChildren;
125
174
  }
126
175
 
176
+ // Add content from last pair (without the strong tags)
177
+ addChildrenRange(lastPair.open + 1, lastPair.close);
178
+
179
+ // Add content after last pair
180
+ addChildrenRange(lastPair.close + 1, children.length);
181
+
182
+ inline.content = contentParts.join('');
183
+ inline.children = newChildren;
127
184
  } else if (openCount === 1) {
128
185
  // Single strong pair: remove completely
129
- const strongOpen = strongPositions.find(p => p.type === 'open').index;
130
- const strongClose = strongPositions.find(p => p.type === 'close').index;
186
+ const strongOpen = firstOpen;
187
+ const strongClose = firstClose;
188
+ if (strongOpen < 0 || strongClose < 0) {
189
+ return;
190
+ }
131
191
 
132
192
  const newChildren = [];
133
193
  const contentParts = [];
@@ -149,74 +209,74 @@ const removeStrongWrappers = (inline) => {
149
209
  }
150
210
  }
151
211
 
152
- const addTheadThScope = (state, theadVar) => {
153
- let isEmpty = false;
212
+ const addTheadThScope = (state, theadVar, allowFallback) => {
213
+ const tokens = state.tokens;
154
214
  let firstThPos = theadVar.pos;
155
215
  let j = theadVar.i + 2;
156
216
  //if (state.tokens[theadVar.i + 1].type !== 'tr_open') return threadVar
157
- while (j < state.tokens.length) {
158
- if (state.tokens[j].type === 'th_open') {
159
- state.tokens[j].attrPush(['scope', 'col']);
217
+ while (j < tokens.length) {
218
+ if (tokens[j].type === 'th_open') {
219
+ tokens[j].attrPush(['scope', 'col']);
160
220
  if (j === theadVar.i + 2) {
161
- isEmpty = state.tokens[j+1].content === ''
162
- let isStrong = checkedTdReg.test(state.tokens[j+1].content)
163
- if (isStrong) {
221
+ const inline = tokens[j + 1];
222
+ const content = inline.content;
223
+ if (content === '' || isStrongWrappedInline(inline, allowFallback)) {
164
224
  firstThPos = j
165
225
  // Only remove strong tags from the first th content for matrix processing
166
226
  // The actual removal will be done in changeTdToTh if matrix conditions are met
167
- } else if (isEmpty) {
168
- firstThPos = j
169
227
  }
170
228
  }
171
229
  }
172
- if (state.tokens[j].type === 'tr_close') break
230
+ if (tokens[j].type === 'tr_close') break
173
231
  j++
174
232
  }
175
- return {i: j, firstThPos: firstThPos, isEmpty: isEmpty}
233
+ return {i: j, firstThPos: firstThPos}
176
234
  }
177
235
 
178
- const changeTdToTh = (state, tdPoses, hasThead) => {
236
+ const changeTdToTh = (state, tdPoses, hasThead, allowFallback) => {
179
237
  //console.log('hasThead: ' + hasThead + ', tdPoses: ' + tdPoses)
238
+ const tokens = state.tokens;
180
239
  let j = 0
181
240
  while(j < tdPoses.length) {
182
241
  //console.log('state.tokens[' + j + '].type: ' + state.tokens[tdPoses[j]].type)
183
242
  const pos = tdPoses[j]
184
243
  if (j > 0 || (!hasThead && j === 0)) {
185
- state.tokens[pos].type = 'th_open';
186
- state.tokens[pos].tag = 'th';
187
- state.tokens[pos].attrPush(['scope', 'row']);
188
- state.tokens[pos + 2].type = 'th_close';
189
- state.tokens[pos + 2].tag = 'th';
244
+ tokens[pos].type = 'th_open';
245
+ tokens[pos].tag = 'th';
246
+ tokens[pos].attrPush(['scope', 'row']);
247
+ tokens[pos + 2].type = 'th_close';
248
+ tokens[pos + 2].tag = 'th';
190
249
  }
191
250
 
192
251
  // Remove strong tags that wrap the entire content
193
- const inline = state.tokens[pos + 1];
194
- removeStrongWrappers(inline);
252
+ const inline = tokens[pos + 1];
253
+ removeStrongWrappers(inline, allowFallback);
195
254
  j++
196
255
  }
197
256
  }
198
257
 
199
- const checkTbody = (state, tbodyVar) => {
258
+ const checkTbody = (state, tbodyVar, allowFallback) => {
259
+ const tokens = state.tokens;
200
260
  let isAllFirstTh = true
201
261
  let tbodyFirstThPoses = []
202
262
  let j = tbodyVar.i + 1
203
- while (j < state.tokens.length) {
204
- if (state.tokens[j].type === 'tr_open') {
263
+ while (j < tokens.length) {
264
+ if (tokens[j].type === 'tr_open') {
205
265
  j++
206
- if (state.tokens[j].type === 'td_open' && state.tokens[j + 1].content.match(checkedTdReg)) {
266
+ if (tokens[j].type === 'td_open' && isStrongWrappedInline(tokens[j + 1], allowFallback)) {
207
267
  tbodyFirstThPoses.push(j)
208
268
  } else {
209
269
  isAllFirstTh = false
210
270
  break
211
271
  }
212
272
  }
213
- if (state.tokens[j].type === 'tbody_close') break
273
+ if (tokens[j].type === 'tbody_close') break
214
274
  j++
215
275
  }
216
276
  return { i: j, isAllFirstTh: isAllFirstTh, tbodyFirstThPoses: tbodyFirstThPoses}
217
277
  }
218
278
 
219
- const setColgroup = (state, tableOpenIdx, opt) => {
279
+ const setColgroup = (state, tableOpenIdx, opt, allowFallback) => {
220
280
  const tokens = state.tokens;
221
281
  const Token = state.Token;
222
282
 
@@ -244,23 +304,36 @@ const setColgroup = (state, tableOpenIdx, opt) => {
244
304
  // Calculate group names and colspan for the first row of th
245
305
  const groupData = {
246
306
  spans: [],
247
- names: [],
248
307
  rawNames: [],
249
308
  headerCount: 0
250
309
  };
251
310
 
311
+ const colgroupMatchReg = opt.colgroupWithNoAsterisk
312
+ ? /^([^::]+)(?::|:)\s*/
313
+ : /^\*\*([^*::]+)[::]\*\*\s*/;
314
+ const origColgroupMatchReg = opt.colgroupWithNoAsterisk
315
+ ? /^[^::]+(?::|:)\s*(.*)$/
316
+ : /^\*\*[^*::]+[::]\*\*\s*(.*)$/;
317
+
252
318
  let thIdx = tr1 + 1;
253
- const thTokens = [];
319
+ const origThs = [];
320
+ let trCloseIdx = -1;
254
321
 
255
- while (thIdx < tokens.length && tokens[thIdx].type !== 'tr_close') {
256
- if (tokens[thIdx].type === 'th_open') {
257
- thTokens.push(thIdx);
322
+ while (thIdx < tokens.length) {
323
+ const tokenType = tokens[thIdx].type;
324
+ if (tokenType === 'th_open') {
258
325
  const inline = tokens[thIdx + 1];
326
+ origThs.push(inline);
259
327
  const content = inline.content;
260
- const colgroupMatchReg = opt.colgroupWithNoAsterisk
261
- ? /^([^::]+)(?::|:)\s*/
262
- : /^\*\*([^*::]+)[::]\*\*\s*/;
263
- const match = content.match(colgroupMatchReg);
328
+ const hasLeadingStrong = hasLeadingStrongMarker(inline, allowFallback);
329
+ let match = null;
330
+ if (opt.colgroupWithNoAsterisk) {
331
+ if (content.indexOf(':') !== -1 || content.indexOf(':') !== -1) {
332
+ match = content.match(colgroupMatchReg);
333
+ }
334
+ } else if (hasLeadingStrong) {
335
+ match = content.match(colgroupMatchReg);
336
+ }
264
337
 
265
338
  if (match) {
266
339
  const group = match[1].trim();
@@ -277,6 +350,9 @@ const setColgroup = (state, tableOpenIdx, opt) => {
277
350
  groupData.rawNames.push(null);
278
351
  groupData.spans.push(1);
279
352
  }
353
+ } else if (tokenType === 'tr_close') {
354
+ trCloseIdx = thIdx;
355
+ break;
280
356
  }
281
357
  thIdx++;
282
358
  }
@@ -284,15 +360,7 @@ const setColgroup = (state, tableOpenIdx, opt) => {
284
360
  // If there are not two or more grouped header cells, do not auto-generate
285
361
  if (groupData.headerCount < 2) return;
286
362
 
287
- // Determine group names
288
- const nonNullGroups = groupData.rawNames.filter(name => name !== null);
289
- const allSame = nonNullGroups.length > 0 && nonNullGroups.every(name => name === nonNullGroups[0]);
290
-
291
- if (allSame) {
292
- groupData.names = [...groupData.rawNames];
293
- } else {
294
- groupData.names = [...groupData.rawNames];
295
- }
363
+ const groupNames = groupData.rawNames;
296
364
 
297
365
  // Generate colgroup if needed
298
366
  const hasSpan = groupData.spans.some(span => span > 1);
@@ -322,13 +390,12 @@ const setColgroup = (state, tableOpenIdx, opt) => {
322
390
 
323
391
  // Update indices
324
392
  const offset = insertTokens.length;
325
- theadOpen += offset;
326
393
  tr1 += offset;
327
394
  theadClose += offset;
395
+ trCloseIdx += offset;
328
396
  }
329
397
 
330
398
  // Generate two-row header
331
- const trCloseIdx = tokens.findIndex((t, idx) => idx > tr1 && t.type === 'tr_close');
332
399
 
333
400
  // Create new rows more efficiently
334
401
  const newTr1 = [
@@ -342,37 +409,21 @@ const setColgroup = (state, tableOpenIdx, opt) => {
342
409
  Object.assign(new Token('text', '', 0), { content: '\n' })
343
410
  ];
344
411
 
345
- // Enumerate th_open/inline between tr1 and trCloseIdx
346
- const origThs = [];
347
- for (let i = tr1 + 1; i < trCloseIdx; i++) {
348
- if (tokens[i].type === 'th_open') {
349
- origThs.push({
350
- th_open: tokens[i],
351
- inline: tokens[i + 1]
352
- });
353
- }
354
- }
355
-
356
412
  let thPtr = 0;
357
413
  for (let i = 0; i < groupData.spans.length; i++) {
358
- if (groupData.names[i] === null) {
414
+ if (groupNames[i] === null) {
359
415
  // Leftmost cell: rowspan=2
360
416
  const thOpen = new Token('th_open', 'th', 1);
361
417
  thOpen.attrSet('rowspan', '2');
362
418
  thOpen.attrSet('scope', 'col');
363
419
 
364
420
  const thInline = new Token('inline', '', 0);
365
- const origInline = origThs[thPtr]?.inline;
421
+ const origInline = origThs[thPtr];
366
422
 
367
423
  if (origInline) {
368
- // Create a copy for processing
369
- const tempInline = {
370
- content: origInline.content,
371
- children: origInline.children ? [...origInline.children] : []
372
- };
373
- removeStrongWrappers(tempInline);
374
- thInline.content = tempInline.content;
375
- thInline.children = tempInline.children;
424
+ removeStrongWrappers(origInline, allowFallback);
425
+ thInline.content = origInline.content;
426
+ thInline.children = Array.isArray(origInline.children) ? origInline.children : [];
376
427
  } else {
377
428
  thInline.content = '';
378
429
  thInline.children = [];
@@ -394,8 +445,8 @@ const setColgroup = (state, tableOpenIdx, opt) => {
394
445
  thOpen.attrSet('scope', 'col');
395
446
 
396
447
  const thInline = new Token('inline', '', 0);
397
- thInline.content = groupData.names[i];
398
- thInline.children = [{ type: 'text', content: groupData.names[i], level: 0 }];
448
+ thInline.content = groupNames[i];
449
+ thInline.children = [{ type: 'text', content: groupNames[i], level: 0 }];
399
450
 
400
451
  newTr1.push(
401
452
  thOpen,
@@ -410,11 +461,16 @@ const setColgroup = (state, tableOpenIdx, opt) => {
410
461
  th2Open.attrSet('scope', 'col');
411
462
 
412
463
  const th2Inline = new Token('inline', '', 0);
413
- const orig = origThs[thPtr]?.inline?.content || '';
414
- const origColgroupMatchReg = opt.colgroupWithNoAsterisk
415
- ? /^[^::]+(?::|:)\s*(.*)$/
416
- : /^\*\*[^*::]+[::]\*\*\s*(.*)$/;
417
- const match = orig.match(origColgroupMatchReg);
464
+ const origInline = origThs[thPtr];
465
+ const orig = origInline?.content || '';
466
+ let match = null;
467
+ if (opt.colgroupWithNoAsterisk) {
468
+ if (orig.indexOf(':') !== -1 || orig.indexOf(':') !== -1) {
469
+ match = orig.match(origColgroupMatchReg);
470
+ }
471
+ } else if (hasLeadingStrongMarker(origInline, allowFallback)) {
472
+ match = orig.match(origColgroupMatchReg);
473
+ }
418
474
  th2Inline.content = match ? match[1] : orig;
419
475
  th2Inline.children = [{ type: 'text', content: th2Inline.content, level: 0 }];
420
476
 
@@ -469,11 +525,12 @@ const setColgroup = (state, tableOpenIdx, opt) => {
469
525
  const tdIdx = j + 1;
470
526
  if (tokens[tdIdx].type === 'td_open') {
471
527
  const inline = tokens[tdIdx + 1];
472
- if (inline && typeof inline.content === 'string' && checkedTdReg.test(inline.content)) {
528
+ if (inline && typeof inline.content === 'string' &&
529
+ isStrongWrappedInline(inline, allowFallback)) {
473
530
  tokens[tdIdx].type = 'th_open';
474
531
  tokens[tdIdx].tag = 'th';
475
532
  tokens[tdIdx].attrSet('scope', 'row');
476
- removeStrongWrappers(inline);
533
+ removeStrongWrappers(inline, allowFallback);
477
534
  if (tokens[tdIdx + 2].type === 'td_close') {
478
535
  tokens[tdIdx + 2].type = 'th_close';
479
536
  tokens[tdIdx + 2].tag = 'th';
@@ -492,16 +549,20 @@ const setColgroup = (state, tableOpenIdx, opt) => {
492
549
  // Calculate group names and colspan for multi-row case
493
550
  const groupData = {
494
551
  spans: [],
495
- names: [],
496
552
  rawNames: []
497
553
  };
554
+ const groupMatchReg = /^\*\*([^*:]+):\*\*/;
498
555
 
499
556
  let thIdx = tr1 + 1;
500
557
  while (thIdx < tokens.length && tokens[thIdx].type !== 'tr_close') {
501
558
  if (tokens[thIdx].type === 'th_open') {
502
559
  const inline = tokens[thIdx + 1];
503
560
  const content = inline.content;
504
- const match = content.match(/^\*\*([^*:]+):\*\*/);
561
+ const hasLeadingStrong = hasLeadingStrongMarker(inline, allowFallback);
562
+ let match = null;
563
+ if (hasLeadingStrong) {
564
+ match = content.match(groupMatchReg);
565
+ }
505
566
 
506
567
  if (match) {
507
568
  const group = match[1].trim();
@@ -521,7 +582,8 @@ const setColgroup = (state, tableOpenIdx, opt) => {
521
582
  thIdx++;
522
583
  }
523
584
 
524
- groupData.names = [...groupData.rawNames];
585
+ const groupNames = groupData.rawNames;
586
+ const groupStripReg = /^\*\*[^*:]+:\*\*\s*(.*)$/;
525
587
 
526
588
  // Generate colgroup if needed
527
589
  const hasSpan = groupData.spans.some(span => span > 1);
@@ -544,11 +606,11 @@ const setColgroup = (state, tableOpenIdx, opt) => {
544
606
  let th1 = tr1 + 1, th2 = tr2 + 1, groupIdx = 0;
545
607
  while (th1 < tokens.length && tokens[th1].type !== 'tr_close') {
546
608
  if (tokens[th1].type === 'th_open') {
547
- if (groupData.names[groupIdx] === null) {
609
+ if (groupNames[groupIdx] === null) {
548
610
  // Leftmost cell: rowspan=2
549
611
  tokens[th1].attrSet('rowspan', '2');
550
612
  tokens[th1].attrSet('scope', 'col');
551
- removeStrongWrappers(tokens[th1 + 1]);
613
+ removeStrongWrappers(tokens[th1 + 1], allowFallback);
552
614
 
553
615
  // Find corresponding th in second row
554
616
  let t2 = th2;
@@ -567,7 +629,7 @@ const setColgroup = (state, tableOpenIdx, opt) => {
567
629
  tokens[th1].attrSet('colspan', groupData.spans[groupIdx].toString());
568
630
  }
569
631
  tokens[th1].attrSet('scope', 'col');
570
- tokens[th1 + 1].content = groupData.names[groupIdx];
632
+ tokens[th1 + 1].content = groupNames[groupIdx];
571
633
 
572
634
  // Process corresponding ths in second row
573
635
  let count = 0, t2 = th2;
@@ -575,8 +637,12 @@ const setColgroup = (state, tableOpenIdx, opt) => {
575
637
  if (tokens[t2].type === 'th_open') {
576
638
  tokens[t2].attrSet('scope', 'col');
577
639
  // Remove group name part from content
578
- const t2content = tokens[t2 + 1].content;
579
- const t2match = t2content.match(/^\*\*[^*:]+:\*\*\s*(.*)$/);
640
+ const t2Inline = tokens[t2 + 1];
641
+ const t2content = t2Inline.content;
642
+ let t2match = null;
643
+ if (hasLeadingStrongMarker(t2Inline, allowFallback)) {
644
+ t2match = t2content.match(groupStripReg);
645
+ }
580
646
  if (t2match) {
581
647
  tokens[t2 + 1].content = t2match[1];
582
648
  }
@@ -594,7 +660,8 @@ const setColgroup = (state, tableOpenIdx, opt) => {
594
660
 
595
661
  const tableEx = (state, opt) => {
596
662
  const tokens = state.tokens;
597
- const tokenLength = tokens.length;
663
+ let tokenLength = tokens.length;
664
+ const allowStrongFallback = !hasInlineRule(state.md, 'strong_ja');
598
665
 
599
666
  let idx = 0;
600
667
  while (idx < tokenLength) {
@@ -611,54 +678,57 @@ const tableEx = (state, opt) => {
611
678
  const linebreakToken = new state.Token('text', '', 0);
612
679
  linebreakToken.content = '\n';
613
680
  tokens.splice(idx, 0, wrapperStartToken, linebreakToken);
681
+ tokenLength += 2;
614
682
  idx += 2;
615
683
  }
616
684
 
617
685
  let theadVar = {
618
686
  i: idx + 1,
619
687
  firstThPos: -1,
620
- isEmpty: false,
621
688
  };
622
689
 
623
690
  const hasThead = tokens[theadVar.i] && tokens[theadVar.i].type === 'thead_open';
624
691
  if (hasThead) {
625
- theadVar = addTheadThScope(state, theadVar);
692
+ theadVar = addTheadThScope(state, theadVar, allowStrongFallback);
626
693
  idx = theadVar.i + 1;
627
694
  if (opt.colgroup) {
628
- setColgroup(state, tableOpenIdx, opt);
695
+ const beforeLength = tokens.length;
696
+ setColgroup(state, tableOpenIdx, opt, allowStrongFallback);
697
+ tokenLength += tokens.length - beforeLength;
629
698
  }
630
699
  }
631
700
 
632
- let tbodyVar = {
633
- i: idx + 1,
634
- isAllFirstTh: false,
635
- tbodyFirstThPoses: [],
636
- };
637
-
638
- const hasTbody = tokens[tbodyVar.i] && tokens[tbodyVar.i].type === 'tbody_open';
639
- if (hasTbody) {
640
- tbodyVar = checkTbody(state, tbodyVar);
641
- idx = tbodyVar.i + 1;
642
- }
643
-
644
- if (theadVar.firstThPos && tbodyVar.isAllFirstTh) {
645
- const firstTdPoses = [...tbodyVar.tbodyFirstThPoses];
646
- if (hasThead) {
647
- firstTdPoses.unshift(theadVar.firstThPos);
701
+ if (opt.matrix) {
702
+ let tbodyVar = {
703
+ i: idx + 1,
704
+ isAllFirstTh: false,
705
+ tbodyFirstThPoses: [],
706
+ };
707
+
708
+ const hasTbody = tokens[tbodyVar.i] && tokens[tbodyVar.i].type === 'tbody_open';
709
+ if (hasTbody) {
710
+ tbodyVar = checkTbody(state, tbodyVar, allowStrongFallback);
711
+ idx = tbodyVar.i + 1;
648
712
  }
649
- if (opt.matrix) {
650
- changeTdToTh(state, firstTdPoses, hasThead, theadVar);
713
+
714
+ if (theadVar.firstThPos && tbodyVar.isAllFirstTh) {
715
+ const firstTdPoses = [...tbodyVar.tbodyFirstThPoses];
716
+ if (hasThead) {
717
+ firstTdPoses.unshift(theadVar.firstThPos);
718
+ }
719
+ changeTdToTh(state, firstTdPoses, hasThead, allowStrongFallback);
651
720
  }
652
721
  }
653
722
 
654
723
  // Find table_close more efficiently
655
- while (idx < tokens.length) {
724
+ while (idx < tokenLength) {
656
725
  if (tokens[idx].type === 'table_close') {
657
726
  if (opt.wrapper) {
658
727
  const wrapperEndToken = new state.Token('div_close', 'div', -1);
659
728
  const linebreakToken = new state.Token('text', '', 0);
660
729
  linebreakToken.content = '\n';
661
730
  tokens.splice(idx + 1, 0, wrapperEndToken, linebreakToken);
731
+ tokenLength += 2;
662
732
  idx += 2;
663
733
  }
664
734
  break;