clean-mla-gdoc 1.0.3 → 1.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/README.md CHANGED
@@ -1,15 +1,15 @@
1
- # cleanMLAGDoc.js v1.0.3
1
+ # cleanMLAGDoc.js v1.1.0
2
2
 
3
3
  A lightweight JavaScript library specifically designed to reformat **Google Docs HTML exports** into the **MLA (Modern Language Association) Style** layout. It handles physical pagination, alphabetical sorting of Works Cited, and provides a "DRAFT" watermark feature.
4
4
 
5
5
  > [!IMPORTANT]
6
- > **Compatibility Note:** This library is specifically tailored for the class structures (`.c1`, `.c7`, `.doc-content`) generated by **Google Docs** when exported as HTML. For best results, ensure your Google Doc is using the **Report** preset before exporting.
6
+ > **Compatibility Note:** This library is specifically tailored for the **Report** preset generated by **Google Docs** when before exporting as HTML.
7
7
 
8
8
  ## Features
9
9
  * **Dynamic Tab Title**: Automatically renames the browser tab to `[LastName] [FirstName] [AssignmentName]`.
10
10
  * **Auto-Pagination**: Breaks content into 8.5" x 11" pages with 1-inch margins.
11
11
  * **MLA Header**: Processes the 4-line header and title from Google Docs classes.
12
- * **Smart Date Mapping**: Converts numerical dates (e.g., `12/25/2026`) to MLA format (`25 Dec. 2026`).
12
+ * **Smart Date Mapping**: Converts numerical dates (e.g., `25/12/2026`) to MLA format (`25 Dec. 2026`).
13
13
  * **Works Cited Sorting**: Automatically alphabetizes the "Works Cited" section.
14
14
  * **Draft Mode**: Trigger a "DRAFT" watermark by adding `| draft` to your document header.
15
15
 
@@ -32,16 +32,10 @@ Any content following an element containing the text "Works Cited" will be moved
32
32
  Include the minified script in your project. The library will automatically detect if jQuery is present; if not, it will load it along with other necessary assets from a CDN.
33
33
 
34
34
  ```html
35
- <!-- Include the library at the end of your body tag -->
36
- <script src="https://cdn.jsdelivr.net"></script>
35
+ <!-- Include the library after the meta tag of your head tag -->
36
+ <script src="https://cdn.jsdelivr.net/npm/clean-mla-gdoc@v1.1.0/dist/clean-mla-gdoc.min.js"></script>
37
37
  ```
38
38
 
39
- ## Printing
40
- Printing is **seamless**. Because the library handles all pagination, margins, and font styling via a specialized `@media print` stylesheet, you do not need to adjust your browser's print settings. Simply use `Ctrl + P` (or `Cmd + P`) and the document will print exactly as it appears on the screen.
41
-
42
- > [!TIP]
43
- > **Print Settings:** For the cleanest output, ensure **"Headers and Footers"** is **unchecked** in your browser's print dialog to avoid redundant browser-generated page numbers or URLs. To see the "DRAFT" watermark, ensure **"Background Graphics"** is **checked**.
44
-
45
39
  ## Contributing
46
40
  Contributions are welcome! If Google Docs updates its HTML export structure and changes class names (like `.c1` or `.c7`), please feel free to:
47
41
  1. **Fork** the repository.
