Skip to content

Commit a6a22c3

Browse files
author
sanka
committed
js added
1 parent 12c944b commit a6a22c3

File tree

5 files changed

+386
-6
lines changed

5 files changed

+386
-6
lines changed

js/converter.js

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
document.getElementById("upload").addEventListener("change", handleFile);
2+
document.getElementById("download").addEventListener("click", downloadHTML);
3+
4+
async function handleFile(event) {
5+
const file = event.target.files[0];
6+
if (!file) return;
7+
8+
if (!isValidIDML(file)) {
9+
alert("Please upload a valid IDML file.");
10+
return;
11+
}
12+
13+
document.getElementById("loadingProgress").value = 25;
14+
const zip = new JSZip();
15+
try {
16+
const zipContents = await zip.loadAsync(file);
17+
document.getElementById("loadingProgress").value = 50;
18+
19+
const extractedData = await processIDML(zipContents);
20+
document.getElementById("loadingProgress").value = 75;
21+
22+
displayData(extractedData);
23+
document.getElementById("loadingProgress").value = 100;
24+
document.getElementById("download").style.display = "block";
25+
} catch (error) {
26+
console.error("Error processing IDML file:", error);
27+
alert("There was an error processing the IDML file.");
28+
}
29+
}
30+
31+
// Function to process the IDML contents (stories, spreads, and images)
32+
async function processIDML(zipContents) {
33+
let stories = [], spreads = [];
34+
35+
// Loop through the zip file and extract stories and spreads
36+
for (let filename of Object.keys(zipContents.files)) {
37+
if (filename.startsWith("Stories/") && filename.endsWith(".xml")) {
38+
const xmlText = await zipContents.files[filename].async("text");
39+
stories.push(...parseStoryXML(xmlText)); // Process story XML
40+
} else if (filename.startsWith("Spreads/") && filename.endsWith(".xml")) {
41+
const xmlText = await zipContents.files[filename].async("text");
42+
spreads.push(...parseSpreadXML(xmlText)); // Process spread XML (images)
43+
}
44+
}
45+
return { stories, spreads };
46+
}
47+
48+
// Parse the Story XML content (extract only Content)
49+
function parseStoryXML(xmlText) {
50+
const parser = new DOMParser();
51+
const xmlDoc = parser.parseFromString(xmlText, "text/xml");
52+
let contentArray = [];
53+
54+
// Extract the text content from Content elements only (ignoring formatting)
55+
xmlDoc.querySelectorAll("Content").forEach(node => {
56+
let content = node.textContent.trim();
57+
if (content) {
58+
content = cleanContent(content); // Clean the extracted content
59+
contentArray.push(content); // Push to the result array
60+
}
61+
});
62+
63+
return contentArray;
64+
}
65+
66+
// Parse the Spread XML content (extract image links)
67+
function parseSpreadXML(xmlText) {
68+
let imagePatterns = [];
69+
const imageLinks = xmlText.match(/LinkResourceURI="([^"]*)"/g);
70+
71+
if (imageLinks) {
72+
imageLinks.forEach(link => {
73+
const path = link.replace('LinkResourceURI="', "").replace('"', "");
74+
imagePatterns.push(`<p class="img">${path}</p>`); // Create HTML img tag with path
75+
});
76+
}
77+
return imagePatterns;
78+
}
79+
80+
// Clean up content from unnecessary tags and spaces (we keep the content, remove metadata)
81+
function cleanContent(content) {
82+
const cleanPatterns = [
83+
{ regex: /<Content>/g, replacement: "" },
84+
{ regex: /<\/Content>/g, replacement: "" },
85+
{ regex: /<ParagraphStyleRange\s+[^>]*>/g, replacement: "" }, // Remove paragraph styles
86+
{ regex: /<\/ParagraphStyleRange>/g, replacement: "" }, // Remove paragraph styles
87+
{ regex: /AppliedParagraphStyle="[^"]*"/g, replacement: "" }, // Remove style attributes
88+
{ regex: /\xE2\x80\xA8/g, replacement: "" }, // Clean non-breaking spaces
89+
{ regex: /\s+/g, replacement: " " } // Replace multiple spaces with a single space
90+
];
91+
92+
cleanPatterns.forEach(pattern => {
93+
content = content.replace(pattern.regex, pattern.replacement);
94+
});
95+
96+
return content.trim(); // Return cleaned content
97+
}
98+
99+
// Display the extracted data (stories and image links)
100+
function displayData({ stories, spreads }) {
101+
const output = document.getElementById("output");
102+
output.innerHTML = "";
103+
104+
let content = "";
105+
106+
if (stories.length) {
107+
content += "<h2>Stories</h2>";
108+
stories.forEach(story => content += `<p>${story}</p>`); // Display each extracted content in paragraphs
109+
}
110+
111+
if (spreads.length) {
112+
content += "<h2>Images</h2>";
113+
spreads.forEach(img => content += img); // Display image links as <p> elements
114+
}
115+
116+
output.innerHTML = content;
117+
}
118+
119+
// Trigger HTML download
120+
function downloadHTML() {
121+
const output = document.getElementById("output").innerHTML;
122+
const blob = new Blob(["<html><body>" + output + "</body></html>"], { type: "text/html" });
123+
const link = document.createElement("a");
124+
link.href = URL.createObjectURL(blob);
125+
link.download = "output.html";
126+
document.body.appendChild(link);
127+
link.click();
128+
document.body.removeChild(link);
129+
}
130+
131+
// Check if the uploaded file is a valid IDML file
132+
function isValidIDML(file) {
133+
const fileExtension = file.name.split('.').pop().toLowerCase();
134+
return fileExtension === 'idml'; // Validate that the file is an IDML
135+
}
136+

