-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstitt.html
1469 lines (1332 loc) · 80.9 KB
/
stitt.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="en"><head>
<!--Version 1.4c-->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
:root {
counter-reset: newsentence;
--general-display: block; /*alternates with "none"*/
--summary-display: list-item; /*alternates with "none"*/
}
* {
background-color: rgb(247, 247, 226);
scroll-behavior: smooth;
}
body {
margin-left: 50px;
font-family: Arial, Helvetica, sans-serif;
}
p.sanskrit *, p.tibetan * { /*this takes care of e.g. an added <em> in the editable fields changing the background color to off-white*/
background-color: white;
}
p.correspondences *, p.glosses *, p.translation *, p.notes * { /*this takes care of e.g. an added <em> in the editable fields changing the background color to white*/
background-color: rgb(247, 247, 226);
}
.sentence { /*sentence block style*/
background-color: rgb(247, 247, 226);
padding-top: 32px;
padding-bottom: 25px;
border-top: 1px dotted black;
position: relative;
}
/*A note on the display of the Sanskrit and Tibetan paragraphs:
At 20px font size in FireFox, the rendered height of a paragraph with English text is 24px, excluding padding and margin.
At the same font size, Devanagari paragraphs are rendered 27px high, and Tibetan Uchen paragraphs are rendered 41px high.
This page was designed for use with Devanagari and Uchen. Other alphabets may make the display look off-kilter.
In particular, the alignment of the sidebar buttons is designed for blocks where the first two fields are written in Devanagari and Uchen.*/
div.wrapper {
background-color: white;
border-radius: 5px;
}
.sanskrit { /*first field*/
background-color: white;
font-size: 20px;
padding-top: 5px;
white-space: pre-wrap;
margin: 8px 0 0 0;
padding-top: 5px;
/*padding-left: 50px;
text-indent: -50px;*/
display: list-item;
margin-left: 50px;
padding-left: 10px;
}
.tibetan { /*second field*/
background-color: white;
font-size: 20px;
white-space: pre-wrap;
margin: 0 0 8px 0;
display: list-item;
margin-left: 50px;
padding-left: 10px;
}
/*fields 3-6 are taken care of by the details and summary styles for the dropdown menus*/
details { /*details style*/
padding-left: 10px;
margin-top: 16px;
margin-bottom: 16px;
}
summary { /*summary style*/
cursor: pointer;
color: darkslategrey;
}
details p {
padding-left: 20px;
color: black;
margin: 8px;
}
/*Comment styles -- not currently in use*/
.comment {
background-color: yellow;
}
span.comment span {
display: none;
}
span.comment:hover span {
display: inline;
background-color: #ffffcc;
padding-left: 5px;
}
/*Setting the counter styling*/
div.sentence {
counter-increment: newsentence;
}
/*.counter-p::before {
content: counter(newsentence) ".";
}*/
/*p.sanskrit::before {
content: " " counter(newsentence) ". ";
}
p.tibetan::before {
content: " ";
}*/
p.sanskrit::marker {
content: counter(newsentence) ". ";
font-size: 18px;
}
p.tibetan::marker {
content: "";
}
/*note: you can use devanagari and tibetan as counter styles*/
/*button styles*/
#open button {
background-color: lightgreen;
}
#close button {
background-color: lightcoral;
}
#show button {
background-color: white;
}
#hide button {
background-color: black;
color: white;
}
.sidebar {
display: flex;
flex-direction: column;
max-width: 25px;
position: absolute;
left: -37.5px;
top: 0;
justify-content: space-evenly;
height: 100%;
}
button {
cursor: pointer;
}
table, tr, td {
border: 1px dotted darkslategrey;
border-collapse: collapse;
}
td {
padding: 0 5px;
}
/* SHOC display rules - only use these particular selectors once in the CSS to avoid causing issues where the SHOC buttons affect the wrong declaration.*/
/* Note: the sidebar is already declared above */
.correspondences-dropdown {
display: block;
}
.glosses-dropdown {
display: block;
}
.translation-dropdown {
display: block;
}
.notes-dropdown {
display: block;
}
summary:not(#technical) {
display: list-item;
}
input {
max-width: 100px;
}
/* Custom styles -- if you want to e.g. write a custom span class, you can declare its properties in this section.*/
span.red {
color: red;
}
/* Experimental */
p.glossesTop {
cursor: pointer;
color: darkslategrey;
-webkit-user-select: none; /* Safari */
-ms-user-select: none; /* IE 10 and IE 11 */
user-select: none; /* Standard syntax */
display: list-item;
list-style-type: disclosure-closed;
margin-left: 24px; /*24 looks best on firefox, 28 on chrome*/
padding-left: 0; /*this overrides div p {padding-left:20px} above*/
}
p.glossesBottom {
display: none;
margin-left: 18px;
padding-left: 20px;
}
</style>
</head>
<body>
<h2><strong>Sanskrit-Tibetan interlinear text tool (STITT)</strong></h2>
<p>Note: This version of STITT works best in Firefox. Content may not be editable in other browsers.</p>
<p>Click on a text field in the user interface below to edit it.</p>
<p>Click the "<em>New block</em>" button at the bottom of the page to add a new sentence.</p>
<p>Click ctrl+s or the "<em>Save current page</em>" button to download a copy of your work.</p>
<p>Open the saved copy in a web browser to continue where you left off.</p>
<details><summary id="technical">Technical details</summary>
<p><em>STITT is a program for working with interlinear textual data. It provides a structured user interface for inputting, annotating, and exporting interlinear text data. It was created by Nick Prior.</em></p>
<p><em>It was written using HTML, CSS, and JavaScript, so it can easily be edited or extended by a front-end web developer.</em></p>
<p><em>The user interface consists of a set of menus and a work area that contains a series of "blocks" with automatic numbering. Each block includes six fields: the Sanskrit source text, the Tibetan source text, language correspondences, glosses, your translation, and notes.
The use of HTML allows data to be structured hierarchically, which makes it easy to rearrange and extract using the output menu at the bottom of the program. You can use a STITT file in conjunction with Git version control to manage a collaborative translation project with multiple contributors.
</em></p>
<p><em>If you run into technical issues, please contact a front-end web developer or check <a href="https://tibetanlanguage.school/resources">my website</a> for troubleshooting.</em></p>
<p><em>This file is using STITT version 1.4c. Please refer to the <a href="https://github.com/nickscottprior/stitt">STITT GitHub page</a> for the most up-to-date version.</em></p>
</details>
<hr>
<div id="input-menu" style="display:flex; flex-direction: row;justify-content: space-between;margin-right: 10px;">
<p><strong>Input menu</strong></p>
<p style="font-size: 14px;"><a href="#output-menu">Jump to bottom⤵</a></p>
</div>
<span style="font-size:14px;text-decoration: underline;">Automatic text importer</span><br>
<textarea style="padding:10px;background-color:white; width: 80%; height: 50px" id="input-box" placeholder="Copy-paste some text into this box"></textarea>
<div style="margin:10px 0;" title="These buttons will automatically insert text from the input box into the work area.">
<button onclick="importSanskrit()">Import Sanskrit text (GRETIL e-text)</button>
<button onclick="importTibetan()">Import Tibetan text (ACIP e-text)</button>
</div>
<details><summary>Text importer instructions</summary><p style="font-size:14px;font-style:italic; padding-right: 20px;">To insert a text into STITT, you can either use the automatic text importer above, or you can manually add new blocks with the "New block" button at the bottom of the work area.<br>
<br>The text importer breaks a text into individual lines and automatically inserts those lines one-by-one into the work area below. It is much faster than manually breaking a text at every line. You can easily search for a text to work on using the <a href="http://databases.aibs.columbia.edu/">AIBS database</a>, then click on a search result to see if it has a GRETIL and/or ACIP e-text available.</a><br>
<br>Sanskrit <a href="http://gretil.sub.uni-goettingen.de/gretil.html">GRETIL e-texts</a> are romanized by default. They can either be imported as-is in their romanized form, or they can be preprocessed and converted to Devanagari first. To do this, 1. copy-paste the text into the <a href="#input-box2">input box</a> at the bottom of the page, 2. click the IAST preprocessor button, 3. copy-paste the output into the IAST box of an <a href="https://www.yesvedanta.com/transliterate/">Indic script converter</a>, 4. copy-paste the resulting Devanagari into the automatic text importer above, and 5. click the "Import Sanskrit text (GRETIL e-text)" button.<br>
<br>Tibetan <a href="https://asianclassics.org/library/downloads/">ACIP e-texts</a> have already been converted into Tibetan script. To import them, you can simply 1. copy-paste the text directly into the automatic text importer and 2. click the "Process Tibetan text (ACIP e-text)" button. If you want to convert them to Wylie, no pre-processing is required; simply 1. copy-paste the text into a <a href="https://www.thlib.org/reference/transliteration/wyconverter.php">Tibetan script converter</a>, 2. copy-paste the result into the automatic text importer above, and 3. click the "Import Tibetan text (ACIP e-text)" button.<br>
<br>Since Sanskrit and Tibetan texts do not always contain the exact same content or divide sentences the same way, STITT cannot automatically combine and align both languages at the same time. As a result, you will have to automatically import the text one language and then manually input the corresponding passages for the other language.</p>
</details>
<!--<p class="glossesTop" onclick="openFunction()">Click me (experimental)</p>
<p class="glossesBottom" contenteditable>This text should originally be hidden</p>-->
<hr style="border:none;">
<!--<details>
<summary>General notes</summary>
<p contenteditable>Add notes + links for the project here</p></details>-->
<span style="font-size:14px;text-decoration: underline;">SHOC menu</span><br>
<span id="show" style="font-size: 14px;" title="These buttons will make the chosen field visible.">Show:<br>
<button id="showCorrespondences">Correspondences</button>
<button id="showGlosses">Glosses</button>
<button id="showTranslation">Translation</button>
<button id="showNotes">Notes</button>
<button id="showAll">All fields</button>
<button id="showFieldNames">Field names</button>
<button id="showSidebar">Sidebar</button>
</span>
<br>
<span id="hide" style="font-size: 14px;" title="These buttons will make the chosen field invisible.">Hide:<br>
<button id="hideCorrespondences">Correspondences</button>
<button id="hideGlosses">Glosses</button>
<button id="hideTranslation">Translation</button>
<button id="hideNotes">Notes</button>
<button id="hideAll">All fields</button>
<button id="hideFieldNames">Field names</button>
<button id="hideSidebar">Sidebar</button>
</span>
<br>
<span id="open" style="font-size: 14px;" title="These buttons will open the dropdown for the chosen field.">Open:<br>
<button id="openCorrespondences">Correspondences</button>
<button id="openGlosses">Glosses</button>
<button id="openTranslation">Translation</button>
<button id="openNotes">Notes</button>
<button id="openAll">All fields</button>
</span>
<br>
<span id="close" style="font-size: 14px;" title="These buttons will close the dropdown for the chosen field.">Close:<br>
<button id="closeCorrespondences">Correspondences</button>
<button id="closeGlosses">Glosses</button>
<button id="closeTranslation">Translation</button>
<button id="closeNotes">Notes</button>
<button id="closeAll">All fields</button>
</span>
<br>
<div style="height: 20px;"></div>
<span style="font-size:14px;text-decoration: underline;" title="A place to input notes, links, and other information relevant to your project.">Project notes</span><br>
<p style="font-size:14px;padding:5px;background-color:white;" contenteditable="">Sanskrit text: https://gretil.sub.uni-goettingen.de/gretil/1_sanskr/6_sastra/3_phil/buddh/bsa065_u.htm<br>Tibetan text: https://tibetan.works/etext/reader.php?collection=tengyur&index=4059<br>ā ī ū ṛ ṝ ḷ ḹ ṃ ḥ ṭ ḍ ṇ ś ṣ ḻ ä ö ü é</p>
<br id="begin">
<div class="sentence">
<div class="wrapper">
<p class="sanskrit" contenteditable="">पञ्चस्कन्धाः रूपस्कन्धः वेदनास्कन्धः संज्ञास्कन्धः संस्कारस्कन्धः विज्ञानस्कन्धश्च ॥</p>
<p class="tibetan" contenteditable="">ཕུང་པོ་ལྔ་ནི་གཟུགས་ཀྱི་ཕུང་པོ་དང༌། ཚོར་བའི་ཕུང་པོ་དང༌། འདུ་ཤེས་ཀྱི་ཕུང་པོ་དང༌། འདུ་བྱེད་ཀྱི་ཕུང་པོ་དང༌། རྣམ་པར་ཤེས་པའི་ཕུང་པོའོ། །</p>
</div>
<details class="correspondences-dropdown">
<summary>Correspondences</summary>
<p class="correspondences" contenteditable="">पञ्चस्कन्धाः, ཕུང་པོ་ལྔ་<br>रूप, གཟུགས་<br>वेदना, ཚོར་བ་<br>संज्ञा, འདུ་ཤེས་<br>संस्कार, འདུ་བྱེད་<br>विज्ञान, རྣམ་པར་ཤེས་པ་<br></p>
</details>
<details class="glosses-dropdown">
<summary>Glosses</summary>
<p class="glosses" contenteditable="">Saṃskāras are producers, and not produced things. The translation "formation" should be avoided in most contexts.<br>1. per the Khajjaniya sutta: "And why, monks, do you call them 'saṅkhāras'? Because they produce produced things, monks, that is why they are called 'saṅkhāras'."<br>2. བྱེད་ is an agentive ending in Tibetan grammar, analogous to the English suffix "-er" or "-or", and is contrasted with the patientive ending བྱས་. Cf. saṃskṛta འདུས་བྱས་ "produced thing".<br>3. Miphams Khejuk says "རྣམ་ཤེས་ལ་ཡང་སྲིད་ཀྱི་ས་བོན་འདེབས་པས་འདུ་བྱེད་ཅེས་བྱ་སྟེ།" i.e. "Because they plant the seed of rebirth in one's consciousness, they are called 'producers.' "<br>4. The illustrative example for saṃskāras in the བཀའ་གདམས་ཕ་ཆོས། is a potter. The text says "འདུ་བྱེད་དག་ནི་རྫ་མཁན་བཞིན།" i.e. "Producers are like a potter." Potters are producers, not products.<br></p>
</details>
<details class="translation-dropdown">
<summary>Translation</summary>
<p class="translation" contenteditable="">The five heaps are the heap of form, the heap of feeling, the heap of perception, the heap of producers, and the heap of consciousness.<br></p>
</details>
<details class="notes-dropdown">
<summary>Notes</summary>
<p class="notes" contenteditable="">Per Sthiramati, the skandhas are presented in a particular order going from most coarse to least coarse<br></p>
</details>
<div class="sidebar">
<button class="up" title="Move this block up">∧</button>
<button class="delete" title="Delete this block">−</button>
<button class="insert" title="Insert new block">+</button>
<button class="down" title="Move this block down">∨</button>
</div>
</div>
<div class="sentence">
<div class="wrapper">
<p class="sanskrit" contenteditable="">रूपं कतमत् । यत् किञ्चिद् रूपं सर्वं तच्चत्वारि महाभूतानि चत्वारि च महाभूतान्युपादाय ॥</p>
<p class="tibetan" contenteditable="">གཟུགས་གང་ཞེ་ན། གཟུགས་གང་ཡིན་པ་ཅི་ཡང་རུང་སྟེ། དེ་དག་ཐམས་ཅད་འབྱུང་བ་ཆེན་པོ་བཞི་དག་དང༌། འབྱུང་བ་ཆེན་པོའི་བཞི་དག་རྒྱུར་བྱས་པའོ། །</p>
</div>
<details class="correspondences-dropdown">
<summary>Correspondences</summary>
<p class="correspondences" contenteditable="">महाभूतानि, འབྱུང་བ་ཆེན་པོ་བཞི་དག་<br>महाभूतान्युपादाय, འབྱུང་བ་ཆེན་པོའི་[sic]བཞི་དག་རྒྱུར་བྱས་པ་<br></p>
</details>
<details class="glosses-dropdown">
<summary>Glosses</summary>
<p class="glosses" contenteditable="">महाभूतान्युपादाय is an irregular compound, meaning something like "derivative of the four great elements"<br></p>
</details>
<details class="translation-dropdown">
<summary>Translation</summary>
<p class="translation" contenteditable="">What is form? Anything at all which is form, that is the four great elements and that which derives from the four great elements.<br></p>
</details>
<details class="notes-dropdown">
<summary>Notes</summary>
<p class="notes" contenteditable="">-odd placement of genitive marker in the Tibetan<br>-upādāya receives very little description in most dictionaries<br></p>
</details>
<div class="sidebar">
<button class="up" title="Move this block up">∧</button>
<button class="delete" title="Delete this block">−</button>
<button class="insert" title="Insert new block">+</button>
<button class="down" title="Move this block down">∨</button>
</div>
</div>
<div class="sentence">
<div class="wrapper">
<p class="sanskrit" contenteditable="">कतमानि चत्वारि महाभूतानि । पृथिवी धातुः अब्धातुः तेजोधातुःवायुधातुश्च ॥</p>
<p class="tibetan" contenteditable="">འབྱུང་བ་ཆེན་པོ་བཞི་དག་གང་ཞེ་ན། སའི་ཁམས་དང༌། ཆུའི་ཁམས་དང༌། མེའི་ཁམས་དང༌། རླུང་གི་ཁམས་སོ། །</p>
</div>
<details class="correspondences-dropdown">
<summary>Correspondences</summary>
<p class="correspondences" contenteditable="">पृथिवी धातुः, སའི་ཁམས་<br>अब्धातुः, ཆུའི་ཁམས་<br>तेजोधातुः, མེའི་ཁམས་<br>वायुधातु, རླུང་གི་ཁམས་<br></p>
</details>
<details class="glosses-dropdown">
<summary>Glosses</summary>
<p class="glosses" contenteditable="">In the དག་ཡིག་གསར་བསྒྲིགས།, this usage of ཁམས་ is glossed as "nature" or "essence" (རང་གཤིས་སམ་ངོ་བོ་)<br></p>
</details>
<details class="translation-dropdown">
<summary>Translation</summary>
<p class="translation" contenteditable="">What are the four great elements? They are the element of earth, the element of water, the element of fire, and the element of wind.<br></p>
</details>
<details class="notes-dropdown">
<summary>Notes</summary>
<p class="notes" contenteditable="">Per Sthiramati, these elements are called "great" due to having a great scope, since they are the basis of all derived form (upādayarūpa)<br></p>
</details>
<div class="sidebar">
<button class="up" title="Move this block up">∧</button>
<button class="delete" title="Delete this block">−</button>
<button class="insert" title="Insert new block">+</button>
<button class="down" title="Move this block down">∨</button>
</div>
</div>
<br id="end">
<div id="output-menu" style="display:flex; flex-direction: row;justify-content: space-between;margin-right: 10px;align-items: flex-end;">
<span>
<button id="newBlock" style="background-color:lightgreen">New block</button>
<button id="savePage" style="background-color: lightblue;">Save current page</button>
</span>
<p style="font-size: 14px;"><a href="#input-menu">Jump to top⤴</a></p>
</div>
<br>
<div>
<button onclick="massDelete()" style="background-color:lightcoral">Delete blocks in range:</button>
<input id="minimum" type="number" placeholder="First block #">
<input id="maximum" type="number" placeholder="Last block #">
</div>
<br>
<button id="button-parseHTML" style="background-color:khaki">Parse HTML</button>
<button id="button-purgeHTML" style="background-color:plum">Purge HTML</button>
<div style="height: 30px;"></div>
<p><strong>Output menu </strong></p>
<p style="font-size:14px;margin-top:0;">Output text from the work area:</p>
<div style="font-size: 14px;margin:5px;" title="These buttons will output the chosen field from the work area with line breaks between each block.">Block-by-block:<br>
<button onclick="linebylineSanskritA()">Sanskrit</button>
<button onclick="linebylineTibetanA()">Tibetan</button>
<button onclick="linebylineTranslationA()">Translation</button>
<button onclick="sktAndTranslation()">Sanskrit + translation</button>
<button onclick="tbtAndTranslation()">Tibetan + translation</button>
<button onclick="blockbyblockInterlinear()">All three</button>
<button id="button-continuousCorrespondences">Correspondences</button></div>
<div style="font-size: 14px;margin:5px;" title="These buttons will output the chosen field from the work area as a continuous text without line breaks.">Continuous:<br>
<button id="button-continuousSanskrit">Sanskrit</button>
<button id="button-continuousTibetan">Tibetan</button>
<button id="button-continuousTranslation">Translation</button></div>
<div style="font-size: 14px;margin:5px;" title="These buttons will output a table that you can easily import into Word, Excel, or a flashcard program like Anki. To avoid data extraction errors, please try to use a consistent column separator for all the correspondences fields in the document.">Chart correspondences using a specific column separator:<br>
<button onclick="commaCorrespondences()">comma (,)</button>
<button onclick="equalCorrespondences()">equals sign (=)</button>
<button onclick="slashCorrespondences()">slash (/)</button>
<button onclick="hyphenCorrespondences()">hyphen (-)</button>
<button onclick="dblspaceCorrespondences()">double space ( )</button><br>
</div>
<div style="margin-top: 25px;padding:8px 0px;border-top: 1px dotted black; border-bottom: 1px dotted black;">
<p style="font-size: 14px;">Output text from an input box:</p>
<textarea style="padding:10px;background-color:white; width: 80%; height: 50px" id="input-box2" placeholder="Copy-paste some text into this box"></textarea><br>
<button onclick="linebylineSanskritB()">Line-by-line Sanskrit (GRETIL e-text)</button>
<button onclick="continuousSanskritB()">Continuous Sanskrit (GRETIL e-text)</button><br>
<button onclick="linebylineTibetanB()">Line-by-line Tibetan (ACIP e-text)</button>
<button onclick="continuousTibetanB()">Continuous Tibetan (ACIP e-text)</button><br>
<button onclick="gretilPre()">IAST preprocessor</button>
<!--<button onclick="wyliePost()">Wylie postprocessor</button> I realized I don't need this, I built the functionality
of the postprocessor right into the main import button. I can't do the same with Sanskrit because the Sanskrit
transliteration converter doesn't actually work with the raw GRETIL text, so preprocessing is required.-->
<button onclick="removeSpace()">Remove extra spaces & lines</button>
<button onclick="clearOutput()">Clear output</button>
<div style="height: 15px;"></div>
</div>
<hr style="border: none;">
<p style="text-decoration: underline;">View output below:</p>
<div id="displayBox" style="margin-left:0;">[Output will display here]</div>
<div style="height: 30px;"></div>
<script>
/*function appendSentence() {
let sentenceNode = document.createElement("div");
let sentenceNodeParent = document.getElementsByTagName("body");
sentenceNodeParent.appendChild(sentenceNode);
It says appendChild is not a function, but it is.
I think you just can't do x.method(y) within a function.
}*/
var newDiv = document.createElement('div');
var divParent = document.getElementsByTagName("body");
var oldButton = document.getElementById("newBlock");
//note: I had to remove the global definition of sentenceBlock because it was stopping the open/close buttons from
//working with new blocks
/*var sentenceBlock =
'<div class="sentence"><p class="sanskrit" contenteditable>Add Sanskrit text</p>' +
'<p class="tibetan" contenteditable>Add Tibetan text</p>' +
'<details class="correspondences-dropdown"'+correspondencesOpen+'><summary>Correspondences</summary><p class="correspondences" contenteditable>Add correspondences</p></details>' +
'<details class="glosses-dropdown"'+glossesOpen+'><summary>Glosses</summary><p class="glosses" contenteditable>Add glosses</p></details>' +
'<details class="translation-dropdown"'+translationOpen+'><summary>Translation</summary><p class="translation" contenteditable>Add translation</p></details>' +
'<details class="notes-dropdown"'+notesOpen+'><summary>Notes</summary><p class="notes" contenteditable>Add notes</p></details>' +
'<div class="sidebar"><button class="up" title="Move this block up">∧</button><button class="delete" title="Delete this block">−</button><button class="insert" title="Insert new block">+</button><button class="down" title="Move this block down">∨</button></div>';*/
document.getElementById("newBlock").onclick = function () {
let sentenceBlock =
'<div class="sentence"><div class="wrapper"><p class="sanskrit" contenteditable>Add Sanskrit text</p>' +
'<p class="tibetan" contenteditable>Add Tibetan text</p></div>' +
'<details class="correspondences-dropdown"'+correspondencesOpen+'><summary>Correspondences</summary><p class="correspondences" contenteditable>Add correspondences</p></details>' +
'<details class="glosses-dropdown"'+glossesOpen+'><summary>Glosses</summary><p class="glosses" contenteditable>Add glosses</p></details>' +
'<details class="translation-dropdown"'+translationOpen+'><summary>Translation</summary><p class="translation" contenteditable>Add translation</p></details>' +
'<details class="notes-dropdown"'+notesOpen+'><summary>Notes</summary><p class="notes" contenteditable>Add notes</p></details>' +
'<div class="sidebar"><button class="up" title="Move this block up">∧</button><button class="delete" title="Delete this block">−</button><button class="insert" title="Insert new block">+</button><button class="down" title="Move this block down">∨</button></div>';
document.getElementById('end').insertAdjacentHTML('beforebegin',sentenceBlock);
}
/*function () {
divParent.insertBefore(newDiv, oldButton);
document.body.appendChild(newDiv);}*/
document.getElementById("savePage").onclick = function save() { //Thanks to Uri Barenholz on StackOverflow for this function!
const data = "<!DOCTYPE html>\n"+document.documentElement.outerHTML;
const link = document.createElement('a');
link.setAttribute('download', 'default_download_name.html');
link.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(data));
link.click();}
//below is for continuous sanskrit
/*let sanskritCollection = document.getElementsByClassName("sanskrit");
let sanskritArray = [].map.call(sanskritCollection, item => item.textContent);
let sanskritOutput = "";*/
//This gets the elements in a non-live array format:
//const sanskritCollection = document.querySelectorAll('.sanskrit');
//This gets the content, but in a non-array format:
//const sanskritCollection = document.getElementsByClassName("sanskrit").innerHTML;
//This gets the elements in a collection format, which can be accessed like an array with for:
//const sanskritCollection = document.getElementsByClassName("sanskrit");
//so, there is no need to assign an array using [].map.call() or Array.from().
//The other method for inserting the continuous result, rather than doing .innerHTML, would be
//to do document.getElementById('displayBox').insertAdjacentHTML('beforeend',tibetanOutput);
//but this leads to weird behaviour where the array gets constantly larger.
const sanskritCollection = document.getElementsByClassName("sanskrit");
//const sanskritArray = Array.from(sanskritCollection); no array needed here
let sanskritOutput = "";
document.getElementById("button-continuousSanskrit").onclick = function () {
for(let i = 0; i < sanskritCollection.length; i++) {
let x = sanskritCollection[i].innerHTML;
//note: i.innerHTML won't work; you have to do array[i].innerHTML
sanskritOutput += x + " ";
}
document.getElementById("displayBox").innerHTML = sanskritOutput;
sanskritOutput = ""
}
//linebylineSanskritA
function linebylineSanskritA() {
for(let i = 0; i < sanskritCollection.length; i++) {
let x = sanskritCollection[i].innerHTML;
sanskritOutput += x + "<br>";
}
document.getElementById("displayBox").innerHTML = sanskritOutput;
sanskritOutput = ""
}
//continuous tibetan button
//the ___Continuous variables have to be declared with let, or else you can't redefine them in the for() loop
//adding ___Continuous = "" to the very end of the function makes the variable memory "clear" with each pass.
const tibetanCollection = document.getElementsByClassName("tibetan");
let tibetanOutput = "";
document.getElementById("button-continuousTibetan").onclick = function () {
for(let i = 0; i < tibetanCollection.length; i++) {
let x = tibetanCollection[i].innerHTML;
tibetanOutput += x + " ";
}
tibetanOutput = tibetanOutput.replaceAll("། ། ", "། །")
tibetanOutput = tibetanOutput.replaceAll("/_/ ", "/_/")
document.getElementById("displayBox").innerHTML = tibetanOutput;
tibetanOutput = "";
}
//linebylineTibetanA
function linebylineTibetanA() {
for(let i = 0; i < tibetanCollection.length; i++) {
let x = tibetanCollection[i].innerHTML;
tibetanOutput += x + "<br>";
}
document.getElementById("displayBox").innerHTML = tibetanOutput;
tibetanOutput = "";
}
//continuousCorrespondences button -- this is a misnomer, it's blockbyblockCorrespondences
const correspondenceCollection = document.getElementsByClassName("correspondences");
let correspondenceContinuous = "";
document.getElementById("button-continuousCorrespondences").onclick = function () {
for(let i = 0; i < correspondenceCollection.length; i++) {
let x = correspondenceCollection[i].innerHTML;
correspondenceContinuous += x + "<br>";
correspondenceContinuous = correspondenceContinuous.replaceAll("<br><br>", "<br>")
}
document.getElementById("displayBox").innerHTML = correspondenceContinuous;
correspondenceContinuous = "";
}
//continuousTranslation button
const translationCollection = document.getElementsByClassName("translation");
let translationOutput = "";
document.getElementById("button-continuousTranslation").onclick = function () {
for(let i = 0; i < translationCollection.length; i++) {
let x = translationCollection[i].innerHTML;
x = x.replaceAll("<br>", " "); //to manage ghost <br>s at the end of an editable p
translationOutput += x + " ";
}
document.getElementById("displayBox").innerHTML = translationOutput;
translationOutput = "";
}
//linebylineTranslationA
function linebylineTranslationA() {
for(let i = 0; i < translationCollection.length; i++) {
let x = translationCollection[i].innerHTML;
translationOutput += x + "<br>";
translationOutput = translationOutput.replaceAll("<br><br>", "<br>") //to manage ghost <br>s at the end of an editable p
}
document.getElementById("displayBox").innerHTML = translationOutput;
translationOutput = "";
}
//parseHTML button
/*const parser = new DOMParser();*/
//const fieldCollection = document.getElementsByTagName("p");
document.getElementById("button-parseHTML").onclick = function () {
let fieldCollection = document.querySelectorAll("p[contenteditable]:not(.correspondences)");
if (confirm("Note: this will parse all user-input HTML tags, excluding those that are in the correspondences. For security, you should only use and parse HTML tags if you know and understand the risks of XSS.\nDo you want to continue?")) {
for(let i = 0; i < fieldCollection.length; i++) {
//if(fieldCollection[i].getAttribute(contenteditable)==true) {
let x = fieldCollection[i].innerHTML;
let leftParsed = "";
leftParsed += x.replaceAll("<","<");
let rightParsed = leftParsed.replaceAll(">",">");
fieldCollection[i].innerHTML = rightParsed;
//}
}
}
}
//purgeHTML button
document.getElementById("button-purgeHTML").onclick = function () {
let fieldCollection = document.querySelectorAll("p[contenteditable]:not(.correspondences)");
if (confirm("Note: this will remove all HTML formatting (including line breaks) from all user-input fields other than the correspondences.\nDo you want to continue?")) {
for(let i = 0; i < fieldCollection.length; i++) {
let x = fieldCollection[i].innerHTML;
let leftParsed = "";
leftParsed += x.replaceAll("<","<");
let rightParsed = leftParsed.replaceAll(">",">");
fieldCollection[i].innerHTML = rightParsed;
}
}
}
//openAll + closeAll
/* This code doesn't work; it doesn't return an error, and an event is listed beside the button, but it doesn't do anything:
document.getElementById("openAll").onclick = function () {
document.getElementsByTagName("details").open = true;
}
This also doesn't work, and doesn't return an error:
let detailsAll = document.getElementsByTagName("details");
document.getElementById("openAll").onclick = function () {
detailsAll.open = true;
}
But the below solution worked! So, if using a live collection, you can't simultaneously edit the whole collection.
You have to iterate through the collection with a for() loop.
You have 2 options to select all elements:
let detailsAll = document.getElementsByTagName("details");
or
let detailsAll = document.querySelectorAll("details");
However, because querySelectorAll is static, the selector won't update with any new blocks that you add.
(at least, if you are assigning the selector to a variable outside of a function)
It needs to be re-assigned to take the new DOM into account.
So, I'm going to try moving the variable declaration *inside* the function.
-that worked!
*/
/*Design note: I can either manipulate content based on numbers + position, or use hard-coded classes.
The first approach has better UX but worse DX. It could make things more customizable, and users could
add new fields with custom names that automatically update the names of the SHOC deleteButtons, for example.
But, the second approach makes it easier to read, write, and troubleshoot the code.
I think I'm going to do the second approach for now, and then figure out the first one in a later version.
The second approach also doesn't require rewriting deleteButtons if a user does gain the ability to insert a
custom field type.
The SHOC deleteButtons in 1.3 use a position-based approach, with a declaration like:
let detailsArray = document.querySelectorAll(".sentence details:nth-child(3)")
In 1.4 I'm going to change this to a class-based approach, with the declaration like:
let detailsArray = document.querySelectorAll(".correspondences-dropdown")
*/
//openAll + closeAll
//note: for a while I used The Old Method below, which didn't affect new blocks:
/* The Old Method:
document.getElementById("openAll").onclick = function () {
let detailsArray = document.querySelectorAll(".sentence details");
for (let i = 0; i < detailsArray.length; i++) {
let x = detailsArray[i];
x.open = true;
}
}*/
//side-note: might be good to eventually move away from details+summary, towards a visiblity-based scripted system
//but now I am using The New Method to edit the CSS directly in the head:
//open/close all fields:
document.getElementById("openAll").onclick = function () {
let detailsArray = document.querySelectorAll(".sentence details");
for (i = 0; i < detailsArray.length; i++) {
detailsArray[i].setAttribute("open", "");
}
correspondencesOpen = "open";
glossesOpen = "open";
translationOpen = "open";
notesOpen = "open";
}
document.getElementById("closeAll").onclick = function () {
let detailsArray = document.querySelectorAll(".sentence details");
for (i = 0; i < detailsArray.length; i++) {
detailsArray[i].removeAttribute("open");
}
correspondencesOpen = "";
glossesOpen = "";
translationOpen = "";
notesOpen = "";
}
//openCorrespondences + closeCorrespondences
var correspondencesOpen="";
document.getElementById("openCorrespondences").onclick = function () {
let detailsArray = document.getElementsByClassName("correspondences-dropdown");
for (i = 0; i < detailsArray.length; i++) {
//detailsArray[i].style.visibility = "visible";
detailsArray[i].setAttribute("open", "");
}
correspondencesOpen = "open";
}
document.getElementById("closeCorrespondences").onclick = function () {
let detailsArray = document.getElementsByClassName("correspondences-dropdown");
for (i = 0; i < detailsArray.length; i++) {
detailsArray[i].removeAttribute("open");
}
correspondencesOpen = "";
}
//openGlosses + closeGlosses
var glossesOpen = "";
document.getElementById("openGlosses").onclick = function () {
let detailsArray = document.getElementsByClassName("glosses-dropdown");
for (i = 0; i < detailsArray.length; i++) {
detailsArray[i].setAttribute("open", "");
}
glossesOpen = "open";
}
document.getElementById("closeGlosses").onclick = function () {
let detailsArray = document.getElementsByClassName("glosses-dropdown");
for (i = 0; i < detailsArray.length; i++) {
detailsArray[i].removeAttribute("open");
}
glossesOpen = "";
}
//openTranslation + closeTranslation
var translationOpen = "";
document.getElementById("openTranslation").onclick = function () {
let detailsArray = document.getElementsByClassName("translation-dropdown");
for (i = 0; i < detailsArray.length; i++) {
detailsArray[i].setAttribute("open", "");
}
translationOpen = "open";
}
document.getElementById("closeTranslation").onclick = function () {
let detailsArray = document.getElementsByClassName("translation-dropdown");
for (i = 0; i < detailsArray.length; i++) {
detailsArray[i].removeAttribute("open");
}
translationOpen = "";
}
//openNotes + closeNotes
var notesOpen = "";
document.getElementById("openNotes").onclick = function () {
let detailsArray = document.getElementsByClassName("notes-dropdown");
for (i = 0; i < detailsArray.length; i++) {
detailsArray[i].setAttribute("open", "");
}
notesOpen = "open";
}
document.getElementById("closeNotes").onclick = function () {
let detailsArray = document.getElementsByClassName("notes-dropdown");
for (i = 0; i < detailsArray.length; i++) {
detailsArray[i].removeAttribute("open");
}
notesOpen = "";
}
//SHOW AND HIDE BUTTONS
//show and hide correspondences
//first method below worked, but not for new blocks.
//second method below works for new blocks by changing the element style in the head.
//first method:
/*document.getElementById("showCorrespondences").onclick = function () {
let detailsArray = document.querySelectorAll(".correspondences-dropdown");
for (let i = 0; i < detailsArray.length; i++) {
let x = detailsArray[i];
x.style.display = "block";
}
}*/
//second method:
document.getElementById("showCorrespondences").onclick = function () {
let cssRuleList = document.styleSheets[0].rules;
for (i = 0; i < cssRuleList.length; i++) {
if (cssRuleList[i].selectorText == ".correspondences-dropdown") {
cssRuleList[i].style.display = "block";
}
}
}
//I have changed all the other show/hide rules to the second method.
//The above comment is the only relic of the first method that I've preserved, to keep the code easier to read.
//Note: the second method requires declaring the selector in the head.
//However, you don't need to declare the display or visibility property in its selector block. It will set it regardless.
//If it's not declared in the head, then the if() method won't find a match, and so no code will run, but no error will be thrown either.
//I wonder -- rather than using "if()", is there a way to search in an array?
document.getElementById("hideCorrespondences").onclick = function () {
let cssRuleList = document.styleSheets[0].rules;
for (i = 0; i < cssRuleList.length; i++) {
if (cssRuleList[i].selectorText == ".correspondences-dropdown") {
cssRuleList[i].style.display = "none";
}
}
}
//show and hide glosses
document.getElementById("showGlosses").onclick = function () {
let cssRuleList = document.styleSheets[0].rules;
for (i = 0; i < cssRuleList.length; i++) {
if (cssRuleList[i].selectorText == ".glosses-dropdown") {
cssRuleList[i].style.display = "block";
}
}
}
document.getElementById("hideGlosses").onclick = function () {
let cssRuleList = document.styleSheets[0].rules;
for (i = 0; i < cssRuleList.length; i++) {
if (cssRuleList[i].selectorText == ".glosses-dropdown") {
cssRuleList[i].style.display = "none";
}
}
}
//show and hide translation
document.getElementById("showTranslation").onclick = function () {
let cssRuleList = document.styleSheets[0].rules;
for (i = 0; i < cssRuleList.length; i++) {
if (cssRuleList[i].selectorText == ".translation-dropdown") {
cssRuleList[i].style.display = "block";
}
}
}
document.getElementById("hideTranslation").onclick = function () {
let cssRuleList = document.styleSheets[0].rules;
for (i = 0; i < cssRuleList.length; i++) {
if (cssRuleList[i].selectorText == ".translation-dropdown") {
cssRuleList[i].style.display = "none";
}
}
}
//show and hide notes
document.getElementById("showNotes").onclick = function () {
let cssRuleList = document.styleSheets[0].rules;
for (i = 0; i < cssRuleList.length; i++) {
if (cssRuleList[i].selectorText == ".notes-dropdown") {
cssRuleList[i].style.display = "block";
}
}
}
document.getElementById("hideNotes").onclick = function () {
let cssRuleList = document.styleSheets[0].rules;
for (i = 0; i < cssRuleList.length; i++) {
if (cssRuleList[i].selectorText == ".notes-dropdown") {
cssRuleList[i].style.display = "none";
}
}
}
//show and hide all
//for some reason these don't work if you do a single if() condition like so:
//if (cssRuleList[i].selectorText == ".correspondences-dropdown" || ".glosses-dropdown" || ".translation-dropdown" || ".notes-dropdown")
//instead, I had to do a series of "else" statements.
document.getElementById("showAll").onclick = function () {
let cssRuleList = document.styleSheets[0].rules;
for (i = 0; i < cssRuleList.length; i++) {
if (cssRuleList[i].selectorText == ".correspondences-dropdown") {
cssRuleList[i].style.display = "block";
} else if (cssRuleList[i].selectorText == ".glosses-dropdown") {
cssRuleList[i].style.display = "block";
} else if (cssRuleList[i].selectorText == ".translation-dropdown") {
cssRuleList[i].style.display = "block";
} else if (cssRuleList[i].selectorText == ".notes-dropdown") {
cssRuleList[i].style.display = "block";
}
}
}
document.getElementById("hideAll").onclick = function () {
let cssRuleList = document.styleSheets[0].rules;
for (i = 0; i < cssRuleList.length; i++) {
if (cssRuleList[i].selectorText == ".correspondences-dropdown") {
cssRuleList[i].style.display = "none";
} else if (cssRuleList[i].selectorText == ".glosses-dropdown") {
cssRuleList[i].style.display = "none";
} else if (cssRuleList[i].selectorText == ".translation-dropdown") {
cssRuleList[i].style.display = "none";
} else if (cssRuleList[i].selectorText == ".notes-dropdown") {
cssRuleList[i].style.display = "none";
}
}
}
//show and hide sidebar
document.getElementById("showSidebar").onclick = function () {
let cssRuleList = document.styleSheets[0].rules;
for (i = 0; i < cssRuleList.length; i++) {
if (cssRuleList[i].selectorText == ".sidebar") {
cssRuleList[i].style.visibility = "visible";
}
}
}
document.getElementById("hideSidebar").onclick = function () {
let cssRuleList = document.styleSheets[0].rules;
for (i = 0; i < cssRuleList.length; i++) {
if (cssRuleList[i].selectorText == ".sidebar") {
cssRuleList[i].style.visibility = "hidden";
}
}
}
//show and hide fieldnames
//this used to use a ".sentence details summary" selector
document.getElementById("showFieldNames").onclick = function () {
let cssRuleList = document.styleSheets[0].rules;
for (i = 0; i < cssRuleList.length; i++) {
if (cssRuleList[i].selectorText == "summary:not(#technical)") {
cssRuleList[i].style.display = "list-item";
}
}
}
document.getElementById("hideFieldNames").onclick = function () {
let cssRuleList = document.styleSheets[0].rules;
for (i = 0; i < cssRuleList.length; i++) {
if (cssRuleList[i].selectorText == "summary:not(#technical)") {
cssRuleList[i].style.display = "none";
}
}
}
//delete block button
//try 1 mostly worked, but didn't work on newly inserted blocks. doesn't matter whether I used querySelectorAll
//or getElementsByClassNAme
//var deleteButtons = document.querySelectorAll(".delete");
/*var deleteButtons = document.getElementsByClassName("delete");
for(let i = 0; i< deleteButtons.length; i++) {
deleteButtons[i].addEventListener("click", function(){
if (confirm("Are you sure you want to delete block " + (i + 1)+ "?" + "\nWarning: deleting a block cannot be undone.")) {
buttonBlock = this.parentElement.parentElement;
buttonBlock.remove();
}
})
}*/
//try 2 worked, using event bubbling. just doesn't have index of block.
/*let container=document.body;
container.addEventListener('click', function (e) {
if (e.target.classList.contains('delete')) {
if (confirm("Are you sure you want to delete this block?" + "\nWarning: deleting a block cannot be undone.")) {
buttonBlock = e.target.parentElement.parentElement;
buttonBlock.remove();
}
}
})*/
//try 3 in case I can do if(deletebuttons[i]==e.target) to find the index of the target
let container=document.body;
container.addEventListener('click', function (e) {
if (e.target.classList.contains('delete')) {
let deleteButtons = document.getElementsByClassName("delete");
for(let i = 0; i< deleteButtons.length; i++) {
if (deleteButtons[i] == e.target) {
if (confirm("Are you sure you want to delete block " + (i+1) + "?" + "\nWarning: deleting a block cannot be undone.")) {
buttonBlock = e.target.parentElement.parentElement;
buttonBlock.remove();
}
}
}
}
})
//wahoo!! that worked
//sidebar insert button
//try 1 -- didn't work for newly inserted blocks.
//I think the other buttons work for newly inserted blocks because they don't rely on eventlisteners.
/*document.getElementsByClassName("insert").onclick = function () {
let insertButtons = document.getElementsByClassName("insert");
for(let i = 0; i< insertButtons.length; i++) {
insertButtons[i].addEventListener("click", function(){
this.parentElement.parentElement.insertAdjacentHTML('afterend',sentenceBlock);
})
}
}*/
//try 2 and 3: