wrap google doc content with divs and turn into html file - html

I'm trying to turn google docs file into html file. I gave html tags to the content via google docs (h1, h2, p, etc) and then I downloaded it as HTML file and it works great.
I have one problem - I want to wrap specific contents in my google doc file with divs, a div for each chapter for example. Right now the file is just a list of html tags (h1, p, h2 etc) and I need it to be more correct hierarchically.
Is there a way to do that? I tried to use page break and other similar options but it just adds another element to the list and not wrapping a specific content in a div as I wish.
A javascript solution will be good too.
Would appreciate any help, Thanks!
Nir

You can try this approach on Google Apps Script. This is in no way the only solution. This is only a simple code you can try from many possible solutions available. Feel free to modify if needed depending on your use case.
Code:
function myFunction() {
var doc = DocumentApp.getActiveDocument();
var content = doc.getBody();
var numChildren = content.getNumChildren();
var output = [];
// Send email to this address
var sendTo = "test#gmail.com";
output.push("<html><body>");
for(var i=0; i < numChildren; i++){
var item = content.getChild(i).asText();
var text = item.getText();
// Assuming that a chapter always starts in bold headers, we start the div there
if(item.isBold()) {
// Add opening div tags on every start of header, see doc format below
output.push('<div>');
output.push('<h1>' + text + '</h1>');
}
// If not bold, then that element is assumed as the content of the chapter
else if (text){
output.push('<p>' + text + '</p>');
}
}
output.push("</body></html>");
// Get all indices where div is (except the first) and reverse
var indexes = getAllIndexes(output, "<div>");
// Per div found, insert closing div tag </div> before it
indexes.forEach(function(index){
output.splice(index, 0, "</div>");
});
// Join output array and treat it as html
var html = Utilities.newBlob(output.join(""), 'text/html', 'doc_to_html.html');
// Send to your email (modify email above if needed) the converted file with div tags
MailApp.sendEmail(sendTo, "Attached html-converted document", "file is attached below", {name: 'doc-to-html', attachments: [html], noReply: true});
}
function getAllIndexes(arr, val) {
var indexes = [], i = -1;
while ((i = arr.indexOf(val, i+1)) != -1){
indexes.push(i);
}
// Remove the first index (we don't need to add closing divs before the first div)
indexes.shift();
// Return reversed indices since we will add from the end since we are inserting closing div tags (</div>)
// Inserting from the start will change the indices of those succeeding opening div tags (<div>) we need to close
return indexes.reverse();
}
Email:
HTML Attachment:
Note:
This was assumed that per chapter, there is a single header at the start (we insert the <div> here), and paragraph/s below it. The closing div tags </divs> are inserted every before the next <div> tags found.

Related

How do I get a Google Apps Script Document Outline from the API?

I am able to get the contents of a documents outline by searching all the paragraphs and seeing the heading each paragraph is using. This works but isn't ideal. If I manually remove an item from the outline, it doesn't change the heading of that item to normal. Therefore when I run my code again it'll detect the removed item as still being part of the outline since it's heading matches one used for the outline.
var searchHeading = DocumentApp.ParagraphHeading.HEADING4;
var paragraphs = getParagraphs();
return paragraphs.filter(function(paragraph) {
return (paragraph.header == searchHeading);
});
function getParagraphs() {
var paragraphs = DocumentApp.getActiveDocument().getBody().getParagraphs();
return paragraphs.map(function(p, index) {
return {
paragraph: p.getText(),
header: p.getHeading(),
id: index
};
});
}
Is there anyway to get the contents of the outline without parsing each paragraph and filtering those whose header matches a specific heading, ideally from an API call?
EDIT: Added getParagraphs()

HTML script to convert a text string(s) into a specified hyperlink