js/index.html

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
2+
3+
<!DOCTYPE html>
4+
<html lang="en">
5+
<head>
6+
<meta name="generator" content="Hugo 0.54.0">
7+
<meta charset="UTF-8">
8+
<meta http-equiv="x-ua-compatible" content="IE=edge">
9+
<meta name="viewport" content="width=device-width,initial-scale=1">
10+
<meta property="og:type" content="article">
11+
<meta property="og:site_name" content="Convert IDML to HTML">
12+
<meta property="og:title" content="ChatGPT Detector">
13+
<meta itemprop="name" content="ChatGPT Detector">
14+
<meta property="og:url" content="https://textvisualization.app/idml2html/">
15+
<meta property="og:image" content="https://textvisualization.app/img/back3.webp">
16+
<meta itemprop="image" content="https://textvisualization.app/img/back3.webp">
17+
<meta property="og:locale" content="en_US">
18+
<meta property="og:locale:alternate" content="en_US">
19+
<meta property="fb:admins" content="">
20+
<meta name="twitter:card" content="summary_large_image">
21+
<meta name="twitter:site" content="https://textvisualization.app">
22+
<meta name="twitter:creator" content="">
23+
<meta name="twitter:image" content="https://textvisualization.app/img/back3.webp">
24+
<meta name="twitter:domain" content="https://textvisualization.app">
25+
<meta property="og:description" content="Convert IDML to HTML">
26+
<meta name="description" content="Convert IDML to HTML">
27+
<meta name="DC.Description" content="Convert IDML to HTML">
28+
<meta name="twitter:description" content="Convert IDML to HTML">
29+
<meta itemprop="description" content="Convert IDML to HTML">
30+
<meta name="DC.Title" content="Convert IDML to HTML">
31+
<meta name="twitter:title" content="Convert IDML to HTML">
32+
<meta name="DC.Creator" content="Convert IDML to HTML">
33+
<meta itemprop="author" content="Convert IDML to HTML">
34+
<meta name="DC.Type" content="Text">
35+
<meta name="DC.Format" content="text/html">
36+
<meta name="DC.Language" content="en_US">
37+
<meta http-equiv="content-language" content="en-US">
38+
<link rel="canonical" href="https://textvisualization.app/idml2html/">
39+
<title>Free IDML Convterter</title>
40+
41+
42+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.9.4/css/bulma.min.css">
43+
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/js/all.min.js"></script>
44+
45+
<style>
46+
.hero.is-info.is-bold {
47+
background: linear-gradient(to right, #ff0000, #ff9900, #ffcc00, #33cc33, #3e8ed0, #cc33ff);
48+
background-size: 600% 600%;
49+
animation: gradientAnimation 30s ease infinite
50+
}
51+
52+
@keyframes gradientAnimation {
53+
0% {
54+
background-position: 0
55+
}
56+
57+
50% {
58+
background-position: 100%
59+
}
60+
61+
100% {
62+
background-position: 0
63+
}
64+
}
65+
66+
.card-image::before {
67+
content: '';
68+
position: absolute;
69+
top: 0;
70+
left: 0;
71+
width: 100%;
72+
height: 100%;
73+
background: linear-gradient(to right, #f17c7c, #f5b64b, #f8d648, #a0de77, #6ab7ff, #c991e1);
74+
opacity: .8;
75+
z-index: 1
76+
}
77+
78+
.card-image img {
79+
position: relative;
80+
z-index: 2
81+
}
82+
83+
.card-content {
84+
background-color: #f5f5f5
85+
}
86+
87+
.is-shady {
88+
animation: flyintoright .4s backwards;
89+
background: #fff;
90+
box-shadow: rgba(0, 0, 0, .1) 0 1px 0;
91+
border-radius: 4px;
92+
display: inline-block;
93+
position: relative;
94+
transition: all .2s ease-in-out
95+
}
96+
97+
.is-shady:hover {
98+
box-shadow: 0 10px 16px rgba(0, 0, 0, .13), 0 6px 6px rgba(0, 0, 0, .19)
99+
}
100+
</style>
101+
<link rel="apple-touch-icon" sizes="180x180" href="https://textvisualization.app/apple-touch-icon.png">
102+
<link rel="icon" type="image/png" sizes="32x32" href="https://textvisualization.app/favicon-32x32.png">
103+
<link rel="icon" type="image/png" sizes="16x16" href="https://textvisualization.app/favicon-16x16.png">
104+
<link rel="manifest" href="https://textvisualization.app/site.webmanifest">
105+
<link rel="mask-icon" href="https://textvisualization.app/safari-pinned-tab.svg" color="#5bbad5">
106+
<meta name="msapplication-TileColor" content="#da532c">
107+
<meta name="theme-color" content="#ffffff">
108+
109+
<style>
110+
.highlight {
111+
background-color: yellow;
112+
font-weight: bold;
113+
}
114+
</style>
115+
116+
117+
</head>
118+
119+
<script async src="https://www.googletagmanager.com/gtag/js?id=G-0B4W23KH9S"></script>
120+
<script>
121+
window.dataLayer = window.dataLayer || [];
122+
function gtag(){dataLayer.push(arguments);}
123+
gtag('js', new Date());
124+
125+
gtag('config', 'G-0B4W23KH9S');
126+
</script>
127+
128+
<body>
129+
130+
<section class="hero is-info is-medium is-bold">
131+
<div class="hero-head">
132+
<nav class="navbar">
133+
<div class="container">
134+
<div class="navbar-brand">
135+
<a class="navbar-item" href="https://textvisualization.app/">
136+
<big>Semascope 👁️</big>
137+
</a>
138+
<span class="navbar-burger burger" data-target="navbarMenu">
139+
<span></span>
140+
<span></span>
141+
<span></span>
142+
</span>
143+
</div>
144+
<div id="navbarMenu" class="navbar-menu">
145+
<div class="navbar-end">
146+
<div class="tabs is-right">
147+
<ul>
148+
<li><a href="https://textvisualization.app/idml2html/"><b>ChatGPT Detector</b></a></li>
149+
<li><a href="https://textvisualization.app/free-online-keyword-extractor/">Keyword Extractor</a></li>
150+
<li><a href="https://textvisualization.app/hugo-yaml-markdown-generator/">Hugo Markdown Editor</a></li>
151+
</ul>
152+
</div>
153+
</div>
154+
</div>
155+
</div>
156+
</nav>
157+
</div>
158+
</section>
159+
160+
161+
162+
163+
164+
165+
166+
167+
168+
169+
170+
171+
<section class="section">
172+
<div class="container">
173+
<h1 class="title has-text-centered">Free IDML to HTML Converter</h1>
174+
<p class="subtitle has-text-centered">Upload an IDML file to extract content and convert it to HTML. Works with Google Chrome. Wait after uploads.</p>
175+
176+
<p class="subtitle has-text-centered"> If you need to convert thousands of files (IDML, PDF, DOC, HTML archives) into nicely formatted web-ready content, please <a href="mailto:a.sotov@yahoo.co.uk">write</a>. I will optimize your unique content management workflow, remove any formatting-related noise from input files, and create custom automation to prepare your old content for the web. Legacy formats, unsearchable archives, zoos of weakly compatible systems are common woes, but fear not, you are not alone.</p>
177+
178+
<p class="subtitle has-text-centered">This app is available for free, copy and modify it as needed. Please, star my <a href="https://github.com/roverbird/idml2html-python/">repo</a>.</p>
179+
180+
<p class="subtitle has-text-centered"> I am available for <a href="https://www.linkedin.com/in/alexander-sotov/">hire</a>.</p>
181+
182+
<div class="file has-name is-boxed is-centered">
183+
<label class="file-label">
184+
<input class="file-input" type="file" id="upload" accept=".idml">
185+
<span class="file-cta">
186+
<span class="file-icon">
187+
<i class="fas fa-upload"></i>
188+
</span>
189+
<span class="file-label">Choose an IDML file...</span>
190+
</span>
191+
<span id="file-name" class="file-name">No file selected</span>
192+
</label>
193+
</div>
194+
195+
<div class="column is-12 mt-3">
196+
<progress id="loadingProgress" class="progress is-primary" value="0" max="100" style="display: none;"></progress>
197+
</div>
198+
199+
<div class="buttons is-centered mt-4">
200+
<button class="button is-primary" id="download" style="display: none;">
201+
<i class="fas fa-download"></i> Download HTML
202+
</button>
203+
<button class="button is-light" id="view" style="display: none;">
204+
<i class="fas fa-eye"></i> View Output
205+
</button>
206+
</div>
207+
<div class="content mt-4" id="output"></div>
208+
209+
<p class="subtitle has-text-centered">Built and maintained by <br><a href="https://github.com/roverbird"><img src="https://coalmillsafety.com/img/logo-s-small.svg"></a></small></p>
210+
</div>
211+
</section>
212+
213+
<script src="https://cdn.jsdelivr.net/npm/jszip@3.10.1/dist/jszip.min.js"></script>
214+
215+
<!-- Import DeepIDML.js and Converter.js as Modules -->
216+
<script type="module">
217+
import './converter.js'; // Import the converter.js
218+
</script>
219+
220+
221+
<script>
222+
// Update file name display when a file is chosen
223+
document.getElementById("upload").addEventListener("change", function(event) {
224+
const file = event.target.files[0];
225+
if (file) {
226+
document.getElementById("file-name").textContent = file.name;
227+
document.getElementById("loadingProgress").style.display = "block";
228+
}
229+
});
230+
</script>
231+
</body>
232+
</html>
233+
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)