How to use Node.js to create modified versions of html documents? - node.js

I am trying to do this:
Read html document "myDocument.html" with Node
Insert contents of another html document named "foo.html" immediately after the open body tag of myDocument.html.
Insert contents of yet another html document named "bar.html" immediately before the close body tag of myDocument.html.
Save the modified version of "myDocument.html".
To do the above, I would need to search the DOM with Node to find the open and closing body tags.
How can this be done?

Very simply, you can use the native Filesystem module that comes with Node.JS. (var fs = require("fs")). This allows you to read and convert the HTML to a string, perform string replace functions, and finally save the file again by rewriting it.
The advantage is that this solution is completely native, and requires no external libraries. It is also completely faithful to the original HTML file.
//Starts reading the file and converts to string.
fs.readFile('myDocument.html', function (err, myDocData) {
fs.readFile('foo.html', function (err, fooData) { //reads foo file
myDocData.replace(/\<body\>/, "<body>" + fooData); //adds foo file to HTML
fs.readFile('bar.html', function (err, barData) { //reads bar file
myDocData.replace(/\<\/body\>/, barData + "</body>"); //adds bar file to HTML
fs.writeFile('myDocumentNew.html', myDocData, function (err) {}); //writes new file.
});
});
});

In a simple but not accurate way, you can do this:
str = str.replace(/(<body.*?>)/i, "$1"+read('foo.html'));
str = str.replace(/(<\/body>)/i, read('bar.html')+'$1');
It will not work if the myDocument content contains multiple "<body ..' or '</body>', e.g. in javascript, and also the foo.html and bar.html can not contains '$1' or '$2'...
If you can edit the content of myDocument, then you can leave some "placeholder" there(as html comments), like
<!--foo.html-->
Then, it's easy, just replace this "placeholder" .

Use the cheerio library, which has a simplified jQuery-ish API.
var cheerio = require('cheerio');
var dom = cheerio(myDocumentHTMLString);
dom('body').prepend(fooHTMLString);
dom('body').append(barHTMLString);
var finalHTML = dom.html();
And just to be clear since the legions of pro-regex individuals are already appearing in droves, yes you need a real parser. No you cannot use a regular expression. Read Stackoverflow lead developer Jeff Atwood's post on parsing HTML the Cthulhu way.

Related

After passing object to handlebars, how can I access that object in script tag?

I first get the data from sql then pass it into handlebars.
Inside the tag in .handlebars/using view.js, I want to access doctors, but i keep getting[object][object]. I tried json.stringifying it before but still no luck. What is the best way to do this?
umd.matchDocs(val2, function(data) {
console.log(data);
var renderDocs = {
doctors: data
}
res.render("dashboard", renderDocs);
});
After passing object to handlebars, how can I access that object in script tag?
No, not by default. But you can make the data available manually if you want.
Data you pass to handlebars rendering operation is available during the rendering operation only. If you want to be able to access some of that data later in client-side <script> tags, then you can "render" Javascript variables into the <script> tags that contain the desired data.
Remember when rendering data into Javascript variables, you need to render the actual Javascript text (converting to JSON will often create the text for you).
In your specific example, you could do something like this in your rendering code:
umd.matchDocs(val2, function(data) {
console.log(data);
var renderDocs = {
doctors: JSON.stringify(data)
}
res.render("dashboard", renderDocs);
});
And, then in the template:
<script>
var doctors = {{{doctors}}};
</script>
Then, this array of doctors would be available to the Javascript in your page.
In case you haven't seen the triple braces like shown above, that's to tell handlebars to skip any HTML escaping in the data (because this isn't HTML).

Get Add my own content to gmail compose box using inboxsdk

I am developing a chrome addon and I want to append my own content at the end to mail content using InboxSDK. I am using the following code, but it's appending to my cursor position in Gmail Compose Box.
var cv = event.composeView;
cv.insertTextIntoBodyAtCursor('My Content');
also, I want to append content before sending mail. So, How I can achieve it using InboxSDK.
Thanks in advance
You could just get the whole messages body, modify and set the modified version as the new messages body. There is two ways to approach it.
1. getBodyElement()
Get the whole messages HTML and append whatever you want to append and set this as the new body HTML.
var $content = $(composeView.getBodyElement());
var $myContent = $('<div class="my_content">Hello World!</div>');
$content.append($myContent );
composeView.setBodyHTML($content.html());
2. getHTMLContent()
It would also work with the HTML string of the messages body.
var contentString = composeView.getHTMLContent();
var myContent = '<div class="my_content">Hello World!</div>';
contentString += myContent;
composeView.setBodyHTML(contentString);

Passing JavaScript variable into snippet

I'm working on a search form for my ModX application that is consisted of a chunk and a snippet. What I'm trying to achieve is to pass what was entered into the search box into a javascript variable and then pass it to my snippet, however, the snippet receives the literal text, and not the value that I enter into the parameter when I call it.
I don't know if what I'm attempting is possible in ModX or if I need to take a different approach, but I would be hugely thankful for anyone who can provide any insight.
Chunk:
<script>
$('.search-btn').click(function() {
var search = $('.search-entry').val();
[[showSearchResults? &q=`search`]]
});
</script>
Snippet:
<?php
$search = $modx->getOption('q', $scriptProperties);
echo $search; // this always prints "search"
?>
I doubt that this code makes sense:
<script>
$('.search-btn').click(function() {
var search = $('.search-entry').val();
[[showSearchResults? &q=`search`]]
});
</script>
The snippet call returns the result of snippet's execution with param q always equal to the string 'search' in your case and finally on your page you will have something like this:
<script>
$('.search-btn').click(function() {
var search = $('.search-entry').val();
'search' // assuming your snippet just returns what has been passed to it.
});
</script>
In order to accomplish your task you can use a simple trick. Call your snippet like this:
[[!yourSnippet? &yourVar=`[[!#POST.yourVar]]` ]] // or GET
Lets say this snippet call is located on a page accessible via url /test/ on your server. So, now you just have to send the parameters you collected from your search form using AJAX to the /test/ page where your snippet is:
var yourVar = $('.search-entry').val();
$.ajax({
type: "POST",
url: "/test/",
data: {yourVar: yourVar},
success: success,
dataType: "html"
});
Hope it helps :)
PS If you want to search Resource content and TV content, I can highly recommend an extra called SimpleSearch.

