I need to create dynamic pdf with html templates in servicenow, but my problem is that these pdf must contain images and styles, and I have not been able to solve it.
try using the api of GeneralPDF of servicenow and get the template converted to pdf but only when it contains text. when I put images I get the following error:
This error appears to me when executing my code:
ExceptionConverter: java.io.IOException: The document has no pages.:
org.mozilla.javascript.JavaScriptException: ExceptionConverter:
java.io.IOException: The document has no pages.:
this is in a script include and is called from UI Action
my code to convert the html to pdf is the following:
create : function (sys_id){
var carta = new GlideRecord('x_solsa_casos_plant_doc');
carta.addQuery('sys_id','6f1e4ac8db29f300ab7c0f95ca96197a');
carta.query();
if(carta.next()){
var parsedBody = carta.body;
var gr = new GlideRecord('x_solsa_casos_x_solsa_casos');
gr.get('sys_id',sys_id);
var sampleString=parsedBody.toString();
var reg = new SNC.Regex('/\\$\\{(.*?)\\}/i');
var match = reg.match(sampleString);
var count =0;
var variables = [];
var values = [];
var tmpValue;
while (match != null)
{
variables.push(match.toString().substring(match.toString().indexOf(',')+1));
match = reg.match();
values.push(variables[count]);
gs.log("array values : " + values);
if(gr.getDisplayValue(values[count])==null || JSUtil.nil(gr.getDisplayValue(values[count])))
{
tmpValue='';
}else{
tmpValue=gr.getDisplayValue(values[count]);
gs.log("tmpValue :" +tmpValue);
}
parsedBody = parsedBody.replace('${'+variables[count]+'}', tmpValue);
count++;
gs.log("parsedBody : " + parsedBody);
}
this.createPDF(parsedBody,'x_solsa_casos_x_solsa_casos',sys_id,'carta.pdf');
}
},
createPDF : function(html, table, sys_id, filename) {
var pdfDoc = new GeneralPDF.Document(null, null, null, null, null, null);
this._document = new GeneralPDF(pdfDoc);
this._document.startHTMLParser();
this._document.addHTML(html);
this._document.stopHTMLParser();
this.saveAs(table, sys_id, filename);
},
saveAs : function (table, sys_id, filename){
var att = new GeneralPDF.Attachment();
att.setTableName(table);
att.setTableId(sys_id);
att.setName(filename);
att.setType('application/pdf');
att.setBody(this._document.get());
GeneralPDF.attach(att);
},
Looks like parsedBody is empty or doesn't always contain HTML. According to this answer, paseXHtml (which ServiceNow probably uses and should be in the complete stack trace) expects HTML tags, not just text:
https://stackoverflow.com/a/20902124/2157581
Related
I used the command to export the hard drive ID to drive C:
var command="wmic diskdrive get SerialNumber > C:/idhdd.txt";
app.system("cmd.exe /c\""+command+"" );
I get the text file
SerialNumber
2012062914345300
Is there a JavaScript statement to remove SerialNumber, I just want to get the ID in the text file and save it to the hard drive C.
Here's ready-to-use getDriveIDs() function that should work in any Adobe app and will return array of HDD ID strings for you. I hope this can be easily generalized for other scenarios with Windows scripting inside Adobe scripting ;-)
//----------------------------------------------------------------------//
// Detects IDs (serial numbers) of connected drives and returns them as array of strings.
var getDriveIDs = function() {
var idFile = File(Folder.temp + '/saved_hdd_serials.txt');
var scriptFile = File(Folder.temp + '/dump_hdd_serials.bat');
var scriptContent = 'wmic diskdrive get SerialNumber > ' + idFile.fsName + '\n';
var ids = []
withTempFile(scriptFile, scriptContent, function() {
scriptFile.execute();
$.writeln(idFile.length == 0); // wait for asynchronous script execution to finish
$.sleep(1);
withTempFile(idFile, undefined, function(file, lines) {
ids = lines.slice(1);
});
});
return ids;
};
//----------------------------------------------------------------------//
// utilities
var withTempFile = function(file, content, callback) {
if (undefined == content) { // read temp file
file.open('r');
content = [];
while (!file.eof)
content.push(file.readln());
} else { // write temp file
file.open('w');
file.write(content);
content = undefined;
}
file.close();
callback(file, content);
file.remove();
}
//----------------------------------------------------------------------//
// main: demo
var ids = getDriveIDs();
alert('Drive IDs:\n\t' + ids.join('\n\t'));
These are the functions which load file and write file. I used JavaScript and XPCOM for these operations. You can use these functions in iMacros JavaScript file.
Edit: These functions work best in iMacros version 8.9.7 and corresponding Firefox. The later versions of iMacros addon don't support JavaScript. Also it's best to use Firefox 47 with disabled updates. And you can use latest Pale Moon browser with addon 8.9.7 . If there is a content in file the WriteFile function simply adds data in new line.
//This function load content of the file from a location
//Example: LoadFile("C:\\test\\test.txt")
function LoadFile(path) {
try {
Components.utils.import("resource://gre/modules/FileUtils.jsm");
var file = new FileUtils.File(path);
file.initWithPath(path);
var charset = 'UTF8';
var fileStream = Components.classes['#mozilla.org/network/file-input-stream;1']
.createInstance(Components.interfaces.nsIFileInputStream);
fileStream.init(file, 1, 0, false);
var converterStream = Components.classes['#mozilla.org/intl/converter-input-stream;1']
.createInstance(Components.interfaces.nsIConverterInputStream);
converterStream.init(fileStream, charset, fileStream.available(),
converterStream.DEFAULT_REPLACEMENT_CHARACTER);
var out = {};
converterStream.readString(fileStream.available(), out);
var fileContents = out.value;
converterStream.close();
fileStream.close();
return fileContents;
} catch (e) {
alert("Error " + e + "\nPath" + path)
}
}
//This function writes string into a file
function WriteFile(path, string) {
try {
//import FileUtils.jsm
Components.utils.import("resource://gre/modules/FileUtils.jsm");
//declare file
var file = new FileUtils.File(path);
//declare file path
file.initWithPath(path);
//if it exists move on if not create it
if (!file.exists()) {
file.create(file.NORMAL_FILE_TYPE, 0666);
}
var charset = 'UTF8';
var fileStream = Components.classes['#mozilla.org/network/file-output-stream;1']
.createInstance(Components.interfaces.nsIFileOutputStream);
fileStream.init(file, 18, 0x200, false);
var converterStream = Components
.classes['#mozilla.org/intl/converter-output-stream;1']
.createInstance(Components.interfaces.nsIConverterOutputStream);
converterStream.init(fileStream, charset, string.length,
Components.interfaces.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
//write file to location
converterStream.writeString(string + "\r\n");
converterStream.close();
fileStream.close();
} catch (e) {
alert("Error " + e + "\nPath" + path)
}
}
//this function removes file from location
function RemoveFile(path) {
var file = Components.classes["#mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
//file.initWithPath("c:\\batstarter6_ff.cmd");
file.initWithPath(path);
/*
var file = IO.newFile(path, name);
*/
file.remove(false);
}
// Download a file form a url.
function saveFile(url) {
try {
// Get file name from url.
var filename = url.substring(url.lastIndexOf("/") + 1).split("?")[0];
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.onload = function () {
var a = document.createElement('a');
a.href = window.URL.createObjectURL(xhr.response); // xhr.response is a blob
a.download = filename; // Set the file name.
a.style.display = 'none';
document.body.appendChild(a);
a.click();
delete a;
};
xhr.open('GET', url);
xhr.send();
} catch (e) {
alert("Error " + e)
}
}
/*
This function runs file from given path
*/
function RunFile(path) {
var file = Components.classes["#mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
//file.initWithPath("c:\\batstarter6_ff.cmd");
file.initWithPath(path);
file.launch();
}
//this function downloads a file from url
function downloadFile(httpLoc, path) {
try {
//new obj_URI object
var obj_URI = Components.classes["#mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService).newURI(httpLoc, null, null);
//new file object
var obj_TargetFile = Components.classes["#mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
//set file with path
obj_TargetFile.initWithPath(path);
//if file doesn't exist, create
if (!obj_TargetFile.exists()) {
obj_TargetFile.create(0x00, 0644);
}
//new persitence object
var obj_Persist = Components.classes["#mozilla.org/embedding/browser/nsWebBrowserPersist;1"].createInstance(Components.interfaces.nsIWebBrowserPersist);
// with persist flags if desired
const nsIWBP = Components.interfaces.nsIWebBrowserPersist;
const flags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
obj_Persist.persistFlags = flags | nsIWBP.PERSIST_FLAGS_FROM_CACHE;
/*
var privacyContext = sourceWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIWebNavigation)
.QueryInterface(Components.interfaces.nsILoadContext);*/
//save file to target
obj_Persist.saveURI(obj_URI, null, null, null, null, obj_TargetFile, null);
} catch (e) {
alert(e);
}
}
//This function prompts user to select file from folder and use it
function PickFile(title) {
const nsIFilePicker = Components.interfaces.nsIFilePicker;
var fp = Components.classes["#mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
fp.init(window, title, nsIFilePicker.modeOpen);
fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterText);
var rv = fp.show();
if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
var file = fp.file;
// Get the path as string. Note that you usually won't
// need to work with the string paths.
var path = fp.file.path;
// work with returned nsILocalFile...
return path;
}
}
//This function prompts user to select folder from folder and use it
function PickFolder(title) {
var picker = Components.classes["#mozilla.org/filepicker;1"].createInstance(Components.interfaces.nsIFilePicker);
picker.appendFilters(Components.interfaces.nsIFilePicker.filterAll);
//folder
picker.init(window, title, Components.interfaces.nsIFilePicker.modeGetFolder);
//or file
// picker.init (window, "Choice file", Components.interfaces.nsIFilePicker.modeOpen);
if (picker.show() == Components.interfaces.nsIFilePicker.returnOK) {
return picker.file.path;
} else {
return false;
}
}
//this function offers a set of options to select from
//items is an array of options
//title is the dialog title
//qustion is a question asked to user.
function Select(items, title, question) {
var prompts = Components.classes["#mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
//var items = ["Articles", "Modules", "Both"]; // list items
var selected = {};
var result = prompts.select(null, title, question, items.length,
items, selected);
// result is true if OK was pressed, false if cancel. selected is the index of the item array
// that was selected. Get the item using items[selected.value].
var selected = items[selected.value];
return selected;
}
Edit: I am also adding iMacros version 8.9.7 addon to download because version 10 doesn't support JavaScript http://download.imacros.net/imacros_for_firefox-8.9.7-fx.xpi
Edit1: I added some more useful functions for iMacros.
I'm trying to create a basic script on a 12-hour timer trigger that loops through each of my Google Calendars by their ICAL URL, and downloads the ICAL for a folder on my Google Drive (for backup purposes). It throws this error
"No item with the given ID could be found, or you do not have permission to access it. (line 23, file "Code")" (Line #23 is var folder... )
Running the script does download and save the ICAL file on the first run through the loop (and if I manually pass in each unique ICAL URL one at a time), but the error then terminates the loop. Seeing as how I've authorized access already and am the owner of everything here, I'm not sure what else I need.
var calendarsToSave = [
"https://calendar.google.com/calendar/ical/inXXXXXXX.com/privateXXXX/basic.ics",
"https://calendar.google.com/calendar/ical/XXXXX.com_XXXXXXup.calendar.google.com/private-XXXXXXX/basic.ics"
];
var folder = '123xxxxxxxxv789'; // my gdrive folder
function downloadFile(calendarURL,folder) {
var fileName = "";
var fileSize = 0;
for (var i = 0; i < calendarsToSave.length; i++) {
var calendarURL = calendarsToSave[i];
var response = UrlFetchApp.fetch(calendarURL, {muteHttpExceptions: true});
var rc = response.getResponseCode();
if (rc == 200) {
var fileBlob = response.getBlob()
var folder = DriveApp.getFolderById(folder); // << returns a permissions error thus terminating the for loop
var file = folder.createFile(fileBlob);
fileName = file.getName();
fileSize = file.getSize();
}
var fileInfo = { "rc":rc, "fileName":fileName, "fileSize":fileSize };
return fileInfo;
} // end for loop
}
Updated: You are also re-initializing a variable that already exists from the parameters and as a global variable so we can remove the parameter if you want to keep the global variable.
We can also move the place where you get the Google Folder object. It stays the same every time so we don't need to retrieve it again.
var calendarsToSave = [
"https://calendar.google.com/calendar/ical/inXXXXXXX.com/privateXXXX/basic.ics",
"https://calendar.google.com/calendar/ical/XXXXX.com_XXXXXXup.calendar.google.com/private-XXXXXXX/basic.ics"
];
var folder = '123xxxxxxxxv789'; // my gdrive folder
function downloadFile(calendarURL) {
var fileName = "";
var fileSize = 0;
var gfolder = DriveApp.getFolderById(folder);
for (var i = 0; i < calendarsToSave.length; i++) {
var calendarURL = calendarsToSave[i];
var response = UrlFetchApp.fetch(calendarURL, {muteHttpExceptions: true});
var rc = response.getResponseCode();
if (rc == 200) {
var fileBlob = response.getBlob()
var file = gfolder.createFile(fileBlob);
fileName = file.getName();
fileSize = file.getSize();
}
var fileInfo = { "rc":rc, "fileName":fileName, "fileSize":fileSize };
return fileInfo;
} // end for loop
}
Let see where that gets us.
Your "folder" variable is outside the function, causing the data to be inaccessible to the "downloadFile" function.
Google apps coding seems to require variables to be in a function to be defined. I would recommend moving both "calendarsToSave" and "folder" to the inside of "downloadFile"
Here is an example that will return your error:
var folder = '1HSFBPfPIsXWvFEb_AalFYalkPwrOAyxD';
function myFunction() {
var folder = DriveApp.getFolderById(folder);
var name = folder.getName();
Logger.log(name);
}
And here is one that will return the file name:
function myFunction() {
var folder = '1HSFBPfPIsXWvFEb_AalFYalkPwrOAyxD';
var folder = DriveApp.getFolderById(folder);
var name = folder.getName();
Logger.log(name);
}
I'm playing with getting the revision history of a document through Google Apps Script and I'm looking for some advice on how to programmatically access the content of the revision.
Using the Drive API, I can access an array of revisions on the document and iterate based on user. The returned object does not include the content of the revision, just an ID. But, you can get a download URL for various content types (pdf, plaintext, etc).
I'd like to retrieve a download URL using UrlFetchApp and get that content to append to a document. The problem is that the fetch app returns the entire document markup (HTML and CSS) and I'd only like the content of the file.
Script
function revisionHistoryLite() {
var doc = DocumentApp.getActiveDocument();
var eds = doc.getEditors();
var body = doc.getBody();
var revs = Drive.Revisions.list(doc.getId())
var editsList = [];
for(var i=0; i<revs.items.length; i++) {
var revision = revs.items[i];
editsList.push([revision.id, revision.kind, revision.modifiedDate, revision.lastModifyingUser.emailAddress]);
if(revision.lastModifyingUser.emailAddress == "bbennett#elkhart.k12.in.us") {
var revUrl = Drive.Revisions.get(doc.getId(), revision.id).exportLinks["text/plain"];
// revUrl returns https://docs.google.com/feeds/download/documents/export/Export?id=docIdString&revision=1&exportFormat=txt
var revString = UrlFetchApp.fetch(revUrl, { contentType: "text/plain", }).getContentText();
Logger.log(revString); // Contains full HTTP markup
// Append the body contents to a temporary document for further processing
// var tempDoc = DocumentApp.create("Temp").getBody().appendParagraph(revString);
}
}
}
When it downloads files from exportLinks using UrlFetchApp.fetch(), the authorization is required. So please modify your script as follows.
From :
var revUrl = Drive.Revisions.get(doc.getId(), revision.id).exportLinks["text/plain"];
var revString = UrlFetchApp.fetch(revUrl, { contentType: "text/plain", }).getContentText();
To :
var revUrl = Drive.Revisions.get(doc.getId(), revision.id).exportLinks["text/plain"] + "&access_token=" + ScriptApp.getOAuthToken();
var revString = UrlFetchApp.fetch(revUrl).getContentText();
By this, you can download text data from the revision data.
Edit :
function revisionHistoryLite() {
var doc = DocumentApp.getActiveDocument();
var eds = doc.getEditors();
var body = doc.getBody();
var revs = Drive.Revisions.list(doc.getId())
var editsList = [];
for(var i=0; i<revs.items.length; i++) {
var revision = revs.items[i];
editsList.push([revision.id, revision.kind, revision.modifiedDate, revision.lastModifyingUser.emailAddress]);
if(revision.lastModifyingUser.emailAddress == "### mail address ###") {
var revUrl = Drive.Revisions.get(doc.getId(), revision.id).exportLinks["text/plain"] + "&access_token=" + ScriptApp.getOAuthToken();
var revString = UrlFetchApp.fetch(revUrl).getContentText();
Logger.log(revString); // Contains full HTTP markup
}
}
}
Updated: February 7, 2020
From January, 2020, the access token cannot be used with the query parameter like access_token=###. Ref So please use the access token to the request header instead of the query parameter. It's as follows.
var res = UrlFetchApp.fetch(url, {headers: {Authorization: "Bearer " + ScriptApp.getOAuthToken()}});
I'm using this code to get a blob passed to a function:
function submit(e){
var arrayBlob = e.parameter.arrayBlob;
Logger.log("arrayBlob #2 = " + arrayBlob.getDataAsString());
This is the error I get:
Execution failed: TypeError: Can not find getDataAsString function in
the Blob object.'arrayBlob'
How do I get the string value of this blob?
Here is my code:
function showList(folderID) {
var folder = DocsList.getFolderById(folderID);
var files = folder.getFiles();
var arrayList = [];
for (var file in files) {
file = files[file];
var thesesName = file.getName();
var thesesId = file.getId();
var thesesDoc = DocumentApp.openById(thesesId);
for (var child = 0; child < thesesDoc.getNumChildren(); child++){
var thesesFirstParagraph = thesesDoc.getChild(child);
var thesesType = thesesFirstParagraph.getText();
if (thesesType != ''){
var newArray = [thesesName, thesesType, thesesId];
arrayList.push(newArray);
break;
}
}
}
arrayList.sort();
var result = userProperties.getProperty('savedArray');
arrayList = JSON.stringify(arrayList);
var arrayBlob = Utilities.newBlob(arrayList);
Logger.log("arrayBlob #1 = " + arrayBlob.getDataAsString()); // Here it`s OK
var mydoc = SpreadsheetApp.getActiveSpreadsheet();
var app = UiApp.createApplication().setWidth(550).setHeight(450);
var panel = app.createVerticalPanel()
.setId('panel');
panel.add(app.createHidden('arrayBlob', arrayBlob));
var label = app.createLabel("Selecione os itens desejados").setStyleAttribute("fontSize", 18);
app.add(label);
arrayList = JSON.parse(arrayList);
panel.add(app.createHidden('checkbox_total', arrayList.length));
for(var i = 0; i < arrayList.length; i++){
var checkbox = app.createCheckBox().setName('checkbox_isChecked_'+i).setText(arrayList[i][0]);
Logger.log("arrayList[i][0] = " + arrayList[i][0]);
Logger.log("arrayList[i] ====> " + arrayList[i]);
panel.add(checkbox);
}
var handler = app.createServerHandler('submit').addCallbackElement(panel);
panel.add(app.createButton('Submit', handler));
var scroll = app.createScrollPanel().setPixelSize(500, 400);
scroll.add(panel);
app.add(scroll);
mydoc.show(app);
}
function submit(e){
var arrayBlob = e.parameter.arrayBlob;
Logger.log("arrayBlob #2 = " + arrayBlob.getDataAsString());
// Continues...
}
I'd like the solution worked with more than one user simultaneous using the script.
Update:
Add a global variable OUTSIDE of any function:
var arrayBlob = Utilities.newBlob("dummy data");
function showList(folderID) {
Code here ....
};
Check that the code has access to the blob:
function submit(e){
Logger.log("arrayBlob.getDataAsString(): " + arrayBlob.getDataAsString());
//More Code . . .
}
This solution eliminates the need of embedding a hidden element in the dialog box with a value of the blob.
You won't need this line:
panel.add(app.createHidden('arrayBlob', arrayBlob));
There are other changes I'd make to the code, but I simply want to show the main issue.
Old Info:
In the function showList(), the method getDataAsString() works on the blob named arrayBlob.
Logger.log("arrayBlob #1 = " + arrayBlob.getDataAsString()); // Here it`s OK
In the function, submit(), the same method does not work.
var arrayBlob = e.parameter.arrayBlob;
In the function showList(), the code is assigning a newBlob to the variable arrayBlob. So arrayBlob is available to have the getDataAsString() method used on it.
var arrayBlob = Utilities.newBlob(arrayList);
In the function, submit(), you are trying to pass the arrayBlob blob variable into the submit() function, and reference it with e.parameter.
If you put a Logger.log() statement in the submit() function.
function submit(e){
Logger.log('e: ' + e);
Logger.log('e.parameter` + e.parameter);
var arrayBlob = e.parameter.arrayBlob;
Those Logger.log statements should show something in them. If there is nothing in e.parameter, then there is nothing for the .getDataAsString() to work on.
It looks like you are putting the arrayBlob into a hidden panel.
panel.add(app.createHidden('arrayBlob', arrayBlob));
But when the object is getting passed to the submit(e) function, the arrayBlob might not be getting put into that object.
So, what I'm saying is, the:
Logger.log("arrayBlob #2 = " + arrayBlob.getDataAsString());
Line may be perfectly good, but there is no arrayBlob there to work on. This hasn't fixed your problem, but do you think I'm understanding part of what is going on?
I'm not sure why you are using Blob's here at all, you could simply work with JSON instead.
However, if you have a reason to use Blobs, you can pass the JSON data through your form and create the Blob in your handler, as the modified code below does:
function showList(folderID) {
var folder = DocsList.getFolderById(folderID);
var files = folder.getFiles();
var arrayList = [];
for (var file in files) {
file = files[file];
var thesesName = file.getName();
var thesesId = file.getId();
var thesesDoc = DocumentApp.openById(thesesId);
for (var child = 0; child < thesesDoc.getNumChildren(); child++){
var thesesFirstParagraph = thesesDoc.getChild(child);
var thesesType = thesesFirstParagraph.getText();
if (thesesType != ''){
var newArray = [thesesName, thesesType, thesesId];
arrayList.push(newArray);
break;
}
}
}
arrayList.sort();
var result = UserProperties.getProperty('savedArray');
//get JSON data pass through form.
var arrayBlob = JSON.stringify(arrayList);
var mydoc = SpreadsheetApp.getActiveSpreadsheet();
var app = UiApp.createApplication().setWidth(550).setHeight(450);
var panel = app.createVerticalPanel()
.setId('panel');
//include JSON Data in the form.
panel.add(app.createHidden('arrayBlob', arrayBlob));
var label = app.createLabel("Selecione os itens desejados").setStyleAttribute("fontSize", 18);
app.add(label);
panel.add(app.createHidden('checkbox_total', arrayList.length));
for(var i = 0; i < arrayList.length; i++){
var checkbox = app.createCheckBox().setName('checkbox_isChecked_'+i).setText(arrayList[i][0]);
Logger.log("arrayList[i][0] = " + arrayList[i][0]);
Logger.log("arrayList[i] ====> " + arrayList[i]);
panel.add(checkbox);
}
var handler = app.createServerHandler('submit').addCallbackElement(panel);
panel.add(app.createButton('Submit', handler));
var scroll = app.createScrollPanel().setPixelSize(500, 400);
scroll.add(panel);
app.add(scroll);
mydoc.show(app);
}
function submit(e){
var arrayBlob = Utilities.newBlob(e.parameter.arrayBlob);
Logger.log("arrayBlob #2 = " + arrayBlob.getDataAsString());
// Continues...
}
In the method you were using originally, the Blob itself was never included in the form, you were simply passing the string "Blob" around.
This is because the function createHidden(name, value); expects two strings as parameters, so it calls ".toString()" on the arrayBlob object, which returns the string "Blob".