My basic requirement is to convert given text anchors into hyperlinks, using HTML or any browser side script. We use Windows10/IE/Edge, FYI.
Example : Given text
ABC
CDE
EFG
Required Output:
www.xyz.com/test/ABC
www.xyz.com/test/CDE
www.xyz.com/test/EFG
I have found a bash to hyperlink query here. My requirement is similar but need a browser based script
How to create html links based on a text document of inputs
Put text in <textarea> and read it's content using js/jQuery.
Create links dynamically in memory and later use where needed (I simply append to DOM)
var urlBase = 'https://example.com';
$(document).ready(function () {
$('#input').change(function () {
var lines = $(this).val().split("\n"); // Split textarea content by new line
var links = $('#links'); // Links container
links.html(''); // Empty container content
$.each(lines, function (i, line) {
links.append($('<a>', {href: urlBase + '/' + line, text: line})); // Append new link element
})
})
})
#links a {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea id="input"></textarea>
<div id="links"></div>
Get the value of the text input, split it at the new lines and create a string of the a's with teh desired hrefs. I am using a ul to display the results.
Note the advantage of creating a string that is then added to the DOM is that it affects the DOM only once - rather than on each loop of the append option. A small matter for when there is a small list, but if there are millions of lines to be appended - the constant DOM maniulation may cause issues if you use .append().
function convertText(){
var links= document.getElementById('textInput').value;
var linksArr = links.split('\n');
var linkStr = '';
linksArr.forEach(function(link){
linkStr += '<li>'+link+'</li>';
})
document.getElementById('results').innerHTML = linkStr;
}
<textarea id="textInput"></textarea>
<hr/>
<button type="button" onclick="convertText()">Convert text to links</button>
<ul id="results"></ul>

How to shift last word of paragraph to next line without using any tag because content is coming dynamic

My content is coming dynamically, and content is in "p" tag i want last word of paragraph should be shifted to next line.Without adding "span" or any other tag. Is there any css ??
I think the best solution is use JavaScript. I wrote a pice of code with jQuery to accomplish your task.
https://codepen.io/Czeran/pen/yoXGVb
$(document).ready(function() {
var text = $('p').html();
var splitedText = text.split(" ");
splitedText.splice(splitedText.length-1, 0, '<br>');
var joinedText = splitedText.join(" ");
$('p').empty();
$('p').html(joinedText);
});
Unfortunatelly I don't know any CSS or HTML way to do it.

Copying Google Doc Header Formatting with GAS

I'm trying to copy a Header/Footer from the active Doc acting as a template to a newly created Doc. I'm able to get the text easy enough, but I'm not able to get the formatting, fonts or horizontal alignment.
My theory was that I could do something like
newDocHeader.setAttributes(activeDocHeader.getAttributes());
But, I still only see plain text that is left aligned. When inspecting the attributes object on the header i get the following:
({
FONT_SIZE:null,
ITALIC:null,
STRIKETHROUGH:null,
FOREGROUND_COLOR:null,
BOLD:null,
LINK_URL:null,
UNDERLINE:null,
FONT_FAMILY:null,
BACKGROUND_COLOR:null
})
I tried to loop through the Child objects of the Header and perform a similar setAttributes(getAttributes) on each child, but to no avail.
I also thought the copy() function on the Header/Footer object would be promising, but when I tried
newDocFooter = activeDocFooter.copy();
But, this produces a blank footer with no text or formatting.
Is there a good way to copy the formatting, font and horizontal alignment from one Header/Footer to another?
I'm entirely unfamiliar with DocumentApp, but this broadly worked for me:
/**
* Copies headers from one document to another.
* #param {string} source The source document URL.
* #param {string} target The target document URL.
*/
function copyHeader(source, target) {
// Used to map from child types to required "append<X>" method
var functionMap = {
PARAGRAPH: 'appendParagraph',
LIST_ITEM: 'appendListItem',
HORIZONTAL_RULE: 'appendHorizontalRule',
IMAGE: 'appendImage',
TABLE: 'appendTable'
};
var t = DocumentApp.openByUrl(target);
var s = DocumentApp.openByUrl(source);
var sourceHeader = s.getHeader();
var targetHeader = t.getHeader();
// If there is no header in the target doc, add one
if (!targetHeader) {
targetHeader = t.addHeader();
}
targetHeader.clear();
// Docs requires one child element, so one will remain even
// after clearing. Get a reference to it so it can be removed
var targetNumChild = targetHeader.getNumChildren();
if (targetNumChild === 1) {
var placeholderElement = targetHeader.getChild(0);
}
// Copy across each element to the target doc
var c = sourceHeader.getNumChildren();
for (var i = 0; i < c; i++) {
var element = sourceHeader.getChild(i).copy();
var method = functionMap[element.getType()];
targetHeader[method](element);
}
// Remove the saved element if required
if (targetHeader.getNumChildren() > 1 && placeholderElement) {
targetHeader.removeChild(placeholderElement);
}
}
I say broadly only because formatting such as bold, horizontal centering, horizontal rules etc all copied across fine, but bizarrely, lists seem to change from being numbered to being bulleted, so something was lost in translation.
It might need a little tweaking, and surely there is an easier way, but in the absence of anything else, this might be a start.
Source document:
Target document, note that the list type isn't quite right!:
Hope it helps.

