I am trying to do an XML import automatically from a startup script when a document is loaded. I am successful in getting most of the content to populate, but images are being ignored. Everything works, including images, when I do a manual 'Import XML' through the UI, or through a manual script.
Below is my manual script:
var myDocument = app.activeDocument;
var xmlFile = File('/c/Full/Path/To/data.xml');
myDocument.importXML(xmlFile);
But the goal is to do it on startup. Below is my startup script:
#targetengine "session"
app.addEventListener('afterOpen', function(myEvent) {
if (myEvent.target.constructor.name !== 'Document') {
return;
}
var myDocument = myEvent.target;
var xmlFile = File('/c/Full/Path/To/data.xml');
myDocument.importXML(xmlFile);
});
Below is the XML tag for the image:
<Image href="file:///C:/Full/Path/To/Image/02.png" />
I'm wondering if there is an issue with the 'afterOpen' event callback, and that's the reason why it works manually using the same method, but not in the startup script.
I was able to get around the issue by avoiding the event listener altogether.
main();
function main () {
// create a path for a file object
var curFile = File('/c/Path/To/file.indd');
var xmlFile = File('/c/Path/To/data.xml');
// close app if files don't exist
if (!curFile.exists || !xmlFile.exists) {
app.quit(SaveOptions.NO);
}
// open the file
var curDoc = app.open(curFile);
// import the xml
curDoc.importXML(xmlFile);
// create a new file object
var pdfFile = new File(curFile.parent + '/' + curFile.name + '.pdf');
// export to pdf
curDoc.exportFile(ExportFormat.PDF_TYPE, pdfFile);
// close app
app.quit(SaveOptions.NO);
}
Related
I'm writing a website using VueJS which allows (selected) users to add scripts that are automatically executed upon page load. Here's a sample text that a user might upload:
<script src="https://cdnjs.cloudflare.com/ajax/libs/howler/2.0.5/howler.js"></script>
<script>
var sound = new howler.Howl({
src: ['./sample.mp3']
)}.play();
</script>
This text is stored into a string after retrieving from API backend. The problem now is: I couldn't get it to execute however I try. Is there an option in VueJS that can automatically execute javascripts in strings?
As a reference, here's my code:
var temp_arr = utils.preprocess(vm.chapterInfo.Content)
vm.display = temp_arr[0]
vm.control_script = console.log(temp_arr[1])
// None of below worked
eval(vm.control_script)
document.getElementsByTagName("head")[0].appendChild(control_script)
The problem isn't a Vue one, but a JavaScript one.
I assume that you already understand the security implications of allowing users to run JavaScript; it's rarely a good idea. Sites like JSFiddle do it successfully, however it will take a lot of work and understanding to make it safe, so if you're not 100% sure with what you are doing, then as #WaldemarIce said, you shouldn't do it!
Right, with the warning out the way, you need to do a few things to get this to work:
1) Load the external scripts:
loadScripts() {
return new Promise(resolve => {
let scriptEl = document.createElement("script");
scriptEl.src = "https://cdnjs.cloudflare.com/ajax/libs/howler/2.0.5/howler.js";
scriptEl.type = "text/javascript";
// Attach script to head
document.getElementsByTagName("head")[0].appendChild(scriptEl);
// Wait for tag to load before promise is resolved
scriptEl.addEventListener('load',() => {
resolve();
});
});
}
Here I'm simply attaching the external script to the head of the document and attaching a load event, which resolves the Promise when loaded.
2) Now we have loaded the external script we can execute the remainder of the script. You will need to strip out the script tags, so you can do something like this:
executeScript() {
// remove script tags from string (this has been declared globally)
let script = string.replace(/<\/?script>/g,"")
eval(script)
}
Form the Vue perspective, you can then execute this inside the created hook:
created() {
this.loadScripts().then(() => {
this.executeScript();
});
},
I'll leave it to you to extract the external scripts you want to load from your user input, but here's a JSFiddle: https://jsfiddle.net/49dq563d/
I recently came across this problem and had to extend on the answer from #craig_h. The example below allows full embed code to be sent through as string (HTML elements as well as scripts and inline JS). This is using DOMParser.
<div ref="htmlDump"></div>
<script>
import Vue from "vue";
export default {
...
methods: {
cloneAttributes(element, sourceNode) {
let attr;
let attributes = Array.prototype.slice.call(sourceNode.attributes);
while(attr = attributes.pop()) {
element.setAttribute(attr.nodeName, attr.nodeValue);
}
}
},
mounted(){
if(this.embedString && this.embedString.length > 0)
{
//Parse the code given from the API into a new DOM so we can easily manipulate it
var parser = new DOMParser();
var htmlDoc = parser.parseFromString(this.embedString, 'text/html');
//Get the contents of the new DOM body and loop through.
//We want to add all HTML elements to the page and run / load all JS
var kids = [...htmlDoc.body.children];
let len = kids.length;
for (var i = 0; i < len; i++) {
var item = kids[i];
if(item.tagName == "SCRIPT")
{
//If we have a 'src' attribute then we're loading in a script
if(item.hasAttribute('src'))
{
//Create a new element within the current doc to trigger the script load
let scriptEl = document.createElement("script");
//Copy all attributes from the source element to the new one
this.cloneAttributes(scriptEl, item);
//Attach script to the DOM to trigger it to load
this.$refs.htmlDump.appendChild(scriptEl);
} else {
//if we don't have a 'src' attribute then we have some code to run
eval(item.innerText);
}
} else{
this.$refs.htmlDump.appendChild(item);
}
}
}
}
...
}
</script>
I have a web application. It runs in Google Chrome and is not required to work in any other browser.
I have PDF data which has been generated on the server and sent back to the client in an AJAX request.
I create a blob from the PDF data.
I use window.URL.createObjectURL to create a URL from the blob, which I then load into a window (my preview_window) which has previously been created to show the PDF.
To load the URL, I set preview_window.location.href.
I would like to call revokeObjectURL to avoid wasting more and more resources as new PDFs are generated and previewed in the window.
The problem is that calling it immediately after setting preview_window.location.href is too soon, and stops the PDF from being displayed. So I would like to call revokeObjectURL only once the URL has been loaded.
I have tried setting preview_window.onload to a callback for this purpose, but it never gets called.
I would like to know:
Is it possible to trigger a callback when the window has loaded the URL, as I am trying to do? How?
Is there another approach to ensure revokeObjectURL gets called in a timely manner?
If I cannot trigger revokeObjectURL when the window finishes loading the URL, I may revoke each URL immediately before generating a new one. But I would rather revoke the URL as soon as it is done loading, if possible.
I have prepared a html file which demonstrates the situation pretty well:
<html>
<head>
<title>Show PDF Demo</title>
<script>
var build_blob = function(mime_type, data) {
var buf = new ArrayBuffer(data.length);
var ia = new Uint8Array(buf);
for (var i = 0; i < data.length; i++) ia[i] = data.charCodeAt(i);
var blob = new Blob([ buf ], { type: mime_type });
return blob;
};
window.onload = function(e) {
document.getElementById('preview_button').onclick = function(e) {
// open the window in the onclick handler so we don't trigger popup blocking
var preview_window = window.open(null, 'preview_window');
// use setTimeout to simulate an asynchronous AJAX request
setTimeout(function(e) {
var pdf_data = atob(
"JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZwovT3V0bGluZXMgMiAwIFIKL1BhZ2Vz" +
"IDMgMCBSCj4+CmVuZG9iagoyIDAgb2JqCjw8IC9UeXBlIC9PdXRsaW5lcwovQ291bnQgMAo+Pgpl" +
"bmRvYmoKMyAwIG9iago8PCAvVHlwZSAvUGFnZXMKL0tpZHMgWzQgMCBSXQovQ291bnQgMQo+Pgpl" +
"bmRvYmoKNCAwIG9iago8PCAvVHlwZSAvUGFnZQovUGFyZW50IDMgMCBSCi9NZWRpYUJveCBbMCAw" +
"IDUwMCAyMDBdCi9Db250ZW50cyA1IDAgUgovUmVzb3VyY2VzIDw8IC9Qcm9jU2V0IDYgMCBSCi9G" +
"b250IDw8IC9GMSA3IDAgUiA+Pgo+Pgo+PgplbmRvYmoKNSAwIG9iago8PCAvTGVuZ3RoIDczID4+" +
"CnN0cmVhbQpCVAovRjEgMjQgVGYKMTAwIDEwMCBUZAooU01BTEwgVEVTVCBQREYgRklMRSkgVGoK" +
"RVQKZW5kc3RyZWFtCmVuZG9iago2IDAgb2JqClsvUERGIC9UZXh0XQplbmRvYmoKNyAwIG9iago8" +
"PCAvVHlwZSAvRm9udAovU3VidHlwZSAvVHlwZTEKL05hbWUgL0YxCi9CYXNlRm9udCAvSGVsdmV0" +
"aWNhCi9FbmNvZGluZyAvTWFjUm9tYW5FbmNvZGluZwo+PgplbmRvYmoKeHJlZgowIDgKMDAwMDAw" +
"MDAwMCA2NTUzNSBmCjAwMDAwMDAwMDkgMDAwMDAgbgowMDAwMDAwMDc0IDAwMDAwIG4KMDAwMDAw" +
"MDEyMCAwMDAwMCBuCjAwMDAwMDAxNzkgMDAwMDAgbgowMDAwMDAwMzY0IDAwMDAwIG4KMDAwMDAw" +
"MDQ2NiAwMDAwMCBuCjAwMDAwMDA0OTYgMDAwMDAgbgp0cmFpbGVyCjw8IC9TaXplIDgKL1Jvb3Qg" +
"MSAwIFIKPj4Kc3RhcnR4cmVmCjYyNQolJUVPRg=="
);
/*
Warning: for my Chrome (Version 44.0.2403.155 m), the in-built PDF viewer doesn't seem
to work with a blob when this html page is loaded from the local filesystem. I have only
got this to work when fetching this page via HTTP.
*/
var pdf_blob = build_blob('application/pdf', pdf_data);
var pdf_url = window.URL.createObjectURL(pdf_blob);
preview_window.onload = function(e) {
console.log("preview_window.onload called"); // never happens
window.URL.revokeObjectURL(pdf_url);
};
preview_window.location.href = pdf_url;
console.log("preview_window.location.href set");
}, 500);
};
};
</script>
</head>
<body>
<button id="preview_button">Show Preview</button>
</body>
</html>
Although my demo code above avoids it, I do have jQuery loaded for my application, so if that makes things easier I'm open to using it.
I did find this question in a search, but in that situation the main window ("window") is pointed to a new URL, and the OP never got a response when asking in comments whether it makes a difference if the window came from window.open.
As you found out, you can't set open()ed windows' onload event from the opener.
You will have to inject some script in the second page that will call its window.opener functions.
But since you are opening a pdf file, the browser will re-parse entirely your page and your injected code will vanish.
The solution, as you found out yourself in the comments, is to inject the blob's url in an iframe, and wait for this iframe's load event.
Here is how :
index.html
<script>
// The callback that our pop-up will call when loaded
function imDone(url){
window.URL.revokeObjectURL(url);
}
var build_blob = function(mime_type, data) {
var buf = new ArrayBuffer(data.length);
var ia = new Uint8Array(buf);
for (var i = 0; i < data.length; i++) ia[i] = data.charCodeAt(i);
var blob = new Blob([ buf ], { type: mime_type });
return blob;
};
var preview_window=null;
window.onload = function(e) {
document.getElementById('preview_button').onclick = function(e) {
if(preview_window===null || preview_window.closed){
// open the window in the onclick handler so we don't trigger popup blocking
preview_window = window.open('html2.html', 'preview_window');
}
// avoid reopening the window since it may cache our last blob
else preview_window.focus();
// use setTimeout to simulate an asynchronous AJAX request
setTimeout(function(e) {
var pdf_data = /* Your pdf data */
var pdf_blob = build_blob('application/pdf', pdf_data);
var pdf_url = window.URL.createObjectURL(pdf_blob);
// Simple loop if our target document is not ready yet
var loopLoad = function(url){
var doc = preview_window.document;
if(doc){
var iframe = doc.querySelector('iframe');
if(iframe)iframe.src = url;
else setTimeout(function(){loopLoad(url);},200);
}
else setTimeout(function(){loopLoad(url);},200)
};
loopLoad(pdf_url);
}, 0);
};
};
</script>
and the html2.html
<html>
<head>
<title>Iframe PDF Demo</title>
<style>
body, html, iframe{margin:0; border:0}
</style>
</head>
<body>
<iframe width="100%" height="100%"></iframe>
<script>
document.querySelector('iframe').onload = function(){
//first check that our src is set
if(this.src.indexOf('blob')===0)
// then call index.html's callback
window.opener.imDone(this.src);
}
</script>
</body>
</html>
Live Demo
I have the script partially working.
It saves all the open psd as jpg into into a separate directory and it close some of the open files not all.
The directory has five files. The script saves only three files,
What am I doing wrong?
#target photoshop
if (app.documents.length > 0) {
//flatten the active document
app.activeDocument.flatten();
//jpeg options
var myJPEGOptions = new JPEGSaveOptions();
myJPEGOptions.embedColorProfile = true;
myJPEGOptions.formatOptions = FormatOptions.STANDARDBASELINE;
myJPEGOptions.matte = MatteType.WHITE;
myJPEGOptions.quality = 12;
myJPEGOptions.scans = 3;
// get documents;
var docs = app.documents;
for (var m = 0; m < app.documents.length; m++) {
app.activeDocument = docs[m];
try {
//save file to folder
var myFile = new File(("~/Desktop/forum-test") + "/" + activeDocument.name);
app.activeDocument.saveAs(myFile, myJPEGOptions, true);
//close the document
activeDocument.close(SaveOptions.DONOTSAVECHANGES);
}
catch (e) {
alert ("Error the script did not execute");
}
}
}
The app.documents collection is dynamic so when you close a document this collection is changed accordingly.
Because you are closing your document inside a for loop where you compare an incrementing index against app.documents.length you are not processing all files (as app.documents.length decreases by one each time the loop is processed).
Try a while loop instead:
while (app.documents.length){
// Save and close the active document here.
}
I think this two lines are wrong:
//save file to folder
var myFile = new File(("~/Desktop/forum-test") + "/" + activeDocument.name);
and
//close the document
activeDocument.close(SaveOptions.DONOTSAVECHANGES);
Shouldn't you be using app.activeDocument instead of activeDocument? What is activeDocument anyway?
I need to load cross-domain JavaScript
files dynamically for bookmarklets in my site http://jsbookmarklets.com/
The solution should satisfy:
Fetch the path of current file
The domain of current web-page and JS file in execution are different
The solution should be cross-browser
Multiple scripts might be loaded at once asynchronously (that's why the related questions mentioned below are not a fit)
I want to get the file path of currently executing JavaScript code for dynamically loading few more resources (more CSS files and JS files like custom code and jQuery, jQuery UI and Ext JS libraries) which are stored in the same/relative folder as the JavaScript Bookmarklet.
The following approach does not fit my problem:
var scripts = document.getElementsByTagName("script");
var src = scripts[scripts.length-1].src;
alert("THIS IS: "+src);
Related questions which do not fit my problem:
Get the url of currently executing js file when dynamically loaded
Get script path
The current solution that I'm using, which works, but is very lengthy:
var fnFullFilePathToFileParentPath = function(JSFullFilePath){
var JSFileParentPath = '';
if(JSFullFilePath) {
JSFileParentPath = JSFullFilePath.substring(0,JSFullFilePath.lastIndexOf('/')+1);
} else {
JSFileParentPath = null;
}
return JSFileParentPath;
};
var fnExceptionToFullFilePath = function(e){
var JSFullFilePath = '';
if(e.fileName) { // firefox
JSFullFilePath = e.fileName;
} else if (e.stacktrace) { // opera
var tempStackTrace = e.stacktrace;
tempStackTrace = tempStackTrace.substr(tempStackTrace.indexOf('http'));
tempStackTrace = tempStackTrace.substr(0,tempStackTrace.indexOf('Dummy Exception'));
tempStackTrace = tempStackTrace.substr(0,tempStackTrace.lastIndexOf(':'));
JSFullFilePath = tempStackTrace;
} else if (e.stack) { // firefox, opera, chrome
(function(){
var str = e.stack;
var tempStr = str;
var strProtocolSeparator = '://';
var idxProtocolSeparator = tempStr.indexOf(strProtocolSeparator)+strProtocolSeparator.length;
var tempStr = tempStr.substr(idxProtocolSeparator);
if(tempStr.charAt(0)=='/') {
tempStr = tempStr.substr(1);
idxProtocolSeparator++;
}
var idxHostSeparator = tempStr.indexOf('/');
tempStr = tempStr.substr(tempStr.indexOf('/'));
var idxFileNameEndSeparator = tempStr.indexOf(':');
var finalStr = (str.substr(0,idxProtocolSeparator + idxHostSeparator + idxFileNameEndSeparator));
finalStr = finalStr.substr(finalStr.indexOf('http'));
JSFullFilePath = finalStr;
}());
} else { // internet explorer
JSFullFilePath = null;
}
return JSFullFilePath;
};
var fnExceptionToFileParentPath = function(e){
return fnFullFilePathToFileParentPath(fnExceptionToFullFilePath(e));
};
var fnGetJSFileParentPath = function() {
try {
throw new Error('Dummy Exception');
} catch (e) {
return fnExceptionToFileParentPath(e);
}
};
var JSFileParentPath = fnGetJSFileParentPath();
alert('File parent path: ' + JSFileParentPath);
var s = document.createElement('script');
s.setAttribute('src', 'code.js');
document.body.appendChild(s);
Can you not simply do this?
var myScriptDir = 'http://somesite.tld/path-to-stuff/';
var s = document.createElement('script');
s.setAttribute('src', myScriptDir + 'code.js');
document.body.appendChild(s);
// code inside http://somesite.tld/path-to-stuff/code.js will use myScriptDir to load futher resources from the same directory.
If you don't want to have code inside the script to be responsible for loading further resources you can use the onload attribute of the script tag, like s.onload=function(){...}. For cross browser compatibility you might first load jQuery and then use the getScript function. Relevant links are http://www.learningjquery.com/2009/04/better-stronger-safer-jquerify-bookmarklet and http://api.jquery.com/jQuery.getScript/
Some of the comments have already mentioned this, but I'll try to elaborate a bit more.
The simplest, most cross-browser, cross-domain way of figuring out the path of the current script is to hard-code the script's path into the script itself.
In general, you may be loading third-party script files, so this would not be possible. But in your case, all the script files are under your control. You're already adding code to load resources (CSS, JS, etc.), you might as well include the script path as well.
I'm trying to figure out how to get my XUL app to open an HTML file and load it into an editor element. However, documentation is sparse.
Right now, I have a content window like so:
<hbox id="main-frame" flex="1">
<tabbox id="workspace-tabbox" flex="1">
<tabs id="workspace-tabs"/>
<tabpanels id="workspace-tabpanels" flex="1" context="clipmenu"/>
</tabbox>
<splitter id="main-frame-splitter"/>
<iframe id="preview-frame" src="about:blank" flex="1"/>
</hbox>
With javascript I append a <tab> to the <tabs>, and a <tabpanel> to the <tabpanels>. I then create an <editor>, append it to the <tabpanel>, and make it editable.
Then, there is an Open button linked to this function:
function promptFile() {
var filepicker = Components.classes["#mozilla.org/filepicker;1"].createInstance(Components.interfaces.nsIFilePicker);
var file;
var choice;
var path = null;
filepicker.init(window, "Open", filepicker.modeOpen);
filepicker.appendFilters(filepicker.filterHTML);
choice = filepicker.show();
if (choice == filepicker.returnOK) {
file = filepicker.file.QueryInterface(Components.interfaces.nsIFile);
path = file.path;
}
return path;
}
From here, I don't know how to load it into the <editor>. I'm also not sure if I'm on the right path getting the 'path', or if I need to do something with the 'file' object, instead.
Any insight or help on this would be greatly appreciated.
An <editor> is essentially a frame - you simply need to load the correct URL into that frame. You can use nsIIOService.newFileURI to get that URL from an nsIFile instance:
Components.utils.import("resource://gre/modules/Services.jsm");
var uri = Services.io.newFileURI(file);
var editor = document.getElementById("editor");
var loadFlags = Components.interfaces.nsIWebNavigation.LOAD_FLAGS_NONE;
editor.webNavigation.loadURI(uri.spec, loadFlags, null, null, null);
editor.makeEditable("html", true);
For reference: nsIWebNavigation.loadURI