How to seamlessly display scraped web page which has intermittent updates - javascript

I want to display a web page in kiosk mode on a 1080p monitor showing "what's on". The data can be scraped from an existing web page (which I don't control). It must then be displayed with some new formatting (background image, no scroll bars, new header and footer).
The original web page is updated at infrequent intervals. I am happy for my page to update every 15 minutes or so. My page should maximise the fonts to display as large as possible (and reduce the font on any overflow).
This is all being done on a Raspberry Pi Zero so horsepower is limited.
I am currently scraping the original web page using a perl program scheduled with cron. This program extracts the relevant table of data and adds new header and footer. To try to make the change seamless, my web page includes this new page as an iframe - actually two iframes that it swaps between so the rendering is invisible:
<body onload="load(); update();">
<h1>What's on this week</h1>
<div id="floating-div">
<iframe src="iframe.html" allowtransparency="true" frameborder="0" style="width:100%;height:100%;" id="if1"></iframe>
<iframe src="iframe.html" allowtransparency="true" frameborder="0" style="width:100%;height:100%;" id="if2"></iframe>
</div>
</body>
function update() {
if1 = document.getElementById("if1");
if2 = document.getElementById("if2");
if (if1.style.display == "none") {
if1.style.display = "block";
if2.style.display = "none";
if2.src = "iframe.html";
} else {
if2.style.display = "block";
if1.style.display = "none";
if1.src = "iframe.html";
}
}
function load() {
setInterval(update, 15000);
}
The iframe.html page (created by perl) is:
<body onload="set_size();">
<div class="cdiv">
<table id="calendar" class="weekly">
...
</table>
</div>
</body>
I am struggling on how to adjust the font size dynamically in the set_size() function. I can adjust the size on the visible iframe but this doesn't work on the one loading while invisible. The function I am using to detect overflow is:
function check(el) {
var curOverf = el.style.overflow;
if (!curOverf || curOverf === "visible")
el.style.overflow = "hidden";
var isOverflowing = el.clientWidth < el.scrollWidth ||
el.clientHeight < el.scrollHeight;
el.style.overflow = curOverf;
return isOverflowing;
}
How can I do this seamless display update with dynamic font sizing?
Apologies for the long post but I wanted to explain the whole problem I am trying to solve.

Ok, so I gave up on my original approach. I have now re-written the perl program to implement a Web Socket server which scrapes the web site every 15 minutes. If there is a change in the scraped data, it sends it to any connections.
The javascript in the local web page receives this data via a web socket and replaces the table that it had with the new table. The font resize routine then runs as before. Since the page is visible, it works as intended.
The downside is that this update will be visible. However, updates for the current week are infrequent so this is acceptable.

Related

Adobe XD embedded app-prototype causes page to 'jump' down to mid-page after loading

I use Squarespace for my portfolio site. They have a "block" that allows me to use embedded Adobe XD code (below) to show my app prototype. The prototype works fine but when the page loads, it automatically shifts half way down the page to the prototype. link to page
<center>
<iframe id="nautilab" width="414" height="736" src="https://xd.adobe.com/embed/afb3c48a-11a6-4296-73d9-068cd5b0c5ef-d982" allowfullscreen" frameborder="0">
</iframe>
</center>
I would like for my page to remain at the top when fully loaded instead of jumping down mid-page to the prototype. I've tried countless solutions such as using sandbox, lazy loading, and loading on scroll view. I've tried using "data src=""" as well among other options.
Unfortunately none of these solutions worked. The only time it doesn't jump down to the prototype is when it doesn't load at all (which happened with lazy loading, loading on scroll). How can I fix this? I'm able to use HTML, CSS, and JavaScript as well.
My best guess is to delay loading the prototype until it's in view. The only other option would be placing the prototype at the top of the page, which is not what I want (it messes up the flow of the project).
Any help is greatly appreciated!
Option 1: Use the "sandbox" attribute.
This appears to be a known issue within the Adobe Community site, with a solution proposed using the sandbox iframe attribute.
<iframe id="nautilab" width="414" height="736" src="https://xd.adobe.com/embed/afb3c48a-11a6-4296-73d9-068cd5b0c5ef-d982" allowfullscreen" frameborder="0" sandbox="allow-same-origin allow-forms allow-scripts"></iframe>
Or, possibly even more restrictive (which may cause the iframe not to work):
<iframe id="nautilab" width="414" height="736" src="https://xd.adobe.com/embed/afb3c48a-11a6-4296-73d9-068cd5b0c5ef-d982" allowfullscreen" frameborder="0" sandbox></iframe>
Option 2: Use the "onload" attribute with "scroll()"
If neither of the above work, you could try using the onload attribute to force the scroll position:
<iframe id="nautilab" width="414" height="736" src="https://xd.adobe.com/embed/afb3c48a-11a6-4296-73d9-068cd5b0c5ef-d982" allowfullscreen" frameborder="0" onload="scroll(0,0);"></iframe>
Option 3: Load the iframe only when in view.
If neither option 1 or 2 work, you could only load the iframe once it is already in view for the user (once they have scrolled down). For browsers that don't support IntersectionObserver, keep the external link as you have it. For browsers that do, hide the link and load the iframe. Insert the following via a code block above the image block that has your external link:
<iframe id="nautilab" width="0" height="0" frameborder="0" allowFullScreen></iframe>
Then insert the following via sitewide footer code injection
<script>
(function() {
var target,
io,
ioCallback,
ioOptions,
linkBlock;
// Exit if id "nautilab" not found.
target = document.querySelector('#nautilab');
if (!target) {
return;
}
// Check for IntersectionObserver Support: https://github.com/w3c/IntersectionObserver/issues/296#issuecomment-452230176
if (!('IntersectionObserver' in window) ||
!('IntersectionObserverEntry' in window) ||
!('intersectionRatio' in window.IntersectionObserverEntry.prototype)) {
target.style.display = "none";
return;
}
// Because IntersectionObserver is supported, hide external link to prototype.
linkBlock = document.querySelector('#block-yui_3_17_2_1_1574114822673_377170');
linkBlock.style.display = "none";
// Loads the iframe when the 'target' is in view.
ioCallback = function(entries, observer) {
entries.forEach(function(entry) {
if (entry.intersectionRatio) {
observer.disconnect();
target.height = "736"
target.width = "414";
target.src = "https://xd.adobe.com/embed/afb3c48a-11a6-4296-73d9-068cd5b0c5ef-d982";
}
});
};
ioOptions = {
root: null,
rootMargin: "0px",
threshold: 1
};
// Observe for 'target' to be in view.
io = new IntersectionObserver(ioCallback, ioOptions);
io.observe(target);
})();
</script>
You'll still have to center the prototype using CSS, which shouldn't be too difficult.

Looking for the right code architecture to embed audio while preventing flickering on page updates (HTML/javascript/Ajax)

I'm creating a little game. The main playing field needs server-side updates to prevent cheating. After every update of the playing-field I want a part of the page's HTML to remain unchanged, in order to play a background music that persists while the player navigates from screen to screen.
The problem is that every attempt of achieving this results in flickering of the playing field on page-updates when the player moves along the game.
I've tried HTML (IFRAME, FRAME & FRAMESET tags), and Ajax (no framework) as solutions to exclude the playing of music from the updates of the main playing field, to no avail.
Without music the game does not flicker on updates as there's no need for static unchanging HTML in the page to play consistent background music.
Music implemented, using HTML FRAME and FRAMEST tags, results in flickering on page updates of the main playing field.
Finally, background music implemented using a Ajax function that only updates the playing field also results in flickering of the screen on updates as the player moves along from screen to screen.
Any advice here on a working architecture (without the use of popups) that prevents this flickering, preferably without using a framework?
The IFRAME test was just that. A simple IFRAME.
<iframe src=game.php etc etc>
The FRAMESET code is also straightforward:
<frameset id="music" rows="100%,0%">
<frame id="pageContent" name="pageContent" frameborder=0 marginheight="0" src="index.php">
<frame id="pageMusic" name="pageMusic" frameborder=0 marginheight="0" src="./music/07. Koholint Island.mp3">
</frameset>
THE AJAX code is also very basic:
<html>
<head>
<script type="text/javascript">
<!--
//Browser Support Code
function ajaxFunction(URL) {
var ajaxRequest; // The variable that makes Ajax possible!
try {
// Opera 8.0+, Firefox, Safari
ajaxRequest = new XMLHttpRequest();
}
catch (e) {
// Internet Explorer Browsers
try {
ajaxRequest = new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e) {
try {
ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e) {
// Something went wrong (User Probably Doesn't have JS or JS is turned off)
alert("You Browser Doesn't support AJAX.");
return false;
}
}
}
// Create a function that will receive data sent from the server
ajaxRequest.onreadystatechange = function()
{
//This if satement will check if the status of the script
//If the readyState is not equal to 4 (The request is complete)
//It will display text that says loading...
if(ajaxRequest.readyState < 4)
{
//AJAX in the prenthacies, is what id element in the body will be changed.
document.getElementById('AJAX').innerHTML = "Loading...";
}
//Once the readyState is equal to four,
//this means that the request was sent,
//and successfully processed.
if(ajaxRequest.readyState == 4)
{
//This is where the output of the file we called and it will be placed
//in the div where we named the ID = AJAX
document.getElementById('AJAX').innerHTML = ajaxRequest.responseText;
}
}
//This section processes the data
ajaxRequest.open("GET", URL, true);
ajaxRequest.send(null);
}
//-->
</script>
</head>
<body>
music here
<audio autoplay loop preload>
<source src="./music/07. Koholint Island.mp3" type="audio/mp3">
<p>Your browser doesn't support HTML5 audio. Here is a link to the audio instead.</p>
</audio>
<div id="AJAX">
<a href=# onCLick="ajaxFunction('test2.php?a=1')">
start game
</a>
</div>
</body>
</html>
The PHP code returns the HTML of the playing field.
I expected the screen to update smoothly as in the first demo when adding the static unchanging music HTML. After these three tests I can't seem to think of a working architecture.
solved it (for now). removing "document.getElementById('AJAX').innerHTML = "Loading...";" made the flickering similar to the first demo. thanks.

How to get iframe's refreshed src after interacting

I have an iframe tag that is linked with a Grafana graph, which supports interaction (zoom in/zoom out on x axis by clicking/double clicking on the iframe).
When I open the url of the iframe's src on a new tab I can interact with the graph and see that my browser's url params keeps refreshing with new from/to values (which indicates the range of the graph's x axis). Unfortunately when it is on an iframe I don't see any changes on the 'src' attribute, on any situation. I need the url parameters changes to apply on other graphs being displayed (sync them all).
How can I solve this situation?
My iframe on Angular's component.html:
<iframe [src]="url_grafana_primary" width="100%" height="300" frameborder="0"></iframe>
Iframe after being rendered:
<iframe _ngcontent-c8="" frameborder="0" height="300" width="100%" src="http://146.250.180.213/grafana/dashboard-solo/script/script_graph.js?scenario_id=rrc_succ_rate&cell_id=ESICAS23B_ESICAS23&refresh=5s&orgId=1&panelId=4&from=1555045266010&to=1555168469864&var-cell_id=ESICAS23B_ESICAS23&var-scenario_id=ESICAS23B_ESICAS23"></iframe>
Some screenshots:
Before changes: https://i.imgur.com/6M5JoHX.png
After changes: https://i.imgur.com/MN0KIha.png
You can see that src keep the same value on both situations.
The attribute of src is not changing, but the location object should behaves the same as it behaves when grafana is outside of the iframe.
You have 2 options,
if the frame of grafana & its parent are served from the same origin, you can access the location object with iframe.contentWindow.location.href.
if they are with different origins, you need to communicate between the frame and its parent, you can use postMessage for that.
check this example: https://robertnyman.com/html5/postMessage/postMessage.html
You can detect the onload event, but to get the URL, you will have to store a cookie or localStorage.
var firstTimeLoad = true; // Because the function will call on the initial page
function iframeLoad(iframe) {
if (firstTimeLoad) {
firstTimeLoad = false;
return;
}
alert("New page load");
}
<iframe width="400" height="244" src="https://wooden-utensil.github.io/linkingSite1/" onload="iframeLoad(this)" frameBorder="0"></iframe> <!-- linkingSite1 contains a link (a href) that links to linkingSite2, and vice versa -->
You just need to set the src of the iframe to the same old src. You may need to add a query string variable with random number like timestamp to avoid caching issues.
function refresh(){
var iframe = document.getElementById('myframe');
iframe.src = iframe.src;
}
iframe{
width:100%;
height:300px;
}
<p>
<button onclick="refresh()">Refresh</button>
</p>
<iframe id="myframe" src="https://www.chartjs.org/samples/latest/charts/line/basic.html" frameborder="0"></iframe>
In case when you are not the owner of the content in an iframe, you can't do much. The best you can do is to have a loop (setInterval), that will check the value against the last value you have seen.
<iframe #graph></iframe>
#ViewChild('graph', { static: true }) graph!: ElementRef<HTMLIFrameElement>;
private srcWatcher = new Observable(observer => {
const interval = setInterval(() => {
observer.next(this.graph && this.graph.nativeElement && this.graph.nativeElement.src);
}, 10);
return () => clearInterval(interval);
}).pipe(
distinctUntilChanged(),
);
having observable as this one, you can now react to any changes on it.
NOTE: I would also suggest that the observable should run outside of zoneJS if the interval timeout should be low. Otherwise Angular would run global change detection on every interval. By global I mean, that it would check the whole app any all of the subtrees, that uses the default change detection strategy.

ASP.NET MVC 5; using Office 365 online doc viewer to display word docs showing up in tiny thin window

We have been trying to display a Word document (.docx) in an iframe on our website using the Office 365 service. Our document is stored in One-Drive for business online and shared appropriately. We signed in and got a link to our document using various means available on Office 365 online. We then stored the link in our SQL database. Our website references the table with the stored URL and puts that information in the 'src' of the iframe and the document shows up in tiny sliver, despite setting the height of the iframe to 1100 and the width to 800. Code is below:
pseudo url
https://microsoft-my.sharepoint.com/personal/accountName/_layouts/15/WopiFrame.aspx?sourcedoc=blahblahblah&action=embedview
Partial View code:
#using Website.DataAccess.Models
#model List<Report>
#if (Model != null && Model.Count > 0)
{
foreach (var report in Model)
{
<div align="center">
#switch (report.DocType)
{
case ".docx":
<iframe src="#report.URL" frameborder="0" style="width:100%; height:1050px;" scrolling="yes" sandbox="allow-forms allow-same-origin allow-scripts"></iframe>
break;
case ".mp4":
<video width="800" height="600" controls>
<source src="#report.URL" type="video/mp4"/>
Video not supported.
</video>
break;
default:
<p>Document type not supported.</p>
break;
}
</div>
<div align="center">
#report.Name
</div>
}
}
else
{
<p>No files found.</p>
}
// don't honestly think this script is doing anything useful; just trying anyuthing
<script type="text/javascript">
window.addEventListener('load', function () {
if (typeof (EmbedManager) === undefined) {
EmbedManager.loadResult();
}
}, false);
</script>
We have tried for many hours to find a solution, including experimenting with the parameters of the url and the iframe and have come up with nothing. We haven't found anyone out there with this problem. Again, the viewport in the iframe is sometimes a centimeter tall, sometimes 5 centimeters tall, sometimes it shows the whole document. We have tried setting the size both in style and outside of style and nothing.
The document displays fine when you paste it into the address bar of the browser, but not inside an iframe on our setup. When running our site, nothing shows up in FireFox and Edge except an error saying we can't use an iframe for this content. Shows up in IE and Chrome though, just more often than not it's inside a thin viewport.
Any help would be appreciated, thank you.
Okay sorry everyone. Looks like UI from the main view was somehow interfering with the viewer's ability to display correctly. We are not sure what caused it, but have since moved partial view to a new page to solve the problem.
Thanks for taking the time to read my question and think about.
-L_Laxton

Loading strategy of client side rendered html resources for html5 game

I am working on an HTML5 facebook game (inside facebook canvas iframe) in which I use jquery in addition to some other js files, css files (image files, in the css files), font files, sound files and screens (html divs in seperate files).
I want to have a loading script as the size of the resources is around 1 MB. There are two options;
first one is writing a resource loader and load everything in correct order which really is painful.
second one is first having a simple loading screen at startup, which will be quicly loaded, upon loading this page, starting to load the actual html (with js, css and everyting) and handing over the loading process to the browser client.
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
function iframeIsLoaded()
{
...
}
</script>
</head>
<body>
<div id="loadingScreen" class="..."> <!-- iframe will be under this div -->
...
</div>
<iframe ...>
</iframe>
...
obviously second option is much better but I don't have a clue how to do it. As shown above, I may use an iframe under the loading screen div but is there a way to send a message to the upper div from the iframe?
I am also open to other solutions!
You can do this using the iframe.load event.
What you will want to do is hide the iframe on page load and show the loading screen, then you want to wait until the content is loaded then Show the frame and hide the Loading screen.
(This example assumes you are using the src attribute of the IFrame to load the content)
Pure Javascript : Example JSFiddle
var frame = document.getElementById('iframeID');
var loading = document.getElementById('loadingScreen');
frame.style.display = 'none';//Originally hide the frame
loading.style.display = 'block';//Originally show the Loading Screen
frame.src = 'http://www.bing.com';//Set the src attribute
frame.onload = function() {
frame.style.display = 'block';//Show the frame after it is loaded
loading.style.display = 'none';//Hide the loading screen
}
EDIT : (Removed jQuery Example and Added a new Example based on Comment)
Here is a new example that checks the child page for the variable done to check if it is set to true.
Warning this example has the potential to not work due to Cross-Domain Scripting Security, this should only be used if you are 100% that both pages are on the same Domain
Child Page :
var done = false;
setTimeout(function () {
done = true;
}, 10000);
Parent Page : (Script needs placing after the HTML / Before the end of the Body Tag ())
<div>
<div id="loading">
Loading...
</div>
<iframe id="iframeID"></iframe>
</div>
<script type="text/javascript">
var frame = document.getElementById('iframeID');
var loading = document.getElementById('loading');
frame.style.display = 'none'; //Originally hide the frame
loading.style.display = 'block'; //Originally show the Loading Screen
frame.src = 'Test2.aspx'; //Set the src attribute
frame.onload = function () {
console.log('Loaded Frame');
}
var $interval = setInterval(CheckFrameDone, 500);
function CheckFrameDone() {
var done = frame.contentWindow.done;
console.log(done);
if (done) {
console.log('Frame is Finished Loading');
frame.style.display = 'block';
loading.style.display = 'none';
clearInterval($interval);
} else {
console.log('Still Waiting...');
}
}
</script>
With the second example you will notice that every 500 Milliseconds the parent page will check the child page for the done value, if it is true it will show the frame and clear the interval. Otherwise it will just continue to check.

Categories