How to load HTML file into <editor> - javascript

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

Related

lightbox onclick change src then change back

I am using Materialize CSS and have the "Material Box" which is a lightbox plugin. I want all of the thumbnails to be the same size. When clicked I want the full photo to load.
I am using onclick to change the src. How do I change it back to the thumbnail when the large photo closes (either with a click or the escape key)?
<div class="col s6 m3">
<img class="materialboxed responsive-img" src="images/thumb1.jpg" onclick='this.src="images/photo1"'>
</div>
Material Box Javascript
document.addEventListener('DOMContentLoaded', function() {
var elems = document.querySelectorAll('.materialboxed');
var options = {}
var instances = M.Materialbox.init(elems, options);
});
// Or with jQuery
$(document).ready(function(){
$('.materialboxed').materialbox();
});
Materializecss.com - https://materializecss.com/media.html
I haven't found an easy other way of achieving the lightbox effect with cropped square thumbnails. Any advice would be greatly appreciated!
Here is one implementation of what you want, keeping track of the image click state.
$(document).ready(function(){
$('.materialboxed').materialbox();
// Image sources
const srcThumb = '/images/thumb1.jpg'
const srcPhoto = '/images/photo1.jpg'
// Click state
var clicked = false
// Get image element and bind click event
const img = $('.materialboxed')
img.on('click', function() {
img.attr('src', clicked ? srcPhoto : srcThumb)
clicked = !clicked
})
});
No need to rely on onclick in this case.
Materialize is already binding onclick for those images.
And it provides the following native methods we can use for doing exactly what you want using pure JS (no jQuery):
onOpenStart Function null Callback function called before materialbox is opened.
onCloseEnd Function null Callback function called after materialbox is closed.
In this example below, we assume there is a normal materialboxed photo gallery containing thumbnails named thumb_whatever.jpg, for example. But we're also serving the original sized photo named whatever.jpg in the same directory.
Then we're changing src attribute dynamically removing the thumb_ prefix to get the original image, which in this case will be imediately lightboxed by materialize.
And after closing the lightbox, the src attribute is being set back again without the thumb_ prefix.
We do that while initializing Materialbox:
// Initializing Materialbox
const mb = document.querySelectorAll('.materialboxed')
M.Materialbox.init(mb, {
onOpenStart: (el) => {
var src = el.getAttribute('src') // get the src
var path = src.substring(0,src.lastIndexOf('/')) // get the path from the src
var fileName = src.substring(src.lastIndexOf('/')).replace('thumb_','') // get the filename and removes 'thumb_' prefix
var newSrc = path+fileName // re-assemble without the 'thumb_' prefix
el.setAttribute('src', newSrc)
},
onCloseEnd: (el) => {
var src = el.getAttribute('src') // get the src
var path = src.substring(0,src.lastIndexOf('/')) // get the path from the src
var fileName = src.substring(src.lastIndexOf('/')).replace('/', '/thumb_') // get the filename and adds 'thumb_' prefix
var newSrc = path+fileName // re-assemble with the 'thumb_' prefix
el.setAttribute('src', newSrc)
}
})
This solution is also working like a charm for me, crossplatform.

Issue with InDesign startup script not loading images from XML import

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);
}

Sending a chunk of HTML to another URL using postMessage()