Preserve highlight when copy from Ace Editor

I am using Ace Editor in my web app. Wonder if it's possible to copy the text inside Ace Editor to clipboard with highlight. With default configurations, if I copy the selected text in Ace Editor to clipboard, it seems that only text content is copied with no html styles.
Thanks a lot for your help!
Unfortunately there is no api for this. you'll need to modify https://github.com/ajaxorg/ace/blob/v1.2.5/lib/ace/keyboard/textinput.js#L290 to also set text/html mime type to some html, rendered similar to https://github.com/ajaxorg/ace/blob/v1.2.5/lib/ace/layer/text.js#L442.
Also you'll need to include the css for the theme in the copied html
I know this is late, but this might be helpful for someone like me who stumbled upon this problem this late.
The basic idea is to get the text that is being copied and use Ace's tokenizer to generate HTML from it:
Add an event listener on the copy/cut event on the editor's container.
You can use clipboard object in event to get the data currently being copied: event.clipboardData?.getData('text/plain')
Rest steps are in code below
// get current tokenizer
const tokenizer = aceSession.getMode().getTokenizer();
// get `Text` object from ace , this will help in generating HTML
const Text = ace.require('ace/layer/text').Text;
// create a wrapper div, all your resultant HTML will come inside this
// also this will contain the basic HTML required to initialize the editor
const root = document.createElement('div');
// this is the main magic object
const rootText = new Text(root);
lines.forEach(line => {
// this converts your text to tokens
const tokens = tokenizer.getLineTokens(line, 'start') as any;
const leadingSpacesCount = (line.match(/^\s*/) || [])[0].length;
const lineGroupEl = document.createElement('div');
lineGroupEl.className = 'ace_line_group';
const lineEl = document.createElement('div');
lineEl.className = 'ace_line';
const spaceSpan = document.createElement('span');
if (tokens && tokens.tokens.length) {
//////////////////////////////////////////////////////
// Magic Happens here, this line is responsible for converting our tokens to HTML elements
rootText.$renderSimpleLine(lineEl, tokens.tokens);
//////////////////////////////////////////////////////
// Leading spaces do not get translated to HTML, add them separately
spaceSpan.innerHTML = ' '.repeat(leadingSpacesCount);
lineEl.insertBefore(spaceSpan, lineEl.children[0]);
} else {
spaceSpan.innerHTML = ' ';
lineEl.appendChild(spaceSpan);
}
lineGroupEl.appendChild(lineEl);
// `root` has a wrapper div, inside which our main div (with class "ace_layer ace_text-layer") lies
root.children[0].appendChild(lineGroupEl);
});
return root.innerHTML;
Now finally, in your eventlistener you can wrap this with any div to give your own custom color to it, and put it to clipboardData with text\html mime type:
event.clipboardData?.setData('text/html', htmlContent);