ponkotsu-md-editor 0.2.32 → 0.2.34
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.
- checksums.yaml +4 -4
- data/app/assets/javascripts/markdown_editor.js +77 -37
- data/lib/ponkotsu/md/editor/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e3b1c79f7f7011ffcd76111ea8598c5346790a17feb8c0a6e7f34b1ff7109176
|
|
4
|
+
data.tar.gz: 753c49c6d2c30d63059ff982b4f0620cecf94f6f1e5ef2b00f70814112dc35ee
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0337a5e3775a7b445a0eec9dfb92c2f3eeda5925fafdcb408496a2010bebb9bc0ab63d1689b45de015a0946c0a5c7674f1b0f8a76f1e9bd2b464e1828eaf8963
|
|
7
|
+
data.tar.gz: 719c69fa598fb5a7983cf8ab277c3d4246763569eeb1e72e9083bd9040fbbc37f0fadce92ddca229c3eae3719d1ad22d5d8431f198a5136a9c5ec90909b65ff8
|
|
@@ -117,28 +117,62 @@
|
|
|
117
117
|
}, { passive: false });
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
const syncToHidden = () => {
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
const syncToHidden = (() => {
|
|
121
|
+
let lastValue = '';
|
|
122
|
+
let pendingUpdate = false;
|
|
123
|
+
|
|
124
|
+
return () => {
|
|
125
|
+
if (pendingUpdate) return;
|
|
126
|
+
|
|
127
|
+
// テキスト取得(innerTextよりtextContentの方が高速)
|
|
128
|
+
const currentText = (textarea.textContent || '').replaceAll('\u00A0', ' ');
|
|
129
|
+
|
|
130
|
+
// 変更がない場合はスキップ
|
|
131
|
+
if (currentText === lastValue) return;
|
|
132
|
+
|
|
133
|
+
pendingUpdate = true;
|
|
134
|
+
requestAnimationFrame(() => {
|
|
135
|
+
hiddenField.value = currentText;
|
|
136
|
+
lastValue = currentText;
|
|
137
|
+
pendingUpdate = false;
|
|
138
|
+
});
|
|
139
|
+
};
|
|
140
|
+
})();
|
|
123
141
|
|
|
124
142
|
// 初期化時に同期
|
|
125
143
|
syncToHidden();
|
|
126
144
|
|
|
127
|
-
//
|
|
145
|
+
// フォーム送信時の処理を非同期化
|
|
128
146
|
const form = textarea.closest('form');
|
|
129
147
|
if (form) {
|
|
130
148
|
form.addEventListener('submit', function(e) {
|
|
131
|
-
|
|
132
|
-
|
|
149
|
+
e.preventDefault();
|
|
150
|
+
|
|
151
|
+
// 同期的に値を設定(確実性のため)
|
|
152
|
+
hiddenField.value = (textarea.textContent || '').replaceAll('\u00A0', ' ');
|
|
153
|
+
|
|
154
|
+
// 次のティックで送信
|
|
155
|
+
setTimeout(() => {
|
|
156
|
+
// console.log('Form submitting with content length:', hiddenField.value.length);
|
|
157
|
+
form.submit();
|
|
158
|
+
}, 0);
|
|
133
159
|
});
|
|
134
160
|
}
|
|
135
161
|
|
|
136
162
|
// 初期状態でプレースホルダを表示するための空要素チェック
|
|
137
163
|
function updatePlaceholderVisibility() {
|
|
138
|
-
//
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
164
|
+
// innerHTML を一度だけ取得してキャッシュ
|
|
165
|
+
const html = textarea.innerHTML.trim();
|
|
166
|
+
|
|
167
|
+
// 空判定を簡潔に
|
|
168
|
+
const isEmpty = html === '' ||
|
|
169
|
+
html === ' ' ||
|
|
170
|
+
html === '<br>' ||
|
|
171
|
+
html === '<div><br></div>' ||
|
|
172
|
+
html === '<div></div>' ||
|
|
173
|
+
html === '<div> </div>';
|
|
174
|
+
|
|
175
|
+
if (isEmpty) {
|
|
142
176
|
textarea.classList.add('empty');
|
|
143
177
|
} else {
|
|
144
178
|
textarea.classList.remove('empty');
|
|
@@ -240,7 +274,7 @@
|
|
|
240
274
|
}
|
|
241
275
|
|
|
242
276
|
onReady(function() {
|
|
243
|
-
console.log('Enhanced Markdown editor initializing...');
|
|
277
|
+
// console.log('Enhanced Markdown editor initializing...');
|
|
244
278
|
|
|
245
279
|
// Get DOM elements
|
|
246
280
|
const textarea = document.getElementById('editor_content');
|
|
@@ -306,7 +340,7 @@
|
|
|
306
340
|
'Expected:' + expected + '\n' +
|
|
307
341
|
'Actual:', actual);
|
|
308
342
|
} else {
|
|
309
|
-
console.log('Assertion passed:', message);
|
|
343
|
+
// console.log('Assertion passed:', message);
|
|
310
344
|
}
|
|
311
345
|
}
|
|
312
346
|
|
|
@@ -417,16 +451,16 @@
|
|
|
417
451
|
}
|
|
418
452
|
|
|
419
453
|
// === デバッグ出力 ===
|
|
420
|
-
console.log('[DEBUG] getContentEditableSelection');
|
|
421
|
-
console.log('fullText:', JSON.stringify(fullText));
|
|
422
|
-
console.log('startPos:', startPos, 'endPos:', endPos);
|
|
423
|
-
console.log('selectedText:', JSON.stringify(selectedText));
|
|
424
|
-
console.log('rangeText:', JSON.stringify(rangeText));
|
|
425
|
-
console.log('textNodes:', textNodes.map(n => n.textContent));
|
|
426
|
-
console.log('range.startContainer:', range.startContainer);
|
|
427
|
-
console.log('range.startOffset:', range.startOffset);
|
|
428
|
-
console.log('range.endContainer:', range.endContainer);
|
|
429
|
-
console.log('range.endOffset:', range.endOffset);
|
|
454
|
+
// console.log('[DEBUG] getContentEditableSelection');
|
|
455
|
+
// console.log('fullText:', JSON.stringify(fullText));
|
|
456
|
+
// console.log('startPos:', startPos, 'endPos:', endPos);
|
|
457
|
+
// console.log('selectedText:', JSON.stringify(selectedText));
|
|
458
|
+
// console.log('rangeText:', JSON.stringify(rangeText));
|
|
459
|
+
// console.log('textNodes:', textNodes.map(n => n.textContent));
|
|
460
|
+
// console.log('range.startContainer:', range.startContainer);
|
|
461
|
+
// console.log('range.startOffset:', range.startOffset);
|
|
462
|
+
// console.log('range.endContainer:', range.endContainer);
|
|
463
|
+
// console.log('range.endOffset:', range.endOffset);
|
|
430
464
|
// ===================
|
|
431
465
|
|
|
432
466
|
return {
|
|
@@ -864,6 +898,8 @@
|
|
|
864
898
|
}
|
|
865
899
|
}
|
|
866
900
|
|
|
901
|
+
const MAX_CACHE_SIZE = 500;
|
|
902
|
+
|
|
867
903
|
function analyzeHtml(target, isCountEmptyDiv = false) {
|
|
868
904
|
|
|
869
905
|
const cacheKey = `${target}_${isCountEmptyDiv}`;
|
|
@@ -937,9 +973,13 @@
|
|
|
937
973
|
}
|
|
938
974
|
}
|
|
939
975
|
|
|
940
|
-
if (analyzeHtmlCache.size >
|
|
941
|
-
|
|
942
|
-
|
|
976
|
+
if (analyzeHtmlCache.size > MAX_CACHE_SIZE) {
|
|
977
|
+
// 古いエントリを複数削除(より効率的)
|
|
978
|
+
const deleteCount = Math.floor(MAX_CACHE_SIZE * 0.2); // 20%削除
|
|
979
|
+
const keys = Array.from(analyzeHtmlCache.keys());
|
|
980
|
+
for (let i = 0; i < deleteCount; i++) {
|
|
981
|
+
analyzeHtmlCache.delete(keys[i]);
|
|
982
|
+
}
|
|
943
983
|
}
|
|
944
984
|
|
|
945
985
|
analyzeHtmlCache.set(cacheKey, lines);
|
|
@@ -1030,10 +1070,10 @@
|
|
|
1030
1070
|
const endOffset = getOffsetInContainer(textarea, range.endContainer, range.endOffset);
|
|
1031
1071
|
|
|
1032
1072
|
// === デバッグ出力(削除可能)===
|
|
1033
|
-
console.log('DOM-based text:', JSON.stringify(domBasedText));
|
|
1034
|
-
console.log('beginEndLenStrings:', beginEndLenStrings);
|
|
1035
|
-
console.log('Selection offsets:', startOffset, endOffset);
|
|
1036
|
-
console.log('Selected text should be:', JSON.stringify(domBasedText.substring(startOffset, endOffset)));
|
|
1073
|
+
// console.log('DOM-based text:', JSON.stringify(domBasedText));
|
|
1074
|
+
// console.log('beginEndLenStrings:', beginEndLenStrings);
|
|
1075
|
+
// console.log('Selection offsets:', startOffset, endOffset);
|
|
1076
|
+
// console.log('Selected text should be:', JSON.stringify(domBasedText.substring(startOffset, endOffset)));
|
|
1037
1077
|
// =============================
|
|
1038
1078
|
|
|
1039
1079
|
const startPos = getLineAndCharIndex(textarea, startOffset);
|
|
@@ -1228,10 +1268,10 @@
|
|
|
1228
1268
|
const range = selection.getRangeAt(0);
|
|
1229
1269
|
const selectedText = range.toString();
|
|
1230
1270
|
|
|
1231
|
-
console.log('Markdown apply debug:');
|
|
1232
|
-
console.log('Selected text:', JSON.stringify(selectedText));
|
|
1233
|
-
console.log('Before markup:', JSON.stringify(before));
|
|
1234
|
-
console.log('After markup:', JSON.stringify(after));
|
|
1271
|
+
// console.log('Markdown apply debug:');
|
|
1272
|
+
// console.log('Selected text:', JSON.stringify(selectedText));
|
|
1273
|
+
// console.log('Before markup:', JSON.stringify(before));
|
|
1274
|
+
// console.log('After markup:', JSON.stringify(after));
|
|
1235
1275
|
|
|
1236
1276
|
if (selectedText.length === 0) {
|
|
1237
1277
|
// No selection - insert markers at cursor position
|
|
@@ -1261,7 +1301,7 @@
|
|
|
1261
1301
|
newSelection.removeAllRanges();
|
|
1262
1302
|
newSelection.addRange(newRange);
|
|
1263
1303
|
|
|
1264
|
-
console.log('DOM replacement completed successfully');
|
|
1304
|
+
// console.log('DOM replacement completed successfully');
|
|
1265
1305
|
|
|
1266
1306
|
// Fire input event for change detection
|
|
1267
1307
|
element.dispatchEvent(new Event('input', { bubbles: true }));
|
|
@@ -1645,7 +1685,7 @@
|
|
|
1645
1685
|
script.onload = () => {
|
|
1646
1686
|
this.loaded = true;
|
|
1647
1687
|
this.loading = false;
|
|
1648
|
-
console.log('Twitter widgets.js loaded successfully');
|
|
1688
|
+
// console.log('Twitter widgets.js loaded successfully');
|
|
1649
1689
|
resolve();
|
|
1650
1690
|
};
|
|
1651
1691
|
|
|
@@ -1676,7 +1716,7 @@
|
|
|
1676
1716
|
|
|
1677
1717
|
if (window.twttr && window.twttr.widgets) {
|
|
1678
1718
|
await window.twttr.widgets.load(container);
|
|
1679
|
-
console.log('Twitter widgets initialized');
|
|
1719
|
+
// console.log('Twitter widgets initialized');
|
|
1680
1720
|
}
|
|
1681
1721
|
} catch (error) {
|
|
1682
1722
|
console.error('Failed to initialize Twitter widgets:', error);
|
|
@@ -2199,7 +2239,7 @@
|
|
|
2199
2239
|
});
|
|
2200
2240
|
}
|
|
2201
2241
|
|
|
2202
|
-
console.log('ponkotsu Markdown editor initialized successfully');
|
|
2242
|
+
// console.log('ponkotsu Markdown editor initialized successfully');
|
|
2203
2243
|
});
|
|
2204
2244
|
|
|
2205
2245
|
// Enhanced error handling
|