Objective: Copy a chunk of HTML and send it to a website on another domain.
My problem: The website I'm working on and the website in the iframe are on different domains. I own both of them and have set the Access-Control-Allow-Origin to allow the websites to communicate to each other. However, I can't seem to pass the HTML chunk to the parent window.
I've tried parent.window.postMessage(chunk, http://www.parent-page.com) (chunk is the chunk of HTML code) but I get this error:
Uncaught DataCloneError: Failed to execute 'postMessage' on 'Window': An object could not be cloned.
I have also tried to use ajax to send a PUT request to the parent window but, I get a 404 error that it cannot find the parent window. *I am running the parent window from my local server.
My Question: Can anyone tell me the best way to send an object, containing HTML code, from an iframe to the parent window given that the two websites are NOT on the same domain?
EDIT: I removed the stuff about a skeleton object as that was out of the scope of the question I was really trying to ask.
Here is what I wrote to solve this. Any constructive criticism is welcome.
Code on parent window's website:
//send a message to the website in the iframe
$("#frame").on("load", function (event) {
var viewContainer = $('#element-highlight');
var iframe = document.querySelector('iframe');
var receiver = iframe.contentWindow;
var location = 'http://www.child-website.com';
event.preventDefault();
receiver.postMessage("sendStructure",location);
});
//listen for a response
window.addEventListener('message', function(event) { //event = onmessage event object
if (~event.origin.indexOf('http://ccook.oegllc.com')) { //indexOf returns a -1 if the searchValue is not found
var structure = event.data;
var container = document.getElementById("element-highlight");
container.innerHTML = structure;
}
}
<script src="../jquery/3.0.0/jquery.min.js"></script>
<body>
<div id="frame-container">
<iframe id="frame" src="http://www.main-site.com" frameborder="0"></iframe>
<div id="element-highlight">
<!-- Store Skeleton Copies here -->
</div>
</div>
</body>
Code on the website that is shown in the iframe:
I can't get the case statement below to look any better.
//listen for command from main-site.com
window.addEventListener('message', function(event) { //event = onmessage event object
if (~event.origin.indexOf('http://www.main-site.com')) { //indexOf returns a -1 if the searchValue is not found
switch(event.data){
case "sendStructure":
var structure = getStructure(),
tempNode = document.createElement("div");
structure.appendTo(tempNode); //appends structure to html document
var str = tempNode.innerHTML; //creates a serilized version of the structure
parent.window.postMessage(str, event.origin); //send structure string to main-site.com
break;
//I reccomend using a case statement if the two sites will be sending more than one message to each other
default:
sendError();
}
}
});
function getStructure(){
//this method creates a skeleton of the course page you are on
//returns a skeleton object
console.log("compiling structure");
var viewFrame = $('body').contents(); //<-change 'body' to whatever element you want to copy
var visible = viewFrame.find('*:not(html body)').filter(':visible');
var overlayElements = visible.map(function (i, el) {
var el = $(el);
var overlayEl = $('<div>');
overlayEl.addClass('highlight').css($.extend({
position: 'absolute',
width: el.outerWidth(),
height: el.outerHeight(),
'z-index': el.css('z-index')
}, el.offset()));
return overlayEl.get();
});
return overlayElements;
}
function sendError(){
console.log("main-website's request could not be understood");
}

JavaScript previous and next buttons to cycle through an array of .js files

I am having difficulty writing some JavaScript that will cycle through an array of .js files.
I have some JavaScript widgets saved in .js files.
I want to be able to click a "Next" or "Previous" button to cycle through an array of those .js files and have the widgets called and displayed on my HTML page. They can be displayed in an iFrame if that would be a better solution.
I will continue researching until a kind soul helps out. Thanks a bunch in advance!
I have tried:
<script>
function onWindowLoad(){
document.getElementById('js_type').innerHTML = ****.settings.type;
var widget_arr = [1column.js,2column.js,1row.js,modal.js]; //etc..etc..
var currentWidget = 0;
theBtn.onRelease = function(){
currentWidget++;
if(currentWidget == widget_arr.length){
currentWidget=0;
}
var selectedWidget = widget_arr[currentWidget];
//now you have a variable pointing to the next widget..
//what you do with it is up to you.. add the code you need..
}
and this
<SCRIPT LANGUAGE="JavaScript">
<!--
// Use the following variable to specify
// the number of widgets
var NumberOfWidgets = 4
var widget = new Array(NumberOfWidgets)
// Use the following variables to specify the widget names:
widget[0] = "1column.js"
widget[1] = "2column.js"
widget[2] = "1row.js"
widget[3] = "modal.js"
var widgetNumber = 0
function NextWidget()
{
widgetNumber++
if (widgetNumber == NumberOfWidgets)
widgetNumber = 0
document.widgets["VCRWidget"].src = widget[widgetNumber]
}
function PreviousWidget()
{
widgetNumber--
if (widgetNumber < 0)
widgetNumber = NumberOfWidgets - 1
document.widgets["VCRWidget"].src = widget[widgetNumber]
<IMG SRC="modal.js" NAME="VCRWidget">
}
//-->
</SCRIPT>
Code for the previous and next buttons:
<A HREF="javascript:PreviousWidget()">
<IMG SRC="prev.png" BORDER=0></A>
<A HREF="javascript:NextWidget()">
<IMG SRC="next.png" BORDER=0></A>
One idea can be to use the fact thst even a static page can have parameters in the url. You can for example:
create the html page that will be opened in the <iframe> with any js include you need (e.g. jquery)
add to this page a js function that given a widget js filename will create a <script> tag loading the widget creation code.
extract the name of the js file to use to call the function in (2) from document.location.href by looking at the part of the string after ?
in your main page create dinamically the iframe using for example contdiv.innerHTML = "<iframe src=\"widgetpage.html?" + widgetname + ".js\"></iframe>";
With this approach the widgets will be shown in a separate html page without interferring with your main page.

Open window in JavaScript with HTML inserted

How would I open a new window in JavaScript and insert HTML data instead of just linking to an HTML file?
I would not recomend you to use document.write as others suggest, because if you will open such window twice your HTML will be duplicated 2 times (or more).
Use innerHTML instead
var win = window.open("", "Title", "toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes,width=780,height=200,top="+(screen.height-400)+",left="+(screen.width-840));
win.document.body.innerHTML = "HTML";
You can use window.open to open a new window/tab(according to browser setting) in javascript.
By using document.write you can write HTML content to the opened window.
When you create a new window using open, it returns a reference to the new window, you can use that reference to write to the newly opened window via its document object.
Here is an example:
var newWin = open('url','windowName','height=300,width=300');
newWin.document.write('html to write...');
Here's how to do it with an HTML Blob, so that you have control over the entire HTML document:
https://codepen.io/trusktr/pen/mdeQbKG?editors=0010
This is the code, but StackOverflow blocks the window from being opened (see the codepen example instead):
const winHtml = `<!DOCTYPE html>
<html>
<head>
<title>Window with Blob</title>
</head>
<body>
<h1>Hello from the new window!</h1>
</body>
</html>`;
const winUrl = URL.createObjectURL(
new Blob([winHtml], { type: "text/html" })
);
const win = window.open(
winUrl,
"win",
`width=800,height=400,screenX=200,screenY=200`
);
You can open a new popup window by following code:
var myWindow = window.open("", "newWindow", "width=500,height=700");
//window.open('url','name','specs');
Afterwards, you can add HTML using both myWindow.document.write(); or myWindow.document.body.innerHTML = "HTML";
What I will recommend is that first you create a new html file with any name.
In this example I am using
newFile.html
And make sure to add all content in that file such as bootstrap cdn or jquery, means all the links and scripts. Then make a div with some id or use your body and give that a id. in this example I have given id="mainBody" to my newFile.html <body> tag
<body id="mainBody">
Then open this file using
<script>
var myWindow = window.open("newFile.html", "newWindow", "width=500,height=700");
</script>
And add whatever you want to add in your body tag. using following code
<script>
var myWindow = window.open("newFile.html","newWindow","width=500,height=700");
myWindow.onload = function(){
let content = "<button class='btn btn-primary' onclick='window.print();'>Confirm</button>";
myWindow.document.getElementById('mainBody').innerHTML = content;
}
myWindow.window.close();
</script>
it is as simple as that.
You can also create an "example.html" page which has your desired html and give that page's url as parameter to window.open
var url = '/example.html';
var myWindow = window.open(url, "", "width=800,height=600");
Use this one. It worked for me very perfect.
For New window:
new_window = window.open(URL.createObjectURL(new Blob([HTML_CONTENT], { type: "text/html" })))
for pop-up
new_window = window.open(URL.createObjectURL(new Blob([HTML_CONTENT], { type: "text/html" })),"width=800,height=600")
Replace HTML_CONTENT with your own HTML Code
Like:
new_window = window.open(URL.createObjectURL(new Blob(["<h1>Hello</h1>"], { type: "text/html" })))
if your window.open() & innerHTML works fine, ignore this answer.
following answer only focus on cross-origin access exception
#key-in_short,workaround:: [for cross-origin access exception]
when you exec code in main.html -- which tries to access file window_ImageGallery.html by using window.open() & innerHTML
for anyone who encounter cross-origin access exception
and you dont want to disable/mess_around_with Chrome security policy
-> you may use query string to transfer the html code data, as a workaround.
#details::
#problem-given_situation,#problem-arise_problem::
say you exec following simple window.open command as other answer suggested.
let window_Test = window.open('window_ImageGallery.html', 'Image Enlarged Window' + $(this).attr('src'), 'width=1000,height=800,top=50,left=50');
window_Test.document.body.innerHTML = 'aaaaaa';
you may encounter following cross-origin access exception
window_Test.document.body.innerHTML = 'aaaaaa'; // < Exception here
Uncaught DOMException: Blocked a frame with origin "null" from accessing a cross-origin frame.
=> #problem-solution-workaround::
you may use query string to transfer the html code data, as a workaround. <- Transfer data from one HTML file to another
#eg::
in your main.html
// #>> open ViewerJs in a new html window
eleJq_Img.click(function() {
// #>>> send some query string data -- a list of <img> tags, to the new html window
// #repeat: must use Query String to pass html code data, else you get `Uncaught DOMException: Blocked a frame with origin "null" from accessing a cross-origin frame.` (cross origin access issue)
let id_ThisImg = this.id;
let ind_ThisImg = this.getAttribute('data-index-img');
let url_file_html_window_ImageGallery = 'window_ImageGallery.html'
+ '?queryStr_html_ListOfImages=' + encodeURIComponent(html_ListOfImages)
+ '&queryStr_id_ThisImg=' + encodeURIComponent(id_ThisImg)
+ '&queryStr_ind_ThisImg=' + encodeURIComponent(ind_ThisImg);
// #>>> open ViewerJs in a new html window
let window_ImageGallery = window.open(url_file_html_window_ImageGallery, undefined, 'width=1000,height=800,top=50,left=50');
});
in your window_ImageGallery.html
window.onload = function () {
// #>> get parameter from URL
// #repeat: must use Query String to pass html code data, else you get `Uncaught DOMException: Blocked a frame with origin "null" from accessing a cross-origin frame.` (cross origin access issue)
// https://stackoverflow.com/questions/17502071/transfer-data-from-one-html-file-to-another
let data = getParamFromUrl();
let html_ListOfImages = decodeURIComponent(data.queryStr_html_ListOfImages);
let id_ThisImgThatOpenedTheHtmlWindow = decodeURIComponent(data.queryStr_id_ThisImg);
let ind_ThisImgThatOpenedTheHtmlWindow = decodeURIComponent(data.queryStr_ind_ThisImg);
// #>> add the Images to the list
document.getElementById('windowImageGallery_ContainerOfInsertedImages').innerHTML = html_ListOfImages;
// -------- do your stuff with the html code data
};
function getParamFromUrl() {
let url = document.location.href;
let params = url.split('?')[1].split('&');
let data = {};
let tmp;
for (let i = 0, l = params.length; i < l; i++) {
tmp = params[i].split('=');
data[tmp[0]] = tmp[1];
}
return data
}
#minor-note::
(seems) sometimes you may not get the cross-origin access exception
due to, if you modify the html of 'window_ImageGallery.html' in main.html before window_ImageGallery.html is loaded
above statement is based on my test
& another answer -- window.open: is it possible open a new window with modify its DOM
if you want to make sure to see that Exception,
you can try to wait until the opening html window finish loading, then continue execute your code
#eg::
use defer() <- Waiting for child window loading to complete
let window_ImageGallery = window.open('window_ImageGallery.html', undefined, 'width=1000,height=800,top=50,left=50');
window_ImageGallery.addEventListener("unload", function () {
defer(function (){
console.log(window_ImageGallery.document.body); // < Exception here
});
});
function defer (callback) {
var channel = new MessageChannel();
channel.port1.onmessage = function (e) {
callback();
};
channel.port2.postMessage(null);
}
or use sleep() with async What is the JavaScript version of sleep()?
eleJq_Img.click(async function() {
...
let window_Test = window.open( ...
...
await new Promise(r => setTimeout(r, 2000));
console.log(window_Test.document.body.innerHTML); // < Exception here
});
or you get null pointer exception
if you try to access elements in window_ImageGallery.html
#minor-comment::
There are too many similar Posts about the cross-origin issue. And there are some posts about window.open()
Idk which post is the best place to place the answer. And I picked here.

Categories