Including a pug filter which name is in a variable

In the Pug Language reference there is an easy example on how to use the markdown filter on a .md file and include it.
include:markdown-it myfile.md
However, I can't get to work reading from a file which name is in a variable. I expected this syntax to work:
include:markdown-it ${article}
Being var article = 'myfile.md'. But the code crash saying it can't find file '${article}' in my views folder.
What is the correct way of doing it? Or is it just imposible?
Regards.
EDIT: As suggested here, it's not possible to do what I want. The solution I found was rendering the markdown file first,
function loadmd(md) {
var fs = require('fs');
var markdown = require('markdown-it')();
var mdfile = fs.readFileSync(__dirname + '/mds/' + md + '.md');
return markdown.render(mdfile.toString());
}
and then pass it to the pug renderer as a variable:
res.render('art', {md: loadmd(req.params.article)}, function renderDone(err, html) { ... }
Template:
doctype html
html(lang=es)
head
include htmlhead
title !{pagetitle}
body
include pageheader
include pagemenu
section
article !{md}
footer
include pagefooter

NetSuite SuiteScript to modify file in the file cabinet

We have files within the NetSuite file cabient which need to be updated (the url field has changed). I found the noted article on this site but there is no code example to perform the requested. It indicates to use the nlapiLoadFile and nlapiSubmitFile calls; would anyone be able to assist with a code example?
Link:
Can Netsuite Suitescript modify a file in the file cabinet?
Ya, it seems a bit odd. The only way I found is:
Load The File
Create a file handle with:
Set the file name to one that you intended.
Set the content to intended one
Set the folder and submit.
I have attached a code snippet
var file = nlapiLoadFile(file_id);
var content = file.getValue();
content = '...put your content...';
file = nlapiCreateFile(file.getName(), 'FILE TYPE', content);
file.setFolder(required_folder_id);
nlapiSubmitFile(file);
Hope this helps.
There is no special API function to edit an existing file, you could take the details of the existing file and create a new file with the same details but changing the data field only and deleting the old file.
var start = function(request, response)
{
var fileId = "107524";//get the existing file id
var file = nlapiLoadFile(fileId);
var data = file.getValue();
var name = file.getName();
var folderId = file.getFolder();
var fileType = file.getType();
nlapiDeleteFile(fileId);//delete the older file
data += ",this is the appended data";//change the data
var newFile = nlapiCreateFile(name, fileType, data);//create a new file with the same details
newFile.setFolder(folderId);
nlapiSubmitFile(newFile);//submit it
}
Do you mean file instead of field? If you use nlapiLoadFile(/path/file), you can then use getURL() to provide a link to that file.
NetSuite does not have a edit file kind of an API. You will have to load the original file, modify the contents as per your needs and then submit that data by creating a new file with same file name and inside the same folder. This simply overrides the existing file.
Here's the code sample:
var original = nlapiLoadFile(FILE_ID_OR_FILE_PATH_IN_FILE_CABINET);
var originalContent = original.getValue(); //Return the value (base64 encoded for binary types) of the file
var updated = nlapiCreateFile(original.getName(), FILE_TYPE, UPDATED_FILE_CONTENTS);
updated.setFolder(original.getFolder());
nlapiSubmitFile(updated);
I experienced a similar error when trying to modify a file using SuiteScript in Netsuite at the server-side. I was using the way explained in the documentation, where they say copying a new file through file.copy() with conflictResolution: file.ConflictResolution.OVERWRITE. However, that way didn't work for me as it neither created the file nor overwrote it. Finally, I use the following form to get it working:
...
let fileNew = file.create({
name: 'same_name_of_the_original_file',
fileType: file.Type.PLAINTEXT, // change it depending the file type you will create
contents: credentials.body,
});
fileNew.folder = folder_id;
let fileId = fileNew.save();
...
So, the key is to change the folder and saving it after the file is created. After that, saving the file would overwrite the original.

Resources