html2canvas wait for images to load - javascript

I'm having this problem for some time ow and I can't seem to find a solution.
I'm using the latest html2canvas js plugin to take a screenshot of chart made with flotcharts and then submit the base64 screenshot via a hidden input. The problem is the div with the chart also has some images and html2canvas return a base64 string before the images are loaded. I can put a setTimeout on the submit untill they are loaded but apparently chrome opens the page where I submitted as a popup (which is really not ok cause our clients don't know how allow popups).
So this is what I tried but the images are not preloaded (because of the async nature of html2canvas)
function getPNGBase64forHtml($container) {
var imageString;
html2canvas($container, {
useCORS: true,
onrendered: function(canvas) {
imageString = canvas.toDataURL('image/png');
}
});
return imageString;
}
And also this (this works ok but it doesn't load the images in time):
function getPNGBase64forHtml($container) {
var h2canvas = html2canvas($container);
var queue = h2canvas.parse();
var canvas = h2canvas.render(queue);
return canvas.toDataURL('image/png');
}
So the problem is with waiting till the images are loaded in html2canvas then exeuting the rest of my stuff.
If anyone can please help, that would be very much appreciated, I bow to you kind sirs and madams! :)
Edit:
Here is the html of the part that I capture, this all is in a print-container div thats all. The arrow (only one is showed) in the timeline-prognose doesn't get captured cause it's a image, everything else does:
<div id="timeline-outer-container">
<div id="timeline-container" class="flot-container">
<div id="timeline-chart" class="flot-chart">
<canvas class="flot-base" width="888" height="335" ></canvas>
<div class="flot-text" >
<div class="flot-x-axis flot-x1-axis xAxis x1Axis">
<div class="flot-tick-label tickLabel" >Q1</div>
<div class="flot-tick-label tickLabel">Q2</div>
...
</div>
<div class="flot-y-axis flot-y1-axis yAxis y1Axis">
<div class="flot-tick-label tickLabel">75</div>
<div class="flot-tick-label tickLabel">100</div>
...
</div>
</div>
<canvas class="flot-overlay" width="888" height="335"></canvas>
<div class="axis-label xaxis">Zeitraum</div>
<div class="axis-label yaxis rotate-90">Anzahl</div>
<div id="zoom-out-button" class="timeline-zoom-button"><i class="fa fa-zoom-out"></i></div>
<div id="zoom-in-button" class="timeline-zoom-button"><i class="fa fa-zoom-in"></i></div>
<div id="zoom-default-button" class="timeline-zoom-button"><i class="fa fa-repeat"></i></div>
</div>
</div>
</div>
<div id="timeline-prognose">
<img id="timeline-arrow-up" class="timeline-arrows" src="/portal//images/arrows/up.png" alt="">
<img id="timeline-arrow-down" class="timeline-arrows" src="/portal//images/arrows/down.png" alt="">
</div>

So since you're using remotes images you can use the following fix doing some modifications in your script:
function getPNGBase64forHtml() {
var imageString;
html2canvas(document.body, {
useCORS: true,
logging : true, //Enable log (use Web Console for get Errors and Warnings)
proxy :"html2canvasproxy.php",
onrendered: function(canvas) {
var img = new Image();
img.onload = function() {
img.onload = null;
document.body.appendChild(img);
};
img.onerror = function() {
img.onerror = null;
if(window.console.log) {
window.console.log("Not loaded image from canvas.toDataURL");
} else {
alert("Not loaded image from canvas.toDataURL");
}
};
imageString = canvas.toDataURL('image/png');
}
});
return imageString;
}
If you are using php then the proxy setting must use the following script: html2canvas-php-proxy
Otherwise with .NET projects you can use these script resources:
html2canvas proxy with asp-classic - vb
html2canvas proxy with asp.net - csharp
If you decided to use local images this bug will not appear.
Hope it works for you, here is the original thread for this html2canvas bug https://github.com/niklasvh/html2canvas/issues/145

Related

Why are my Filepond uploads not working on Android devices only?