@@ -58,4 +52,4 @@ This project is licensed under the **GNU General Public License v3.0**.
58
52
  Full license text available at: [https://www.gnu.org](https://www.gnu.org)
59
53
 
60
54
  ---
61
- **Copyright (c) 2026 Musicalisk <Musicalisk.travail@tuta.io>**
55
+ **Copyright (c) 2026 Musicalisk <Musicalisk.travail@tuta.io>**
@@ -0,0 +1,205 @@
1
+ /*
2
+ * clean-mla-gdoc.js v1.1.0
3
+ * Copyright (c) 2026 Musicalisk <Musicalisk.travail@tuta.io>
4
+ * Licensed under the GNU General Public License v3.0
5
+ * Full license text: https://www.gnu.org
6
+ */
7
+
8
+ (function() {
9
+ // Configuration for external dependencies and the Google Docs CSS signature
10
+ const L = {
11
+ jqueryUrl: "https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js",
12
+ faviconUrl: "https://cdn.jsdelivr.net/npm/favicon.js@1.0.0/dist/favicon.min.js",
13
+ iconFrames: [
14
+ 'https://img.icons8.com/?size=16&id=1395&format=png&color=FA5252',
15
+ 'https://img.icons8.com/?size=16&id=1395&format=png&color=20C997',
16
+ 'https://img.icons8.com/?size=16&id=1395&format=png&color=339AF0'
17
+ ],
18
+ signatureCSS: "padding-top:0pt;text-indent:36pt;padding-bottom:0pt;line-height:2.0;orphans:2;widows:2;text-align:right"
19
+ };
20
+
21
+ // Helper to load external scripts dynamically
22
+ const loadScript = (url, callback) => {
23
+ let script = document.createElement("script");
24
+ script.src = url;
25
+ script.onload = callback;
26
+ document.head.appendChild(script);
27
+ };
28
+
29
+ // Main formatting logic
30
+ const initializeCleanMLAG = () => {
31
+ /**
32
+ * CSS INJECTION
33
+ */
34
+ $('<style>').html(`
35
+ @page { size: 8.5in 11in; margin: 0; }
36
+ html { background: #d0d0d0 !important; width: 100% !important; }
37
+ body {
38
+ visibility: hidden;
39
+ margin: 0 !important;
40
+ padding: 40px 0 !important;
41
+ display: flex !important;
42
+ flex-direction: column !important;
43
+ align-items: center !important;
44
+ width: 100% !important;
45
+ max-width: none !important;
46
+ background: transparent !important;
47
+ font-family: "Times New Roman", serif !important;
48
+ line-height: 2.0 !important;
49
+ color: #000 !important;
50
+ }
51
+ .mla-page-container {
52
+ background: #fff !important;
53
+ box-shadow: 0 0 20px rgba(0,0,0,.2) !important;
54
+ width: 8.5in !important;
55
+ height: 11in !important;
56
+ margin: 0 auto 40px auto !important;
57
+ padding: 1in !important;
58
+ box-sizing: border-box !important;
59
+ position: relative;
60
+ overflow: hidden;
61
+ flex-shrink: 0;
62
+ }
63
+ .watermark::before {
64
+ content: 'DRAFT' !important;
65
+ position: absolute !important;
66
+ top: 50% !important;
67
+ left: 50% !important;
68
+ transform: translate(-50%, -50%) rotate(-45deg) !important;
69
+ font-size: 150px !important;
70
+ color: rgba(0,0,0,0.12) !important;
71
+ z-index: 99 !important;
72
+ pointer-events: none !important;
73
+ white-space: nowrap !important;
74
+ display: block !important;
75
+ }
76
+ .no-indent { text-indent: 0 !important; }
77
+ p { text-indent: .5in !important; margin: 0 !important; }
78
+ .header-wrapper {
79
+ position: absolute;
80
+ top: .5in;
81
+ right: 1in;
82
+ text-align: right !important;
83
+ text-indent: 0 !important;
84
+ width: 6.5in;
85
+ height: .5in;
86
+ line-height: .5in !important;
87
+ z-index: 10;
88
+ }
89
+ .hanging-indent { text-indent: -.5in !important; padding-left: .5in !important; }
90
+ .centered-title { text-align: center !important; text-indent: 0 !important; }
91
+ @media print {
92
+ html { background: #fff !important; }
93
+ body { padding: 0 !important; visibility: visible !important; }
94
+ .mla-page-container { margin: 0 auto !important; box-shadow: none !important; page-break-after: always !important; }
95
+ }
96
+ `).appendTo('head');
97
+
98
+ setTimeout(() => {
99
+ /**
100
+ * HEADER DETECTION VIA CSS SIGNATURE
101
+ */
102
+ let dH = '';
103
+ const targetSigClean = L.signatureCSS.replace(/\s+/g, '');
104
+
105
+ $('style').each(function() {
106
+ const cssText = $(this).text().replace(/\s+/g, '');
107
+ // Find the class (e.g. .c5) matching the orphans/widows/text-align:right signature
108
+ const match = cssText.match(new RegExp(`\\.([^\\{]+)\\{[^\\}]*${targetSigClean.replace(/;/g, '[^\\}]*;')}`));
109
+ if (match && match[1]) {
110
+ dH = match[1];
111
+ return false;
112
+ }
113
+ });
114
+
115
+ // Use detected class or fallback to .c1
116
+ const sel = dH ? `.${dH}` : '.c1';
117
+ const h = $(sel).first(), ht = h.text().trim();
118
+ const isD = /\|\s*draft/i.test(ht);
119
+ const an = String(ht.replace(/\|\s*draft.*/i, '').trim() || "Assignment");
120
+
121
+ // Extract Name and metadata (exclude the detected header paragraph)
122
+ let pgs = $('p').filter(function() {
123
+ return $(this).text().trim().length > 0 && !$(this).hasClass(dH);
124
+ });
125
+
126
+ let sLine = pgs.first().text().trim(),
127
+ nParts = sLine.split(' '),
128
+ fName = String(nParts[0] || "First"),
129
+ lName = String(nParts[nParts.length - 1] || "Last");
130
+
131
+ document.title = lName + " " + fName + " " + an;
132
+
133
+ // Cleanup raw header elements
134
+ $(sel).remove();
135
+ $('*').filter(function() {
136
+ return ($(this).css('text-align') === 'right' || $(this).css('position') === 'absolute') && $(this).text().includes(lName);
137
+ }).remove();
138
+
139
+ let e = $('.doc-content > *,.c7 > *,body > *').not('script,style,.mla-page-container').get();
140
+ $('body').children().not('script,style').remove();
141
+
142
+ const pMaker = (v) => {
143
+ let g = $('<div class="mla-page-container"></div>');
144
+ if (isD) g.addClass('watermark');
145
+ g.append(`<div class="header-wrapper">${lName} ${v}</div>`);
146
+ return g
147
+ };
148
+
149
+ let c = 1, u = pMaker(c), y = 0, x = 860, z = false, ph = false, pt = false, ci = [];
150
+ $('body').append(u);
151
+
152
+ const gm = (n) => ["", "Jan.", "Feb.", "Mar.", "Apr.", "May", "June", "July", "Aug.", "Sept.", "Oct.", "Nov.", "Dec."][parseInt(n)] || "",
153
+ dr = /^(\d{1,2})[\/\-\.](\d{1,2})[\/\-\.](\d{2,4})$/,
154
+ re = (o) => {
155
+ let k = $('<div style="width:6.5in;position:absolute;visibility:hidden;line-height:2.0"></div>').append(o.clone()).appendTo('body'),
156
+ q = k.outerHeight(true);
157
+ k.remove();
158
+ if (y + q > x) {
159
+ c++; u = pMaker(c); $('body').append(u); y = 0
160
+ }
161
+ u.append(o); y += q
162
+ };
163
+
164
+ $(e).each(function() {
165
+ let o = $(this), v = o.text().trim();
166
+ if (v.length === 0 && !o.is('img,table')) return;
167
+
168
+ if (v.toLowerCase().includes("works cited") && !z) {
169
+ z = true; c++; u = pMaker(c); $('body').append(u); y = 0;
170
+ o.addClass('centered-title no-indent'); re(o); return
171
+ }
172
+
173
+ if (!z) {
174
+ if (!ph) {
175
+ let m = v.match(dr);
176
+ if (m) { o.text(m[1] + " " + gm(m[2]) + " " + m[3]); ph = true }
177
+ o.addClass('no-indent')
178
+ } else if (!pt) {
179
+ o.addClass('centered-title no-indent'); pt = true
180
+ } else {
181
+ let h = o.html();
182
+ if (h.includes('&nbsp;')) o.html(h.replace(/&nbsp;/g, ' '))
183
+ }
184
+ re(o)
185
+ } else {
186
+ ci.push(o.addClass('hanging-indent no-indent'))
187
+ }
188
+ });
189
+
190
+ if (ci.length > 0) {
191
+ ci.sort((a, b) => a.text().trim().localeCompare(b.text().trim())).forEach(i => re(i))
192
+ }
193
+
194
+ $('body').css('visibility', 'visible');
195
+
196
+ // Execute Favicon animation
197
+ loadScript(L.faviconUrl, () => {
198
+ if (window.favicon || window.favico) (window.favicon || window.favico).animate(L.iconFrames, 1750);
199
+ });
200
+ }, 600);
201
+ };
202
+
203
+ // Entry point
204
+ window.jQuery ? initializeCleanMLAG() : loadScript(L.jqueryUrl, initializeCleanMLAG);
205
+ })();
@@ -0,0 +1 @@
1
+ /* clean-mla-gdoc.min.js v1.1.0 | Copyright (c) 2026 Musicalisk <Musicalisk.travail@tuta.io> | Licensed under the GNU General Public License v3.0 | Full license text: https://www.gnu.org */(function(){const L={j:"https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js",f:"https://cdn.jsdelivr.net/npm/favicon.js@1.0.0/dist/favicon.min.js",i:['https://img.icons8.com/?size=16&id=1395&format=png&color=FA5252','https://img.icons8.com/?size=16&id=1395&format=png&color=20C997','https://img.icons8.com/?size=16&id=1395&format=png&color=339AF0'],s:"padding-top:0pt;text-indent:36pt;padding-bottom:0pt;line-height:2.0;orphans:2;widows:2;text-align:right"};const l=(u,c)=>{let s=document.createElement("script");s.src=u;s.onload=c;document.head.appendChild(s)};const r=()=>{$('<style>').html(`@page{size:8.5in 11in;margin:0}html{background:#d0d0d0!important;width:100%!important}body{visibility:hidden;margin:0!important;padding:40px 0!important;display:flex!important;flex-direction:column!important;align-items:center!important;width:100%!important;max-width:none!important;background:transparent!important;font-family:"Times New Roman",serif!important;line-height:2.0!important;color:#000!important}.mla-page-container{background:#fff!important;box-shadow:0 0 20px rgba(0,0,0,.2)!important;width:8.5in!important;height:11in!important;margin:0 auto 40px auto!important;padding:1in!important;box-sizing:border-box!important;position:relative;overflow:hidden;flex-shrink:0}.watermark::before{content:'DRAFT'!important;position:absolute!important;top:50%!important;left:50%!important;transform:translate(-50%,-50%) rotate(-45deg)!important;font-size:150px!important;color:rgba(0,0,0,0.12)!important;z-index:99!important;pointer-events:none!important;white-space:nowrap!important;display:block!important}.no-indent{text-indent:0!important}p{text-indent:.5in!important;margin:0!important}.header-wrapper{position:absolute;top:.5in;right:1in;text-align:right!important;text-indent:0!important;width:6.5in;height:.5in;line-height:.5in!important;z-index:10}.hanging-indent{text-indent:-.5in!important;padding-left:.5in!important}.centered-title{text-align:center!important;text-indent:0!important}@media print{html{background:#fff!important}body{padding:0!important;visibility:visible!important}.mla-page-container{margin:0 auto!important;box-shadow:none!important;page-break-after:always!important}}`).appendTo('head');setTimeout(()=>{let dH='';const tC=L.s.replace(/\s+/g,'');$('style').each(function(){const cT=$(this).text().replace(/\s+/g,'');const m=cT.match(new RegExp(`\\.([^\\{]+)\\{[^\\}]*${tC.replace(/;/g,'[^\\}]*;')}`));if(m)dH=m[1]});const sel=dH?`.${dH}`:'.c1',h=$(sel).first(),ht=h.text().trim(),isD=/\|\s*draft/i.test(ht),an=String(ht.replace(/\|\s*draft.*/i,'').trim()||"Assignment");let pgs=$('p').filter(function(){return $(this).text().trim().length>0&&!$(this).hasClass(dH)});let sLine=pgs.first().text().trim(),n=sLine.split(' '),f=String(n[0]||"First"),ln=String(n[n.length-1]||"Last");document.title=`${ln} ${f} ${an}`;$(sel).remove();$('*').filter(function(){return($(this).css('text-align')==='right'||$(this).css('position')==='absolute')&&$(this).text().includes(ln)}).remove();let e=$('.doc-content > *,.c7 > *,body > *').not('script,style,.mla-page-container').get();$('body').children().not('script,style').remove();const pMaker=(v)=>{let g=$('<div class="mla-page-container"></div>');if(isD)g.addClass('watermark');g.append(`<div class="header-wrapper">${ln} ${v}</div>`);return g};let c=1,u=pMaker(c),y=0,x=860,z=false,ph=false,pt=false,ci=[];$('body').append(u);const gm=(n)=>["","Jan.","Feb.","Mar.","Apr.","May","June","July","Aug.","Sept.","Oct.","Nov.","Dec."][parseInt(n)]||"",dr=/^(\d{1,2})[\/\-\.](\d{1,2})[\/\-\.](\d{2,4})$/,re=(o)=>{let k=$('<div style="width:6.5in;position:absolute;visibility:hidden;line-height:2.0"></div>').append(o.clone()).appendTo('body'),q=k.outerHeight(true);k.remove();if(y+q>x){c++;u=pMaker(c);$('body').append(u);y=0}u.append(o);y+=q};$(e).each(function(){let o=$(this),v=o.text().trim();if(v.length===0&&!o.is('img,table'))return;if(v.toLowerCase().includes("works cited")&&!z){z=true;c++;u=pMaker(c);$('body').append(u);y=0;o.addClass('centered-title no-indent');re(o);return}if(!z){if(!ph){let m=v.match(dr);if(m){o.text(`${m[1]} ${gm(m[2])} ${m[3]}`);ph=true}o.addClass('no-indent')}else if(!pt){o.addClass('centered-title no-indent');pt=true}else{let h=o.html();if(h.includes('&nbsp;'))o.html(h.replace(/&nbsp;/g,' '))}re(o)}else{ci.push(o.addClass('hanging-indent no-indent'))}});if(ci.length>0){ci.sort((a,b)=>a.text().trim().localeCompare(b.text().trim())).forEach(i=>re(i))}$('body').css('visibility','visible');l(L.f,()=>{if(window.favicon||window.favico)(window.favicon||window.favico).animate(L.i,1750)})},600)};window.jQuery?r():l(L.j,r)})();
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "clean-mla-gdoc",
3
- "version": "1.0.3",
3
+ "version": "1.1.0",
4
4
  "description": "A lightweight JavaScript library to reformat Google Docs HTML exports into MLA Style layout with auto-pagination and Works Cited sorting.",
5
- "main": "dist/cleanMLAGDoc.js",
6
- "browser": "dist/cleanMLAGDoc.min.js",
5
+ "main": "dist/clean-mla-gdoc.js",
6
+ "browser": "dist/clean-mla-gdoc.min.js",
7
7
  "keywords": [
8
8
  "\"mla\"",
9
9
  "\"google-docs\"",
10
10
  "\"academic\""
11
11
  ],
12
- "homepage": "https://github.com/Musicalisk/clean-mla-gdoc#readme",
12
+ "homepage": "https://www.npmjs.com/package/clean-mla-gdoc",
13
13
  "bugs": {
14
14
  "url": "https://github.com/Musicalisk/clean-mla-gdoc/issues"
15
15
  },
@@ -1,246 +0,0 @@
1
- /*
2
- * cleanMLAGDoc.js v1.0.3
3
- * Copyright (c) 2026 Musicalisk <Musicalisk.travail@tuta.io>
4
- * Licensed under the GNU General Public License v3.0
5
- * Full license text: https://www.gnu.org
6
- */
7
-
8
- (function() {
9
- // Configuration for external dependencies and assets
10
- const CONFIG = {
11
- jqueryUrl: "https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js",
12
- faviconUrl: "https://cdn.jsdelivr.net/npm/favicon.js@1.0.0/dist/favicon.min.js",
13
- iconFrames: [
14
- 'https://img.icons8.com/?size=16&id=1395&format=png&color=FA5252',
15
- 'https://img.icons8.com/?size=16&id=1395&format=png&color=20C997',
16
- 'https://img.icons8.com/?size=16&id=1395&format=png&color=339AF0'
17
- ]
18
- };
19
-
20
- // Helper to load external scripts dynamically
21
- const loadScript = (url, callback) => {
22
- let script = document.createElement("script");
23
- script.src = url;
24
- script.onload = callback;
25
- document.head.appendChild(script);
26
- };
27
-
28
- // Main formatting logic
29
- const initializeCleanMLAG = () => {
30
- /**
31
- * CSS INJECTION
32
- * Defines the physical 8.5x11 inch page structure and the "DRAFT" watermark.
33
- */
34
- $('<style>').html(`
35
- @page { size: 8.5in 11in; margin: 0; }
36
- html { background: #d0d0d0 !important; width: 100% !important; }
37
- body {
38
- visibility: hidden;
39
- margin: 0 !important;
40
- padding: 40px 0 !important;
41
- display: flex !important;
42
- flex-direction: column !important;
43
- align-items: center !important;
44
- width: 100% !important;
45
- max-width: none !important;
46
- background: transparent !important;
47
- font-family: "Times New Roman", serif !important;
48
- line-height: 2.0 !important;
49
- color: #000 !important;
50
- }
51
- .mla-page-container {
52
- background: #fff !important;
53
- box-shadow: 0 0 20px rgba(0,0,0,.2) !important;
54
- width: 8.5in !important;
55
- height: 11in !important;
56
- margin: 0 auto 40px auto !important;
57
- padding: 1in !important;
58
- box-sizing: border-box !important;
59
- position: relative;
60
- overflow: hidden;
61
- flex-shrink: 0;
62
- }
63
- .watermark::before {
64
- content: 'DRAFT' !important;
65
- position: absolute !important;
66
- top: 50% !important;
67
- left: 50% !important;
68
- transform: translate(-50%, -50%) rotate(-45deg) !important;
69
- font-size: 150px !important;
70
- color: rgba(0,0,0,0.12) !important;
71
- z-index: 99 !important;
72
- pointer-events: none !important;
73
- white-space: nowrap !important;
74
- display: block !important;
75
- }
76
- .no-indent { text-indent: 0 !important; }
77
- p { text-indent: .5in !important; margin: 0 !important; }
78
- .header-wrapper {
79
- position: absolute;
80
- top: .5in;
81
- right: 1in;
82
- text-align: right !important;
83
- text-indent: 0 !important;
84
- width: 6.5in;
85
- height: .5in;
86
- line-height: .5in !important;
87
- z-index: 10;
88
- }
89
- .hanging-indent { text-indent: -.5in !important; padding-left: .5in !important; }
90
- .centered-title { text-align: center !important; text-indent: 0 !important; }
91
- @media print {
92
- html { background: #fff !important; }
93
- body { padding: 0 !important; visibility: visible !important; }
94
- .mla-page-container { margin: 0 auto !important; box-shadow: none !important; page-break-after: always !important; }
95
- }
96
- `).appendTo('head');
97
-
98
- setTimeout(() => {
99
- /**
100
- * DRAFT & METADATA DETECTION
101
- * Checks the header for the "| draft" string and extracts the assignment title.
102
- */
103
- const headerElement = $('.c1').first();
104
- const headerText = headerElement.text().trim();
105
- const isDraft = /\|\s*draft/i.test(headerText);
106
- const assignmentName = String(headerText.replace(/\|\s*draft.*/i, '').trim() || "Assignment");
107
-
108
- // Extract Name and Page Content
109
- let paragraphs = $('p').filter(function() {
110
- return $(this).text().trim().length > 0 && !$(this).hasClass('c1');
111
- });
112
-
113
- let firstLine = paragraphs.first().text().trim();
114
- let nameParts = firstLine.split(' ');
115
- let firstName = String(nameParts[0] || "First");
116
- let lastName = String(nameParts[nameParts.length - 1] || "Last");
117
-
118
- document.title = lastName + " " + firstName + " " + assignmentName;
119
-
120
- // Cleanup: remove raw elements and duplicate headers
121
- $('.c1').remove();
122
- $('*').filter(function() {
123
- return ($(this).css('text-align') === 'right' || $(this).css('position') === 'absolute') && $(this).text().includes(lastName);
124
- }).remove();
125
-
126
- let elementsToProcess = $('.doc-content > *,.c7 > *,body > *').not('script,style,.mla-page-container').get();
127
- $('body').children().not('script,style').remove();
128
-
129
- /**
130
- * PAGE FACTORY
131
- * Creates a new page and applies the "DRAFT" watermark if flagged.
132
- */
133
- const createPage = (pageNum) => {
134
- let page = $('<div class="mla-page-container"></div>');
135
- if (isDraft) page.addClass('watermark');
136
- page.append(`<div class="header-wrapper">${lastName} ${pageNum}</div>`);
137
- return page;
138
- };
139
-
140
- // Pagination state
141
- let currentPageNum = 1;
142
- let currentPage = createPage(currentPageNum);
143
- let currentHeight = 0;
144
- const maxHeight = 860; // Max vertical content height before page break
145
- let worksCitedStarted = false;
146
- let processedHeader = false;
147
- let processedTitle = false;
148
- let worksCitedItems = [];
149
-
150
- $('body').append(currentPage);
151
-
152
- /**
153
- * DATE LOGIC
154
- * Converts "MM/DD/YYYY" or "DD/MM/YYYY" styles to "DD Mon. YYYY".
155
- */
156
- const getMonthName = (n) => ["", "Jan.", "Feb.", "Mar.", "Apr.", "May", "June", "July", "Aug.", "Sept.", "Oct.", "Nov.", "Dec."][parseInt(n)] || "";
157
- const dateRegex = /^(\d{1,2})[\/\-\.](\d{1,2})[\/\-\.](\d{2,4})$/;
158
-
159
- /**
160
- * RENDERING ENGINE
161
- * Measures element height in a hidden container to determine if it fits on the current page.
162
- */
163
- const renderElement = (element) => {
164
- let tempWrapper = $('<div style="width:6.5in;position:absolute;visibility:hidden;line-height:2.0"></div>')
165
- .append(element.clone())
166
- .appendTo('body');
167
- let elementHeight = tempWrapper.outerHeight(true);
168
- tempWrapper.remove();
169
-
170
- if (currentHeight + elementHeight > maxHeight) {
171
- currentPageNum++;
172
- currentPage = createPage(currentPageNum);
173
- $('body').append(currentPage);
174
- currentHeight = 0;
175
- }
176
- currentPage.append(element);
177
- currentHeight += elementHeight;
178
- };
179
-
180
- // Content processing loop
181
- $(elementsToProcess).each(function() {
182
- let $el = $(this);
183
- let text = $el.text().trim();
184
-
185
- if (text.length === 0 && !$el.is('img,table')) return;
186
-
187
- // Detect start of Works Cited
188
- if (text.toLowerCase().includes("works cited") && !worksCitedStarted) {
189
- worksCitedStarted = true;
190
- currentPageNum++;
191
- currentPage = createPage(currentPageNum);
192
- $('body').append(currentPage);
193
- currentHeight = 0;
194
- $el.addClass('centered-title no-indent');
195
- renderElement($el);
196
- return;
197
- }
198
-
199
- if (!worksCitedStarted) {
200
- // Logic for first-page MLA header
201
- if (!processedHeader) {
202
- $el.addClass('no-indent');
203
- let match = text.match(dateRegex);
204
- if (match) {
205
- // match[1] = Day, match[2] = Month, match[3] = Year
206
- $el.text(match[1] + " " + getMonthName(match[2]) + " " + match[3]);
207
- processedHeader = true;
208
- }
209
- } else if (!processedTitle) {
210
- $el.addClass('centered-title no-indent');
211
- processedTitle = true;
212
- } else {
213
- let html = $el.html();
214
- if (html.includes('&nbsp;')) $el.html(html.replace(/&nbsp;/g, ' '));
215
- }
216
- renderElement($el);
217
- } else {
218
- // Gather entries for alphabetical sorting
219
- worksCitedItems.push($el.addClass('hanging-indent no-indent'));
220
- }
221
- });
222
-
223
- /**
224
- * WORKS CITED ALPHABETICAL SORT
225
- * Sorts entries and renders them to the final pages.
226
- */
227
- if (worksCitedItems.length > 0) {
228
- worksCitedItems.sort((a, b) => a.text().trim().localeCompare(b.text().trim()))
229
- .forEach(item => renderElement(item));
230
- }
231
-
232
- // Reveal document
233
- $('body').css('visibility', 'visible');
234
-
235
- // Optional Favicon Animation
236
- loadScript(CONFIG.faviconUrl, () => {
237
- if (window.favicon || window.favico) {
238
- (window.favicon || window.favico).animate(CONFIG.iconFrames, 1750);
239
- }
240
- });
241
- }, 600);
242
- };
243
-
244
- // Entry point: ensure jQuery is loaded
245
- window.jQuery ? initializeCleanMLAG() : loadScript(CONFIG.jqueryUrl, initializeCleanMLAG);
246
- })();
@@ -1 +0,0 @@
1
- /* cleanMLAGDoc.min.js v1.0.3 | Copyright (c) 2026 Musicalisk <Musicalisk.travail@tuta.io> | Licensed under the GNU General Public License v3.0 | Full license text: https://www.gnu.org */(function(){const L={j:"https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js",f:"https://cdn.jsdelivr.net/npm/favicon.js@1.0.0/dist/favicon.min.js",i:['https://img.icons8.com/?size=16&id=1395&format=png&color=FA5252','https://img.icons8.com/?size=16&id=1395&format=png&color=20C997','https://img.icons8.com/?size=16&id=1395&format=png&color=339AF0']};const l=(u,c)=>{let s=document.createElement("script");s.src=u;s.onload=c;document.head.appendChild(s)};const r=()=>{$('<style>').html(`@page{size:8.5in 11in;margin:0}html{background:#d0d0d0!important;width:100%!important}body{visibility:hidden;margin:0!important;padding:40px 0!important;display:flex!important;flex-direction:column!important;align-items:center!important;width:100%!important;max-width:none!important;background:transparent!important;font-family:"Times New Roman",serif!important;line-height:2.0!important;color:#000!important}.mla-page-container{background:#fff!important;box-shadow:0 0 20px rgba(0,0,0,.2)!important;width:8.5in!important;height:11in!important;margin:0 auto 40px auto!important;padding:1in!important;box-sizing:border-box!important;position:relative;overflow:hidden;flex-shrink:0}.watermark::before{content:'DRAFT'!important;position:absolute!important;top:50%!important;left:50%!important;transform:translate(-50%,-50%) rotate(-45deg)!important;font-size:150px!important;color:rgba(0,0,0,0.12)!important;z-index:99!important;pointer-events:none!important;white-space:nowrap!important;display:block!important}.no-indent{text-indent:0!important}p{text-indent:.5in!important;margin:0!important}.header-wrapper{position:absolute;top:.5in;right:1in;text-align:right!important;text-indent:0!important;width:6.5in;height:.5in;line-height:.5in!important;z-index:10}.hanging-indent{text-indent:-.5in!important;padding-left:.5in!important}.centered-title{text-align:center!important;text-indent:0!important}@media print{html{background:#fff!important}body{padding:0!important;visibility:visible!important}.mla-page-container{margin:0 auto!important;box-shadow:none!important;page-break-after:always!important}}`).appendTo('head');setTimeout(()=>{const h=$('.c1').first(),ht=h.text().trim();const isD=/\|\s*draft/i.test(ht);const an=String(ht.replace(/\|\s*draft.*/i,'').trim()||"Assignment");let pgs=$('p').filter(function(){return $(this).text().trim().length>0&&!$(this).hasClass('c1')});let s=pgs.first().text().trim(),n=s.split(' '),f=String(n[0]||"First"),ln=String(n[n.length-1]||"Last");document.title=ln+" "+f+" "+an;$('.c1').remove();$('*').filter(function(){return($(this).css('text-align')==='right'||$(this).css('position')==='absolute')&&$(this).text().includes(ln)}).remove();let e=$('.doc-content > *,.c7 > *,body > *').not('script,style,.mla-page-container').get();$('body').children().not('script,style').remove();const p=(v)=>{let g=$('<div class="mla-page-container"></div>');if(isD)g.addClass('watermark');g.append(`<div class="header-wrapper">${ln} ${v}</div>`);return g};let c=1,u=p(c),y=0,x=860,z=false,ph=false,pt=false,ci=[];$('body').append(u);const gm=(n)=>["","Jan.","Feb.","Mar.","Apr.","May","June","July","Aug.","Sept.","Oct.","Nov.","Dec."][parseInt(n)]||"",dr=/^(\d{1,2})[\/\-\.](\d{1,2})[\/\-\.](\d{2,4})$/,re=(o)=>{let k=$('<div style="width:6.5in;position:absolute;visibility:hidden;line-height:2.0"></div>').append(o.clone()).appendTo('body'),q=k.outerHeight(true);k.remove();if(y+q>x){c++;u=p(c);$('body').append(u);y=0}u.append(o);y+=q};$(e).each(function(j){let o=$(this),v=o.text().trim();if(v.length===0&&!o.is('img,table'))return;if(v.toLowerCase().includes("works cited")&&!z){z=true;c++;u=p(c);$('body').append(u);y=0;o.addClass('centered-title no-indent');re(o);return}if(!z){if(!ph){let m=v.match(dr);if(m){o.text(m[1]+" "+gm(m[2])+" "+m[3]);ph=true}o.addClass('no-indent')}else if(!pt){o.addClass('centered-title no-indent');pt=true}else{let h=o.html();if(h.includes('&nbsp;'))o.html(h.replace(/&nbsp;/g,' '))}re(o)}else{ci.push(o.addClass('hanging-indent no-indent'))}});if(ci.length>0){ci.sort((a,b)=>a.text().trim().localeCompare(b.text().trim())).forEach(i=>re(i))} $('body').css('visibility','visible');l(L.f,()=>{if(window.favicon||window.favico)(window.favicon||window.favico).animate(L.i,1750)})},600)};window.jQuery?r():l(L.j,r)})();