I'm using a package called laravel-admin.
I created a custom signature field with popular SignaturePad package, the data got converted into image successfully. However when I tried to submit the form I've got an error saying CSRF token miss match.
Here's my code for rendering SignaturePad class:
public function render()
{
$this->script = <<<EOT
var canvas = document.getElementById('signature-pad');
// Adjust canvas coordinate space taking into account pixel ratio,
// to make it look crisp on mobile devices.
// This also causes canvas to be cleared.
function resizeCanvas() {
// When zoomed out to less than 100%, for some very strange reason,
// some browsers report devicePixelRatio as less than 1
// and only part of the canvas is cleared then.
var ratio = Math.max(window.devicePixelRatio || 1, 1);
canvas.width = canvas.offsetWidth * ratio;
canvas.height = canvas.offsetHeight * ratio;
canvas.getContext("2d").scale(ratio, ratio);
}
window.onresize = resizeCanvas;
resizeCanvas();
var signaturePad = new SignaturePad(canvas, {
backgroundColor: 'rgb(255, 255, 255)' // necessary for saving image as JPEG; can be removed is only saving as PNG or SVG
});
$('button[type="submit"]').click(function() {
console.log(signaturePad.toDataURL());
$('input[type=hidden]').val(signaturePad.toDataURL());
});
EOT;
return parent::render();
}
If I remove the line below then I won't get it, but obviously my data is not sent. The way it works is I converts the value of the image and put it to the hidden input field to be sent. Can anyone help?
$('button[type="submit"]').click(function() {
console.log(signaturePad.toDataURL());
$('input[type=hidden]').val(signaturePad.toDataURL());
});
Apparently I forgot that laravel gives hidden input containing CSRF token that I am changing all together with jQuery code
$('button[type="submit"]').click(function() {
console.log(signaturePad.toDataURL());
$('input[type=hidden]').val(signaturePad.toDataURL());
});
Thanks everyone
Related
I have been buying animated 3D models from TurboSquid and they fall into two starkly different categories:
Ready to use - I just load the FBX file and it looks fine in my ThreeJS scene
Missing textures - The loaded FBX model looks geometrically fine and the animations work, but all the surfaces are white
I want to know how to load the provided textures using the FBXLoader module. I have noticed that all of the FBX models, which I believe are PBR material based, have this set of files:
*002_BaseColor.png
*002_Emissive.png
*002_Height.png
*002_Metallic.png
*002_Roughness.png
*002_Normal.png
I have these questions:
How would I use the FBXLoader module to load the textures "in the right places"? In other words, how do I make the calls so that the BaseColor texture goes to the right place, the Emissive texture so it goes where it should, etc?
Do I need to pass it a custom material loader to the FBXLoader and if so, which one and how?
UPDATE: I trace through the ThreeJS FBX loader code I discovered that the FBX model that is not showing any textures does not have a Texture node at all. The FBX model that does show the textures does, as you can see in this screenshot of the debugger:
I'll keep tracing. If worse comes to worse I'll write custom code to add a Textures node with the right fields since I don't know how to use Blender to add the textures there. My concern is of course that even if I create a Textures node at run-time, they still won't end up in the proper place on the model.
This is the code I am currently using to load FBX models:
this.initializeModel = function (
parentAnimManagerObj,
initModelArgs= null,
funcCallWhenInitialized = null,
) {
const methodName = self.constructor.name + '::' + `initializeModel`;
const errPrefix = '(' + methodName + ') ';
self.CAC.parentAnimManagerObj = parentAnimManagerObj;
self.CAC.modelLoader = new FBXLoader();
self.CAC.modelLoader.funcTranslateUrl =
((url) => {
// Fix up the texture file names.
const useRobotColor = misc_shared_lib.uppercaseFirstLetter(robotColor);
let useUrl =
url.replace('low_Purple_TarologistRobot', `Tarologist${useRobotColor}`);
return useUrl;
});
// PARAMETERS: url, onLoad, onProgress, onError
self.CAC.modelLoader.load(
MODEL_FILE_NAME_TAROLOGIST_ROBOT_1,
(fbxObj) => {
CommonAnimationCode.allChildrenCastAndReceiveShadows(fbxObj);
fbxObj.scale.x = initModelArgs.theScale;
fbxObj.scale.y = initModelArgs.theScale;
fbxObj.scale.z = initModelArgs.theScale;
self.CAC.modelInstance = fbxObj;
// Set the initial position.
self.CAC.modelInstance.position.x = initModelArgs.initialPos_X;
self.CAC.modelInstance.position.y = initModelArgs.initialPos_Y;
self.CAC.modelInstance.position.z = initModelArgs.initialPos_Z;
// Create an animation mixer for this model.
self.CAC.modelAnimationMixer = new THREE.AnimationMixer(self.CAC.modelInstance);
// Speed
self.CAC.modelAnimationMixer.timeScale = initModelArgs.theTimeScale;
// Add the model to the scene.
g_ThreeJsScene.add(self.CAC.modelInstance);
// Don't let this part of the code crash the entire
// load call. We may have just added a new model and
// don't know certain values yet.
try {
// Play the active action.
self.CAC.activeAction = self.CAC.modelActions[DEFAULT_IDLE_ANIMATION_NAME_FOR_TAROLOGIST_ROBOT_1];
self.CAC.activeAction.play();
// Execute the desired callback function, if any.
if (funcCallWhenInitialized)
funcCallWhenInitialized(self);
} catch (err) {
const errMsg =
errPrefix + misc_shared_lib.conformErrorObjectMsg(err);
console.error(`${errMsg} - try`);
}
},
// onProgress
undefined,
// onError
(err) => {
// Log the error message from the loader.
console.error(errPrefix + misc_shared_lib.conformErrorObjectMsg(err));
}
);
}
I'm currently working on creating pdf files with jspdf and the AutoTable plugin.
My plan is to create a table like this:
I have the images as local urls, and I'm trying to add them to the pdf using the new Image and adding them as .src to the image.
When I directly run the jspdf.addImage function with the image, the images display correctly.
But I'm struggling to get the correct scaling to work. So I wanted to use the width and height properties from the image, but for this to work you need to wait for the image to load.
I tried to use the onload function, but it stops rendering the table in general, because it skips over the function and continues with the next table before the images load.
If you have any suggestions of how to get something like this to work it would be greatly appreciated. The images all have variable resolution and need to be scaled down to properly fit in the table. I'll paste my working code (but without the height and width scaling) below.
doc.autoTable({
head: [{comments: 'Photos'}],
body: body,
styles: {
lineColor: [0, 0, 0],
lineWidth: 0.4,
},
headStyles: {
fillColor: [191, 191, 191],
textColor: [0, 0, 0],
},
didDrawCell: function (data) {
if (data.section === 'body') {
console.log(data);
const image = new Image();
// image.onload = function() {
// console.log(this);
// const width = this.width;
// const height = this.height;
// console.log({width, height})
// doc.addImage(
// this,
// 'JPEG',
// data.cell.x + 5,
// data.cell.y + 2,
// )
// }
image.src = QuestionPhotos.link(photosObject[data.cell.raw])
doc.addImage(
image,
'JPEG',
data.cell.x + 5,
data.cell.y + 2,
)
}
}
The commented out part of this code was my other attempt where I would add the image after it was loaded, but this made the image not appear at all in the pdf.
I have been able to solve most of my question using the following functions:
The scaling I was able to fix by storing the width and height together with my images. I then set my height to be fixed (in my case 80mm) and calculated the width like this: photo.height / photo.width * 80.
I tried the other functions Ihsan mentioned, but without any luck.
The didDrawCell is used for inserting the images after the cell has been drawn. willDrawCell draws the image behind the table. didParseCell will insert the images in the top corner of the page instead of in the table itself.
I also initially used the didParseCell to increase the size of the cells to be the correct size for the images, but later on changed this to adding the style when creating the body of the table.
body.push([{content:'', styles: {minCellHeight: height + 10}}]);
I kept track of the row index to know at what rows to insert the image and so that the sizing is correct.
I'm aware of binding a pop-up to ESRI's L.esri.DynamicMapLayer here. The following code below is successful.
$.ajax({
type: 'GET',
url: url + '?f=json',
data: { layer: fooType },
dataType: 'json',
success: function(json) {
var foo_layer = fooLayers[fooType].layers;
foo = L.esri.dynamicMapLayer({
url: url,
layers: [foo_layer],
transparent: true
}).addTo(map).bringToFront();
foo.bindPopup(function(error, featureCollection) {
if (error || featureCollection.features.length === 0) {
return false;
} else {
var obj = featureCollection.features[0].properties;
var val = obj['Pixel Value'];
var lat = featureCollection.features[0].geometry.coordinates[1];
var lon = featureCollection.features[0].geometry.coordinates[0];
new L.responsivePopup({
autoPanPadding: [10, 10],
closeButton: true,
autoPan: false
}).setContent(parseFloat(val).toFixed(2)).setLatLng([lat, lon]).openOn(map);
}
});
}
});
But rather than a click response I am wondering as to whether you can mouseover using bindTooltip instead on a dynamic map. I've looked at the documentation for L.esri.DynamicMapLayer which says it is an extension of L.ImageOverlay. But perhaps there is an issue outlined here that I'm not fully understanding. Maybe it is not even related.
Aside, I've been testing multiple variations of even the simplest code to get things to work below but have been unsuccessful. Perhaps because this is asynchronous behavior it isn't possible. Looking for any guidance and/or explanation(s). Very novice programmer and much obliged for expertise.
$.ajax({
type: 'GET',
url: url + '?f=json',
data: { layer: fooType },
dataType: 'json',
success: function(json) {
var foo_layer = fooLayers[fooType].layers;
foo = L.esri.dynamicMapLayer({
url: url,
layers: [foo_layer],
transparent: true
}).addTo(map).bringToFront();
foo.bindTooltip(function(error, featureCollection) {
if (error || featureCollection.features.length === 0) {
return false;
} else {
new L.tooltip({
sticky: true
}).setContent('blah').setLatLng([lat,lng]).openOn(map);
}
});
}
});
Serendipitously, I have been working on a different problem, and one of the byproducts of that problem may come in handy for you.
Your primary issue is the asynchronous nature of the click event. If you open up your map (the first jsfiddle in your comment), open your dev tools network tab, and start clicking around, you will see a new network request made for every click. That's how a lot of esri query functions work - they need to query the server and check the database for the value you want at the given latlng. If you tried to attach that same behavior to a mousemove event, you'll trigger a huge number of network requests and you'll overload the browser - bad news.
One solution of what you can do, and its a lot more work, is to read the pixel data under the cursor of the image returned from the esri image service. If you know the exact rgb value of the pixel under the cursor, and you know what value that rgb value corresponds to in the map legend, you can achieve your result.
Here is a working example
And Here is the codesandbox source code. Don't be afraid to hit refresh, CSB is little wonky in the way it transpiles the modules.
What is happening here? Let's look step by step:
On map events like load, zoomend, moveend, a specialized function is fetching the same image that L.esri.dynamicMapLayer does, using something called EsriImageRequest, which is a class I wrote that reuses a lot of esri-leaflet's internal logic:
map.on("load moveend zoomend resize", applyImage);
const flashFloodImageRequest = new EsriImageRequest({
url: layer_url,
f: "image",
sublayer: "3",
});
function applyImage() {
flashFloodImageRequest
.fetchImage([map.getBounds()], map.getZoom())
.then((image) => {
//do something with the image
});
}
An instance of EsriImageRequest has the fetchImage method, which takes an array of L.LatLngBounds and a map zoom level, and returns an image - the same image that your dynamicMapLayer displays on the map.
EsriImageRequest is probably extra code that you don't need, but I happen to have just run into this issue. I wrote this because my app runs on a nodejs server, and I don't have a map instance with an L.esri.dynamicMapLayer. As a simpler alternative, you can target the leaflet DOM <img> element that shows your dynamicMapLayer, use that as your image source that we'll need in step 2. You will have to set up a listener on the src attribute of that element, and run the applyImage in that listener. If you're not familiar with how leaflet manages the DOM, look into your elements tab in the inspector, and you can find the <img> element here:
I'd recommend doing it that way, and not the way my example shows. Like I said, I happened to have just been working on a sort-of related issue.
Earlier in the code, I had set up a canvas, and using the css position, pointer-events, and opacity properties, it lays exactly over the map, but is set to take no interaction (I gave it a small amount of opacity in the example, but you'd probably want to set opacity to 0). In the applyImage function, the image we got is written to that canvas:
// earlier...
const mapContainer = document.getElementById("leafletMapid");
const canvas = document.getElementById("mycanvas");
const height = mapContainer.getBoundingClientRect().height;
const width = mapContainer.getBoundingClientRect().width;
canvas.height = height;
canvas.width = width;
const ctx = canvas.getContext("2d");
// inside applyImage .then:
.then((image) => {
image.crossOrigin = "*";
ctx.drawImage(image, 0, 0, width, height);
});
Now we have an invisible canvas who's pixel content is exactly the same as the dynamicMapLayer's.
Now we can listen to the map's mousemove event, and get the mouse's rgba pixel value from the canvas we created. If you read into my other question, you can see how I got the array of legend values, and how I'm using that array to map the pixel's rgba value back to the legend's value for that color. We can use the legend's value for that pixel, and set the popup content to that value.
map.on("mousemove", (e) => {
// get xy position on cavnas of the latlng
const { x, y } = map.latLngToContainerPoint(e.latlng);
// get the pixeldata for that xy position
const pixelData = ctx.getImageData(x, y, 1, 1);
const [R, G, B, A] = pixelData.data;
const rgbvalue = { R, G, B, A };
// get the value of that pixel according to the layer's legend
const value = legend.find((symbol) =>
compareObjectWithTolerance(symbol.rgbvalue, rgbvalue, 5)
);
// open the popup if its not already open
if (!popup.isOpen()) {
popup.setLatLng(e.latlng);
popup.openOn(map);
}
// set the position of the popup to the mouse cursor
popup.setLatLng(e.latlng);
// set the value of the popup content to the value you got from the legend
popup.setContent(`Value: ${value?.label || "unknown"}`);
});
As you can see, I'm also setting the latlng of the popup to wherever the mouse is. With closeButton: false in the popup options, it behaves much like a tooltip. I tried getting it to work with a proper L.tooltip, but I was having some trouble myself. This seems to create the same effect.
Sorry if this was a long answer. There are many ways to adapt / improve my code sample, but this should get you started.
I would like to validate by file dimensions (resolution).
on the documentation page there is only information regarding file name and size, nothing at all in the docs about dimensions, and I also had no luck on Google.
The purpose of this is that I don't want users to upload low-res photos to my server. Thanks.
As Ray Nicholus had suggested, using the getFile method to get the File object and then use that with the internal instance object qq.ImageValidation to run fineuploader's validation on the file. A promise must be return because this proccess is async.
function onSubmit(e, id, filename){
var promise = validateByDimensions(id, [1024, 600]);
return promise;
}
function validateByDimensions(id, dimensionsArr){
var deferred = new $.Deferred(),
file = uploaderElm.fineUploader('getFile', id),
imageValidator = new qq.ImageValidation(file, function(){}),
result = imageValidator.validate({
minWidth : dimensionsArr[0],
minHeight : dimensionsArr[1]
});
result.done(function(status){
if( status )
deferred.reject();
else
deferred.resolve();
});
return deferred.promise();
}
Remained question:
Now I wonder how to show the thumbnail of the image that was rejected, while not uploading it to the server, the UI could mark in a different color as an "invalid image", yet the user could see which images we valid and which weren't...
- Update - (regarding the question above)
While I do not see how I could have the default behavior of a thumbnail added to the uploader, but not being uploaded, but there is a way to generate thumbnail manually, like so:
var img = new Image();
uploaderElm.fineUploader("drawThumbnail", id, img, 200, false);
but then I'll to create an item to be inserted to qq-upload-list myself, and handle it all myself..but still it's not so hard.
Update (get even more control over dimensions validation)
You will have to edit (currently) the qq.ImageValidation function to expose outside the private function getWidthHeight. just change that function deceleration to:
this.getWidthHeight = function(){
Also, it would be even better to change the this.validate function to:
this.validate = function(limits) {
var validationEffort = new qq.Promise();
log("Attempting to validate image.");
if (hasNonZeroLimits(limits)) {
this.getWidthHeight().done(function(dimensions){
var failingLimit = getFailingLimit(limits, dimensions);
if (failingLimit) {
validationEffort.failure({ fail:failingLimit, dimensions:dimensions });
}
else {
validationEffort.success({ dimensions:dimensions });
}
}, validationEffort.success);
}
else {
validationEffort.success();
}
return validationEffort;
};
So you would get the fail reason, as well as the dimensions. always nice to have more control.
Now, we could write the custom validation like this:
function validateFileDimensions(dimensionsLimits){
var deferred = new $.Deferred(),
file = this.holderElm.fineUploader('getFile', id),
imageValidator = new qq.ImageValidation(file, function(){});
imageValidator.getWidthHeight().done(function(dimensions){
var minWidth = dimensions.width > dimensionsLimits.width,
minHeight = dimensions.height > dimensionsLimits.height;
// if min-width or min-height satisfied the limits, then approve the image
if( minWidth || minHeight )
deferred.resolve();
else
deferred.reject();
});
return deferred.promise();
}
This approach gives much more flexibility. For example, you would want to have different validation for portrait images than landscape ones, you could easily identify the image orientation and run your own custom code to do whatever.
I am using iScroll5 in a PhoneGap project. On the index page, user will click on a series of thumbnails generated from a database, then the image ID chosen will be written to localstorage, the page will change, the image ID will be pulled from localstorage and the image displayed.
It works fine if I reference the image directly (not from the DB) this way (as a test):
<body onload="loaded()">
<div id='wrapper'><div id='scroller'>
<ul><li><a id='output' href='index.html' onclick='returnTo()'></a></li></ul>
</div></div>
<script>
var newWP = document.createElement('img');
newWP.src = '0buggies/0118_buggies/wallpaper-18b2.jpg';
document.getElementById('output').appendChild(newWP);
</script>
</body>
I can pinch/zoom to resize the image for the screen (the main function my app requires), and scroll the image on the X and Y axis, then upon tapping the image, I will be returned to the index page. All of this works.
But if I pull the image out of a database and reference it the following way, all other aspects of the page code being the same, pinch/zoom does not work, though the picture is displayed and I can scroll on X and Y:
// ... DB code here ...
function querySuccess(tx, results) {
var path = results.rows.item.category +
"/" + results.rows.item.subcat +
"/" + results.rows.item.filename_lg;
document.getElementById("output").innerHTML = "<img src='" + path +
"'>";
}
// ... more DB code here ...
<body onload="loaded()">
<div id='wrapper'> <ul><li><a id='output' href='index.html'
onclick='returnTo()'></a></li></ul> </div>
How do I make iScroll5 work when the image is generated from a DB? I'm using the same CSS and iScroll JS on both pages. (iScroll4 has the same problem as iScroll 5 above.) I am using the SQLite DB plugin (from http://iphonedevlog.wordpress.com/2014/04/07/installing-chris-brodys-sqlite-database-with-cordova-cli-android/ which is my own site).
Try calling refresh on the scrollbar to get it to recognize the DOM change.
Best to wrap it in a 0-delay setTimeout, like so (Stolen from http://iscrolljs.com/#refresh)
:
setTimeout(function () {
myScroll.refresh();
}, 0);
If it takes time for the image to load, you'll want to wait until it's loaded entirely, unless you know the dimensions up-front.
When dealing with images loaded dynamically things get a little more complicated. The reason is that the image dimensions are known to the browser only when the image itself has been fully loaded (and not when the img tag has been added to the DOM).
Your best bet is to explicitly declare the image width/height. You'd do this like so:
function querySuccess (results) {
var path = results.rows.item.category +
"/" + results.rows.item.subcat +
"/" + results.rows.item.filename_lg;
var img = document.createElement('img');
img.width = 100;
img.height = 100;
img.src = path;
document.getElementById('output').appendChild(img);
// need to refresh iscroll in case the previous img was smaller/bigger than the new one
iScrollInstance.refresh();
}
If width/height are unknown you could save the image dimensions into the database and retrieve them together with the image path.
function querySuccess (results) {
var path = results.rows.item.category +
"/" + results.rows.item.subcat +
"/" + results.rows.item.filename_lg;
var img = document.createElement('img');
img.width = results.width;
img.height = results.height;
img.src = path;
document.getElementById('output').appendChild(img);
// need to refresh iscroll in case the previous img was smaller/bigger than the new one
iScrollInstance.refresh();
}
If you can't evaluate the image dimensions in any way then you have to wait for the image to be fully loaded and at that point you can perform an iScroll.refresh(). Something like this:
function querySuccess (results) {
var path = results.rows.item.category +
"/" + results.rows.item.subcat +
"/" + results.rows.item.filename_lg;
var img = document.createElement('img');
img.onload = function () {
setTimeout(iScrollInstance.refresh.bind(iScrollInstance), 10); // give 10ms rest
}
img.onerror = function () {
// you may want to deal with error404 or connection errors
}
img.src = path;
document.getElementById('output').appendChild(img);
}
Why is the viewport user-scalable prop different on each sample? works=no, broken=yes
Just an observation.
fwiw, here are a few things to look into:
Uncomment the deviceReady addListener, as Cordova init really depends on this.
Your loaded() method assigns myScroll a new iScroll, then explicitly calls onDeviceReady(), which then declares var myScroll; -- this seems inherently problematic - rework this.
If 1 & 2 don't help, then I suggest moving queryDB(tx); from populateDB() to successCB() and commenting out the myScroll.refresh()
And just a note, I find that logging to console is less intrusive than using alerts when trying to track down a symptom that seems to be messing with events firing, or timing concerns.