clean-mla-gdoc 1.0.4 → 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 +6 -12
- package/dist/clean-mla-gdoc.js +205 -0
- package/dist/clean-mla-gdoc.min.js +1 -0
- package/package.json +4 -4
- package/dist/cleanMLAGDoc.js +0 -246
- package/dist/cleanMLAGDoc.min.js +0 -1
package/README.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
# cleanMLAGDoc.js v1.0
|
|
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
|
|
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/
|
|
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
|
|
36
|
-
<script src="https://cdn.jsdelivr.net/npm/clean-mla-gdoc@v1.0
|
|
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(' ')) o.html(h.replace(/ /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(' '))o.html(h.replace(/ /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
|
+
"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/
|
|
6
|
-
"browser": "dist/
|
|
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://
|
|
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
|
},
|
package/dist/cleanMLAGDoc.js
DELETED
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* cleanMLAGDoc.js v1.0.4
|
|
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(' ')) $el.html(html.replace(/ /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
|
-
})();
|
package/dist/cleanMLAGDoc.min.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
/* cleanMLAGDoc.min.js v1.0.4 | 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(' '))o.html(h.replace(/ /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)})();
|