I am currently using Filepond to upload images to an S3 bucket. This functionality works great on a computer or on iOS devices but refuses to work on Android devices and I'm having the time of my life figuring out the root cause. It should upload just fine across all devices. Has anyone experienced any issues similar to this on an Android device using Filepond? I've also attached below the relevant Filepond code. I don't see any issues with it, but of course, I may be missing something. If anyone could offer a pointer or any kind of advice it would be greatly appreciated or if I left anything out please let me know. Thanks!
JS
<!-- Load FilePond library -->
<script src="https://unpkg.com/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.js"></script>
<script src="https://unpkg.com/filepond/dist/filepond.js"></script>
<script src="//cdn.jsdelivr.net/npm/sweetalert2#10"></script>
<!-- include FilePond jQuery adapter -->
<script src="https://unpkg.com/jquery-filepond/filepond.jquery.js"></script>
<script type="application/javascript">
let tags = [];
let folderOpen = false;
let currentTag = "Uncategorized";
let imageUploadUrl = "{{ url_for('testing_jobs.job_image_upload', id=job.id) }}";
// Page init code
$( document ).ready(() => {
// Select Fields
$(".select2").select2();
// Setup FilePond inputs
$.fn.filepond.registerPlugin(FilePondPluginImagePreview);
// Turn input element into a pond
$('.filepond').filepond();
// Set FilePond properties
$('.filepond').filepond('allowMultiple', true);
$('.filepond').filepond('instantUpload', true);
$('.filepond').filepond('server', {
url: imageUploadUrl,
process: {
onload: (res) => {
// Select the response field to use as a unique ID
let image = JSON.parse(res).image;
if (folderOpen) {
getJobImages(currentTag);
} else {
alert("Image added")
}
return image.id;
}
}
});
// Listen for addfile event
$('.filepond').on('FilePond:addfile', function(e) {
console.log('file added event', e);
// Set the current tag for the image
e.detail.file.setMetadata("tag",currentTag);
});
// Listen for processfile event
$('.filepond').on('FilePond:processfile', function(e) {
console.log('file process event', e);
setTimeout(() => {
e.detail.pond.removeFile();
}, 2000);
});
Html for submitting
<div class="card">
<div class="card-header">
<h4>Upload Images</h4>
</div>
<div class="card-body">
<input type="file" class="filepond">
</div>
</div>
The issue turned out to be the image attachments on my Android device. Set up some checks for checking the file types and now it all works.

Video error - Uncaught (in promise) DOMException: The play() request was interrupted by a call to pause()

I have recently purchased a video script with a built-in ads system. Seems to work pretty good. I can dynamically add various types of ads, etc. There is an issue, however, with it playing one of my videos. The video file plays fine in Windows, also plays fine in Chrome and Edge. It also played fine using a native HTML5 video player, as well as with video.js. When I switched to this new script, I tested out all of my videos and saw that one video that previously worked as I mentioned, no longer will play. In the Google console it provides the message "Uncaught (in promise) DOMException: The play() request was interrupted by a call to pause()."
Unlike the other video players that I have used, all of the HTML tags for this player are generated by the js file which grabs the video file location, thumb location, ad locations / times, etc. from the dynamically generated divs, etc.
I have researched A LOT regarding this issue, and I can't seem to figure out what the problem is. Please take a look at the code and let me know if anyone sees anything that sticks out. Thanks in advance! Oh, and I should say that all other videos work with this new player, besides this one.
Note: Since the JS file has >300k lines I cannot include it in this message area, however you can view the JS file here: https://www.stoners.org/videos/java/vplayer.unmin.js
I initialize the player with this:
<script src="/videos/java/vplayer.unmin.js" type="text/javascript"></script>
<script type="text/javascript">
FWDUVPUtils.onReady(function(){
new FWDUVPlayer({
//main settings
instanceName:"player1",
parentId:"myDiv",
playlistsId:"playlists",
mainFolderPath:"content",
skinPath:"minimal_skin_dark",
displayType:"responsive",
initializeOnlyWhenVisible:"no",
useFontAwesomeIcons:"no",
fillEntireVideoScreen:"no",
useHEXColorsForSkin:"no",
normalHEXButtonsColor:"#FF0000",
selectedHEXButtonsColor:"#000000",
useDeepLinking:"yes",
rightClickContextMenu:"default",
addKeyboardSupport:"yes",
showPreloader:"yes",
preloaderColors:["#999999", "#FFFFFF"],
autoScale:"yes",
showButtonsToolTip:"yes",
stopVideoWhenPlayComplete:"no",
playAfterVideoStop:"no",
autoPlay:"yes",
loop:"no",
shuffle:"no",
showErrorInfo:"yes",
maxWidth:980,
maxHeight:552,
buttonsToolTipHideDelay:1.5,
volume:.8,
backgroundColor:"#000000",
videoBackgroundColor:"#000000",
posterBackgroundColor:"#000000",
buttonsToolTipFontColor:"#5a5a5a",
//logo settingscate
showLogo:"yes",
hideLogoWithController:"no",
logoPosition:"topRight",
logoLink:"https://www.stoners.org/videos",
logoMargins:5,
//playlists/categories settings
showPlaylistsSearchInput:"no",
usePlaylistsSelectBox:"no",
showPlaylistsButtonAndPlaylists:"no",
showPlaylistsByDefault:"no",
thumbnailSelectedType:"opacity",
startAtPlaylist:0,
buttonsMargins:0,
thumbnailMaxWidth:350,
thumbnailMaxHeight:350,
horizontalSpaceBetweenThumbnails:40,
verticalSpaceBetweenThumbnails:40,
inputBackgroundColor:"#333333",
inputColor:"#999999",
//playlist settings
showPlaylistButtonAndPlaylist:"no",
playlistPosition:"right",
showPlaylistByDefault:"yes",
showPlaylistName:"yes",
showSearchInput:"no",
showLoopButton:"yes",
showShuffleButton:"yes",
showNextAndPrevButtons:"yes",
showThumbnail:"yes",
forceDisableDownloadButtonForFolder:"yes",
addMouseWheelSupport:"yes",
startAtRandomVideo:"no",
stopAfterLastVideoHasPlayed:"no",
folderVideoLabel:"VIDEO ",
playlistRightWidth:310,
playlistBottomHeight:599,
startAtVideo:0,
maxPlaylistItems:50,
thumbnailWidth:70,
thumbnailHeight:70,
spaceBetweenControllerAndPlaylist:2,
spaceBetweenThumbnails:2,
scrollbarOffestWidth:8,
scollbarSpeedSensitivity:.5,
playlistBackgroundColor:"#000000",
playlistNameColor:"#FFFFFF",
thumbnailNormalBackgroundColor:"#1b1b1b",
thumbnailHoverBackgroundColor:"#313131",
thumbnailDisabledBackgroundColor:"#272727",
searchInputBackgroundColor:"#000000",
searchInputColor:"#999999",
youtubeAndFolderVideoTitleColor:"#FFFFFF",
folderAudioSecondTitleColor:"#999999",
youtubeOwnerColor:"#888888",
youtubeDescriptionColor:"#888888",
mainSelectorBackgroundSelectedColor:"#FFFFFF",
mainSelectorTextNormalColor:"#FFFFFF",
mainSelectorTextSelectedColor:"#000000",
mainButtonBackgroundNormalColor:"#212021",
mainButtonBackgroundSelectedColor:"#FFFFFF",
mainButtonTextNormalColor:"#FFFFFF",
mainButtonTextSelectedColor:"#000000",
//controller settings
showController:"yes",
showControllerWhenVideoIsStopped:"yes",
showNextAndPrevButtonsInController:"no",
showRewindButton:"yes",
showPlaybackRateButton:"yes",
showVolumeButton:"yes",
showTime:"yes",
showQualityButton:"yes",
showInfoButton:"no",
showDownloadButton:"no",
showFacebookButton:"yes",
showEmbedButton:"yes",
showFullScreenButton:"yes",
disableVideoScrubber:"no",
showDefaultControllerForVimeo:"no",
repeatBackground:"yes",
controllerHeight:37,
controllerHideDelay:3,
startSpaceBetweenButtons:7,
spaceBetweenButtons:8,
scrubbersOffsetWidth:2,
mainScrubberOffestTop:14,
timeOffsetLeftWidth:5,
timeOffsetRightWidth:3,
timeOffsetTop:0,
volumeScrubberHeight:80,
volumeScrubberOfsetHeight:12,
timeColor:"#888888",
youtubeQualityButtonNormalColor:"#888888",
youtubeQualityButtonSelectedColor:"#FFFFFF",
//advertisement on pause window
aopwTitle:"Sponsor",
aopwWidth:400,
aopwHeight:240,
aopwBorderSize:6,
aopwTitleColor:"#FFFFFF",
//subtitle
subtitlesOffLabel:"Subtitle off",
//popup add windows
showPopupAdsCloseButton:"yes",
//embed window and info window
embedAndInfoWindowCloseButtonMargins:0,
borderColor:"#333333",
mainLabelsColor:"#FFFFFF",
secondaryLabelsColor:"#a1a1a1",
shareAndEmbedTextColor:"#5a5a5a",
inputBackgroundColor:"#000000",
inputColor:"#FFFFFF",
//audio visualizer
audioVisualizerLinesColor:"#0099FF",
audioVisualizerCircleColor:"#FFFFFF",
//lightbox settings
lightBoxBackgroundOpacity:.6,
lightBoxBackgroundColor:"#000000",
//sticky display settings
showOpener:"yes",
showOpenerPlayPauseButton:"yes",
verticalPosition:"bottom",
horizontalPosition:"center",
showPlayerByDefault:"yes",
animatePlayer:"yes",
openerAlignment:"right",
mainBackgroundImagePath:"https://www.stoners.org/videos/content/minimal_skin_dark/main-background.png",
openerEqulizerOffsetTop:-1,
openerEqulizerOffsetLeft:3,
offsetX:0,
offsetY:0,
//loggin
isLoggedIn:"no",
playVideoOnlyWhenLoggedIn:"no",
loggedInMessage:"Please login to view this video.",
//playback rate / speed
defaultPlaybackRate:1, //0.25, 0.5, 1, 1.25, 1.2, 2
//cuepoints
executeCuepointsOnlyOnce:"no",
//ads
openNewPageAtTheEndOfTheAds:"no",
playAdsOnlyOnce:"no",
adsButtonsPosition:"right",
skipToVideoText:"You can skip ad in: ",
skipToVideoButtonText:"Skip Ad",
adsTextNormalColor:"#888888",
adsTextSelectedColor:"#FFFFFF",
adsBorderNormalColor:"#666666",
adsBorderSelectedColor:"#FFFFFF"
});
});
</script>
You can see that it doesn't work in the player by going here:
https://www.stoners.org/videos/profiles/9/weed-truffles-75mg#/?playlistId=0&videoId=0
You can access the video file in question directly to see that the file will actually open in Chrome, Windows, etc by going here: https://www.stoners.org/videos/library/1527856863.mp4
Please let me know if there is anything else I can provide to help figure out what the problem is. Appreciate it!
I managed to solve this by using the following code:
function fix(item) {
var thePromise = item.play();
if (thePromise != undefined) {
thePromise.then(function(_) {
item.pause();
item.currentTime = 0;
});
}
}
Just execute that with whatever audio/video you have, and it'll stop giving that error.
Great! The fix of Jack Bashford worked.
I added a Video to a Revolution Slider, which needed to be played as soon as the Slide is visible:
HTML Markup:
<div class="tp-caption" data-x="center" data-y="0" data-speed="1500" data-start="500">
<video id="xxx" title="xxx" webkit-playsinline="true" autoplay="true" muted="muted" preload="auto" controls="false" playsinline="true">
<source src="file" type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"'>
</video>
</div>
JavaScript with your fix:
jQuery(document).ready(function() {
$('.owl-carousel').owlCarousel();
App.init();
// RevolutionSlider.initRSfullWidth();
var revapi;
revapi = jQuery('.tp-banner').revolution(
{
delay:5500,
startwidth:1170,
startheight:600,
hideThumbs:10,
navigationType:'none',
navigationStyle:'square',
navigationHAlign:'center',
navigationVAlign:'bottom',
navigationArrows:'none',
onHoverStop: 'off',
navigation: {
onHoverStop: 'off'
}
});
revapi.on('revolution.slide.onchange', function(event, data) {
console.log("Current Slide: "+data.slideIndex);
if(data.slideIndex==2) {
console.log("Play the video v2");
var vid = document.getElementById("fiveyears");
vid.controls = false;
var thePromise = vid.play();
if (thePromise != undefined) {
console.log("Caught Promise Error");
thePromise.then(function(_) {
vid.pause();
vid.currentTime = 0;
vid.play();
});
} else {
vid.play();
}
vid.controls = false;
}
});
});

PDF.JS - Detect when page has been rendered

Once PDF.JS has completed rendered each page, I want to then do a find/replace on the contents of that page.
I invoke PDF.JS by putting the following in a document in an iFrame:
<script>
fileId=0;
function getURLParameter(name) {
return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search)||[,""])[1].replace(/\+/g, '%20'))||null
}
var fileId = getURLParameter("fileId");
var DEFAULT_URL = '/viewer/fetchpdf.php?fileId='+fileId;
</script>
and then setting the URL from the parent frame:
url = '/_third_party/pdfjs/web/viewer.html?fileId='+$(this).attr('href');
$("#iframeViewPdf").attr('src', url);
I've noticed when using PDF.JS to render a PDF, it initialises each page with a loading placeholder:
<div id="pageContainer3" class="page" style="width: 991px; height: 1319px;">
<div class="loadingIcon"></div>
</div>
<div id="pageContainer4...
It then renders the PDF as html, e.g.
<div id="pageContainer3" class="page" style="width: 991px; height: 1319px;">
<div class="canvasWrapper" style="width: 991px; height: 1319px;">
<canvas id="page46" width="991" height="1319" style="width: 991px; height: 1319px;">
</canvas>
</div>
<div class="textLayer" style="width: 991px; height: 1319px;">
...
</div>
</div>
<div id="pageContainer4...
With the clarification, it is a very different story. You are not using PDF.JS directly, but their web wrapper. One thing that I think you can use (I've never done it, just reading the code now) is the fact that they are emitting pageRendered event on the document, so if you can add a listener to it, you should be fine:
var frameDoc = document.getElementById('iframeViewPdf').contentWindow.document;
frameDoc.addEventListener('pagerendered', function (evt) {
console.log(evt); // see what goodies hide here! like page number etc
}
(Didn't test, might need tweaking.)
So this is how we can detect the rendering of a page. It's important to wait for the iframe contents to load before setting up the listener.
$( "#iframeViewPdf" ).load(function() { // wait for iframe to load
var frameDoc = $("#iframeViewPdf").contents()[0];
frameDoc.addEventListener("pagerendered", function (evt) {
console.log(evt.detail);
});
});
//Step 1: store a refer to the renderer
var pageRendering = page.render(renderContext);
//Step : hook into the pdf render complete event
var completeCallback = pageRendering.internalRenderTask.callback;
pageRendering.internalRenderTask.callback = function (error) {
//Step 2: what you want to do before calling the complete method
completeCallback.call(this, error);
//Step 3: do some more stuff
};

Knockout.js: Changing the value of Observable does not change the value of attribute in html

I have a fairly simple file which renders a pencil image like this:
define(['durandal/events', 'knockout'], function (events, ko) {
var pencilVM= function () {
this.pencilimagepath = ko.observable("img/pencil.png");
};
return pencilVM;
});
and a html:
<div>
<img data-bind="attr: {src: pencilimagepath}" />
</div>
Now when I change the name of the image from pencil.png to someImg.png, the final html rendered in the browser is not updated. It stills stays as "pencil.png"
I think the browser is caching the image.
Just add the timestamp to your new image source
var d = new Date();
this.pencilimagepath("img/someImg.png" + d.getTime());
Hope that helps.

Dropzone remove previous image

I'm needing help with an answer to a previous question
How to limit the number of dropzone.js files uploaded?
where there was a solution to use
Dropzone.options.myAwesomeDropzone = {
accept: function(file, done) {
console.log("uploaded");
done();
},
init: function() {
this.on("addedfile", function() {
if (this.files[1]!=null){
this.removeFile(this.files[0]);
}
});
}
};
But being a js numpty, I don't know where to put this. I assume that my form must have the id myAwesomeDropzone and that the code above needs to be slotted into the dropzone.js document, but where? if you could provide a 'after' and 'before' or replace answer please.
If someone could give me some pointer it'd be great.
My reputation is less than 50 so I couldn't comment on the original thread. If me posting this as a new thread is wrong, please admins don't just chastise me and close it, but move it or do whatever to allow people to provide help accrodingly.
Cheers
Andy
Your html form should be like below. Notice that the
class="dropzone"
and
id="my-dropzone"
The "my-dropzone" ID is important , DropZone takes this as "myDropzone" , ( hyphen removed and camelcased ). So when you refer to this id in DropZone.options , it should be "myDropzone". This is very important.
<div>
<form action="/file-upload" class="dropzone" id="my-dropzone" style="border:2px dotted #337ab7">
<div class="dz-message">
Drag n drop photos here or click to upload.
<br />
<span class="note">(The photos are uploaded to twitter only when the tweet is sent.)</span>
</div>
</form>
</div>
Your should link the css and js file in your html page like so
<script src="./js/dropzone.min.js"></script>
<link href="./css/dropzone.min.css" rel="stylesheet" type="text/css">
On the page load , you should init the dropzone configuration in your JS code for your page as below. I am restricting dropzone to accept only one file and its size should not exceed 5MB.
self.options.maxFilesize = 5; self.options.maxFiles = 1;
Dropzone.autoDiscover = false;
Dropzone.options.myDropzone = {
init: function() {
var self = this;
// config
self.options.addRemoveLinks = true;
self.options.autoProcessQueue = false;
self.options.maxFilesize = 5;
self.options.maxFiles = 1;
self.options.dictFileTooBig =
"Twitter do not allow files greater than 5MB.";
//New file added
self.on("addedfile", function(file) {
console.log('new file added ', file.path);
});
self.on("maxfilesexceeded", function(file) {
alert(
"Only one photo can be attached with the tweet."
);
});
}
};
Now when your page loads , you will see the form & get the drag n drop functionality in your div.
To access the files you can do like below in the javascript on some button click event.
// to get queued files
var myDropzone = Dropzone.forElement("#my-dropzone");
var files = myDropzone.getQueuedFiles();

Categories